diff --git a/setup.py b/setup.py index 31bfe3137ae..779f3da6ccd 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,6 @@ +from __future__ import print_function + import json import os import os.path @@ -16,7 +18,7 @@ try: except ImportError: print("Ansible now needs setuptools in order to build. Install it using" " your package manager (usually python-setuptools) or via pip (pip" - " install setuptools).") + " install setuptools).", file=sys.stderr) sys.exit(1) sys.path.insert(0, os.path.abspath('lib')) @@ -49,7 +51,7 @@ def _find_symlinks(topdir, extension=''): def _cache_symlinks(symlink_data): with open(SYMLINK_CACHE, 'w') as f: - f.write(json.dumps(symlink_data)) + json.dump(symlink_data, f) def _maintain_symlinks(symlink_type, base_path): @@ -58,7 +60,7 @@ def _maintain_symlinks(symlink_type, base_path): # Try the cache first because going from git checkout to sdist is the # only time we know that we're going to cache correctly with open(SYMLINK_CACHE, 'r') as f: - symlink_data = json.loads(f.read()) + symlink_data = json.load(f) except (IOError, OSError) as e: # IOError on py2, OSError on py3. Both have errno if e.errno == 2: @@ -129,41 +131,87 @@ class SDistCommand(SDist): SDist.run(self) -with open('requirements.txt') as requirements_file: - install_requirements = requirements_file.read().splitlines() - if not install_requirements: - print("Unable to read requirements from the requirements.txt file" - "That indicates this copy of the source code is incomplete.") - sys.exit(2) - -# pycrypto or cryptography. We choose a default but allow the user to -# override it. This translates into pip install of the sdist deciding what -# package to install and also the runtime dependencies that pkg_resources -# knows about -crypto_backend = os.environ.get('ANSIBLE_CRYPTO_BACKEND', None) -if crypto_backend: - if crypto_backend.strip() == 'pycrypto': +def read_file(file_name): + """Read file and return its contents.""" + with open(file_name, 'r') as f: + return f.read() + + +def read_requirements(file_name): + """Read requirements file as a list.""" + reqs = read_file(file_name).splitlines() + if not reqs: + raise RuntimeError( + "Unable to read requirements from the %s file" + "That indicates this copy of the source code is incomplete." + % file_name + ) + return reqs + + +PYCRYPTO_DIST = 'pycrypto' + + +def get_crypto_req(): + """Detect custom crypto from ANSIBLE_CRYPTO_BACKEND env var. + + pycrypto or cryptography. We choose a default but allow the user to + override it. This translates into pip install of the sdist deciding what + package to install and also the runtime dependencies that pkg_resources + knows about. + """ + crypto_backend = os.environ.get('ANSIBLE_CRYPTO_BACKEND', '').strip() + + if crypto_backend == PYCRYPTO_DIST: # Attempt to set version requirements - crypto_backend = 'pycrypto >= 2.6' + return '%s >= 2.6' % PYCRYPTO_DIST + + return crypto_backend or None + + +def substitute_crypto_to_req(req): + """Replace crypto requirements if customized.""" + crypto_backend = get_crypto_req() + + if crypto_backend is None: + return req + + def is_not_crypto(r): + CRYPTO_LIBS = PYCRYPTO_DIST, 'cryptography' + return not any(r.lower().startswith(c) for c in CRYPTO_LIBS) + + return [r for r in req if is_not_crypto(r)] + [crypto_backend] + - install_requirements = [r for r in install_requirements if not (r.lower().startswith('pycrypto') or r.lower().startswith('cryptography'))] - install_requirements.append(crypto_backend) +def read_extras(): + """Specify any extra requirements for installation.""" + extras = dict() + extra_requirements_dir = 'packaging/requirements' + for extra_requirements_filename in os.listdir(extra_requirements_dir): + filename_match = re.search(r'^requirements-(\w*).txt$', extra_requirements_filename) + if not filename_match: + continue + extra_req_file_path = os.path.join(extra_requirements_dir, extra_requirements_filename) + try: + extras[filename_match.group(1)] = read_file(extra_req_file_path).splitlines() + except RuntimeError: + pass + return extras -# specify any extra requirements for installation -extra_requirements = dict() -extra_requirements_dir = 'packaging/requirements' -for extra_requirements_filename in os.listdir(extra_requirements_dir): - filename_match = re.search(r'^requirements-(\w*).txt$', extra_requirements_filename) - if filename_match: - with open(os.path.join(extra_requirements_dir, extra_requirements_filename)) as extra_requirements_file: - extra_requirements[filename_match.group(1)] = extra_requirements_file.read().splitlines() -# Retrieve the long description from the README -with open('README.rst', 'r') as readme_file: - longdesc = readme_file.read() +def get_dynamic_setup_params(): + """Add dynamically calculated setup params to static ones.""" + return { + # Retrieve the long description from the README + 'long_description': read_file('README.rst'), + 'install_requires': substitute_crypto_to_req( + read_requirements('requirements.txt'), + ), + 'extras_require': read_extras(), + } -setup_params = dict( +static_setup_params = dict( # Use the distutils SDist so that symlinks are not expanded # Use a custom Build for the same reason cmdclass={ @@ -176,7 +224,6 @@ setup_params = dict( name='ansible', version=__version__, description='Radically simple IT automation', - long_description=longdesc, author=__author__, author_email='info@ansible.com', url='https://ansible.com/', @@ -189,7 +236,6 @@ setup_params = dict( license='GPLv3+', # Ansible will also make use of a system copy of python-six and # python-selectors2 if installed but use a Bundled copy if it's not. - install_requires=install_requirements, python_requires='>=2.6,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*', package_dir={'': 'lib'}, packages=find_packages('lib'), @@ -237,7 +283,6 @@ setup_params = dict( 'bin/ansible-inventory', ], data_files=[], - extras_require=extra_requirements, # Installing as zip files would break due to references to __file__ zip_safe=False ) @@ -245,6 +290,7 @@ setup_params = dict( def main(): """Invoke installation process using setuptools.""" + setup_params = dict(static_setup_params, **get_dynamic_setup_params()) setup(**setup_params)