Beispiel #1
0
def cli_mkdir():
    """Make directory CLI."""
    parser = argparse.ArgumentParser()
    parser.add_argument("paths",
                        metavar="DIRECTORY",
                        nargs="+",
                        help="path of remote directory to create")
    parser.add_argument(
        "-p",
        "--parents",
        action="store_true",
        help="no error if existing, make parent directories as needed")
    args = parser.parse_args()

    onedrive.log.logging_setup()
    client = _init_client()

    returncode = 0
    for path in args.paths:
        try:
            if args.parents:
                metadata = client.makedirs(path, exist_ok=True)
            else:
                metadata = client.mkdir(path)
            cprogress("directory '%s' created at '%s'" %
                      (path, metadata["webUrl"]))
        except Exception as err:
            cerror("failed to create directory '%s': %s: %s" %
                   (path, type(err).__name__, str(err)))
            returncode = 1
    return returncode
Beispiel #2
0
def cli_rm():
    """Remove CLI."""
    parser = argparse.ArgumentParser()
    parser.add_argument("paths",
                        metavar="PATH",
                        nargs="+",
                        help="path of remote item to remove")
    parser.add_argument(
        "-r",
        "-R",
        "--recursive",
        action="store_true",
        help="remove directories and their contents recursively")
    args = parser.parse_args()

    onedrive.log.logging_setup()
    client = _init_client()

    returncode = 0
    for path in args.paths:
        try:
            client.rm(path, recursive=args.recursive)
            cprogress("'%s' removed from OneDrive" % path)
        except Exception as err:
            cerror("failed to remove '%s': %s: %s" %
                   (path, type(err).__name__, str(err)))
            returncode = 1
    return returncode
Beispiel #3
0
def cli_mkdir():
    """Make directory CLI."""
    parser = argparse.ArgumentParser()
    parser.add_argument("paths", metavar="DIRECTORY", nargs="+",
                        help="path of remote directory to create")
    parser.add_argument("-p", "--parents", action="store_true",
                        help="no error if existing, make parent directories as needed")
    args = parser.parse_args()

    onedrive.log.logging_setup()
    client = _init_client()

    returncode = 0
    for path in args.paths:
        try:
            if args.parents:
                metadata = client.makedirs(path, exist_ok=True)
            else:
                metadata = client.mkdir(path)
            cprogress("directory '%s' created at '%s'" %
                      (path, metadata["webUrl"]))
        except Exception as err:
            cerror("failed to create directory '%s': %s: %s" %
                   (path, type(err).__name__, str(err)))
            returncode = 1
    return returncode
Beispiel #4
0
    def __call__(self, args):
        """Download a remote file.

        ``args`` could either be a single path, which is interpreted as
        path to the remote file to download (to the current working
        directory), or a pair ``(remotepath, localdir)``, where localdir
        is interpreted as the destination directory.

        """
        if isinstance(args, tuple):
            remotepath, localdir = args
        else:
            remotepath = args
            localdir = None
        try:
            self._client.download(remotepath, destdir=localdir, **self._download_kwargs)
            cprogress("finished downloading '%s'" % remotepath)
            return 0
        except KeyboardInterrupt:
            cerror("download of '%s' interrupted" % remotepath)
            return 1
        except Exception as err:
            # catch any exception in a multiprocessing environment
            cerror("failed to download '%s': %s: %s" %
                   (remotepath, type(err).__name__, str(err)))
            return 1
Beispiel #5
0
    def __call__(self, args):
        """Download a remote file.

        ``args`` could either be a single path, which is interpreted as
        path to the remote file to download (to the current working
        directory), or a pair ``(remotepath, localdir)``, where localdir
        is interpreted as the destination directory.

        """
        if isinstance(args, tuple):
            remotepath, localdir = args
        else:
            remotepath = args
            localdir = None
        try:
            self._client.download(remotepath,
                                  destdir=localdir,
                                  **self._download_kwargs)
            cprogress("finished downloading '%s'" % remotepath)
            return 0
        except KeyboardInterrupt:
            cerror("download of '%s' interrupted" % remotepath)
            return 1
        except Exception as err:
            # catch any exception in a multiprocessing environment
            cerror("failed to download '%s': %s: %s" %
                   (remotepath, type(err).__name__, str(err)))
            return 1
Beispiel #6
0
def main():
    """CLI interface."""
    description = "Convert duration in seconds to human readable format."
    parser = argparse.ArgumentParser(description=description)
    parser.add_argument("-d", "--decimal-digits", metavar="NUM_DIGITS",
                        nargs="?", type=int, const=2, default=0,
                        help="""Print digits after the decimal point. By
                        default the duration is rounded to whole
                        seconds, but this option enables decimal
                        digits. If NUM_DIGITS argument is given, print
                        that many digits after the decimal point; if
                        this option is specified but no NUM_DIGITS is
                        given, print 2 digits after the decimal
                        point.""")
    parser.add_argument("-1", "--one-hour-digit", action="store_true",
                        help="""Only print one hour digit when the
                        duration is less than ten hours. By default the
                        hour is zero-padded to two digits.""")
    parser.add_argument("seconds", type=float,
                        help="Total number of seconds. Must be nonnegative.")
    args = parser.parse_args()
    try:
        print(humantime(args.seconds, ndigits=args.decimal_digits,
                        one_hour_digit=args.one_hour_digit))
    except ValueError as err:
        cerror(str(err))
        return 1
Beispiel #7
0
 def __call__(self, local_path):
     """Upload a local file."""
     try:
         self._client.upload(self._directory, local_path, **self._upload_kwargs)
         cprogress("finished uploading '%s'" % local_path)
         return 0
     except KeyboardInterrupt:
         cerror("upload of '%s' interrupted" % local_path)
         return 1
     except Exception as err:
         # catch any exception in a multiprocessing environment
         cerror("failed to upload '%s' to '%s': %s: %s" %
                (local_path, self._directory, type(err).__name__, str(err)))
         return 1
