blob: 97f9aa75cec8072b6278307c9a85ec95e4aa24d9 [file] [log] [blame]
# 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 sys
import re
import fnmatch
import setuptools
from setuptools import setup
from distutils.core import Command
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_35 = sys.version_info < (3, 5, 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', 'Python 3.5+']
# 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/
INSTALL_REQUIREMENTS = [
'requests>=2.5.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 = [
'mock',
'requests_mock',
'pytest',
'pytest-runner'
] + INSTALL_REQUIREMENTS
if PY_pre_35:
version = '.'.join([str(x) for x in sys.version_info[:3]])
print('Version ' + version + ' 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' % ', '.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.5.*, <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.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: Implementation :: CPython',
'Programming Language :: Python :: Implementation :: PyPy'
]
)