|
57 | 57 | buckets_cli = typer_factory(help="Commands to interact with buckets.") |
58 | 58 |
|
59 | 59 |
|
| 60 | +def _is_hf_handle(path: str) -> bool: |
| 61 | + return path.startswith("hf://") |
| 62 | + |
| 63 | + |
60 | 64 | def _parse_bucket_argument(argument: str) -> tuple[str, str]: |
61 | 65 | """Parse a bucket argument accepting both 'namespace/name(/prefix)' and 'hf://buckets/namespace/name(/prefix)'. |
62 | 66 |
|
@@ -928,28 +932,44 @@ def sync( |
928 | 932 | "hf buckets cp my-config.json hf://buckets/user/my-bucket/logs/", |
929 | 933 | "hf buckets cp my-config.json hf://buckets/user/my-bucket/remote-config.json", |
930 | 934 | "hf buckets cp - hf://buckets/user/my-bucket/config.json", |
| 935 | + "hf buckets cp hf://buckets/user/my-bucket/logs/ hf://buckets/user/archive-bucket/logs/", |
| 936 | + "hf buckets cp hf://datasets/user/my-dataset/processed/ hf://buckets/user/my-bucket/dataset/processed/", |
931 | 937 | ], |
932 | 938 | ) |
933 | 939 | def cp( |
934 | | - src: Annotated[str, typer.Argument(help="Source: local file, hf://buckets/... path, or - for stdin")], |
| 940 | + src: Annotated[str, typer.Argument(help="Source: local file, HF handle (hf://...), or - for stdin")], |
935 | 941 | dst: Annotated[ |
936 | | - str | None, typer.Argument(help="Destination: local path, hf://buckets/... path, or - for stdout") |
| 942 | + str | None, typer.Argument(help="Destination: local path, HF handle (hf://...), or - for stdout") |
937 | 943 | ] = None, |
938 | 944 | quiet: QuietOpt = False, |
939 | 945 | token: TokenOpt = None, |
940 | 946 | ) -> None: |
941 | | - """Copy a single file to or from a bucket.""" |
| 947 | + """Copy files to or from buckets.""" |
942 | 948 | api = get_hf_api(token=token) |
943 | 949 |
|
| 950 | + src_is_hf = _is_hf_handle(src) |
| 951 | + dst_is_hf = dst is not None and _is_hf_handle(dst) |
944 | 952 | src_is_bucket = _is_bucket_path(src) |
945 | 953 | dst_is_bucket = dst is not None and _is_bucket_path(dst) |
946 | 954 | src_is_stdin = src == "-" |
947 | 955 | dst_is_stdout = dst == "-" |
948 | 956 |
|
949 | | - # --- Validation --- |
950 | | - if src_is_bucket and dst_is_bucket: |
951 | | - raise typer.BadParameter("Remote-to-remote copy not supported.") |
| 957 | + # Remote to remote copy |
| 958 | + if src_is_hf and dst_is_hf: |
| 959 | + if quiet: |
| 960 | + disable_progress_bars() |
| 961 | + try: |
| 962 | + api.copy_files(src, dst) # type: ignore |
| 963 | + finally: |
| 964 | + if quiet: |
| 965 | + enable_progress_bars() |
| 966 | + |
| 967 | + if not quiet: |
| 968 | + print(f"Copied: {src} -> {dst}") |
| 969 | + return |
952 | 970 |
|
| 971 | + # Local to remote copy |
| 972 | + # --- Validation --- |
953 | 973 | if not src_is_bucket and not dst_is_bucket and not src_is_stdin: |
954 | 974 | if dst is None: |
955 | 975 | raise typer.BadParameter("Missing destination. Provide a bucket path as DST.") |
|
0 commit comments