Beispiel #8
0
 def __call__(self, local_path):
     """Upload a local file."""
     try:
         self._client.upload(self._directory, local_path,
                             **self._upload_kwargs)
         cprogress("finished uploading '%s'" % local_path)
         return 0
     except KeyboardInterrupt:
         cerror("upload of '%s' interrupted" % local_path)
         return 1
     except Exception as err:
         # catch any exception in a multiprocessing environment
         cerror("failed to upload '%s' to '%s': %s: %s" %
                (local_path, self._directory, type(err).__name__, str(err)))
         return 1
Beispiel #9
0
def _init_client():
    """Init a client or exit with 1.

    Print a helpful error message and exit with code 1 if client
    initialization somehow fails due to problematic config file.

    Returns
    -------
    onedrive.api.OneDriveAPIClient

    """
    try:
        return onedrive.api.OneDriveAPIClient()
    except OSError as err:
        cerror(str(err))
        exit(1)
Beispiel #10
0
def _init_client():
    """Init a client or exit with 1.

    Print a helpful error message and exit with code 1 if client
    initialization somehow fails due to problematic config file.

    Returns
    -------
    onedrive.api.OneDriveAPIClient

    """
    try:
        return onedrive.api.OneDriveAPIClient()
    except OSError as err:
        cerror(str(err))
        exit(1)
Beispiel #11
0
def cli_geturl():
    """Get URL CLI."""
    parser = argparse.ArgumentParser()
    parser.add_argument("path", help="remote path (file or directory)")
    args = parser.parse_args()

    onedrive.log.logging_setup()
    client = _init_client()

    path = args.path
    try:
        print(client.geturl(path))
        return 0
    except onedrive.exceptions.FileNotFoundError:
        cerror("'%s' not found on OneDrive" % path)
        return 1
    except Exception as err:
        cerror("failed to get URL for '%s': %s: %s" %
               (path, type(err).__name__, str(err)))
        return 1
Beispiel #12
0
def cli_metadata():
    """Display metadata CLI."""
    parser = argparse.ArgumentParser(description="Dump JSON metadata of item.")
    parser.add_argument("path", help="remote path (file or directory)")
    args = parser.parse_args()

    onedrive.log.logging_setup()
    client = _init_client()

    path = args.path
    try:
        print(json.dumps(client.metadata(path), indent=4))
        return 0
    except onedrive.exceptions.FileNotFoundError:
        cerror("'%s' not found on OneDrive" % path)
        return 1
    except Exception as err:
        cerror("failed to get URL for '%s': %s: %s" %
               (path, type(err).__name__, str(err)))
        return 1
Beispiel #13
0
def cli_rmdir():
    """Remove empty directory CLI."""
    parser = argparse.ArgumentParser()
    parser.add_argument("paths", metavar="DIRECTORY", nargs="+",
                        help="path of remote directory to remove")
    args = parser.parse_args()

    onedrive.log.logging_setup()
    client = _init_client()

    returncode = 0
    for path in args.paths:
        try:
            client.rmdir(path)
            cprogress("directory '%s' removed from OneDrive" % path)
        except Exception as err:
            cerror("failed to remove '%s': %s: %s" %
                   (path, type(err).__name__, str(err)))
            returncode = 1
    return returncode
Beispiel #14
0
def cli_metadata():
    """Display metadata CLI."""
    parser = argparse.ArgumentParser(description="Dump JSON metadata of item.")
    parser.add_argument("path", help="remote path (file or directory)")
    args = parser.parse_args()

    onedrive.log.logging_setup()
    client = _init_client()

    path = args.path
    try:
        print(json.dumps(client.metadata(path), indent=4))
        return 0
    except onedrive.exceptions.FileNotFoundError:
        cerror("'%s' not found on OneDrive" % path)
        return 1
    except Exception as err:
        cerror("failed to get URL for '%s': %s: %s" %
               (path, type(err).__name__, str(err)))
        return 1
Beispiel #15
0
def cli_geturl():
    """Get URL CLI."""
    parser = argparse.ArgumentParser()
    parser.add_argument("path", help="remote path (file or directory)")
    args = parser.parse_args()

    onedrive.log.logging_setup()
    client = _init_client()

    path = args.path
    try:
        print(client.geturl(path))
        return 0
    except onedrive.exceptions.FileNotFoundError:
        cerror("'%s' not found on OneDrive" % path)
        return 1
    except Exception as err:
        cerror("failed to get URL for '%s': %s: %s" %
               (path, type(err).__name__, str(err)))
        return 1
Beispiel #16
0
    def run(self):
        """Run the copy operation and monitor status.

        The exit code is either 0 or 1, indicating success or failure.

        """
        try:
            if not self._recursive:
                self._client.assert_file(self.src)
            self._client.copy(self.src, self.dst,
                              overwrite=self._overwrite, show_progress=self._show_progress)
            cprogress("finished copying '%s' to '%s'" % (self.src, self.dst))
            return 0
        except KeyboardInterrupt:
            cerror("copying '%s' to '%s' interrupted" % (self.src, self.dst))
            return 1
        except Exception as err:
            # catch any exception in a multiprocessing environment
            cerror("failed to copy '%s' to '%s': %s: %s" %
                   (self.src, self.dst, type(err).__name__, str(err)))
            return 1
Beispiel #17
0
def cli_rm():
    """Remove CLI."""
    parser = argparse.ArgumentParser()
    parser.add_argument("paths", metavar="PATH", nargs="+",
                        help="path of remote item to remove")
    parser.add_argument("-r", "-R", "--recursive", action="store_true",
                        help="remove directories and their contents recursively")
    args = parser.parse_args()

    onedrive.log.logging_setup()
    client = _init_client()

    returncode = 0
    for path in args.paths:
        try:
            client.rm(path, recursive=args.recursive)
            cprogress("'%s' removed from OneDrive" % path)
        except Exception as err:
            cerror("failed to remove '%s': %s: %s" %
                   (path, type(err).__name__, str(err)))
            returncode = 1
    return returncode
