diff --git a/MANIFEST.in b/MANIFEST.in index a8ddee371d..d6195e6878 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,3 +1,5 @@ +global-exclude *.py[cod] +global-exclude .pytest_cache include LICENSE include NOTICE include example_*.py @@ -8,21 +10,10 @@ include pyproject.toml include requirements-tests.txt include requirements-lint.txt include libcloud/data/pricing.json -prune libcloud/test/secrets.py -prune requirements-rtd.txt include demos/* include scripts/check_file_names.sh -include libcloud/test/*.py -include libcloud/test/pricing_test.json -include libcloud/test/secrets.py-dist -include libcloud/test/common/*.py -include libcloud/test/compute/*.py -include libcloud/test/storage/*.py -include libcloud/test/loadbalancer/*.py -include libcloud/test/dns/*.py -include libcloud/test/common/fixtures/*/* -include libcloud/test/compute/fixtures/*/* -include libcloud/test/compute/fixtures/*/*/* -include libcloud/test/storage/fixtures/*/* -include libcloud/test/loadbalancer/fixtures/*/* -include libcloud/test/dns/fixtures/*/* +recursive-exclude libcloud/test secrets.py +prune libcloud/test/secrets.py +prune requirements-rtd.txt +prune dist +prune build diff --git a/pyproject.toml b/pyproject.toml index f90cbf306e..fb91c42a17 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,10 +1,93 @@ +# 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. + [build-system] -requires = ["setuptools~=66.1", "wheel~=0.37.1"] +requires = [ + "setuptools~=66.1", + "wheel~=0.37.1" +] build-backend = "setuptools.build_meta" +[project] +name = "apache-libcloud" +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" +authors = [ + {name = "Apache Software Foundation", email = "dev@libcloud.apache.org"}, +] +keywords = [ + "cloud", + "libcloud", + "apache", + "aws", + "ec2", +] +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", + "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 :: 3.12", + "Programming Language :: Python :: Implementation :: CPython", + "Programming Language :: Python :: Implementation :: PyPy", + "Topic :: Software Development :: Libraries :: Python Modules", +] +requires-python = ">=3.7" +dependencies = [ + "requests>=2.26.0", +] +license = {text = "Apache License (2.0)"} +dynamic = ["version", "readme"] + +[project.urls] +homepage = "https://libcloud.apache.org" +documentation = "https://libcloud.readthedocs.io" +source = "https://github.com/apache/libcloud" +issues = "https://github.com/apache/libcloud/issues" +changelog = "https://github.com/apache/libcloud/blob/trunk/CHANGES.rst" + + +[project.optional-dependencies] +test = [ + "pytest", + "requests_mock", +] + +[tool.setuptools.packages.find] +where = ["./"] +include = ["libcloud"] + +[tool.setuptools] +exclude-package-data = { "*" = ["secrets.py"], "libcloud.test" = ["secrets.py"] } + +[tool.setuptools.dynamic] +version = {attr = "libcloud.__version__"} +readme = {file = ["README.rst"]} + + [tool.black] line_length = 100 -target_version = ['py37', 'py38', 'py39', 'py310'] +target_version = ['py37', 'py38', 'py39', 'py310', 'py311'] include = '\.pyi?$' extended_exclude = ''' ( diff --git a/setup.py b/setup.py index 802eb28381..4bc8bd8de2 100644 --- a/setup.py +++ b/setup.py @@ -21,272 +21,4 @@ import setuptools from setuptools import setup -# 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) - - -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, - 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 :: 3.12", - "Programming Language :: Python :: Implementation :: CPython", - "Programming Language :: Python :: Implementation :: PyPy", - ], -) +setup() diff --git a/tox.ini b/tox.ini index 05c6dc643f..a4d44ef1e0 100644 --- a/tox.ini +++ b/tox.ini @@ -369,7 +369,7 @@ deps = setenv = CRYPTOGRAPHY_ALLOW_OPENSSL_102=1 commands = cp libcloud/test/secrets.py-dist libcloud/test/secrets.py - coverage run --source=libcloud setup.py test + coverage run --source=libcloud -m pytest --benchmark-disable [testenv:coverage-ci] passenv = @@ -382,7 +382,7 @@ deps = setenv = CRYPTOGRAPHY_ALLOW_OPENSSL_102=1 commands = cp libcloud/test/secrets.py-dist libcloud/test/secrets.py - coverage run --source=libcloud setup.py test + coverage run --source=libcloud -m pytest --benchmark-disable coverage xml [testenv:isort]