Compare commits

..
35 Commits
Author SHA1 Message Date
Ruslan Kuprieiev 51c9357cd2 dvc: bump to 0.26.0 2019-02-10 21:51:16 +02:00
Ruslan Kuprieiev 9bfff1f957 Merge pull request #1600 from iterative/cached-metrics
Cached metrics
2019-02-10 21:33:04 +02:00
Ivan Shcheklein 41befea210 address PR review comments 2019-02-10 11:06:23 -08:00
Ruslan Kuprieiev fbd5393a2d appveyor: add chocolatey cache 2019-02-10 07:50:51 +02:00
Ivan Shcheklein 8817864e7a fix one more test on windows 2019-02-09 20:04:35 -08:00
Ivan Shcheklein bd28b711f9 minor: remove debug print 2019-02-09 19:58:10 -08:00
Ivan Shcheklein 5563f13273 fix metrics tests on windows 2019-02-09 19:57:41 -08:00
Ivan Shcheklein 36c4a3a5af add metrics tests 2019-02-09 19:00:40 -08:00
Ivan Shcheklein dc1242a107 fix error messages in metrics show 2019-02-09 18:57:15 -08:00
Ivan Shcheklein 17c6858ad3 minor: better variable names in stage class 2019-02-09 18:56:17 -08:00
Ivan Shcheklein dab347f424 fix few checks and messages in dvc run 2019-02-09 18:54:54 -08:00
Ivan Shcheklein 3287c3f407 add ability run tests from specified file(s) 2019-02-09 18:52:46 -08:00
Ivan Shcheklein 0ed44cf151 fix formatting 2019-02-09 15:44:12 -08:00
Ivan Shcheklein 2419f94ef3 use python3 in hook 2019-02-09 15:00:16 -08:00
Ruslan Kuprieiev 1eee7e3226 travis: enable cron builds
Signed-off-by: Ruslan Kuprieiev <[email protected]>
2019-02-09 23:52:15 +02:00
Ivan Shcheklein 16570bad03 add cached metrics, cleanup warnings 2019-02-09 13:48:18 -08:00
Ruslan Kuprieiev fd2f9119e3 Merge pull request #1597 from mroutis/fix-1484
remote: define command error on the base
2019-02-09 22:46:45 +02:00
Ruslan Kuprieiev cbf24cd279 dvc: bump to 0.25.4 2019-02-09 21:07:57 +02:00
Ruslan Kuprieiev 8b35c69a95 dvc: generate version even without gitpython 2019-02-09 21:07:17 +02:00
Mr. Outis ec77bfa1af remote: define command error on the base
fix: #1484
2019-02-09 12:03:17 -06:00
Ruslan Kuprieiev bac1393fc2 Merge pull request #1592 from mroutis/fix-1591
remote: notify when operation is not supported
2019-02-09 08:44:19 +02:00
Ruslan Kuprieiev cb8e24237f windows: gen_config: don't use compat open 2019-02-09 07:31:59 +02:00
Ruslan Kuprieiev 5b548d679c dvc: bump to 0.25.3 2019-02-09 07:04:03 +02:00
Ruslan Kuprieiev 005239ade7 py2: cast env keys to bytes
Signed-off-by: Ruslan Kuprieiev <[email protected]>
2019-02-09 06:59:21 +02:00
Ruslan Kuprieiev b67546d648 dvc: bump to 0.25.2 2019-02-09 04:17:56 +02:00
Ruslan Kuprieiev 40c84fe4ed py2: fix environ issue
Signed-off-by: Ruslan Kuprieiev <[email protected]>
2019-02-09 03:59:45 +02:00
Mr. Outis 0d51d10ccf remote: notify when operation is not supported
close #1591
2019-02-08 19:54:54 -06:00
Ruslan Kuprieiev 9210e66b0c dvc: bump to 0.25.1 2019-02-09 03:06:18 +02:00
Ruslan Kuprieiev d3bb4f85df travis: don't forget to init pyenv
Signed-off-by: Ruslan Kuprieiev <[email protected]>
2019-02-09 02:12:17 +02:00
Ruslan Kuprieiev cbce38074e travis: fix typo
Signed-off-by: Ruslan Kuprieiev <[email protected]>
2019-02-09 02:01:52 +02:00
Ruslan Kuprieiev 1bb101d250 travis: debug pyenv
Signed-off-by: Ruslan Kuprieiev <[email protected]>
2019-02-09 01:40:58 +02:00
Ruslan Kuprieiev 1d272f156f config: handle run outside of dvc repo
Signed-off-by: Ruslan Kuprieiev <[email protected]>
2019-02-09 01:38:49 +02:00
Ruslan Kuprieiev 05bd4d4dc4 travis: use pyenv shell
Signed-off-by: Ruslan Kuprieiev <[email protected]>
2019-02-08 22:20:41 +02:00
Ruslan Kuprieiev e22da1e957 travis: fix typo
Signed-off-by: Ruslan Kuprieiev <[email protected]>
2019-02-08 21:52:13 +02:00
Ruslan Kuprieiev 4f36ce35ad travis: activate pyenv
Signed-off-by: Ruslan Kuprieiev <[email protected]>
2019-02-08 21:39:15 +02:00
24 changed files with 271 additions and 106 deletions
+4
View File
@@ -3,6 +3,10 @@ branches:
- master
- devel
cache:
- C:\ProgramData\chocolatey\bin -> appveyor.yml
- C:\ProgramData\chocolatey\lib -> appveyor.yml
platform: x86
environment:
+2 -2
View File
@@ -3,9 +3,9 @@ repos:
rev: stable
hooks:
- id: black
language_version: python3.6
language_version: python3
- repo: https://gitlab.com/pycqa/flake8
rev: master
hooks:
- id: flake8
language_version: python3.6
language_version: python3
+1 -5
View File
@@ -62,12 +62,8 @@ matrix:
- PYTHON_CONFIGURE_OPTS="--enable-framework"
before_install:
- bash ./scripts/ci/before_install.sh
- if [[ -n "$PYTHON_VER" ]]; then
eval "$(pyenv init -)";
pyenv install --skip-existing --keep --verbose "$PYTHON_VER";
pyenv shell "$PYTHON_VER";
fi
- source env.sh
- python --version
install:
- bash ./scripts/ci/install.sh
script:
+6 -3
View File
@@ -10,7 +10,7 @@ import os
import warnings
VERSION_BASE = "0.25.0"
VERSION_BASE = "0.26.0"
__version__ = VERSION_BASE
PACKAGEPATH = os.path.abspath(os.path.dirname(__file__))
@@ -20,8 +20,11 @@ VERSIONPATH = os.path.join(PACKAGEPATH, "version.py")
def _update_version_file():
"""Dynamically update version file."""
from git import Repo
from git.exc import InvalidGitRepositoryError
try:
from git import Repo
from git.exc import InvalidGitRepositoryError
except ImportError:
return __version__
try:
repo = Repo(HOMEPATH)
+16 -5
View File
@@ -17,12 +17,14 @@ class CmdRun(CmdBase):
self.args.deps,
self.args.outs,
self.args.outs_no_cache,
self.args.metrics,
self.args.metrics_no_cache,
self.args.command,
]
): # pragma: no cover
logger.error(
"too few arguments. Specify at least one: '-d', "
"'-o', '-O', 'command'."
"'-o', '-O', '-m', '-M', 'command'."
)
return 1
@@ -31,6 +33,7 @@ class CmdRun(CmdBase):
cmd=self._parsed_cmd(),
outs=self.args.outs,
outs_no_cache=self.args.outs_no_cache,
metrics=self.args.metrics,
metrics_no_cache=self.args.metrics_no_cache,
deps=self.args.deps,
fname=self.args.file,
@@ -88,22 +91,30 @@ def add_parser(subparsers, parent_parser):
"--outs",
action="append",
default=[],
help="Declare output data file or data directory.",
help="Declare output file or directory.",
)
run_parser.add_argument(
"-O",
"--outs-no-cache",
action="append",
default=[],
help="Declare output regular file or directory "
"(sync to Git, not DVC cache).",
help="Declare output file or directory "
"(do not put into DVC cache).",
)
run_parser.add_argument(
"-m",
"--metrics",
action="append",
default=[],
help="Declare output metric file or directory.",
)
run_parser.add_argument(
"-M",
"--metrics-no-cache",
action="append",
default=[],
help="Declare output metric file or " "directory (not cached by DVC).",
help="Declare output metric file or directory "
"(do not put into DVC cache).",
)
run_parser.add_argument(
"-f",
+3
View File
@@ -382,6 +382,9 @@ class Config(object): # pylint: disable=too-many-instance-attributes
cache[self.PRIVATE_CWD] = os.path.dirname(fname)
def _resolve_paths(self, config, fname):
if fname is None:
return
self._resolve_cache_path(config, fname)
for section in config.values():
if self.SECTION_REMOTE_URL not in section.keys():
+4 -1
View File
@@ -9,6 +9,7 @@ from subprocess import Popen
import dvc.logger as logger
from dvc.utils import is_binary, fix_env
from dvc.utils.compat import cast_bytes_py2
CREATE_NEW_PROCESS_GROUP = 0x00000200
@@ -78,7 +79,9 @@ def daemon(args):
env = fix_env()
file_path = os.path.abspath(inspect.stack()[0][1])
env["PYTHONPATH"] = os.path.dirname(os.path.dirname(file_path))
env[cast_bytes_py2("PYTHONPATH")] = cast_bytes_py2(
os.path.dirname(os.path.dirname(file_path))
)
logger.debug("Trying to spawn '{}' with env '{}'".format(cmd, env))
+2 -2
View File
@@ -11,7 +11,7 @@ class DvcException(Exception):
"""Base class for all dvc exceptions.
Args:
msg (str): message for this exception.
msg (unicode): message for this exception.
cause (Exception): optional cause exception.
"""
@@ -33,7 +33,7 @@ class OutputDuplicationError(DvcException):
stage.
Args:
output (str): path to the file/directory.
output (unicode): path to the file/directory.
stages (list): list of paths to stages.
"""
+8 -3
View File
@@ -2,6 +2,7 @@
from __future__ import unicode_literals
from dvc.exceptions import DvcException
from dvc.utils.compat import str
import re
@@ -63,8 +64,8 @@ def box(message, border_color=None):
"""Prints a message in a box.
Args:
message (str): message to print.
border_color (str): name of a color to outline the box with.
message (unicode): message to print.
border_color (unicode): name of a color to outline the box with.
"""
lines = message.split("\n")
max_width = max(_visual_width(line) for line in lines)
@@ -230,7 +231,7 @@ def _walk_exc(exc):
tb_list.insert(0, str(exc.cause_tb))
exc = exc.cause
return (exc_list, tb_list)
return exc_list, tb_list
def _parse_exc():
@@ -260,6 +261,10 @@ def _description(message, exception):
description = "{exception}"
elif message:
description = "{message}"
else:
raise DvcException(
"Unexpected error - either exception or message must be provided"
)
return description.format(message=message, exception=exception)
+33 -19
View File
@@ -370,10 +370,11 @@ class Project(object):
def run(
self,
cmd=None,
deps=[],
outs=[],
outs_no_cache=[],
metrics_no_cache=[],
deps=None,
outs=None,
outs_no_cache=None,
metrics=None,
metrics_no_cache=None,
fname=None,
cwd=os.curdir,
no_exec=False,
@@ -383,6 +384,17 @@ class Project(object):
):
from dvc.stage import Stage
if outs is None:
outs = []
if deps is None:
deps = []
if outs_no_cache is None:
outs_no_cache = []
if metrics is None:
metrics = []
if metrics_no_cache is None:
metrics_no_cache = []
with self.state:
stage = Stage.create(
project=self,
@@ -391,6 +403,7 @@ class Project(object):
cwd=cwd,
outs=outs,
outs_no_cache=outs_no_cache,
metrics=metrics,
metrics_no_cache=metrics_no_cache,
deps=deps,
overwrite=overwrite,
@@ -1017,14 +1030,16 @@ class Project(object):
)
return self._local_status(target, with_deps=with_deps)
def _read_metric_json(self, fd, json_path):
@staticmethod
def _read_metric_json(fd, json_path):
import json
from jsonpath_rw import parse
parser = parse(json_path)
return [x.value for x in parser.find(json.load(fd))]
def _do_read_metric_xsv(self, reader, row, col):
@staticmethod
def _do_read_metric_xsv(reader, row, col):
if col is not None and row is not None:
return [reader[row][col]]
elif col is not None:
@@ -1033,22 +1048,24 @@ class Project(object):
return reader[row]
return None
def _read_metric_hxsv(self, fd, hxsv_path, delimiter):
@classmethod
def _read_metric_hxsv(cls, fd, hxsv_path, delimiter):
import csv
col, row = hxsv_path.split(",")
row = int(row)
reader = list(csv.DictReader(fd, delimiter=builtin_str(delimiter)))
return self._do_read_metric_xsv(reader, row, col)
return cls._do_read_metric_xsv(reader, row, col)
def _read_metric_xsv(self, fd, xsv_path, delimiter):
@classmethod
def _read_metric_xsv(cls, fd, xsv_path, delimiter):
import csv
col, row = xsv_path.split(",")
row = int(row)
col = int(col)
reader = list(csv.reader(fd, delimiter=builtin_str(delimiter)))
return self._do_read_metric_xsv(reader, row, col)
return cls._do_read_metric_xsv(reader, row, col)
def _read_metric(self, path, typ=None, xpath=None):
ret = None
@@ -1137,7 +1154,7 @@ class Project(object):
if os.path.isdir(path):
logger.warning(
"Path '{path}' is a directory. "
"Consider runnig with '-R'.".format(path=path)
"Consider running with '-R'.".format(path=path)
)
return {}
@@ -1182,11 +1199,10 @@ class Project(object):
return res
if path and os.path.isdir(path):
msg = "file '{}' does not exist".format(path)
return res
if path:
msg = "file '{}' does not exist".format(path)
msg = "file '{}' does not exist or malformed".format(path)
else:
msg = (
"no metric files in this repository."
@@ -1215,10 +1231,6 @@ class Project(object):
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))
if typ:
if not isinstance(out.metric, dict):
out.metric = {}
@@ -1366,7 +1378,8 @@ class Project(object):
stages.extend(list(nx.get_node_attributes(G, "stage").values()))
return stages
def _welcome_message(self):
@staticmethod
def _welcome_message():
import colorama
logger.box(
@@ -1390,7 +1403,8 @@ class Project(object):
)
)
def _expand_target_path(self, from_path, to_path):
@staticmethod
def _expand_target_path(from_path, to_path):
if os.path.isdir(to_path) and not os.path.isdir(from_path):
return os.path.join(to_path, os.path.basename(from_path))
return to_path
+1 -1
View File
@@ -36,7 +36,7 @@ def confirm(statement):
"""Ask the user for confirmation about the specified statement.
Args:
statement (str): statement to ask the user confirmation about.
statement (unicode): statement to ask the user confirmation about.
Returns:
bool: whether or not specified statement was confirmed.
+19 -11
View File
@@ -35,10 +35,18 @@ class DataCloudError(DvcException):
super(DataCloudError, self).__init__("Data sync error: {}".format(msg))
class RemoteBaseCmdError(DvcException):
def __init__(self, cmd, ret, err):
m = "SSH command '{}' finished with non-zero return code '{}': {}"
super(RemoteBaseCmdError, self).__init__(m.format(cmd, ret, err))
class RemoteCmdError(DvcException):
def __init__(self, remote, cmd, ret, err):
super(RemoteCmdError, self).__init__(
"{remote} command '{cmd}' finished with non-zero return code"
" {ret}': {err}".format(remote=remote, cmd=cmd, ret=ret, err=err)
)
class RemoteActionNotImplemented(DvcException):
def __init__(self, action, scheme):
m = "{} is not supported by {} remote".format(action, scheme)
super(RemoteActionNotImplemented, self).__init__(m)
class RemoteMissingDepsError(DvcException):
@@ -165,7 +173,7 @@ class RemoteBase(object):
return False
def save(self, path_info):
raise NotImplementedError
raise RemoteActionNotImplemented("save", self.scheme)
def download(
self,
@@ -175,19 +183,19 @@ class RemoteBase(object):
name=None,
resume=False,
):
raise NotImplementedError
raise RemoteActionNotImplemented("download", self.scheme)
def upload(self, from_infos, to_infos, path_info, names=None):
raise NotImplementedError
def upload(self, from_infos, to_infos, names=None):
raise RemoteActionNotImplemented("upload", self.scheme)
def remove(self, path_info):
raise NotImplementedError
raise RemoteActionNotImplemented("remove", self.scheme)
def move(self, path_info):
raise NotImplementedError
raise RemoteActionNotImplemented("move", self.scheme)
def copy(self, from_info, to_info):
raise NotImplementedError
raise RemoteActionNotImplemented("copy", self.scheme)
def _makedirs(self, fname):
dname = os.path.dirname(fname)
+4 -8
View File
@@ -8,14 +8,10 @@ from subprocess import Popen, PIPE
import dvc.logger as logger
from dvc.config import Config
from dvc.remote.base import RemoteBase, RemoteBaseCmdError
from dvc.remote.base import RemoteBase, RemoteCmdError
from dvc.utils import fix_env
class RemoteHDFSCmdError(RemoteBaseCmdError):
pass
class RemoteHDFS(RemoteBase):
scheme = "hdfs"
REGEX = r"^hdfs://((?P<user>.*)@)?.*$"
@@ -55,7 +51,7 @@ class RemoteHDFS(RemoteBase):
)
out, err = p.communicate()
if p.returncode != 0:
raise RemoteHDFSCmdError(cmd, p.returncode, err)
raise RemoteCmdError(self.scheme, cmd, p.returncode, err)
return out.decode("utf-8")
@staticmethod
@@ -123,7 +119,7 @@ class RemoteHDFS(RemoteBase):
try:
self.hadoop_fs("test -e {}".format(path_info["path"]))
return True
except RemoteHDFSCmdError:
except RemoteCmdError:
return False
def upload(self, from_infos, to_infos, names=None):
@@ -177,7 +173,7 @@ class RemoteHDFS(RemoteBase):
def list_cache_paths(self):
try:
self.hadoop_fs("test -e {}".format(self.prefix))
except RemoteHDFSCmdError:
except RemoteCmdError:
return []
stdout = self.hadoop_fs("ls -R {}".format(self.prefix))
+3 -7
View File
@@ -16,7 +16,7 @@ from dvc.config import Config
from dvc.progress import progress
from dvc.utils.compat import urlparse
from dvc.exceptions import DvcException
from dvc.remote.base import RemoteBase, RemoteBaseCmdError
from dvc.remote.base import RemoteBase, RemoteCmdError
def sizeof_fmt(num, suffix="B"):
@@ -43,10 +43,6 @@ def create_cb(name):
return lambda cur, tot: percent_cb(name, cur, tot)
class RemoteSSHCmdError(RemoteBaseCmdError):
pass
class RemoteSSH(RemoteBase):
scheme = "ssh"
@@ -134,7 +130,7 @@ class RemoteSSH(RemoteBase):
try:
self._exec(ssh, "test -e {}".format(path_info["path"]))
exists = True
except RemoteSSHCmdError:
except RemoteCmdError:
exists = False
return exists
@@ -184,7 +180,7 @@ class RemoteSSH(RemoteBase):
ret = stdout.channel.recv_exit_status()
if ret != 0:
err = b"".join(stderr_chunks).decode("utf-8")
raise RemoteSSHCmdError(cmd, ret, err)
raise RemoteCmdError(self.scheme, cmd, ret, err)
return b"".join(stdout_chunks).decode("utf-8")
+35 -13
View File
@@ -116,11 +116,16 @@ class Stage(object):
path=None,
cmd=None,
cwd=os.curdir,
deps=[],
outs=[],
deps=None,
outs=None,
md5=None,
locked=False,
):
if deps is None:
deps = []
if outs is None:
outs = []
self.project = project
self.path = path
self.cmd = cmd
@@ -342,10 +347,11 @@ class Stage(object):
def create(
project=None,
cmd=None,
deps=[],
outs=[],
outs_no_cache=[],
metrics_no_cache=[],
deps=None,
outs=None,
outs_no_cache=None,
metrics=None,
metrics_no_cache=None,
fname=None,
cwd=os.curdir,
locked=False,
@@ -355,9 +361,23 @@ class Stage(object):
remove_outs=False,
):
if outs is None:
outs = []
if deps is None:
deps = []
if outs_no_cache is None:
outs_no_cache = []
if metrics is None:
metrics = []
if metrics_no_cache is None:
metrics_no_cache = []
stage = Stage(project=project, cwd=cwd, cmd=cmd, locked=locked)
stage.outs = output.loads_from(stage, outs, use_cache=True)
stage.outs += output.loads_from(
stage, metrics, use_cache=True, metric=True
)
stage.outs += output.loads_from(stage, outs_no_cache, use_cache=False)
stage.outs += output.loads_from(
stage, metrics_no_cache, use_cache=False, metric=True
@@ -512,7 +532,8 @@ class Stage(object):
if any(missing):
raise MissingDep(missing)
def _warn_if_fish(self, executable): # pragma: no cover
@staticmethod
def _warn_if_fish(executable): # pragma: no cover
if (
executable is None
or os.path.basename(os.path.realpath(executable)) != "fish"
@@ -530,8 +551,8 @@ class Stage(object):
def _check_circular_dependency(self):
from dvc.exceptions import CircularDependencyError
circular_dependencies = set(file.path for file in self.deps) & set(
file.path for file in self.outs
circular_dependencies = set(d.path for d in self.deps) & set(
o.path for o in self.outs
)
if circular_dependencies:
@@ -541,11 +562,11 @@ class Stage(object):
from dvc.exceptions import ArgumentDuplicationError
from collections import Counter
count = Counter(file.path for file in self.deps + self.outs)
path_counts = Counter(edge.path for edge in self.deps + self.outs)
for file, occurrence in count.items():
for path, occurrence in path_counts.items():
if occurrence > 1:
raise ArgumentDuplicationError(file)
raise ArgumentDuplicationError(path)
def _run(self):
self._check_missing_deps()
@@ -619,7 +640,8 @@ class Stage(object):
for out in self.outs:
out.checkout(force=force)
def _status(self, entries, name):
@staticmethod
def _status(entries, name):
ret = {}
for entry in entries:
+3 -2
View File
@@ -1,7 +1,7 @@
"""Helpers for other modules."""
from __future__ import unicode_literals
from dvc.utils.compat import str, builtin_str, open
from dvc.utils.compat import str, builtin_str, open, cast_bytes_py2
import os
import sys
@@ -195,7 +195,8 @@ def fix_env(env=None):
lp_key = "LD_LIBRARY_PATH"
lp_orig = env.get(lp_key + "_ORIG", None)
if lp_orig is not None:
env[lp_key] = lp_orig
# NOTE: py2 doesn't like unicode strings in environ
env[cast_bytes_py2(lp_key)] = cast_bytes_py2(lp_orig)
else:
env.pop(lp_key, None)
+23
View File
@@ -12,6 +12,27 @@ is_py2 = _ver[0] == 2
#: Python 3.x?
is_py3 = _ver[0] == 3
# NOTE: cast_bytes_py2 is taken from https://github.com/ipython/ipython_genutils
# simplified version of ipython_genutils/encoding.py
DEFAULT_ENCODING = sys.getdefaultencoding()
def no_code(x, encoding=None):
return x
def encode(u, encoding=None):
encoding = encoding or DEFAULT_ENCODING
return u.encode(encoding, "replace")
def cast_bytes(s, encoding=None):
if not isinstance(s, bytes):
return encode(s, encoding)
return s
if is_py2:
from urlparse import urlparse, urljoin # noqa: F401
from StringIO import StringIO # noqa: F401
@@ -27,6 +48,7 @@ if is_py2:
numeric_types = (int, long, float) # noqa: F821
integer_types = (int, long) # noqa: F821
input = raw_input # noqa: F821
cast_bytes_py2 = cast_bytes
elif is_py3:
from urllib.parse import urlparse, urljoin # noqa: F401
@@ -45,3 +67,4 @@ elif is_py3:
integer_types = (int,) # noqa: F821
input = input # noqa: F821
open = open # noqa: F821
cast_bytes_py2 = no_code
+8 -1
View File
@@ -53,7 +53,14 @@ if [ -n "$TRAVIS_OS_NAME" ] && [ "$TRAVIS_OS_NAME" != "osx" ] \
bash "$scriptdir/install_hadoop.sh"
fi
if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
brew install openssl
brew cask install google-cloud-sdk
fi
if [[ -n "$PYTHON_VER" ]]; then
eval "$(pyenv init -)"
pyenv install --skip-existing --keep --verbose "$PYTHON_VER"
echo 'eval "$(pyenv init -)"' >> env.sh
echo "pyenv shell $PYTHON_VER" >> env.sh
fi
+14 -12
View File
@@ -11,22 +11,24 @@ if [[ "$TRAVIS_PULL_REQUEST" == "false" && \
codecov
fi
if [[ ! -z "$TRAVIS_TAG" ]]; then
if [[ -n "$TRAVIS_TAG" || "$TRAVIS_EVENT_TYPE" == "cron" ]]; then
./scripts/build_package.sh
# Test version
pip uninstall -y dvc
if [ -x "$(command -v dvc)" ]; then
echo "ERROR: dvc command already exists!!!"
exit 1
fi
if [[ -n "$TRAVIS_TAG" ]]; then
# Test version
pip uninstall -y dvc
if [ -x "$(command -v dvc)" ]; then
echo "ERROR: dvc command already exists!!!"
exit 1
fi
pip install dist/dvc-*.whl
if [[ "$(dvc --version)" != "$TRAVIS_TAG" ]]; then
echo "ERROR: 'dvc --version'$(dvc -V) doesn't match '$TRAVIS_TAG'"
exit 1
pip install dist/dvc-*.whl
if [[ "$(dvc --version)" != "$TRAVIS_TAG" ]]; then
echo "ERROR: 'dvc --version'$(dvc -V) doesn't match '$TRAVIS_TAG'"
exit 1
fi
pip uninstall -y dvc
fi
pip uninstall -y dvc
PY_VER=$(python -c 'import sys; print(sys.version_info[0:2])')
if [[ "$PY_VER" != '(3, 7)' ]]; then
+1 -1
View File
@@ -1,6 +1,6 @@
# This script generates config.ini for setup.iss script
from dvc import VERSION
from dvc.utils.compat import ConfigParser, open
from dvc.utils.compat import ConfigParser
config = ConfigParser.ConfigParser()
config.add_section("Version")
+9 -1
View File
@@ -1,6 +1,8 @@
import os
import sys
from subprocess import check_call
REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
os.chdir(REPO_ROOT)
@@ -10,8 +12,14 @@ os.putenv(
os.putenv("DVC_HOME", REPO_ROOT)
os.putenv("DVC_TEST", "true")
if len(sys.argv) == 1:
scope = "--all-modules"
else:
scope = " ".join(sys.argv[1:])
cmd = (
"nosetests -v --processes=-1 --process-timeout=500 --cover-inclusive "
"--cover-erase --cover-package=dvc --with-coverage --all-modules --with-flaky"
"--cover-erase --cover-package=dvc --with-coverage --with-flaky "
"{scope} ".format(scope=scope)
)
check_call(cmd, shell=True)
+37 -6
View File
@@ -359,7 +359,7 @@ class TestNoMetrics(TestDvc):
class TestCachedMetrics(TestDvc):
def _do_write(self, branch):
def _do_add(self, branch):
self.dvc.scm.checkout(branch)
self.dvc.checkout(force=True)
@@ -369,20 +369,45 @@ class TestCachedMetrics(TestDvc):
stages = self.dvc.add("metrics.json")
self.assertEqual(len(stages), 1)
stage = stages[0]
self.assertNotEqual(stage, None)
self.assertIsNotNone(stage)
self.dvc.scm.add([".gitignore", "metrics.json.dvc"])
self.dvc.scm.commit(branch)
def test(self):
def _do_run(self, branch):
self.dvc.scm.checkout(branch)
self.dvc.checkout(force=True)
with open("code.py", "w+") as fobj:
fobj.write("import sys\n")
fobj.write("import os\n")
fobj.write("import json\n")
fobj.write(
'print(json.dumps({{"metrics": "{branch}"}}))\n'.format(
branch=branch
)
)
stage = self.dvc.run(
deps=["code.py"],
metrics=["metrics.json"],
cmd="python code.py metrics.json > metrics.json",
)
self.assertIsNotNone(stage)
self.assertEqual(stage.relpath, "metrics.json.dvc")
self.dvc.scm.add([".gitignore", "metrics.json.dvc"])
self.dvc.scm.commit(branch)
def _test_metrics(self, func):
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")
func("master")
func("one")
func("two")
self.dvc = Project(".")
@@ -398,3 +423,9 @@ class TestCachedMetrics(TestDvc):
"two": {"metrics.json": ["two"]},
},
)
def test_add(self):
self._test_metrics(self._do_add)
def test_run(self):
self._test_metrics(self._do_run)
+13 -1
View File
@@ -50,7 +50,7 @@ class TestRun(TestDvc):
self.assertTrue(stage.path, fname)
with self.assertRaises(OutputDuplicationError):
stage = self.dvc.run(
self.dvc.run(
cmd=cmd,
deps=deps,
outs=outs,
@@ -530,6 +530,18 @@ class TestCmdRunOverwrite(TestDvc):
self.assertNotEqual(stage_mtime, os.path.getmtime("out.dvc"))
class TestCmdRunCliMetrics(TestDvc):
def test_cached(self):
ret = main(["run", "-m", "metrics.txt", "echo test > metrics.txt"])
self.assertEqual(ret, 0)
self.assertEqual(open("metrics.txt", "r").read().rstrip(), "test")
def test_not_cached(self):
ret = main(["run", "-M", "metrics.txt", "echo test > metrics.txt"])
self.assertEqual(ret, 0)
self.assertEqual(open("metrics.txt", "r").read().rstrip(), "test")
class TestRunDeterministicBase(TestDvc):
def setUp(self):
super(TestRunDeterministicBase, self).setUp()
+22 -2
View File
@@ -1,8 +1,8 @@
import mock
from unittest import TestCase
from dvc.remote.base import RemoteBase, RemoteMissingDepsError
from dvc.remote import REMOTES, RemoteLOCAL
from dvc.remote import REMOTES, RemoteLOCAL, RemoteSSH, RemoteHDFS
from dvc.remote.base import RemoteBase, RemoteCmdError, RemoteMissingDepsError
class TestMissingDeps(TestCase):
@@ -13,3 +13,23 @@ class TestMissingDeps(TestCase):
with mock.patch.object(remote_class, "REQUIRES", REQUIRES):
with self.assertRaises(RemoteMissingDepsError):
remote_class(None, {})
class TestCmdError(TestCase):
def test(self):
for remote_class in [RemoteSSH, RemoteHDFS]:
project = None
config = {}
remote_name = remote_class.scheme
cmd = "sed 'hello'"
ret = "1"
err = "sed: expression #1, char 2: extra characters after command"
with mock.patch.object(
remote_class,
"remove",
side_effect=RemoteCmdError(remote_name, cmd, ret, err),
):
with self.assertRaises(RemoteCmdError):
remote_class(project, config).remove("file")