Beispiel #18
0
def cli_rmdir():
    """Remove empty directory CLI."""
    parser = argparse.ArgumentParser()
    parser.add_argument("paths",
                        metavar="DIRECTORY",
                        nargs="+",
                        help="path of remote directory to remove")
    args = parser.parse_args()

    onedrive.log.logging_setup()
    client = _init_client()

    returncode = 0
    for path in args.paths:
        try:
            client.rmdir(path)
            cprogress("directory '%s' removed from OneDrive" % path)
        except Exception as err:
            cerror("failed to remove '%s': %s: %s" %
                   (path, type(err).__name__, str(err)))
            returncode = 1
    return returncode
Beispiel #19
0
    def run(self):
        """Run the copy operation and monitor status.

        The exit code is either 0 or 1, indicating success or failure.

        """
        try:
            if not self._recursive:
                self._client.assert_file(self.src)
            self._client.copy(self.src,
                              self.dst,
                              overwrite=self._overwrite,
                              show_progress=self._show_progress)
            cprogress("finished copying '%s' to '%s'" % (self.src, self.dst))
            return 0
        except KeyboardInterrupt:
            cerror("copying '%s' to '%s' interrupted" % (self.src, self.dst))
            return 1
        except Exception as err:
            # catch any exception in a multiprocessing environment
            cerror("failed to copy '%s' to '%s': %s: %s" %
                   (self.src, self.dst, type(err).__name__, str(err)))
            return 1
Beispiel #20
0
def cli_mv_or_cp(util, util_name=None):
    """Mimic the behavior of coreutils ``mv`` or ``cp``.

    Parameters
    ----------
    util : {"mv", "cp"}
    util_name : str, optional
        Utility name shown in usage and help text. If omitted, will be
        set to the value of ``util``.

    """
    usage = """
    {util_name} [options] [-T] SOURCE DEST
    {util_name} [options] SOURCE... DIRECTORY
    {util_name} [options] -t DIRECTORY SOURCE...""".format(util_name=util_name)
    parser = argparse.ArgumentParser(usage=usage)
    parser.add_argument("paths", nargs="+",
                        help="sources, destination or directory, depending on invocation")

    parser.add_argument("-t", "--target-directory", action="store_true",
                        help="copy all SOURCE arguments into DIRECTORY")
    parser.add_argument("-T", "--no-target-directory", action="store_true",
                        help="treat DEST as exact destination, not directory")
    parser.add_argument("-f", "--force", action="store_true",
                        help="overwrite existing destinations")
    if util == "cp":
        parser.add_argument("-R", "-r", "--recursive", action="store_true",
                            help="copy directories recursively")
    args = parser.parse_args()

    onedrive.log.logging_setup()
    client = None  # defer setup

    # list of (src, dst) pairs, where dst is the full destination
    src_dst_list = []

    if args.target_directory and args.no_target_directory:
        cfatal_error("conflicting options -t and -T; see %s -h" % util_name)
        return 1
    if len(args.paths) < 2:
        cfatal_error("at least two paths required; see %s -h" % util_name)
        return 1
    elif len(args.paths) == 2:
        # single source item
        if args.target_directory:
            # mv/cp -t DIRECTORY SOURCE
            directory, source = args.paths
            dest = posixpath.join(directory, posixpath.basename(source))
        elif args.no_target_directory:
            # mv/cp -T SOURCE DEST
            source, dest = args.paths
        else:
            # no -t or -T flag
            # automatically decide based on whether dest is an existing directory
            client = _init_client()
            if client.isdir(args.paths[1]):
                # mv/cp SOURCE DIRECTORY
                source, directory = args.paths
                dest = posixpath.join(directory, posixpath.basename(source))
            else:
                # mv/cp SOURCE DEST
                source, dest = args.paths
        src_dst_list.append((source, dest))
    else:
        # multiple source items
        if args.no_target_directory:
            cerror("option -T cannot be specified when there are multiple source items")
            return 1
        elif args.target_directory:
            # mv/cp -t DIRECTORY SOURCE...
            sources = args.paths[1:]
            directory = args.paths[0]
        else:
            # mv/cp SOURCE... DIRECTORY
            sources = args.paths[:-1]
            directory = args.paths[-1]

        src_dst_list = [(source, posixpath.join(directory, posixpath.basename(source)))
                        for source in sources]

    if client is None:
        client = _init_client()

    # 3, 2, 1, action!
    returncode = 0
    if util == "mv":
        # move each item synchronously
        for src_dst_pair in src_dst_list:
            src, dst = src_dst_pair
            try:
                client.move(src, dst, overwrite=args.force)
                cprogress("moved '%s' to '%s'" % (src, dst))
            except Exception as err:
                cerror("failed to move '%s' to '%s': %s: %s" %
                       (src, dst, type(err).__name__, str(err)))
                returncode = 1
    else:
        # cp is more involved
        num_items = len(src_dst_list)
        show_progress = (num_items == 1) and zmwangx.pbar.autopbar()
        workers = [CopyWorker(client, src, dst, recursive=args.recursive, overwrite=args.force,
                              show_progress=show_progress)
                   for src, dst in src_dst_list]
        try:
            for worker in workers:
                worker.start()
            for worker in workers:
                worker.join()
                if worker.exitcode != 0:
                    returncode = 1
        except KeyboardInterrupt:
            returncode = 1

    return returncode
