Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/_distutils_hack/__init__.py: 35%

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

103 statements  

1# don't import any costly modules 

2import os 

3import sys 

4 

5report_url = ( 

6 "https://github.com/pypa/setuptools/issues/new?" 

7 "template=distutils-deprecation.yml" 

8) 

9 

10 

11def warn_distutils_present(): 

12 if 'distutils' not in sys.modules: 

13 return 

14 import warnings 

15 

16 warnings.warn( 

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

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

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

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

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

22 "that setuptools is always imported before distutils." 

23 ) 

24 

25 

26def clear_distutils(): 

27 if 'distutils' not in sys.modules: 

28 return 

29 import warnings 

30 

31 warnings.warn( 

32 "Setuptools is replacing distutils. Support for replacing " 

33 "an already imported distutils is deprecated. In the future, " 

34 "this condition will fail. " 

35 f"Register concerns at {report_url}" 

36 ) 

37 mods = [ 

38 name 

39 for name in sys.modules 

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

41 ] 

42 for name in mods: 

43 del sys.modules[name] 

44 

45 

46def enabled(): 

47 """ 

48 Allow selection of distutils by environment variable. 

49 """ 

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

51 if which == 'stdlib': 

52 import warnings 

53 

54 warnings.warn( 

55 "Reliance on distutils from stdlib is deprecated. Users " 

56 "must rely on setuptools to provide the distutils module. " 

57 "Avoid importing distutils or import setuptools first, " 

58 "and avoid setting SETUPTOOLS_USE_DISTUTILS=stdlib. " 

59 f"Register concerns at {report_url}" 

60 ) 

61 return which == 'local' 

62 

63 

64def ensure_local_distutils(): 

65 import importlib 

66 

67 clear_distutils() 

68 

69 # With the DistutilsMetaFinder in place, 

70 # perform an import to cause distutils to be 

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

72 with shim(): 

73 importlib.import_module('distutils') 

74 

75 # check that submodules load as expected 

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

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

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

79 

80 

81def do_override(): 

82 return True 

83 

84def do_override2(): 

85 """ 

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

87 

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

89 for more motivation. 

90 """ 

91 if enabled(): 

92 warn_distutils_present() 

93 ensure_local_distutils() 

94 

95 

96class _TrivialRe: 

97 def __init__(self, *patterns): 

98 self._patterns = patterns 

99 

100 def match(self, string): 

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

102 

103 

104class DistutilsMetaFinder: 

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

106 # optimization: only consider top level modules and those 

107 # found in the CPython test suite. 

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

109 return None 

110 

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

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

113 return method() 

114 

115 def spec_for_distutils(self): 

116 if self.is_cpython(): 

117 return None 

118 

119 import importlib 

120 import importlib.abc 

121 import importlib.util 

122 

123 try: 

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

125 except Exception: 

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

127 # may not be present: 

128 # - An older Setuptools without a local distutils is 

129 # taking precedence. Ref #2957. 

130 # - Path manipulation during sitecustomize removes 

131 # setuptools from the path but only after the hook 

132 # has been loaded. Ref #2980. 

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

134 return None 

135 

136 class DistutilsLoader(importlib.abc.Loader): 

137 def create_module(self, spec): 

138 mod.__name__ = 'distutils' 

139 return mod 

140 

141 def exec_module(self, module): 

142 pass 

143 

144 return importlib.util.spec_from_loader( 

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

146 ) 

147 

148 @staticmethod 

149 def is_cpython(): 

150 """ 

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

152 Ref #2965 and #3007. 

153 """ 

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

155 

156 def spec_for_pip(self): 

157 """ 

158 Ensure stdlib distutils when running under pip. 

159 See pypa/pip#8761 for rationale. 

160 """ 

161 if sys.version_info >= (3, 12) or self.pip_imported_during_build(): 

162 return 

163 clear_distutils() 

164 self.spec_for_distutils = lambda: None 

165 

166 @classmethod 

167 def pip_imported_during_build(cls): 

168 """ 

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

170 """ 

171 import traceback 

172 

173 return any( 

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

175 ) 

176 

177 @staticmethod 

178 def frame_file_is_setup(frame): 

179 """ 

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

181 """ 

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

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

184 

185 def spec_for_sensitive_tests(self): 

186 """ 

187 Ensure stdlib distutils when running select tests under CPython. 

188 

189 python/cpython#91169 

190 """ 

191 clear_distutils() 

192 self.spec_for_distutils = lambda: None 

193 

194 sensitive_tests = ( 

195 [ 

196 'test.test_distutils', 

197 'test.test_peg_generator', 

198 'test.test_importlib', 

199 ] 

200 if sys.version_info < (3, 10) 

201 else [ 

202 'test.test_distutils', 

203 ] 

204 ) 

205 

206 

207for name in DistutilsMetaFinder.sensitive_tests: 

208 setattr( 

209 DistutilsMetaFinder, 

210 f'spec_for_{name}', 

211 DistutilsMetaFinder.spec_for_sensitive_tests, 

212 ) 

213 

214 

215DISTUTILS_FINDER = DistutilsMetaFinder() 

216 

217 

218def add_shim(): 

219 DISTUTILS_FINDER in sys.meta_path or insert_shim() 

220 

221 

222class shim: 

223 def __enter__(self) -> None: 

224 insert_shim() 

225 

226 def __exit__(self, exc: object, value: object, tb: object) -> None: 

227 _remove_shim() 

228 

229 

230def insert_shim(): 

231 sys.meta_path.insert(0, DISTUTILS_FINDER) 

232 

233 

234def _remove_shim(): 

235 try: 

236 sys.meta_path.remove(DISTUTILS_FINDER) 

237 except ValueError: 

238 pass 

239 

240 

241if sys.version_info < (3, 12): 

242 # DistutilsMetaFinder can only be disabled in Python < 3.12 (PEP 632) 

243 remove_shim = _remove_shim