Ejemplo n.º 1
0
    def test_download_fs_fs(self):
        # Download files from local to remote (which is empty)
        local = FsTarget(os.path.join(PYFTPSYNC_TEST_FOLDER, "remote"))
        remote = FsTarget(os.path.join(PYFTPSYNC_TEST_FOLDER, "local"))
        opts = {"force": False, "delete": False, "dry_run": False}
        s = DownloadSynchronizer(local, remote, opts)
        s.run()
        stats = s.get_stats()
#        pprint(stats)
        self.assertEqual(stats["local_dirs"], 0)
        self.assertEqual(stats["local_files"], 0)
        self.assertEqual(stats["remote_dirs"], 2)
        self.assertEqual(stats["remote_files"], 4) # currently files are not counted, when inside a *new* folder
        self.assertEqual(stats["files_written"], 6)
        self.assertEqual(stats["dirs_created"], 2)
        self.assertEqual(stats["bytes_written"], 16403)
        # Again: nothing to do
        s = DownloadSynchronizer(local, remote, opts)
        s.run()
        stats = s.get_stats()
#        pprint(stats)
        self.assertEqual(stats["local_dirs"], 2)
        self.assertEqual(stats["local_files"], 6)
        self.assertEqual(stats["remote_dirs"], 2)
        self.assertEqual(stats["remote_files"], 6)
        self.assertEqual(stats["files_written"], 0)
        self.assertEqual(stats["dirs_created"], 0)
        self.assertEqual(stats["bytes_written"], 0)
        # file times are preserved
        self.assertEqual(_get_test_file_date("local/file1.txt"), STAMP_20140101_120000)
        self.assertEqual(_get_test_file_date("remote/file1.txt"), STAMP_20140101_120000)
Ejemplo n.º 2
0
    def test_issue_24(self):
        if not self.use_ftp_target:
            raise SkipTest("Only FTP targets.")

#         empty_folder()
        empty_folder(os.path.join(PYFTPSYNC_TEST_FOLDER, "local"))
        empty_folder(os.path.join(PYFTPSYNC_TEST_FOLDER, "remote"))

        local_target = make_target(self.local_url)
        remote_target = make_target(self.remote_url)

        SIZE = 1000 * 1000
        write_test_file("local/large1.txt", size=SIZE)
        write_test_file("remote/large2.txt", size=SIZE)

        opts = {
            "verbose": 5,
            "match": "large*.txt",
        }
        synchronizer = DownloadSynchronizer(local_target, remote_target, opts)

        assert is_test_file("local/large1.txt")
        assert is_test_file("remote/large2.txt")
        assert not is_test_file("remote/large1.txt")
        assert not is_test_file("local/large2.txt")

        synchronizer.run()

        # Expect that large2 was downloaded
        assert not is_test_file("remote/large1.txt")
        assert get_test_file_size("local/large2.txt") == SIZE
Ejemplo n.º 3
0
    def sync_from_server(self):
        """Sync today's readings from FTP server

        Note: this recreates the download/upload synchronisers each time to
        avoid issues with flags set in these classes (bug in pyftpsync; see
        https://github.com/mar10/pyftpsync/issues/20)
        """

        # current date and time
        now = datetime.datetime.utcnow()

        # FTP download options
        download_opts = {"force": True,
                         "resolve": "remote",
                         "verbose": 3,
                         # match today's filename (wildcard required)
                         "match": "*" + self.filename_from_date(now)}

        # create downloader
        ftp_downloader = DownloadSynchronizer(self.local_target,
                                              self.remote_target,
                                              download_opts)

        # download
        ftp_downloader.run()
Ejemplo n.º 4
0
    def test_issue_24(self):
        if not self.use_ftp_target:
            raise SkipTest("Only FTP targets.")

        #         empty_folder()
        empty_folder(os.path.join(PYFTPSYNC_TEST_FOLDER, "local"))
        empty_folder(os.path.join(PYFTPSYNC_TEST_FOLDER, "remote"))

        local_target = make_target(self.local_url)
        remote_target = make_target(self.remote_url)

        SIZE = 1000 * 1000
        write_test_file("local/large1.txt", size=SIZE)
        write_test_file("remote/large2.txt", size=SIZE)

        opts = {"verbose": 5, "match": "large*.txt"}
        synchronizer = DownloadSynchronizer(local_target, remote_target, opts)

        assert is_test_file("local/large1.txt")
        assert is_test_file("remote/large2.txt")
        assert not is_test_file("remote/large1.txt")
        assert not is_test_file("local/large2.txt")

        synchronizer.run()

        # Expect that large2 was downloaded
        assert not is_test_file("remote/large1.txt")
        assert get_test_file_size("local/large2.txt") == SIZE