Beispiel #21
0
def cli_dirupload():
    """Directory upload CLI."""
    # TODO: how to handle uploading to an existing and non-empty directory tree?
    # TODO: concurrency
    # TODO: option to skip creating directories (useful for resuming dirupload)
    parser = argparse.ArgumentParser()
    parser.add_argument("remotedir",
                        help="remote *parent* directory to upload to")
    parser.add_argument("localdir",
                        help="path to the local directory to upload")
    parser.add_argument("-n",
                        "--name",
                        help="""name of the remote directory (by default
                        it is just the basename of the local
                        directory)""")
    args = parser.parse_args()

    localroot = os.path.abspath(args.localdir)
    remoteparent = args.remotedir
    remotename = args.name if args.name is not None else os.path.basename(
        localroot)
    remoteroot = posixpath.join(remoteparent, remotename)

    onedrive.log.logging_setup()
    client = _init_client()

    if not os.path.isdir(localroot):
        cfatal_error("'%s' is not an existing local directory" % localroot)
        return 1

    if not client.isdir(remoteparent):
        cfatal_error("'%s' is not an existing remote directory" % remoteparent)
        return 1

    try:  # KeyboardInterrupt guard block
        show_progress = zmwangx.pbar.autopbar()
        cprogress("creating directories...")
        # uploads is a list of tuples (remotedir, localfile, filesize) to upload
        # TODO: default exclusions (e.g., .DS_Store) and user-specified exclusions
        # TODO: save calls by creating leaves only (topdown false, and use a
        # set to keep track of already created relpaths, and once a leaf is
        # created, add to the set itself and all its parents)
        uploads = []
        for localdir, _, files in os.walk(localroot):
            normalized_relpath = onedrive.util.normalized_posixpath(
                os.path.relpath(localdir, start=localroot))
            remotedir = posixpath.normpath(
                posixpath.join(remoteroot, normalized_relpath))
            client.makedirs(remotedir, exist_ok=True)  # TODO: exist_ok?
            print(remotedir, file=sys.stderr)

            for filename in files:
                localfile = os.path.join(localdir, filename)
                uploads.append(
                    (remotedir, localfile, os.path.getsize(localfile)))

        # upload files in ascending order of filesize
        uploads = sorted(uploads, key=lambda upload: upload[2])
        returncode = 0
        total = remaining = len(uploads)
        total_bytes = remaining_bytes = sum([upload[2] for upload in uploads])
        cprogress("uploading %d files..." % total)

        for upload in uploads:
            remotedir, localfile, filesize = upload
            cprogress("remaining: %d/%d files, %s/%s" %
                      (remaining, total,
                       zmwangx.humansize.humansize(
                           remaining_bytes, prefix="iec", unit=""),
                       zmwangx.humansize.humansize(
                           total_bytes, prefix="iec", unit="")))
            try:
                client.upload(remotedir,
                              localfile,
                              show_progress=show_progress)
                cprogress("finished uploading '%s'" % localfile)
            except Exception as err:
                cerror("failed to upload '%s' to '%s': %s: %s" %
                       (localfile, remotedir, type(err).__name__, str(err)))
                returncode = 1

            remaining -= 1
            remaining_bytes -= filesize

        return returncode
    except KeyboardInterrupt:
        cerror("interrupted" % localfile)
        return 1
Beispiel #22
0
def cli_mv_or_cp(util, util_name=None):
    """Mimic the behavior of coreutils ``mv`` or ``cp``.

    Parameters
    ----------
    util : {"mv", "cp"}
    util_name : str, optional
        Utility name shown in usage and help text. If omitted, will be
        set to the value of ``util``.

    """
    usage = """
    {util_name} [options] [-T] SOURCE DEST
    {util_name} [options] SOURCE... DIRECTORY
    {util_name} [options] -t DIRECTORY SOURCE...""".format(util_name=util_name)
    parser = argparse.ArgumentParser(usage=usage)
    parser.add_argument(
        "paths",
        nargs="+",
        help="sources, destination or directory, depending on invocation")

    parser.add_argument("-t",
                        "--target-directory",
                        action="store_true",
                        help="copy all SOURCE arguments into DIRECTORY")
    parser.add_argument("-T",
                        "--no-target-directory",
                        action="store_true",
                        help="treat DEST as exact destination, not directory")
    parser.add_argument("-f",
                        "--force",
                        action="store_true",
                        help="overwrite existing destinations")
    if util == "cp":
        parser.add_argument("-R",
                            "-r",
                            "--recursive",
                            action="store_true",
                            help="copy directories recursively")
    args = parser.parse_args()

    onedrive.log.logging_setup()
    client = None  # defer setup

    # list of (src, dst) pairs, where dst is the full destination
    src_dst_list = []

    if args.target_directory and args.no_target_directory:
        cfatal_error("conflicting options -t and -T; see %s -h" % util_name)
        return 1
    if len(args.paths) < 2:
        cfatal_error("at least two paths required; see %s -h" % util_name)
        return 1
    elif len(args.paths) == 2:
        # single source item
        if args.target_directory:
            # mv/cp -t DIRECTORY SOURCE
            directory, source = args.paths
            dest = posixpath.join(directory, posixpath.basename(source))
        elif args.no_target_directory:
            # mv/cp -T SOURCE DEST
            source, dest = args.paths
        else:
            # no -t or -T flag
            # automatically decide based on whether dest is an existing directory
            client = _init_client()
            if client.isdir(args.paths[1]):
                # mv/cp SOURCE DIRECTORY
                source, directory = args.paths
                dest = posixpath.join(directory, posixpath.basename(source))
            else:
                # mv/cp SOURCE DEST
                source, dest = args.paths
        src_dst_list.append((source, dest))
    else:
        # multiple source items
        if args.no_target_directory:
            cerror(
                "option -T cannot be specified when there are multiple source items"
            )
            return 1
        elif args.target_directory:
            # mv/cp -t DIRECTORY SOURCE...
            sources = args.paths[1:]
            directory = args.paths[0]
        else:
            # mv/cp SOURCE... DIRECTORY
            sources = args.paths[:-1]
            directory = args.paths[-1]

        src_dst_list = [(source,
                         posixpath.join(directory, posixpath.basename(source)))
                        for source in sources]

    if client is None:
        client = _init_client()

    # 3, 2, 1, action!
    returncode = 0
    if util == "mv":
        # move each item synchronously
        for src_dst_pair in src_dst_list:
            src, dst = src_dst_pair
            try:
                client.move(src, dst, overwrite=args.force)
                cprogress("moved '%s' to '%s'" % (src, dst))
            except Exception as err:
                cerror("failed to move '%s' to '%s': %s: %s" %
                       (src, dst, type(err).__name__, str(err)))
                returncode = 1
    else:
        # cp is more involved
        num_items = len(src_dst_list)
        show_progress = (num_items == 1) and zmwangx.pbar.autopbar()
        workers = [
            CopyWorker(client,
                       src,
                       dst,
                       recursive=args.recursive,
                       overwrite=args.force,
                       show_progress=show_progress)
            for src, dst in src_dst_list
        ]
        try:
            for worker in workers:
                worker.start()
            for worker in workers:
                worker.join()
                if worker.exitcode != 0:
                    returncode = 1
        except KeyboardInterrupt:
            returncode = 1

    return returncode
