Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/setuptools/_distutils/sysconfig.py: 0%

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

242 statements  

1"""Provide access to Python's configuration information. The specific 

2configuration variables available depend heavily on the platform and 

3configuration. The values may be retrieved using 

4get_config_var(name), and the list of variables is available via 

5get_config_vars().keys(). Additional convenience functions are also 

6available. 

7 

8Written by: Fred L. Drake, Jr. 

9Email: <fdrake@acm.org> 

10""" 

11 

12import functools 

13import os 

14import pathlib 

15import re 

16import sys 

17import sysconfig 

18 

19from ._functools import pass_none 

20from .compat import py39 

21from .errors import DistutilsPlatformError 

22from .util import is_mingw 

23 

24IS_PYPY = '__pypy__' in sys.builtin_module_names 

25 

26# These are needed in a couple of spots, so just compute them once. 

27PREFIX = os.path.normpath(sys.prefix) 

28EXEC_PREFIX = os.path.normpath(sys.exec_prefix) 

29BASE_PREFIX = os.path.normpath(sys.base_prefix) 

30BASE_EXEC_PREFIX = os.path.normpath(sys.base_exec_prefix) 

31 

32# Path to the base directory of the project. On Windows the binary may 

33# live in project/PCbuild/win32 or project/PCbuild/amd64. 

34# set for cross builds 

35if "_PYTHON_PROJECT_BASE" in os.environ: 

36 project_base = os.path.abspath(os.environ["_PYTHON_PROJECT_BASE"]) 

37else: 

38 if sys.executable: 

39 project_base = os.path.dirname(os.path.abspath(sys.executable)) 

40 else: 

41 # sys.executable can be empty if argv[0] has been changed and Python is 

42 # unable to retrieve the real program name 

43 project_base = os.getcwd() 

44 

45 

46def _is_python_source_dir(d): 

47 """ 

48 Return True if the target directory appears to point to an 

49 un-installed Python. 

50 """ 

51 modules = pathlib.Path(d).joinpath('Modules') 

52 return any(modules.joinpath(fn).is_file() for fn in ('Setup', 'Setup.local')) 

53 

54 

55_sys_home = getattr(sys, '_home', None) 

56 

57 

58def _is_parent(dir_a, dir_b): 

59 """ 

60 Return True if a is a parent of b. 

61 """ 

62 return os.path.normcase(dir_a).startswith(os.path.normcase(dir_b)) 

63 

64 

65if os.name == 'nt': 

66 

67 @pass_none 

68 def _fix_pcbuild(d): 

69 # In a venv, sys._home will be inside BASE_PREFIX rather than PREFIX. 

70 prefixes = PREFIX, BASE_PREFIX 

71 matched = ( 

72 prefix 

73 for prefix in prefixes 

74 if _is_parent(d, os.path.join(prefix, "PCbuild")) 

75 ) 

76 return next(matched, d) 

77 

78 project_base = _fix_pcbuild(project_base) 

79 _sys_home = _fix_pcbuild(_sys_home) 

80 

81 

82def _python_build(): 

83 if _sys_home: 

84 return _is_python_source_dir(_sys_home) 

85 return _is_python_source_dir(project_base) 

86 

87 

88python_build = _python_build() 

89 

90 

91# Calculate the build qualifier flags if they are defined. Adding the flags 

92# to the include and lib directories only makes sense for an installation, not 

93# an in-source build. 

94build_flags = '' 

95try: 

96 if not python_build: 

97 build_flags = sys.abiflags 

98except AttributeError: 

99 # It's not a configure-based build, so the sys module doesn't have 

100 # this attribute, which is fine. 

101 pass 

102 

103 

104def get_python_version(): 

105 """Return a string containing the major and minor Python version, 

106 leaving off the patchlevel. Sample return values could be '1.5' 

107 or '2.2'. 

108 """ 

109 return '%d.%d' % sys.version_info[:2] 

110 

111 

112def get_python_inc(plat_specific=False, prefix=None): 

