mirror of
https://github.com/Stone-Red-Code/dvc.git
synced 2026-11-05 15:28:27 +01:00
dvc find command
This commit is contained in:
+28
@@ -4,6 +4,7 @@ import sys
|
||||
import argparse
|
||||
from multiprocessing import cpu_count
|
||||
|
||||
from dvc.command.find import CmdFind
|
||||
from dvc.command.init import CmdInit
|
||||
from dvc.command.remove import CmdRemove
|
||||
from dvc.command.run import CmdRun
|
||||
@@ -420,6 +421,33 @@ def parse_args(argv=None):
|
||||
help='Checkout')
|
||||
checkout_parser.set_defaults(func=CmdCheckout)
|
||||
|
||||
# Find
|
||||
find_parser = subparsers.add_parser(
|
||||
'find',
|
||||
parents=[parent_parser],
|
||||
help='Find branch name')
|
||||
find_parser.set_defaults(func=CmdFind)
|
||||
find_parser.add_argument(
|
||||
'target',
|
||||
nargs='?',
|
||||
help='Target metric file.')
|
||||
find_parser.add_argument(
|
||||
'-r',
|
||||
'--branch-regex',
|
||||
help='Branch name regexp.')
|
||||
find_parser.add_argument(
|
||||
'-c',
|
||||
'--criteria',
|
||||
help='Search criteria. By default it finds max.',
|
||||
choices=['max', 'min', 'all'],
|
||||
default='max')
|
||||
find_parser.add_argument(
|
||||
'-s',
|
||||
'--show-value',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help='Show metrics value.')
|
||||
|
||||
if isinstance(argv, str):
|
||||
argv = argv.split()
|
||||
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
from dvc import utils
|
||||
from dvc.command.base import CmdBase, DvcLock
|
||||
from dvc.executor import ExecutorError
|
||||
from dvc.logger import Logger
|
||||
|
||||
|
||||
class CmdFind(CmdBase):
|
||||
def __init__(self, settings):
|
||||
super(CmdFind, self).__init__(settings)
|
||||
|
||||
def run(self):
|
||||
with DvcLock(self.is_locker, self.git):
|
||||
fname = self.parsed_args.target
|
||||
regex = self.parsed_args.branch_regex
|
||||
|
||||
branches = self.git.branches(regex)
|
||||
metrics = [self.read_metrics(fname, b) for b in branches]
|
||||
|
||||
zipped = zip(metrics, branches)
|
||||
res = self.filter(zipped, self.parsed_args.criteria)
|
||||
res = filter(lambda x: x[0] is not None, res)
|
||||
|
||||
for (val, branch) in res:
|
||||
if self.parsed_args.show_value:
|
||||
print('{}: {}'.format(branch, val))
|
||||
else:
|
||||
print(branch)
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
def filter(zipped, criteria):
|
||||
if criteria == 'max':
|
||||
res = [max(zipped, key=lambda t: t[0])]
|
||||
elif criteria == 'min':
|
||||
res = [min(zipped, key=lambda t: t[0])]
|
||||
else:
|
||||
res = zipped
|
||||
return res
|
||||
|
||||
def read_metrics(self, fname, branch):
|
||||
try:
|
||||
lines = self.git.get_file_content(fname, branch).split('\n')
|
||||
except ExecutorError as ex:
|
||||
msg = 'Unable to read metrics file from branch {}: {}'
|
||||
data_item = self.settings.path_factory.data_item(fname)
|
||||
|
||||
try:
|
||||
self.git.get_file_content(data_item.state.relative, branch)
|
||||
Logger.error(msg.format(branch, 'this is data file, not metric file'))
|
||||
except ExecutorError:
|
||||
Logger.error(msg.format(branch, 'file does not exist in this branch'))
|
||||
return None
|
||||
|
||||
return None
|
||||
|
||||
metric = utils.parse_target_metric(lines)
|
||||
if not metric:
|
||||
msg = 'Unable to parse metrics from the first line of file {} in branch {}'
|
||||
Logger.error(msg.format(fname, branch))
|
||||
return None
|
||||
|
||||
return metric
|
||||
@@ -444,3 +444,24 @@ class GitWrapper(GitWrapperI):
|
||||
git_cmd = u'git ls-tree --name-only -r {}'.format(commit)
|
||||
lines = Executor.exec_cmd_only_success(git_cmd.split()).split('\n')
|
||||
return filter(lambda s: s.startswith(ConfigI.STATE_DIR), lines)
|
||||
|
||||
@staticmethod
|
||||
def branches(regexp):
|
||||
cmd = 'git branch --format "%(refname)"'
|
||||
lines = Executor.exec_cmd_only_success(cmd.split()).split('\n')
|
||||
lines = map(lambda l: l.strip('"'), lines)
|
||||
|
||||
if not regexp or '/' not in regexp:
|
||||
# remove remotes like 'refs/heads'
|
||||
lines = map(lambda s: s.split('/')[-1], lines)
|
||||
|
||||
if not regexp:
|
||||
return lines
|
||||
|
||||
reg = re.compile(regexp)
|
||||
return filter(reg.search, lines)
|
||||
|
||||
@staticmethod
|
||||
def get_file_content(fname, branch):
|
||||
cmd = 'git show {}:{}'.format(branch, fname)
|
||||
return Executor.exec_cmd_only_success(cmd.split())
|
||||
|
||||
+5
-22
@@ -4,6 +4,7 @@ import sys
|
||||
import json
|
||||
import re
|
||||
|
||||
from dvc import utils
|
||||
from dvc.exceptions import DvcException
|
||||
from dvc.path.data_item import DataDirError
|
||||
from dvc.system import System
|
||||
@@ -18,8 +19,6 @@ class StateFile(object):
|
||||
MAGIC = 'DVC-State'
|
||||
VERSION = '0.1'
|
||||
|
||||
FLOATS_FROM_STRING = re.compile(r'[-+]?(?:(?:\d*\.\d+)|(?:\d+\.?))(?:[Ee][+-]?\d+)?')
|
||||
|
||||
DVC_PYTHON_FILE_NAME = 'dvc.py'
|
||||
DVC_COMMAND = 'dvc'
|
||||
|
||||
@@ -110,29 +109,13 @@ class StateFile(object):
|
||||
if target_metric:
|
||||
self.target_metrics[StateFile.TARGET_METRICS_SINGLE_METRIC] = target_metric
|
||||
|
||||
@staticmethod
|
||||
def parse_target_metric(file_name):
|
||||
fd = open(file_name, 'r')
|
||||
|
||||
try:
|
||||
lines = fd.readlines(2)
|
||||
except Exception as e:
|
||||
raise StateFileError('Unable to parse target file')
|
||||
|
||||
if len(lines) != 1:
|
||||
raise StateFileError('file contains more then one line')
|
||||
|
||||
# Extract float from string. I.e. from 'AUC: 0.596182'
|
||||
nums = StateFile.FLOATS_FROM_STRING.findall(lines[0])
|
||||
if len(nums) < 1:
|
||||
raise StateFileError("Unable to parse metrics from the first line")
|
||||
|
||||
return float(nums[0])
|
||||
|
||||
@staticmethod
|
||||
def try_parse_target_metric(file_name):
|
||||
try:
|
||||
return StateFile.parse_target_metric(file_name)
|
||||
metric = utils.parse_target_metric_file(file_name)
|
||||
if not metric:
|
||||
raise StateFileError('Unable to parse metrics from the first line of file {}'.format(file_name))
|
||||
return metric
|
||||
except StateFileError:
|
||||
return None
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
Helpers for other modules.
|
||||
"""
|
||||
import os
|
||||
import re
|
||||
import stat
|
||||
import shutil
|
||||
|
||||
@@ -13,6 +14,7 @@ from dvc.logger import Logger
|
||||
|
||||
LOCAL_CHUNK_SIZE = 1024*1024*1024
|
||||
|
||||
|
||||
def cached_property(func):
|
||||
'''A decorator for caching properties in classes.'''
|
||||
def get(self):
|
||||
@@ -82,3 +84,27 @@ def map_progress(func, targets, n_threads):
|
||||
progress.finish()
|
||||
|
||||
return zip(targets, ret)
|
||||
|
||||
|
||||
def parse_target_metric_file(file_name):
|
||||
with open(file_name, 'r') as fd:
|
||||
try:
|
||||
lines = fd.readlines(2)
|
||||
except Exception:
|
||||
return None
|
||||
return parse_target_metric(lines)
|
||||
|
||||
|
||||
FLOATS_FROM_STRING = re.compile(r'[-+]?(?:(?:\d*\.\d+)|(?:\d+\.?))(?:[Ee][+-]?\d+)?')
|
||||
|
||||
|
||||
def parse_target_metric(lines):
|
||||
if len(lines) != 1:
|
||||
return None
|
||||
|
||||
# Extract float from string. I.e. from 'AUC: 0.596182'
|
||||
nums = FLOATS_FROM_STRING.findall(lines[0])
|
||||
if len(nums) < 1:
|
||||
return None
|
||||
|
||||
return float(nums[0])
|
||||
|
||||
Reference in New Issue
Block a user