Beispiel #23
0
def cli_dirdownload():
    """Directory download CLI."""
    parser = argparse.ArgumentParser()
    parser.add_argument("remotedir", help="remote directory to download")
    parser.add_argument("localdir", help="path to the local *parent* directory to download to")
    parser.add_argument("-j", "--jobs", type=int, default=8,
                        help="number of concurrect downloads, use 0 for unlimited; default is 8")
    parser.add_argument("--no-check", action="store_true",
                        help="do not compare checksum of remote and local files")
    parser.add_argument("-f", "--fresh", action="store_true",
                        help="discard any previous failed download")
    parser.add_argument("--curl", dest="downloader", action="store_const", const="curl",
                        help="use curl to download")
    parser.add_argument("--wget", dest="downloader", action="store_const", const="wget",
                        help="use wget to download")
    parser.add_argument("-n", "--name",
                        help="""name of the local directory to create
                        (by default it is just the basename of the
                        remote directory)""")
    args = parser.parse_args()

    remoteroot = args.remotedir
    localparent = os.path.abspath(args.localdir)
    localname = args.name if args.name is not None else os.path.basename(remoteroot)
    localroot = os.path.join(localparent, localname)

    onedrive.log.logging_setup()
    client = _init_client()

    if not os.path.isdir(localparent):
        cfatal_error("'%s' is not an existing local directory" % localparent)
        return 1

    if not client.isdir(remoteroot):
        cfatal_error("'%s' is not an existing remote directory" % remoteroot)
        return 1

    try:  # KeyboardInterrupt guard block
        show_progress = zmwangx.pbar.autopbar()
        cprogress("creating local directories...")
        # downloads is a list of pairs (remotefile, localdir) to download
        downloads = []
        for remotedir, _, files in client.walk(remoteroot, paths_only=True):
            normalized_relpath = onedrive.util.normalized_ospath(
                posixpath.relpath(remotedir, start=remoteroot))
            localdir = os.path.normpath(os.path.join(localroot, normalized_relpath))
            os.makedirs(localdir, exist_ok=True)
            print(localdir, file=sys.stderr)

            for filename in files:
                remotefile = posixpath.join(remotedir, filename)
                downloads.append((remotefile, localdir))

        num_files = len(downloads)
        jobs = min(args.jobs, num_files) if args.jobs > 0 else num_files
        show_progress = (num_files == 1) and zmwangx.pbar.autopbar()

        download_kwargs = {
            "compare_hash": not args.no_check,
            "show_progress": show_progress,
            "resume": not args.fresh,
            "downloader": args.downloader,
        }

        cprogress("downloading %d files..." % num_files)

        with multiprocessing.Pool(processes=jobs, maxtasksperchild=1) as pool:
            downloader = Downloader(client, download_kwargs=download_kwargs)
            returncodes = []
            try:
                returncodes = pool.map(downloader, downloads, chunksize=1)
            except KeyboardInterrupt:
                returncodes.append(1)
            return 1 if 1 in returncodes else 0

    except KeyboardInterrupt:
        cerror("interrupted" % localfile)
        return 1
Beispiel #24
0
def cli_dirdownload():
    """Directory download CLI."""
    parser = argparse.ArgumentParser()
    parser.add_argument("remotedir", help="remote directory to download")
    parser.add_argument(
        "localdir", help="path to the local *parent* directory to download to")
    parser.add_argument(
        "-j",
        "--jobs",
        type=int,
        default=8,
        help="number of concurrect downloads, use 0 for unlimited; default is 8"
    )
    parser.add_argument(
        "--no-check",
        action="store_true",
        help="do not compare checksum of remote and local files")
    parser.add_argument("-f",
                        "--fresh",
                        action="store_true",
                        help="discard any previous failed download")
    parser.add_argument("--curl",
                        dest="downloader",
                        action="store_const",
                        const="curl",
                        help="use curl to download")
    parser.add_argument("--wget",
                        dest="downloader",
                        action="store_const",
                        const="wget",
                        help="use wget to download")
    parser.add_argument("-n",
                        "--name",
                        help="""name of the local directory to create
                        (by default it is just the basename of the
                        remote directory)""")
    args = parser.parse_args()

    remoteroot = args.remotedir
    localparent = os.path.abspath(args.localdir)
    localname = args.name if args.name is not None else os.path.basename(
        remoteroot)
    localroot = os.path.join(localparent, localname)

    onedrive.log.logging_setup()
    client = _init_client()

    if not os.path.isdir(localparent):
        cfatal_error("'%s' is not an existing local directory" % localparent)
        return 1

    if not client.isdir(remoteroot):
        cfatal_error("'%s' is not an existing remote directory" % remoteroot)
        return 1

    try:  # KeyboardInterrupt guard block
        show_progress = zmwangx.pbar.autopbar()
        cprogress("creating local directories...")
        # downloads is a list of pairs (remotefile, localdir) to download
        downloads = []
        for remotedir, _, files in client.walk(remoteroot, paths_only=True):
            normalized_relpath = onedrive.util.normalized_ospath(
                posixpath.relpath(remotedir, start=remoteroot))
            localdir = os.path.normpath(
                os.path.join(localroot, normalized_relpath))
            os.makedirs(localdir, exist_ok=True)
            print(localdir, file=sys.stderr)

            for filename in files:
                remotefile = posixpath.join(remotedir, filename)
                downloads.append((remotefile, localdir))

        num_files = len(downloads)
        jobs = min(args.jobs, num_files) if args.jobs > 0 else num_files
        show_progress = (num_files == 1) and zmwangx.pbar.autopbar()

        download_kwargs = {
            "compare_hash": not args.no_check,
            "show_progress": show_progress,
            "resume": not args.fresh,
            "downloader": args.downloader,
        }

        cprogress("downloading %d files..." % num_files)

        with multiprocessing.Pool(processes=jobs, maxtasksperchild=1) as pool:
            downloader = Downloader(client, download_kwargs=download_kwargs)
            returncodes = []
            try:
                returncodes = pool.map(downloader, downloads, chunksize=1)
            except KeyboardInterrupt:
                returncodes.append(1)
            return 1 if 1 in returncodes else 0

    except KeyboardInterrupt:
        cerror("interrupted" % localfile)
        return 1