113 """Return the directory containing installed Python header files. 

114 

115 If 'plat_specific' is false (the default), this is the path to the 

116 non-platform-specific header files, i.e. Python.h and so on; 

117 otherwise, this is the path to platform-specific header files 

118 (namely pyconfig.h). 

119 

120 If 'prefix' is supplied, use it instead of sys.base_prefix or 

121 sys.base_exec_prefix -- i.e., ignore 'plat_specific'. 

122 """ 

123 default_prefix = BASE_EXEC_PREFIX if plat_specific else BASE_PREFIX 

124 resolved_prefix = prefix if prefix is not None else default_prefix 

125 # MinGW imitates posix like layout, but os.name != posix 

126 os_name = "posix" if is_mingw() else os.name 

127 try: 

128 getter = globals()[f'_get_python_inc_{os_name}'] 

129 except KeyError: 

130 raise DistutilsPlatformError( 

131 "I don't know where Python installs its C header files " 

132 f"on platform '{os.name}'" 

133 ) 

134 return getter(resolved_prefix, prefix, plat_specific) 

135 

136 

137@pass_none 

138def _extant(path): 

139 """ 

140 Replace path with None if it doesn't exist. 

141 """ 

142 return path if os.path.exists(path) else None 

143 

144 

145def _get_python_inc_posix(prefix, spec_prefix, plat_specific): 

146 if IS_PYPY and sys.version_info < (3, 8): 

147 return os.path.join(prefix, 'include') 

148 return ( 

149 _get_python_inc_posix_python(plat_specific) 

150 or _extant(_get_python_inc_from_config(plat_specific, spec_prefix)) 

151 or _get_python_inc_posix_prefix(prefix) 

152 ) 

153 

154 

155def _get_python_inc_posix_python(plat_specific): 

156 """ 

157 Assume the executable is in the build directory. The 

158 pyconfig.h file should be in the same directory. Since 

159 the build directory may not be the source directory, 

160 use "srcdir" from the makefile to find the "Include" 

161 directory. 

162 """ 

163 if not python_build: 

164 return 

165 if plat_specific: 

166 return _sys_home or project_base 

167 incdir = os.path.join(get_config_var('srcdir'), 'Include') 

168 return os.path.normpath(incdir) 

169 

170 

171def _get_python_inc_from_config(plat_specific, spec_prefix): 

172 """ 

173 If no prefix was explicitly specified, provide the include 

174 directory from the config vars. Useful when 

175 cross-compiling, since the config vars may come from 

176 the host 

177 platform Python installation, while the current Python 

178 executable is from the build platform installation. 

179 

180 >>> monkeypatch = getfixture('monkeypatch') 

181 >>> gpifc = _get_python_inc_from_config 

182 >>> monkeypatch.setitem(gpifc.__globals__, 'get_config_var', str.lower) 

183 >>> gpifc(False, '/usr/bin/') 

184 >>> gpifc(False, '') 

185 >>> gpifc(False, None) 

186 'includepy' 

187 >>> gpifc(True, None) 

188 'confincludepy' 

189 """ 

190 if spec_prefix is None: 

191 return get_config_var('CONF' * plat_specific + 'INCLUDEPY') 

192 

193 

194def _get_python_inc_posix_prefix(prefix): 

195 implementation = 'pypy' if IS_PYPY else 'python' 

196 python_dir = implementation + get_python_version() + build_flags 

197 return os.path.join(prefix, "include", python_dir) 

198 

199 

200def _get_python_inc_nt(prefix, spec_prefix, plat_specific): 

201 if python_build: 

202 # Include both include dirs to ensure we can find pyconfig.h 

203 return ( 

204 os.path.join(prefix, "include") 

205 + os.path.pathsep 

206 + os.path.dirname(sysconfig.get_config_h_filename()) 

207 ) 

208 return os.path.join(prefix, "include") 

209 

210 

211# allow this behavior to be monkey-patched. Ref pypa/distutils#2. 

212def _posix_lib(standard_lib, libpython, early_prefix, prefix): 

213 if standard_lib: 

214 return libpython 

215 else: 

216 return os.path.join(libpython, "site-packages") 

217 

218 

219def get_python_lib(plat_specific=False, standard_lib=False, prefix=None): 

