aboutsummaryrefslogtreecommitdiff
path: root/delivery_merge
diff options
context:
space:
mode:
authorJoseph Hunkeler <jhunkeler@gmail.com>2019-05-07 12:27:05 -0400
committerJoseph Hunkeler <jhunkeler@gmail.com>2019-05-07 12:27:05 -0400
commit593537b7183adb0b4371ee19d6c6bd583267d305 (patch)
tree262659a479d1c09a9bd0d4c14ebfcdc0c9460544 /delivery_merge
parent36740d90353c92e814917a0688764875fda49558 (diff)
downloaddelivery_merge-593537b7183adb0b4371ee19d6c6bd583267d305.tar.gz
Docstrings
Diffstat (limited to 'delivery_merge')
-rw-r--r--delivery_merge/conda.py52
-rw-r--r--delivery_merge/merge.py39
-rw-r--r--delivery_merge/utils.py13
3 files changed, 98 insertions, 6 deletions
diff --git a/delivery_merge/conda.py b/delivery_merge/conda.py
index 709dbaf..44e15de 100644
--- a/delivery_merge/conda.py
+++ b/delivery_merge/conda.py
@@ -9,12 +9,25 @@ from subprocess import run
ENV_ORIG = os.environ.copy()
+class BadPlatform(Exception):
+ pass
+
+
def conda_installer(ver, prefix='./miniconda3'):
+ """ Install miniconda into a user-defined prefix and return its path
+
+ :param ver: str: miniconda version (not conda version)
+ :param prefix: str: path to install miniconda into
+ :returns: str: absolute path to installation prefix
+ :raises delivery_merge.conda.BadPlatform: when platform check fails
+ :raises subprocess.CalledProcessError: via check_returncode method
+ """
assert isinstance(ver, str)
assert isinstance(prefix, str)
prefix = os.path.abspath(prefix)
+ # Is miniconda already installed?
if os.path.exists(prefix):
print(f'{prefix}: exists', file=sys.stderr)
return prefix
@@ -23,36 +36,58 @@ def conda_installer(ver, prefix='./miniconda3'):
version = ver
arch = 'x86_64'
platform = sys.platform
+
+ # Emit their installer's concept of "platform"
if sys.platform == 'darwin':
platform = 'MacOSX'
elif sys.platform == 'linux':
platform = 'Linux'
+ else:
+ raise BadPlatform(f'{sys.platform} is not supported.')
url_root = 'https://repo.continuum.io/miniconda'
installer = f'{name}-{version}-{platform}-{arch}.sh'
url = f'{url_root}/{installer}'
install_command = f'./{installer} -b -p {prefix}'.split()
+ # Download installer
if not os.path.exists(installer):
with requests.get(url, stream=True) as data:
with open(installer, 'wb') as fd:
for chunk in data.iter_content(chunk_size=16384):
fd.write(chunk)
os.chmod(installer, 0o755)
- run(install_command)
+
+ # Perform installation
+ run(install_command).check_returncode()
return prefix
def conda_init_path(prefix):
+ """ Redefines $PATH so subsequent shell calls use the just-installed
+ miniconda prefix. This function will not continue prepending to $PATH
+ so it's safe to call more than once.
+
+ :param prefix: str: path to miniconda installation
+ :returns: None
+ """
if os.environ['PATH'] != ENV_ORIG['PATH']:
os.environ['PATH'] = ENV_ORIG['PATH']
os.environ['PATH'] = ':'.join([os.path.join(prefix, 'bin'),
os.environ['PATH']])
- print(f"PATH = {os.environ['PATH']}")
def conda_activate(env_name):
+ """ Activate a conda environment
+
+ Assume: `conda_init_path` as been called beforehand
+ Warning: Arbitrary code execution is possible here due to `shell` usage.
+
+ :param env_name: str: conda environment to activate
+ :returns: dict: new runtime environment
+ :raises subprocess.CalledProcessError: via check_returncode method
+ """
proc = run(f"source activate {env_name} && env",
capture_output=True,
shell=True)
@@ -62,6 +97,15 @@ def conda_activate(env_name):
@contextmanager
def conda_env_load(env_name):
+ """ A simple wrapper for `conda_activate`
+ The current runtime environment is replaced and restored
+
+ >>> with conda_env_load('some_env') as _:
+ >>> # do something
+
+ :param env_name: str: conda environment to activate
+ :returns: None
+ """
last = os.environ.copy()
os.environ = conda_activate(env_name)
try:
@@ -71,4 +115,8 @@ def conda_env_load(env_name):
def conda(*args):
+ """ Execute conda shell commands
+
+ :returns: subprocess.CompletedProcess object
+ """
return sh('conda', *args)
diff --git a/delivery_merge/merge.py b/delivery_merge/merge.py
index 457c433..01b9732 100644
--- a/delivery_merge/merge.py
+++ b/delivery_merge/merge.py
@@ -11,17 +11,27 @@ DMFILE_RE = re.compile(r'^(?P<name>.*)[=<>~\!](?P<version>.*).*$')
def comment_find(s, delims=[';', '#']):
- """ Return index of first match
+ """ Find the first occurence of a comment in a string
+
+ :param s: string
+ :param delims: list: of comment delimiters
+ :returns: integer: index of first match
"""
for delim in delims:
- pos = s.find(delim)
- if pos != -1:
+ index = s.find(delim)
+ if index != -1:
break
- return pos
+ return index
def dmfile(filename):
+ """ Return the contents of a file without comments
+
+ :param filename: string: path to file
+ :returns: list: data in file
+ """
+ # TODO: Use DMFILE_RE here instead of `testable_packages`
result = []
with open(filename, 'r') as fp:
for line in fp:
@@ -39,6 +49,15 @@ def dmfile(filename):
def env_combine(filename, conda_env, conda_channels=[]):
+ """ Install packages listed in `filename` inside `conda_env`.
+ Packages are quote-escaped to prevent spurious file redirection.
+
+ :param filename: str: path to file
+ :param conda_env: str: conda environment name
+ :param conda_channels: list: channel URLs
+ :returns: None
+ :raises subprocess.CalledProcessError: via check_returncode method
+ """
packages = []
channels_result = '--override-channels '
@@ -53,11 +72,16 @@ def env_combine(filename, conda_env, conda_channels=[]):
conda_env, channels_result, packages_result)
if proc.stderr:
print(proc.stderr.decode())
+ proc.check_returncode()
def testable_packages(filename, prefix):
""" Scan a mini/anaconda prefix for unpacked packages matching versions
requested by dmfile.
+
+ :param filename: str: path to file
+ :param prefix: str: path to conda root directory (aka prefix)
+ :returns: dict: git commit hash and repository URL information
"""
pkgdir = os.path.join(prefix, 'pkgs')
paths = []
@@ -96,6 +120,13 @@ def testable_packages(filename, prefix):
def integration_test(pkg_data, conda_env, results_root='.'):
+ """
+ :param pkg_data: dict: data returned by `testable_packages` method
+ :param conda_env: str: conda environment name
+ :param results_root: str: path to store XML reports
+ :returns: None
+ :raises subprocess.CalledProcessError: via check_returncode method
+ """
results_root = os.path.abspath(os.path.join(results_root, 'results'))
src_root = os.path.abspath('src')
diff --git a/delivery_merge/utils.py b/delivery_merge/utils.py
index 2344a13..8935dd7 100644
--- a/delivery_merge/utils.py
+++ b/delivery_merge/utils.py
@@ -4,6 +4,12 @@ from subprocess import run
def sh(prog, *args):
+ """ Execute a program with arguments
+ :param prog: str: path to program
+ :param args: tuple: variadic arguments
+ Accepts any combination of strings passed as arguments
+ :returns: subprocess.CompletedProcess
+ """
command = [prog]
tmp = []
for arg in args:
@@ -15,11 +21,17 @@ def sh(prog, *args):
def git(*args):
+ """ Execute git commands
+ :param args: tuple: variadic arguments to pass to git
+ :returns: subprocess.CompletedProcess
+ """
return sh('git', *args)
def getenv(s):
""" Convert string of key pairs to dictionary format
+ :param s: str: key pairs separated by newlines
+ :returns: dict: converted key pairs
"""
return dict([x.split('=', 1) for x in s.splitlines()])
@@ -27,6 +39,7 @@ def getenv(s):
@contextmanager
def pushd(path):
""" Equivalent to shell pushd/popd behavior
+ :param path: str: path to directory
"""
last = os.path.abspath(os.getcwd())
os.chdir(path)