Beispiel #25
0
def cli_ls():
    """List items CLI."""
    description = """\
    ls for OneDrive.

    By default the long format is used, i.e., for each entry, the following
    fields are printed:

        type, childcount, size, indentation, name.

    * type is a single character, `d' for a directory and `-' for a file;
    * childcount is an integer for a directory and `-' for a file;
    * size is the total size of the item (the size of a directory is calculated
      recursively); this is by default a human-readable string, and can be
      switched to plain byte count using the +h, ++human flag;
    * indentation only appears in tree mode, where each level of depth is
      translated into a four-space indent;
    * name is the basename of the item (note this important difference from
      POSIX ls, which would show full arguments under some circumstances; this
      is considered a defect and might change in the future).

    Long format can be turned off using the +l, ++long format, in which case
    only indentations and names are printed.

    Note that you can turn on **tree mode** using the -t, --tree flag, in which
    case full directory trees are printed (on the fly).

    There is also the -d flag for toggling directory only mode, similar to -d
    of ls(1) or tree(1) (for tree mode).

    Please do not rely on the output format of ls to be stable, and especially
    do not parse it programatically, since there is no guarantee that it won't
    change in the future. Please use the API instead (children, listdir, walk,
    walkn, etc.; see the onedrive.api module).

    """
    parser = argparse.ArgumentParser(
        formatter_class=argparse.RawDescriptionHelpFormatter,
        description=textwrap.dedent(description),
        prefix_chars="-+")
    parser.add_argument("paths", metavar="PATH", nargs="+",
                        help="remote path(s)")
    parser.add_argument("-d", "--directory", action="store_true",
                        help="""list directories themselves, not their
                        contents; when used in conjuction with the --tree
                        option, omit files in a directory tree""")
    parser.add_argument("-t", "--tree", action="store_true",
                        help="display full directory trees")
    parser.add_argument("+h", "++human", action="store_false",
                        help="turn off human readable format")
    parser.add_argument("+l", "++long", action="store_false",
                        help="turn off long format")
    args = parser.parse_args()

    onedrive.log.logging_setup()
    client = _init_client()

    paths = args.paths
    dironly = args.directory
    tree = args.tree
    human = args.human
    long = args.long

    # wrap the whole thing into a try block, since ls might take a long
    # time in tree mode, and the program could be interrupted
    returncode = 0
    try:
        # categorize paths into files and dirs; files will come first
        # the files and dirs lists are lists of tuples (path, metadata)
        files = []
        dirs = []
        for path in paths:
            try:
                metadata = client.metadata(path)
                if "file" in metadata:
                    files.append((path, metadata))
                else:
                    dirs.append((path, metadata))
            except onedrive.exceptions.FileNotFoundError:
                cerror("'%s' not found on OneDrive" % path)
                returncode = 1

        # first list files, if any
        for _, filemetadata in files:
            _cli_ls_print_entry(filemetadata, long=long, human=human)

        if not dirs:
            return returncode

        # list directories

        # common kwargs
        kwargs = {"dironly": dironly, "tree": tree, "human": human, "long": long}

        # handle first directory specially due to special blank line annoyance
        firstdirpath, firstdirmetadata = dirs[0]
        if not dironly or tree:
            if files:
                print("")
            # do not print "dirname:" if we are only listing a single directory
            if len(files) != 0 or len(dirs) != 1:
                print("%s:" % firstdirpath)
        try:
            _cli_ls_single_directory(client, firstdirpath, metadata=firstdirmetadata, **kwargs)
        except Exception as err:
            cerror("failed to list '%s': %s: %s" % (firstdirpath, type(err).__name__, str(err)))

        for dirpath, dirmetadata in dirs[1:]:
            if not dironly or tree:
                print("")
                print("%s:" % dirpath)
            try:
                _cli_ls_single_directory(client, dirpath, metadata=dirmetadata, **kwargs)
            except Exception as err:
                cerror("failed to list '%s': %s: %s" % (dirpath, type(err).__name__, str(err)))

    except KeyboardInterrupt:
        cerror("interrupted")
        returncode = 1

    return returncode
