Skip to content

Commit 76481bd

Browse files
committed
Merge remote-tracking branch 'origin/master' into fix_3736
2 parents 3eea263 + b14ad4f commit 76481bd

File tree

18 files changed

+154
-61
lines changed

18 files changed

+154
-61
lines changed

dvc/command/experiments.py

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -82,29 +82,25 @@ def _show_experiments(all_experiments, console, precision=None):
8282

8383
metric_names, param_names = _collect_names(all_experiments)
8484

85-
table = Table(row_styles=["white", "bright_white"])
86-
table.add_column("Experiment", header_style="black on grey93")
85+
table = Table()
86+
table.add_column("Experiment")
8787
for name in metric_names:
88-
table.add_column(
89-
name, justify="right", header_style="black on cornsilk1"
90-
)
88+
table.add_column(name, justify="right")
9189
for name in param_names:
92-
table.add_column(
93-
name, justify="left", header_style="black on light_cyan1"
94-
)
90+
table.add_column(name, justify="left")
9591

9692
for base_rev, experiments in all_experiments.items():
9793
if Git.is_sha(base_rev):
9894
base_rev = base_rev[:7]
9995

100-
for row, style, in _collect_rows(
96+
for row, _, in _collect_rows(
10197
base_rev,
10298
experiments,
10399
metric_names,
104100
param_names,
105101
precision=precision,
106102
):
107-
table.add_row(*row, style=style)
103+
table.add_row(*row)
108104

109105
console.print(table)
110106

dvc/config.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -314,25 +314,23 @@ def load(self, validate=True):
314314

315315
def _load_config(self, level):
316316
filename = self.files[level]
317-
tree = self.tree if level == "repo" else self.wtree
318317

319-
if tree.exists(filename, use_dvcignore=False):
320-
with tree.open(filename) as fobj:
318+
if self.tree.exists(filename, use_dvcignore=False):
319+
with self.tree.open(filename) as fobj:
321320
conf_obj = configobj.ConfigObj(fobj)
322321
else:
323322
conf_obj = configobj.ConfigObj()
324323
return _parse_remotes(_lower_keys(conf_obj.dict()))
325324

326325
def _save_config(self, level, conf_dict):
327326
filename = self.files[level]
328-
tree = self.tree if level == "repo" else self.wtree
329327

330328
logger.debug(f"Writing '{filename}'.")
331329

332-
tree.makedirs(os.path.dirname(filename))
330+
self.tree.makedirs(os.path.dirname(filename))
333331

334332
config = configobj.ConfigObj(_pack_remotes(conf_dict))
335-
with tree.open(filename, "wb") as fobj:
333+
with self.tree.open(filename, "wb") as fobj:
336334
config.write(fobj)
337335
config.filename = filename
338336

dvc/daemon.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,17 @@
1818
def _spawn_windows(cmd, env):
1919
from subprocess import STARTUPINFO, STARTF_USESHOWWINDOW
2020

21+
prefix = [sys.executable]
22+
if not is_binary():
23+
prefix += [sys.argv[0]]
24+
2125
creationflags = CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS
2226

2327
startupinfo = STARTUPINFO()
2428
startupinfo.dwFlags |= STARTF_USESHOWWINDOW
2529

2630
Popen(
27-
cmd,
31+
prefix + cmd,
2832
env=env,
2933
close_fds=True,
3034
shell=False,
@@ -34,6 +38,8 @@ def _spawn_windows(cmd, env):
3438

3539

3640
def _spawn_posix(cmd, env):
41+
from dvc.main import main
42+
3743
# NOTE: using os._exit instead of sys.exit, because dvc built
3844
# with PyInstaller has trouble with SystemExit exception and throws
3945
# errors such as "[26338] Failed to execute script __main__"
@@ -59,7 +65,8 @@ def _spawn_posix(cmd, env):
5965
sys.stdout.close()
6066
sys.stderr.close()
6167

62-
Popen(cmd, env=env, close_fds=True, shell=False).communicate()
68+
os.environ.update(env)
69+
main(cmd)
6370

6471
os._exit(0) # pylint: disable=protected-access
6572

@@ -87,10 +94,7 @@ def daemon(args):
8794
logger.debug("skipping launching a new daemon.")
8895
return
8996

90-
cmd = [sys.executable]
91-
if not is_binary():
92-
cmd += [sys.argv[0]]
93-
cmd += ["daemon", "-q"] + args
97+
cmd = ["daemon", "-q"] + args
9498

9599
env = fix_env()
96100
file_path = os.path.abspath(inspect.stack()[0][1])

dvc/dependency/param.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from collections import defaultdict
33

44
import dpath.util
5+
import toml
56
import yaml
67
from voluptuous import Any
78

@@ -21,6 +22,8 @@ class ParamsDependency(LocalDependency):
2122
PARAM_PARAMS = "params"
2223
PARAM_SCHEMA = {PARAM_PARAMS: Any(dict, list, None)}
2324
DEFAULT_PARAMS_FILE = "params.yaml"
25+
PARAMS_FILE_LOADERS = defaultdict(lambda: yaml.safe_load)
26+
PARAMS_FILE_LOADERS.update({".toml": toml.load})
2427

2528
def __init__(self, stage, path, params):
2629
info = {}
@@ -87,8 +90,10 @@ def read_params(self):
8790

8891
with self.repo.tree.open(self.path_info, "r") as fobj:
8992
try:
90-
config = yaml.safe_load(fobj)
91-
except yaml.YAMLError as exc:
93+
config = self.PARAMS_FILE_LOADERS[
94+
self.path_info.suffix.lower()
95+
](fobj)
96+
except (yaml.YAMLError, toml.TomlDecodeError) as exc:
9297
raise BadParamFileError(
9398
f"Unable to read parameters from '{self}'"
9499
) from exc

dvc/ignore.py

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import os
33
import re
44
from collections import namedtuple
5-
from itertools import groupby
5+
from itertools import groupby, takewhile
66

77
from pathspec.patterns import GitWildMatchPattern
88
from pathspec.util import normalize_file
@@ -183,18 +183,19 @@ def __init__(self, tree, root_dir):
183183
self.ignores_trie_tree[root_dir] = DvcIgnorePatterns(
184184
default_ignore_patterns, root_dir
185185
)
186-
for root, dirs, _ in self.tree.walk(self.root_dir):
187-
self._update(root)
188-
self._update_sub_repo(root, dirs)
189-
dirs[:], _ = self(root, dirs, [])
186+
self._update(self.root_dir)
190187

191188
def _update(self, dirname):
189+
old_pattern = self.ignores_trie_tree.longest_prefix(dirname).value
190+
matches = old_pattern.matches(dirname, DvcIgnore.DVCIGNORE_FILE, False)
191+
192192
ignore_file_path = os.path.join(dirname, DvcIgnore.DVCIGNORE_FILE)
193-
if self.tree.exists(ignore_file_path):
193+
if not matches and self.tree.exists(
194+
ignore_file_path, use_dvcignore=False
195+
):
194196
new_pattern = DvcIgnorePatterns.from_files(
195197
ignore_file_path, self.tree
196198
)
197-
old_pattern = self._get_trie_pattern(dirname)
198199
if old_pattern:
199200
self.ignores_trie_tree[dirname] = DvcIgnorePatterns(
200201
*merge_patterns(
@@ -206,12 +207,19 @@ def _update(self, dirname):
206207
)
207208
else:
208209
self.ignores_trie_tree[dirname] = new_pattern
210+
elif old_pattern:
211+
self.ignores_trie_tree[dirname] = old_pattern
212+
213+
# NOTE: using `walk` + `break` because tree doesn't have `listdir()`
214+
for root, dirs, _ in self.tree.walk(dirname, use_dvcignore=False):
215+
self._update_sub_repo(root, dirs)
216+
break
209217

210218
def _update_sub_repo(self, root, dirs):
211219
for d in dirs:
212220
if self._is_dvc_repo(root, d):
213-
old_pattern = self._get_trie_pattern(root)
214221
new_pattern = DvcIgnorePatterns(["/{}/".format(d)], root)
222+
old_pattern = self.ignores_trie_tree.longest_prefix(root).value
215223
if old_pattern:
216224
self.ignores_trie_tree[root] = DvcIgnorePatterns(
217225
*merge_patterns(
@@ -232,8 +240,28 @@ def __call__(self, root, dirs, files):
232240
return dirs, files
233241

234242
def _get_trie_pattern(self, dirname):
235-
ignore_pattern = self.ignores_trie_tree.longest_prefix(dirname).value
236-
return ignore_pattern
243+
ignore_pattern = self.ignores_trie_tree.get(dirname)
244+
if ignore_pattern:
245+
return ignore_pattern
246+
247+
prefix = self.ignores_trie_tree.longest_prefix(dirname).key
248+
if not prefix:
249+
# outside of the repo
250+
return None
251+
252+
dirs = list(
253+
takewhile(
254+
lambda path: path != prefix,
255+
(parent.fspath for parent in PathInfo(dirname).parents),
256+
)
257+
)
258+
dirs.reverse()
259+
dirs.append(dirname)
260+
261+
for parent in dirs:
262+
self._update(parent)
263+
264+
return self.ignores_trie_tree.get(dirname)
237265

238266
def _is_ignored(self, path, is_dir=False):
239267
if self._outside_repo(path):

dvc/repo/params/show.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import logging
22

3+
import toml
34
import yaml
45

56
from dvc.dependency.param import ParamsDependency
@@ -34,8 +35,10 @@ def _read_params(repo, configs, rev):
3435

3536
with repo.tree.open(config, "r") as fobj:
3637
try:
37-
res[str(config)] = yaml.safe_load(fobj)
38-
except yaml.YAMLError:
38+
res[str(config)] = ParamsDependency.PARAMS_FILE_LOADERS[
39+
config.suffix.lower()
40+
](fobj)
41+
except (yaml.YAMLError, toml.TomlDecodeError):
3942
logger.debug(
4043
"failed to read '%s' on '%s'", config, rev, exc_info=True
4144
)

dvc/tree/git.py

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,8 @@ def dvcignore(self):
4646
from dvc.ignore import DvcIgnoreFilter, DvcIgnoreFilterNoop
4747

4848
root = self.dvcignore_root or self.tree_root
49-
if not self.use_dvcignore:
50-
return DvcIgnoreFilterNoop(self, root)
51-
self.use_dvcignore = False
52-
ret = DvcIgnoreFilter(self, root)
53-
self.use_dvcignore = True
54-
return ret
49+
cls = DvcIgnoreFilter if self.use_dvcignore else DvcIgnoreFilterNoop
50+
return cls(self, root)
5551

5652
def open(
5753
self, path, mode="r", encoding="utf-8"
@@ -160,7 +156,7 @@ def _walk(self, tree, topdown=True):
160156
if not topdown:
161157
yield os.path.normpath(tree.abspath), dirs, nondirs
162158

163-
def walk(self, top, topdown=True, onerror=None):
159+
def walk(self, top, topdown=True, onerror=None, use_dvcignore=True):
164160
"""Directory tree generator.
165161
166162
See `os.walk` for the docs. Differences:
@@ -178,9 +174,10 @@ def walk(self, top, topdown=True, onerror=None):
178174
return
179175

180176
for root, dirs, files in self._walk(tree, topdown=topdown):
181-
dirs[:], files[:] = self.dvcignore(
182-
os.path.abspath(root), dirs, files
183-
)
177+
if use_dvcignore:
178+
dirs[:], files[:] = self.dvcignore(
179+
os.path.abspath(root), dirs, files
180+
)
184181
yield root, dirs, files
185182

186183
def isexec(self, path):

dvc/tree/local.py

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,8 @@ def dvcignore(self):
5858
from dvc.ignore import DvcIgnoreFilter, DvcIgnoreFilterNoop
5959

6060
root = self.dvcignore_root or self.tree_root
61-
if not self.use_dvcignore:
62-
return DvcIgnoreFilterNoop(self, root)
63-
self.use_dvcignore = False
64-
ret = DvcIgnoreFilter(self, root)
65-
self.use_dvcignore = True
66-
return ret
61+
cls = DvcIgnoreFilter if self.use_dvcignore else DvcIgnoreFilterNoop
62+
return cls(self, root)
6763

6864
@staticmethod
6965
def open(path_info, mode="r", encoding=None):
@@ -101,7 +97,7 @@ def iscopy(self, path_info):
10197
System.is_symlink(path_info) or System.is_hardlink(path_info)
10298
)
10399

104-
def walk(self, top, topdown=True, onerror=None):
100+
def walk(self, top, topdown=True, onerror=None, use_dvcignore=True):
105101
"""Directory tree generator.
106102
107103
See `os.walk` for the docs. Differences:
@@ -110,9 +106,10 @@ def walk(self, top, topdown=True, onerror=None):
110106
for root, dirs, files in os.walk(
111107
top, topdown=topdown, onerror=onerror
112108
):
113-
dirs[:], files[:] = self.dvcignore(
114-
os.path.abspath(root), dirs, files
115-
)
109+
if use_dvcignore:
110+
dirs[:], files[:] = self.dvcignore(
111+
os.path.abspath(root), dirs, files
112+
)
116113

117114
yield os.path.normpath(root), dirs, files
118115

dvc/utils/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/build.py

dvc/utils/fs.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,7 @@ def move(src, dst, mode=None):
9696
tmp = f"{dst}.{uuid()}"
9797

9898
if os.path.islink(src):
99-
# readlink does not accept path-like obj for Windows in Python <3.8
100-
shutil.copy(os.readlink(os.fspath(src)), tmp)
99+
shutil.copy(src, tmp)
101100
os.unlink(src)
102101
else:
103102
shutil.move(src, tmp)

dvc/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import os
77
import subprocess
88

9-
_BASE_VERSION = "1.1.11"
9+
_BASE_VERSION = "1.2.1"
1010

1111

1212
def _generate_version(base_version):

scripts/hooks/hook-dvc.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# https://github.com/pypa/setuptools/issues/1963
2+
hiddenimports = ["pkg_resources.py2_warn"]

setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ count=true
1717
[isort]
1818
include_trailing_comma=true
1919
known_first_party=dvc,tests
20-
known_third_party=PyInstaller,RangeHTTPServer,boto3,colorama,configobj,distro,dpath,flaky,flufl,funcy,git,grandalf,mock,moto,nanotime,networkx,packaging,pathspec,pygtrie,pylint,pytest,requests,ruamel,setuptools,shortuuid,shtab,tqdm,voluptuous,yaml,zc
20+
known_third_party=PyInstaller,RangeHTTPServer,boto3,colorama,configobj,distro,dpath,flaky,flufl,funcy,git,grandalf,mock,moto,nanotime,networkx,packaging,pathspec,pygtrie,pylint,pytest,requests,ruamel,setuptools,shortuuid,shtab,toml,tqdm,voluptuous,yaml,zc
2121
line_length=79
2222
force_grid_wrap=0
2323
use_parentheses=True

setup.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ def run(self):
6262
"appdirs>=1.4.3",
6363
"PyYAML>=5.1.2,<5.4", # Compatibility with awscli
6464
"ruamel.yaml>=0.16.1",
65+
"toml>=0.10.1",
6566
"funcy>=1.14",
6667
"pathspec>=0.6.0",
6768
"shortuuid>=0.5.0",
@@ -74,7 +75,7 @@ def run(self):
7475
"networkx>=2.1,<2.5",
7576
"pydot>=1.2.4",
7677
"speedcopy>=2.0.1; python_version < '3.8' and sys_platform == 'win32'",
77-
"flatten_json>=0.1.6",
78+
"flatten_json>=0.1.6,<0.1.8",
7879
"tabulate>=0.8.7",
7980
"pygtrie==2.3.2",
8081
"dpath>=2.0.1,<3",

tests/func/params/test_show.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,16 @@ def test_show(tmp_dir, dvc):
1414
assert dvc.params.show() == {"": {"params.yaml": {"foo": "bar"}}}
1515

1616

17+
def test_show_toml(tmp_dir, dvc):
18+
tmp_dir.gen("params.toml", "[foo]\nbar = 42\nbaz = [1, 2]\n")
19+
dvc.run(
20+
cmd="echo params.toml", params=["params.toml:foo"], single_stage=True
21+
)
22+
assert dvc.params.show() == {
23+
"": {"params.toml": {"foo": {"bar": 42, "baz": [1, 2]}}}
24+
}
25+
26+
1727
def test_show_multiple(tmp_dir, dvc):
1828
tmp_dir.gen("params.yaml", "foo: bar\nbaz: qux\n")
1929
dvc.run(

0 commit comments

Comments
 (0)