| # Licensed to the Apache Software Foundation (ASF) under one or more |
| # contributor license agreements. See the NOTICE file distributed with |
| # this work for additional information regarding copyright ownership. |
| # The ASF licenses this file to You under the Apache License, Version 2.0 |
| # (the "License"); you may not use this file except in compliance with |
| # the License. You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| import os |
| import re |
| import sys |
| import fnmatch |
| from distutils.core import Command |
| |
| import setuptools |
| from setuptools import setup |
| |
| try: |
| import epydoc # NOQA |
| |
| has_epydoc = True |
| except ImportError: |
| has_epydoc = False |
| |
| # NOTE: Those functions are intentionally moved in-line to prevent setup.py dependening on any |
| # Libcloud code which depends on libraries such as typing, enum, requests, etc. |
| # START: Taken From Twisted Python which licensed under MIT license |
| # https://github.com/powdahound/twisted/blob/master/twisted/python/dist.py |
| # https://github.com/powdahound/twisted/blob/master/LICENSE |
| |
| # Names that are excluded from globbing results: |
| EXCLUDE_NAMES = ["{arch}", "CVS", ".cvsignore", "_darcs", "RCS", "SCCS", ".svn"] |
| EXCLUDE_PATTERNS = ["*.py[cdo]", "*.s[ol]", ".#*", "*~", "*.py"] |
| |
| |
| def _filter_names(names): |
| """ |
| Given a list of file names, return those names that should be copied. |
| """ |
| names = [n for n in names if n not in EXCLUDE_NAMES] |
| # This is needed when building a distro from a working |
| # copy (likely a checkout) rather than a pristine export: |
| for pattern in EXCLUDE_PATTERNS: |
| names = [n for n in names if not fnmatch.fnmatch(n, pattern) and not n.endswith(".py")] |
| return names |
| |
| |
| def relative_to(base, relativee): |
| """ |
| Gets 'relativee' relative to 'basepath'. |
| |
| i.e., |
| |
| >>> relative_to('/home/', '/home/radix/') |
| 'radix' |
| >>> relative_to('.', '/home/radix/Projects/Twisted') |
| 'Projects/Twisted' |
| |
| The 'relativee' must be a child of 'basepath'. |
| """ |
| basepath = os.path.abspath(base) |
| relativee = os.path.abspath(relativee) |
| if relativee.startswith(basepath): |
| relative = relativee[len(basepath) :] |
| if relative.startswith(os.sep): |
| relative = relative[1:] |
| return os.path.join(base, relative) |
| raise ValueError("%s is not a subpath of %s" % (relativee, basepath)) |
| |
| |
| def get_packages(dname, pkgname=None, results=None, ignore=None, parent=None): |
| """ |
| Get all packages which are under dname. This is necessary for |
| Python 2.2's distutils. Pretty similar arguments to getDataFiles, |
| including 'parent'. |
| """ |
| parent = parent or "" |
| prefix = [] |
| if parent: |
| prefix = [parent] |
| bname = os.path.basename(dname) |
| ignore = ignore or [] |
| if bname in ignore: |
| return [] |
| if results is None: |
| results = [] |
| if pkgname is None: |
| pkgname = [] |
| subfiles = os.listdir(dname) |
| abssubfiles = [os.path.join(dname, x) for x in subfiles] |
| |
| if "__init__.py" in subfiles: |
| results.append(prefix + pkgname + [bname]) |
| for subdir in filter(os.path.isdir, abssubfiles): |
| get_packages( |
| subdir, |
| pkgname=pkgname + [bname], |
| results=results, |
| ignore=ignore, |
| parent=parent, |
| ) |
| res = [".".join(result) for result in results] |
| return res |
| |
| |
| def get_data_files(dname, ignore=None, parent=None): |
| """ |
| Get all the data files that should be included in this distutils Project. |
| |
| 'dname' should be the path to the package that you're distributing. |
| |
| 'ignore' is a list of sub-packages to ignore. This facilitates |
| disparate package hierarchies. That's a fancy way of saying that |
| the 'twisted' package doesn't want to include the 'twisted.conch' |
| package, so it will pass ['conch'] as the value. |
| |
| 'parent' is necessary if you're distributing a subpackage like |
| twisted.conch. 'dname' should point to 'twisted/conch' and 'parent' |
| should point to 'twisted'. This ensures that your data_files are |
| generated correctly, only using relative paths for the first element |
| of the tuple ('twisted/conch/*'). |
| The default 'parent' is the current working directory. |
| """ |
| parent = parent or "." |
| ignore = ignore or [] |
| result = [] |
| for directory, subdirectories, filenames in os.walk(dname): |
| resultfiles = [] |
| for exname in EXCLUDE_NAMES: |
| if exname in subdirectories: |
| subdirectories.remove(exname) |
| for ig in ignore: |
| if ig in subdirectories: |
| subdirectories.remove(ig) |
| for filename in _filter_names(filenames): |
| resultfiles.append(filename) |
| if resultfiles: |
| for filename in resultfiles: |
| file_path = os.path.join(directory, filename) |
| if parent: |
| file_path = file_path.replace(parent + os.sep, "") |
| result.append(file_path) |
| |
| return result |
| |
| |
| # END: Taken from Twisted |
| |
| |
| # Different versions of python have different requirements. We can't use |
| # libcloud.utils.py3 here because it relies on backports dependency being |
| # installed / available |
| PY_pre_37 = sys.version_info < (3, 7, 0) |
| |
| HTML_VIEWSOURCE_BASE = "https://svn.apache.org/viewvc/libcloud/trunk" |
| PROJECT_BASE_DIR = "https://libcloud.apache.org" |
| TEST_PATHS = [ |
| "libcloud/test", |
| "libcloud/test/common", |
| "libcloud/test/compute", |
| "libcloud/test/storage", |
| "libcloud/test/loadbalancer", |
| "libcloud/test/dns", |
| "libcloud/test/container", |
| "libcloud/test/backup", |
| ] |
| DOC_TEST_MODULES = [ |
| "libcloud.compute.drivers.dummy", |
| "libcloud.storage.drivers.dummy", |
| "libcloud.dns.drivers.dummy", |
| "libcloud.container.drivers.dummy", |
| "libcloud.backup.drivers.dummy", |
| ] |
| |
| SUPPORTED_VERSIONS = ["PyPy 3.7+", "Python 3.7+"] |
| |
| # NOTE: python_version syntax is only supported when build system has |
| # setuptools >= 36.2 |
| # For installation, minimum required pip version is 1.4 |
| # Reference: https://hynek.me/articles/conditional-python-dependencies/ |
| # We rely on >= 2.26.0 to avoid issues with LGPL transitive dependency |
| # See https://github.com/apache/libcloud/issues/1594 for more context |
| INSTALL_REQUIREMENTS = [] |
| INSTALL_REQUIREMENTS.append("requests>=2.26.0") |
| |
| |
| setuptools_version = tuple(setuptools.__version__.split(".")[0:2]) |
| setuptools_version = tuple([int(c) for c in setuptools_version]) |
| |
| if setuptools_version < (36, 2): |
| if "bdist_wheel" in sys.argv: |
| # NOTE: We need to do that because we use universal wheel |
| msg = ( |
| "Need to use latest version of setuptools when building wheels to ensure included " |
| "metadata is correct. Current version: %s" % (setuptools.__version__) |
| ) |
| raise RuntimeError(msg) |
| |
| TEST_REQUIREMENTS = [ |
| "requests_mock", |
| "pytest", |
| "pytest-runner", |
| ] + INSTALL_REQUIREMENTS |
| |
| if PY_pre_37: |
| version = ".".join([str(x) for x in sys.version_info[:3]]) |
| print( |
| "Python version %s is not supported. Supported versions are: %s. " |
| "Latest version which supports Python 2.7 and Python 3 < 3.5.0 is " |
| "Libcloud v2.8.2 and Libcloud v3.5.x for Python 3.5." |
| % (version, ", ".join(SUPPORTED_VERSIONS)) |
| ) |
| sys.exit(1) |
| |
| |
| def read_version_string(): |
| version = None |
| cwd = os.path.dirname(os.path.abspath(__file__)) |
| version_file = os.path.join(cwd, "libcloud/__init__.py") |
| |
| with open(version_file) as fp: |
| content = fp.read() |
| |
| match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]", content, re.M) |
| |
| if match: |
| version = match.group(1) |
| return version |
| |
| raise Exception("Cannot find version in libcloud/__init__.py") |
| |
| |
| def forbid_publish(): |
| argv = sys.argv |
| if "upload" in argv: |
| print( |
| "You shouldn't use upload command to upload a release to PyPi. " |
| "You need to manually upload files generated using release.sh " |
| "script.\n" |
| 'For more information, see "Making a release section" in the ' |
| "documentation" |
| ) |
| sys.exit(1) |
| |
| |
| class ApiDocsCommand(Command): |
| description = "generate API documentation" |
| user_options = [] |
| |
| def initialize_options(self): |
| pass |
| |
| def finalize_options(self): |
| pass |
| |
| def run(self): |
| if not has_epydoc: |
| raise RuntimeError('Missing "epydoc" package!') |
| |
| os.system( |
| "pydoctor" |
| " --add-package=libcloud" |
| " --project-name=libcloud" |
| " --make-html" |
| ' --html-viewsource-base="%s"' |
| " --project-base-dir=`pwd`" |
| ' --project-url="%s"' % (HTML_VIEWSOURCE_BASE, PROJECT_BASE_DIR) |
| ) |
| |
| |
| forbid_publish() |
| |
| needs_pytest = {"pytest", "test", "ptr"}.intersection(sys.argv) |
| pytest_runner = ["pytest-runner"] if needs_pytest else [] |
| |
| setup( |
| name="apache-libcloud", |
| version=read_version_string(), |
| description="A standard Python library that abstracts away differences" |
| + " among multiple cloud provider APIs. For more information" |
| + " and documentation, please see https://libcloud.apache.org", |
| long_description=open("README.rst").read(), |
| author="Apache Software Foundation", |
| author_email="dev@libcloud.apache.org", |
| install_requires=INSTALL_REQUIREMENTS, |
| python_requires=">=3.6, <4", |
| packages=get_packages("libcloud"), |
| package_dir={ |
| "libcloud": "libcloud", |
| }, |
| package_data={ |
| "libcloud": get_data_files("libcloud", parent="libcloud") + ["py.typed"], |
| }, |
| license="Apache License (2.0)", |
| url="https://libcloud.apache.org/", |
| setup_requires=pytest_runner, |
| tests_require=TEST_REQUIREMENTS, |
| cmdclass={ |
| "apidocs": ApiDocsCommand, |
| }, |
| zip_safe=False, |
| classifiers=[ |
| "Development Status :: 5 - Production/Stable", |
| "Environment :: Console", |
| "Intended Audience :: Developers", |
| "Intended Audience :: System Administrators", |
| "License :: OSI Approved :: Apache Software License", |
| "Operating System :: OS Independent", |
| "Programming Language :: Python", |
| "Topic :: Software Development :: Libraries :: Python Modules", |
| "Programming Language :: Python :: 3", |
| "Programming Language :: Python :: 3.7", |
| "Programming Language :: Python :: 3.8", |
| "Programming Language :: Python :: 3.9", |
| "Programming Language :: Python :: 3.10", |
| "Programming Language :: Python :: 3.11", |
| "Programming Language :: Python :: Implementation :: CPython", |
| "Programming Language :: Python :: Implementation :: PyPy", |
| ], |
| ) |