From 603da1ca1ed1b71aae85e6bf4247e39e2788a538 Mon Sep 17 00:00:00 2001 From: dberenbaum Date: Mon, 11 Mar 2024 09:19:55 -0400 Subject: [PATCH] api: update remote config tests --- dvc/api/data.py | 45 +++++++++++++++++++++++++++++++++++-- tests/func/api/test_data.py | 43 +++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 2 deletions(-) diff --git a/dvc/api/data.py b/dvc/api/data.py index bea5e1d7c..03ab787f0 100644 --- a/dvc/api/data.py +++ b/dvc/api/data.py @@ -25,7 +25,14 @@ def _wrap_exceptions(repo, url): raise PathMissingError(exc.path, url) from exc -def get_url(path, repo=None, rev=None, **kwargs): +def get_url( + path: str, + repo: Optional[str] = None, + rev: Optional[str] = None, + remote: Optional[str] = None, + config: Optional[dict[str, Any]] = None, + remote_config: Optional[dict[str, Any]] = None, +): """ Returns the URL to the storage location of a data file or directory tracked in a DVC repo. For Git repos, HEAD is used unless a rev argument is @@ -35,11 +42,45 @@ def get_url(path, repo=None, rev=None, **kwargs): NOTE: This function does not check for the actual existence of the file or directory in the remote storage. + + Args: + path (str): location and file name of the target, relative to the root + of `repo`. + repo (str, optional): location of the DVC project or Git Repo. + Defaults to the current DVC project (found by walking up from the + current working directory tree). + It can be a URL or a file system path. + Both HTTP and SSH protocols are supported for online Git repos + (e.g. [user@]server:project.git). + rev (str, optional): Any `Git revision`_ such as a branch or tag name, + a commit hash or a dvc experiment name. + Defaults to HEAD. + If `repo` is not a Git repo, this option is ignored. + remote (str, optional): Name of the `DVC remote`_ used to form the + returned URL string. + Defaults to the `default remote`_ of `repo`. + For local projects, the cache is tried before the default remote. + config(dict, optional): config to be passed to the DVC repository. + Defaults to None. + remote_config(dict, optional): remote config to be passed to the DVC + repository. + Defaults to None. + + Returns: + str: URL to the file or directory. """ from dvc.config import NoRemoteError from dvc_data.index import StorageKeyError - with Repo.open(repo, rev=rev, subrepos=True, uninitialized=True, **kwargs) as _repo: + with Repo.open( + repo, + rev=rev, + subrepos=True, + uninitialized=True, + remote=remote, + config=config, + remote_config=remote_config, + ) as _repo: index, entry = _repo.get_data_index_entry(path) with reraise( (StorageKeyError, ValueError), diff --git a/tests/func/api/test_data.py b/tests/func/api/test_data.py index 8700cd2d7..47d51c7dc 100644 --- a/tests/func/api/test_data.py +++ b/tests/func/api/test_data.py @@ -31,6 +31,40 @@ def test_get_url_requires_dvc(tmp_dir, scm): api.get_url("foo", repo=f"file://{tmp_dir.as_posix()}") +def test_get_url_from_remote(tmp_dir, erepo_dir, cloud, local_cloud): + erepo_dir.add_remote(config=cloud.config, name="other") + erepo_dir.add_remote(config=local_cloud.config, default=True) + with erepo_dir.chdir(): + erepo_dir.dvc_gen("foo", "foo", commit="add foo") + + # Using file url to force clone to tmp repo + repo_url = f"file://{erepo_dir.as_posix()}" + expected_rel_path = os.path.join( + "files", "md5", "ac/bd18db4cc2f85cedef654fccc4a4d8" + ) + + # Test default remote + assert api.get_url("foo", repo=repo_url) == (local_cloud / expected_rel_path).url + + # Test remote arg + assert ( + api.get_url("foo", repo=repo_url, remote="other") + == (cloud / expected_rel_path).url + ) + + # Test config arg + assert ( + api.get_url("foo", repo=repo_url, config={"core": {"remote": "other"}}) + == (cloud / expected_rel_path).url + ) + + # Test remote_config arg + assert ( + api.get_url("foo", repo=repo_url, remote_config={"url": cloud.url}) + == (cloud / expected_rel_path).url + ) + + def test_open_external(tmp_dir, erepo_dir, cloud): erepo_dir.add_remote(config=cloud.config) @@ -253,3 +287,12 @@ def test_read_from_remote(tmp_dir, erepo_dir, cloud, local_cloud): ) == "foo content" ) + + assert ( + api.read( + os.path.join("dir", "foo"), + repo=f"file://{erepo_dir.as_posix()}", + remote_config={"url": cloud.url}, + ) + == "foo content" + )