|
22 | 22 | import concurrent.futures |
23 | 23 |
|
24 | 24 |
|
25 | | -from .common import CHUNK_SIZE, ClientError, DeltaChangeType, InvalidProject, PullActionType |
| 25 | +from .common import CHUNK_SIZE, ClientError, DeltaChangeType, PullActionType |
26 | 26 | from .models import ProjectDelta, ProjectDeltaChange, PullAction |
27 | 27 | from .merginproject import MerginProject |
28 | 28 | from .utils import cleanup_tmp_dir, is_versioned_file, save_to_file |
@@ -82,8 +82,8 @@ def dump(self): |
82 | 82 |
|
83 | 83 | class DownloadScratchContext: |
84 | 84 | """ |
85 | | - Minimal stand-in for MerginProject, used by download_files_async() when downloading files |
86 | | - directly by project name ("<workspace>/<project>") without an existing local project checkout. |
| 85 | + Minimal stand-in for MerginProject used when downloading a file directly by |
| 86 | + project name ("<workspace>/<project>") without an existing local project checkout. |
87 | 87 |
|
88 | 88 | Provides only what the shared download job code actually needs from MerginProject. |
89 | 89 | """ |
@@ -793,6 +793,23 @@ def download_file_finalize(job): |
793 | 793 | download_files_finalize(job) |
794 | 794 |
|
795 | 795 |
|
| 796 | +def download_project_file_async(mc, project_path: str, file_path: str, output_file: str, version: str = None): |
| 797 | + """ |
| 798 | + Starts background download of a single project file at specified version, fetched directly |
| 799 | + from the server without needing an existing local project checkout. |
| 800 | + Returns handle to the pending download. |
| 801 | +
|
| 802 | + :param project_path: full project name ("<workspace>/<project>") |
| 803 | + :param output_file: destination path for the downloaded file |
| 804 | + """ |
| 805 | + if not output_file: |
| 806 | + raise ClientError("output_file must be provided when downloading a file without a local project checkout") |
| 807 | + |
| 808 | + tmp_dir = tempfile.TemporaryDirectory(prefix="python-api-client-") |
| 809 | + mp = DownloadScratchContext(mc, tmp_dir.name) |
| 810 | + return _download_files_async(mc, mp, project_path, [file_path], [output_file], version, tmp_dir) |
| 811 | + |
| 812 | + |
796 | 813 | def download_diffs_async(mc, project_directory, file_path, versions): |
797 | 814 | """ |
798 | 815 | Starts background download project file diffs for specified versions. |
@@ -916,35 +933,29 @@ def download_diffs_finalize(job: PullJob) -> List[str]: |
916 | 933 |
|
917 | 934 |
|
918 | 935 | def download_files_async( |
919 | | - mc, project_dir: str, file_paths: typing.List[str], output_paths: typing.List[str], version: str |
| 936 | + mc, project_dir: str, file_paths: typing.List[str], output_paths: typing.List[str] = None, version: str = None |
920 | 937 | ): |
921 | 938 | """ |
922 | 939 | Starts background download project files at specified version. |
923 | 940 | Returns handle to the pending download. |
924 | 941 |
|
925 | | - `project_dir` can either be an existing local project directory (previously fetched with |
926 | | - download_project()), or a full project name ("<workspace>/<project>") to download files |
927 | | - directly from the server without needing a local checkout. In the latter case, `output_paths` |
928 | | - must be provided explicitly, as there is no project directory to place files into by default. |
| 942 | + `project_dir` must be an existing local project directory. |
929 | 943 | """ |
930 | | - # temporary directory to stage downloaded chunks in |
| 944 | + mp = MerginProject(project_dir) |
| 945 | + project_path = mp.project_full_name() |
931 | 946 | tmp_dir = tempfile.TemporaryDirectory(prefix="python-api-client-") |
| 947 | + return _download_files_async(mc, mp, project_path, file_paths, output_paths, version, tmp_dir) |
932 | 948 |
|
933 | | - mp: Union[MerginProject, "DownloadScratchContext"] |
934 | | - try: |
935 | | - mp = MerginProject(project_dir) |
936 | | - project_path = mp.project_full_name() |
937 | | - except InvalidProject: |
938 | | - # project_dir is not an existing local checkout - treat it as a full project name |
939 | | - # ("<workspace>/<project>") and download straight from the server instead |
940 | | - if output_paths is None: |
941 | | - cleanup_tmp_dir(mc, tmp_dir) |
942 | | - raise ClientError( |
943 | | - "output_paths must be provided when downloading files without an existing local project checkout" |
944 | | - ) |
945 | | - project_path = project_dir |
946 | | - mp = DownloadScratchContext(mc, tmp_dir.name) |
947 | 949 |
|
| 950 | +def _download_files_async( |
| 951 | + mc, |
| 952 | + mp: Union[MerginProject, "DownloadScratchContext"], |
| 953 | + project_path: str, |
| 954 | + file_paths: typing.List[str], |
| 955 | + output_paths: typing.List[str], |
| 956 | + version: str, |
| 957 | + tmp_dir: tempfile.TemporaryDirectory, |
| 958 | +): |
948 | 959 | ver_info = f"at version {version}" if version is not None else "at latest version" |
949 | 960 | mp.log.info(f"Getting [{', '.join(file_paths)}] {ver_info}") |
950 | 961 | latest_proj_info = mc.project_info(project_path) |
|
0 commit comments