dvc-utils 0.0.3__py3-none-any.whl → 0.0.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dvc_utils/main.py +119 -34
- {dvc_utils-0.0.3.dist-info → dvc_utils-0.0.5.dist-info}/METADATA +12 -6
- dvc_utils-0.0.5.dist-info/RECORD +9 -0
- dvc_utils-0.0.3.dist-info/RECORD +0 -9
- {dvc_utils-0.0.3.dist-info → dvc_utils-0.0.5.dist-info}/LICENSE +0 -0
- {dvc_utils-0.0.3.dist-info → dvc_utils-0.0.5.dist-info}/WHEEL +0 -0
- {dvc_utils-0.0.3.dist-info → dvc_utils-0.0.5.dist-info}/entry_points.txt +0 -0
- {dvc_utils-0.0.3.dist-info → dvc_utils-0.0.5.dist-info}/top_level.txt +0 -0
dvc_utils/main.py
CHANGED
@@ -1,14 +1,11 @@
|
|
1
1
|
from functools import cache
|
2
2
|
from os import environ as env, getcwd
|
3
|
-
|
4
|
-
from typing import Optional, Tuple
|
5
|
-
|
6
|
-
import shlex
|
7
3
|
from os.path import join, relpath
|
4
|
+
import shlex
|
5
|
+
from subprocess import Popen, PIPE
|
6
|
+
from typing import Optional, Tuple
|
8
7
|
|
9
8
|
from click import option, argument, group
|
10
|
-
from subprocess import Popen
|
11
|
-
|
12
9
|
import click
|
13
10
|
import yaml
|
14
11
|
from utz import process, singleton, err
|
@@ -52,7 +49,7 @@ def dvc_cache_dir(log: bool = False) -> str:
|
|
52
49
|
def dvc_md5(git_ref: str, dvc_path: str, log: bool = False) -> str:
|
53
50
|
dir_path = get_dir_path()
|
54
51
|
dir_path = '' if dir_path == '.' else f'{dir_path}/'
|
55
|
-
dvc_spec = process.output('git', 'show', f'{git_ref}:{dir_path}{dvc_path}', log=log)
|
52
|
+
dvc_spec = process.output('git', 'show', f'{git_ref}:{dir_path}{dvc_path}', log=err if log else None)
|
56
53
|
dvc_obj = yaml.safe_load(dvc_spec)
|
57
54
|
out = singleton(dvc_obj['outs'], dedupe=False)
|
58
55
|
md5 = out['md5']
|
@@ -72,29 +69,105 @@ def dvc_cache_path(ref: str, dvc_path: Optional[str] = None, log: bool = False)
|
|
72
69
|
return join(dvc_cache_dir(log=log), 'files', 'md5', dirname, basename)
|
73
70
|
|
74
71
|
|
75
|
-
def diff_cmds(
|
76
|
-
|
72
|
+
def diff_cmds(
|
73
|
+
cmds1: list[str],
|
74
|
+
cmds2: list[str],
|
75
|
+
verbose: bool = False,
|
76
|
+
color: bool = False,
|
77
|
+
unified: int | None = None,
|
78
|
+
ignore_whitespace: bool = False,
|
79
|
+
**kwargs,
|
80
|
+
):
|
81
|
+
"""Run two sequences of piped commands and diff their output.
|
82
|
+
|
83
|
+
Args:
|
84
|
+
cmds1: First sequence of commands to pipe together
|
85
|
+
cmds2: Second sequence of commands to pipe together
|
86
|
+
verbose: Whether to print commands being executed
|
87
|
+
color: Whether to show colored diff output
|
88
|
+
unified: Number of unified context lines, or None
|
89
|
+
ignore_whitespace: Whether to ignore whitespace changes
|
90
|
+
**kwargs: Additional arguments passed to subprocess.Popen
|
91
|
+
|
92
|
+
Each command sequence will be piped together before being compared.
|
93
|
+
For example, if cmds1 = ['cat foo.txt', 'sort'], the function will
|
94
|
+
execute 'cat foo.txt | sort' before comparing with cmds2's output.
|
77
95
|
|
78
96
|
Adapted from https://stackoverflow.com/a/28840955"""
|
79
97
|
with named_pipes(n=2) as pipes:
|
80
98
|
(pipe1, pipe2) = pipes
|
81
|
-
|
99
|
+
diff_cmd = [
|
100
|
+
'diff',
|
101
|
+
*(['-w'] if ignore_whitespace else []),
|
102
|
+
*(['-U', str(unified)] if unified is not None else []),
|
103
|
+
*(['--color=always'] if color else []),
|
104
|
+
pipe1,
|
105
|
+
pipe2,
|
106
|
+
]
|
107
|
+
diff = Popen(diff_cmd)
|
82
108
|
processes = []
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
109
|
+
|
110
|
+
for pipe, cmds in ((pipe1, cmds1), (pipe2, cmds2)):
|
111
|
+
if verbose:
|
112
|
+
err(f"Running pipeline: {' | '.join(cmds)}")
|
113
|
+
|
114
|
+
# Create the pipeline of processes
|
115
|
+
prev_process = None
|
116
|
+
for i, cmd in enumerate(cmds):
|
117
|
+
is_last = i + 1 == len(cmds)
|
118
|
+
|
119
|
+
# For the first process, take input from the original source
|
120
|
+
stdin = None if prev_process is None else prev_process.stdout
|
121
|
+
|
122
|
+
# For the last process, output to the named pipe
|
123
|
+
if is_last:
|
124
|
+
with open(pipe, 'wb', 0) as pipe_fd:
|
125
|
+
proc = Popen(
|
126
|
+
cmd,
|
127
|
+
stdin=stdin,
|
128
|
+
stdout=pipe_fd,
|
129
|
+
close_fds=True,
|
130
|
+
**kwargs
|
131
|
+
)
|
132
|
+
# For intermediate processes, output to a pipe
|
133
|
+
else:
|
134
|
+
proc = Popen(
|
135
|
+
cmd,
|
136
|
+
stdin=stdin,
|
137
|
+
stdout=PIPE,
|
138
|
+
close_fds=True,
|
139
|
+
**kwargs
|
140
|
+
)
|
141
|
+
|
142
|
+
if prev_process is not None:
|
143
|
+
prev_process.stdout.close()
|
144
|
+
|
145
|
+
processes.append(proc)
|
146
|
+
prev_process = proc
|
147
|
+
|
88
148
|
for p in [diff] + processes:
|
89
149
|
p.wait()
|
90
150
|
|
91
151
|
|
92
152
|
@cli.command('diff', short_help='Diff a DVC-tracked file at two commits (or one commit vs. current worktree), optionally passing both through another command first')
|
153
|
+
@option('-c', '--color', is_flag=True, help='Colorize the output')
|
93
154
|
@option('-r', '--refspec', default='HEAD', help='<commit 1>..<commit 2> (compare two commits) or <commit> (compare <commit> to the worktree)')
|
94
155
|
@option('-S', '--no-shell', is_flag=True, help="Don't pass `shell=True` to Python `subprocess`es")
|
156
|
+
@option('-U', '--unified', type=int, help='Number of lines of context to show (passes through to `diff`)')
|
95
157
|
@option('-v', '--verbose', is_flag=True, help="Log intermediate commands to stderr")
|
96
|
-
@
|
97
|
-
|
158
|
+
@option('-w', '--ignore-whitespace', is_flag=True, help="Ignore whitespace differences (pass `-w` to `diff`)")
|
159
|
+
@option('-x', '--exec-cmd', 'exec_cmds', multiple=True, help='Command(s) to execute before diffing; alternate syntax to passing commands as positional arguments')
|
160
|
+
@argument('args', metavar='[exec_cmd...] <path>', nargs=-1)
|
161
|
+
def dvc_utils_diff(
|
162
|
+
color: bool,
|
163
|
+
refspec: str | None,
|
164
|
+
no_shell: bool,
|
165
|
+
unified: int | None,
|
166
|
+
verbose: bool,
|
167
|
+
ignore_whitespace: bool,
|
168
|
+
exec_cmds: Tuple[str, ...],
|
169
|
+
args: Tuple[str, ...],
|
170
|
+
):
|
98
171
|
"""Diff a file at two commits (or one commit vs. current worktree), optionally passing both through `cmd` first
|
99
172
|
|
100
173
|
Examples:
|
@@ -107,14 +180,8 @@ def dvc_utils_diff(refspec, no_shell, verbose, args):
|
|
107
180
|
raise click.UsageError('Must specify [cmd...] <path>')
|
108
181
|
|
109
182
|
shell = not no_shell
|
110
|
-
|
111
|
-
|
112
|
-
cmd = shlex.split(cmd)
|
113
|
-
elif len(args) == 1:
|
114
|
-
cmd = None
|
115
|
-
path, = args
|
116
|
-
else:
|
117
|
-
raise click.UsageError('Maximum 2 positional args: [cmd] <path>')
|
183
|
+
*cmds, path = args
|
184
|
+
cmds = list(exec_cmds) + cmds
|
118
185
|
|
119
186
|
path, dvc_path = dvc_paths(path)
|
120
187
|
|
@@ -131,15 +198,33 @@ def dvc_utils_diff(refspec, no_shell, verbose, args):
|
|
131
198
|
before_path = dvc_cache_path(before, dvc_path, log=log)
|
132
199
|
after_path = path if after is None else dvc_cache_path(after, dvc_path, log=log)
|
133
200
|
|
134
|
-
if
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
201
|
+
if cmds:
|
202
|
+
cmd, *sub_cmds = cmds
|
203
|
+
if not shell:
|
204
|
+
sub_cmds = [ shlex.split(c) for c in sub_cmds ]
|
205
|
+
before_cmds = [
|
206
|
+
shlex.split(f'{cmd} {before_path}'),
|
207
|
+
*sub_cmds,
|
208
|
+
]
|
209
|
+
after_cmds = [
|
210
|
+
shlex.split(f'{cmd} {after_path}'),
|
211
|
+
*sub_cmds,
|
212
|
+
]
|
213
|
+
shell_kwargs = {}
|
214
|
+
else:
|
215
|
+
before_cmds = [ f'{cmd} {before_path}', *sub_cmds ]
|
216
|
+
after_cmds = [ f'{cmd} {after_path}', *sub_cmds ]
|
217
|
+
shell_kwargs = dict(shell=shell)
|
218
|
+
|
219
|
+
diff_cmds(
|
220
|
+
before_cmds,
|
221
|
+
after_cmds,
|
222
|
+
verbose=verbose,
|
223
|
+
color=color,
|
224
|
+
unified=unified,
|
225
|
+
ignore_whitespace=ignore_whitespace,
|
226
|
+
**shell_kwargs,
|
227
|
+
)
|
143
228
|
else:
|
144
229
|
process.run('diff', before_path, after_path, log=log)
|
145
230
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: dvc-utils
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.5
|
4
4
|
Summary: CLI for diffing DVC files at two commits (or one commit vs. current worktree), optionally passing both through another command first
|
5
5
|
Home-page: https://github.com/runsascoded/dvc-utils
|
6
6
|
Author: Ryan Williams
|
@@ -29,6 +29,7 @@ pip install dvc-utils
|
|
29
29
|
```
|
30
30
|
|
31
31
|
## Usage <a id="usage"></a>
|
32
|
+
<!-- `bmdf -- dvc-utils --help` -->
|
32
33
|
```bash
|
33
34
|
dvc-utils --help
|
34
35
|
# Usage: dvc-utils [OPTIONS] COMMAND [ARGS]...
|
@@ -42,6 +43,7 @@ dvc-utils --help
|
|
42
43
|
```
|
43
44
|
|
44
45
|
### `dvc-utils diff` <a id="dvc-utils-diff"></a>
|
46
|
+
<!-- `bmdf -- dvc-utils diff --help` -->
|
45
47
|
```bash
|
46
48
|
dvc-utils diff --help
|
47
49
|
# Usage: dvc-utils diff [OPTIONS] [cmd...] <path>
|
@@ -59,11 +61,15 @@ dvc-utils diff --help
|
|
59
61
|
# optional) at HEAD (last committed value) vs. the current worktree content.
|
60
62
|
#
|
61
63
|
# Options:
|
62
|
-
# -
|
63
|
-
#
|
64
|
-
#
|
65
|
-
# -
|
66
|
-
# --
|
64
|
+
# -c, --color Colorize the output
|
65
|
+
# -r, --refspec TEXT <commit 1>..<commit 2> (compare two commits) or
|
66
|
+
# <commit> (compare <commit> to the worktree)
|
67
|
+
# -S, --no-shell Don't pass `shell=True` to Python `subprocess`es
|
68
|
+
# -U, --unified INTEGER Number of lines of context to show (passes through
|
69
|
+
# to `diff`)
|
70
|
+
# -v, --verbose Log intermediate commands to stderr
|
71
|
+
# -w, --ignore-whitespace Ignore whitespace differences (pass `-w` to `diff`)
|
72
|
+
# --help Show this message and exit.
|
67
73
|
```
|
68
74
|
|
69
75
|
## Examples <a id="examples"></a>
|
@@ -0,0 +1,9 @@
|
|
1
|
+
dvc_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
+
dvc_utils/main.py,sha256=UhCm28Zu_xwsmfzOKgmAYJrCrfumryid_10F_-Rg-2M,7850
|
3
|
+
dvc_utils/named_pipes.py,sha256=VQ2t9BYCazFq_-MABj4t2HS7GHDvSqXXx8fOLz5DsTc,492
|
4
|
+
dvc_utils-0.0.5.dist-info/LICENSE,sha256=ZS8AReay7xmQzBAHwxIuTouGXz3SKgUa2_Sz8Ip0EzQ,1070
|
5
|
+
dvc_utils-0.0.5.dist-info/METADATA,sha256=y46AoDbejlmv318zUD4aKiY9SprlRh9hdCBcoGZ4H4M,6924
|
6
|
+
dvc_utils-0.0.5.dist-info/WHEEL,sha256=eOLhNAGa2EW3wWl_TU484h7q1UNgy0JXjjoqKoxAAQc,92
|
7
|
+
dvc_utils-0.0.5.dist-info/entry_points.txt,sha256=W9OuZ6CX8QF9ojbqLtfXFo8Q2hnJ-zlcGY4_7nO8paM,49
|
8
|
+
dvc_utils-0.0.5.dist-info/top_level.txt,sha256=jT0-PJa2t_eFRE9rn-52AjdnZ8nQeEHllf2kJmaGh80,10
|
9
|
+
dvc_utils-0.0.5.dist-info/RECORD,,
|
dvc_utils-0.0.3.dist-info/RECORD
DELETED
@@ -1,9 +0,0 @@
|
|
1
|
-
dvc_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
-
dvc_utils/main.py,sha256=0rpJptELszWdFLoCKoYOY6VQtAm2mp-3_-kN075T-TE,4743
|
3
|
-
dvc_utils/named_pipes.py,sha256=VQ2t9BYCazFq_-MABj4t2HS7GHDvSqXXx8fOLz5DsTc,492
|
4
|
-
dvc_utils-0.0.3.dist-info/LICENSE,sha256=ZS8AReay7xmQzBAHwxIuTouGXz3SKgUa2_Sz8Ip0EzQ,1070
|
5
|
-
dvc_utils-0.0.3.dist-info/METADATA,sha256=ExDOJDxXQTUrklQfYm6qTdWNW8Le6xknle4mG2sQFpg,6572
|
6
|
-
dvc_utils-0.0.3.dist-info/WHEEL,sha256=eOLhNAGa2EW3wWl_TU484h7q1UNgy0JXjjoqKoxAAQc,92
|
7
|
-
dvc_utils-0.0.3.dist-info/entry_points.txt,sha256=W9OuZ6CX8QF9ojbqLtfXFo8Q2hnJ-zlcGY4_7nO8paM,49
|
8
|
-
dvc_utils-0.0.3.dist-info/top_level.txt,sha256=jT0-PJa2t_eFRE9rn-52AjdnZ8nQeEHllf2kJmaGh80,10
|
9
|
-
dvc_utils-0.0.3.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|