poetry/tests/utils/fixtures/setups/ansible/setup.py

327 lines
10 KiB
Python

from __future__ import annotations
import json
import os
import os.path
import re
import sys
import warnings
from collections import defaultdict
from distutils.command.build_scripts import build_scripts as BuildScripts
from distutils.command.sdist import sdist as SDist
try:
from setuptools import find_packages
from setuptools import setup
from setuptools.command.build_py import build_py as BuildPy
from setuptools.command.install_lib import install_lib as InstallLib
from setuptools.command.install_scripts import install_scripts as InstallScripts
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).",
file=sys.stderr,
)
sys.exit(1)
sys.path.insert(0, os.path.abspath("lib"))
from ansible.release import __author__
from ansible.release import __version__
SYMLINK_CACHE = "SYMLINK_CACHE.json"
def _find_symlinks(topdir, extension=""):
"""Find symlinks that should be maintained
Maintained symlinks exist in the bin dir or are modules which have
aliases. Our heuristic is that they are a link in a certain path which
point to a file in the same directory.
"""
symlinks = defaultdict(list)
for base_path, dirs, files in os.walk(topdir):
for filename in files:
filepath = os.path.join(base_path, filename)
if os.path.islink(filepath) and filename.endswith(extension):
target = os.readlink(filepath)
if os.path.dirname(target) == "":
link = filepath[len(topdir) :]
if link.startswith("/"):
link = link[1:]
symlinks[os.path.basename(target)].append(link)
return symlinks
def _cache_symlinks(symlink_data):
with open(SYMLINK_CACHE, "w") as f:
json.dump(symlink_data, f)
def _maintain_symlinks(symlink_type, base_path):
"""Switch a real file into a symlink"""
try:
# 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) as f:
symlink_data = json.load(f)
except OSError as e:
# IOError on py2, OSError on py3. Both have errno
if e.errno == 2:
# SYMLINKS_CACHE doesn't exist. Fallback to trying to create the
# cache now. Will work if we're running directly from a git
# checkout or from an sdist created earlier.
symlink_data = {
"script": _find_symlinks("bin"),
"library": _find_symlinks("lib", ".py"),
}
# Sanity check that something we know should be a symlink was
# found. We'll take that to mean that the current directory
# structure properly reflects symlinks in the git repo
if "ansible-playbook" in symlink_data["script"]["ansible"]:
_cache_symlinks(symlink_data)
else:
raise
else:
raise
symlinks = symlink_data[symlink_type]
for source in symlinks:
for dest in symlinks[source]:
dest_path = os.path.join(base_path, dest)
if not os.path.islink(dest_path):
try:
os.unlink(dest_path)
except OSError as e:
if e.errno == 2:
# File does not exist which is all we wanted
pass
os.symlink(source, dest_path)
class BuildPyCommand(BuildPy):
def run(self):
BuildPy.run(self)
_maintain_symlinks("library", self.build_lib)
class BuildScriptsCommand(BuildScripts):
def run(self):
BuildScripts.run(self)
_maintain_symlinks("script", self.build_dir)
class InstallLibCommand(InstallLib):
def run(self):
InstallLib.run(self)
_maintain_symlinks("library", self.install_dir)
class InstallScriptsCommand(InstallScripts):
def run(self):
InstallScripts.run(self)
_maintain_symlinks("script", self.install_dir)
class SDistCommand(SDist):
def run(self):
# have to generate the cache of symlinks for release as sdist is the
# only command that has access to symlinks from the git repo
symlinks = {
"script": _find_symlinks("bin"),
"library": _find_symlinks("lib", ".py"),
}
_cache_symlinks(symlinks)
SDist.run(self)
def read_file(file_name):
"""Read file and return its contents."""
with open(file_name) 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
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]
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
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(),
}
static_setup_params = dict(
# Use the distutils SDist so that symlinks are not expanded
# Use a custom Build for the same reason
cmdclass={
"build_py": BuildPyCommand,
"build_scripts": BuildScriptsCommand,
"install_lib": InstallLibCommand,
"install_scripts": InstallScriptsCommand,
"sdist": SDistCommand,
},
name="ansible",
version=__version__,
description="Radically simple IT automation",
author=__author__,
author_email="info@ansible.com",
url="https://ansible.com/",
project_urls={
"Bug Tracker": "https://github.com/ansible/ansible/issues",
"CI: Shippable": "https://app.shippable.com/github/ansible/ansible",
"Code of Conduct": "https://docs.ansible.com/ansible/latest/community/code_of_conduct.html",
"Documentation": "https://docs.ansible.com/ansible/",
"Mailing lists": "https://docs.ansible.com/ansible/latest/community/communication.html#mailing-list-information",
"Source Code": "https://github.com/ansible/ansible",
},
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.
python_requires=">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*",
package_dir={"": "lib"},
packages=find_packages("lib"),
package_data={
"": [
"module_utils/powershell/*.psm1",
"module_utils/powershell/*/*.psm1",
"modules/windows/*.ps1",
"modules/windows/*/*.ps1",
"galaxy/data/*/*.*",
"galaxy/data/*/*/.*",
"galaxy/data/*/*/*.*",
"galaxy/data/*/tests/inventory",
"config/base.yml",
"config/module_defaults.yml",
]
},
classifiers=[
"Development Status :: 5 - Production/Stable",
"Environment :: Console",
"Intended Audience :: Developers",
"Intended Audience :: Information Technology",
"Intended Audience :: System Administrators",
"License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
"Natural Language :: English",
"Operating System :: POSIX",
"Programming Language :: Python :: 2",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Topic :: System :: Installation/Setup",
"Topic :: System :: Systems Administration",
"Topic :: Utilities",
],
scripts=[
"bin/ansible",
"bin/ansible-playbook",
"bin/ansible-pull",
"bin/ansible-doc",
"bin/ansible-galaxy",
"bin/ansible-console",
"bin/ansible-connection",
"bin/ansible-vault",
"bin/ansible-config",
"bin/ansible-inventory",
],
data_files=[],
# Installing as zip files would break due to references to __file__
zip_safe=False,
)
def main():
"""Invoke installation process using setuptools."""
setup_params = dict(static_setup_params, **get_dynamic_setup_params())
ignore_warning_regex = (
r"Unknown distribution option: '(project_urls|python_requires)'"
)
warnings.filterwarnings(
"ignore",
message=ignore_warning_regex,
category=UserWarning,
module="distutils.dist",
)
setup(**setup_params)
warnings.resetwarnings()
if __name__ == "__main__":
main()