220 """Return the directory containing the Python library (standard or 

221 site additions). 

222 

223 If 'plat_specific' is true, return the directory containing 

224 platform-specific modules, i.e. any module from a non-pure-Python 

225 module distribution; otherwise, return the platform-shared library 

226 directory. If 'standard_lib' is true, return the directory 

227 containing standard Python library modules; otherwise, return the 

228 directory for site-specific modules. 

229 

230 If 'prefix' is supplied, use it instead of sys.base_prefix or 

231 sys.base_exec_prefix -- i.e., ignore 'plat_specific'. 

232 """ 

233 

234 if IS_PYPY and sys.version_info < (3, 8): 

235 # PyPy-specific schema 

236 if prefix is None: 

237 prefix = PREFIX 

238 if standard_lib: 

239 return os.path.join(prefix, "lib-python", sys.version[0]) 

240 return os.path.join(prefix, 'site-packages') 

241 

242 early_prefix = prefix 

243 

244 if prefix is None: 

245 if standard_lib: 

246 prefix = plat_specific and BASE_EXEC_PREFIX or BASE_PREFIX 

247 else: 

248 prefix = plat_specific and EXEC_PREFIX or PREFIX 

249 

250 if os.name == "posix" or is_mingw(): 

251 if plat_specific or standard_lib: 

252 # Platform-specific modules (any module from a non-pure-Python 

253 # module distribution) or standard Python library modules. 

254 libdir = getattr(sys, "platlibdir", "lib") 

255 else: 

256 # Pure Python 

257 libdir = "lib" 

258 implementation = 'pypy' if IS_PYPY else 'python' 

259 libpython = os.path.join(prefix, libdir, implementation + get_python_version()) 

260 return _posix_lib(standard_lib, libpython, early_prefix, prefix) 

261 elif os.name == "nt": 

262 if standard_lib: 

263 return os.path.join(prefix, "Lib") 

264 else: 

265 return os.path.join(prefix, "Lib", "site-packages") 

266 else: 

267 raise DistutilsPlatformError( 

268 f"I don't know where Python installs its library on platform '{os.name}'" 

269 ) 

270 

271 

272@functools.lru_cache 

273def _customize_macos(): 

274 """ 

275 Perform first-time customization of compiler-related 

276 config vars on macOS. Use after a compiler is known 

277 to be needed. This customization exists primarily to support Pythons 

278 from binary installers. The kind and paths to build tools on 

279 the user system may vary significantly from the system 

280 that Python itself was built on. Also the user OS 

281 version and build tools may not support the same set 

282 of CPU architectures for universal builds. 

283 """ 

284 

285 sys.platform == "darwin" and __import__('_osx_support').customize_compiler( 

286 get_config_vars() 

287 ) 

288 

289 

290def customize_compiler(compiler): # noqa: C901 

291 """Do any platform-specific customization of a CCompiler instance. 

292 

293 Mainly needed on Unix, so we can plug in the information that 

294 varies across Unices and is stored in Python's Makefile. 

295 """ 

296 if compiler.compiler_type in ["unix", "cygwin"] or ( 

297 compiler.compiler_type == "mingw32" and is_mingw() 

298 ): 

299 _customize_macos() 

300 

301 ( 

302 cc, 

303 cxx, 

304 cflags, 

305 ccshared, 

306 ldshared, 

307 shlib_suffix, 

308 ar, 

309 ar_flags, 

310 ) = get_config_vars( 

311 'CC', 

312 'CXX', 

313 'CFLAGS', 

314 'CCSHARED', 

315 'LDSHARED', 

316 'SHLIB_SUFFIX', 

317 'AR', 

318 'ARFLAGS', 

319 ) 

320 

321 if 'CC' in os.environ: 

322 newcc = os.environ['CC'] 

323 if 'LDSHARED' not in os.environ and ldshared.startswith(cc): 

324 # If CC is overridden, use that as the default 

325 # command for LDSHARED as well 

326 ldshared = newcc + ldshared[len(cc) :] 

327 cc = newcc 

328 if 'CXX' in os.environ: 

329 cxx = os.environ['CXX'] 

330 if 'LDSHARED' in os.environ: 

331 ldshared = os.environ['LDSHARED'] 

332 if 'CPP' in os.environ: 