Ejemplo n.º 5
0
 def test_issue_5(self):
     """issue #5: Unable to navigate to working directory '' (Windows)"""
     if not on_windows:
         raise SkipTest("Windows only")
     local = targets.FsTarget("c:/temp")
     remote = FtpTarget("/", "www.wwwendt.de", None, self.username, self.password)
     opts = {"resolve": "remote", "verbose": 3, "dry_run": True}
     s = DownloadSynchronizer(local, remote, opts)
     s.run()
Ejemplo n.º 6
0
 def test_issue_5(self):
     """issue #5: Unable to navigate to working directory '' (Windows)"""
     if not on_windows:
         raise SkipTest("Windows only")
     local = targets.FsTarget("c:/temp")
     remote = FtpTarget("/", "www.wwwendt.de", None, self.username,
                        self.password)
     opts = {"resolve": "remote", "verbose": 3, "dry_run": True}
     s = DownloadSynchronizer(local, remote, opts)
     s.run()
Ejemplo n.º 7
0
 def test_download_fs_fs(self):
     # Download files from local to remote (which is empty)
     local = FsTarget(os.path.join(PYFTPSYNC_TEST_FOLDER, "remote"))
     remote = FsTarget(os.path.join(PYFTPSYNC_TEST_FOLDER, "local"))
     opts = {"force": False, "delete": False, "verbose": self.verbose}
     s = DownloadSynchronizer(local, remote, opts)
     s.run()
     stats = s.get_stats()
     #        pprint(stats)
     self.assertEqual(stats["local_dirs"], 0)
     self.assertEqual(stats["local_files"], 0)
     self.assertEqual(stats["remote_dirs"], 2)
     # currently files are not counted, when inside a *new* folder:
     self.assertEqual(stats["remote_files"], 4)
     self.assertEqual(stats["files_written"], 6)
     self.assertEqual(stats["dirs_created"], 2)
     self.assertEqual(stats["bytes_written"], 16403)
     # Again: nothing to do
     s = DownloadSynchronizer(local, remote, opts)
     s.run()
     stats = s.get_stats()
     #        pprint(stats)
     self.assertEqual(stats["local_dirs"], 2)
     self.assertEqual(stats["local_files"], 6)
     self.assertEqual(stats["remote_dirs"], 2)
     self.assertEqual(stats["remote_files"], 6)
     self.assertEqual(stats["files_written"], 0)
     self.assertEqual(stats["dirs_created"], 0)
     self.assertEqual(stats["bytes_written"], 0)
     # file times are preserved
     self.assertEqual(get_test_file_date("local/file1.txt"), STAMP_20140101_120000)
     self.assertEqual(get_test_file_date("remote/file1.txt"), STAMP_20140101_120000)
Ejemplo n.º 8
0
    def test_issue_20(self):

        opts = {"verbose": 5}

        local_target = make_target(self.local_url)
        remote_target = make_target(self.remote_url)

        ftp_downloader = DownloadSynchronizer(local_target, remote_target, opts)
        ftp_uploader = UploadSynchronizer(local_target, remote_target, opts)

        write_test_file("local/large1.txt", size=10 * 1000)
        write_test_file("remote/large2.txt", size=10 * 1000)
        ftp_downloader.run()
        ftp_uploader.run()
Ejemplo n.º 9
0
def download_prolog():

    localpath = path_config.local_ftp_path  # 本地保存的ftp目录
    os.makedirs(localpath, 0o777)  # 创建本地日志文件夹

    local = FsTarget(localpath)
    remote = FtpTarget(path_config.ftp_remotepath,
                       path_config.ftp_server,
                       username=path_config.ftp_username,
                       password=path_config.ftp_password)
    opts = {"resolve": "skip", "verbose": 1}

    try:
        s = DownloadSynchronizer(local, remote, opts)  # 下载ftp日志文件夹
        s.run()

    except SyntaxError:
        pass