Beispiel #26
0
def cli_rename():
    """Batch renaming CLI."""
    parser = argparse.ArgumentParser(
        description="Batch rename all items in a directory.")
    parser.add_argument("-F",
                        "--files-only",
                        action="store_true",
                        help="""only process files (by default direct
                        subdirectories are also processed)""")
    parser.add_argument(
        "-d",
        "--dry-run",
        action="store_true",
        help="""print what would be renamed to stderr but don't do them""")
    parser.add_argument("-s",
                        "--show",
                        action="store_true",
                        help="""print what was renamed to what""")
    parser.add_argument("stmts",
                        help="""Python statement(s) to be executed on
                        each filename; the statement(s) should modify
                        the '_' variable, which holds the filename; note
                        that the STL 're' module is already imported for
                        you, and the statement(s) can also make use of a
                        variable 'n', which is the one-based index of
                        the filename currently being handled (indexed in
                        alphabetical order)""")
    parser.add_argument("directory", help="path to remote directory")
    args = parser.parse_args()

    stmts = args.stmts
    try:
        ast.parse(stmts)
    except SyntaxError as err:
        cfatal_error("invalid statements '%s': %s" % (stmts, str(err)))
        return 1

    onedrive.log.logging_setup()
    client = _init_client()

    directory = args.directory
    try:
        client.assert_dir(directory)
    except Exception as err:
        cfatal_error("%s: %s" % (type(err).__name__, str(err)))
        return 1

    returncode = 0
    children = client.children(directory)
    index = 0
    for child in children:
        if args.files_only and "folder" in child:
            continue

        oldname = child["name"]
        index += 1
        globaldict = {"re": re}
        localdict = {"_": oldname, "n": index}
        try:
            # pylint: disable=exec-used
            exec(stmts, globaldict, localdict)
            newname = localdict["_"]
        except Exception as err:
            cerror("failed to generate new name for '%s': %s: %s" %
                   (oldname, type(err).__name__, str(err)))
            returncode = 1
            continue

        if oldname == newname:
            continue

        if args.dry_run or args.show:
            print("%s => %s" % (oldname, newname))

        if not args.dry_run:
            try:
                client.move(posixpath.join(directory, oldname),
                            posixpath.join(directory, newname))
            except Exception as err:
                cerror("failed to move '%s' for '%s': %s: %s" %
                       (oldname, newname, type(err).__name__, str(err)))
                returncode = 1

    return returncode
Beispiel #27
0
def cli_rename():
    """Batch renaming CLI."""
    parser = argparse.ArgumentParser(description="Batch rename all items in a directory.")
    parser.add_argument("-F", "--files-only", action="store_true",
                        help="""only process files (by default direct
                        subdirectories are also processed)""")
    parser.add_argument("-d", "--dry-run", action="store_true",
                        help="""print what would be renamed to stderr but don't do them""")
    parser.add_argument("-s", "--show", action="store_true",
                        help="""print what was renamed to what""")
    parser.add_argument("stmts",
                        help="""Python statement(s) to be executed on
                        each filename; the statement(s) should modify
                        the '_' variable, which holds the filename; note
                        that the STL 're' module is already imported for
                        you, and the statement(s) can also make use of a
                        variable 'n', which is the one-based index of
                        the filename currently being handled (indexed in
                        alphabetical order)""")
    parser.add_argument("directory", help="path to remote directory")
    args = parser.parse_args()

    stmts = args.stmts
    try:
        ast.parse(stmts)
    except SyntaxError as err:
        cfatal_error("invalid statements '%s': %s" % (stmts, str(err)))
        return 1

    onedrive.log.logging_setup()
    client = _init_client()

    directory = args.directory
    try:
        client.assert_dir(directory)
    except Exception as err:
        cfatal_error("%s: %s" % (type(err).__name__, str(err)))
        return 1

    returncode = 0
    children = client.children(directory)
    index = 0
    for child in children:
        if args.files_only and "folder" in child:
            continue

        oldname = child["name"]
        index += 1
        globaldict = {"re": re}
        localdict = {"_": oldname, "n": index}
        try:
            # pylint: disable=exec-used
            exec(stmts, globaldict, localdict)
            newname = localdict["_"]
        except Exception as err:
            cerror("failed to generate new name for '%s': %s: %s" %
                   (oldname, type(err).__name__, str(err)))
            returncode = 1
            continue

        if oldname == newname:
            continue

        if args.dry_run or args.show:
            print("%s => %s" % (oldname, newname))

        if not args.dry_run:
            try:
                client.move(posixpath.join(directory, oldname),
                            posixpath.join(directory, newname))
            except Exception as err:
                cerror("failed to move '%s' for '%s': %s: %s" %
                       (oldname, newname, type(err).__name__, str(err)))
                returncode = 1

    return returncode
