Spaces:
Building
Building
"""Extensions to the 'distutils' for large or complex distributions""" | |
from fnmatch import fnmatchcase | |
import functools | |
import os | |
import re | |
import _distutils_hack.override # noqa: F401 | |
import distutils.core | |
from distutils.errors import DistutilsOptionError | |
from distutils.util import convert_path | |
from ._deprecation_warning import SetuptoolsDeprecationWarning | |
import setuptools.version | |
from setuptools.extension import Extension | |
from setuptools.dist import Distribution | |
from setuptools.depends import Require | |
from . import monkey | |
__all__ = [ | |
'setup', | |
'Distribution', | |
'Command', | |
'Extension', | |
'Require', | |
'SetuptoolsDeprecationWarning', | |
'find_packages', | |
'find_namespace_packages', | |
] | |
__version__ = setuptools.version.__version__ | |
bootstrap_install_from = None | |
class PackageFinder: | |
""" | |
Generate a list of all Python packages found within a directory | |
""" | |
def find(cls, where='.', exclude=(), include=('*',)): | |
"""Return a list all Python packages found within directory 'where' | |
'where' is the root directory which will be searched for packages. It | |
should be supplied as a "cross-platform" (i.e. URL-style) path; it will | |
be converted to the appropriate local path syntax. | |
'exclude' is a sequence of package names to exclude; '*' can be used | |
as a wildcard in the names, such that 'foo.*' will exclude all | |
subpackages of 'foo' (but not 'foo' itself). | |
'include' is a sequence of package names to include. If it's | |
specified, only the named packages will be included. If it's not | |
specified, all found packages will be included. 'include' can contain | |
shell style wildcard patterns just like 'exclude'. | |
""" | |
return list( | |
cls._find_packages_iter( | |
convert_path(where), | |
cls._build_filter('ez_setup', '*__pycache__', *exclude), | |
cls._build_filter(*include), | |
) | |
) | |
def _find_packages_iter(cls, where, exclude, include): | |
""" | |
All the packages found in 'where' that pass the 'include' filter, but | |
not the 'exclude' filter. | |
""" | |
for root, dirs, files in os.walk(where, followlinks=True): | |
# Copy dirs to iterate over it, then empty dirs. | |
all_dirs = dirs[:] | |
dirs[:] = [] | |
for dir in all_dirs: | |
full_path = os.path.join(root, dir) | |
rel_path = os.path.relpath(full_path, where) | |
package = rel_path.replace(os.path.sep, '.') | |
# Skip directory trees that are not valid packages | |
if '.' in dir or not cls._looks_like_package(full_path): | |
continue | |
# Should this package be included? | |
if include(package) and not exclude(package): | |
yield package | |
# Keep searching subdirectories, as there may be more packages | |
# down there, even if the parent was excluded. | |
dirs.append(dir) | |
def _looks_like_package(path): | |
"""Does a directory look like a package?""" | |
return os.path.isfile(os.path.join(path, '__init__.py')) | |
def _build_filter(*patterns): | |
""" | |
Given a list of patterns, return a callable that will be true only if | |
the input matches at least one of the patterns. | |
""" | |
return lambda name: any(fnmatchcase(name, pat=pat) for pat in patterns) | |
class PEP420PackageFinder(PackageFinder): | |
def _looks_like_package(path): | |
return True | |
find_packages = PackageFinder.find | |
find_namespace_packages = PEP420PackageFinder.find | |
def _install_setup_requires(attrs): | |
# Note: do not use `setuptools.Distribution` directly, as | |
# our PEP 517 backend patch `distutils.core.Distribution`. | |
class MinimalDistribution(distutils.core.Distribution): | |
""" | |
A minimal version of a distribution for supporting the | |
fetch_build_eggs interface. | |
""" | |
def __init__(self, attrs): | |
_incl = 'dependency_links', 'setup_requires' | |
filtered = {k: attrs[k] for k in set(_incl) & set(attrs)} | |
distutils.core.Distribution.__init__(self, filtered) | |
def finalize_options(self): | |
""" | |
Disable finalize_options to avoid building the working set. | |
Ref #2158. | |
""" | |
dist = MinimalDistribution(attrs) | |
# Honor setup.cfg's options. | |
dist.parse_config_files(ignore_option_errors=True) | |
if dist.setup_requires: | |
dist.fetch_build_eggs(dist.setup_requires) | |
def setup(**attrs): | |
# Make sure we have any requirements needed to interpret 'attrs'. | |
_install_setup_requires(attrs) | |
return distutils.core.setup(**attrs) | |
setup.__doc__ = distutils.core.setup.__doc__ | |
_Command = monkey.get_unpatched(distutils.core.Command) | |
class Command(_Command): | |
__doc__ = _Command.__doc__ | |
command_consumes_arguments = False | |
def __init__(self, dist, **kw): | |
""" | |
Construct the command for dist, updating | |
vars(self) with any keyword parameters. | |
""" | |
_Command.__init__(self, dist) | |
vars(self).update(kw) | |
def _ensure_stringlike(self, option, what, default=None): | |
val = getattr(self, option) | |
if val is None: | |
setattr(self, option, default) | |
return default | |
elif not isinstance(val, str): | |
raise DistutilsOptionError( | |
"'%s' must be a %s (got `%s`)" % (option, what, val) | |
) | |
return val | |
def ensure_string_list(self, option): | |
r"""Ensure that 'option' is a list of strings. If 'option' is | |
currently a string, we split it either on /,\s*/ or /\s+/, so | |
"foo bar baz", "foo,bar,baz", and "foo, bar baz" all become | |
["foo", "bar", "baz"]. | |
""" | |
val = getattr(self, option) | |
if val is None: | |
return | |
elif isinstance(val, str): | |
setattr(self, option, re.split(r',\s*|\s+', val)) | |
else: | |
if isinstance(val, list): | |
ok = all(isinstance(v, str) for v in val) | |
else: | |
ok = False | |
if not ok: | |
raise DistutilsOptionError( | |
"'%s' must be a list of strings (got %r)" % (option, val) | |
) | |
def reinitialize_command(self, command, reinit_subcommands=0, **kw): | |
cmd = _Command.reinitialize_command(self, command, reinit_subcommands) | |
vars(cmd).update(kw) | |
return cmd | |
def _find_all_simple(path): | |
""" | |
Find all files under 'path' | |
""" | |
results = ( | |
os.path.join(base, file) | |
for base, dirs, files in os.walk(path, followlinks=True) | |
for file in files | |
) | |
return filter(os.path.isfile, results) | |
def findall(dir=os.curdir): | |
""" | |
Find all files under 'dir' and return the list of full filenames. | |
Unless dir is '.', return full filenames with dir prepended. | |
""" | |
files = _find_all_simple(dir) | |
if dir == os.curdir: | |
make_rel = functools.partial(os.path.relpath, start=dir) | |
files = map(make_rel, files) | |
return list(files) | |
class sic(str): | |
"""Treat this string as-is (https://en.wikipedia.org/wiki/Sic)""" | |
# Apply monkey patches | |
monkey.patch_all() | |