Merge pull request #1514 from efiop/master

RFC: second iteration of complying with pylint
This commit is contained in:
Ruslan Kuprieiev
2019-01-23 12:27:18 +02:00
committed by GitHub
8 changed files with 373 additions and 238 deletions
+10 -10
View File
@@ -53,7 +53,7 @@ class Project(object):
self.cloud = DataCloud(self, config=self.config.config)
self.updater = Updater(self.dvc_dir)
self._files_to_git_add = []
self.files_to_git_add = []
self._ignore()
@@ -84,14 +84,14 @@ class Project(object):
return os.path.join(root_dir, Project.DVC_DIR)
def _remind_to_git_add(self):
if not self._files_to_git_add:
if not self.files_to_git_add:
return
logger.info('\n'
'To track the changes with git run:\n'
'\n'
'\tgit add {files}'
.format(files=' '.join(self._files_to_git_add)))
.format(files=' '.join(self.files_to_git_add)))
@staticmethod
def init(root_dir=os.curdir, no_scm=False, force=False):
@@ -143,8 +143,8 @@ class Project(object):
scm.add([config.config_file])
if scm.ignore_file():
scm.add([os.path.join(dvc_dir, scm.ignore_file())])
if scm.ignore_file:
scm.add([os.path.join(dvc_dir, scm.ignore_file)])
logger.info('\nYou can now commit the changes to git.\n')
proj._welcome_message()
@@ -197,7 +197,7 @@ class Project(object):
path = os.path.join(root, f)
if Stage.is_stage_file(path):
continue
if os.path.basename(path) == self.scm.ignore_file():
if os.path.basename(path) == self.scm.ignore_file:
continue
if self.scm.is_tracked(path):
continue
@@ -206,7 +206,7 @@ class Project(object):
fnames = [fname]
stages = []
self._files_to_git_add = []
self.files_to_git_add = []
with self.state:
for f in fnames:
stage = Stage.create(project=self,
@@ -386,7 +386,7 @@ class Project(object):
self._check_dag(self.stages() + [stage])
self._files_to_git_add = []
self.files_to_git_add = []
with self.state:
if not no_exec:
stage.run()
@@ -410,7 +410,7 @@ class Project(object):
self._check_dag(self.stages() + [stage])
self._files_to_git_add = []
self.files_to_git_add = []
with self.state:
stage.run()
@@ -474,7 +474,7 @@ class Project(object):
else:
targets.append(target)
self._files_to_git_add = []
self.files_to_git_add = []
ret = []
with self.state:
+22 -4
View File
@@ -1,16 +1,18 @@
"""Manages user prompts."""
import sys
from getpass import getpass
try:
# NOTE: in Python3 raw_input() was renamed to input()
input = raw_input
input = raw_input # pylint: disable=redefined-builtin, invalid-name
except NameError:
pass
def ask(prompt, limited_to=None):
def _ask(prompt, limited_to=None):
if not sys.stdout.isatty():
return
return None
while True:
answer = input(prompt + ' ').lower()
@@ -26,11 +28,27 @@ def ask(prompt, limited_to=None):
def confirm(statement):
"""Ask the user for confirmation about the specified statement.
Args:
statement (str): statement to ask the user confirmation about.
Returns:
bool: whether or not specified statement was confirmed.
"""
prompt = '{statement} [y/n]'.format(statement=statement)
answer = ask(prompt, limited_to=['yes', 'no', 'y', 'n'])
answer = _ask(prompt, limited_to=['yes', 'no', 'y', 'n'])
return answer and answer.startswith('y')
def password(statement):
"""Ask the user for a password.
Args:
statement (str): string to prompt the user with.
Returns:
str: password entered by the user.
"""
prompt = '{statement}: '.format(statement=statement)
return getpass(prompt)
+22
View File
@@ -0,0 +1,22 @@
"""Manages source control systems(e.g. Git)."""
from dvc.scm.base import Base
from dvc.scm.git import Git
def SCM(root_dir, project=None): # pylint: disable=invalid-name
"""Returns SCM instance that corresponds to a project at the specified
path.
Args:
root_dir (str): path to a root directory of the project.
project (dvc.project.Project): dvc project instance that root_dir
belongs to.
Returns:
dvc.scm.base.Base: SCM instance.
"""
if Git.is_repo(root_dir) or Git.is_submodule(root_dir):
return Git(root_dir, project=project)
return Base(root_dir, project=project)
+142
View File
@@ -0,0 +1,142 @@
"""Manages source control systems(e.g. Git) in dvc."""
import os
from dvc.exceptions import DvcException
class SCMError(DvcException):
"""Base class for source control management errors."""
class FileNotInRepoError(DvcException):
"""Thrown when trying to find .gitignore for a file that is not in a scm
repository.
"""
class Base(object):
"""Base class for source control management driver implementations."""
def __init__(self, root_dir=os.curdir, project=None):
self.project = project
self.root_dir = root_dir
def __repr__(self):
return "{class_name}: '{directory}'".format(
class_name=type(self).__name__,
directory=self.dir
)
@property
def dir(self):
"""Path to a directory with SCM specific information."""
return None
@staticmethod
def is_repo(root_dir): # pylint: disable=unused-argument
"""Returns whether or not root_dir is a valid SCM repository."""
return True
@staticmethod
def is_submodule(root_dir): # pylint: disable=unused-argument
"""Returns whether or not root_dir is a valid SCM repository
submodule.
"""
return True
def ignore(self, path): # pylint: disable=unused-argument
"""Makes SCM ignore a specified path."""
def ignore_remove(self, path): # pylint: disable=unused-argument
"""Makes SCM stop ignoring a specified path."""
@property
def ignore_file(self):
"""Filename for a file that contains ignored paths for this SCM."""
def ignore_list(self, p_list):
"""Makes SCM ignore all paths specified in a list."""
return [self.ignore(path) for path in p_list]
def add(self, paths):
"""Makes SCM start tracking every path from a specified list of paths.
"""
def commit(self, msg):
"""Makes SCM create a commit."""
def checkout(self, branch, create_new=False):
"""Makes SCM checkout a branch."""
def branch(self, branch):
"""Makes SCM create a branch with a specified name."""
def tag(self, tag):
"""Makes SCM create a tag with a specified name."""
def brancher(self,
branches=None,
all_branches=False,
tags=None,
all_tags=False):
"""Generator that iterates over specified revisions.
Args:
branches (list): a list of branches to iterate over.
all_branches (bool): iterate over all available branches.
tags (list): a list of tags to iterate over.
all_tags (bool): iterate over all available tags.
Yields:
str: the current revision.
"""
if not branches and not all_branches \
and not tags and not all_tags:
yield ''
return
saved = self.active_branch()
revs = []
if all_branches:
branches = self.list_branches()
if all_tags:
tags = self.list_tags()
if branches is None:
revs.extend([saved])
else:
revs.extend(branches)
if tags is not None:
revs.extend(tags)
for rev in revs:
self.checkout(rev)
yield rev
self.checkout(saved)
def untracked_files(self): # pylint: disable=no-self-use
"""Returns a list of untracked files."""
return []
def is_tracked(self, path): # pylint: disable=no-self-use, unused-argument
"""Returns whether or not a specified path is tracked."""
return False
def active_branch(self): # pylint: disable=no-self-use
"""Returns current branch in the project."""
return ''
def list_branches(self): # pylint: disable=no-self-use
"""Returns a list of available branches in the project."""
return []
def list_tags(self): # pylint: disable=no-self-use
"""Returns a list of available tags in the project."""
return []
def install(self):
"""Adds dvc commands to SCM hooks for the project."""
+23 -132
View File
@@ -1,121 +1,18 @@
"""Manages Git."""
# NOTE: need this one because python 2 would re-import this module
# (called git.py) instead of importing gitpython's git module.
from __future__ import absolute_import
import os
import dvc.logger as logger
from dvc.exceptions import DvcException
from dvc.utils import fix_env
class SCMError(DvcException):
pass
class FileNotInRepoError(DvcException):
pass
class Base(object):
def __init__(self, root_dir=os.curdir, project=None):
self.project = project
self.root_dir = root_dir
def __repr__(self):
return "{class_name}: '{directory}'".format(
class_name=type(self).__name__,
directory=self.dir
)
@property
def dir(self):
return None
@staticmethod
def is_repo(root_dir):
return True
@staticmethod
def is_submodule(root_dir):
return True
def ignore(self, path):
pass
def ignore_remove(self, path):
pass
def ignore_file(self):
pass
def ignore_list(self, p_list):
return [self.ignore(path) for path in p_list]
def add(self, paths):
pass
def commit(self, msg):
pass
def checkout(self, branch):
pass
def branch(self, branch):
pass
def tag(self, tag):
pass
def brancher(self,
branches=None,
all_branches=False,
tags=None,
all_tags=False):
if not branches and not all_branches \
and not tags and not all_tags:
yield ''
return
saved = self.active_branch()
revs = []
if all_branches:
branches = self.list_branches()
if all_tags:
tags = self.list_tags()
if branches is None:
revs.extend([saved])
else:
revs.extend(branches)
if tags is not None:
revs.extend(tags)
for rev in revs:
self.checkout(rev)
yield rev
self.checkout(saved)
def untracked_files(self):
pass
def is_tracked(self, path):
pass
def active_branch(self):
pass
def list_branches(self):
pass
def list_tags(self):
pass
def install(self):
pass
from dvc.scm.base import Base, SCMError, FileNotInRepoError
class Git(Base):
"""Class for managing Git."""
GITIGNORE = '.gitignore'
GIT_DIR = '.git'
@@ -133,8 +30,8 @@ class Git(Base):
# NOTE: fixing LD_LIBRARY_PATH for binary built by PyInstaller.
# http://pyinstaller.readthedocs.io/en/stable/runtime-information.html
env = fix_env(None)
lp = env.get('LD_LIBRARY_PATH', None)
self.repo.git.update_environment(LD_LIBRARY_PATH=lp)
libpath = env.get('LD_LIBRARY_PATH', None)
self.repo.git.update_environment(LD_LIBRARY_PATH=libpath)
@staticmethod
def is_repo(root_dir):
@@ -152,6 +49,7 @@ class Git(Base):
def dir(self):
return self.repo.git_dir
@property
def ignore_file(self):
return self.GITIGNORE
@@ -174,7 +72,7 @@ class Git(Base):
ignore_list = open(gitignore, 'r').readlines()
filtered = list(filter(lambda x: x.strip() == entry.strip(),
ignore_list))
if len(filtered) != 0:
if filtered:
return
msg = "Adding '{}' to '{}'.".format(os.path.relpath(path),
@@ -182,14 +80,14 @@ class Git(Base):
logger.info(msg)
content = entry
if len(ignore_list) > 0:
if ignore_list:
content = '\n' + content
with open(gitignore, 'a') as fd:
fd.write(content)
with open(gitignore, 'a') as fobj:
fobj.write(content)
if self.project is not None:
self.project._files_to_git_add.append(os.path.relpath(gitignore))
self.project.files_to_git_add.append(os.path.relpath(gitignore))
def ignore_remove(self, path):
entry, gitignore = self._get_gitignore(path)
@@ -197,16 +95,16 @@ class Git(Base):
if not os.path.exists(gitignore):
return
with open(gitignore, 'r') as fd:
lines = fd.readlines()
with open(gitignore, 'r') as fobj:
lines = fobj.readlines()
filtered = list(filter(lambda x: x.strip() != entry.strip(), lines))
with open(gitignore, 'w') as fd:
fd.writelines(filtered)
with open(gitignore, 'w') as fobj:
fobj.writelines(filtered)
if self.project is not None:
self.project._files_to_git_add.append(os.path.relpath(gitignore))
self.project.files_to_git_add.append(os.path.relpath(gitignore))
def add(self, paths):
# NOTE: GitPython is not currently able to handle index version >= 3.
@@ -263,17 +161,10 @@ class Git(Base):
if os.path.isfile(hook):
msg = "git hook '{}' already exists."
raise SCMError(msg.format(os.path.relpath(hook)))
with open(hook, 'w+') as fd:
fd.write('#!/bin/sh\nexec dvc {}\n'.format(cmd))
with open(hook, 'w+') as fobj:
fobj.write('#!/bin/sh\nexec dvc {}\n'.format(cmd))
os.chmod(hook, 0o777)
def install(self):
self._install_hook('post-checkout', 'checkout')
self._install_hook('pre-commit', 'status')
def SCM(root_dir, no_scm=False, project=None):
if Git.is_repo(root_dir) or Git.is_submodule(root_dir):
return Git(root_dir, project=project)
return Base(root_dir, project=project)
+1 -1
View File
@@ -458,7 +458,7 @@ class Stage(object):
with open(fname, 'w') as fd:
yaml.safe_dump(self.dumpd(), fd, default_flow_style=False)
self.project._files_to_git_add.append(os.path.relpath(fname))
self.project.files_to_git_add.append(os.path.relpath(fname))
def _compute_md5(self):
from dvc.output.local import OutputLOCAL
+151 -89
View File
@@ -1,3 +1,5 @@
"""Manages state database used for checksum caching."""
import os
import time
import sqlite3
@@ -10,11 +12,31 @@ from dvc.utils import file_md5, remove
from dvc.exceptions import DvcException
class StateDuplicateError(DvcException):
pass
class StateVersionTooNewError(DvcException):
"""Thrown when dvc version is older than the state database version."""
def __init__(self, dvc_version, expected, actual):
super(StateVersionTooNewError, self).__init__(
"you are using an old version '{dvc_version}' of dvc that is "
"using state file version '{expected}' which is not compatible "
"with the state file version '{actual}' that is used in this "
"projet. Please upgrade right now!"
.format(dvc_version=dvc_version,
expected=expected,
actual=actual))
class State(object):
class State(object): # pylint: disable=too-many-instance-attributes
"""Class for the state database.
Args:
project (dvc.project.Project): project instance that this state
belongs to.
config (configobj.ConfigObj): config for the state.
Raises:
StateVersionTooNewError: thrown when dvc version is older than the
state database version.
"""
VERSION = 3
STATE_FILE = 'state'
STATE_TABLE = 'state'
@@ -47,11 +69,12 @@ class State(object):
self.row_limit = 100
self.row_cleanup_quota = 50
c = config.get(Config.SECTION_STATE, {})
self.row_limit = c.get(Config.SECTION_STATE_ROW_LIMIT,
self.STATE_ROW_LIMIT)
self.row_cleanup_quota = c.get(Config.SECTION_STATE_ROW_CLEANUP_QUOTA,
self.STATE_ROW_CLEANUP_QUOTA)
state_config = config.get(Config.SECTION_STATE, {})
self.row_limit = state_config.get(Config.SECTION_STATE_ROW_LIMIT,
self.STATE_ROW_LIMIT)
self.row_cleanup_quota = state_config.get(
Config.SECTION_STATE_ROW_CLEANUP_QUOTA,
self.STATE_ROW_CLEANUP_QUOTA)
if not self.dvc_dir:
self.state_file = None
@@ -65,23 +88,31 @@ class State(object):
self.state_file + '-wal',
]
self.db = None
self.c = None
self.database = None
self.cursor = None
self.inserts = 0
def __enter__(self):
self.load()
def __exit__(self, type, value, tb):
def __exit__(self, typ, value, tbck):
self.dump()
def _collect(self, path):
if os.path.isdir(path):
return self.project.cache.local.collect_dir_cache(path)
else:
return (file_md5(path)[0], None)
return (file_md5(path)[0], None)
def changed(self, path, md5):
"""Check if file/directory has the expected md5.
Args:
path (str): path to the file/directory to check.
md5 (str): expected md5.
Returns:
bool: True if path has the expected md5, False otherwise.
"""
actual = self.update(path)
msg = "File '{}', md5 '{}', actual '{}'"
@@ -94,32 +125,32 @@ class State(object):
def _execute(self, cmd):
logger.debug(cmd)
return self.c.execute(cmd)
return self.cursor.execute(cmd)
def _fetchall(self):
ret = self.c.fetchall()
ret = self.cursor.fetchall()
logger.debug("fetched: {}".format(ret))
return ret
def _to_sqlite(self, n):
assert n >= 0
assert n < self.MAX_UINT
def _to_sqlite(self, num):
assert num >= 0
assert num < self.MAX_UINT
# NOTE: sqlite stores unit as signed ints, so maximum uint is 2^63-1
# see http://jakegoulding.com/blog/2011/02/06/sqlite-64-bit-integers/
if n > self.MAX_INT:
ret = -(n - self.MAX_INT)
if num > self.MAX_INT:
ret = -(num - self.MAX_INT)
else:
ret = n
assert self._from_sqlite(ret) == n
ret = num
assert self._from_sqlite(ret) == num
return ret
def _from_sqlite(self, n):
assert abs(n) <= self.MAX_INT
if n < 0:
return abs(n) + self.MAX_INT
assert n < self.MAX_UINT
assert n >= 0
return n
def _from_sqlite(self, num):
assert abs(num) <= self.MAX_INT
if num < 0:
return abs(num) + self.MAX_INT
assert num < self.MAX_UINT
assert num >= 0
return num
def _prepare_db(self, empty=False):
from dvc import VERSION
@@ -134,11 +165,7 @@ class State(object):
version = ret[0][0]
if version > self.VERSION:
msg = "you are using an old version '{}' of dvc that is " \
"using state file version '{}' which is not " \
"compatible with the state file version '{}' that " \
"is used in this projet. Please upgrade right now!"
raise DvcException(msg.format(VERSION, self.VERSION, version))
raise StateVersionTooNewError(VERSION, self.VERSION, version)
elif version < self.VERSION:
msg = "State file version '{}' is too old. " \
"Reformatting to the current version '{}'."
@@ -166,14 +193,15 @@ class State(object):
self._execute(cmd.format(self.VERSION))
def load(self):
"""Loads state database."""
retries = 1
while True:
assert self.db is None
assert self.c is None
assert self.database is None
assert self.cursor is None
assert self.inserts == 0
empty = not os.path.exists(self.state_file)
self.db = sqlite3.connect(self.state_file)
self.c = self.db.cursor()
self.database = sqlite3.connect(self.state_file)
self.cursor = self.database.cursor()
# Try loading once to check that the file is indeed a database
# and reformat it if it is not.
@@ -181,10 +209,10 @@ class State(object):
self._prepare_db(empty=empty)
return
except sqlite3.DatabaseError:
self.c.close()
self.db.close()
self.db = None
self.c = None
self.cursor.close()
self.database.close()
self.database = None
self.cursor = None
self.inserts = 0
if retries > 0:
os.unlink(self.state_file)
@@ -194,12 +222,13 @@ class State(object):
def _vacuum(self):
# NOTE: see https://bugs.python.org/issue28518
self.db.isolation_level = None
self.database.isolation_level = None
self._execute("VACUUM")
self.db.isolation_level = ''
self.database.isolation_level = ''
def dump(self):
assert self.db is not None
"""Saves state database."""
assert self.database is not None
cmd = "SELECT count from {} WHERE rowid={}"
self._execute(cmd.format(self.STATE_INFO_TABLE,
@@ -236,34 +265,34 @@ class State(object):
self._to_sqlite(count),
self.STATE_INFO_ROW))
self.db.commit()
self.c.close()
self.db.close()
self.db = None
self.c = None
self.database.commit()
self.cursor.close()
self.database.close()
self.database = None
self.cursor = None
self.inserts = 0
@staticmethod
def mtime_and_size(path):
def _mtime_and_size(path):
size = os.path.getsize(path)
mtime = os.path.getmtime(path)
if os.path.isdir(path):
for root, dirs, files in os.walk(path):
for name in dirs + files:
p = os.path.join(root, name)
st = os.stat(p)
size += st.st_size
m = st.st_mtime
if m > mtime:
mtime = m
entry = os.path.join(root, name)
stat = os.stat(entry)
size += stat.st_size
entry_mtime = stat.st_mtime
if entry_mtime > mtime:
mtime = entry_mtime
# State of files handled by dvc is stored in db as TEXT.
# We cast results to string for later comparisons with stored values.
return str(int(nanotime.timestamp(mtime))), str(size)
@staticmethod
def inode(path):
def _inode(path):
logger.debug('Path {} inode {}'.format(path, System.inode(path)))
return System.inode(path)
@@ -274,70 +303,98 @@ class State(object):
if not os.path.exists(path):
return (None, None)
mtime, size = self.mtime_and_size(path)
inode = self.inode(path)
actual_mtime, actual_size = self._mtime_and_size(path)
actual_inode = self._inode(path)
cmd = 'SELECT * from {} WHERE inode={}'.format(self.STATE_TABLE,
self._to_sqlite(inode))
cmd = ('SELECT * from {} WHERE inode={}'
.format(self.STATE_TABLE, self._to_sqlite(actual_inode)))
self._execute(cmd)
ret = self._fetchall()
if len(ret) == 0:
if not ret:
md5, info = self._collect(path)
cmd = 'INSERT INTO {}(inode, mtime, size, md5, timestamp) ' \
'VALUES ({}, "{}", "{}", "{}", "{}")'
self._execute(cmd.format(self.STATE_TABLE,
self._to_sqlite(inode),
mtime,
size,
self._to_sqlite(actual_inode),
actual_mtime,
actual_size,
md5,
int(nanotime.timestamp(time.time()))))
self.inserts += 1
else:
assert len(ret) == 1
assert len(ret[0]) == 5
i, m, s, md5, timestamp = ret[0]
i = self._from_sqlite(i)
assert i == inode
msg = ("Inode '{}', mtime '{}', actual mtime '{}', "
"size '{}', actual size '{}'.")
logger.debug(msg.format(i, m, mtime, s, size))
if mtime != m or size != s:
inode, mtime, size, md5, _ = ret[0]
inode = self._from_sqlite(inode)
assert inode == actual_inode
logger.debug(
"Inode '{}', mtime '{}', actual mtime '{}', size '{}', "
"actual size '{}'."
.format(inode, mtime, actual_mtime, size, actual_size))
if actual_mtime != mtime or actual_size != size:
md5, info = self._collect(path)
cmd = ('UPDATE {} SET '
'mtime = "{}", size = "{}", '
'md5 = "{}", timestamp = "{}" '
'WHERE inode = {}')
self._execute(cmd.format(self.STATE_TABLE,
mtime,
size,
actual_mtime,
actual_size,
md5,
int(nanotime.timestamp(time.time())),
self._to_sqlite(inode)))
self._to_sqlite(actual_inode)))
else:
info = None
cmd = 'UPDATE {} SET timestamp = "{}" WHERE inode = {}'
self._execute(cmd.format(self.STATE_TABLE,
int(nanotime.timestamp(time.time())),
self._to_sqlite(inode)))
self._to_sqlite(actual_inode)))
return (md5, info)
def update(self, path):
"""Gets the checksum for the specified path. Checksum will be
retrieved from the state database if available, otherwise it will be
computed and cached in the state database for the further use.
Args:
path (str): path to get the checksum for.
Returns:
str: checksum for the specified path.
"""
return self._do_update(path)[0]
def update_info(self, path):
"""Gets the checksum and the directory info (if applicable) for the
specified path.
Args:
path (str): path to get the checksum and the directory info for.
Returns:
tuple: checksum for the specified path along with a directory info
(list of {relative_path: checksum} entries for each file in the
directory) if applicable, otherwise None.
"""
md5, info = self._do_update(path)
if not info:
info = self.project.cache.local.load_dir_cache(md5)
return (md5, info)
def update_link(self, path):
"""Adds the specified path to the list of links created by dvc. This
list is later used on `dvc checkout` to cleanup old links.
Args:
path (str): path to add to the list of links.
"""
if not os.path.exists(path):
return
mtime, _ = self.mtime_and_size(path)
inode = self.inode(path)
mtime, _ = self._mtime_and_size(path)
inode = self._inode(path)
relpath = os.path.relpath(path, self.root_dir)
cmd = 'REPLACE INTO {}(path, inode, mtime) ' \
@@ -348,13 +405,18 @@ class State(object):
self._execute(cmd)
def remove_unused_links(self, used):
"""Removes all saved links except the ones that are used.
Args:
used (list): list of used links that should not be removed.
"""
unused = []
self._execute('SELECT * FROM {}'.format(self.LINK_STATE_TABLE))
for row in self.c:
p, i, m = row
i = self._from_sqlite(i)
path = os.path.join(self.root_dir, p)
for row in self.cursor:
relpath, inode, mtime = row
inode = self._from_sqlite(inode)
path = os.path.join(self.root_dir, relpath)
if path in used:
continue
@@ -362,14 +424,14 @@ class State(object):
if not os.path.exists(path):
continue
inode = self.inode(path)
mtime, _ = self.mtime_and_size(path)
actual_inode = self._inode(path)
actual_mtime, _ = self._mtime_and_size(path)
if i == inode and m == mtime:
if inode == actual_inode and mtime == actual_mtime:
logger.debug("Removing '{}' as unused link.".format(path))
remove(path)
unused.append(p)
unused.append(relpath)
for p in unused:
for relpath in unused:
cmd = 'DELETE FROM {} WHERE path = "{}"'
self._execute(cmd.format(self.LINK_STATE_TABLE, p))
self._execute(cmd.format(self.LINK_STATE_TABLE, relpath))
+2 -2
View File
@@ -6,8 +6,8 @@ from tests.basic_env import TestDvc
class TestMtimeAndSize(TestDvc):
def test(self):
file_time, file_size = State.mtime_and_size(self.DATA)
dir_time, dir_size = State.mtime_and_size(self.DATA_DIR)
file_time, file_size = State._mtime_and_size(self.DATA)
dir_time, dir_size = State._mtime_and_size(self.DATA_DIR)
actual_file_size = os.path.getsize(self.DATA)
actual_dir_size = (os.path.getsize(self.DATA_DIR)