Ejemplo n.º 10
0
    def _transfer_files(self, count, size):
        temp1_path = os.path.join(PYFTPSYNC_TEST_FOLDER, "temp1")
        _empty_folder(temp1_path)  # remove standard test files

        local = FsTarget(temp1_path)
        remote = self.remote

        for i in range(count):
            _write_test_file("temp1/file_%s.txt" % i, size=size)

        # Upload all of /temp1 to remote

        opts = {
            "force": False,
            "delete": False,
            "verbose": 3,
            "dry_run": False
        }
        s = UploadSynchronizer(local, remote, opts)
        s.run()
        stats = s.get_stats()
        #        pprint(stats)

        self.assertEqual(stats["files_written"], count)
        self.assertEqual(stats["bytes_written"], count * size)
        #        pprint(stats)
        print("Upload %s x %s bytes took %s: %s" %
              (count, size, stats["upload_write_time"],
               stats["upload_rate_str"]),
              file=sys.stderr)

        # Download all of remote to /temp2

        local = FsTarget(os.path.join(PYFTPSYNC_TEST_FOLDER, "temp2"))

        opts = {"force": False, "delete": True, "verbose": 3, "dry_run": False}
        s = DownloadSynchronizer(local, remote, opts)
        s.run()
        stats = s.get_stats()
        #        pprint(stats)

        self.assertEqual(stats["files_written"], count)
        self.assertEqual(stats["bytes_written"], count * size)

        #        pprint(stats)
        print("Download %s x %s bytes took %s: %s" %
              (count, size, stats["download_write_time"],
               stats["download_rate_str"]),
              file=sys.stderr)
Ejemplo n.º 11
0
    def _transfer_files(self, count, size):
        temp1_path = os.path.join(PYFTPSYNC_TEST_FOLDER, "local")
        empty_folder(temp1_path)  # remove standard test files

        local = FsTarget(temp1_path)
        remote = self.remote

        for i in range(count):
            write_test_file("local/file_{}.txt".format(i), size=size)

        # Upload all of temp/local to remote

        opts = {"force": False, "delete": False, "verbose": 3}
        s = UploadSynchronizer(local, remote, opts)
        s.run()
        stats = s.get_stats()
        #        pprint(stats)

        self.assertEqual(stats["files_written"], count)
        self.assertEqual(stats["bytes_written"], count * size)
        #        pprint(stats)
        print(
            "Upload {} x {} bytes took {}: {}".format(
                count, size, stats["upload_write_time"],
                stats["upload_rate_str"]),
            file=sys.stderr,
        )

        # Download all of remote to temp/remote

        local = FsTarget(os.path.join(PYFTPSYNC_TEST_FOLDER, "remote"))

        opts = {"force": False, "delete": True, "verbose": 3}
        s = DownloadSynchronizer(local, remote, opts)
        s.run()
        stats = s.get_stats()
        #        pprint(stats)

        self.assertEqual(stats["files_written"], count)
        self.assertEqual(stats["bytes_written"], count * size)

        #        pprint(stats)
        print(
            "Download {} x {} bytes took {}: {}".format(
                count, size, stats["download_write_time"],
                stats["download_rate_str"]),
            file=sys.stderr,
        )