333 cpp = os.environ['CPP'] 

334 else: 

335 cpp = cc + " -E" # not always 

336 if 'LDFLAGS' in os.environ: 

337 ldshared = ldshared + ' ' + os.environ['LDFLAGS'] 

338 if 'CFLAGS' in os.environ: 

339 cflags = cflags + ' ' + os.environ['CFLAGS'] 

340 ldshared = ldshared + ' ' + os.environ['CFLAGS'] 

341 if 'CPPFLAGS' in os.environ: 

342 cpp = cpp + ' ' + os.environ['CPPFLAGS'] 

343 cflags = cflags + ' ' + os.environ['CPPFLAGS'] 

344 ldshared = ldshared + ' ' + os.environ['CPPFLAGS'] 

345 if 'AR' in os.environ: 

346 ar = os.environ['AR'] 

347 if 'ARFLAGS' in os.environ: 

348 archiver = ar + ' ' + os.environ['ARFLAGS'] 

349 else: 

350 archiver = ar + ' ' + ar_flags 

351 

352 cc_cmd = cc + ' ' + cflags 

353 compiler.set_executables( 

354 preprocessor=cpp, 

355 compiler=cc_cmd, 

356 compiler_so=cc_cmd + ' ' + ccshared, 

357 compiler_cxx=cxx, 

358 linker_so=ldshared, 

359 linker_exe=cc, 

360 archiver=archiver, 

361 ) 

362 

363 if 'RANLIB' in os.environ and compiler.executables.get('ranlib', None): 

364 compiler.set_executables(ranlib=os.environ['RANLIB']) 

365 

366 compiler.shared_lib_extension = shlib_suffix 

367 

368 

369def get_config_h_filename(): 

370 """Return full pathname of installed pyconfig.h file.""" 

371 return sysconfig.get_config_h_filename() 

372 

373 

374def get_makefile_filename(): 

375 """Return full pathname of installed Makefile from the Python build.""" 

376 return sysconfig.get_makefile_filename() 

377 

378 

379def parse_config_h(fp, g=None): 

380 """Parse a config.h-style file. 

381 

382 A dictionary containing name/value pairs is returned. If an 

383 optional dictionary is passed in as the second argument, it is 

384 used instead of a new dictionary. 

385 """ 

386 return sysconfig.parse_config_h(fp, vars=g) 

387 

388 

389# Regexes needed for parsing Makefile (and similar syntaxes, 

390# like old-style Setup files). 

391_variable_rx = re.compile(r"([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)") 

392_findvar1_rx = re.compile(r"\$\(([A-Za-z][A-Za-z0-9_]*)\)") 

393_findvar2_rx = re.compile(r"\${([A-Za-z][A-Za-z0-9_]*)}") 

394 

395 

396def parse_makefile(fn, g=None): # noqa: C901 

397 """Parse a Makefile-style file. 

398 

399 A dictionary containing name/value pairs is returned. If an 

400 optional dictionary is passed in as the second argument, it is 

401 used instead of a new dictionary. 

402 """ 

403 from distutils.text_file import TextFile 

404 

405 fp = TextFile( 

406 fn, 

407 strip_comments=True, 

408 skip_blanks=True, 

409 join_lines=True, 

410 errors="surrogateescape", 

411 ) 

412 

413 if g is None: 

414 g = {} 

415 done = {} 

416 notdone = {} 

417 

418 while True: 

419 line = fp.readline() 

420 if line is None: # eof 

421 break 

422 m = _variable_rx.match(line) 

423 if m: 

424 n, v = m.group(1, 2) 

425 v = v.strip() 

426 # `$$' is a literal `$' in make 

427 tmpv = v.replace('$$', '') 

428 

429 if "$" in tmpv: 

430 notdone[n] = v 

431 else: 

432 try: 

433 v = int(v) 

434 except ValueError: 

435 # insert literal `$' 

436 done[n] = v.replace('$$', '$') 

437 else: 

438 done[n] = v 

439 

440 # Variables with a 'PY_' prefix in the makefile. These need to 

441 # be made available without that prefix through sysconfig. 

442 # Special care is needed to ensure that variable expansion works, even 