Beispiel #28
0
def cli_dirupload():
    """Directory upload CLI."""
    # TODO: how to handle uploading to an existing and non-empty directory tree?
    # TODO: concurrency
    # TODO: option to skip creating directories (useful for resuming dirupload)
    parser = argparse.ArgumentParser()
    parser.add_argument("remotedir", help="remote *parent* directory to upload to")
    parser.add_argument("localdir", help="path to the local directory to upload")
    parser.add_argument("-n", "--name",
                        help="""name of the remote directory (by default
                        it is just the basename of the local
                        directory)""")
    args = parser.parse_args()

    localroot = os.path.abspath(args.localdir)
    remoteparent = args.remotedir
    remotename = args.name if args.name is not None else os.path.basename(localroot)
    remoteroot = posixpath.join(remoteparent, remotename)

    onedrive.log.logging_setup()
    client = _init_client()

    if not os.path.isdir(localroot):
        cfatal_error("'%s' is not an existing local directory" % localroot)
        return 1

    if not client.isdir(remoteparent):
        cfatal_error("'%s' is not an existing remote directory" % remoteparent)
        return 1

    try:  # KeyboardInterrupt guard block
        show_progress = zmwangx.pbar.autopbar()
        cprogress("creating directories...")
        # uploads is a list of tuples (remotedir, localfile, filesize) to upload
        # TODO: default exclusions (e.g., .DS_Store) and user-specified exclusions
        # TODO: save calls by creating leaves only (topdown false, and use a
        # set to keep track of already created relpaths, and once a leaf is
        # created, add to the set itself and all its parents)
        uploads = []
        for localdir, _, files in os.walk(localroot):
            normalized_relpath = onedrive.util.normalized_posixpath(
                os.path.relpath(localdir, start=localroot))
            remotedir = posixpath.normpath(posixpath.join(remoteroot, normalized_relpath))
            client.makedirs(remotedir, exist_ok=True)  # TODO: exist_ok?
            print(remotedir, file=sys.stderr)

            for filename in files:
                localfile = os.path.join(localdir, filename)
                uploads.append((remotedir, localfile, os.path.getsize(localfile)))

        # upload files in ascending order of filesize
        uploads = sorted(uploads, key=lambda upload: upload[2])
        returncode = 0
        total = remaining = len(uploads)
        total_bytes = remaining_bytes = sum([upload[2] for upload in uploads])
        cprogress("uploading %d files..." % total)

        for upload in uploads:
            remotedir, localfile, filesize = upload
            cprogress("remaining: %d/%d files, %s/%s" %
                      (remaining, total,
                       zmwangx.humansize.humansize(remaining_bytes, prefix="iec", unit=""),
                       zmwangx.humansize.humansize(total_bytes, prefix="iec", unit="")))
            try:
                client.upload(remotedir, localfile, show_progress=show_progress)
                cprogress("finished uploading '%s'" % localfile)
            except Exception as err:
                cerror("failed to upload '%s' to '%s': %s: %s" %
                       (localfile, remotedir, type(err).__name__, str(err)))
                returncode = 1

            remaining -= 1
            remaining_bytes -= filesize

        return returncode
    except KeyboardInterrupt:
        cerror("interrupted" % localfile)
        return 1
Beispiel #29
0
def cli_ls():
    """List items CLI."""
    description = """\
    ls for OneDrive.

    By default the long format is used, i.e., for each entry, the following
    fields are printed:

        type, childcount, size, indentation, name.

    * type is a single character, `d' for a directory and `-' for a file;
    * childcount is an integer for a directory and `-' for a file;
    * size is the total size of the item (the size of a directory is calculated
      recursively); this is by default a human-readable string, and can be
      switched to plain byte count using the +h, ++human flag;
    * indentation only appears in tree mode, where each level of depth is
      translated into a four-space indent;
    * name is the basename of the item (note this important difference from
      POSIX ls, which would show full arguments under some circumstances; this
      is considered a defect and might change in the future).

    Long format can be turned off using the +l, ++long format, in which case
    only indentations and names are printed.

    Note that you can turn on **tree mode** using the -t, --tree flag, in which
    case full directory trees are printed (on the fly).

    There is also the -d flag for toggling directory only mode, similar to -d
    of ls(1) or tree(1) (for tree mode).

    Please do not rely on the output format of ls to be stable, and especially
    do not parse it programatically, since there is no guarantee that it won't
    change in the future. Please use the API instead (children, listdir, walk,
    walkn, etc.; see the onedrive.api module).

    """
    parser = argparse.ArgumentParser(
        formatter_class=argparse.RawDescriptionHelpFormatter,
        description=textwrap.dedent(description),
        prefix_chars="-+")
    parser.add_argument("paths",
                        metavar="PATH",
                        nargs="+",
                        help="remote path(s)")
    parser.add_argument("-d",
                        "--directory",
                        action="store_true",
                        help="""list directories themselves, not their
                        contents; when used in conjuction with the --tree
                        option, omit files in a directory tree""")
    parser.add_argument("-t",
                        "--tree",
                        action="store_true",
                        help="display full directory trees")
    parser.add_argument("+h",
                        "++human",
                        action="store_false",
                        help="turn off human readable format")
    parser.add_argument("+l",
                        "++long",
                        action="store_false",
                        help="turn off long format")
    args = parser.parse_args()

    onedrive.log.logging_setup()
    client = _init_client()

    paths = args.paths
    dironly = args.directory
    tree = args.tree
    human = args.human
    long = args.long

    # wrap the whole thing into a try block, since ls might take a long
    # time in tree mode, and the program could be interrupted
    returncode = 0
    try:
        # categorize paths into files and dirs; files will come first
        # the files and dirs lists are lists of tuples (path, metadata)
        files = []
        dirs = []
        for path in paths:
            try:
                metadata = client.metadata(path)
                if "file" in metadata:
                    files.append((path, metadata))
                else:
                    dirs.append((path, metadata))
            except onedrive.exceptions.FileNotFoundError:
                cerror("'%s' not found on OneDrive" % path)
                returncode = 1

        # first list files, if any
        for _, filemetadata in files:
            _cli_ls_print_entry(filemetadata, long=long, human=human)

        if not dirs:
            return returncode

        # list directories

        # common kwargs
        kwargs = {
            "dironly": dironly,
            "tree": tree,
            "human": human,
            "long": long
        }

        # handle first directory specially due to special blank line annoyance
        firstdirpath, firstdirmetadata = dirs[0]
        if not dironly or tree:
            if files:
                print("")
            # do not print "dirname:" if we are only listing a single directory
            if len(files) != 0 or len(dirs) != 1:
                print("%s:" % firstdirpath)
        try:
            _cli_ls_single_directory(client,
                                     firstdirpath,
                                     metadata=firstdirmetadata,
                                     **kwargs)
        except Exception as err:
            cerror("failed to list '%s': %s: %s" %
                   (firstdirpath, type(err).__name__, str(err)))

        for dirpath, dirmetadata in dirs[1:]:
            if not dironly or tree:
                print("")
                print("%s:" % dirpath)
            try:
                _cli_ls_single_directory(client,
                                         dirpath,
                                         metadata=dirmetadata,
                                         **kwargs)
            except Exception as err:
                cerror("failed to list '%s': %s: %s" %
                       (dirpath, type(err).__name__, str(err)))

    except KeyboardInterrupt:
        cerror("interrupted")
        returncode = 1

    return returncode