Ejemplo n.º 12
0
    def test_sync_fs_ftp(self):
        local = FsTarget(os.path.join(PYFTPSYNC_TEST_FOLDER, "local"))
        remote = self.remote

        # Upload all of temp/local to remote

        opts = {"force": False, "delete": True, "verbose": 3}
        s = UploadSynchronizer(local, remote, opts)
        s.run()

        stats = s.get_stats()
        #         pprint(stats)

        self.assertEqual(stats["local_dirs"], 2)
        # currently files are not counted, when inside a *new* folder:
        self.assertEqual(stats["local_files"], 4)
        self.assertEqual(stats["remote_dirs"], 0)
        self.assertEqual(stats["remote_files"], 0)
        self.assertEqual(stats["files_written"], 6)
        self.assertEqual(stats["dirs_created"], 2)
        self.assertEqual(stats["bytes_written"], 16403)

        # Change one file and upload again

        touch_test_file("local/file1.txt")

        opts = {"force": False, "delete": True, "verbose": 3}
        s = UploadSynchronizer(local, remote, opts)
        s.run()
        stats = s.get_stats()
        #         pprint(stats)
        #         assert False

        self.assertEqual(stats["entries_seen"], 18)  # ???
        self.assertEqual(stats["entries_touched"], 1)
        self.assertEqual(stats["files_created"], 0)
        self.assertEqual(stats["files_deleted"], 0)
        self.assertEqual(stats["files_written"], 1)
        self.assertEqual(stats["dirs_created"], 0)
        self.assertEqual(stats["download_files_written"], 0)
        self.assertEqual(stats["upload_files_written"], 1)
        self.assertEqual(stats["conflict_files"], 0)
        self.assertEqual(stats["bytes_written"], 3)

        # Download all from remote to temp/remote

        local = FsTarget(os.path.join(PYFTPSYNC_TEST_FOLDER, "remote"))

        opts = {"force": False, "delete": True, "verbose": 3}
        s = DownloadSynchronizer(local, remote, opts)
        s.run()
        stats = s.get_stats()
        #        pprint(stats)

        self.assertEqual(stats["entries_seen"], 8)
        self.assertEqual(stats["entries_touched"], 8)
        #        self.assertEqual(stats["files_created"], 6)
        self.assertEqual(stats["files_deleted"], 0)
        self.assertEqual(stats["files_written"], 6)
        self.assertEqual(stats["dirs_created"], 2)
        self.assertEqual(stats["download_files_written"], 6)
        self.assertEqual(stats["upload_files_written"], 0)
        self.assertEqual(stats["conflict_files"], 0)
        self.assertEqual(stats["bytes_written"], 16403)

        # Original file times are preserved, even when retrieved from FTP

        self.assertNotEqual(get_test_file_date("local/file1.txt"),
                            STAMP_20140101_120000)
        self.assertEqual(
            get_test_file_date("local/file1.txt"),
            get_test_file_date("local//file1.txt"),
        )

        self.assertEqual(get_test_file_date("local/file2.txt"),
                         STAMP_20140101_120000)
        self.assertEqual(get_test_file_date("remote//file2.txt"),
                         STAMP_20140101_120000)

        # Synchronize temp/local <=> remote : nothing to do

        local = FsTarget(os.path.join(PYFTPSYNC_TEST_FOLDER, "local"))

        opts = {"verbose": 3}
        s = BiDirSynchronizer(local, remote, opts)
        s.run()
        stats = s.get_stats()
        pprint(stats)
        self.assertEqual(stats["entries_touched"], 0)
        self.assertEqual(stats["conflict_files"], 0)
        self.assertEqual(stats["bytes_written"], 0)

        # Synchronize temp/remote <=> remote : nothing to do

        local = FsTarget(os.path.join(PYFTPSYNC_TEST_FOLDER, "remote"))

        opts = {"verbose": 3}
        s = BiDirSynchronizer(local, remote, opts)
        s.run()
        stats = s.get_stats()
        pprint(stats)
        self.assertEqual(stats["entries_touched"], 0)
        self.assertEqual(stats["conflict_files"], 0)
        self.assertEqual(stats["bytes_written"], 0)
