Coverage for /opt/homebrew/lib/python3.11/site-packages/_distutils_hack/__init__.py: 5%

98 statements  

« prev     ^ index     » next       coverage.py v7.2.3, created at 2023-05-04 13:14 +0700

1# don't import any costly modules 

2import sys 

3import os 

4 

5 

6is_pypy = '__pypy__' in sys.builtin_module_names 

7 

8 

9def warn_distutils_present(): 

10 if 'distutils' not in sys.modules: 

11 return 

12 if is_pypy and sys.version_info < (3, 7): 

13 # PyPy for 3.6 unconditionally imports distutils, so bypass the warning 

14 # https://foss.heptapod.net/pypy/pypy/-/blob/be829135bc0d758997b3566062999ee8b23872b4/lib-python/3/site.py#L250 

15 return 

16 import warnings 

17 

18 warnings.warn( 

19 "Distutils was imported before Setuptools, but importing Setuptools " 

20 "also replaces the `distutils` module in `sys.modules`. This may lead " 

21 "to undesirable behaviors or errors. To avoid these issues, avoid " 

22 "using distutils directly, ensure that setuptools is installed in the " 

23 "traditional way (e.g. not an editable install), and/or make sure " 

24 "that setuptools is always imported before distutils." 

25 ) 

26 

27 

28def clear_distutils(): 

29 if 'distutils' not in sys.modules: 

30 return 

31 import warnings 

32 

33 warnings.warn("Setuptools is replacing distutils.") 

34 mods = [ 

35 name 

36 for name in sys.modules 

37 if name == "distutils" or name.startswith("distutils.") 

38 ] 

39 for name in mods: 

40 del sys.modules[name] 

41 

42 

43def enabled(): 

44 """ 

45 Allow selection of distutils by environment variable. 

46 """ 

47 which = os.environ.get('SETUPTOOLS_USE_DISTUTILS', 'local') 

48 return which == 'local' 

49 

50 

51def ensure_local_distutils(): 

52 import importlib 

53 

54 clear_distutils() 

55 

56 # With the DistutilsMetaFinder in place, 

57 # perform an import to cause distutils to be 

58 # loaded from setuptools._distutils. Ref #2906. 

59 with shim(): 

60 importlib.import_module('distutils') 

61 

62 # check that submodules load as expected 

63 core = importlib.import_module('distutils.core') 

64 assert '_distutils' in core.__file__, core.__file__ 

65 assert 'setuptools._distutils.log' not in sys.modules 

66 

67 

68def do_override(): 

69 """ 

70 Ensure that the local copy of distutils is preferred over stdlib. 

71 

72 See https://github.com/pypa/setuptools/issues/417#issuecomment-392298401 

73 for more motivation. 

74 """ 

75 if enabled(): 

76 warn_distutils_present() 

77 ensure_local_distutils() 

78 

79 

80class _TrivialRe: 

81 def __init__(self, *patterns): 

82 self._patterns = patterns 

83 

84 def match(self, string): 

85 return all(pat in string for pat in self._patterns) 

86 

87 

88class DistutilsMetaFinder: 

89 def find_spec(self, fullname, path, target=None): 

90 # optimization: only consider top level modules and those 

91 # found in the CPython test suite. 

92 if path is not None and not fullname.startswith('test.'): 

93 return 

94 

95 method_name = 'spec_for_{fullname}'.format(**locals()) 

96 method = getattr(self, method_name, lambda: None) 

97 return method() 

98 

99 def spec_for_distutils(self): 

100 if self.is_cpython(): 

101 return 

102 

103 import importlib 

104 import importlib.abc 

105 import importlib.util 

106 

107 try: 

108 mod = importlib.import_module('setuptools._distutils') 

109 except Exception: 

110 # There are a couple of cases where setuptools._distutils 

111 # may not be present: 

112 # - An older Setuptools without a local distutils is 

113 # taking precedence. Ref #2957. 

114 # - Path manipulation during sitecustomize removes 

115 # setuptools from the path but only after the hook 

116 # has been loaded. Ref #2980. 

117 # In either case, fall back to stdlib behavior. 

118 return 

119 

120 class DistutilsLoader(importlib.abc.Loader): 

121 def create_module(self, spec): 

122 mod.__name__ = 'distutils' 

123 return mod 

124 

125 def exec_module(self, module): 

126 pass 

127 

128 return importlib.util.spec_from_loader( 

129 'distutils', DistutilsLoader(), origin=mod.__file__ 

130 ) 

131 

132 @staticmethod 

133 def is_cpython(): 

134 """ 

135 Suppress supplying distutils for CPython (build and tests). 

136 Ref #2965 and #3007. 

137 """ 

138 return os.path.isfile('pybuilddir.txt') 

139 

140 def spec_for_pip(self): 

141 """ 

142 Ensure stdlib distutils when running under pip. 

143 See pypa/pip#8761 for rationale. 

144 """ 

145 if self.pip_imported_during_build(): 

146 return 

147 clear_distutils() 

148 self.spec_for_distutils = lambda: None 

149 

150 @classmethod 

151 def pip_imported_during_build(cls): 

152 """ 

153 Detect if pip is being imported in a build script. Ref #2355. 

154 """ 

155 import traceback 

156 

157 return any( 

158 cls.frame_file_is_setup(frame) for frame, line in traceback.walk_stack(None) 

159 ) 

160 

161 @staticmethod 

162 def frame_file_is_setup(frame): 

163 """ 

164 Return True if the indicated frame suggests a setup.py file. 

165 """ 

166 # some frames may not have __file__ (#2940) 

167 return frame.f_globals.get('__file__', '').endswith('setup.py') 

168 

169 def spec_for_sensitive_tests(self): 

170 """ 

171 Ensure stdlib distutils when running select tests under CPython. 

172 

173 python/cpython#91169 

174 """ 

175 clear_distutils() 

176 self.spec_for_distutils = lambda: None 

177 

178 sensitive_tests = ( 

179 [ 

180 'test.test_distutils', 

181 'test.test_peg_generator', 

182 'test.test_importlib', 

183 ] 

184 if sys.version_info < (3, 10) 

185 else [ 

186 'test.test_distutils', 

187 ] 

188 ) 

189 

190 

191for name in DistutilsMetaFinder.sensitive_tests: 

192 setattr( 

193 DistutilsMetaFinder, 

194 f'spec_for_{name}', 

195 DistutilsMetaFinder.spec_for_sensitive_tests, 

196 ) 

197 

198 

199DISTUTILS_FINDER = DistutilsMetaFinder() 

200 

201 

202def add_shim(): 

203 DISTUTILS_FINDER in sys.meta_path or insert_shim() 

204 

205 

206class shim: 

207 def __enter__(self): 

208 insert_shim() 

209 

210 def __exit__(self, exc, value, tb): 

211 remove_shim() 

212 

213 

214def insert_shim(): 

215 sys.meta_path.insert(0, DISTUTILS_FINDER) 

216 

217 

218def remove_shim(): 

219 try: 

220 sys.meta_path.remove(DISTUTILS_FINDER) 

221 except ValueError: 

222 pass