mirror of
https://github.com/Stone-Red-Code/dvc.git
synced 2026-11-01 23:08:43 +01:00
dvc: refactor config (#3298)
* dvc: refactor config
Advantages:
- read like a dict
- modify as a dict for testing or manipulation purposes
- wrap modifications into context manager to save them:
with config.edit("local") as conf:
conf["core"]["remote"] = "new-default"
Design decisions:
- dropped string constants
- remote sections are parsed into a simple subdict upon load
- remote config schema is now defined by url scheme
- relative paths are handled transparently upon load/save
- user config and remote manipulations are moved to commands
- same dev manipulations are done by simply modifying a config dict
* test: do not set cache.type for remotes that doesn't support it
* dvc: fix windows absolute paths validation in config
* test: fix windows separate drive test
* dvc: stylistic fixes in config
This commit is contained in:
+2
-5
@@ -54,11 +54,8 @@ def is_enabled():
|
||||
return False
|
||||
|
||||
enabled = to_bool(
|
||||
Config(validate=False)
|
||||
.config.get(Config.SECTION_CORE, {})
|
||||
.get(Config.SECTION_CORE_ANALYTICS, "true")
|
||||
Config(validate=False).get("core", {}).get("analytics", "true")
|
||||
)
|
||||
|
||||
logger.debug("Analytics is {}abled.".format("en" if enabled else "dis"))
|
||||
|
||||
return enabled
|
||||
@@ -144,7 +141,7 @@ def _find_or_create_user_id():
|
||||
|
||||
IDs are generated randomly with UUID.
|
||||
"""
|
||||
config_dir = Config.get_global_config_dir()
|
||||
config_dir = Config.get_dir("global")
|
||||
fname = os.path.join(config_dir, "user_id")
|
||||
lockfile = os.path.join(config_dir, "user_id.lock")
|
||||
|
||||
|
||||
+9
-41
@@ -1,25 +1,8 @@
|
||||
"""Manages cache of a DVC repo."""
|
||||
import os
|
||||
from collections import defaultdict
|
||||
|
||||
from funcy import cached_property
|
||||
|
||||
from dvc.config import Config
|
||||
|
||||
|
||||
class CacheConfig(object):
|
||||
def __init__(self, config):
|
||||
self.config = config
|
||||
|
||||
def set_dir(self, dname, level=None):
|
||||
from dvc.remote.config import RemoteConfig
|
||||
|
||||
configobj = self.config.get_configobj(level)
|
||||
path = RemoteConfig.resolve_path(dname, configobj.filename)
|
||||
self.config.set(
|
||||
Config.SECTION_CACHE, Config.SECTION_CACHE_DIR, path, level=level
|
||||
)
|
||||
|
||||
|
||||
def _make_remote_property(name):
|
||||
"""
|
||||
@@ -70,37 +53,22 @@ class Cache(object):
|
||||
from dvc.remote import Remote
|
||||
|
||||
self.repo = repo
|
||||
self.config = config = repo.config["cache"]
|
||||
|
||||
self.config = config = repo.config.config[Config.SECTION_CACHE]
|
||||
local = config.get(Config.SECTION_CACHE_LOCAL)
|
||||
local = config.get("local")
|
||||
|
||||
if local:
|
||||
name = Config.SECTION_REMOTE_FMT.format(local)
|
||||
settings = repo.config.config[name]
|
||||
settings = {"name": local}
|
||||
else:
|
||||
default_cache_dir = os.path.join(repo.dvc_dir, self.CACHE_DIR)
|
||||
cache_dir = config.get(Config.SECTION_CACHE_DIR, default_cache_dir)
|
||||
cache_type = config.get(Config.SECTION_CACHE_TYPE)
|
||||
protected = config.get(Config.SECTION_CACHE_PROTECTED)
|
||||
shared = config.get(Config.SECTION_CACHE_SHARED)
|
||||
|
||||
settings = {
|
||||
Config.PRIVATE_CWD: config.get(
|
||||
Config.PRIVATE_CWD, repo.dvc_dir
|
||||
),
|
||||
Config.SECTION_REMOTE_URL: cache_dir,
|
||||
Config.SECTION_CACHE_TYPE: cache_type,
|
||||
Config.SECTION_CACHE_PROTECTED: protected,
|
||||
Config.SECTION_CACHE_SHARED: shared,
|
||||
}
|
||||
settings = {**config, "url": config["dir"]}
|
||||
|
||||
self.local = Remote(repo, **settings)
|
||||
|
||||
s3 = _make_remote_property(Config.SECTION_CACHE_S3)
|
||||
gs = _make_remote_property(Config.SECTION_CACHE_GS)
|
||||
ssh = _make_remote_property(Config.SECTION_CACHE_SSH)
|
||||
hdfs = _make_remote_property(Config.SECTION_CACHE_HDFS)
|
||||
azure = _make_remote_property(Config.SECTION_CACHE_AZURE)
|
||||
s3 = _make_remote_property("s3")
|
||||
gs = _make_remote_property("gs")
|
||||
ssh = _make_remote_property("ssh")
|
||||
hdfs = _make_remote_property("hdfs")
|
||||
azure = _make_remote_property("azure")
|
||||
|
||||
|
||||
class NamedCache(object):
|
||||
|
||||
+2
-5
@@ -1,11 +1,8 @@
|
||||
import argparse
|
||||
import logging
|
||||
|
||||
from dvc.command.base import append_doc_link
|
||||
from dvc.command.base import CmdBase
|
||||
from dvc.exceptions import DvcException
|
||||
from dvc.exceptions import RecursiveAddingWhileUsingFilename
|
||||
|
||||
from dvc.command.base import append_doc_link, CmdBase
|
||||
from dvc.exceptions import DvcException, RecursiveAddingWhileUsingFilename
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@ class CmdBase(object):
|
||||
self.repo = Repo()
|
||||
self.config = self.repo.config
|
||||
self.args = args
|
||||
hardlink_lock = self.config.config["core"].get("hardlink_lock", False)
|
||||
hardlink_lock = self.config["core"].get("hardlink_lock", False)
|
||||
updater = Updater(self.repo.dvc_dir, hardlink_lock=hardlink_lock)
|
||||
updater.check()
|
||||
|
||||
|
||||
@@ -1,18 +1,13 @@
|
||||
import argparse
|
||||
|
||||
from dvc.cache import CacheConfig
|
||||
from dvc.command.base import append_doc_link
|
||||
from dvc.command.base import fix_subparsers
|
||||
from dvc.command.base import append_doc_link, fix_subparsers
|
||||
from dvc.command.config import CmdConfig
|
||||
|
||||
|
||||
class CmdCacheDir(CmdConfig):
|
||||
def __init__(self, args):
|
||||
super().__init__(args)
|
||||
self.cache_config = CacheConfig(self.config)
|
||||
|
||||
def run(self):
|
||||
self.cache_config.set_dir(self.args.value, level=self.args.level)
|
||||
with self.config.edit(level=self.args.level) as edit:
|
||||
edit["cache"]["dir"] = self.args.value
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
+29
-18
@@ -1,10 +1,8 @@
|
||||
import argparse
|
||||
import logging
|
||||
|
||||
from dvc.command.base import append_doc_link
|
||||
from dvc.command.base import CmdBaseNoRepo
|
||||
from dvc.config import Config
|
||||
|
||||
from dvc.command.base import append_doc_link, CmdBaseNoRepo
|
||||
from dvc.config import Config, ConfigError
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -18,17 +16,21 @@ class CmdConfig(CmdBaseNoRepo):
|
||||
def run(self):
|
||||
section, opt = self.args.name.lower().strip().split(".", 1)
|
||||
|
||||
if self.args.unset:
|
||||
self.config.unset(section, opt, level=self.args.level)
|
||||
elif self.args.value is None:
|
||||
logger.info(self.config.get(section, opt, level=self.args.level))
|
||||
else:
|
||||
self.config.set(
|
||||
section, opt, self.args.value, level=self.args.level
|
||||
)
|
||||
if self.args.value is None and not self.args.unset:
|
||||
conf = self.config.load_one(self.args.level)
|
||||
self._check(conf, section, opt)
|
||||
logger.info(conf[section][opt])
|
||||
return 0
|
||||
|
||||
is_write = self.args.unset or self.args.value is not None
|
||||
if is_write and self.args.name == "cache.type":
|
||||
with self.config.edit(self.args.level) as conf:
|
||||
if self.args.unset:
|
||||
self._check(conf, section, opt)
|
||||
del conf[section][opt]
|
||||
else:
|
||||
self._check(conf, section)
|
||||
conf[section][opt] = self.args.value
|
||||
|
||||
if self.args.name == "cache.type":
|
||||
logger.warning(
|
||||
"You have changed the 'cache.type' option. This doesn't update"
|
||||
" any existing workspace file links, but it can be done with:"
|
||||
@@ -37,30 +39,39 @@ class CmdConfig(CmdBaseNoRepo):
|
||||
|
||||
return 0
|
||||
|
||||
def _check(self, conf, section, opt=None):
|
||||
if section not in conf:
|
||||
msg = "section {} doesn't exist"
|
||||
raise ConfigError(msg.format(self.args.name))
|
||||
|
||||
if opt and opt not in conf[section]:
|
||||
msg = "option {} doesn't exist"
|
||||
raise ConfigError(msg.format(self.args.name))
|
||||
|
||||
|
||||
parent_config_parser = argparse.ArgumentParser(add_help=False)
|
||||
parent_config_parser.add_argument(
|
||||
"--global",
|
||||
dest="level",
|
||||
action="store_const",
|
||||
const=Config.LEVEL_GLOBAL,
|
||||
const="global",
|
||||
help="Use global config.",
|
||||
)
|
||||
parent_config_parser.add_argument(
|
||||
"--system",
|
||||
dest="level",
|
||||
action="store_const",
|
||||
const=Config.LEVEL_SYSTEM,
|
||||
const="system",
|
||||
help="Use system config.",
|
||||
)
|
||||
parent_config_parser.add_argument(
|
||||
"--local",
|
||||
dest="level",
|
||||
action="store_const",
|
||||
const=Config.LEVEL_LOCAL,
|
||||
const="local",
|
||||
help="Use local config.",
|
||||
)
|
||||
parent_config_parser.set_defaults(level=Config.LEVEL_REPO)
|
||||
parent_config_parser.set_defaults(level="repo")
|
||||
|
||||
|
||||
def add_parser(subparsers, parent_parser):
|
||||
|
||||
+60
-33
@@ -1,72 +1,99 @@
|
||||
import argparse
|
||||
import logging
|
||||
|
||||
from dvc.command.base import append_doc_link
|
||||
from dvc.command.base import fix_subparsers
|
||||
from dvc.config import ConfigError
|
||||
from dvc.command.base import append_doc_link, fix_subparsers
|
||||
from dvc.command.config import CmdConfig
|
||||
from dvc.remote.config import RemoteConfig
|
||||
from dvc.utils import format_link
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CmdRemoteConfig(CmdConfig):
|
||||
class CmdRemote(CmdConfig):
|
||||
def __init__(self, args):
|
||||
super().__init__(args)
|
||||
self.remote_config = RemoteConfig(self.config)
|
||||
|
||||
if getattr(self.args, "name", None):
|
||||
self.args.name = self.args.name.lower()
|
||||
|
||||
def _check_exists(self, conf):
|
||||
if self.args.name not in conf["remote"]:
|
||||
raise ConfigError(
|
||||
"remote '{}' doesn't exists.".format(self.args.name)
|
||||
)
|
||||
|
||||
|
||||
class CmdRemoteAdd(CmdRemoteConfig):
|
||||
class CmdRemoteAdd(CmdRemote):
|
||||
def run(self):
|
||||
if self.args.default:
|
||||
logger.info(
|
||||
"Setting '{}' as a default remote.".format(self.args.name)
|
||||
)
|
||||
self.remote_config.add(
|
||||
self.args.name,
|
||||
self.args.url,
|
||||
force=self.args.force,
|
||||
default=self.args.default,
|
||||
level=self.args.level,
|
||||
)
|
||||
|
||||
with self.config.edit(self.args.level) as conf:
|
||||
if self.args.name in conf["remote"] and not self.args.force:
|
||||
raise ConfigError(
|
||||
"remote '{}' already exists. Use `-f|--force` to "
|
||||
"overwrite it.".format(self.args.name)
|
||||
)
|
||||
|
||||
conf["remote"][self.args.name] = {"url": self.args.url}
|
||||
if self.args.default:
|
||||
conf["core"]["remote"] = self.args.name
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
class CmdRemoteRemove(CmdRemoteConfig):
|
||||
class CmdRemoteRemove(CmdRemote):
|
||||
def run(self):
|
||||
self.remote_config.remove(self.args.name, level=self.args.level)
|
||||
with self.config.edit(self.args.level) as conf:
|
||||
self._check_exists(conf)
|
||||
del conf["remote"][self.args.name]
|
||||
|
||||
# Remove core.remote refs to this remote in any shadowing configs
|
||||
for level in reversed(self.config.LEVELS):
|
||||
with self.config.edit(level) as conf:
|
||||
if conf["core"].get("remote") == self.args.name:
|
||||
del conf["core"]["remote"]
|
||||
|
||||
if level == self.args.level:
|
||||
break
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
class CmdRemoteModify(CmdRemoteConfig):
|
||||
class CmdRemoteModify(CmdRemote):
|
||||
def run(self):
|
||||
self.remote_config.modify(
|
||||
self.args.name,
|
||||
self.args.option,
|
||||
self.args.value,
|
||||
level=self.args.level,
|
||||
)
|
||||
with self.config.edit(self.args.level) as conf:
|
||||
self._check_exists(conf)
|
||||
conf["remote"][self.args.name][self.args.option] = self.args.value
|
||||
return 0
|
||||
|
||||
|
||||
class CmdRemoteDefault(CmdRemoteConfig):
|
||||
class CmdRemoteDefault(CmdRemote):
|
||||
def run(self):
|
||||
|
||||
if self.args.name is None and not self.args.unset:
|
||||
name = self.remote_config.get_default(level=self.args.level)
|
||||
print(name)
|
||||
conf = self.config.load_one(self.args.level)
|
||||
try:
|
||||
print(conf["core"]["remote"])
|
||||
except KeyError:
|
||||
logger.info("No default remote set")
|
||||
return 1
|
||||
else:
|
||||
self.remote_config.set_default(
|
||||
self.args.name, unset=self.args.unset, level=self.args.level
|
||||
)
|
||||
with self.config.edit(self.args.level) as conf:
|
||||
if self.args.unset:
|
||||
conf["core"].pop("remote", None)
|
||||
else:
|
||||
conf["core"]["remote"] = self.args.name
|
||||
return 0
|
||||
|
||||
|
||||
class CmdRemoteList(CmdRemoteConfig):
|
||||
class CmdRemoteList(CmdRemote):
|
||||
def run(self):
|
||||
for name, url in self.remote_config.list(
|
||||
level=self.args.level
|
||||
).items():
|
||||
logger.info("{}\t{}".format(name, url))
|
||||
conf = self.config.load_one(self.args.level)
|
||||
for name, conf in conf["remote"].items():
|
||||
logger.info("{}\t{}".format(name, conf["url"]))
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
+249
-401
@@ -1,17 +1,17 @@
|
||||
"""DVC config objects."""
|
||||
|
||||
import copy
|
||||
import errno
|
||||
from contextlib import contextmanager
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from funcy import cached_property, re_find, walk_values, compact
|
||||
import configobj
|
||||
from voluptuous import Schema, Required, Optional, Invalid
|
||||
from voluptuous import All, Any, Lower, Range, Coerce, Match
|
||||
from voluptuous import Schema, Optional, Invalid, ALLOW_EXTRA
|
||||
from voluptuous import All, Any, Lower, Range, Coerce
|
||||
|
||||
from dvc.exceptions import DvcException
|
||||
from dvc.exceptions import NotDvcRepoError
|
||||
from dvc.exceptions import DvcException, NotDvcRepoError
|
||||
from dvc.utils import relpath
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -48,13 +48,12 @@ def supported_cache_type(types):
|
||||
|
||||
|
||||
# Checks that value is either true or false and converts it to bool
|
||||
Bool = All(
|
||||
to_bool = Bool = All(
|
||||
Lower,
|
||||
Any("true", "false"),
|
||||
lambda v: v == "true",
|
||||
msg="expected true or false",
|
||||
)
|
||||
to_bool = Schema(Bool)
|
||||
|
||||
|
||||
def Choices(*choices):
|
||||
@@ -67,7 +66,125 @@ def Choices(*choices):
|
||||
return Any(*choices, msg="expected one of {}".format(", ".join(choices)))
|
||||
|
||||
|
||||
class Config(object): # pylint: disable=too-many-instance-attributes
|
||||
def ByUrl(mapping):
|
||||
schemas = walk_values(Schema, mapping)
|
||||
|
||||
def validate(data):
|
||||
if "url" not in data:
|
||||
raise Invalid("expected 'url'")
|
||||
|
||||
parsed = urlparse(data["url"])
|
||||
# Windows absolute paths should really have scheme == "" (local)
|
||||
if os.name == "nt" and len(parsed.scheme) == 1 and parsed.netloc == "":
|
||||
return schemas[""](data)
|
||||
if parsed.scheme not in schemas:
|
||||
raise Invalid("Unsupported URL type {}://".format(parsed.scheme))
|
||||
|
||||
return schemas[parsed.scheme](data)
|
||||
|
||||
return validate
|
||||
|
||||
|
||||
class RelPath(str):
|
||||
pass
|
||||
|
||||
|
||||
REMOTE_COMMON = {
|
||||
"url": str,
|
||||
"checksum_jobs": All(Coerce(int), Range(1)),
|
||||
"no_traverse": Bool,
|
||||
"verify": Bool,
|
||||
}
|
||||
LOCAL_COMMON = {
|
||||
"type": supported_cache_type,
|
||||
Optional("protected", default=False): Bool,
|
||||
"shared": All(Lower, Choices("group")),
|
||||
Optional("slow_link_warning", default=True): Bool,
|
||||
}
|
||||
SCHEMA = {
|
||||
"core": {
|
||||
"remote": Lower,
|
||||
"checksum_jobs": All(Coerce(int), Range(1)),
|
||||
"loglevel": All(Lower, Choices("info", "debug", "warning", "error")),
|
||||
Optional("interactive", default=False): Bool,
|
||||
Optional("analytics", default=True): Bool,
|
||||
Optional("hardlink_lock", default=False): Bool,
|
||||
},
|
||||
"cache": {
|
||||
"local": str,
|
||||
"s3": str,
|
||||
"gs": str,
|
||||
"hdfs": str,
|
||||
"ssh": str,
|
||||
"azure": str,
|
||||
# This is for default local cache
|
||||
"dir": str,
|
||||
**LOCAL_COMMON,
|
||||
},
|
||||
"remote": {
|
||||
str: ByUrl(
|
||||
{
|
||||
"": {**LOCAL_COMMON, **REMOTE_COMMON},
|
||||
"s3": {
|
||||
"region": str,
|
||||
"profile": str,
|
||||
"credentialpath": str,
|
||||
"endpointurl": str,
|
||||
Optional("listobjects", default=False): Bool,
|
||||
Optional("use_ssl", default=True): Bool,
|
||||
"sse": str,
|
||||
"acl": str,
|
||||
"grant_read": str,
|
||||
"grant_read_acp": str,
|
||||
"grant_write_acp": str,
|
||||
"grant_full_control": str,
|
||||
**REMOTE_COMMON,
|
||||
},
|
||||
"gs": {
|
||||
"projectname": str,
|
||||
"credentialpath": str,
|
||||
**REMOTE_COMMON,
|
||||
},
|
||||
"ssh": {
|
||||
"type": supported_cache_type,
|
||||
"port": Coerce(int),
|
||||
"user": str,
|
||||
"password": str,
|
||||
"ask_password": Bool,
|
||||
"keyfile": str,
|
||||
"timeout": Coerce(int),
|
||||
"gss_auth": Bool,
|
||||
**REMOTE_COMMON,
|
||||
},
|
||||
"hdfs": {"user": str, **REMOTE_COMMON},
|
||||
"azure": {"connection_string": str, **REMOTE_COMMON},
|
||||
"oss": {
|
||||
"oss_key_id": str,
|
||||
"oss_key_secret": str,
|
||||
"oss_endpoint": str,
|
||||
**REMOTE_COMMON,
|
||||
},
|
||||
"gdrive": {
|
||||
"gdrive_client_id": str,
|
||||
"gdrive_client_secret": str,
|
||||
"gdrive_user_credentials_file": str,
|
||||
**REMOTE_COMMON,
|
||||
},
|
||||
"http": REMOTE_COMMON,
|
||||
"https": REMOTE_COMMON,
|
||||
"remote": {str: object}, # Any of the above options are valid
|
||||
}
|
||||
)
|
||||
},
|
||||
"state": {
|
||||
"row_limit": All(Coerce(int), Range(1)),
|
||||
"row_cleanup_quota": All(Coerce(int), Range(0, 100)),
|
||||
},
|
||||
}
|
||||
COMPILED_SCHEMA = Schema(SCHEMA)
|
||||
|
||||
|
||||
class Config(dict):
|
||||
"""Class that manages configuration files for a DVC repo.
|
||||
|
||||
Args:
|
||||
@@ -85,168 +202,14 @@ class Config(object): # pylint: disable=too-many-instance-attributes
|
||||
APPNAME = "dvc"
|
||||
APPAUTHOR = "iterative"
|
||||
|
||||
# NOTE: used internally in RemoteLOCAL to know config
|
||||
# location, that url should resolved relative to.
|
||||
PRIVATE_CWD = "_cwd"
|
||||
# In the order they shadow each other
|
||||
LEVELS = ("system", "global", "repo", "local")
|
||||
|
||||
CONFIG = "config"
|
||||
CONFIG_LOCAL = "config.local"
|
||||
|
||||
CREDENTIALPATH = "credentialpath"
|
||||
|
||||
LEVEL_LOCAL = 0
|
||||
LEVEL_REPO = 1
|
||||
LEVEL_GLOBAL = 2
|
||||
LEVEL_SYSTEM = 3
|
||||
|
||||
SECTION_CORE = "core"
|
||||
SECTION_CORE_LOGLEVEL = "loglevel"
|
||||
SECTION_CORE_LOGLEVEL_SCHEMA = All(
|
||||
Lower, Choices("info", "debug", "warning", "error")
|
||||
)
|
||||
SECTION_CORE_REMOTE = "remote"
|
||||
SECTION_CORE_INTERACTIVE = "interactive"
|
||||
SECTION_CORE_ANALYTICS = "analytics"
|
||||
SECTION_CORE_CHECKSUM_JOBS = "checksum_jobs"
|
||||
SECTION_CORE_HARDLINK_LOCK = "hardlink_lock"
|
||||
|
||||
SECTION_CACHE = "cache"
|
||||
SECTION_CACHE_DIR = "dir"
|
||||
SECTION_CACHE_TYPE = "type"
|
||||
SECTION_CACHE_PROTECTED = "protected"
|
||||
SECTION_CACHE_SHARED = "shared"
|
||||
SECTION_CACHE_SHARED_SCHEMA = All(Lower, Choices("group"))
|
||||
SECTION_CACHE_LOCAL = "local"
|
||||
SECTION_CACHE_S3 = "s3"
|
||||
SECTION_CACHE_GS = "gs"
|
||||
SECTION_CACHE_SSH = "ssh"
|
||||
SECTION_CACHE_HDFS = "hdfs"
|
||||
SECTION_CACHE_AZURE = "azure"
|
||||
SECTION_CACHE_SLOW_LINK_WARNING = "slow_link_warning"
|
||||
SECTION_CACHE_SCHEMA = {
|
||||
SECTION_CACHE_LOCAL: str,
|
||||
SECTION_CACHE_S3: str,
|
||||
SECTION_CACHE_GS: str,
|
||||
SECTION_CACHE_HDFS: str,
|
||||
SECTION_CACHE_SSH: str,
|
||||
SECTION_CACHE_AZURE: str,
|
||||
SECTION_CACHE_DIR: str,
|
||||
SECTION_CACHE_TYPE: supported_cache_type,
|
||||
Optional(SECTION_CACHE_PROTECTED, default=False): Bool,
|
||||
SECTION_CACHE_SHARED: SECTION_CACHE_SHARED_SCHEMA,
|
||||
PRIVATE_CWD: str,
|
||||
Optional(SECTION_CACHE_SLOW_LINK_WARNING, default=True): Bool,
|
||||
}
|
||||
|
||||
SECTION_CORE_SCHEMA = {
|
||||
SECTION_CORE_LOGLEVEL: SECTION_CORE_LOGLEVEL_SCHEMA,
|
||||
SECTION_CORE_REMOTE: Lower,
|
||||
Optional(SECTION_CORE_INTERACTIVE, default=False): Bool,
|
||||
Optional(SECTION_CORE_ANALYTICS, default=True): Bool,
|
||||
SECTION_CORE_CHECKSUM_JOBS: All(Coerce(int), Range(1)),
|
||||
Optional(SECTION_CORE_HARDLINK_LOCK, default=False): Bool,
|
||||
}
|
||||
|
||||
# aws specific options
|
||||
SECTION_AWS_CREDENTIALPATH = CREDENTIALPATH
|
||||
SECTION_AWS_ENDPOINT_URL = "endpointurl"
|
||||
SECTION_AWS_LIST_OBJECTS = "listobjects"
|
||||
SECTION_AWS_REGION = "region"
|
||||
SECTION_AWS_PROFILE = "profile"
|
||||
SECTION_AWS_USE_SSL = "use_ssl"
|
||||
SECTION_AWS_SSE = "sse"
|
||||
SECTION_AWS_ACL = "acl"
|
||||
SECTION_AWS_GRANT_READ = "grant_read"
|
||||
SECTION_AWS_GRANT_READ_ACP = "grant_read_acp"
|
||||
SECTION_AWS_GRANT_WRITE_ACP = "grant_write_acp"
|
||||
SECTION_AWS_GRANT_FULL_CONTROL = "grant_full_control"
|
||||
|
||||
# gcp specific options
|
||||
SECTION_GCP_CREDENTIALPATH = CREDENTIALPATH
|
||||
SECTION_GCP_PROJECTNAME = "projectname"
|
||||
|
||||
# azure specific option
|
||||
SECTION_AZURE_CONNECTION_STRING = "connection_string"
|
||||
|
||||
# Alibabacloud oss options
|
||||
SECTION_OSS_ACCESS_KEY_ID = "oss_key_id"
|
||||
SECTION_OSS_ACCESS_KEY_SECRET = "oss_key_secret"
|
||||
SECTION_OSS_ENDPOINT = "oss_endpoint"
|
||||
|
||||
# GDrive options
|
||||
SECTION_GDRIVE_CLIENT_ID = "gdrive_client_id"
|
||||
SECTION_GDRIVE_CLIENT_SECRET = "gdrive_client_secret"
|
||||
SECTION_GDRIVE_USER_CREDENTIALS_FILE = "gdrive_user_credentials_file"
|
||||
|
||||
SECTION_REMOTE_CHECKSUM_JOBS = "checksum_jobs"
|
||||
SECTION_REMOTE_REGEX = r'^\s*remote\s*"(?P<name>.*)"\s*$'
|
||||
SECTION_REMOTE_FMT = 'remote "{}"'
|
||||
SECTION_REMOTE_URL = "url"
|
||||
SECTION_REMOTE_USER = "user"
|
||||
SECTION_REMOTE_PORT = "port"
|
||||
SECTION_REMOTE_KEY_FILE = "keyfile"
|
||||
SECTION_REMOTE_TIMEOUT = "timeout"
|
||||
SECTION_REMOTE_PASSWORD = "password"
|
||||
SECTION_REMOTE_ASK_PASSWORD = "ask_password"
|
||||
SECTION_REMOTE_GSS_AUTH = "gss_auth"
|
||||
SECTION_REMOTE_NO_TRAVERSE = "no_traverse"
|
||||
SECTION_REMOTE_VERIFY = "verify"
|
||||
SECTION_REMOTE_SCHEMA = {
|
||||
Required(SECTION_REMOTE_URL): str,
|
||||
SECTION_AWS_REGION: str,
|
||||
SECTION_AWS_PROFILE: str,
|
||||
SECTION_AWS_CREDENTIALPATH: str,
|
||||
SECTION_AWS_ENDPOINT_URL: str,
|
||||
Optional(SECTION_AWS_LIST_OBJECTS, default=False): Bool,
|
||||
Optional(SECTION_AWS_USE_SSL, default=True): Bool,
|
||||
SECTION_AWS_SSE: str,
|
||||
SECTION_AWS_ACL: str,
|
||||
SECTION_AWS_GRANT_READ: str,
|
||||
SECTION_AWS_GRANT_READ_ACP: str,
|
||||
SECTION_AWS_GRANT_WRITE_ACP: str,
|
||||
SECTION_AWS_GRANT_FULL_CONTROL: str,
|
||||
SECTION_GCP_PROJECTNAME: str,
|
||||
SECTION_CACHE_TYPE: supported_cache_type,
|
||||
Optional(SECTION_CACHE_PROTECTED, default=False): Bool,
|
||||
SECTION_REMOTE_CHECKSUM_JOBS: All(Coerce(int), Range(1)),
|
||||
SECTION_REMOTE_USER: str,
|
||||
SECTION_REMOTE_PORT: Coerce(int),
|
||||
SECTION_REMOTE_KEY_FILE: str,
|
||||
SECTION_REMOTE_TIMEOUT: Coerce(int),
|
||||
SECTION_REMOTE_PASSWORD: str,
|
||||
SECTION_REMOTE_ASK_PASSWORD: Bool,
|
||||
SECTION_REMOTE_GSS_AUTH: Bool,
|
||||
SECTION_AZURE_CONNECTION_STRING: str,
|
||||
SECTION_OSS_ACCESS_KEY_ID: str,
|
||||
SECTION_OSS_ACCESS_KEY_SECRET: str,
|
||||
SECTION_OSS_ENDPOINT: str,
|
||||
SECTION_GDRIVE_CLIENT_ID: str,
|
||||
SECTION_GDRIVE_CLIENT_SECRET: str,
|
||||
SECTION_GDRIVE_USER_CREDENTIALS_FILE: str,
|
||||
PRIVATE_CWD: str,
|
||||
SECTION_REMOTE_NO_TRAVERSE: Bool,
|
||||
SECTION_REMOTE_VERIFY: Bool,
|
||||
}
|
||||
|
||||
SECTION_STATE = "state"
|
||||
SECTION_STATE_ROW_LIMIT = "row_limit"
|
||||
SECTION_STATE_ROW_CLEANUP_QUOTA = "row_cleanup_quota"
|
||||
SECTION_STATE_SCHEMA = {
|
||||
SECTION_STATE_ROW_LIMIT: All(Coerce(int), Range(1)),
|
||||
SECTION_STATE_ROW_CLEANUP_QUOTA: All(Coerce(int), Range(0, 100)),
|
||||
}
|
||||
|
||||
SCHEMA = {
|
||||
Optional(SECTION_CORE, default={}): SECTION_CORE_SCHEMA,
|
||||
Match(SECTION_REMOTE_REGEX): SECTION_REMOTE_SCHEMA,
|
||||
Optional(SECTION_CACHE, default={}): SECTION_CACHE_SCHEMA,
|
||||
Optional(SECTION_STATE, default={}): SECTION_STATE_SCHEMA,
|
||||
}
|
||||
COMPILED_SCHEMA = Schema(SCHEMA)
|
||||
|
||||
def __init__(self, dvc_dir=None, validate=True):
|
||||
self.dvc_dir = dvc_dir
|
||||
self.should_validate = validate
|
||||
|
||||
if not dvc_dir:
|
||||
try:
|
||||
@@ -258,33 +221,31 @@ class Config(object): # pylint: disable=too-many-instance-attributes
|
||||
else:
|
||||
self.dvc_dir = os.path.abspath(os.path.realpath(dvc_dir))
|
||||
|
||||
self.load()
|
||||
self.load(validate=validate)
|
||||
|
||||
@staticmethod
|
||||
def get_global_config_dir():
|
||||
"""Returns global config location. E.g. ~/.config/dvc/config.
|
||||
@classmethod
|
||||
def get_dir(cls, level):
|
||||
from appdirs import user_config_dir, site_config_dir
|
||||
|
||||
Returns:
|
||||
str: path to the global config directory.
|
||||
"""
|
||||
from appdirs import user_config_dir
|
||||
assert level in ("global", "system")
|
||||
|
||||
return user_config_dir(
|
||||
appname=Config.APPNAME, appauthor=Config.APPAUTHOR
|
||||
)
|
||||
if level == "global":
|
||||
return user_config_dir(cls.APPNAME, cls.APPAUTHOR)
|
||||
if level == "system":
|
||||
return site_config_dir(cls.APPNAME, cls.APPAUTHOR)
|
||||
|
||||
@staticmethod
|
||||
def get_system_config_dir():
|
||||
"""Returns system config location. E.g. /etc/dvc.conf.
|
||||
@cached_property
|
||||
def files(self):
|
||||
files = {
|
||||
level: os.path.join(self.get_dir(level), self.CONFIG)
|
||||
for level in ("system", "global")
|
||||
}
|
||||
|
||||
Returns:
|
||||
str: path to the system config directory.
|
||||
"""
|
||||
from appdirs import site_config_dir
|
||||
if self.dvc_dir is not None:
|
||||
files["repo"] = os.path.join(self.dvc_dir, self.CONFIG)
|
||||
files["local"] = os.path.join(self.dvc_dir, self.CONFIG_LOCAL)
|
||||
|
||||
return site_config_dir(
|
||||
appname=Config.APPNAME, appauthor=Config.APPAUTHOR
|
||||
)
|
||||
return files
|
||||
|
||||
@staticmethod
|
||||
def init(dvc_dir):
|
||||
@@ -300,250 +261,137 @@ class Config(object): # pylint: disable=too-many-instance-attributes
|
||||
open(config_file, "w+").close()
|
||||
return Config(dvc_dir)
|
||||
|
||||
def _resolve_cache_path(self, config):
|
||||
cache = config.get(self.SECTION_CACHE)
|
||||
if cache is None:
|
||||
return
|
||||
|
||||
cache_dir = cache.get(self.SECTION_CACHE_DIR)
|
||||
if cache_dir is None:
|
||||
return
|
||||
|
||||
cache[self.PRIVATE_CWD] = os.path.dirname(config.filename)
|
||||
|
||||
def _resolve_paths(self, config):
|
||||
if config.filename is None:
|
||||
return config
|
||||
|
||||
ret = copy.deepcopy(config)
|
||||
self._resolve_cache_path(ret)
|
||||
|
||||
for section in ret.values():
|
||||
if self.SECTION_REMOTE_URL not in section.keys():
|
||||
continue
|
||||
|
||||
section[self.PRIVATE_CWD] = os.path.dirname(ret.filename)
|
||||
|
||||
return ret
|
||||
|
||||
def _load_configs(self):
|
||||
system_config_file = os.path.join(
|
||||
self.get_system_config_dir(), self.CONFIG
|
||||
)
|
||||
|
||||
global_config_file = os.path.join(
|
||||
self.get_global_config_dir(), self.CONFIG
|
||||
)
|
||||
|
||||
self._system_config = configobj.ConfigObj(system_config_file)
|
||||
self._global_config = configobj.ConfigObj(global_config_file)
|
||||
self._repo_config = configobj.ConfigObj()
|
||||
self._local_config = configobj.ConfigObj()
|
||||
|
||||
if not self.dvc_dir:
|
||||
return
|
||||
|
||||
config_file = os.path.join(self.dvc_dir, self.CONFIG)
|
||||
config_local_file = os.path.join(self.dvc_dir, self.CONFIG_LOCAL)
|
||||
|
||||
self._repo_config = configobj.ConfigObj(config_file)
|
||||
self._local_config = configobj.ConfigObj(config_local_file)
|
||||
|
||||
@property
|
||||
def config_local_file(self):
|
||||
return self._local_config.filename
|
||||
|
||||
@property
|
||||
def config_file(self):
|
||||
return self._repo_config.filename
|
||||
|
||||
def load(self):
|
||||
def load(self, validate=True):
|
||||
"""Loads config from all the config files.
|
||||
|
||||
Raises:
|
||||
dvc.config.ConfigError: thrown if config has invalid format.
|
||||
"""
|
||||
self._load_configs()
|
||||
conf = {}
|
||||
for level in self.LEVELS:
|
||||
if level in self.files:
|
||||
_merge(conf, self.load_one(level))
|
||||
|
||||
self.config = configobj.ConfigObj()
|
||||
for c in [
|
||||
self._system_config,
|
||||
self._global_config,
|
||||
self._repo_config,
|
||||
self._local_config,
|
||||
]:
|
||||
c = self._resolve_paths(c)
|
||||
c = self._lower(c)
|
||||
self.config.merge(c)
|
||||
if validate:
|
||||
conf = self.validate(conf)
|
||||
|
||||
if not self.should_validate:
|
||||
return
|
||||
self.clear()
|
||||
self.update(conf)
|
||||
|
||||
d = self.validate(self.config)
|
||||
self.config = configobj.ConfigObj(d, write_empty_values=True)
|
||||
# Add resolved default cache.dir
|
||||
if not self["cache"].get("dir"):
|
||||
self["cache"]["dir"] = os.path.join(self.dvc_dir, "cache")
|
||||
|
||||
def save(self, config=None):
|
||||
"""Saves config to config files.
|
||||
def load_one(self, level):
|
||||
conf = _load_config(self.files[level])
|
||||
conf = self._load_paths(conf, self.files[level])
|
||||
|
||||
Raises:
|
||||
dvc.config.ConfigError: thrown if failed to write config file.
|
||||
"""
|
||||
if config is not None:
|
||||
clist = [config]
|
||||
else:
|
||||
clist = [
|
||||
self._system_config,
|
||||
self._global_config,
|
||||
self._repo_config,
|
||||
self._local_config,
|
||||
]
|
||||
# Autovivify sections
|
||||
for key in COMPILED_SCHEMA.schema:
|
||||
conf.setdefault(key, {})
|
||||
|
||||
for conf in clist:
|
||||
self._save(conf)
|
||||
return conf
|
||||
|
||||
@staticmethod
|
||||
def _load_paths(conf, filename):
|
||||
abs_conf_dir = os.path.abspath(os.path.dirname(filename))
|
||||
|
||||
def resolve(path):
|
||||
if os.path.isabs(path) or re.match(r"\w+://", path):
|
||||
return path
|
||||
return RelPath(os.path.join(abs_conf_dir, path))
|
||||
|
||||
return Config._map_dirs(conf, resolve)
|
||||
|
||||
@staticmethod
|
||||
def _save_paths(conf, filename):
|
||||
conf_dir = os.path.dirname(filename)
|
||||
|
||||
def rel(path):
|
||||
if re.match(r"\w+://", path):
|
||||
return path
|
||||
|
||||
if isinstance(path, RelPath) or not os.path.isabs(path):
|
||||
return relpath(path, conf_dir)
|
||||
return path
|
||||
|
||||
return Config._map_dirs(conf, rel)
|
||||
|
||||
@staticmethod
|
||||
def _map_dirs(conf, func):
|
||||
dirs_schema = {"cache": {"dir": func}, "remote": {str: {"url": func}}}
|
||||
return Schema(dirs_schema, extra=ALLOW_EXTRA)(conf)
|
||||
|
||||
@contextmanager
|
||||
def edit(self, level="repo"):
|
||||
if level in {"repo", "local"} and self.dvc_dir is None:
|
||||
raise ConfigError("Not inside a dvc repo")
|
||||
|
||||
conf = self.load_one(level)
|
||||
yield conf
|
||||
|
||||
conf = self._save_paths(conf, self.files[level])
|
||||
_save_config(self.files[level], conf)
|
||||
self.load()
|
||||
|
||||
@staticmethod
|
||||
def _save(config):
|
||||
if config.filename is None:
|
||||
return
|
||||
|
||||
logger.debug("Writing '{}'.".format(config.filename))
|
||||
dname = os.path.dirname(os.path.abspath(config.filename))
|
||||
def validate(data):
|
||||
try:
|
||||
os.makedirs(dname)
|
||||
except OSError as exc:
|
||||
if exc.errno != errno.EEXIST:
|
||||
raise
|
||||
config.write()
|
||||
|
||||
def validate(self, config):
|
||||
try:
|
||||
return self.COMPILED_SCHEMA(config.dict())
|
||||
return COMPILED_SCHEMA(data)
|
||||
except Invalid as exc:
|
||||
raise ConfigError(str(exc)) from exc
|
||||
raise ConfigError(str(exc)) from None
|
||||
|
||||
def unset(self, section, opt=None, level=None, force=False):
|
||||
"""Unsets specified option and/or section in the config.
|
||||
|
||||
Args:
|
||||
section (str): section name.
|
||||
opt (str): optional option name.
|
||||
level (int): config level to use.
|
||||
force (bool): don't error-out even if section doesn't exist. False
|
||||
by default.
|
||||
def _load_config(filename):
|
||||
conf_obj = configobj.ConfigObj(filename)
|
||||
return _parse_remotes(_lower_keys(conf_obj.dict()))
|
||||
|
||||
Raises:
|
||||
dvc.config.ConfigError: thrown if section doesn't exist and
|
||||
`force != True`.
|
||||
"""
|
||||
config = self.get_configobj(level)
|
||||
|
||||
if section not in config.keys():
|
||||
if force:
|
||||
return
|
||||
raise ConfigError("section '{}' doesn't exist".format(section))
|
||||
def _save_config(filename, conf_dict):
|
||||
logger.debug("Writing '{}'.".format(filename))
|
||||
os.makedirs(os.path.dirname(filename), exist_ok=True)
|
||||
|
||||
if opt:
|
||||
if opt not in config[section].keys():
|
||||
if force:
|
||||
return
|
||||
raise ConfigError(
|
||||
"option '{}.{}' doesn't exist".format(section, opt)
|
||||
)
|
||||
del config[section][opt]
|
||||
config = configobj.ConfigObj(_pack_remotes(conf_dict))
|
||||
config.filename = filename
|
||||
config.write()
|
||||
|
||||
if not config[section]:
|
||||
del config[section]
|
||||
|
||||
def _parse_remotes(conf):
|
||||
result = {"remote": {}}
|
||||
|
||||
for section, val in conf.items():
|
||||
name = re_find(r'^\s*remote\s*"(.*)"\s*$', section)
|
||||
if name:
|
||||
result["remote"][name] = val
|
||||
else:
|
||||
del config[section]
|
||||
result[section] = val
|
||||
|
||||
self.save(config)
|
||||
return result
|
||||
|
||||
def set(self, section, opt, value, level=None, force=True):
|
||||
"""Sets specified option in the config.
|
||||
|
||||
Args:
|
||||
section (str): section name.
|
||||
opt (str): option name.
|
||||
value: value to set option to.
|
||||
level (int): config level to use.
|
||||
force (bool): set option even if section already exists. True by
|
||||
default.
|
||||
def _pack_remotes(conf):
|
||||
# Drop empty sections
|
||||
result = compact(conf)
|
||||
|
||||
Raises:
|
||||
dvc.config.ConfigError: thrown if section already exists and
|
||||
`force != True`.
|
||||
# Transform remote.name -> 'remote "name"'
|
||||
for name, val in conf["remote"].items():
|
||||
result['remote "{}"'.format(name)] = val
|
||||
result.pop("remote", None)
|
||||
|
||||
"""
|
||||
config = self.get_configobj(level)
|
||||
return result
|
||||
|
||||
if section not in config.keys():
|
||||
config[section] = {}
|
||||
elif not force:
|
||||
raise ConfigError(
|
||||
"Section '{}' already exists. Use `-f|--force` to overwrite "
|
||||
"section with new value.".format(section)
|
||||
)
|
||||
|
||||
config[section][opt] = value
|
||||
def _merge(into, update):
|
||||
"""Merges second dict into first recursively"""
|
||||
for key, val in update.items():
|
||||
if isinstance(into.get(key), dict) and isinstance(val, dict):
|
||||
_merge(into[key], val)
|
||||
else:
|
||||
into[key] = val
|
||||
|
||||
result = copy.deepcopy(self.config)
|
||||
result.merge(config)
|
||||
self.validate(result)
|
||||
|
||||
self.save(config)
|
||||
|
||||
def get(self, section, opt=None, level=None):
|
||||
"""Return option value from the config.
|
||||
|
||||
Args:
|
||||
section (str): section name.
|
||||
opt (str): option name.
|
||||
level (int): config level to use.
|
||||
|
||||
Returns:
|
||||
value (str, int): option value.
|
||||
"""
|
||||
config = self.get_configobj(level)
|
||||
|
||||
if section not in config.keys():
|
||||
raise ConfigError("section '{}' doesn't exist".format(section))
|
||||
|
||||
if opt not in config[section].keys():
|
||||
raise ConfigError(
|
||||
"option '{}.{}' doesn't exist".format(section, opt)
|
||||
)
|
||||
|
||||
return config[section][opt]
|
||||
|
||||
@staticmethod
|
||||
def _lower(config):
|
||||
new_config = configobj.ConfigObj()
|
||||
for s_key, s_value in config.items():
|
||||
new_s = {}
|
||||
for key, value in s_value.items():
|
||||
new_s[key.lower()] = str(value)
|
||||
new_config[s_key.lower()] = new_s
|
||||
return new_config
|
||||
|
||||
def get_configobj(self, level):
|
||||
configs = {
|
||||
self.LEVEL_LOCAL: self._local_config,
|
||||
self.LEVEL_REPO: self._repo_config,
|
||||
self.LEVEL_GLOBAL: self._global_config,
|
||||
self.LEVEL_SYSTEM: self._system_config,
|
||||
}
|
||||
|
||||
return configs.get(level, self._repo_config)
|
||||
|
||||
def list_options(self, section_regex, option, level=None):
|
||||
ret = {}
|
||||
config = self.get_configobj(level)
|
||||
for section in config.keys():
|
||||
r = re.match(section_regex, section)
|
||||
if r:
|
||||
name = r.group("name")
|
||||
value = config[section].get(option, "")
|
||||
ret[name] = value
|
||||
return ret
|
||||
def _lower_keys(data):
|
||||
return {
|
||||
k.lower(): _lower_keys(v) if isinstance(v, dict) else v
|
||||
for k, v in data.items()
|
||||
}
|
||||
|
||||
+2
-17
@@ -2,7 +2,6 @@
|
||||
|
||||
import logging
|
||||
|
||||
from dvc.config import Config
|
||||
from dvc.config import NoRemoteError
|
||||
from dvc.remote import Remote
|
||||
|
||||
@@ -24,28 +23,14 @@ class DataCloud(object):
|
||||
def __init__(self, repo):
|
||||
self.repo = repo
|
||||
|
||||
@property
|
||||
def _config(self):
|
||||
return self.repo.config.config
|
||||
|
||||
@property
|
||||
def _core(self):
|
||||
return self._config.get(Config.SECTION_CORE, {})
|
||||
|
||||
def get_remote(self, remote=None, command="<command>"):
|
||||
if not remote:
|
||||
remote = self._core.get(Config.SECTION_CORE_REMOTE)
|
||||
remote = self.repo.config["core"].get("remote")
|
||||
|
||||
if remote:
|
||||
return self._init_remote(remote)
|
||||
|
||||
has_non_default_remote = bool(
|
||||
self.repo.config.list_options(
|
||||
Config.SECTION_REMOTE_REGEX, Config.SECTION_REMOTE_URL
|
||||
)
|
||||
)
|
||||
|
||||
if has_non_default_remote:
|
||||
if bool(self.repo.config["remote"]):
|
||||
error_msg = (
|
||||
"no remote specified. Setup default remote with\n"
|
||||
" dvc remote default <remote name>\n"
|
||||
|
||||
+8
-10
@@ -9,11 +9,10 @@ from funcy import retry, suppress, wrap_with, cached_property
|
||||
|
||||
from dvc.compat import fspath
|
||||
from dvc.repo import Repo
|
||||
from dvc.config import Config, NoRemoteError, NotDvcRepoError
|
||||
from dvc.config import NoRemoteError, NotDvcRepoError
|
||||
from dvc.exceptions import NoRemoteInExternalRepoError
|
||||
from dvc.exceptions import OutputNotFoundError, NoOutputInExternalRepoError
|
||||
from dvc.exceptions import FileMissingError, PathMissingError
|
||||
from dvc.remote import RemoteConfig
|
||||
from dvc.utils.fs import remove, fs_copy
|
||||
from dvc.scm.git import Git
|
||||
|
||||
@@ -110,19 +109,18 @@ class ExternalRepo(Repo):
|
||||
# check if the URL is local and no default remote is present
|
||||
# add default remote pointing to the original repo's cache location
|
||||
if os.path.isdir(self.url):
|
||||
rconfig = RemoteConfig(self.config)
|
||||
if not rconfig.has_default():
|
||||
if not self.config["core"].get("remote"):
|
||||
src_repo = Repo(self.url)
|
||||
try:
|
||||
rconfig.add(
|
||||
"auto-generated-upstream",
|
||||
src_repo.cache.local.cache_dir,
|
||||
default=True,
|
||||
level=Config.LEVEL_LOCAL,
|
||||
)
|
||||
cache_dir = src_repo.cache.local.cache_dir
|
||||
finally:
|
||||
src_repo.close()
|
||||
|
||||
self.config["remote"]["auto-generated-upstream"] = {
|
||||
"url": cache_dir
|
||||
}
|
||||
self.config["core"]["remote"] = "auto-generated-upstream"
|
||||
|
||||
|
||||
class ExternalGitRepo:
|
||||
def __init__(self, root_dir, url):
|
||||
|
||||
+40
-7
@@ -1,4 +1,6 @@
|
||||
from .config import RemoteConfig
|
||||
import posixpath
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from dvc.remote.azure import RemoteAZURE
|
||||
from dvc.remote.gdrive import RemoteGDrive
|
||||
from dvc.remote.gs import RemoteGS
|
||||
@@ -25,9 +27,9 @@ REMOTES = [
|
||||
]
|
||||
|
||||
|
||||
def _get(config):
|
||||
def _get(remote_conf):
|
||||
for remote in REMOTES:
|
||||
if remote.supported(config):
|
||||
if remote.supported(remote_conf):
|
||||
return remote
|
||||
return RemoteLOCAL
|
||||
|
||||
@@ -35,8 +37,39 @@ def _get(config):
|
||||
def Remote(repo, **kwargs):
|
||||
name = kwargs.get("name")
|
||||
if name:
|
||||
remote_config = RemoteConfig(repo.config)
|
||||
settings = remote_config.get_settings(name)
|
||||
remote_conf = repo.config["remote"][name.lower()]
|
||||
else:
|
||||
settings = kwargs
|
||||
return _get(settings)(repo, settings)
|
||||
remote_conf = kwargs
|
||||
remote_conf = _resolve_remote_refs(repo.config, remote_conf)
|
||||
return _get(remote_conf)(repo, remote_conf)
|
||||
|
||||
|
||||
def _resolve_remote_refs(config, remote_conf):
|
||||
# Support for cross referenced remotes.
|
||||
# This will merge the settings, shadowing base ref with remote_conf.
|
||||
# For example, having:
|
||||
#
|
||||
# dvc remote add server ssh://localhost
|
||||
# dvc remote modify server user root
|
||||
# dvc remote modify server ask_password true
|
||||
#
|
||||
# dvc remote add images remote://server/tmp/pictures
|
||||
# dvc remote modify images user alice
|
||||
# dvc remote modify images ask_password false
|
||||
# dvc remote modify images password asdf1234
|
||||
#
|
||||
# Results on a config dictionary like:
|
||||
#
|
||||
# {
|
||||
# "url": "ssh://localhost/tmp/pictures",
|
||||
# "user": "alice",
|
||||
# "password": "asdf1234",
|
||||
# "ask_password": False,
|
||||
# }
|
||||
parsed = urlparse(remote_conf["url"])
|
||||
if parsed.scheme != "remote":
|
||||
return remote_conf
|
||||
|
||||
base = config["remote"][parsed.netloc]
|
||||
url = posixpath.join(base["url"], parsed.path.lstrip("/"))
|
||||
return {**base, **remote_conf, "url": url}
|
||||
|
||||
+2
-3
@@ -7,7 +7,6 @@ import threading
|
||||
|
||||
from funcy import cached_property, wrap_prop
|
||||
|
||||
from dvc.config import Config
|
||||
from dvc.path_info import CloudURLInfo
|
||||
from dvc.progress import Tqdm
|
||||
from dvc.remote.base import RemoteBASE
|
||||
@@ -33,7 +32,7 @@ class RemoteAZURE(RemoteBASE):
|
||||
def __init__(self, repo, config):
|
||||
super().__init__(repo, config)
|
||||
|
||||
url = config.get(Config.SECTION_REMOTE_URL, "azure://")
|
||||
url = config.get("url", "azure://")
|
||||
|
||||
match = re.match(self.REGEX, url) # backward compatibility
|
||||
path = match.group("path")
|
||||
@@ -44,7 +43,7 @@ class RemoteAZURE(RemoteBASE):
|
||||
)
|
||||
|
||||
self.connection_string = (
|
||||
config.get(Config.SECTION_AZURE_CONNECTION_STRING)
|
||||
config.get("connection_string")
|
||||
or match.group("connection_string") # backward compatibility
|
||||
or os.getenv("AZURE_STORAGE_CONNECTION_STRING")
|
||||
)
|
||||
|
||||
+10
-33
@@ -13,7 +13,6 @@ from operator import itemgetter
|
||||
from shortuuid import uuid
|
||||
|
||||
import dvc.prompt as prompt
|
||||
from dvc.config import Config
|
||||
from dvc.exceptions import (
|
||||
DvcException,
|
||||
ConfirmRemoveError,
|
||||
@@ -89,24 +88,18 @@ class RemoteBASE(object):
|
||||
self.repo = repo
|
||||
|
||||
self._check_requires(config)
|
||||
self.checksum_jobs = self._get_checksum_jobs(config)
|
||||
|
||||
self.checksum_jobs = (
|
||||
config.get("checksum_jobs")
|
||||
or (self.repo and self.repo.config["core"].get("checksum_jobs"))
|
||||
or self.CHECKSUM_JOBS
|
||||
)
|
||||
self.protected = False
|
||||
self.no_traverse = config.get(
|
||||
Config.SECTION_REMOTE_NO_TRAVERSE, self.DEFAULT_NO_TRAVERSE
|
||||
)
|
||||
self.verify = config.get(
|
||||
Config.SECTION_REMOTE_VERIFY, self.DEFAULT_VERIFY
|
||||
)
|
||||
self.no_traverse = config.get("no_traverse", self.DEFAULT_NO_TRAVERSE)
|
||||
self.verify = config.get("verify", self.DEFAULT_VERIFY)
|
||||
self._dir_info = {}
|
||||
|
||||
types = config.get(Config.SECTION_CACHE_TYPE, None)
|
||||
if types:
|
||||
if isinstance(types, str):
|
||||
types = [t.strip() for t in types.split(",")]
|
||||
self.cache_types = types
|
||||
else:
|
||||
self.cache_types = copy(self.DEFAULT_CACHE_TYPES)
|
||||
self.cache_types = config.get("type") or copy(self.DEFAULT_CACHE_TYPES)
|
||||
self.cache_type_confirmed = False
|
||||
|
||||
def _check_requires(self, config):
|
||||
@@ -123,9 +116,7 @@ class RemoteBASE(object):
|
||||
if not missing:
|
||||
return
|
||||
|
||||
url = config.get(
|
||||
Config.SECTION_REMOTE_URL, "{}://".format(self.scheme)
|
||||
)
|
||||
url = config.get("url", "{}://".format(self.scheme))
|
||||
msg = (
|
||||
"URL '{}' is supported but requires these missing "
|
||||
"dependencies: {}. If you have installed dvc using pip, "
|
||||
@@ -146,19 +137,6 @@ class RemoteBASE(object):
|
||||
).format(url, missing, " ".join(missing), self.scheme)
|
||||
raise RemoteMissingDepsError(msg)
|
||||
|
||||
def _get_checksum_jobs(self, config):
|
||||
checksum_jobs = config.get(Config.SECTION_REMOTE_CHECKSUM_JOBS)
|
||||
if checksum_jobs:
|
||||
return checksum_jobs
|
||||
|
||||
if self.repo:
|
||||
core = self.repo.config.config.get(Config.SECTION_CORE, {})
|
||||
return core.get(
|
||||
Config.SECTION_CORE_CHECKSUM_JOBS, self.CHECKSUM_JOBS
|
||||
)
|
||||
|
||||
return self.CHECKSUM_JOBS
|
||||
|
||||
def __repr__(self):
|
||||
return "{class_name}: '{path_info}'".format(
|
||||
class_name=type(self).__name__,
|
||||
@@ -170,7 +148,7 @@ class RemoteBASE(object):
|
||||
if isinstance(config, (str, bytes)):
|
||||
url = config
|
||||
else:
|
||||
url = config[Config.SECTION_REMOTE_URL]
|
||||
url = config["url"]
|
||||
|
||||
# NOTE: silently skipping remote, calling code should handle that
|
||||
parsed = urlparse(url)
|
||||
@@ -893,7 +871,6 @@ class RemoteBASE(object):
|
||||
"""Optional: Implement only if the remote needs to create
|
||||
directories before copying/linking/moving data
|
||||
"""
|
||||
pass
|
||||
|
||||
def _checkout_dir(
|
||||
self,
|
||||
|
||||
@@ -1,163 +0,0 @@
|
||||
import logging
|
||||
import os
|
||||
import posixpath
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from dvc.config import Config, ConfigError
|
||||
from dvc.utils import relpath
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class RemoteConfig(object):
|
||||
def __init__(self, config):
|
||||
self.config = config
|
||||
|
||||
def get_settings(self, name):
|
||||
"""
|
||||
Args:
|
||||
name (str): The name of the remote that we want to retrieve
|
||||
|
||||
Returns:
|
||||
dict: The content beneath the given remote name.
|
||||
|
||||
Example:
|
||||
>>> config = {'remote "server"': {'url': 'ssh://localhost/'}}
|
||||
>>> get_settings("server")
|
||||
{'url': 'ssh://localhost/'}
|
||||
"""
|
||||
settings = self.config.config.get(
|
||||
Config.SECTION_REMOTE_FMT.format(name.lower())
|
||||
)
|
||||
|
||||
if settings is None:
|
||||
raise ConfigError(
|
||||
"Unable to find remote section '{}'".format(name)
|
||||
)
|
||||
|
||||
parsed = urlparse(settings["url"])
|
||||
|
||||
# Support for cross referenced remotes.
|
||||
# This will merge the settings, giving priority to the outer reference.
|
||||
# For example, having:
|
||||
#
|
||||
# dvc remote add server ssh://localhost
|
||||
# dvc remote modify server user root
|
||||
# dvc remote modify server ask_password true
|
||||
#
|
||||
# dvc remote add images remote://server/tmp/pictures
|
||||
# dvc remote modify images user alice
|
||||
# dvc remote modify images ask_password false
|
||||
# dvc remote modify images password asdf1234
|
||||
#
|
||||
# Results on a config dictionary like:
|
||||
#
|
||||
# {
|
||||
# "url": "ssh://localhost/tmp/pictures",
|
||||
# "user": "alice",
|
||||
# "password": "asdf1234",
|
||||
# "ask_password": False,
|
||||
# }
|
||||
#
|
||||
if parsed.scheme == "remote":
|
||||
reference = self.get_settings(parsed.netloc)
|
||||
url = posixpath.join(reference["url"], parsed.path.lstrip("/"))
|
||||
merged = reference.copy()
|
||||
merged.update(settings)
|
||||
merged["url"] = url
|
||||
return merged
|
||||
|
||||
return settings
|
||||
|
||||
@staticmethod
|
||||
def resolve_path(path, config_file):
|
||||
"""Resolve path relative to config file location.
|
||||
|
||||
Args:
|
||||
path: Path to be resolved.
|
||||
config_file: Path to config file, which `path` is specified
|
||||
relative to.
|
||||
|
||||
Returns:
|
||||
Path relative to the `config_file` location. If `path` is an
|
||||
absolute path then it will be returned without change.
|
||||
|
||||
"""
|
||||
if os.path.isabs(path):
|
||||
return path
|
||||
return relpath(path, os.path.dirname(config_file))
|
||||
|
||||
def add(self, name, url, default=False, force=False, level=None):
|
||||
from dvc.remote import _get, RemoteLOCAL
|
||||
|
||||
configobj = self.config.get_configobj(level)
|
||||
remote = _get({Config.SECTION_REMOTE_URL: url})
|
||||
if remote == RemoteLOCAL and not url.startswith("remote://"):
|
||||
url = self.resolve_path(url, configobj.filename)
|
||||
|
||||
self.config.set(
|
||||
Config.SECTION_REMOTE_FMT.format(name),
|
||||
Config.SECTION_REMOTE_URL,
|
||||
url,
|
||||
force=force,
|
||||
level=level,
|
||||
)
|
||||
if default:
|
||||
self.config.set(
|
||||
Config.SECTION_CORE,
|
||||
Config.SECTION_CORE_REMOTE,
|
||||
name,
|
||||
level=level,
|
||||
)
|
||||
|
||||
def remove(self, name, level=None):
|
||||
self.config.unset(Config.SECTION_REMOTE_FMT.format(name), level=level)
|
||||
|
||||
if level is None:
|
||||
level = Config.LEVEL_REPO
|
||||
|
||||
for lev in [
|
||||
Config.LEVEL_LOCAL,
|
||||
Config.LEVEL_REPO,
|
||||
Config.LEVEL_GLOBAL,
|
||||
Config.LEVEL_SYSTEM,
|
||||
]:
|
||||
self.config.unset(
|
||||
Config.SECTION_CORE,
|
||||
Config.SECTION_CORE_REMOTE,
|
||||
level=lev,
|
||||
force=True,
|
||||
)
|
||||
if lev == level:
|
||||
break
|
||||
|
||||
def modify(self, name, option, value, level=None):
|
||||
# try to get remote settings to verify that section exists
|
||||
self.get_settings(name)
|
||||
|
||||
self.config.set(
|
||||
Config.SECTION_REMOTE_FMT.format(name), option, value, level=level
|
||||
)
|
||||
|
||||
def list(self, level=None):
|
||||
return self.config.list_options(
|
||||
Config.SECTION_REMOTE_REGEX, Config.SECTION_REMOTE_URL, level=level
|
||||
)
|
||||
|
||||
def set_default(self, name, unset=False, level=None):
|
||||
if unset:
|
||||
self.config.unset(Config.SECTION_CORE, Config.SECTION_CORE_REMOTE)
|
||||
return
|
||||
self.config.set(
|
||||
Config.SECTION_CORE, Config.SECTION_CORE_REMOTE, name, level=level
|
||||
)
|
||||
|
||||
def get_default(self, level=None):
|
||||
return self.config.get(
|
||||
Config.SECTION_CORE, Config.SECTION_CORE_REMOTE, level=level
|
||||
)
|
||||
|
||||
def has_default(self):
|
||||
core = self.config.config[Config.SECTION_CORE]
|
||||
return bool(core.get(Config.SECTION_CORE_REMOTE))
|
||||
+7
-13
@@ -12,7 +12,6 @@ from dvc.progress import Tqdm
|
||||
from dvc.scheme import Schemes
|
||||
from dvc.path_info import CloudURLInfo
|
||||
from dvc.remote.base import RemoteBASE
|
||||
from dvc.config import Config
|
||||
from dvc.exceptions import DvcException
|
||||
from dvc.utils import tmp_fname, format_link
|
||||
|
||||
@@ -95,25 +94,20 @@ class RemoteGDrive(RemoteBASE):
|
||||
|
||||
def __init__(self, repo, config):
|
||||
super().__init__(repo, config)
|
||||
url = config[Config.SECTION_REMOTE_URL]
|
||||
self.path_info = self.path_cls(url)
|
||||
self.config = config
|
||||
self.path_info = self.path_cls(config["url"])
|
||||
|
||||
if not self.path_info.bucket:
|
||||
raise DvcException(
|
||||
"Empty Google Drive URL '{}'. Learn more at "
|
||||
"{}.".format(
|
||||
url, format_link("https://man.dvc.org/remote/add")
|
||||
config["url"],
|
||||
format_link("https://man.dvc.org/remote/add"),
|
||||
)
|
||||
)
|
||||
|
||||
self._bucket = self.path_info.bucket
|
||||
self._client_id = self.config.get(
|
||||
Config.SECTION_GDRIVE_CLIENT_ID, None
|
||||
)
|
||||
self._client_secret = self.config.get(
|
||||
Config.SECTION_GDRIVE_CLIENT_SECRET, None
|
||||
)
|
||||
self._client_id = config.get("gdrive_client_id")
|
||||
self._client_secret = config.get("gdrive_client_secret")
|
||||
if not self._client_id or not self._client_secret:
|
||||
raise DvcException(
|
||||
"Please specify Google Drive's client id and "
|
||||
@@ -123,8 +117,8 @@ class RemoteGDrive(RemoteBASE):
|
||||
self._gdrive_user_credentials_path = (
|
||||
tmp_fname(os.path.join(self.repo.tmp_dir, ""))
|
||||
if os.getenv(RemoteGDrive.GDRIVE_USER_CREDENTIALS_DATA)
|
||||
else self.config.get(
|
||||
Config.SECTION_GDRIVE_USER_CREDENTIALS_FILE,
|
||||
else config.get(
|
||||
"gdrive_user_credentials_file",
|
||||
os.path.join(
|
||||
self.repo.tmp_dir, self.DEFAULT_USER_CREDENTIALS_FILE
|
||||
),
|
||||
|
||||
+3
-4
@@ -7,7 +7,6 @@ import threading
|
||||
|
||||
from funcy import cached_property, wrap_prop
|
||||
|
||||
from dvc.config import Config
|
||||
from dvc.exceptions import DvcException
|
||||
from dvc.path_info import CloudURLInfo
|
||||
from dvc.progress import Tqdm
|
||||
@@ -75,11 +74,11 @@ class RemoteGS(RemoteBASE):
|
||||
def __init__(self, repo, config):
|
||||
super().__init__(repo, config)
|
||||
|
||||
url = config.get(Config.SECTION_REMOTE_URL, "gs:///")
|
||||
url = config.get("url", "gs:///")
|
||||
self.path_info = self.path_cls(url)
|
||||
|
||||
self.projectname = config.get(Config.SECTION_GCP_PROJECTNAME, None)
|
||||
self.credentialpath = config.get(Config.SECTION_GCP_CREDENTIALPATH)
|
||||
self.projectname = config.get("projectname", None)
|
||||
self.credentialpath = config.get("credentialpath")
|
||||
|
||||
@wrap_prop(threading.Lock())
|
||||
@cached_property
|
||||
|
||||
+2
-4
@@ -10,7 +10,6 @@ from urllib.parse import urlparse
|
||||
|
||||
from .base import RemoteBASE, RemoteCmdError
|
||||
from .pool import get_connection
|
||||
from dvc.config import Config
|
||||
from dvc.scheme import Schemes
|
||||
from dvc.utils import fix_env, tmp_fname
|
||||
|
||||
@@ -26,13 +25,12 @@ class RemoteHDFS(RemoteBASE):
|
||||
def __init__(self, repo, config):
|
||||
super().__init__(repo, config)
|
||||
self.path_info = None
|
||||
url = config.get(Config.SECTION_REMOTE_URL)
|
||||
url = config.get("url")
|
||||
if not url:
|
||||
return
|
||||
|
||||
parsed = urlparse(url)
|
||||
|
||||
user = parsed.username or config.get(Config.SECTION_REMOTE_USER)
|
||||
user = parsed.username or config.get("user")
|
||||
|
||||
self.path_info = self.path_cls.from_parts(
|
||||
scheme=self.scheme,
|
||||
|
||||
+1
-2
@@ -3,7 +3,6 @@ import threading
|
||||
|
||||
from funcy import cached_property, wrap_prop
|
||||
|
||||
from dvc.config import Config
|
||||
from dvc.config import ConfigError
|
||||
from dvc.exceptions import DvcException, HTTPError
|
||||
from dvc.progress import Tqdm
|
||||
@@ -24,7 +23,7 @@ class RemoteHTTP(RemoteBASE):
|
||||
def __init__(self, repo, config):
|
||||
super().__init__(repo, config)
|
||||
|
||||
url = config.get(Config.SECTION_REMOTE_URL)
|
||||
url = config.get("url")
|
||||
self.path_info = self.path_cls(url) if url else None
|
||||
|
||||
if not self.no_traverse:
|
||||
|
||||
+9
-25
@@ -7,27 +7,17 @@ from functools import partial
|
||||
|
||||
from shortuuid import uuid
|
||||
|
||||
from dvc.config import Config
|
||||
from dvc.exceptions import DownloadError
|
||||
from dvc.exceptions import DvcException
|
||||
from dvc.exceptions import UploadError
|
||||
from dvc.compat import fspath_py35
|
||||
from dvc.exceptions import DvcException, DownloadError, UploadError
|
||||
from dvc.path_info import PathInfo
|
||||
from dvc.progress import Tqdm
|
||||
from dvc.remote.base import RemoteBASE
|
||||
from dvc.remote.base import STATUS_DELETED
|
||||
from dvc.remote.base import STATUS_MAP
|
||||
from dvc.remote.base import STATUS_MISSING
|
||||
from dvc.remote.base import STATUS_NEW
|
||||
from dvc.remote.base import RemoteBASE, STATUS_MAP
|
||||
from dvc.remote.base import STATUS_DELETED, STATUS_MISSING, STATUS_NEW
|
||||
from dvc.scheme import Schemes
|
||||
from dvc.scm.tree import is_working_tree
|
||||
from dvc.system import System
|
||||
from dvc.utils.fs import copyfile
|
||||
from dvc.utils import file_md5
|
||||
from dvc.utils import relpath
|
||||
from dvc.utils import tmp_fname
|
||||
from dvc.compat import fspath_py35
|
||||
from dvc.utils.fs import move, makedirs, remove
|
||||
from dvc.utils.fs import walk_files
|
||||
from dvc.utils import file_md5, relpath, tmp_fname
|
||||
from dvc.utils.fs import copyfile, move, makedirs, remove, walk_files
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -46,22 +36,16 @@ class RemoteLOCAL(RemoteBASE):
|
||||
|
||||
def __init__(self, repo, config):
|
||||
super().__init__(repo, config)
|
||||
self.protected = config.get(Config.SECTION_CACHE_PROTECTED, False)
|
||||
self.protected = config.get("protected", False)
|
||||
|
||||
shared = config.get(Config.SECTION_CACHE_SHARED)
|
||||
shared = config.get("shared")
|
||||
self._file_mode, self._dir_mode = self.SHARED_MODE_MAP[shared]
|
||||
|
||||
if self.protected:
|
||||
# cache files are set to be read-only for everyone
|
||||
self._file_mode = stat.S_IREAD | stat.S_IRGRP | stat.S_IROTH
|
||||
|
||||
cache_dir = config.get(Config.SECTION_REMOTE_URL)
|
||||
|
||||
if cache_dir is not None and not os.path.isabs(cache_dir):
|
||||
cwd = config[Config.PRIVATE_CWD]
|
||||
cache_dir = os.path.abspath(os.path.join(cwd, cache_dir))
|
||||
|
||||
self.cache_dir = cache_dir
|
||||
self.cache_dir = config.get("url")
|
||||
self._dir_info = {}
|
||||
|
||||
@property
|
||||
|
||||
+4
-7
@@ -4,7 +4,6 @@ import threading
|
||||
|
||||
from funcy import cached_property, wrap_prop
|
||||
|
||||
from dvc.config import Config
|
||||
from dvc.path_info import CloudURLInfo
|
||||
from dvc.progress import Tqdm
|
||||
from dvc.remote.base import RemoteBASE
|
||||
@@ -42,21 +41,19 @@ class RemoteOSS(RemoteBASE):
|
||||
def __init__(self, repo, config):
|
||||
super().__init__(repo, config)
|
||||
|
||||
url = config.get(Config.SECTION_REMOTE_URL)
|
||||
url = config.get("url")
|
||||
self.path_info = self.path_cls(url) if url else None
|
||||
|
||||
self.endpoint = config.get(Config.SECTION_OSS_ENDPOINT) or os.getenv(
|
||||
"OSS_ENDPOINT"
|
||||
)
|
||||
self.endpoint = config.get("oss_endpoint") or os.getenv("OSS_ENDPOINT")
|
||||
|
||||
self.key_id = (
|
||||
config.get(Config.SECTION_OSS_ACCESS_KEY_ID)
|
||||
config.get("oss_key_id")
|
||||
or os.getenv("OSS_ACCESS_KEY_ID")
|
||||
or "defaultId"
|
||||
)
|
||||
|
||||
self.key_secret = (
|
||||
config.get(Config.SECTION_OSS_ACCESS_KEY_SECRET)
|
||||
config.get("oss_key_secret")
|
||||
or os.getenv("OSS_ACCESS_KEY_SECRET")
|
||||
or "defaultSecret"
|
||||
)
|
||||
|
||||
+15
-17
@@ -6,7 +6,7 @@ import threading
|
||||
|
||||
from funcy import cached_property, wrap_prop
|
||||
|
||||
from dvc.config import Config, ConfigError
|
||||
from dvc.config import ConfigError
|
||||
from dvc.exceptions import DvcException
|
||||
from dvc.exceptions import ETagMismatchError
|
||||
from dvc.path_info import CloudURLInfo
|
||||
@@ -26,35 +26,33 @@ class RemoteS3(RemoteBASE):
|
||||
def __init__(self, repo, config):
|
||||
super().__init__(repo, config)
|
||||
|
||||
url = config.get(Config.SECTION_REMOTE_URL, "s3://")
|
||||
url = config.get("url", "s3://")
|
||||
self.path_info = self.path_cls(url)
|
||||
|
||||
self.region = config.get(Config.SECTION_AWS_REGION)
|
||||
self.region = config.get("region")
|
||||
self.profile = config.get("profile")
|
||||
self.endpoint_url = config.get("endpointurl")
|
||||
|
||||
self.profile = config.get(Config.SECTION_AWS_PROFILE)
|
||||
|
||||
self.endpoint_url = config.get(Config.SECTION_AWS_ENDPOINT_URL)
|
||||
|
||||
if config.get(Config.SECTION_AWS_LIST_OBJECTS):
|
||||
if config.get("listobjects"):
|
||||
self.list_objects_api = "list_objects"
|
||||
else:
|
||||
self.list_objects_api = "list_objects_v2"
|
||||
|
||||
self.use_ssl = config.get(Config.SECTION_AWS_USE_SSL, True)
|
||||
self.use_ssl = config.get("use_ssl", True)
|
||||
|
||||
self.extra_args = {}
|
||||
|
||||
self.sse = config.get(Config.SECTION_AWS_SSE, "")
|
||||
self.sse = config.get("sse")
|
||||
if self.sse:
|
||||
self.extra_args["ServerSideEncryption"] = self.sse
|
||||
|
||||
self.acl = config.get(Config.SECTION_AWS_ACL, "")
|
||||
self.acl = config.get("acl")
|
||||
if self.acl:
|
||||
self.extra_args["ACL"] = self.acl
|
||||
|
||||
self._append_aws_grants_to_extra_args(config)
|
||||
|
||||
shared_creds = config.get(Config.SECTION_AWS_CREDENTIALPATH)
|
||||
shared_creds = config.get("credentialpath")
|
||||
if shared_creds:
|
||||
os.environ.setdefault("AWS_SHARED_CREDENTIALS_FILE", shared_creds)
|
||||
|
||||
@@ -322,14 +320,14 @@ class RemoteS3(RemoteBASE):
|
||||
"""
|
||||
|
||||
grants = {
|
||||
Config.SECTION_AWS_GRANT_FULL_CONTROL: "GrantFullControl",
|
||||
Config.SECTION_AWS_GRANT_READ: "GrantRead",
|
||||
Config.SECTION_AWS_GRANT_READ_ACP: "GrantReadACP",
|
||||
Config.SECTION_AWS_GRANT_WRITE_ACP: "GrantWriteACP",
|
||||
"grant_full_control": "GrantFullControl",
|
||||
"grant_read": "GrantRead",
|
||||
"grant_read_acp": "GrantReadACP",
|
||||
"grant_write_acp": "GrantWriteACP",
|
||||
}
|
||||
|
||||
for grant_option, extra_args_key in grants.items():
|
||||
if config.get(grant_option, ""):
|
||||
if config.get(grant_option):
|
||||
if self.acl:
|
||||
raise ConfigError(
|
||||
"`acl` and `grant_*` AWS S3 config options "
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import logging
|
||||
import sys
|
||||
import time
|
||||
|
||||
from functools import wraps
|
||||
|
||||
import colorama
|
||||
|
||||
from dvc.config import Config
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
this = sys.modules[__name__]
|
||||
@@ -31,11 +29,9 @@ def slow_link_guard(f):
|
||||
if this.already_displayed:
|
||||
return f(remote, *args, **kwargs)
|
||||
|
||||
config = remote.repo.config.config.get(Config.SECTION_CACHE, {})
|
||||
cache_type = config.get(Config.SECTION_CACHE_TYPE)
|
||||
should_warn = config.get(Config.SECTION_CACHE_SLOW_LINK_WARNING, True)
|
||||
|
||||
if not should_warn or cache_type:
|
||||
cache_conf = remote.repo.config["cache"]
|
||||
slow_link_warning = cache_conf.get("slow_link_warning", True)
|
||||
if not slow_link_warning or cache_conf.get("type"):
|
||||
return f(remote, *args, **kwargs)
|
||||
|
||||
start = time.time()
|
||||
|
||||
+11
-20
@@ -9,10 +9,9 @@ from concurrent.futures import ThreadPoolExecutor
|
||||
from contextlib import closing, contextmanager
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from funcy import memoize, wrap_with
|
||||
from funcy import memoize, wrap_with, silent, first
|
||||
|
||||
import dvc.prompt as prompt
|
||||
from dvc.config import Config
|
||||
from dvc.progress import Tqdm
|
||||
from dvc.remote.base import RemoteBASE
|
||||
from dvc.remote.pool import get_connection
|
||||
@@ -50,20 +49,20 @@ class RemoteSSH(RemoteBASE):
|
||||
|
||||
def __init__(self, repo, config):
|
||||
super().__init__(repo, config)
|
||||
url = config.get(Config.SECTION_REMOTE_URL)
|
||||
url = config.get("url")
|
||||
if url:
|
||||
parsed = urlparse(url)
|
||||
user_ssh_config = self._load_user_ssh_config(parsed.hostname)
|
||||
|
||||
host = user_ssh_config.get("hostname", parsed.hostname)
|
||||
user = (
|
||||
config.get(Config.SECTION_REMOTE_USER)
|
||||
config.get("user")
|
||||
or parsed.username
|
||||
or user_ssh_config.get("user")
|
||||
or getpass.getuser()
|
||||
)
|
||||
port = (
|
||||
config.get(Config.SECTION_REMOTE_PORT)
|
||||
config.get("port")
|
||||
or parsed.port
|
||||
or self._try_get_ssh_config_port(user_ssh_config)
|
||||
or self.DEFAULT_PORT
|
||||
@@ -80,14 +79,12 @@ class RemoteSSH(RemoteBASE):
|
||||
user_ssh_config = {}
|
||||
|
||||
self.keyfile = config.get(
|
||||
Config.SECTION_REMOTE_KEY_FILE
|
||||
"keyfile"
|
||||
) or self._try_get_ssh_config_keyfile(user_ssh_config)
|
||||
self.timeout = config.get(Config.SECTION_REMOTE_TIMEOUT, self.TIMEOUT)
|
||||
self.password = config.get(Config.SECTION_REMOTE_PASSWORD, None)
|
||||
self.ask_password = config.get(
|
||||
Config.SECTION_REMOTE_ASK_PASSWORD, False
|
||||
)
|
||||
self.gss_auth = config.get(Config.SECTION_REMOTE_GSS_AUTH, False)
|
||||
self.timeout = config.get("timeout", self.TIMEOUT)
|
||||
self.password = config.get("password", None)
|
||||
self.ask_password = config.get("ask_password", False)
|
||||
self.gss_auth = config.get("gss_auth", False)
|
||||
|
||||
@staticmethod
|
||||
def ssh_config_filename():
|
||||
@@ -110,17 +107,11 @@ class RemoteSSH(RemoteBASE):
|
||||
|
||||
@staticmethod
|
||||
def _try_get_ssh_config_port(user_ssh_config):
|
||||
try:
|
||||
return int(user_ssh_config.get("port"))
|
||||
except (ValueError, TypeError):
|
||||
return None
|
||||
return silent(int)(user_ssh_config.get("port"))
|
||||
|
||||
@staticmethod
|
||||
def _try_get_ssh_config_keyfile(user_ssh_config):
|
||||
identity_file = user_ssh_config.get("identityfile")
|
||||
if identity_file and len(identity_file) > 0:
|
||||
return identity_file[0]
|
||||
return None
|
||||
return first(user_ssh_config.get("identityfile") or ())
|
||||
|
||||
def ensure_credentials(self, path_info=None):
|
||||
if path_info is None:
|
||||
|
||||
@@ -88,7 +88,7 @@ class Repo(object):
|
||||
self.tmp_dir = os.path.join(self.dvc_dir, "tmp")
|
||||
makedirs(self.tmp_dir, exist_ok=True)
|
||||
|
||||
hardlink_lock = self.config.config["core"].get("hardlink_lock", False)
|
||||
hardlink_lock = self.config["core"].get("hardlink_lock", False)
|
||||
self.lock = make_lock(
|
||||
os.path.join(self.dvc_dir, "lock"),
|
||||
tmp_dir=os.path.join(self.dvc_dir, "tmp"),
|
||||
@@ -98,11 +98,9 @@ class Repo(object):
|
||||
|
||||
# NOTE: storing state and link_state in the repository itself to avoid
|
||||
# any possible state corruption in 'shared cache dir' scenario.
|
||||
self.state = State(self, self.config.config)
|
||||
self.state = State(self)
|
||||
|
||||
core = self.config.config[Config.SECTION_CORE]
|
||||
|
||||
level = core.get(Config.SECTION_CORE_LOGLEVEL)
|
||||
level = self.config.get("core", {}).get("loglevel")
|
||||
if level:
|
||||
logger.setLevel(level.upper())
|
||||
|
||||
@@ -170,7 +168,7 @@ class Repo(object):
|
||||
updater = Updater(self.dvc_dir)
|
||||
|
||||
flist = (
|
||||
[self.config.config_local_file, updater.updater_file]
|
||||
[self.config.files["local"], updater.updater_file]
|
||||
+ [self.lock.lockfile, updater.lock.lockfile, self.tmp_dir]
|
||||
+ self.state.files
|
||||
)
|
||||
|
||||
+1
-1
@@ -89,7 +89,7 @@ def init(root_dir=os.curdir, no_scm=False, force=False):
|
||||
config = Config.init(dvc_dir)
|
||||
proj = Repo(root_dir)
|
||||
|
||||
scm.add([config.config_file])
|
||||
scm.add([config.files["repo"]])
|
||||
|
||||
if scm.ignore_file:
|
||||
scm.add([os.path.join(dvc_dir, scm.ignore_file)])
|
||||
|
||||
@@ -65,11 +65,7 @@ def reproduce(
|
||||
|
||||
interactive = kwargs.get("interactive", False)
|
||||
if not interactive:
|
||||
config = self.config
|
||||
core = config.config[config.SECTION_CORE]
|
||||
kwargs["interactive"] = core.get(
|
||||
config.SECTION_CORE_INTERACTIVE, False
|
||||
)
|
||||
kwargs["interactive"] = self.config["core"].get("interactive", False)
|
||||
|
||||
active_graph = _get_active_graph(self.graph)
|
||||
active_pipelines = get_pipelines(active_graph)
|
||||
|
||||
+4
-8
@@ -6,7 +6,6 @@ import re
|
||||
import sqlite3
|
||||
from urllib.parse import urlunparse, urlencode
|
||||
|
||||
from dvc.config import Config
|
||||
from dvc.exceptions import DvcException
|
||||
from dvc.utils import current_timestamp
|
||||
from dvc.utils import relpath
|
||||
@@ -95,18 +94,15 @@ class State(object): # pylint: disable=too-many-instance-attributes
|
||||
MAX_INT = 2 ** 63 - 1
|
||||
MAX_UINT = 2 ** 64 - 2
|
||||
|
||||
def __init__(self, repo, config):
|
||||
def __init__(self, repo):
|
||||
self.repo = repo
|
||||
self.dvc_dir = repo.dvc_dir
|
||||
self.root_dir = repo.root_dir
|
||||
|
||||
state_config = config.get(Config.SECTION_STATE, {})
|
||||
self.row_limit = state_config.get(
|
||||
Config.SECTION_STATE_ROW_LIMIT, self.STATE_ROW_LIMIT
|
||||
)
|
||||
state_config = repo.config.get("state", {})
|
||||
self.row_limit = state_config.get("row_limit", self.STATE_ROW_LIMIT)
|
||||
self.row_cleanup_quota = state_config.get(
|
||||
Config.SECTION_STATE_ROW_CLEANUP_QUOTA,
|
||||
self.STATE_ROW_CLEANUP_QUOTA,
|
||||
"row_cleanup_quota", self.STATE_ROW_CLEANUP_QUOTA
|
||||
)
|
||||
|
||||
if not self.dvc_dir:
|
||||
|
||||
@@ -264,15 +264,15 @@ def run_copy(tmp_dir, dvc):
|
||||
|
||||
@pytest.fixture
|
||||
def erepo_dir(make_tmp_dir):
|
||||
from dvc.remote.config import RemoteConfig
|
||||
|
||||
path = make_tmp_dir("erepo", scm=True, dvc=True)
|
||||
|
||||
# Chdir for git and dvc to work locally
|
||||
with path.chdir():
|
||||
rconfig = RemoteConfig(path.dvc.config)
|
||||
rconfig.add("upstream", path.dvc.cache.local.cache_dir, default=True)
|
||||
path.scm_add([path.dvc.config.config_file], commit="add remote")
|
||||
with path.dvc.config.edit() as conf:
|
||||
cache_dir = path.dvc.cache.local.cache_dir
|
||||
conf["remote"]["upstream"] = {"url": cache_dir}
|
||||
conf["core"]["remote"] = "upstream"
|
||||
path.scm_add([path.dvc.config.files["repo"]], commit="add remote")
|
||||
|
||||
return path
|
||||
|
||||
|
||||
@@ -546,7 +546,7 @@ def temporary_windows_drive(tmp_path_factory):
|
||||
def test_windows_should_add_when_cache_on_different_drive(
|
||||
tmp_dir, dvc, temporary_windows_drive
|
||||
):
|
||||
dvc.config.set("cache", "dir", temporary_windows_drive)
|
||||
dvc.config["cache"]["dir"] = temporary_windows_drive
|
||||
dvc.cache = Cache(dvc)
|
||||
|
||||
(stage,) = tmp_dir.dvc_gen({"file": "file"})
|
||||
@@ -601,7 +601,7 @@ def test_should_relink_on_repeated_add(
|
||||
):
|
||||
from dvc.path_info import PathInfo
|
||||
|
||||
dvc.config.set("cache", "type", link)
|
||||
dvc.config["cache"]["type"] = link
|
||||
|
||||
tmp_dir.dvc_gen({"foo": "foo", "bar": "bar"})
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ from dvc.compat import fspath
|
||||
from dvc.exceptions import FileMissingError
|
||||
from dvc.main import main
|
||||
from dvc.path_info import URLInfo
|
||||
from dvc.remote.config import RemoteConfig
|
||||
from tests.remotes import Azure, GCP, HDFS, Local, OSS, S3, SSH
|
||||
|
||||
|
||||
@@ -109,9 +108,9 @@ def test_missing(remote_url, tmp_dir, dvc):
|
||||
|
||||
|
||||
def _set_remote_url_and_commit(repo, remote_url):
|
||||
rconfig = RemoteConfig(repo.config)
|
||||
rconfig.modify("upstream", "url", remote_url)
|
||||
repo.scm.add([repo.config.config_file])
|
||||
with repo.config.edit() as conf:
|
||||
conf["remote"]["upstream"]["url"] = remote_url
|
||||
repo.scm.add([repo.config.files["repo"]])
|
||||
repo.scm.commit("modify remote")
|
||||
|
||||
|
||||
|
||||
@@ -154,7 +154,7 @@ class TestCmdCacheDir(TestDvc):
|
||||
ret = main(["cache", "dir", dname])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
config = configobj.ConfigObj(self.dvc.config.config_file)
|
||||
config = configobj.ConfigObj(self.dvc.config.files["repo"])
|
||||
self.assertEqual(config["cache"]["dir"], dname)
|
||||
|
||||
def test_relative_path(self):
|
||||
@@ -166,7 +166,7 @@ class TestCmdCacheDir(TestDvc):
|
||||
# NOTE: we are in the repo's root and config is in .dvc/, so
|
||||
# dir path written to config should be just one level above.
|
||||
rel = os.path.join("..", dname)
|
||||
config = configobj.ConfigObj(self.dvc.config.config_file)
|
||||
config = configobj.ConfigObj(self.dvc.config.files["repo"])
|
||||
self.assertEqual(config["cache"]["dir"], rel)
|
||||
|
||||
ret = main(["add", self.FOO])
|
||||
@@ -178,9 +178,8 @@ class TestCmdCacheDir(TestDvc):
|
||||
self.assertEqual(len(files), 1)
|
||||
|
||||
|
||||
class TestShouldCacheBeReflinkOrCopyByDefault(TestDvc):
|
||||
def test(self):
|
||||
self.assertEqual(self.dvc.cache.local.cache_types, ["reflink", "copy"])
|
||||
def test_default_cache_type(dvc):
|
||||
assert dvc.cache.local.cache_types == ["reflink", "copy"]
|
||||
|
||||
|
||||
@pytest.mark.skipif(os.name == "nt", reason="Not supported for Windows.")
|
||||
@@ -189,8 +188,8 @@ class TestShouldCacheBeReflinkOrCopyByDefault(TestDvc):
|
||||
[(False, 0o775, 0o664), (True, 0o775, 0o444)],
|
||||
)
|
||||
def test_shared_cache(tmp_dir, dvc, protected, dir_mode, file_mode):
|
||||
dvc.config.set("cache", "shared", "group")
|
||||
dvc.config.set("cache", "protected", str(protected))
|
||||
with dvc.config.edit() as conf:
|
||||
conf["cache"].update({"shared": "group", "protected": str(protected)})
|
||||
dvc.cache = Cache(dvc)
|
||||
|
||||
tmp_dir.dvc_gen(
|
||||
|
||||
@@ -2,16 +2,13 @@ import pytest
|
||||
import configobj
|
||||
|
||||
from dvc.main import main
|
||||
from dvc.config import Config, ConfigError
|
||||
from dvc.config import ConfigError
|
||||
from tests.basic_env import TestDvc
|
||||
|
||||
|
||||
class TestConfigCLI(TestDvc):
|
||||
def _contains(self, section, field, value, local=False):
|
||||
if local:
|
||||
fname = self.dvc.config.config_local_file
|
||||
else:
|
||||
fname = self.dvc.config.config_file
|
||||
fname = self.dvc.config.files["local" if local else "repo"]
|
||||
|
||||
config = configobj.ConfigObj(fname)
|
||||
if section not in config.keys():
|
||||
@@ -89,9 +86,18 @@ class TestConfigCLI(TestDvc):
|
||||
|
||||
def test_set_invalid_key(dvc):
|
||||
with pytest.raises(ConfigError, match=r"extra keys not allowed"):
|
||||
dvc.config.set("core", "invalid.key", "value")
|
||||
with dvc.config.edit() as conf:
|
||||
conf["core"]["invalid_key"] = "value"
|
||||
|
||||
|
||||
def test_merging_two_levels(dvc):
|
||||
dvc.config.set('remote "test"', "url", "https://example.com")
|
||||
dvc.config.set('remote "test"', "password", "1", level=Config.LEVEL_LOCAL)
|
||||
with dvc.config.edit() as conf:
|
||||
conf["remote"]["test"] = {"url": "ssh://example.com"}
|
||||
|
||||
with dvc.config.edit("local") as conf:
|
||||
conf["remote"]["test"] = {"password": "1"}
|
||||
|
||||
assert dvc.config["remote"]["test"] == {
|
||||
"url": "ssh://example.com",
|
||||
"password": "1",
|
||||
}
|
||||
|
||||
@@ -7,12 +7,11 @@ from unittest import SkipTest
|
||||
|
||||
import pytest
|
||||
|
||||
from dvc.cache import NamedCache
|
||||
from dvc.compat import fspath
|
||||
from dvc.config import Config
|
||||
from dvc.cache import NamedCache
|
||||
from dvc.data_cloud import DataCloud
|
||||
from dvc.main import main
|
||||
from dvc.remote import RemoteAZURE, RemoteConfig
|
||||
from dvc.remote import RemoteAZURE
|
||||
from dvc.remote import RemoteGDrive
|
||||
from dvc.remote import RemoteGS
|
||||
from dvc.remote import RemoteHDFS
|
||||
@@ -21,12 +20,9 @@ from dvc.remote import RemoteLOCAL
|
||||
from dvc.remote import RemoteOSS
|
||||
from dvc.remote import RemoteS3
|
||||
from dvc.remote import RemoteSSH
|
||||
from dvc.remote.base import STATUS_DELETED
|
||||
from dvc.remote.base import STATUS_NEW
|
||||
from dvc.remote.base import STATUS_OK
|
||||
from dvc.remote.base import STATUS_DELETED, STATUS_NEW, STATUS_OK
|
||||
from dvc.utils import file_md5
|
||||
from dvc.utils.stage import dump_stage_file
|
||||
from dvc.utils.stage import load_stage_file
|
||||
from dvc.utils.stage import dump_stage_file, load_stage_file
|
||||
from tests.basic_env import TestDvc
|
||||
|
||||
from tests.remotes import (
|
||||
@@ -39,7 +35,6 @@ from tests.remotes import (
|
||||
SSHMocked,
|
||||
OSS,
|
||||
TEST_CONFIG,
|
||||
TEST_SECTION,
|
||||
TEST_GCP_CREDS_FILE,
|
||||
TEST_GDRIVE_CLIENT_ID,
|
||||
TEST_GDRIVE_CLIENT_SECRET,
|
||||
@@ -49,7 +44,7 @@ from tests.remotes import (
|
||||
|
||||
class TestDataCloud(TestDvc):
|
||||
def _test_cloud(self, config, cl):
|
||||
self.dvc.config.config = config
|
||||
self.dvc.config = config
|
||||
cloud = DataCloud(self.dvc)
|
||||
self.assertIsInstance(cloud.get_remote(), cl)
|
||||
|
||||
@@ -68,7 +63,7 @@ class TestDataCloud(TestDvc):
|
||||
|
||||
for scheme, cl in clist:
|
||||
remote_url = scheme + str(uuid.uuid4())
|
||||
config[TEST_SECTION][Config.SECTION_REMOTE_URL] = remote_url
|
||||
config["remote"][TEST_REMOTE]["url"] = remote_url
|
||||
self._test_cloud(config, cl)
|
||||
|
||||
|
||||
@@ -100,9 +95,8 @@ class TestDataCloudBase(TestDvc):
|
||||
keyfile = self._get_keyfile()
|
||||
|
||||
config = copy.deepcopy(TEST_CONFIG)
|
||||
config[TEST_SECTION][Config.SECTION_REMOTE_URL] = repo
|
||||
config[TEST_SECTION][Config.SECTION_REMOTE_KEY_FILE] = keyfile
|
||||
self.dvc.config.config = config
|
||||
config["remote"][TEST_REMOTE] = {"url": repo, "keyfile": keyfile}
|
||||
self.dvc.config = config
|
||||
self.cloud = DataCloud(self.dvc)
|
||||
|
||||
self.assertIsInstance(self.cloud.get_remote(), self._get_cloud_class())
|
||||
@@ -196,15 +190,13 @@ class TestRemoteS3(S3, TestDataCloudBase):
|
||||
|
||||
def setup_gdrive_cloud(remote_url, dvc):
|
||||
config = copy.deepcopy(TEST_CONFIG)
|
||||
config[TEST_SECTION][Config.SECTION_REMOTE_URL] = remote_url
|
||||
config[TEST_SECTION][
|
||||
Config.SECTION_GDRIVE_CLIENT_ID
|
||||
] = TEST_GDRIVE_CLIENT_ID
|
||||
config[TEST_SECTION][
|
||||
Config.SECTION_GDRIVE_CLIENT_SECRET
|
||||
] = TEST_GDRIVE_CLIENT_SECRET
|
||||
config["remote"][TEST_REMOTE] = {
|
||||
"url": remote_url,
|
||||
"gdrive_client_id": TEST_GDRIVE_CLIENT_ID,
|
||||
"grdive_client_secret": TEST_GDRIVE_CLIENT_SECRET,
|
||||
}
|
||||
|
||||
dvc.config.config = config
|
||||
dvc.config = config
|
||||
remote = DataCloud(dvc).get_remote()
|
||||
remote._create_remote_dir("root", remote.path_info.path)
|
||||
|
||||
@@ -230,11 +222,11 @@ class TestRemoteGS(GCP, TestDataCloudBase):
|
||||
repo = self.get_url()
|
||||
|
||||
config = copy.deepcopy(TEST_CONFIG)
|
||||
config[TEST_SECTION][Config.SECTION_REMOTE_URL] = repo
|
||||
config[TEST_SECTION][
|
||||
Config.SECTION_GCP_CREDENTIALPATH
|
||||
] = TEST_GCP_CREDS_FILE
|
||||
self.dvc.config.config = config
|
||||
config["remote"][TEST_REMOTE] = {
|
||||
"url": repo,
|
||||
"credentialpath": TEST_GCP_CREDS_FILE,
|
||||
}
|
||||
self.dvc.config = config
|
||||
self.cloud = DataCloud(self.dvc)
|
||||
|
||||
self.assertIsInstance(self.cloud.get_remote(), self._get_cloud_class())
|
||||
@@ -272,10 +264,12 @@ class TestRemoteSSHMocked(SSHMocked, TestDataCloudBase):
|
||||
keyfile = self._get_keyfile()
|
||||
|
||||
config = copy.deepcopy(TEST_CONFIG)
|
||||
config[TEST_SECTION][Config.SECTION_REMOTE_URL] = repo
|
||||
config[TEST_SECTION][Config.SECTION_REMOTE_KEY_FILE] = keyfile
|
||||
config[TEST_SECTION][Config.SECTION_REMOTE_NO_TRAVERSE] = False
|
||||
self.dvc.config.config = config
|
||||
config["remote"][TEST_REMOTE] = {
|
||||
"url": repo,
|
||||
"keyfile": keyfile,
|
||||
"no_traverse": False,
|
||||
}
|
||||
self.dvc.config = config
|
||||
self.cloud = DataCloud(self.dvc)
|
||||
|
||||
self.assertIsInstance(self.cloud.get_remote(), self._get_cloud_class())
|
||||
@@ -425,7 +419,7 @@ class TestRemoteGDriveCLI(GDrive, TestDataCloudCLIBase):
|
||||
"remote",
|
||||
"modify",
|
||||
TEST_REMOTE,
|
||||
Config.SECTION_GDRIVE_CLIENT_ID,
|
||||
"gdrive_client_id",
|
||||
TEST_GDRIVE_CLIENT_ID,
|
||||
]
|
||||
)
|
||||
@@ -434,7 +428,7 @@ class TestRemoteGDriveCLI(GDrive, TestDataCloudCLIBase):
|
||||
"remote",
|
||||
"modify",
|
||||
TEST_REMOTE,
|
||||
Config.SECTION_GDRIVE_CLIENT_SECRET,
|
||||
"grdive_client_secret",
|
||||
TEST_GDRIVE_CLIENT_SECRET,
|
||||
]
|
||||
)
|
||||
@@ -669,11 +663,10 @@ def test_verify_checksums(tmp_dir, scm, dvc, mocker, tmp_path_factory):
|
||||
tmp_dir.dvc_gen({"file": "file1 content"}, commit="add file")
|
||||
tmp_dir.dvc_gen({"dir": {"subfile": "file2 content"}}, commit="add dir")
|
||||
|
||||
RemoteConfig(dvc.config).add(
|
||||
"local_remote",
|
||||
fspath(tmp_path_factory.mktemp("local_remote")),
|
||||
default=True,
|
||||
)
|
||||
dvc.config["remote"]["local_remote"] = {
|
||||
"url": fspath(tmp_path_factory.mktemp("local_remote"))
|
||||
}
|
||||
dvc.config["core"]["remote"] = "local_remote"
|
||||
dvc.push()
|
||||
|
||||
# remove artifacts and cache to trigger fetching
|
||||
@@ -689,11 +682,7 @@ def test_verify_checksums(tmp_dir, scm, dvc, mocker, tmp_path_factory):
|
||||
# Removing cache will invalidate existing state entries
|
||||
shutil.rmtree(dvc.cache.local.cache_dir)
|
||||
|
||||
dvc.config.set(
|
||||
Config.SECTION_REMOTE_FMT.format("local_remote"),
|
||||
Config.SECTION_REMOTE_VERIFY,
|
||||
"True",
|
||||
)
|
||||
dvc.config["remote"]["local_remote"]["verify"] = True
|
||||
|
||||
dvc.pull()
|
||||
assert checksum_spy.call_count == 3
|
||||
|
||||
@@ -4,7 +4,6 @@ import os
|
||||
import pytest
|
||||
|
||||
from dvc.cache import Cache
|
||||
from dvc.config import Config
|
||||
from dvc.main import main
|
||||
from dvc.exceptions import PathMissingError
|
||||
from dvc.repo.get import GetDVCFileError
|
||||
@@ -61,12 +60,11 @@ def test_get_git_dir(tmp_dir, erepo_dir):
|
||||
|
||||
def test_cache_type_is_properly_overridden(tmp_dir, erepo_dir):
|
||||
with erepo_dir.chdir():
|
||||
erepo_dir.dvc.config.set(
|
||||
Config.SECTION_CACHE, Config.SECTION_CACHE_TYPE, "symlink"
|
||||
)
|
||||
with erepo_dir.dvc.config.edit() as conf:
|
||||
conf["cache"]["type"] = "symlink"
|
||||
erepo_dir.dvc.cache = Cache(erepo_dir.dvc)
|
||||
erepo_dir.scm_add(
|
||||
[erepo_dir.dvc.config.config_file], "set cache type to symlinks"
|
||||
[erepo_dir.dvc.config.files["repo"]], "set cache type to symlinks"
|
||||
)
|
||||
erepo_dir.dvc_gen("file", "contents", "create file")
|
||||
assert System.is_symlink(erepo_dir / "file")
|
||||
|
||||
@@ -7,7 +7,6 @@ import pytest
|
||||
from mock import patch
|
||||
|
||||
from dvc.cache import Cache
|
||||
from dvc.config import Config
|
||||
from dvc.exceptions import DownloadError, PathMissingError
|
||||
from dvc.config import NoRemoteError
|
||||
from dvc.stage import Stage
|
||||
@@ -162,12 +161,11 @@ def test_pull_imported_stage(tmp_dir, dvc, erepo_dir):
|
||||
|
||||
def test_cache_type_is_properly_overridden(tmp_dir, scm, dvc, erepo_dir):
|
||||
with erepo_dir.chdir():
|
||||
erepo_dir.dvc.config.set(
|
||||
Config.SECTION_CACHE, Config.SECTION_CACHE_TYPE, "symlink"
|
||||
)
|
||||
with erepo_dir.dvc.config.edit() as conf:
|
||||
conf["cache"]["type"] = "symlink"
|
||||
erepo_dir.dvc.cache = Cache(erepo_dir.dvc)
|
||||
erepo_dir.scm_add(
|
||||
[erepo_dir.dvc.config.config_file],
|
||||
[erepo_dir.dvc.config.files["repo"]],
|
||||
"set source repo cache type to symlink",
|
||||
)
|
||||
erepo_dir.dvc_gen("foo", "foo content", "create foo")
|
||||
|
||||
@@ -5,7 +5,6 @@ import sys
|
||||
import pytest
|
||||
|
||||
from dvc.exceptions import GitHookAlreadyExistsError
|
||||
from dvc.remote import RemoteConfig
|
||||
from dvc.utils import file_md5, fspath
|
||||
|
||||
|
||||
@@ -52,9 +51,9 @@ class TestInstall(object):
|
||||
git_remote = temp / "project.git"
|
||||
storage_path = temp / "dvc_storage"
|
||||
|
||||
RemoteConfig(dvc.config).add(
|
||||
"store", fspath(storage_path), default=True
|
||||
)
|
||||
with dvc.config.edit() as conf:
|
||||
conf["remote"]["store"] = {"url": fspath(storage_path)}
|
||||
conf["core"]["remote"] = "store"
|
||||
tmp_dir.dvc_gen("file", "file_content", "commit message")
|
||||
|
||||
file_checksum = file_md5("file")[0]
|
||||
|
||||
+30
-62
@@ -6,11 +6,11 @@ import configobj
|
||||
import pytest
|
||||
from mock import patch
|
||||
|
||||
from dvc.config import Config, ConfigError
|
||||
from dvc.config import Config
|
||||
from dvc.exceptions import DownloadError, UploadError
|
||||
from dvc.main import main
|
||||
from dvc.path_info import PathInfo
|
||||
from dvc.remote import RemoteLOCAL, RemoteConfig
|
||||
from dvc.remote import RemoteLOCAL
|
||||
from dvc.remote.base import RemoteBASE, RemoteCacheRequiredError
|
||||
from dvc.compat import fspath
|
||||
from tests.basic_env import TestDvc
|
||||
@@ -46,7 +46,7 @@ class TestRemote(TestDvc):
|
||||
# NOTE: we are in the repo's root and config is in .dvc/, so
|
||||
# dir path written to config should be just one level above.
|
||||
rel = os.path.join("..", dname)
|
||||
config = configobj.ConfigObj(self.dvc.config.config_file)
|
||||
config = configobj.ConfigObj(self.dvc.config.files["repo"])
|
||||
self.assertEqual(config['remote "mylocal"']["url"], rel)
|
||||
|
||||
def test_overwrite(self):
|
||||
@@ -62,53 +62,29 @@ class TestRemote(TestDvc):
|
||||
assert main(["remote", "add", "foo", "ssh://localhost/"]) == 0
|
||||
assert main(["remote", "add", "bar", "remote://foo/dvc-storage"]) == 0
|
||||
|
||||
config = configobj.ConfigObj(self.dvc.config.config_file)
|
||||
|
||||
config = configobj.ConfigObj(self.dvc.config.files["repo"])
|
||||
assert config['remote "bar"']["url"] == "remote://foo/dvc-storage"
|
||||
|
||||
|
||||
class TestRemoteRemoveDefault(TestDvc):
|
||||
def test(self):
|
||||
remote = "mys3"
|
||||
self.assertEqual(
|
||||
main(["remote", "add", "--default", remote, "s3://bucket/name"]), 0
|
||||
)
|
||||
self.assertEqual(
|
||||
main(["remote", "modify", remote, "profile", "default"]), 0
|
||||
)
|
||||
self.assertEqual(main(["config", "--local", "core.remote", remote]), 0)
|
||||
def test_remove_default(tmp_dir, dvc):
|
||||
remote = "mys3"
|
||||
assert (
|
||||
main(["remote", "add", "--default", remote, "s3://bucket/name"]) == 0
|
||||
)
|
||||
assert main(["remote", "modify", remote, "profile", "default"]) == 0
|
||||
assert main(["config", "--local", "core.remote", remote]) == 0
|
||||
|
||||
config = configobj.ConfigObj(
|
||||
os.path.join(self.dvc.dvc_dir, Config.CONFIG)
|
||||
)
|
||||
local_config = configobj.ConfigObj(
|
||||
os.path.join(self.dvc.dvc_dir, Config.CONFIG_LOCAL)
|
||||
)
|
||||
self.assertEqual(
|
||||
config[Config.SECTION_CORE][Config.SECTION_CORE_REMOTE], remote
|
||||
)
|
||||
self.assertEqual(
|
||||
local_config[Config.SECTION_CORE][Config.SECTION_CORE_REMOTE],
|
||||
remote,
|
||||
)
|
||||
config = configobj.ConfigObj(dvc.config.files["repo"])
|
||||
local_config = configobj.ConfigObj(dvc.config.files["local"])
|
||||
assert config["core"]["remote"] == remote
|
||||
assert local_config["core"]["remote"] == remote
|
||||
|
||||
self.assertEqual(main(["remote", "remove", remote]), 0)
|
||||
config = configobj.ConfigObj(
|
||||
os.path.join(self.dvc.dvc_dir, Config.CONFIG)
|
||||
)
|
||||
local_config = configobj.ConfigObj(
|
||||
os.path.join(self.dvc.dvc_dir, Config.CONFIG_LOCAL)
|
||||
)
|
||||
section = Config.SECTION_REMOTE_FMT.format(remote)
|
||||
self.assertTrue(section not in config.keys())
|
||||
assert main(["remote", "remove", remote]) == 0
|
||||
|
||||
core = config.get(Config.SECTION_CORE, None)
|
||||
if core is not None:
|
||||
self.assertTrue(Config.SECTION_CORE_REMOTE not in core.keys())
|
||||
|
||||
core = local_config.get(Config.SECTION_CORE, None)
|
||||
if core is not None:
|
||||
self.assertTrue(Config.SECTION_CORE_REMOTE not in core.keys())
|
||||
config = configobj.ConfigObj(dvc.config.files["repo"])
|
||||
local_config = configobj.ConfigObj(dvc.config.files["local"])
|
||||
assert config.get("core", {}).get("remote") is None
|
||||
assert local_config.get("core", {}).get("remote") is None
|
||||
|
||||
|
||||
class TestRemoteRemove(TestDvc):
|
||||
@@ -134,17 +110,13 @@ class TestRemoteDefault(TestDvc):
|
||||
self.assertEqual(ret, 0)
|
||||
config_file = os.path.join(self.dvc.dvc_dir, Config.CONFIG)
|
||||
config = configobj.ConfigObj(config_file)
|
||||
default = config[Config.SECTION_CORE][Config.SECTION_CORE_REMOTE]
|
||||
default = config["core"]["remote"]
|
||||
self.assertEqual(default, remote)
|
||||
|
||||
ret = main(["remote", "default", "--unset"])
|
||||
self.assertEqual(ret, 0)
|
||||
config = configobj.ConfigObj(config_file)
|
||||
core = config.get(Config.SECTION_CORE)
|
||||
if core is not None:
|
||||
default = core.get(Config.SECTION_CORE_REMOTE)
|
||||
else:
|
||||
default = None
|
||||
default = config.get("core", {}).get("remote")
|
||||
self.assertEqual(default, None)
|
||||
|
||||
|
||||
@@ -199,10 +171,9 @@ def test_dir_checksum_should_be_key_order_agnostic(tmp_dir, dvc):
|
||||
|
||||
|
||||
def test_partial_push_n_pull(tmp_dir, dvc, tmp_path_factory):
|
||||
remote_config = RemoteConfig(dvc.config)
|
||||
remote_config.add(
|
||||
"upstream", fspath(tmp_path_factory.mktemp("upstream")), default=True
|
||||
)
|
||||
url = fspath(tmp_path_factory.mktemp("upstream"))
|
||||
dvc.config["remote"]["upstream"] = {"url": url}
|
||||
dvc.config["core"]["remote"] = "upstream"
|
||||
|
||||
foo = tmp_dir.dvc_gen({"foo": "foo content"})[0].outs[0]
|
||||
bar = tmp_dir.dvc_gen({"bar": "bar content"})[0].outs[0]
|
||||
@@ -235,9 +206,9 @@ def test_partial_push_n_pull(tmp_dir, dvc, tmp_path_factory):
|
||||
|
||||
|
||||
def test_raise_on_too_many_open_files(tmp_dir, dvc, tmp_path_factory, mocker):
|
||||
storage = tmp_path_factory.mktemp("test_remote_base")
|
||||
remote_config = RemoteConfig(dvc.config)
|
||||
remote_config.add("local_remote", fspath(storage), default=True)
|
||||
storage = fspath(tmp_path_factory.mktemp("test_remote_base"))
|
||||
dvc.config["remote"]["local_remote"] = {"url": storage}
|
||||
dvc.config["core"]["remote"] = "local_remote"
|
||||
|
||||
tmp_dir.dvc_gen({"file": "file content"})
|
||||
|
||||
@@ -252,11 +223,8 @@ def test_raise_on_too_many_open_files(tmp_dir, dvc, tmp_path_factory, mocker):
|
||||
assert e.errno == errno.EMFILE
|
||||
|
||||
|
||||
def test_modify_missing_remote(dvc):
|
||||
remote_config = RemoteConfig(dvc.config)
|
||||
|
||||
with pytest.raises(ConfigError, match=r"Unable to find remote section"):
|
||||
remote_config.modify("myremote", "gdrive_client_id", "xxx")
|
||||
def test_modify_missing_remote(tmp_dir, dvc):
|
||||
assert main(["remote", "modify", "myremote", "user", "xxx"]) == 251
|
||||
|
||||
|
||||
def test_external_dir_resource_on_no_cache(tmp_dir, dvc, tmp_path_factory):
|
||||
|
||||
@@ -8,7 +8,7 @@ from dvc.compat import fspath
|
||||
|
||||
|
||||
def test_destroy(tmp_dir, dvc):
|
||||
dvc.config.set("cache", "type", "symlink")
|
||||
dvc.config["cache"]["type"] = ["symlink"]
|
||||
dvc.cache = Cache(dvc)
|
||||
|
||||
tmp_dir.dvc_gen("file", "text")
|
||||
|
||||
+13
-12
@@ -808,6 +808,8 @@ class TestCmdReproChdir(TestDvc):
|
||||
|
||||
|
||||
class TestReproExternalBase(TestDvc):
|
||||
cache_type = None
|
||||
|
||||
@staticmethod
|
||||
def should_test():
|
||||
return False
|
||||
@@ -816,10 +818,6 @@ class TestReproExternalBase(TestDvc):
|
||||
def cache_scheme(self):
|
||||
return self.scheme
|
||||
|
||||
@property
|
||||
def cache_type(self):
|
||||
return "copy"
|
||||
|
||||
@property
|
||||
def scheme(self):
|
||||
return None
|
||||
@@ -876,8 +874,9 @@ class TestReproExternalBase(TestDvc):
|
||||
self.assertEqual(ret, 0)
|
||||
ret = main(["remote", "add", "myrepo", cache])
|
||||
self.assertEqual(ret, 0)
|
||||
ret = main(["remote", "modify", "myrepo", "type", self.cache_type])
|
||||
self.assertEqual(ret, 0)
|
||||
if self.cache_type:
|
||||
ret = main(["remote", "modify", "myrepo", "type", self.cache_type])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
remote_name = "myremote"
|
||||
remote_key = str(uuid.uuid4())
|
||||
@@ -887,8 +886,11 @@ class TestReproExternalBase(TestDvc):
|
||||
|
||||
ret = main(["remote", "add", remote_name, remote])
|
||||
self.assertEqual(ret, 0)
|
||||
ret = main(["remote", "modify", remote_name, "type", self.cache_type])
|
||||
self.assertEqual(ret, 0)
|
||||
if self.cache_type:
|
||||
ret = main(
|
||||
["remote", "modify", remote_name, "type", self.cache_type]
|
||||
)
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
self.dvc = DvcRepo(".")
|
||||
|
||||
@@ -1052,6 +1054,7 @@ class TestReproExternalHDFS(HDFS, TestReproExternalBase):
|
||||
@flaky(max_runs=3, min_passes=1)
|
||||
class TestReproExternalSSH(SSH, TestReproExternalBase):
|
||||
_dir = None
|
||||
cache_type = "copy"
|
||||
|
||||
@property
|
||||
def scheme(self):
|
||||
@@ -1095,6 +1098,8 @@ class TestReproExternalSSH(SSH, TestReproExternalBase):
|
||||
|
||||
|
||||
class TestReproExternalLOCAL(Local, TestReproExternalBase):
|
||||
cache_type = "hardlink"
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.tmpdir = self.mkdtemp()
|
||||
@@ -1102,10 +1107,6 @@ class TestReproExternalLOCAL(Local, TestReproExternalBase):
|
||||
self.assertEqual(ret, 0)
|
||||
self.dvc = DvcRepo(".")
|
||||
|
||||
@property
|
||||
def cache_type(self):
|
||||
return "hardlink"
|
||||
|
||||
@property
|
||||
def cache_scheme(self):
|
||||
return "local"
|
||||
|
||||
@@ -11,7 +11,7 @@ def test_state(tmp_dir, dvc):
|
||||
path_info = PathInfo(path)
|
||||
md5 = file_md5(path)[0]
|
||||
|
||||
state = State(dvc, dvc.config.config)
|
||||
state = State(dvc)
|
||||
|
||||
with state:
|
||||
state.save(path_info, md5)
|
||||
@@ -34,7 +34,7 @@ def test_state(tmp_dir, dvc):
|
||||
def test_state_overflow(tmp_dir, dvc):
|
||||
# NOTE: trying to add more entries than state can handle,
|
||||
# to see if it will clean up and vacuum successfully
|
||||
dvc.config.set("state", "row_limit", 10)
|
||||
dvc.config["state"]["row_limit"] = 10
|
||||
|
||||
path = tmp_dir / "dir"
|
||||
path.mkdir()
|
||||
@@ -55,7 +55,7 @@ def mock_get_inode(inode):
|
||||
def test_get_state_record_for_inode(get_inode_mock, tmp_dir, dvc):
|
||||
tmp_dir.gen("foo", "foo content")
|
||||
|
||||
state = State(dvc, dvc.config.config)
|
||||
state = State(dvc)
|
||||
inode = state.MAX_INT + 2
|
||||
assert inode != state._to_sqlite(inode)
|
||||
|
||||
|
||||
+3
-5
@@ -7,7 +7,6 @@ from contextlib import contextmanager
|
||||
from subprocess import CalledProcessError, check_output, Popen
|
||||
|
||||
from dvc.utils import env2bool
|
||||
from dvc.config import Config
|
||||
from dvc.remote import RemoteGDrive
|
||||
from dvc.remote.gs import RemoteGS
|
||||
from dvc.remote.s3 import RemoteS3
|
||||
@@ -17,11 +16,10 @@ from moto.s3 import mock_s3
|
||||
|
||||
|
||||
TEST_REMOTE = "upstream"
|
||||
TEST_SECTION = 'remote "{}"'.format(TEST_REMOTE)
|
||||
TEST_CONFIG = {
|
||||
Config.SECTION_CACHE: {},
|
||||
Config.SECTION_CORE: {Config.SECTION_CORE_REMOTE: TEST_REMOTE},
|
||||
TEST_SECTION: {Config.SECTION_REMOTE_URL: ""},
|
||||
"cache": {},
|
||||
"core": {"remote": TEST_REMOTE},
|
||||
"remote": {TEST_REMOTE: {"url": ""}},
|
||||
}
|
||||
|
||||
TEST_AWS_REPO_BUCKET = os.environ.get("DVC_TEST_AWS_REPO_BUCKET", "dvc-test")
|
||||
|
||||
@@ -1,41 +1,27 @@
|
||||
from dvc.remote import Remote
|
||||
|
||||
|
||||
def set_config_opts(dvc, commands):
|
||||
list(map(lambda args: dvc.config.set(*args), commands))
|
||||
|
||||
|
||||
def test_remote_with_checksum_jobs(dvc):
|
||||
set_config_opts(
|
||||
dvc,
|
||||
[
|
||||
('remote "with_checksum_jobs"', "url", "s3://bucket/name"),
|
||||
('remote "with_checksum_jobs"', "checksum_jobs", 100),
|
||||
("core", "checksum_jobs", 200),
|
||||
],
|
||||
)
|
||||
dvc.config["remote"]["with_checksum_jobs"] = {
|
||||
"url": "s3://bucket/name",
|
||||
"checksum_jobs": 100,
|
||||
}
|
||||
dvc.config["core"]["checksum_jobs"] = 200
|
||||
|
||||
remote = Remote(dvc, name="with_checksum_jobs")
|
||||
assert remote.checksum_jobs == 100
|
||||
|
||||
|
||||
def test_remote_without_checksum_jobs(dvc):
|
||||
set_config_opts(
|
||||
dvc,
|
||||
[
|
||||
('remote "without_checksum_jobs"', "url", "s3://bucket/name"),
|
||||
("core", "checksum_jobs", "200"),
|
||||
],
|
||||
)
|
||||
dvc.config["remote"]["without_checksum_jobs"] = {"url": "s3://bucket/name"}
|
||||
dvc.config["core"]["checksum_jobs"] = 200
|
||||
|
||||
remote = Remote(dvc, name="without_checksum_jobs")
|
||||
assert remote.checksum_jobs == 200
|
||||
|
||||
|
||||
def test_remote_without_checksum_jobs_default(dvc):
|
||||
set_config_opts(
|
||||
dvc, [('remote "without_checksum_jobs"', "url", "s3://bucket/name")]
|
||||
)
|
||||
dvc.config["remote"]["without_checksum_jobs"] = {"url": "s3://bucket/name"}
|
||||
|
||||
remote = Remote(dvc, name="without_checksum_jobs")
|
||||
assert remote.checksum_jobs == remote.CHECKSUM_JOBS
|
||||
|
||||
@@ -2,7 +2,6 @@ import mock
|
||||
import pytest
|
||||
|
||||
import dvc.remote.slow_link_detection
|
||||
from dvc.config import Config
|
||||
from dvc.remote.slow_link_detection import slow_link_guard
|
||||
|
||||
|
||||
@@ -15,9 +14,8 @@ def timeout_immediately(monkeypatch):
|
||||
def make_remote():
|
||||
def _make_remote(cache_type=None, should_warn=True):
|
||||
remote = mock.Mock()
|
||||
remote.repo.config.config.get.return_value = {
|
||||
Config.SECTION_CACHE_TYPE: cache_type,
|
||||
Config.SECTION_CACHE_SLOW_LINK_WARNING: should_warn,
|
||||
remote.repo.config = {
|
||||
"cache": {"type": cache_type, "slow_link_warning": should_warn}
|
||||
}
|
||||
return remote
|
||||
|
||||
@@ -29,8 +27,8 @@ def test_show_warning_once(caplog, make_remote):
|
||||
slow_link_guard(lambda x: None)(remote)
|
||||
slow_link_guard(lambda x: None)(remote)
|
||||
|
||||
assert caplog.records[0].message == dvc.remote.slow_link_detection.message
|
||||
assert len(caplog.records) == 1
|
||||
assert caplog.records[0].message == dvc.remote.slow_link_detection.message
|
||||
|
||||
|
||||
def test_dont_warn_when_cache_type_is_set(caplog, make_remote):
|
||||
|
||||
@@ -10,19 +10,17 @@ from dvc.cli import parse_args
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def tmp_global_config(tmp_path):
|
||||
def tmp_global_dir(tmp_path):
|
||||
"""
|
||||
Fixture to prevent modifying the actual global config
|
||||
"""
|
||||
with mock.patch(
|
||||
"dvc.config.Config.get_global_config_dir", return_value=str(tmp_path)
|
||||
):
|
||||
with mock.patch("dvc.config.Config.get_dir", return_value=str(tmp_path)):
|
||||
yield
|
||||
|
||||
|
||||
@mock.patch("dvc.daemon._spawn")
|
||||
@mock.patch("json.dump")
|
||||
def test_collect_and_send_report(mock_json, mock_daemon, tmp_global_config):
|
||||
def test_collect_and_send_report(mock_json, mock_daemon, tmp_global_dir):
|
||||
analytics.collect_and_send_report()
|
||||
report = mock_json.call_args[0][0]
|
||||
|
||||
@@ -44,7 +42,7 @@ def test_collect_and_send_report(mock_json, mock_daemon, tmp_global_config):
|
||||
assert mock_daemon.call_count == 2
|
||||
|
||||
|
||||
def test_runtime_info(tmp_global_config):
|
||||
def test_runtime_info(tmp_global_dir):
|
||||
schema = Schema(
|
||||
{
|
||||
"dvc_version": str,
|
||||
@@ -86,10 +84,12 @@ def test_send(mock_post, tmp_path):
|
||||
({"analytics": "false", "unknown": "broken"}, False),
|
||||
],
|
||||
)
|
||||
def test_is_enabled(dvc, config, result, monkeypatch, tmp_global_config):
|
||||
configobj = dvc.config._repo_config
|
||||
configobj["core"] = config
|
||||
configobj.write()
|
||||
def test_is_enabled(dvc, config, result, monkeypatch, tmp_global_dir):
|
||||
import configobj
|
||||
|
||||
conf = configobj.ConfigObj({"core": config})
|
||||
conf.filename = dvc.config.files["repo"]
|
||||
conf.write()
|
||||
|
||||
# reset DVC_TEST env var, which affects `is_enabled()`
|
||||
monkeypatch.delenv("DVC_TEST")
|
||||
@@ -127,7 +127,7 @@ def test_system_info():
|
||||
assert schema(analytics._system_info())
|
||||
|
||||
|
||||
def test_find_or_create_user_id(tmp_global_config):
|
||||
def test_find_or_create_user_id(tmp_global_dir):
|
||||
created = analytics._find_or_create_user_id()
|
||||
found = analytics._find_or_create_user_id()
|
||||
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
from dvc.config import Config
|
||||
from dvc.config import COMPILED_SCHEMA
|
||||
|
||||
|
||||
def test_remote_config_no_traverse():
|
||||
d = Config.COMPILED_SCHEMA({'remote "myremote"': {"url": "url"}})
|
||||
assert "no_traverse" not in d['remote "myremote"']
|
||||
d = COMPILED_SCHEMA({"remote": {"myremote": {"url": "url"}}})
|
||||
assert "no_traverse" not in d["remote"]["myremote"]
|
||||
|
||||
d = Config.COMPILED_SCHEMA(
|
||||
{'remote "myremote"': {"url": "url", "no_traverse": "fAlSe"}}
|
||||
d = COMPILED_SCHEMA(
|
||||
{"remote": {"myremote": {"url": "url", "no_traverse": "fAlSe"}}}
|
||||
)
|
||||
assert not d['remote "myremote"']["no_traverse"]
|
||||
assert not d["remote"]["myremote"]["no_traverse"]
|
||||
|
||||
d = Config.COMPILED_SCHEMA(
|
||||
{'remote "myremote"': {"url": "url", "no_traverse": "tRuE"}}
|
||||
d = COMPILED_SCHEMA(
|
||||
{"remote": {"myremote": {"url": "url", "no_traverse": "tRuE"}}}
|
||||
)
|
||||
assert d['remote "myremote"']["no_traverse"]
|
||||
assert d["remote"]["myremote"]["no_traverse"]
|
||||
|
||||
Reference in New Issue
Block a user