Ejemplo n.º 13
0
def run():
    """CLI main entry point."""

    # Use print() instead of logging when running in CLI mode:
    set_pyftpsync_logger(None)

    parser = argparse.ArgumentParser(
        description="Synchronize folders over FTP.",
        epilog="See also https://github.com/mar10/pyftpsync",
        parents=[verbose_parser],
    )

    # Note: we want to allow --version to be combined with --verbose. However
    # on Py2, argparse makes sub-commands mandatory, unless `action="version"` is used.
    if check_cli_verbose(3) > 3:
        version_info = "pyftpsync/{} Python/{} {}".format(
            __version__, PYTHON_VERSION, platform.platform()
        )
    else:
        version_info = "{}".format(__version__)

    parser.add_argument("-V", "--version", action="version", version=version_info)

    subparsers = parser.add_subparsers(help="sub-command help")

    # --- Create the parser for the "upload" command ---------------------------

    sp = subparsers.add_parser(
        "upload",
        parents=[verbose_parser, common_parser, matcher_parser, creds_parser],
        help="copy new and modified files to remote folder",
    )

    sp.add_argument(
        "local",
        metavar="LOCAL",
        default=".",
        help="path to local folder (default: %(default)s)",
    )
    sp.add_argument("remote", metavar="REMOTE", help="path to remote folder")
    sp.add_argument(
        "--force",
        action="store_true",
        help="overwrite remote files, even if the target is newer "
        "(but no conflict was detected)",
    )
    sp.add_argument(
        "--resolve",
        default="ask",
        choices=["local", "skip", "ask"],
        help="conflict resolving strategy (default: '%(default)s')",
    )
    sp.add_argument(
        "--delete",
        action="store_true",
        help="remove remote files if they don't exist locally",
    )
    sp.add_argument(
        "--delete-unmatched",
        action="store_true",
        help="remove remote files if they don't exist locally "
        "or don't match the current filter (implies '--delete' option)",
    )

    sp.set_defaults(command="upload")

    # --- Create the parser for the "download" command -------------------------

    sp = subparsers.add_parser(
        "download",
        parents=[verbose_parser, common_parser, matcher_parser, creds_parser],
        help="copy new and modified files from remote folder to local target",
    )

    sp.add_argument(
        "local",
        metavar="LOCAL",
        default=".",
        help="path to local folder (default: %(default)s)",
    )
    sp.add_argument("remote", metavar="REMOTE", help="path to remote folder")
    sp.add_argument(
        "--force",
        action="store_true",
        help="overwrite local files, even if the target is newer "
        "(but no conflict was detected)",
    )
    sp.add_argument(
        "--resolve",
        default="ask",
        choices=["remote", "skip", "ask"],
        help="conflict resolving strategy (default: '%(default)s')",
    )
    sp.add_argument(
        "--delete",
        action="store_true",
        help="remove local files if they don't exist on remote target",
    )
    sp.add_argument(
        "--delete-unmatched",
        action="store_true",
        help="remove local files if they don't exist on remote target "
        "or don't match the current filter (implies '--delete' option)",
    )

    sp.set_defaults(command="download")

    # --- Create the parser for the "sync" command -----------------------------

    sp = subparsers.add_parser(
        "sync",
        parents=[verbose_parser, common_parser, matcher_parser, creds_parser],
        help="synchronize new and modified files between remote folder and local target",
    )

    sp.add_argument(
        "local",
        metavar="LOCAL",
        default=".",
        help="path to local folder (default: %(default)s)",
    )
    sp.add_argument("remote", metavar="REMOTE", help="path to remote folder")
    sp.add_argument(
        "--resolve",
        default="ask",
        choices=["old", "new", "local", "remote", "skip", "ask"],
        help="conflict resolving strategy (default: '%(default)s')",
    )

    sp.set_defaults(command="sync")

    # --- Create the parser for the "run" command -----------------------------

    add_run_parser(subparsers)

    # --- Create the parser for the "scan" command -----------------------------

    add_scan_parser(subparsers)

    # --- Create the parser for the "tree" command -----------------------------

    add_tree_parser(subparsers)

    # --- Parse command line ---------------------------------------------------

    args = parser.parse_args()

    args.verbose -= args.quiet
    del args.quiet

    # print("verbose", args.verbose)

    ftp_debug = 0
    if args.verbose >= 6:
        ftp_debug = 1

    if args.debug:
        if args.verbose < 4:
            parser.error("'--debug' requires verbose level >= 4")
        DEBUG_FLAGS.update(args.debug)

    # Modify the `args` from the `pyftpsync.yaml` config:
    if getattr(args, "command", None) == "run":
        handle_run_command(parser, args)

    if callable(getattr(args, "command", None)):
        # scan_handler
        try:
            return args.command(parser, args)
        except KeyboardInterrupt:
            print("\nAborted by user.", file=sys.stderr)
            sys.exit(3)

    elif not hasattr(args, "command"):
        parser.error(
            "missing command (choose from 'upload', 'download', 'run', 'sync', 'scan')"
        )

    # Post-process and check arguments
    if hasattr(args, "delete_unmatched") and args.delete_unmatched:
        args.delete = True

    args.local_target = make_target(args.local, {"ftp_debug": ftp_debug})

    if args.remote == ".":
        parser.error("'.' is expected to be the local target (not remote)")
    args.remote_target = make_target(args.remote, {"ftp_debug": ftp_debug})
    if not isinstance(args.local_target, FsTarget) and isinstance(
        args.remote_target, FsTarget
    ):
        parser.error("a file system target is expected to be local")

    # Let the command handler do its thing
    opts = namespace_to_dict(args)
    if args.command == "upload":
        s = UploadSynchronizer(args.local_target, args.remote_target, opts)
    elif args.command == "download":
        s = DownloadSynchronizer(args.local_target, args.remote_target, opts)
    elif args.command == "sync":
        s = BiDirSynchronizer(args.local_target, args.remote_target, opts)
    else:
        parser.error("unknown command '{}'".format(args.command))

    s.is_script = True

    try:
        s.run()
    except KeyboardInterrupt:
        print("\nAborted by user.", file=sys.stderr)
        sys.exit(3)
    finally:
        # Prevent sporadic exceptions in ftplib, when closing in __del__
        s.local.close()
        s.remote.close()

    stats = s.get_stats()
    if args.verbose >= 5:
        pprint(stats)
    elif args.verbose >= 1:
        if args.dry_run:
            print("(DRY-RUN) ", end="")
        print(
            "Wrote {}/{} files in {} directories, skipped: {}.".format(
                stats["files_written"],
                stats["local_files"],
                stats["local_dirs"],
                stats["conflict_files_skipped"],
            ),
            end="",
        )
        if stats["interactive_ask"]:
            print()
        else:
            print(" Elap: {}.".format(stats["elap_str"]))

    return
