mirror of
https://github.com/Stone-Red-Code/dvc.git
synced 2026-11-02 07:18:25 +01:00
repro: introduce --dry option
Fixes #972 Related to #894 Signed-off-by: Ruslan Kuprieiev <[email protected]>
This commit is contained in:
@@ -324,6 +324,12 @@ def parse_args(argv=None):
|
||||
action='store_true',
|
||||
default=False,
|
||||
help='Show metrics after reproduction')
|
||||
repro_parser.add_argument(
|
||||
'--dry',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help='Only print the commands that would be executed '
|
||||
'without actually executing.')
|
||||
repro_parser.set_defaults(func=CmdRepro)
|
||||
|
||||
# Remove
|
||||
|
||||
@@ -17,7 +17,8 @@ class CmdRepro(CmdBase):
|
||||
try:
|
||||
self.project.reproduce(target,
|
||||
recursive=recursive,
|
||||
force=self.args.force)
|
||||
force=self.args.force,
|
||||
dry=self.args.dry)
|
||||
if self.args.metrics:
|
||||
self.project.metrics_show()
|
||||
except ReproductionError as ex:
|
||||
|
||||
+11
-8
@@ -235,7 +235,7 @@ class Project(object):
|
||||
stage.dump()
|
||||
return stage
|
||||
|
||||
def _reproduce_stage(self, stages, node, force):
|
||||
def _reproduce_stage(self, stages, node, force, dry):
|
||||
stage = stages[node]
|
||||
|
||||
if stage.locked:
|
||||
@@ -243,13 +243,16 @@ class Project(object):
|
||||
'going to be reproduced.'
|
||||
self.logger.warn(msg.format(stage.relpath))
|
||||
|
||||
stage = stage.reproduce(force=force)
|
||||
stage = stage.reproduce(force=force, dry=dry)
|
||||
if not stage:
|
||||
return []
|
||||
stage.dump()
|
||||
|
||||
if not dry:
|
||||
stage.dump()
|
||||
|
||||
return [stage]
|
||||
|
||||
def reproduce(self, target, recursive=True, force=False):
|
||||
def reproduce(self, target, recursive=True, force=False, dry=False):
|
||||
import networkx as nx
|
||||
|
||||
G = self.graph()[1]
|
||||
@@ -259,17 +262,17 @@ class Project(object):
|
||||
raise NotDvcFileError(target)
|
||||
|
||||
if recursive:
|
||||
return self._reproduce_stages(G, stages, node, force)
|
||||
return self._reproduce_stages(G, stages, node, force, dry)
|
||||
|
||||
return self._reproduce_stage(stages, node, force)
|
||||
return self._reproduce_stage(stages, node, force, dry)
|
||||
|
||||
def _reproduce_stages(self, G, stages, node, force):
|
||||
def _reproduce_stages(self, G, stages, node, force, dry):
|
||||
import networkx as nx
|
||||
|
||||
result = []
|
||||
for n in nx.dfs_postorder_nodes(G, node):
|
||||
try:
|
||||
result += self._reproduce_stage(stages, n, force)
|
||||
result += self._reproduce_stage(stages, n, force, dry)
|
||||
except Exception as ex:
|
||||
raise ReproductionError(stages[n].relpath, ex)
|
||||
return result
|
||||
|
||||
+21
-16
@@ -150,17 +150,17 @@ class Stage(object):
|
||||
self.remove_outs(ignore_remove=True)
|
||||
os.unlink(self.path)
|
||||
|
||||
def reproduce(self, force=False):
|
||||
def reproduce(self, force=False, dry=False):
|
||||
if not self.changed() and not force:
|
||||
return None
|
||||
|
||||
if (self.cmd or self.is_import) and not self.locked:
|
||||
if (self.cmd or self.is_import) and not self.locked and not dry:
|
||||
# Removing outputs only if we actually have command to reproduce
|
||||
self.remove_outs(ignore_remove=False)
|
||||
|
||||
self.project.logger.info(u'Reproducing \'{}\''.format(self.relpath))
|
||||
|
||||
self.run()
|
||||
self.run(dry=dry)
|
||||
|
||||
msg = u'\'{}\' was reproduced'.format(self.relpath)
|
||||
self.project.logger.debug(msg)
|
||||
@@ -297,35 +297,40 @@ class Stage(object):
|
||||
for out in self.outs:
|
||||
out.save()
|
||||
|
||||
def run(self):
|
||||
def run(self, dry=False):
|
||||
if self.locked:
|
||||
msg = u'Verifying outputs in locked stage \'{}\''
|
||||
self.project.logger.info(msg.format(self.relpath))
|
||||
self.check_missing_outputs()
|
||||
if not dry:
|
||||
self.check_missing_outputs()
|
||||
elif self.is_import:
|
||||
msg = u'Importing \'{}\' -> \'{}\''
|
||||
self.project.logger.info(msg.format(self.deps[0].path,
|
||||
self.outs[0].path))
|
||||
|
||||
self.deps[0].download(self.outs[0].path_info)
|
||||
if not dry:
|
||||
self.deps[0].download(self.outs[0].path_info)
|
||||
elif self.is_data_source:
|
||||
msg = u'Verifying data sources in \'{}\''.format(self.relpath)
|
||||
self.project.logger.info(msg)
|
||||
self.check_missing_outputs()
|
||||
if not dry:
|
||||
self.check_missing_outputs()
|
||||
else:
|
||||
msg = u'Running command:\n\t{}'.format(self.cmd)
|
||||
self.project.logger.info(msg)
|
||||
|
||||
p = subprocess.Popen(self.cmd,
|
||||
cwd=self.cwd,
|
||||
shell=True,
|
||||
env=os.environ,
|
||||
executable=os.getenv('SHELL'))
|
||||
p.communicate()
|
||||
if p.returncode != 0:
|
||||
raise StageCmdFailedError(self)
|
||||
if not dry:
|
||||
p = subprocess.Popen(self.cmd,
|
||||
cwd=self.cwd,
|
||||
shell=True,
|
||||
env=os.environ,
|
||||
executable=os.getenv('SHELL'))
|
||||
p.communicate()
|
||||
if p.returncode != 0:
|
||||
raise StageCmdFailedError(self)
|
||||
|
||||
self.save()
|
||||
if not dry:
|
||||
self.save()
|
||||
|
||||
def check_missing_outputs(self):
|
||||
outs = [out for out in self.outs if not out.exists]
|
||||
|
||||
@@ -125,6 +125,19 @@ class TestReproChangedData(TestRepro):
|
||||
shutil.copyfile(self.BAR, self.FOO)
|
||||
|
||||
|
||||
class TestReproDry(TestReproChangedData):
|
||||
def test(self):
|
||||
self.swap_foo_with_bar()
|
||||
|
||||
stages = self.dvc.reproduce(self.file1_stage, dry=True)
|
||||
|
||||
self.assertTrue(len(stages), 2)
|
||||
self.assertFalse(filecmp.cmp(self.file1, self.BAR))
|
||||
|
||||
ret = main(['repro', '--dry'])
|
||||
self.assertFalse(filecmp.cmp(self.file1, self.BAR))
|
||||
|
||||
|
||||
class TestReproChangedDeepData(TestReproChangedData):
|
||||
def test(self):
|
||||
file2 = 'file2'
|
||||
|
||||
Reference in New Issue
Block a user