Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/setuptools/monkey.py: 56%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

78 statements  

1""" 

2Monkey patching of distutils. 

3""" 

4 

5import sys 

6import distutils.filelist 

7import platform 

8import types 

9import functools 

10from importlib import import_module 

11import inspect 

12 

13from setuptools.extern import six 

14 

15import setuptools 

16 

17__all__ = [] 

18""" 

19Everything is private. Contact the project team 

20if you think you need this functionality. 

21""" 

22 

23 

24def _get_mro(cls): 

25 """ 

26 Returns the bases classes for cls sorted by the MRO. 

27 

28 Works around an issue on Jython where inspect.getmro will not return all 

29 base classes if multiple classes share the same name. Instead, this 

30 function will return a tuple containing the class itself, and the contents 

31 of cls.__bases__. See https://github.com/pypa/setuptools/issues/1024. 

32 """ 

33 if platform.python_implementation() == "Jython": 

34 return (cls,) + cls.__bases__ 

35 return inspect.getmro(cls) 

36 

37 

38def get_unpatched(item): 

39 lookup = ( 

40 get_unpatched_class if isinstance(item, six.class_types) else 

41 get_unpatched_function if isinstance(item, types.FunctionType) else 

42 lambda item: None 

43 ) 

44 return lookup(item) 

45 

46 

47def get_unpatched_class(cls): 

48 """Protect against re-patching the distutils if reloaded 

49 

50 Also ensures that no other distutils extension monkeypatched the distutils 

51 first. 

52 """ 

53 external_bases = ( 

54 cls 

55 for cls in _get_mro(cls) 

56 if not cls.__module__.startswith('setuptools') 

57 ) 

58 base = next(external_bases) 

59 if not base.__module__.startswith('distutils'): 

60 msg = "distutils has already been patched by %r" % cls 

61 raise AssertionError(msg) 

62 return base 

63 

64 

65def patch_all(): 

66 # we can't patch distutils.cmd, alas 

67 distutils.core.Command = setuptools.Command 

68 

69 has_issue_12885 = sys.version_info <= (3, 5, 3) 

70 

71 if has_issue_12885: 

72 # fix findall bug in distutils (http://bugs.python.org/issue12885) 

73 distutils.filelist.findall = setuptools.findall 

74 

75 needs_warehouse = ( 

76 sys.version_info < (2, 7, 13) 

77 or 

78 (3, 4) < sys.version_info < (3, 4, 6) 

79 or 

80 (3, 5) < sys.version_info <= (3, 5, 3) 

81 ) 

82 

83 if needs_warehouse: 

84 warehouse = 'https://upload.pypi.org/legacy/' 

85 distutils.config.PyPIRCCommand.DEFAULT_REPOSITORY = warehouse 

86 

87 _patch_distribution_metadata() 

88 

89 # Install Distribution throughout the distutils 

90 for module in distutils.dist, distutils.core, distutils.cmd: 

91 module.Distribution = setuptools.dist.Distribution 

92 

93 # Install the patched Extension 

94 distutils.core.Extension = setuptools.extension.Extension 

95 distutils.extension.Extension = setuptools.extension.Extension 

96 if 'distutils.command.build_ext' in sys.modules: 

97 sys.modules['distutils.command.build_ext'].Extension = ( 

98 setuptools.extension.Extension 

99 ) 

100 

101 patch_for_msvc_specialized_compiler() 

102 

103 

104def _patch_distribution_metadata(): 

105 """Patch write_pkg_file and read_pkg_file for higher metadata standards""" 

106 for attr in ('write_pkg_file', 'read_pkg_file', 'get_metadata_version'): 

107 new_val = getattr(setuptools.dist, attr) 

108 setattr(distutils.dist.DistributionMetadata, attr, new_val) 

109 

110 

111def patch_func(replacement, target_mod, func_name): 

112 """ 

113 Patch func_name in target_mod with replacement 

114 

115 Important - original must be resolved by name to avoid 

116 patching an already patched function. 

117 """ 

118 original = getattr(target_mod, func_name) 

119 

120 # set the 'unpatched' attribute on the replacement to 

121 # point to the original. 

122 vars(replacement).setdefault('unpatched', original) 

123 

124 # replace the function in the original module 

125 setattr(target_mod, func_name, replacement) 

126 

127 

128def get_unpatched_function(candidate): 

129 return getattr(candidate, 'unpatched') 

130 

131 

132def patch_for_msvc_specialized_compiler(): 

133 """ 

134 Patch functions in distutils to use standalone Microsoft Visual C++ 

135 compilers. 

136 """ 

137 # import late to avoid circular imports on Python < 3.5 

138 msvc = import_module('setuptools.msvc') 

139 

140 if platform.system() != 'Windows': 

141 # Compilers only availables on Microsoft Windows 

142 return 

143 

144 def patch_params(mod_name, func_name): 

145 """ 

146 Prepare the parameters for patch_func to patch indicated function. 

147 """ 

148 repl_prefix = 'msvc9_' if 'msvc9' in mod_name else 'msvc14_' 

149 repl_name = repl_prefix + func_name.lstrip('_') 

150 repl = getattr(msvc, repl_name) 

151 mod = import_module(mod_name) 

152 if not hasattr(mod, func_name): 

153 raise ImportError(func_name) 

154 return repl, mod, func_name 

155 

156 # Python 2.7 to 3.4 

157 msvc9 = functools.partial(patch_params, 'distutils.msvc9compiler') 

158 

159 # Python 3.5+ 

160 msvc14 = functools.partial(patch_params, 'distutils._msvccompiler') 

161 

162 try: 

163 # Patch distutils.msvc9compiler 

164 patch_func(*msvc9('find_vcvarsall')) 

165 patch_func(*msvc9('query_vcvarsall')) 

166 except ImportError: 

167 pass 

168 

169 try: 

170 # Patch distutils._msvccompiler._get_vc_env 

171 patch_func(*msvc14('_get_vc_env')) 

172 except ImportError: 

173 pass 

174 

175 try: 

176 # Patch distutils._msvccompiler.gen_lib_options for Numpy 

177 patch_func(*msvc14('gen_lib_options')) 

178 except ImportError: 

179 pass