443 # if the expansion uses the name without a prefix. 

444 renamed_variables = ('CFLAGS', 'LDFLAGS', 'CPPFLAGS') 

445 

446 # do variable interpolation here 

447 while notdone: 

448 for name in list(notdone): 

449 value = notdone[name] 

450 m = _findvar1_rx.search(value) or _findvar2_rx.search(value) 

451 if m: 

452 n = m.group(1) 

453 found = True 

454 if n in done: 

455 item = str(done[n]) 

456 elif n in notdone: 

457 # get it on a subsequent round 

458 found = False 

459 elif n in os.environ: 

460 # do it like make: fall back to environment 

461 item = os.environ[n] 

462 

463 elif n in renamed_variables: 

464 if name.startswith('PY_') and name[3:] in renamed_variables: 

465 item = "" 

466 

467 elif 'PY_' + n in notdone: 

468 found = False 

469 

470 else: 

471 item = str(done['PY_' + n]) 

472 else: 

473 done[n] = item = "" 

474 if found: 

475 after = value[m.end() :] 

476 value = value[: m.start()] + item + after 

477 if "$" in after: 

478 notdone[name] = value 

479 else: 

480 try: 

481 value = int(value) 

482 except ValueError: 

483 done[name] = value.strip() 

484 else: 

485 done[name] = value 

486 del notdone[name] 

487 

488 if name.startswith('PY_') and name[3:] in renamed_variables: 

489 name = name[3:] 

490 if name not in done: 

491 done[name] = value 

492 else: 

493 # bogus variable reference; just drop it since we can't deal 

494 del notdone[name] 

495 

496 fp.close() 

497 

498 # strip spurious spaces 

499 for k, v in done.items(): 

500 if isinstance(v, str): 

501 done[k] = v.strip() 

502 

503 # save the results in the global dictionary 

504 g.update(done) 

505 return g 

506 

507 

508def expand_makefile_vars(s, vars): 

509 """Expand Makefile-style variables -- "${foo}" or "$(foo)" -- in 

510 'string' according to 'vars' (a dictionary mapping variable names to 

511 values). Variables not present in 'vars' are silently expanded to the 

512 empty string. The variable values in 'vars' should not contain further 

513 variable expansions; if 'vars' is the output of 'parse_makefile()', 

514 you're fine. Returns a variable-expanded version of 's'. 

515 """ 

516 

517 # This algorithm does multiple expansion, so if vars['foo'] contains 

518 # "${bar}", it will expand ${foo} to ${bar}, and then expand 

519 # ${bar}... and so forth. This is fine as long as 'vars' comes from 

520 # 'parse_makefile()', which takes care of such expansions eagerly, 

521 # according to make's variable expansion semantics. 

522 

523 while True: 

524 m = _findvar1_rx.search(s) or _findvar2_rx.search(s) 

525 if m: 

526 (beg, end) = m.span() 

527 s = s[0:beg] + vars.get(m.group(1)) + s[end:] 

528 else: 

529 break 

530 return s 

531 

532 

533_config_vars = None 

534 

535 

536def get_config_vars(*args): 

537 """With no arguments, return a dictionary of all configuration 

538 variables relevant for the current platform. Generally this includes 

539 everything needed to build extensions and install both pure modules and 

540 extensions. On Unix, this means every variable defined in Python's 

541 installed Makefile; on Windows it's a much smaller set. 

542 

543 With arguments, return a list of values that result from looking up 

544 each argument in the configuration variable dictionary. 

545 """ 

546 global _config_vars 

547 if _config_vars is None: 

548 _config_vars = sysconfig.get_config_vars().copy() 

549 py39.add_ext_suffix(_config_vars) 

550 

551 return [_config_vars.get(name) for name in args] if args else _config_vars 

552 

553 

554def get_config_var(name): 

555 """Return the value of a single variable using the dictionary 

556 returned by 'get_config_vars()'. Equivalent to 

557 get_config_vars().get(name) 

558 """ 

559 if name == 'SO': 

560 import warnings 

561 

562 warnings.warn('SO is deprecated, use EXT_SUFFIX', DeprecationWarning, 2) 

563 return get_config_vars().get(name)