OLD | NEW |
(Empty) | |
| 1 """ |
| 2 Monkey patching of distutils. |
| 3 """ |
| 4 |
| 5 import sys |
| 6 import distutils.filelist |
| 7 import platform |
| 8 import types |
| 9 import functools |
| 10 import inspect |
| 11 |
| 12 from .py26compat import import_module |
| 13 import six |
| 14 |
| 15 import setuptools |
| 16 |
| 17 __all__ = [] |
| 18 """ |
| 19 Everything is private. Contact the project team |
| 20 if you think you need this functionality. |
| 21 """ |
| 22 |
| 23 |
| 24 def get_unpatched(item): |
| 25 lookup = ( |
| 26 get_unpatched_class if isinstance(item, six.class_types) else |
| 27 get_unpatched_function if isinstance(item, types.FunctionType) else |
| 28 lambda item: None |
| 29 ) |
| 30 return lookup(item) |
| 31 |
| 32 |
| 33 def get_unpatched_class(cls): |
| 34 """Protect against re-patching the distutils if reloaded |
| 35 |
| 36 Also ensures that no other distutils extension monkeypatched the distutils |
| 37 first. |
| 38 """ |
| 39 external_bases = ( |
| 40 cls |
| 41 for cls in inspect.getmro(cls) |
| 42 if not cls.__module__.startswith('setuptools') |
| 43 ) |
| 44 base = next(external_bases) |
| 45 if not base.__module__.startswith('distutils'): |
| 46 msg = "distutils has already been patched by %r" % cls |
| 47 raise AssertionError(msg) |
| 48 return base |
| 49 |
| 50 |
| 51 def patch_all(): |
| 52 # we can't patch distutils.cmd, alas |
| 53 distutils.core.Command = setuptools.Command |
| 54 |
| 55 has_issue_12885 = ( |
| 56 sys.version_info < (3, 4, 6) |
| 57 or |
| 58 (3, 5) < sys.version_info <= (3, 5, 3) |
| 59 or |
| 60 (3, 6) < sys.version_info |
| 61 ) |
| 62 |
| 63 if has_issue_12885: |
| 64 # fix findall bug in distutils (http://bugs.python.org/issue12885) |
| 65 distutils.filelist.findall = setuptools.findall |
| 66 |
| 67 needs_warehouse = ( |
| 68 sys.version_info < (2, 7, 13) |
| 69 or |
| 70 (3, 0) < sys.version_info < (3, 3, 7) |
| 71 or |
| 72 (3, 4) < sys.version_info < (3, 4, 6) |
| 73 or |
| 74 (3, 5) < sys.version_info <= (3, 5, 3) |
| 75 or |
| 76 (3, 6) < sys.version_info |
| 77 ) |
| 78 |
| 79 if needs_warehouse: |
| 80 warehouse = 'https://upload.pypi.org/legacy/' |
| 81 distutils.config.PyPIRCCommand.DEFAULT_REPOSITORY = warehouse |
| 82 |
| 83 _patch_distribution_metadata_write_pkg_file() |
| 84 _patch_distribution_metadata_write_pkg_info() |
| 85 |
| 86 # Install Distribution throughout the distutils |
| 87 for module in distutils.dist, distutils.core, distutils.cmd: |
| 88 module.Distribution = setuptools.dist.Distribution |
| 89 |
| 90 # Install the patched Extension |
| 91 distutils.core.Extension = setuptools.extension.Extension |
| 92 distutils.extension.Extension = setuptools.extension.Extension |
| 93 if 'distutils.command.build_ext' in sys.modules: |
| 94 sys.modules['distutils.command.build_ext'].Extension = ( |
| 95 setuptools.extension.Extension |
| 96 ) |
| 97 |
| 98 patch_for_msvc_specialized_compiler() |
| 99 |
| 100 |
| 101 def _patch_distribution_metadata_write_pkg_file(): |
| 102 """Patch write_pkg_file to also write Requires-Python/Requires-External""" |
| 103 distutils.dist.DistributionMetadata.write_pkg_file = ( |
| 104 setuptools.dist.write_pkg_file |
| 105 ) |
| 106 |
| 107 |
| 108 def _patch_distribution_metadata_write_pkg_info(): |
| 109 """ |
| 110 Workaround issue #197 - Python 3 prior to 3.2.2 uses an environment-local |
| 111 encoding to save the pkg_info. Monkey-patch its write_pkg_info method to |
| 112 correct this undesirable behavior. |
| 113 """ |
| 114 environment_local = (3,) <= sys.version_info[:3] < (3, 2, 2) |
| 115 if not environment_local: |
| 116 return |
| 117 |
| 118 distutils.dist.DistributionMetadata.write_pkg_info = ( |
| 119 setuptools.dist.write_pkg_info |
| 120 ) |
| 121 |
| 122 |
| 123 def patch_func(replacement, target_mod, func_name): |
| 124 """ |
| 125 Patch func_name in target_mod with replacement |
| 126 |
| 127 Important - original must be resolved by name to avoid |
| 128 patching an already patched function. |
| 129 """ |
| 130 original = getattr(target_mod, func_name) |
| 131 |
| 132 # set the 'unpatched' attribute on the replacement to |
| 133 # point to the original. |
| 134 vars(replacement).setdefault('unpatched', original) |
| 135 |
| 136 # replace the function in the original module |
| 137 setattr(target_mod, func_name, replacement) |
| 138 |
| 139 |
| 140 def get_unpatched_function(candidate): |
| 141 return getattr(candidate, 'unpatched') |
| 142 |
| 143 |
| 144 def patch_for_msvc_specialized_compiler(): |
| 145 """ |
| 146 Patch functions in distutils to use standalone Microsoft Visual C++ |
| 147 compilers. |
| 148 """ |
| 149 # import late to avoid circular imports on Python < 3.5 |
| 150 msvc = import_module('setuptools.msvc') |
| 151 |
| 152 if platform.system() != 'Windows': |
| 153 # Compilers only availables on Microsoft Windows |
| 154 return |
| 155 |
| 156 def patch_params(mod_name, func_name): |
| 157 """ |
| 158 Prepare the parameters for patch_func to patch indicated function. |
| 159 """ |
| 160 repl_prefix = 'msvc9_' if 'msvc9' in mod_name else 'msvc14_' |
| 161 repl_name = repl_prefix + func_name.lstrip('_') |
| 162 repl = getattr(msvc, repl_name) |
| 163 mod = import_module(mod_name) |
| 164 if not hasattr(mod, func_name): |
| 165 raise ImportError(func_name) |
| 166 return repl, mod, func_name |
| 167 |
| 168 # Python 2.7 to 3.4 |
| 169 msvc9 = functools.partial(patch_params, 'distutils.msvc9compiler') |
| 170 |
| 171 # Python 3.5+ |
| 172 msvc14 = functools.partial(patch_params, 'distutils._msvccompiler') |
| 173 |
| 174 try: |
| 175 # Patch distutils.msvc9compiler |
| 176 patch_func(*msvc9('find_vcvarsall')) |
| 177 patch_func(*msvc9('query_vcvarsall')) |
| 178 except ImportError: |
| 179 pass |
| 180 |
| 181 try: |
| 182 # Patch distutils._msvccompiler._get_vc_env |
| 183 patch_func(*msvc14('_get_vc_env')) |
| 184 except ImportError: |
| 185 pass |
| 186 |
| 187 try: |
| 188 # Patch distutils._msvccompiler.gen_lib_options for Numpy |
| 189 patch_func(*msvc14('gen_lib_options')) |
| 190 except ImportError: |
| 191 pass |
OLD | NEW |