Skip to content

Commit

Permalink
ls/import/get: introduce --remote and --remote-config
Browse files Browse the repository at this point in the history
Fixes #9656
  • Loading branch information
efiop committed Aug 2, 2023
1 parent 502b3df commit b235487
Show file tree
Hide file tree
Showing 11 changed files with 222 additions and 15 deletions.
24 changes: 24 additions & 0 deletions dvc/cli/utils.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,27 @@
import argparse


class DictAction(argparse.Action):
def __call__(self, parser, args, values, option_string=None): # noqa: ARG002
d = getattr(args, self.dest) or {}

if isinstance(values, list):
kvs = values
else:
kvs = [values]

for kv in kvs:
key, value = kv.split("=")
if not value:
raise argparse.ArgumentError(
self,
f'Could not parse argument "{values}" as k1=v1 k2=v2 ... format',
)
d[key] = value

setattr(args, self.dest, d)


def fix_subparsers(subparsers):
"""Workaround for bug in Python 3. See more info at:
https://bugs.python.org/issue16308
Expand Down
19 changes: 18 additions & 1 deletion dvc/commands/get.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from dvc.cli import completion
from dvc.cli.command import CmdBaseNoRepo
from dvc.cli.utils import append_doc_link
from dvc.cli.utils import DictAction, append_doc_link
from dvc.exceptions import DvcException

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -38,6 +38,8 @@ def _get_file_from_repo(self):
jobs=self.args.jobs,
force=self.args.force,
config=self.args.config,
remote=self.args.remote,
remote_config=self.args.remote_config,
)
return 0
except CloneError:
Expand Down Expand Up @@ -111,4 +113,19 @@ def add_parser(subparsers, parent_parser):
"in the target repository."
),
)
get_parser.add_argument(
"--remote",
type=str,
help="Remote name to set as a default in the target repository.",
)
get_parser.add_argument(
"--remote-config",
type=str,
nargs="*",
action=DictAction,
help=(
"Remote config options to merge with a remote's config (default or one "
"specified by '--remote') in the target repository."
),
)
get_parser.set_defaults(func=CmdGet)
19 changes: 18 additions & 1 deletion dvc/commands/imp.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from dvc.cli import completion
from dvc.cli.command import CmdBase
from dvc.cli.utils import append_doc_link
from dvc.cli.utils import DictAction, append_doc_link
from dvc.exceptions import DvcException

logger = logging.getLogger(__name__)
Expand All @@ -23,6 +23,8 @@ def run(self):
no_download=self.args.no_download,
jobs=self.args.jobs,
config=self.args.config,
remote=self.args.remote,
remote_config=self.args.remote_config,
)
except CloneError:
logger.exception("failed to import '%s'", self.args.path)
Expand Down Expand Up @@ -103,4 +105,19 @@ def add_parser(subparsers, parent_parser):
"in the target repository."
),
)
import_parser.add_argument(
"--remote",
type=str,
help="Remote name to set as a default in the target repository.",
)
import_parser.add_argument(
"--remote-config",
type=str,
nargs="*",
action=DictAction,
help=(
"Remote config options to merge with a remote's config (default or one "
"specified by '--remote') in the target repository."
),
)
import_parser.set_defaults(func=CmdImport)
19 changes: 18 additions & 1 deletion dvc/commands/ls/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from dvc.cli import completion
from dvc.cli.command import CmdBaseNoRepo
from dvc.cli.utils import append_doc_link
from dvc.cli.utils import DictAction, append_doc_link
from dvc.commands.ls.ls_colors import LsColors
from dvc.exceptions import DvcException
from dvc.ui import ui
Expand Down Expand Up @@ -35,6 +35,8 @@ def run(self):
recursive=self.args.recursive,
dvc_only=self.args.dvc_only,
config=self.args.config,
remote=self.args.remote,
remote_config=self.args.remote_config,
)
if self.args.json:
ui.write_json(entries)
Expand Down Expand Up @@ -89,6 +91,21 @@ def add_parser(subparsers, parent_parser):
"in the target repository."
),
)
list_parser.add_argument(
"--remote",
type=str,
help="Remote name to set as a default in the target repository.",
)
list_parser.add_argument(
"--remote-config",
type=str,
nargs="*",
action=DictAction,
help=(
"Remote config options to merge with a remote's config (default or one "
"specified by '--remote') in the target repository."
),
)
list_parser.add_argument(
"path",
nargs="?",
Expand Down
18 changes: 17 additions & 1 deletion dvc/dependency/repo.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from typing import TYPE_CHECKING, Dict, Optional, Union

from voluptuous import Required
from voluptuous import Any, Required

from dvc.utils import as_posix

Expand All @@ -17,13 +17,15 @@ class RepoDependency(Dependency):
PARAM_REV = "rev"
PARAM_REV_LOCK = "rev_lock"
PARAM_CONFIG = "config"
PARAM_REMOTE = "remote"

REPO_SCHEMA = {
PARAM_REPO: {
Required(PARAM_URL): str,
PARAM_REV: str,
PARAM_REV_LOCK: str,
PARAM_CONFIG: str,
PARAM_REMOTE: Any(str, dict),
}
}

Expand Down Expand Up @@ -76,6 +78,10 @@ def dumpd(self, **kwargs) -> Dict[str, Union[str, Dict[str, str]]]:
if config:
repo[self.PARAM_CONFIG] = config

remote = self.def_repo.get(self.PARAM_REMOTE)
if remote:
repo[self.PARAM_REMOTE] = remote

return {
self.PARAM_PATH: self.def_path,
self.PARAM_REPO: repo,
Expand All @@ -99,6 +105,14 @@ def _make_fs(
from dvc.config import Config
from dvc.fs import DVCFileSystem

rem = self.def_repo.get("remote")
if isinstance(rem, dict):
remote = None # type: ignore[unreachable]
remote_config = rem
else:
remote = rem
remote_config = None

conf = self.def_repo.get("config")
if conf:
config = Config.load_file(conf)
Expand All @@ -113,6 +127,8 @@ def _make_fs(
rev=rev or self._get_rev(locked=locked),
subrepos=True,
config=config,
remote=remote,
remote_config=remote_config,
)

def _get_rev(self, locked: bool = True):
Expand Down
14 changes: 13 additions & 1 deletion dvc/repo/get.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,17 @@ def __init__(self):
)


def get(url, path, out=None, rev=None, jobs=None, force=False, config=None):
def get(
url,
path,
out=None,
rev=None,
jobs=None,
force=False,
config=None,
remote=None,
remote_config=None,
):
from dvc.config import Config
from dvc.dvcfile import is_valid_filename
from dvc.repo import Repo
Expand All @@ -38,6 +48,8 @@ def get(url, path, out=None, rev=None, jobs=None, force=False, config=None):
subrepos=True,
uninitialized=True,
config=config,
remote=remote,
remote_config=remote_config,
) as repo:
from dvc.fs import download
from dvc.fs.data import DataFileSystem
Expand Down
25 changes: 24 additions & 1 deletion dvc/repo/imp.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,32 @@
def imp(self, url, path, out=None, rev=None, config=None, **kwargs):
def imp(
self,
url,
path,
out=None,
rev=None,
config=None,
remote=None,
remote_config=None,
**kwargs,
):
erepo = {"url": url}
if rev is not None:
erepo["rev"] = rev

if config is not None:
erepo["config"] = config

if remote is not None and remote_config is not None:
conf = erepo.get("config") or {}
remotes = conf.get("remote") or {}
remote_conf = remotes.get(remote) or {}
remote_conf.update(remote_config)
remotes[remote] = remote_conf
conf["remote"] = remotes
erepo["config"] = conf
elif remote is not None:
erepo["remote"] = remote
elif remote_config is not None:
erepo["remote"] = remote_config

return self.imp_url(path, out=out, erepo=erepo, frozen=True, **kwargs)
14 changes: 12 additions & 2 deletions dvc/repo/ls.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ def ls(
recursive: Optional[bool] = None,
dvc_only: bool = False,
config: Optional[str] = None,
remote: Optional[str] = None,
remote_config: Optional[dict] = None,
):
"""Methods for getting files and outputs for the repo.
Expand All @@ -23,7 +25,9 @@ def ls(
rev (str, optional): SHA commit, branch or tag name
recursive (bool, optional): recursively walk the repo
dvc_only (bool, optional): show only DVC-artifacts
config (bool, optional): path to config file
config (str, optional): path to config file
remote (str, optional): remote name to set as a default remote in the repo
remote_config (str, dict): remote config to merge with a remote in the repo
Returns:
list of `entry`
Expand All @@ -47,7 +51,13 @@ def ls(
config_dict = None

with Repo.open(
url, rev=rev, subrepos=True, uninitialized=True, config=config_dict
url,
rev=rev,
subrepos=True,
uninitialized=True,
config=config_dict,
remote=remote,
remote_config=remote_config,
) as repo:
path = path or ""

Expand Down
Loading

0 comments on commit b235487

Please sign in to comment.