111 lines
3.9 KiB
Python
111 lines
3.9 KiB
Python
# encoding=UTF-8
|
|
"""convert-requirements-to-conda-yml.py"""
|
|
|
|
import argparse
|
|
import platform
|
|
import sys
|
|
|
|
YML_TEMPLATE = """channels:
|
|
- conda-forge
|
|
- defaults
|
|
dependencies:
|
|
{conda_dependencies}
|
|
{pip_dependencies}
|
|
"""
|
|
|
|
|
|
def build_environment_from_requirements(cli_args):
|
|
"""Build a conda environment.yml from requirements.txt files.
|
|
|
|
This script assumes a couple of rules about what should be installed by
|
|
conda and what should be installed by pip:
|
|
|
|
1. If the requirement is installed from git or hg, it should be
|
|
installed by pip.
|
|
2. If the requirement is followed by the commend '# pip-only', it
|
|
should be installed by pip.
|
|
3. If the requirement is available on conda-forge, it should be
|
|
installed by conda.
|
|
4. Otherwise, it should be installed by pip.
|
|
|
|
|
|
Arguments:
|
|
cli_args (list): A list of command-line arguments.
|
|
|
|
Returns:
|
|
``None``
|
|
"""
|
|
parser = argparse.ArgumentParser(description=(
|
|
'Convert a set of pip requirements.txt files into an environment '
|
|
'file for use by `conda create`.'
|
|
), prog=__file__)
|
|
|
|
parser.add_argument('req', nargs='+',
|
|
help='A requirements.txt file to analyze')
|
|
|
|
args = parser.parse_args(cli_args)
|
|
requirements_files = args.req
|
|
|
|
pip_requirements = set()
|
|
conda_requirements = set()
|
|
for requirement_file in requirements_files:
|
|
with open(requirement_file) as file:
|
|
for line in file:
|
|
line = line.strip()
|
|
|
|
# Blank line or comment
|
|
if len(line) == 0 or line.startswith('#'):
|
|
continue
|
|
|
|
if line.endswith('# pip-only'):
|
|
# Conda prefers that we explicitly include pip as a
|
|
# requirement if we're using pip.
|
|
conda_requirements.add('pip')
|
|
|
|
pip_requirements.add(line)
|
|
|
|
# If an scm needs to be installed for pip to clone to a
|
|
# revision, add it to the conda package list.
|
|
#
|
|
# Bazaar (bzr, which pip supports) is not listed;
|
|
# deprecated as of 2016 and not available on conda-forge.
|
|
install_compiler = False
|
|
for prefix, scm_conda_pkg in [("git+", "git"),
|
|
("hg+", "mercurial"),
|
|
("svn+", "subversion")]:
|
|
if line.startswith(prefix):
|
|
conda_requirements.add(scm_conda_pkg)
|
|
install_compiler = True
|
|
break # The line can only match 1 prefix
|
|
|
|
# It's less common (like for pygeoprocessing) to have linux
|
|
# wheels. Install a compiler if we're on linux, to be
|
|
# safe.
|
|
if platform.system() == 'Linux' or install_compiler:
|
|
# Always make the compiler available
|
|
# cxx-compiler works on all OSes.
|
|
# NOTE: do not use this as a dependency in a
|
|
# conda-forge package recipe!
|
|
# https://anaconda.org/conda-forge/cxx-compiler
|
|
conda_requirements.add('cxx-compiler')
|
|
|
|
else:
|
|
conda_requirements.add(line)
|
|
|
|
conda_deps_string = '\n'.join(
|
|
[f'- {dep}' for dep in sorted(conda_requirements, key=str.casefold)])
|
|
if pip_requirements:
|
|
pip_deps_string = '- pip:\n' + '\n'.join(
|
|
[' - %s' % dep for dep in sorted(pip_requirements, key=str.casefold)])
|
|
else:
|
|
pip_deps_string = ''
|
|
print(YML_TEMPLATE.format(
|
|
conda_dependencies=conda_deps_string,
|
|
pip_dependencies=pip_deps_string))
|
|
|
|
|
|
if __name__ == '__main__':
|
|
build_environment_from_requirements(sys.argv[1:])
|
|
|
|
# TODO: resolve dependencies by calling conda?
|