Ejemplo n.º 14
0
def run():
    """CLI main entry point."""

    # Use print() instead of logging when running in CLI mode:
    set_pyftpsync_logger(None)

    parser = argparse.ArgumentParser(
        description="Synchronize folders over FTP.",
        epilog="See also https://github.com/mar10/pyftpsync")

    parser.add_argument("-V",
                        "--version",
                        action="version",
                        version="{}".format(__version__))

    subparsers = parser.add_subparsers(help="sub-command help")

    # --- Create the parser for the "upload" command ---------------------------

    sp = subparsers.add_parser(
        "upload", help="copy new and modified files to remote folder")

    sp.add_argument("--force",
                    action="store_true",
                    help="overwrite remote files, even if the target is newer "
                    "(but no conflict was detected)")
    sp.add_argument(
        "--resolve",
        default="ask",
        choices=["local", "skip", "ask"],
        help="conflict resolving strategy (default: '%(default)s')")
    sp.add_argument("--delete",
                    action="store_true",
                    help="remove remote files if they don't exist locally")
    sp.add_argument(
        "--delete-unmatched",
        action="store_true",
        help="remove remote files if they don't exist locally "
        "or don't match the current filter (implies '--delete' option)")

    add_common_sub_args(sp)
    sp.set_defaults(command="upload")

    # --- Create the parser for the "download" command -------------------------

    sp = subparsers.add_parser(
        "download",
        help="copy new and modified files from remote folder to local target")

    sp.add_argument("--force",
                    action="store_true",
                    help="overwrite local files, even if the target is newer "
                    "(but no conflict was detected)")
    sp.add_argument(
        "--resolve",
        default="ask",
        choices=["remote", "skip", "ask"],
        help="conflict resolving strategy (default: '%(default)s')")
    sp.add_argument(
        "--delete",
        action="store_true",
        help="remove local files if they don't exist on remote target")
    sp.add_argument(
        "--delete-unmatched",
        action="store_true",
        help="remove local files if they don't exist on remote target "
        "or don't match the current filter (implies '--delete' option)")

    add_common_sub_args(sp)
    sp.set_defaults(command="download")

    # --- Create the parser for the "sync" command -----------------------------

    sp = subparsers.add_parser(
        "sync",
        help=
        "synchronize new and modified files between remote folder and local target"
    )

    sp.add_argument(
        "--resolve",
        default="ask",
        choices=["old", "new", "local", "remote", "skip", "ask"],
        help="conflict resolving strategy (default: '%(default)s')")

    add_common_sub_args(sp)
    sp.set_defaults(command="synchronize")

    # --- Create the parser for the "scan" command -----------------------------

    add_scan_parser(subparsers)

    # --- Parse command line ---------------------------------------------------

    args = parser.parse_args()

    args.verbose -= args.quiet
    del args.quiet

    ftp_debug = 0
    if args.verbose >= 6:
        ftp_debug = 1

    if callable(getattr(args, "command", None)):
        try:
            return getattr(args, "command")(args)
        except KeyboardInterrupt:
            print("\nAborted by user.", file=sys.stderr)
            sys.exit(3)

    elif not hasattr(args, "command"):
        parser.error(
            "missing command (choose from 'upload', 'download', 'sync', 'scan')"
        )

    # Post-process and check arguments
    if hasattr(args, "delete_unmatched") and args.delete_unmatched:
        args.delete = True

    args.local_target = make_target(args.local, {"ftp_debug": ftp_debug})

    if args.remote == ".":
        parser.error("'.' is expected to be the local target (not remote)")
    args.remote_target = make_target(args.remote, {"ftp_debug": ftp_debug})
    if not isinstance(args.local_target, FsTarget) and isinstance(
            args.remote_target, FsTarget):
        parser.error("a file system target is expected to be local")

    # Let the command handler do its thing
    opts = namespace_to_dict(args)
    if args.command == "upload":
        s = UploadSynchronizer(args.local_target, args.remote_target, opts)
    elif args.command == "download":
        s = DownloadSynchronizer(args.local_target, args.remote_target, opts)
    elif args.command == "synchronize":
        s = BiDirSynchronizer(args.local_target, args.remote_target, opts)
    else:
        parser.error("unknown command {}".format(args.command))

    s.is_script = True

    try:
        s.run()
    except KeyboardInterrupt:
        print("\nAborted by user.", file=sys.stderr)
        sys.exit(3)
    finally:
        # Prevent sporadic exceptions in ftplib, when closing in __del__
        s.local.close()
        s.remote.close()

    stats = s.get_stats()
    if args.verbose >= 5:
        pprint(stats)
    elif args.verbose >= 1:
        if args.dry_run:
            print("(DRY-RUN) ", end="")
        print("Wrote {}/{} files in {} directories, skipped: {}.".format(
            stats["files_written"], stats["local_files"], stats["local_dirs"],
            stats["conflict_files_skipped"]),
              end="")
        if stats["interactive_ask"]:
            print()
        else:
            print(" Elap: {}.".format(stats["elap_str"]))

    return
