root/livinglogic.python.xist/src/ll/scripts/ucp.py @ 4422:fe09ca906d4e

Revision 4422:fe09ca906d4e, 6.9 KB (checked in by Walter Doerwald <walter@…>, 8 years ago)

Bump copyright year. Change encoding of remaining files to UTF-8. Remove trailing whitespace.

RevLine 
[3189]1#!/usr/local/bin/python
2# -*- coding: utf-8 -*-
3
4
[4422]5## Copyright 2007-2011 by LivingLogic AG, Bayreuth/Germany.
6## Copyright 2007-2011 by Walter Dörwald
[3189]7##
8## All Rights Reserved
9##
[3263]10## See ll/__init__.py for the license
[3189]11
12
[4413]13"""
14``ucp`` is a script that copies files/directory contents. It is an URL-enabled
15version of the ``cp`` system command. Via :mod:`ll.url` and :mod:`ll.orasql`
16``ucp`` supports ``ssh`` and ``oracle`` URLs too.
17
18
19Options
20-------
21
22``ucp`` supports the following options:
23
24    ``urls``
25        Two or more URLs. If more than two URLs are given or if the last URL refers
26        to a directory, the last URL is the target directory. All other sources
27        are copied into this target directory. Else one file is copied to another
28        file.
29
30    ``-v``, ``--verbose`` : ``false``, ``no``, ``0``, ``true``, ``yes`` or ``1``
31        Give a report during the copy process about the files copied and their sizes.
32
33    ``-c``, ``--color`` : ``yes``, ``no`` or ``auto``
34        Should the ouput be colored. If ``auto`` is specified (the default) then
35        the output is colored if stdout is a terminal.
36
37    ``-u``, ``--user``
38        A user id or name. If given ``ucp`` will change the owner of the
39        target files.
40
41    ``-g``, ``--group``
42        A group id or name. If given ``ucp`` will change the group of the
43        target files.
44
45    ``-r``, ``--recursive`` : ``false``, ``no``, ``0``, ``true``, ``yes`` or ``1``
46        Copies files recursively.
47
48    ``-x``, ``--ignoreerrors`` : ``false``, ``no``, ``0``, ``true``, ``yes`` or ``1``
49        Ignores errors occuring during the copy process. (Otherwise the copy
50        process is aborted.)
51
52    ``-i``, ``--include`` : regular expression
53        Only copy files that contain the regular expression.
54
55    ``-e``, ``--exclude`` : regular expression
56        Don't copy files that contain the regular expression.
57
58    ``-a``, ``--all`` : ``false``, ``no``, ``0``, ``true``, ``yes`` or ``1``
59        Include dot files (i.e. files whose name starts with a ``.``). Not that
60        the content of directories whose name starts with a dot will still be
61        copied.
62
63
64Examples
65--------
66
67Copy one file to another::
68
69    $ ucp foo.txt bar.txt
70
71Copy a file into an existing directory::
72
73    $ ucp foo.txt dir/
74
75Copy multiple files into a new or existing directory (and give a progress
76report)::
77
78    $ ucp foo.txt bar.txt baz.txt dir/ -v
79    ucp: foo.txt -> dir/foo.txt (1114 bytes)
80    ucp: bar.txt -> dir/bar.txt (2916 bytes)
81    ucp: baz.txt -> dir/baz.txt (35812 bytes)
82
83Recursively copy the schema objects in an Oracle database to a local directory::
84
85    ucp oracle://user:pwd@oracle.example.org/ db/ -r
86
87Recursively copy the schema objects in an Oracle database to a remote directory::
88
89    ucp oracle://user:pwd@oracle.example.org/ ssh://user@www.example.org/~/db/ -r
90"""
91
92
[4381]93import sys, re, argparse, contextlib
[3189]94
[4283]95from ll import misc, url
[3189]96
97try:
98    import astyle
99except ImportError:
100    from ll import astyle
101
[3732]102try:
103    from ll import orasql # activate the oracle scheme
104except ImportError:
105    pass
106
[3189]107
[4413]108__docformat__ = "reStructuredText"
109
110
[3189]111def main(args=None):
[4403]112    def match(url):
113        strurl = str(url)
[4382]114        if args.include is not None and args.include.search(strurl) is None:
115            return False
116        if args.exclude is not None and args.exclude.search(strurl) is not None:
117            return False
[4409]118        if not args.all:
119            if url.file:
120                name = url.file
121            elif len(url.path) >=2:
122                name = url.path[-2]
123            else:
124                name = ""
125            if name.startswith("."):
126                return False
[4382]127        return True
128
[3189]129    def copyone(urlread, urlwrite):
[4381]130        strurlread = str(urlread)
[3783]131        if urlread.isdir():
[4113]132            if args.recursive:
[3783]133                for u in urlread.listdir():
134                    copyone(urlread/u, urlwrite/u)
135            else:
[4113]136                if args.verbose:
[4390]137                    msg = astyle.style_default("ucp: ", astyle.style_url(strurlread), astyle.style_warn(" (directory skipped)"))
[3783]138                    stderr.writeln(msg)
139        else:
[4403]140            if match(urlread):
[4381]141                if args.verbose:
142                    msg = astyle.style_default("ucp: ", astyle.style_url(strurlread), " -> ")
143                    stderr.write(msg)
144                try:
145                    with contextlib.closing(urlread.open("rb")) as fileread:
146                        with contextlib.closing(urlwrite.open("wb")) as filewrite:
147                            size = 0
148                            while True:
149                                data = fileread.read(262144)
150                                if data:
151                                    filewrite.write(data)
152                                    size += len(data)
153                                else:
154                                    break
155                    if user or group:
156                        urlwrite.chown(user, group)
[4387]157                except Exception, exc:
[4381]158                    if args.ignoreerrors:
159                        if args.verbose:
[4389]160                            excname = exc.__class__.__name__
161                            excmodule = exc.__class__.__module__
162                            if excmodule != "exceptions":
163                                excname = "{}.{}".format(excmodule, excname)
164                            excmsg = str(exc).replace("\n", " ").strip()
165                            msg = astyle.style_error(" (failed with {}: {})".format(excname, excmsg))
[4381]166                            stderr.writeln(msg)
167                    else:
168                        raise
169                else:
[4113]170                    if args.verbose:
[4381]171                        msg = astyle.style_default(astyle.style_url(str(urlwrite)), " (", str(size), " bytes)")
[3732]172                        stderr.writeln(msg)
173            else:
[4113]174                if args.verbose:
[4381]175                    msg = astyle.style_default("ucp: ", astyle.style_url(strurlread), astyle.style_warn(" (skipped)"))
[3732]176                    stderr.writeln(msg)
[4042]177
[4113]178    p = argparse.ArgumentParser(description="Copies URLs")
179    p.add_argument("urls", metavar="url", help="either one source and one target file, or multiple source files and one target dir", nargs="*", type=url.URL)
[4299]180    p.add_argument("-v", "--verbose", dest="verbose", help="Be verbose? (default: %(default)s)", action=misc.FlagAction, default=False)
[4283]181    p.add_argument("-c", "--color", dest="color", help="Color output (default: %(default)s)", default="auto", choices=("yes", "no", "auto"))
[4113]182    p.add_argument("-u", "--user", dest="user", help="user id or name for target files")
183    p.add_argument("-g", "--group", dest="group", help="group id or name for target files")
[4299]184    p.add_argument("-r", "--recursive", dest="recursive", help="Copy stuff recursively? (default: %(default)s)", action=misc.FlagAction, default=False)
185    p.add_argument("-x", "--ignoreerrors", dest="ignoreerrors", help="Ignore errors? (default: %(default)s)", action=misc.FlagAction, default=False)
[4381]186    p.add_argument("-i", "--include", dest="include", metavar="PATTERN", help="Include only URLs matching PATTERN (default: %(default)s)", type=re.compile)
187    p.add_argument("-e", "--exclude", dest="exclude", metavar="PATTERN", help="Exclude URLs matching PATTERN (default: %(default)s)", type=re.compile)
[4402]188    p.add_argument("-a", "--all", dest="all", help="Include dot files? (default: %(default)s)", action=misc.FlagAction, default=False)
[4042]189
[4113]190    args = p.parse_args(args)
191    if len(args.urls) < 2:
[3189]192        p.error("need at least one source url and one target url")
193        return 1
194
[4113]195    if args.color == "yes":
[3189]196        color = True
[4113]197    elif args.color == "no":
[3189]198        color = False
199    else:
200        color = None
201    stdout = astyle.Stream(sys.stdout, color)
202    stderr = astyle.Stream(sys.stderr, color)
203
[4113]204    user = args.user
[3189]205    try:
206        user = int(user)
207    except (TypeError, ValueError):
208        pass
209
[4113]210    group = args.group
[3189]211    try:
212        group = int(group)
213    except (TypeError, ValueError):
214        pass
215
216    with url.Context():
[4113]217        urls = args.urls
218        if len(urls) > 2 or urls[-1].isdir(): # treat target as directory
219            for u in urls[:-1]:
220                copyone(u, urls[-1]/u.file)
[3189]221        else:
[4113]222            copyone(urls[0], urls[-1])
[3189]223
224
225if __name__ == "__main__":
226    sys.exit(main())
Note: See TracBrowser for help on using the browser.