| | """ |
| | Monkey patching of distutils. |
| | """ |
| |
|
| | import sys |
| | import distutils.filelist |
| | import platform |
| | import types |
| | import functools |
| | from importlib import import_module |
| | import inspect |
| |
|
| | import setuptools |
| |
|
| | __all__ = [] |
| | """ |
| | Everything is private. Contact the project team |
| | if you think you need this functionality. |
| | """ |
| |
|
| |
|
| | def _get_mro(cls): |
| | """ |
| | Returns the bases classes for cls sorted by the MRO. |
| | |
| | Works around an issue on Jython where inspect.getmro will not return all |
| | base classes if multiple classes share the same name. Instead, this |
| | function will return a tuple containing the class itself, and the contents |
| | of cls.__bases__. See https://github.com/pypa/setuptools/issues/1024. |
| | """ |
| | if platform.python_implementation() == "Jython": |
| | return (cls,) + cls.__bases__ |
| | return inspect.getmro(cls) |
| |
|
| |
|
| | def get_unpatched(item): |
| | lookup = ( |
| | get_unpatched_class if isinstance(item, type) else |
| | get_unpatched_function if isinstance(item, types.FunctionType) else |
| | lambda item: None |
| | ) |
| | return lookup(item) |
| |
|
| |
|
| | def get_unpatched_class(cls): |
| | """Protect against re-patching the distutils if reloaded |
| | |
| | Also ensures that no other distutils extension monkeypatched the distutils |
| | first. |
| | """ |
| | external_bases = ( |
| | cls |
| | for cls in _get_mro(cls) |
| | if not cls.__module__.startswith('setuptools') |
| | ) |
| | base = next(external_bases) |
| | if not base.__module__.startswith('distutils'): |
| | msg = "distutils has already been patched by %r" % cls |
| | raise AssertionError(msg) |
| | return base |
| |
|
| |
|
| | def patch_all(): |
| | |
| | distutils.core.Command = setuptools.Command |
| |
|
| | has_issue_12885 = sys.version_info <= (3, 5, 3) |
| |
|
| | if has_issue_12885: |
| | |
| | distutils.filelist.findall = setuptools.findall |
| |
|
| | needs_warehouse = ( |
| | (3, 4) < sys.version_info < (3, 4, 6) |
| | or |
| | (3, 5) < sys.version_info <= (3, 5, 3) |
| | ) |
| |
|
| | if needs_warehouse: |
| | warehouse = 'https://upload.pypi.org/legacy/' |
| | distutils.config.PyPIRCCommand.DEFAULT_REPOSITORY = warehouse |
| |
|
| | _patch_distribution_metadata() |
| |
|
| | |
| | for module in distutils.dist, distutils.core, distutils.cmd: |
| | module.Distribution = setuptools.dist.Distribution |
| |
|
| | |
| | distutils.core.Extension = setuptools.extension.Extension |
| | distutils.extension.Extension = setuptools.extension.Extension |
| | if 'distutils.command.build_ext' in sys.modules: |
| | sys.modules['distutils.command.build_ext'].Extension = ( |
| | setuptools.extension.Extension |
| | ) |
| |
|
| | patch_for_msvc_specialized_compiler() |
| |
|
| |
|
| | def _patch_distribution_metadata(): |
| | """Patch write_pkg_file and read_pkg_file for higher metadata standards""" |
| | for attr in ('write_pkg_file', 'read_pkg_file', 'get_metadata_version'): |
| | new_val = getattr(setuptools.dist, attr) |
| | setattr(distutils.dist.DistributionMetadata, attr, new_val) |
| |
|
| |
|
| | def patch_func(replacement, target_mod, func_name): |
| | """ |
| | Patch func_name in target_mod with replacement |
| | |
| | Important - original must be resolved by name to avoid |
| | patching an already patched function. |
| | """ |
| | original = getattr(target_mod, func_name) |
| |
|
| | |
| | |
| | vars(replacement).setdefault('unpatched', original) |
| |
|
| | |
| | setattr(target_mod, func_name, replacement) |
| |
|
| |
|
| | def get_unpatched_function(candidate): |
| | return getattr(candidate, 'unpatched') |
| |
|
| |
|
| | def patch_for_msvc_specialized_compiler(): |
| | """ |
| | Patch functions in distutils to use standalone Microsoft Visual C++ |
| | compilers. |
| | """ |
| | |
| | msvc = import_module('setuptools.msvc') |
| |
|
| | if platform.system() != 'Windows': |
| | |
| | return |
| |
|
| | def patch_params(mod_name, func_name): |
| | """ |
| | Prepare the parameters for patch_func to patch indicated function. |
| | """ |
| | repl_prefix = 'msvc14_' |
| | repl_name = repl_prefix + func_name.lstrip('_') |
| | repl = getattr(msvc, repl_name) |
| | mod = import_module(mod_name) |
| | if not hasattr(mod, func_name): |
| | raise ImportError(func_name) |
| | return repl, mod, func_name |
| |
|
| | |
| | msvc14 = functools.partial(patch_params, 'distutils._msvccompiler') |
| |
|
| | try: |
| | |
| | patch_func(*msvc14('_get_vc_env')) |
| | except ImportError: |
| | pass |
| |
|
| | try: |
| | |
| | patch_func(*msvc14('gen_lib_options')) |
| | except ImportError: |
| | pass |
| |
|