mirror of
https://github.com/Stone-Red-Code/dvc.git
synced 2026-11-06 15:28:28 +01:00
When running from binary, we need to fixup environment variables before launching new processes. We also need to detach from the parent tty, because otherwise it causes problems for PyInstaller. Plus, when binary is built as --onefile it still creates pop-up terminal on windows, so we need to switch to --onedir. Fixes #1446 Fixes #1444 Signed-off-by: Ruslan Kuprieiev <[email protected]>
195 lines
4.3 KiB
Python
195 lines
4.3 KiB
Python
"""
|
|
Helpers for other modules.
|
|
"""
|
|
import os
|
|
import sys
|
|
import stat
|
|
import math
|
|
import json
|
|
import shutil
|
|
import hashlib
|
|
|
|
from dvc.progress import progress
|
|
from dvc.istextfile import istextfile
|
|
from dvc.logger import logger
|
|
|
|
|
|
LOCAL_CHUNK_SIZE = 1024*1024
|
|
LARGE_FILE_SIZE = 1024*1024*1024
|
|
LARGE_DIR_SIZE = 100
|
|
|
|
|
|
def dos2unix(data):
|
|
return data.replace(b'\r\n', b'\n')
|
|
|
|
|
|
def file_md5(fname):
|
|
""" get the (md5 hexdigest, md5 digest) of a file """
|
|
if os.path.exists(fname):
|
|
hash_md5 = hashlib.md5()
|
|
binary = not istextfile(fname)
|
|
size = os.path.getsize(fname)
|
|
bar = False
|
|
if size >= LARGE_FILE_SIZE:
|
|
bar = True
|
|
msg = "Computing md5 for a large file {}. This is only done once."
|
|
logger.info(msg.format(os.path.relpath(fname)))
|
|
name = os.path.relpath(fname)
|
|
total = 0
|
|
|
|
with open(fname, 'rb') as fobj:
|
|
while True:
|
|
data = fobj.read(LOCAL_CHUNK_SIZE)
|
|
if not data:
|
|
break
|
|
|
|
if bar:
|
|
total += len(data)
|
|
progress.update_target(name, total, size)
|
|
|
|
if binary:
|
|
chunk = data
|
|
else:
|
|
chunk = dos2unix(data)
|
|
|
|
hash_md5.update(chunk)
|
|
|
|
if bar:
|
|
progress.finish_target(name)
|
|
|
|
return (hash_md5.hexdigest(), hash_md5.digest())
|
|
else:
|
|
return (None, None)
|
|
|
|
|
|
def bytes_md5(byts):
|
|
hasher = hashlib.md5()
|
|
hasher.update(byts)
|
|
return hasher.hexdigest()
|
|
|
|
|
|
def dict_filter(d, exclude=[]):
|
|
"""
|
|
Exclude specified keys from a nested dict
|
|
"""
|
|
|
|
if isinstance(d, list):
|
|
ret = []
|
|
for e in d:
|
|
ret.append(dict_filter(e, exclude))
|
|
return ret
|
|
elif isinstance(d, dict):
|
|
ret = {}
|
|
for k, v in d.items():
|
|
assert isinstance(k, str)
|
|
if k in exclude:
|
|
continue
|
|
ret[k] = dict_filter(v, exclude)
|
|
return ret
|
|
|
|
return d
|
|
|
|
|
|
def dict_md5(d, exclude=[]):
|
|
filtered = dict_filter(d, exclude)
|
|
byts = json.dumps(filtered, sort_keys=True).encode('utf-8')
|
|
return bytes_md5(byts)
|
|
|
|
|
|
def copyfile(src, dest, no_progress_bar=False, name=None):
|
|
'''Copy file with progress bar'''
|
|
copied = 0
|
|
name = name if name else os.path.basename(dest)
|
|
total = os.stat(src).st_size
|
|
|
|
fsrc = open(src, 'rb')
|
|
|
|
if os.path.isdir(dest):
|
|
fdest = open(os.path.join(dest, os.path.basename(src)), 'wb+')
|
|
else:
|
|
fdest = open(dest, 'wb+')
|
|
|
|
while True:
|
|
buf = fsrc.read(LOCAL_CHUNK_SIZE)
|
|
if not buf:
|
|
break
|
|
fdest.write(buf)
|
|
copied += len(buf)
|
|
if not no_progress_bar:
|
|
progress.update_target(name, copied, total)
|
|
|
|
if not no_progress_bar:
|
|
progress.finish_target(name)
|
|
|
|
fsrc.close()
|
|
fdest.close()
|
|
|
|
|
|
def move(src, dst):
|
|
dst = os.path.abspath(dst)
|
|
dname = os.path.dirname(dst)
|
|
if not os.path.exists(dname):
|
|
os.makedirs(dname)
|
|
|
|
if os.path.islink(src):
|
|
shutil.copy(os.readlink(src), dst)
|
|
os.unlink(src)
|
|
return
|
|
|
|
shutil.move(src, dst)
|
|
|
|
|
|
def remove(path):
|
|
if not os.path.exists(path):
|
|
return
|
|
|
|
logger.debug(u'Removing \'{}\''.format(os.path.relpath(path)))
|
|
|
|
def _chmod(func, p, excinfo):
|
|
perm = os.stat(p).st_mode
|
|
perm |= stat.S_IWRITE
|
|
os.chmod(p, perm)
|
|
func(p)
|
|
|
|
if os.path.isfile(path):
|
|
_chmod(os.unlink, path, None)
|
|
else:
|
|
shutil.rmtree(path, onerror=_chmod)
|
|
|
|
|
|
def to_chunks(l, jobs):
|
|
n = int(math.ceil(len(l) / jobs))
|
|
|
|
if len(l) == 1:
|
|
return [l]
|
|
|
|
if n == 0:
|
|
n = 1
|
|
|
|
return [l[x:x+n] for x in range(0, len(l), n)]
|
|
|
|
|
|
# NOTE: Check if we are in a bundle
|
|
# https://pythonhosted.org/PyInstaller/runtime-information.html
|
|
def is_binary():
|
|
return getattr(sys, 'frozen', False)
|
|
|
|
|
|
# NOTE: Fix env variables modified by PyInstaller
|
|
# http://pyinstaller.readthedocs.io/en/stable/runtime-information.html
|
|
def fix_env(env=None):
|
|
if env is None:
|
|
env = os.environ.copy()
|
|
else:
|
|
env = env.copy()
|
|
|
|
if is_binary():
|
|
lp_key = 'LD_LIBRARY_PATH'
|
|
lp_orig = env.get(lp_key + '_ORIG', None)
|
|
if lp_orig is not None:
|
|
env[lp_key] = lp_orig
|
|
else:
|
|
env.pop(lp_key, None)
|
|
|
|
return env
|