Ejemplo n.º 15
0
def run():
    parser = argparse.ArgumentParser(
        description="Synchronize folders over FTP.",
        epilog="See also https://github.com/mar10/pyftpsync")

    qv_group = parser.add_mutually_exclusive_group()
    qv_group.add_argument(
        "--verbose",
        "-v",
        action="count",
        default=3,
        help="increment verbosity by one (default: %(default)s, range: 0..5)")
    qv_group.add_argument("--quiet",
                          "-q",
                          action="count",
                          default=0,
                          help="decrement verbosity by one")

    parser.add_argument("-V",
                        "--version",
                        action="version",
                        version="%s" % __version__)
    parser.add_argument(
        "--progress",
        "-p",
        action="store_true",
        default=False,
        help="show progress info, even if redirected or verbose < 3")

    subparsers = parser.add_subparsers(help="sub-command help")

    def __add_common_sub_args(parser):
        parser.add_argument(
            "local",
            metavar="LOCAL",
            #                             required=True,
            default=".",
            help="path to local folder (default: %(default)s)")
        parser.add_argument("remote",
                            metavar="REMOTE",
                            help="path to remote folder")
        parser.add_argument(
            "--dry-run",
            action="store_true",
            help="just simulate and log results, but don't change anything")
        # parser.add_argument("-x", "--execute",
        #                     action="store_false", dest="dry_run", default=True,
        #                     help="turn off the dry-run mode (which is ON by default), "
        #                     "that would just print status messages but does "
        #                     "not change anything")
        parser.add_argument("-f",
                            "--include-files",
                            help="wildcard for file names (default: all, "
                            "separate multiple values with ',')")
        parser.add_argument(
            "-o",
            "--omit",
            help=
            "wildcard of files and directories to exclude (applied after --include)"
        )
        parser.add_argument("--store-password",
                            action="store_true",
                            help="save password to keyring if login succeeds")
        parser.add_argument("--no-prompt",
                            action="store_true",
                            help="prevent prompting for missing credentials")
        parser.add_argument("--no-color",
                            action="store_true",
                            help="prevent use of ansi terminal color codes")

    # --- Create the parser for the "upload" command ---------------------------

    upload_parser = subparsers.add_parser(
        "upload", help="copy new and modified files to remote folder")
    __add_common_sub_args(upload_parser)

    upload_parser.add_argument(
        "--force",
        action="store_true",
        help=
        "overwrite remote files, even if the target is newer (but no conflict was detected)"
    )
    upload_parser.add_argument(
        "--resolve",
        default="skip",
        choices=["local", "skip", "ask"],
        help="conflict resolving strategy (default: '%(default)s')")
    upload_parser.add_argument(
        "--delete",
        action="store_true",
        help="remove remote files if they don't exist locally")
    upload_parser.add_argument(
        "--delete-unmatched",
        action="store_true",
        help="remove remote files if they don't exist locally "
        "or don't match the current filter (implies '--delete' option)")

    upload_parser.set_defaults(command="upload")

    # --- Create the parser for the "download" command -------------------------

    download_parser = subparsers.add_parser(
        "download",
        help="copy new and modified files from remote folder to local target")
    __add_common_sub_args(download_parser)

    download_parser.add_argument(
        "--force",
        action="store_true",
        help=
        "overwrite local files, even if the target is newer (but no conflict was detected)"
    )
    download_parser.add_argument(
        "--resolve",
        default="skip",
        choices=["remote", "skip", "ask"],
        help="conflict resolving strategy (default: '%(default)s')")
    download_parser.add_argument(
        "--delete",
        action="store_true",
        help="remove local files if they don't exist on remote target")
    download_parser.add_argument(
        "--delete-unmatched",
        action="store_true",
        help="remove local files if they don't exist on remote target "
        "or don't match the current filter (implies '--delete' option)")

    download_parser.set_defaults(command="download")

    # --- Create the parser for the "sync" command -----------------------------

    sync_parser = subparsers.add_parser(
        "sync",
        help=
        "synchronize new and modified files between remote folder and local target"
    )
    __add_common_sub_args(sync_parser)

    sync_parser.add_argument(
        "--resolve",
        default="ask",
        choices=["old", "new", "local", "remote", "skip", "ask"],
        help="conflict resolving strategy (default: '%(default)s')")

    sync_parser.set_defaults(command="synchronize")

    # --- Create the parser for the "scan" command -----------------------------

    scan_parser = add_scan_parser(subparsers)

    # --- Parse command line ---------------------------------------------------

    args = parser.parse_args()

    args.verbose -= args.quiet
    del args.quiet

    ftp_debug = 0
    if args.verbose >= 5:
        ftp_debug = 1

    if callable(getattr(args, "command", None)):
        return getattr(args, "command")(args)
    elif not hasattr(args, "command"):
        parser.error(
            "missing command (choose from 'upload', 'download', 'sync')")

    # Post-process and check arguments
    if hasattr(args, "delete_unmatched") and args.delete_unmatched:
        args.delete = True

    args.local_target = make_target(args.local, {"ftp_debug": ftp_debug})

    if args.remote == ".":
        parser.error("'.' is expected to be the local target (not remote)")
    args.remote_target = make_target(args.remote, {"ftp_debug": ftp_debug})
    if not isinstance(args.local_target, FsTarget) and isinstance(
            args.remote_target, FsTarget):
        parser.error("a file system target is expected to be local")

    # Let the command handler do its thing
    opts = namespace_to_dict(args)
    if args.command == "upload":
        s = UploadSynchronizer(args.local_target, args.remote_target, opts)
    elif args.command == "download":
        s = DownloadSynchronizer(args.local_target, args.remote_target, opts)
    elif args.command == "synchronize":
        s = BiDirSynchronizer(args.local_target, args.remote_target, opts)
    else:
        parser.error("unknown command %s" % args.command)

    try:
        s.run()
    except KeyboardInterrupt:
        print("\nAborted by user.")
        return
    finally:
        # prevent sporadic exceptions in ftplib, when closing in __del__
        s.local.close()
        s.remote.close()

    stats = s.get_stats()
    if args.verbose >= 4:
        pprint(stats)
    elif args.verbose >= 1:
        if args.dry_run:
            print("(DRY-RUN) ", end="")
        print("Wrote %s/%s files in %s dirs. Elap: %s" %
              (stats["files_written"], stats["local_files"],
               stats["local_dirs"], stats["elap_str"]))

    return