metrics: properly handle explicitly specified file

Related to #865

Signed-off-by: Ruslan Kuprieiev <[email protected]>
This commit is contained in:
Ruslan Kuprieiev
2018-07-22 21:34:03 +03:00
committed by Ruslan Kuprieiev
parent 4bbbdb334c
commit 9e576cd55b
3 changed files with 92 additions and 27 deletions
+13 -2
View File
@@ -14,6 +14,17 @@ class DvcException(Exception):
class UnsupportedRemoteError(DvcException):
def __init__(self, config, cause=None):
def __init__(self, config):
msg = "Remote '{}' is not supported.".format(config)
super(UnsupportedRemoteError, self).__init__(msg, cause=cause)
super(UnsupportedRemoteError, self).__init__(msg)
class OutputDuplicationError(DvcException):
def __init__(self, output, stages):
assert isinstance(output, str)
assert isinstance(stages, list)
assert all(isinstance(stage, str) for stage in stages)
msg = "File '{}' is specified as an output in more than one stage: {}"
super(OutputDuplicationError, self).__init__(msg.format(output,
str(stages)))
+43 -25
View File
@@ -455,6 +455,21 @@ class Project(object):
return ret
def _find_output_by_path(self, path, outs=None):
from dvc.exceptions import OutputDuplicationError
if not outs:
outs = [out for stage in self.active_stages() for out in stage.outs]
abs_path = os.path.abspath(path)
matched = [out for out in outs if out.path == abs_path]
stages = [out.stage.path for out in matched]
if len(stages) > 1:
raise OutputDuplicationError(path, stages)
return matched[0] if matched else None
def metrics_show(self, path=None, json_path=None,
tsv_path=None,
htsv_path=None,
@@ -464,9 +479,20 @@ class Project(object):
res = {}
for branch in self.scm.brancher(all_branches=all_branches):
outs = [out for stage in self.active_stages() for out in stage.outs]
metrics = filter(lambda o: o.metric, outs)
fnames = [path] if path else map(lambda o: o.path, metrics)
if path:
out = self._find_output_by_path(path, outs=outs)
stage = out.stage.path if out else None
fnames = [path]
else:
metrics = filter(lambda o: o.metric, outs)
stage = None
fnames = map(lambda o: o.path, metrics)
for fname in fnames:
if stage:
self.checkout(stage)
rel = os.path.relpath(fname)
metric = self._read_metric(fname,
json_path=json_path,
@@ -491,7 +517,6 @@ class Project(object):
if res:
return res
if path:
msg = 'File \'{}\' does not exist'.format(path)
else:
@@ -500,32 +525,25 @@ class Project(object):
raise DvcException(msg)
def _metrics_modify(self, path, val):
apath = os.path.abspath(path)
found = False
for stage in self.stages():
for out in stage.outs:
if apath != out.path:
continue
found = True
if out.path_info['scheme'] != 'local':
msg = 'Output \'{}\' scheme \'{}\' is not supported for metrics'
raise DvcException(msg.format(out.path, out.path_info['scheme']))
if out.use_cache:
msg = 'Cached output \'{}\' is not supported for metrics'
raise DvcException(msg.format(out.rel_path))
out.metric = val
out._verify_metric()
stage.dump()
if not found:
out = self._find_output_by_path(path)
if not out:
msg = 'Unable to find file \'{}\' in the pipeline'.format(path)
raise DvcException(msg)
if out.path_info['scheme'] != 'local':
msg = 'Output \'{}\' scheme \'{}\' is not supported for metrics'
raise DvcException(msg.format(out.path, out.path_info['scheme']))
if out.use_cache:
msg = 'Cached output \'{}\' is not supported for metrics'
raise DvcException(msg.format(out.rel_path))
out.metric = val
out._verify_metric()
out.stage.dump()
def metrics_add(self, path):
self._metrics_modify(path, True)
+36
View File
@@ -1,6 +1,7 @@
import os
import json
from dvc.project import Project
from dvc.main import main
from dvc.exceptions import DvcException
from tests.basic_env import TestDvc
@@ -159,3 +160,38 @@ class TestNoMetrics(TestDvc):
def test(self):
ret = main(['metrics', 'show'])
self.assertNotEqual(ret, 0)
class TestCachedMetrics(TestDvc):
def _do_write(self, branch):
self.dvc.scm.checkout(branch)
self.dvc.checkout()
with open('metrics.json', 'w+') as fd:
json.dump({'metrics': branch}, fd)
stage = self.dvc.add('metrics.json')
self.assertNotEqual(stage, None)
self.dvc.scm.add(['.gitignore', 'metrics.json.dvc'])
self.dvc.scm.commit(branch)
def test(self):
self.dvc.scm.commit('init')
self.dvc.scm.branch('one')
self.dvc.scm.branch('two')
self._do_write('master')
self._do_write('one')
self._do_write('two')
self.dvc = Project('.')
res = self.dvc.metrics_show('metrics.json',
all_branches=True,
json_path='metrics')
self.assertEqual(res, {"master": {"metrics.json": ["master"]},
"one": {"metrics.json": ["one"]},
"two": {"metrics.json": ["two"]}})