Skip to content

Commit b733057

Browse files
authored
Merge pull request #23 from pypa/feature/local-schemes
Allow sysconfig to override install schemes...
2 parents f1b0a2b + 342e02e commit b733057

File tree

3 files changed

+101
-58
lines changed

3 files changed

+101
-58
lines changed

distutils/command/install.py

Lines changed: 74 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44

55
import sys
66
import os
7+
import contextlib
8+
import sysconfig
9+
import itertools
710

811
from distutils import log
912
from distutils.core import Command
@@ -20,69 +23,94 @@
2023
HAS_USER_SITE = True
2124

2225
WINDOWS_SCHEME = {
23-
'purelib': '$base/Lib/site-packages',
24-
'platlib': '$base/Lib/site-packages',
25-
'headers': '$base/Include/$dist_name',
26-
'scripts': '$base/Scripts',
27-
'data' : '$base',
26+
'purelib': '{base}/Lib/site-packages',
27+
'platlib': '{base}/Lib/site-packages',
28+
'headers': '{base}/Include/{dist_name}',
29+
'scripts': '{base}/Scripts',
30+
'data' : '{base}',
2831
}
2932

3033
INSTALL_SCHEMES = {
31-
'unix_prefix': {
32-
'purelib': '$base/lib/$implementation_lower$py_version_short/site-packages',
33-
'platlib': '$platbase/$platlibdir/$implementation_lower$py_version_short/site-packages',
34-
'headers': '$base/include/$implementation_lower$py_version_short$abiflags/$dist_name',
35-
'scripts': '$base/bin',
36-
'data' : '$base',
34+
'posix_prefix': {
35+
'purelib': '{base}/lib/{implementation_lower}{py_version_short}/site-packages',
36+
'platlib': '{platbase}/{platlibdir}/{implementation_lower}{py_version_short}/site-packages',
37+
'headers': '{base}/include/{implementation_lower}{py_version_short}{abiflags}/{dist_name}',
38+
'scripts': '{base}/bin',
39+
'data' : '{base}',
3740
},
38-
'unix_home': {
39-
'purelib': '$base/lib/$implementation_lower',
40-
'platlib': '$base/$platlibdir/$implementation_lower',
41-
'headers': '$base/include/$implementation_lower/$dist_name',
42-
'scripts': '$base/bin',
43-
'data' : '$base',
41+
'posix_home': {
42+
'purelib': '{base}/lib/{implementation_lower}',
43+
'platlib': '{base}/{platlibdir}/{implementation_lower}',
44+
'headers': '{base}/include/{implementation_lower}/{dist_name}',
45+
'scripts': '{base}/bin',
46+
'data' : '{base}',
4447
},
4548
'nt': WINDOWS_SCHEME,
4649
'pypy': {
47-
'purelib': '$base/site-packages',
48-
'platlib': '$base/site-packages',
49-
'headers': '$base/include/$dist_name',
50-
'scripts': '$base/bin',
51-
'data' : '$base',
50+
'purelib': '{base}/site-packages',
51+
'platlib': '{base}/site-packages',
52+
'headers': '{base}/include/{dist_name}',
53+
'scripts': '{base}/bin',
54+
'data' : '{base}',
5255
},
5356
'pypy_nt': {
54-
'purelib': '$base/site-packages',
55-
'platlib': '$base/site-packages',
56-
'headers': '$base/include/$dist_name',
57-
'scripts': '$base/Scripts',
58-
'data' : '$base',
57+
'purelib': '{base}/site-packages',
58+
'platlib': '{base}/site-packages',
59+
'headers': '{base}/include/{dist_name}',
60+
'scripts': '{base}/Scripts',
61+
'data' : '{base}',
5962
},
6063
}
6164

6265
# user site schemes
6366
if HAS_USER_SITE:
6467
INSTALL_SCHEMES['nt_user'] = {
65-
'purelib': '$usersite',
66-
'platlib': '$usersite',
67-
'headers': '$userbase/$implementation$py_version_nodot/Include/$dist_name',
68-
'scripts': '$userbase/$implementation$py_version_nodot/Scripts',
69-
'data' : '$userbase',
68+
'purelib': '{usersite}',
69+
'platlib': '{usersite}',
70+
'headers': '{userbase}/{implementation}{py_version_nodot}/Include/{dist_name}',
71+
'scripts': '{userbase}/{implementation}{py_version_nodot}/Scripts',
72+
'data' : '{userbase}',
7073
}
7174

72-
INSTALL_SCHEMES['unix_user'] = {
73-
'purelib': '$usersite',
74-
'platlib': '$usersite',
75+
INSTALL_SCHEMES['posix_user'] = {
76+
'purelib': '{usersite}',
77+
'platlib': '{usersite}',
7578
'headers':
76-
'$userbase/include/$implementation_lower$py_version_short$abiflags/$dist_name',
77-
'scripts': '$userbase/bin',
78-
'data' : '$userbase',
79+
'{userbase}/include/{implementation_lower}{py_version_short}{abiflags}/{dist_name}',
80+
'scripts': '{userbase}/bin',
81+
'data' : '{userbase}',
7982
}
8083

8184
# The keys to an installation scheme; if any new types of files are to be
8285
# installed, be sure to add an entry to every installation scheme above,
8386
# and to SCHEME_KEYS here.
8487
SCHEME_KEYS = ('purelib', 'platlib', 'headers', 'scripts', 'data')
8588

89+
90+
def _load_sysconfig_schemes():
91+
with contextlib.suppress(AttributeError):
92+
return {
93+
scheme: sysconfig.get_paths(scheme, expand=False)
94+
for scheme in sysconfig.get_scheme_names()
95+
}
96+
97+
98+
def _load_schemes():
99+
"""
100+
Extend default schemes with schemes from sysconfig.
101+
"""
102+
103+
sysconfig_schemes = _load_sysconfig_schemes() or {}
104+
105+
return {
106+
scheme: {
107+
**INSTALL_SCHEMES.get(scheme, {}),
108+
**sysconfig_schemes.get(scheme, {}),
109+
}
110+
for scheme in set(itertools.chain(INSTALL_SCHEMES, sysconfig_schemes))
111+
}
112+
113+
86114
def _get_implementation():
87115
if hasattr(sys, 'pypy_version_info'):
88116
return 'PyPy'
@@ -284,7 +312,7 @@ def finalize_options(self):
284312
# input a heady brew of prefix, exec_prefix, home, install_base,
285313
# install_platbase, user-supplied versions of
286314
# install_{purelib,platlib,lib,scripts,data,...}, and the
287-
# INSTALL_SCHEME dictionary above. Phew!
315+
# install schemes. Phew!
288316

289317
self.dump_dirs("pre-finalize_{unix,other}")
290318

@@ -335,6 +363,8 @@ def finalize_options(self):
335363
# everything else.
336364
self.config_vars['base'] = self.install_base
337365
self.config_vars['platbase'] = self.install_platbase
366+
self.config_vars['installed_base'] = (
367+
sysconfig.get_config_vars()['installed_base'])
338368

339369
if DEBUG:
340370
from pprint import pprint
@@ -431,10 +461,10 @@ def finalize_unix(self):
431461
raise DistutilsPlatformError(
432462
"User base directory is not specified")
433463
self.install_base = self.install_platbase = self.install_userbase
434-
self.select_scheme("unix_user")
464+
self.select_scheme("posix_user")
435465
elif self.home is not None:
436466
self.install_base = self.install_platbase = self.home
437-
self.select_scheme("unix_home")
467+
self.select_scheme("posix_home")
438468
else:
439469
if self.prefix is None:
440470
if self.exec_prefix is not None:
@@ -450,7 +480,7 @@ def finalize_unix(self):
450480

451481
self.install_base = self.prefix
452482
self.install_platbase = self.exec_prefix
453-
self.select_scheme("unix_prefix")
483+
self.select_scheme("posix_prefix")
454484

455485
def finalize_other(self):
456486
"""Finalizes options for non-posix platforms"""
@@ -462,7 +492,7 @@ def finalize_other(self):
462492
self.select_scheme(os.name + "_user")
463493
elif self.home is not None:
464494
self.install_base = self.install_platbase = self.home
465-
self.select_scheme("unix_home")
495+
self.select_scheme("posix_home")
466496
else:
467497
if self.prefix is None:
468498
self.prefix = os.path.normpath(sys.prefix)
@@ -484,7 +514,7 @@ def select_scheme(self, name):
484514
name = 'pypy_nt'
485515
else:
486516
name = 'pypy'
487-
scheme = INSTALL_SCHEMES[name]
517+
scheme = _load_schemes()[name]
488518
for key in SCHEME_KEYS:
489519
attrname = 'install_' + key
490520
if getattr(self, attrname) is None:

distutils/tests/test_install.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ def cleanup():
9494

9595
self.addCleanup(cleanup)
9696

97-
for key in ('nt_user', 'unix_user'):
97+
for key in ('nt_user', 'posix_user'):
9898
self.assertIn(key, INSTALL_SCHEMES)
9999

100100
dist = Distribution({'name': 'xx'})

distutils/util.py

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -242,30 +242,43 @@ def check_environ ():
242242

243243

244244
def subst_vars (s, local_vars):
245-
"""Perform shell/Perl-style variable substitution on 'string'. Every
246-
occurrence of '$' followed by a name is considered a variable, and
247-
variable is substituted by the value found in the 'local_vars'
248-
dictionary, or in 'os.environ' if it's not in 'local_vars'.
245+
"""
246+
Perform variable substitution on 'string'.
247+
Variables are indicated by format-style braces ("{var}").
248+
Variable is substituted by the value found in the 'local_vars'
249+
dictionary or in 'os.environ' if it's not in 'local_vars'.
249250
'os.environ' is first checked/augmented to guarantee that it contains
250251
certain values: see 'check_environ()'. Raise ValueError for any
251252
variables not found in either 'local_vars' or 'os.environ'.
252253
"""
253254
check_environ()
254-
def _subst (match, local_vars=local_vars):
255-
var_name = match.group(1)
256-
if var_name in local_vars:
257-
return str(local_vars[var_name])
258-
else:
259-
return os.environ[var_name]
260-
255+
lookup = dict(os.environ)
256+
lookup.update((name, str(value)) for name, value in local_vars.items())
261257
try:
262-
return re.sub(r'\$([a-zA-Z_][a-zA-Z_0-9]*)', _subst, s)
258+
return _subst_compat(s).format_map(lookup)
263259
except KeyError as var:
264-
raise ValueError("invalid variable '$%s'" % var)
260+
raise ValueError(f"invalid variable {var}")
265261

266262
# subst_vars ()
267263

268264

265+
def _subst_compat(s):
266+
"""
267+
Replace shell/Perl-style variable substitution with
268+
format-style. For compatibility.
269+
"""
270+
def _subst(match):
271+
return f'{{{match.group(1)}}}'
272+
repl = re.sub(r'\$([a-zA-Z_][a-zA-Z_0-9]*)', _subst, s)
273+
if repl != s:
274+
import warnings
275+
warnings.warn(
276+
"shell/Perl-style substitions are deprecated",
277+
DeprecationWarning,
278+
)
279+
return repl
280+
281+
269282
def grok_environment_error (exc, prefix="error: "):
270283
# Function kept for backward compatibility.
271284
# Used to try clever things with EnvironmentErrors,

0 commit comments

Comments
 (0)