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

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

102 statements  

1# don't import any costly modules 

2import sys 

3import os 

4 

5 

6report_url = ( 

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

8 "template=distutils-deprecation.yml" 

9) 

10 

11 

12def warn_distutils_present(): 

13 if 'distutils' not in sys.modules: 

14 return 

15 import warnings 

16 

17 warnings.warn( 

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

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

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

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

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

23 "that setuptools is always imported before distutils." 

24 ) 

25 

26 

27def clear_distutils(): 

28 if 'distutils' not in sys.modules: 

29 return 

30 import warnings 

31 

32 warnings.warn( 

33 "Setuptools is replacing distutils. Support for replacing " 

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

35 "this condition will fail. " 

36 f"Register concerns at {report_url}" 

37 ) 

38 mods = [ 

39 name 

40 for name in sys.modules 

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

42 ] 

43 for name in mods: 

44 del sys.modules[name] 

45 

46 

47def enabled(): 

48 """ 

49 Allow selection of distutils by environment variable. 

50 """ 

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

52 if which == 'stdlib': 

53 import warnings 

54 

55 warnings.warn( 

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

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

58 "Avoid importing distutils or import setuptools first, " 

59 "and avoid setting SETUPTOOLS_USE_DISTUTILS=stdlib. " 

60 f"Register concerns at {report_url}" 

61 ) 

62 return which == 'local' 

63 

64 

65def ensure_local_distutils(): 

66 import importlib 

67 

68 clear_distutils() 

69 

70 # With the DistutilsMetaFinder in place, 

71 # perform an import to cause distutils to be 

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

73 with shim(): 

74 importlib.import_module('distutils') 

75 

76 # check that submodules load as expected 

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

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

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

80 

81 

82def do_override(): 

83 """ 

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

85 

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

87 for more motivation. 

88 """ 

89 if enabled(): 

90 warn_distutils_present() 

91 ensure_local_distutils() 

92 

93 

94class _TrivialRe: 

95 def __init__(self, *patterns): 

96 self._patterns = patterns 

97 

98 def match(self, string): 

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

100 

101 

102class DistutilsMetaFinder: 

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

104 # optimization: only consider top level modules and those 

105 # found in the CPython test suite. 

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

107 return None 

108 

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

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

111 return method() 

112 

113 def spec_for_distutils(self): 

114 if self.is_cpython(): 

115 return None 

116 

117 import importlib 

118 import importlib.abc 

119 import importlib.util 

120 

121 try: 

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

123 except Exception: 

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

125 # may not be present: 

126 # - An older Setuptools without a local distutils is 

127 # taking precedence. Ref #2957. 

128 # - Path manipulation during sitecustomize removes 

129 # setuptools from the path but only after the hook 

130 # has been loaded. Ref #2980. 

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

132 return None 

133 

134 class DistutilsLoader(importlib.abc.Loader): 

135 def create_module(self, spec): 

136 mod.__name__ = 'distutils' 

137 return mod 

138 

139 def exec_module(self, module): 

140 pass 

141 

142 return importlib.util.spec_from_loader( 

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

144 ) 

145 

146 @staticmethod 

147 def is_cpython(): 

148 """ 

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

150 Ref #2965 and #3007. 

151 """ 

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

153 

154 def spec_for_pip(self): 

155 """ 

156 Ensure stdlib distutils when running under pip. 

157 See pypa/pip#8761 for rationale. 

158 """ 

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

160 return 

161 clear_distutils() 

162 self.spec_for_distutils = lambda: None 

163 

164 @classmethod 

165 def pip_imported_during_build(cls): 

166 """ 

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

168 """ 

169 import traceback 

170 

171 return any( 

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

173 ) 

174 

175 @staticmethod 

176 def frame_file_is_setup(frame): 

177 """ 

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

179 """ 

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

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

182 

183 def spec_for_sensitive_tests(self): 

184 """ 

185 Ensure stdlib distutils when running select tests under CPython. 

186 

187 python/cpython#91169 

188 """ 

189 clear_distutils() 

190 self.spec_for_distutils = lambda: None 

191 

192 sensitive_tests = ( 

193 [ 

194 'test.test_distutils', 

195 'test.test_peg_generator', 

196 'test.test_importlib', 

197 ] 

198 if sys.version_info < (3, 10) 

199 else [ 

200 'test.test_distutils', 

201 ] 

202 ) 

203 

204 

205for name in DistutilsMetaFinder.sensitive_tests: 

206 setattr( 

207 DistutilsMetaFinder, 

208 f'spec_for_{name}', 

209 DistutilsMetaFinder.spec_for_sensitive_tests, 

210 ) 

211 

212 

213DISTUTILS_FINDER = DistutilsMetaFinder() 

214 

215 

216def add_shim(): 

217 DISTUTILS_FINDER in sys.meta_path or insert_shim() 

218 

219 

220class shim: 

221 def __enter__(self): 

222 insert_shim() 

223 

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

225 _remove_shim() 

226 

227 

228def insert_shim(): 

229 sys.meta_path.insert(0, DISTUTILS_FINDER) 

230 

231 

232def _remove_shim(): 

233 try: 

234 sys.meta_path.remove(DISTUTILS_FINDER) 

235 except ValueError: 

236 pass 

237 

238 

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

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

241 remove_shim = _remove_shim