blob: 9442d39f5f859171a3f2872379d4f8bcc770dfdd [file] [log] [blame]
Tarek Ziadé5633a802010-01-23 09:23:15 +00001"""Provide access to Python's configuration information.
2
3"""
4import sys
5import os
Florent Xicluna85677612010-03-10 23:58:42 +00006from os.path import pardir, realpath
Tarek Ziadé5633a802010-01-23 09:23:15 +00007
8_INSTALL_SCHEMES = {
9 'posix_prefix': {
10 'stdlib': '{base}/lib/python{py_version_short}',
11 'platstdlib': '{platbase}/lib/python{py_version_short}',
12 'purelib': '{base}/lib/python{py_version_short}/site-packages',
13 'platlib': '{platbase}/lib/python{py_version_short}/site-packages',
14 'include': '{base}/include/python{py_version_short}',
15 'platinclude': '{platbase}/include/python{py_version_short}',
16 'scripts': '{base}/bin',
17 'data': '{base}',
18 },
19 'posix_home': {
20 'stdlib': '{base}/lib/python',
21 'platstdlib': '{base}/lib/python',
22 'purelib': '{base}/lib/python',
23 'platlib': '{base}/lib/python',
24 'include': '{base}/include/python',
25 'platinclude': '{base}/include/python',
26 'scripts': '{base}/bin',
27 'data' : '{base}',
28 },
29 'nt': {
30 'stdlib': '{base}/Lib',
31 'platstdlib': '{base}/Lib',
32 'purelib': '{base}/Lib/site-packages',
33 'platlib': '{base}/Lib/site-packages',
34 'include': '{base}/Include',
35 'platinclude': '{base}/Include',
36 'scripts': '{base}/Scripts',
37 'data' : '{base}',
38 },
39 'os2': {
40 'stdlib': '{base}/Lib',
41 'platstdlib': '{base}/Lib',
42 'purelib': '{base}/Lib/site-packages',
43 'platlib': '{base}/Lib/site-packages',
44 'include': '{base}/Include',
45 'platinclude': '{base}/Include',
46 'scripts': '{base}/Scripts',
47 'data' : '{base}',
48 },
49 'os2_home': {
Tarek Ziadé8f692272010-05-19 22:20:14 +000050 'stdlib': '{userbase}/lib/python{py_version_short}',
51 'platstdlib': '{userbase}/lib/python{py_version_short}',
52 'purelib': '{userbase}/lib/python{py_version_short}/site-packages',
53 'platlib': '{userbase}/lib/python{py_version_short}/site-packages',
Tarek Ziadé5633a802010-01-23 09:23:15 +000054 'include': '{userbase}/include/python{py_version_short}',
55 'scripts': '{userbase}/bin',
56 'data' : '{userbase}',
57 },
58 'nt_user': {
59 'stdlib': '{userbase}/Python{py_version_nodot}',
60 'platstdlib': '{userbase}/Python{py_version_nodot}',
61 'purelib': '{userbase}/Python{py_version_nodot}/site-packages',
62 'platlib': '{userbase}/Python{py_version_nodot}/site-packages',
63 'include': '{userbase}/Python{py_version_nodot}/Include',
64 'scripts': '{userbase}/Scripts',
65 'data' : '{userbase}',
66 },
67 'posix_user': {
Tarek Ziadé8f692272010-05-19 22:20:14 +000068 'stdlib': '{userbase}/lib/python{py_version_short}',
69 'platstdlib': '{userbase}/lib/python{py_version_short}',
70 'purelib': '{userbase}/lib/python{py_version_short}/site-packages',
71 'platlib': '{userbase}/lib/python{py_version_short}/site-packages',
Tarek Ziadé5633a802010-01-23 09:23:15 +000072 'include': '{userbase}/include/python{py_version_short}',
73 'scripts': '{userbase}/bin',
74 'data' : '{userbase}',
75 },
Ronald Oussoren2f88bfd2010-05-08 10:29:06 +000076 'osx_framework_user': {
77 'stdlib': '{userbase}/lib/python',
78 'platstdlib': '{userbase}/lib/python',
79 'purelib': '{userbase}/lib/python/site-packages',
80 'platlib': '{userbase}/lib/python/site-packages',
81 'include': '{userbase}/include',
82 'scripts': '{userbase}/bin',
83 'data' : '{userbase}',
84 },
Tarek Ziadé5633a802010-01-23 09:23:15 +000085 }
86
87_SCHEME_KEYS = ('stdlib', 'platstdlib', 'purelib', 'platlib', 'include',
88 'scripts', 'data')
89_PY_VERSION = sys.version.split()[0]
90_PY_VERSION_SHORT = sys.version[:3]
91_PY_VERSION_SHORT_NO_DOT = _PY_VERSION[0] + _PY_VERSION[2]
92_PREFIX = os.path.normpath(sys.prefix)
93_EXEC_PREFIX = os.path.normpath(sys.exec_prefix)
94_CONFIG_VARS = None
95_USER_BASE = None
Victor Stinnerd2f6ae62010-10-12 22:53:51 +000096
97def _safe_realpath(path):
98 try:
99 return realpath(path)
100 except OSError:
101 return path
102
Victor Stinner4a7e0c852010-03-11 12:34:39 +0000103if sys.executable:
Victor Stinnerd2f6ae62010-10-12 22:53:51 +0000104 _PROJECT_BASE = os.path.dirname(_safe_realpath(sys.executable))
Victor Stinner4a7e0c852010-03-11 12:34:39 +0000105else:
106 # sys.executable can be empty if argv[0] has been changed and Python is
107 # unable to retrieve the real program name
Victor Stinnerd2f6ae62010-10-12 22:53:51 +0000108 _PROJECT_BASE = _safe_realpath(os.getcwd())
Tarek Ziadé5633a802010-01-23 09:23:15 +0000109
110if os.name == "nt" and "pcbuild" in _PROJECT_BASE[-8:].lower():
Victor Stinnerd2f6ae62010-10-12 22:53:51 +0000111 _PROJECT_BASE = _safe_realpath(os.path.join(_PROJECT_BASE, pardir))
Tarek Ziadé5633a802010-01-23 09:23:15 +0000112# PC/VS7.1
113if os.name == "nt" and "\\pc\\v" in _PROJECT_BASE[-10:].lower():
Victor Stinnerd2f6ae62010-10-12 22:53:51 +0000114 _PROJECT_BASE = _safe_realpath(os.path.join(_PROJECT_BASE, pardir, pardir))
Tarek Ziadé5633a802010-01-23 09:23:15 +0000115# PC/AMD64
116if os.name == "nt" and "\\pcbuild\\amd64" in _PROJECT_BASE[-14:].lower():
Victor Stinnerd2f6ae62010-10-12 22:53:51 +0000117 _PROJECT_BASE = _safe_realpath(os.path.join(_PROJECT_BASE, pardir, pardir))
Tarek Ziadé5633a802010-01-23 09:23:15 +0000118
119def is_python_build():
120 for fn in ("Setup.dist", "Setup.local"):
121 if os.path.isfile(os.path.join(_PROJECT_BASE, "Modules", fn)):
122 return True
123 return False
124
125_PYTHON_BUILD = is_python_build()
126
127if _PYTHON_BUILD:
128 for scheme in ('posix_prefix', 'posix_home'):
129 _INSTALL_SCHEMES[scheme]['include'] = '{projectbase}/Include'
130 _INSTALL_SCHEMES[scheme]['platinclude'] = '{srcdir}'
131
132def _subst_vars(s, local_vars):
133 try:
134 return s.format(**local_vars)
135 except KeyError:
136 try:
137 return s.format(**os.environ)
138 except KeyError, var:
139 raise AttributeError('{%s}' % var)
140
141def _extend_dict(target_dict, other_dict):
142 target_keys = target_dict.keys()
143 for key, value in other_dict.items():
144 if key in target_keys:
145 continue
146 target_dict[key] = value
147
148def _expand_vars(scheme, vars):
149 res = {}
150 if vars is None:
151 vars = {}
152 _extend_dict(vars, get_config_vars())
153
154 for key, value in _INSTALL_SCHEMES[scheme].items():
155 if os.name in ('posix', 'nt'):
156 value = os.path.expanduser(value)
157 res[key] = os.path.normpath(_subst_vars(value, vars))
158 return res
159
160def _get_default_scheme():
161 if os.name == 'posix':
162 # the default scheme for posix is posix_prefix
163 return 'posix_prefix'
164 return os.name
165
166def _getuserbase():
167 env_base = os.environ.get("PYTHONUSERBASE", None)
168 def joinuser(*args):
169 return os.path.expanduser(os.path.join(*args))
170
171 # what about 'os2emx', 'riscos' ?
172 if os.name == "nt":
173 base = os.environ.get("APPDATA") or "~"
174 return env_base if env_base else joinuser(base, "Python")
175
Ronald Oussoren2f88bfd2010-05-08 10:29:06 +0000176 if sys.platform == "darwin":
177 framework = get_config_var("PYTHONFRAMEWORK")
178 if framework:
Ned Deily2c8bf042012-02-06 00:55:50 +0100179 return env_base if env_base else \
180 joinuser("~", "Library", framework, "%d.%d"
181 % (sys.version_info[:2]))
Ronald Oussoren2f88bfd2010-05-08 10:29:06 +0000182
Tarek Ziadé5633a802010-01-23 09:23:15 +0000183 return env_base if env_base else joinuser("~", ".local")
184
185
186def _parse_makefile(filename, vars=None):
187 """Parse a Makefile-style file.
188
189 A dictionary containing name/value pairs is returned. If an
190 optional dictionary is passed in as the second argument, it is
191 used instead of a new dictionary.
192 """
193 import re
194 # Regexes needed for parsing Makefile (and similar syntaxes,
195 # like old-style Setup files).
196 _variable_rx = re.compile("([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)")
197 _findvar1_rx = re.compile(r"\$\(([A-Za-z][A-Za-z0-9_]*)\)")
198 _findvar2_rx = re.compile(r"\${([A-Za-z][A-Za-z0-9_]*)}")
199
200 if vars is None:
201 vars = {}
202 done = {}
203 notdone = {}
204
205 with open(filename) as f:
206 lines = f.readlines()
207
208 for line in lines:
209 if line.startswith('#') or line.strip() == '':
210 continue
211 m = _variable_rx.match(line)
212 if m:
213 n, v = m.group(1, 2)
214 v = v.strip()
215 # `$$' is a literal `$' in make
216 tmpv = v.replace('$$', '')
217
218 if "$" in tmpv:
219 notdone[n] = v
220 else:
221 try:
222 v = int(v)
223 except ValueError:
224 # insert literal `$'
225 done[n] = v.replace('$$', '$')
226 else:
227 done[n] = v
228
229 # do variable interpolation here
230 while notdone:
231 for name in notdone.keys():
232 value = notdone[name]
233 m = _findvar1_rx.search(value) or _findvar2_rx.search(value)
234 if m:
235 n = m.group(1)
236 found = True
237 if n in done:
238 item = str(done[n])
239 elif n in notdone:
240 # get it on a subsequent round
241 found = False
242 elif n in os.environ:
243 # do it like make: fall back to environment
244 item = os.environ[n]
245 else:
246 done[n] = item = ""
247 if found:
248 after = value[m.end():]
249 value = value[:m.start()] + item + after
250 if "$" in after:
251 notdone[name] = value
252 else:
253 try: value = int(value)
254 except ValueError:
255 done[name] = value.strip()
256 else:
257 done[name] = value
258 del notdone[name]
259 else:
260 # bogus variable reference; just drop it since we can't deal
261 del notdone[name]
Antoine Pitrou58dab672010-10-10 09:54:59 +0000262 # strip spurious spaces
263 for k, v in done.items():
264 if isinstance(v, str):
265 done[k] = v.strip()
266
Tarek Ziadé5633a802010-01-23 09:23:15 +0000267 # save the results in the global dictionary
268 vars.update(done)
269 return vars
270
Tarek Ziadé5633a802010-01-23 09:23:15 +0000271
272def _get_makefile_filename():
273 if _PYTHON_BUILD:
274 return os.path.join(_PROJECT_BASE, "Makefile")
Barry Warsaw42bb7ca2011-02-14 20:04:00 +0000275 return os.path.join(get_path('platstdlib'), "config", "Makefile")
Tarek Ziadé5633a802010-01-23 09:23:15 +0000276
Tarek Ziadé5633a802010-01-23 09:23:15 +0000277
278def _init_posix(vars):
279 """Initialize the module as appropriate for POSIX systems."""
280 # load the installed Makefile:
281 makefile = _get_makefile_filename()
282 try:
283 _parse_makefile(makefile, vars)
284 except IOError, e:
285 msg = "invalid Python installation: unable to open %s" % makefile
286 if hasattr(e, "strerror"):
287 msg = msg + " (%s)" % e.strerror
288 raise IOError(msg)
289
290 # load the installed pyconfig.h:
291 config_h = get_config_h_filename()
292 try:
Antoine Pitrouf7c24452010-10-14 21:22:52 +0000293 with open(config_h) as f:
294 parse_config_h(f, vars)
Tarek Ziadé5633a802010-01-23 09:23:15 +0000295 except IOError, e:
296 msg = "invalid Python installation: unable to open %s" % config_h
297 if hasattr(e, "strerror"):
298 msg = msg + " (%s)" % e.strerror
299 raise IOError(msg)
300
Tarek Ziadé5633a802010-01-23 09:23:15 +0000301 # On AIX, there are wrong paths to the linker scripts in the Makefile
302 # -- these paths are relative to the Python source, but when installed
303 # the scripts are in another directory.
304 if _PYTHON_BUILD:
305 vars['LDSHARED'] = vars['BLDSHARED']
306
307def _init_non_posix(vars):
308 """Initialize the module as appropriate for NT"""
309 # set basic install directories
310 vars['LIBDEST'] = get_path('stdlib')
311 vars['BINLIBDEST'] = get_path('platstdlib')
312 vars['INCLUDEPY'] = get_path('include')
313 vars['SO'] = '.pyd'
314 vars['EXE'] = '.exe'
315 vars['VERSION'] = _PY_VERSION_SHORT_NO_DOT
Victor Stinnerd2f6ae62010-10-12 22:53:51 +0000316 vars['BINDIR'] = os.path.dirname(_safe_realpath(sys.executable))
Tarek Ziadé5633a802010-01-23 09:23:15 +0000317
318#
319# public APIs
320#
321
Tarek Ziadécc118172010-02-02 22:50:23 +0000322
323def parse_config_h(fp, vars=None):
324 """Parse a config.h-style file.
325
326 A dictionary containing name/value pairs is returned. If an
327 optional dictionary is passed in as the second argument, it is
328 used instead of a new dictionary.
329 """
330 import re
331 if vars is None:
332 vars = {}
333 define_rx = re.compile("#define ([A-Z][A-Za-z0-9_]+) (.*)\n")
334 undef_rx = re.compile("/[*] #undef ([A-Z][A-Za-z0-9_]+) [*]/\n")
335
336 while True:
337 line = fp.readline()
338 if not line:
339 break
340 m = define_rx.match(line)
341 if m:
342 n, v = m.group(1, 2)
343 try: v = int(v)
344 except ValueError: pass
345 vars[n] = v
346 else:
347 m = undef_rx.match(line)
348 if m:
349 vars[m.group(1)] = 0
350 return vars
351
352def get_config_h_filename():
353 """Returns the path of pyconfig.h."""
354 if _PYTHON_BUILD:
355 if os.name == "nt":
356 inc_dir = os.path.join(_PROJECT_BASE, "PC")
357 else:
358 inc_dir = _PROJECT_BASE
359 else:
360 inc_dir = get_path('platinclude')
361 return os.path.join(inc_dir, 'pyconfig.h')
362
Tarek Ziadé5633a802010-01-23 09:23:15 +0000363def get_scheme_names():
Tarek Ziadécc118172010-02-02 22:50:23 +0000364 """Returns a tuple containing the schemes names."""
Tarek Ziadée81b0282010-02-02 22:54:28 +0000365 schemes = _INSTALL_SCHEMES.keys()
366 schemes.sort()
367 return tuple(schemes)
Tarek Ziadé5633a802010-01-23 09:23:15 +0000368
369def get_path_names():
Tarek Ziadécc118172010-02-02 22:50:23 +0000370 """Returns a tuple containing the paths names."""
Tarek Ziadé5633a802010-01-23 09:23:15 +0000371 return _SCHEME_KEYS
372
373def get_paths(scheme=_get_default_scheme(), vars=None, expand=True):
374 """Returns a mapping containing an install scheme.
375
376 ``scheme`` is the install scheme name. If not provided, it will
377 return the default scheme for the current platform.
378 """
379 if expand:
380 return _expand_vars(scheme, vars)
381 else:
382 return _INSTALL_SCHEMES[scheme]
383
384def get_path(name, scheme=_get_default_scheme(), vars=None, expand=True):
385 """Returns a path corresponding to the scheme.
386
387 ``scheme`` is the install scheme name.
388 """
389 return get_paths(scheme, vars, expand)[name]
390
391def get_config_vars(*args):
392 """With no arguments, return a dictionary of all configuration
393 variables relevant for the current platform.
394
395 On Unix, this means every variable defined in Python's installed Makefile;
396 On Windows and Mac OS it's a much smaller set.
397
398 With arguments, return a list of values that result from looking up
399 each argument in the configuration variable dictionary.
400 """
401 import re
402 global _CONFIG_VARS
403 if _CONFIG_VARS is None:
404 _CONFIG_VARS = {}
405 # Normalized versions of prefix and exec_prefix are handy to have;
406 # in fact, these are the standard versions used most places in the
407 # Distutils.
408 _CONFIG_VARS['prefix'] = _PREFIX
409 _CONFIG_VARS['exec_prefix'] = _EXEC_PREFIX
410 _CONFIG_VARS['py_version'] = _PY_VERSION
411 _CONFIG_VARS['py_version_short'] = _PY_VERSION_SHORT
412 _CONFIG_VARS['py_version_nodot'] = _PY_VERSION[0] + _PY_VERSION[2]
413 _CONFIG_VARS['base'] = _PREFIX
414 _CONFIG_VARS['platbase'] = _EXEC_PREFIX
Tarek Ziadé5633a802010-01-23 09:23:15 +0000415 _CONFIG_VARS['projectbase'] = _PROJECT_BASE
416
417 if os.name in ('nt', 'os2'):
418 _init_non_posix(_CONFIG_VARS)
419 if os.name == 'posix':
420 _init_posix(_CONFIG_VARS)
421
Ronald Oussoren2f88bfd2010-05-08 10:29:06 +0000422 # Setting 'userbase' is done below the call to the
423 # init function to enable using 'get_config_var' in
424 # the init-function.
425 _CONFIG_VARS['userbase'] = _getuserbase()
426
Tarek Ziadé5633a802010-01-23 09:23:15 +0000427 if 'srcdir' not in _CONFIG_VARS:
428 _CONFIG_VARS['srcdir'] = _PROJECT_BASE
429
430 # Convert srcdir into an absolute path if it appears necessary.
431 # Normally it is relative to the build directory. However, during
432 # testing, for example, we might be running a non-installed python
433 # from a different directory.
434 if _PYTHON_BUILD and os.name == "posix":
435 base = _PROJECT_BASE
Victor Stinnerd2f6ae62010-10-12 22:53:51 +0000436 try:
437 cwd = os.getcwd()
438 except OSError:
439 cwd = None
Tarek Ziadé5633a802010-01-23 09:23:15 +0000440 if (not os.path.isabs(_CONFIG_VARS['srcdir']) and
Victor Stinnerd2f6ae62010-10-12 22:53:51 +0000441 base != cwd):
Tarek Ziadé5633a802010-01-23 09:23:15 +0000442 # srcdir is relative and we are not in the same directory
443 # as the executable. Assume executable is in the build
444 # directory and make srcdir absolute.
445 srcdir = os.path.join(base, _CONFIG_VARS['srcdir'])
446 _CONFIG_VARS['srcdir'] = os.path.normpath(srcdir)
447
Ned Deily18fae3f2013-01-31 01:24:55 -0800448 # OS X platforms require special customization to handle
449 # multi-architecture, multi-os-version installers
Tarek Ziadé5633a802010-01-23 09:23:15 +0000450 if sys.platform == 'darwin':
Ned Deily18fae3f2013-01-31 01:24:55 -0800451 import _osx_support
452 _osx_support.customize_config_vars(_CONFIG_VARS)
Tarek Ziadé5633a802010-01-23 09:23:15 +0000453
454 if args:
455 vals = []
456 for name in args:
457 vals.append(_CONFIG_VARS.get(name))
458 return vals
459 else:
460 return _CONFIG_VARS
461
462def get_config_var(name):
463 """Return the value of a single variable using the dictionary returned by
464 'get_config_vars()'.
465
466 Equivalent to get_config_vars().get(name)
467 """
468 return get_config_vars().get(name)
469
470def get_platform():
471 """Return a string that identifies the current platform.
472
473 This is used mainly to distinguish platform-specific build directories and
474 platform-specific built distributions. Typically includes the OS name
475 and version and the architecture (as supplied by 'os.uname()'),
476 although the exact information included depends on the OS; eg. for IRIX
477 the architecture isn't particularly important (IRIX only runs on SGI
478 hardware), but for Linux the kernel version isn't particularly
479 important.
480
481 Examples of returned values:
482 linux-i586
483 linux-alpha (?)
484 solaris-2.6-sun4u
485 irix-5.3
486 irix64-6.2
487
488 Windows will return one of:
489 win-amd64 (64bit Windows on AMD64 (aka x86_64, Intel64, EM64T, etc)
490 win-ia64 (64bit Windows on Itanium)
491 win32 (all others - specifically, sys.platform is returned)
492
493 For other non-POSIX platforms, currently just returns 'sys.platform'.
494 """
495 import re
496 if os.name == 'nt':
497 # sniff sys.version for architecture.
498 prefix = " bit ("
499 i = sys.version.find(prefix)
500 if i == -1:
501 return sys.platform
502 j = sys.version.find(")", i)
503 look = sys.version[i+len(prefix):j].lower()
504 if look == 'amd64':
505 return 'win-amd64'
506 if look == 'itanium':
507 return 'win-ia64'
508 return sys.platform
509
510 if os.name != "posix" or not hasattr(os, 'uname'):
511 # XXX what about the architecture? NT is Intel or Alpha,
512 # Mac OS is M68k or PPC, etc.
513 return sys.platform
514
515 # Try to distinguish various flavours of Unix
516 osname, host, release, version, machine = os.uname()
517
518 # Convert the OS name to lowercase, remove '/' characters
519 # (to accommodate BSD/OS), and translate spaces (for "Power Macintosh")
520 osname = osname.lower().replace('/', '')
521 machine = machine.replace(' ', '_')
522 machine = machine.replace('/', '-')
523
524 if osname[:5] == "linux":
525 # At least on Linux/Intel, 'machine' is the processor --
526 # i386, etc.
527 # XXX what about Alpha, SPARC, etc?
528 return "%s-%s" % (osname, machine)
529 elif osname[:5] == "sunos":
530 if release[0] >= "5": # SunOS 5 == Solaris 2
531 osname = "solaris"
532 release = "%d.%s" % (int(release[0]) - 3, release[2:])
Jesus Ceaa8f75da2012-01-18 04:43:50 +0100533 # We can't use "platform.architecture()[0]" because a
534 # bootstrap problem. We use a dict to get an error
535 # if some suspicious happens.
536 bitness = {2147483647:"32bit", 9223372036854775807:"64bit"}
537 machine += ".%s" % bitness[sys.maxint]
Tarek Ziadé5633a802010-01-23 09:23:15 +0000538 # fall through to standard osname-release-machine representation
539 elif osname[:4] == "irix": # could be "irix64"!
540 return "%s-%s" % (osname, release)
541 elif osname[:3] == "aix":
542 return "%s-%s.%s" % (osname, version, release)
543 elif osname[:6] == "cygwin":
544 osname = "cygwin"
545 rel_re = re.compile (r'[\d.]+')
546 m = rel_re.match(release)
547 if m:
548 release = m.group()
549 elif osname[:6] == "darwin":
Ned Deily18fae3f2013-01-31 01:24:55 -0800550 import _osx_support
551 osname, release, machine = _osx_support.get_platform_osx(
552 get_config_vars(),
553 osname, release, machine)
Tarek Ziadé5633a802010-01-23 09:23:15 +0000554
555 return "%s-%s-%s" % (osname, release, machine)
556
557
558def get_python_version():
559 return _PY_VERSION_SHORT