Skip to content

Commit 1b55a34

Browse files
committed
bpo-41282: add vendor config
Signed-off-by: Filipe Laíns <[email protected]>
1 parent 251ffa9 commit 1b55a34

File tree

11 files changed

+122
-6
lines changed

11 files changed

+122
-6
lines changed

Doc/library/site.rst

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,20 @@ It starts by constructing up to four directories from a head and a tail part.
3232
For the head part, it uses ``sys.prefix`` and ``sys.exec_prefix``; empty heads
3333
are skipped. For the tail part, it uses the empty string and then
3434
:file:`lib/site-packages` (on Windows) or
35-
:file:`lib/python{X.Y}/site-packages` (on Unix and Macintosh). For each
35+
:file:`lib/python{X.Y}/site-packages` (on Unix and Macintosh), and finally
36+
the ``purelib`` and ``platlib`` paths for each scheme specified in the
37+
``EXTRA_SITE_INSTALL_SCHEMES`` variable of the vendor config. For each
3638
of the distinct head-tail combinations, it sees if it refers to an existing
3739
directory, and if so, adds it to ``sys.path`` and also inspects the newly
3840
added path for configuration files.
3941

4042
.. versionchanged:: 3.5
4143
Support for the "site-python" directory has been removed.
4244

45+
.. versionchanged:: 3.10
46+
Extra site install schemes specified in the vendor config
47+
(``--with-vendor-config`` configure option) will also be loaded.
48+
4349
If a file named "pyvenv.cfg" exists one directory above sys.executable,
4450
sys.prefix and sys.exec_prefix are set to that directory and
4551
it is also checked for site-packages (sys.base_prefix and

Doc/library/sysconfig.rst

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ Every new component that is installed using :mod:`distutils` or a
7272
Distutils-based system will follow the same scheme to copy its file in the right
7373
places.
7474

75-
Python currently supports seven schemes:
75+
Python currently supports seven mandatory schemes:
7676

7777
- *posix_prefix*: scheme for POSIX platforms like Linux or Mac OS X. This is
7878
the default scheme used when Python or a component is installed.
@@ -85,6 +85,10 @@ Python currently supports seven schemes:
8585
- *nt*: scheme for NT platforms like Windows.
8686
- *nt_user*: scheme for NT platforms, when the *user* option is used.
8787

88+
Additionally to these, Python also supports vendor schemes specified in the
89+
``EXTRA_SITE_INSTALL_SCHEMES`` variable of the vendor config
90+
(``--with-vendor-config`` configure option).
91+
8892
Each scheme is itself composed of a series of paths and each path has a unique
8993
identifier. Python currently uses eight paths:
9094

Doc/using/configure.rst

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,21 @@ General Options
116116

117117
.. versionadded:: 3.10
118118

119+
.. cmdoption:: --with-vendor-config=config.py
120+
121+
Path to the vendor config (none by default).
122+
123+
The vendor config is a Python file that allows configuring some aspects of
124+
the Python distribution.
125+
126+
A `EXTRA_SITE_INSTALL_SCHEMES` variable can be specified in the config to
127+
add extra site install schemes. These schemes will be picked up by the
128+
:mod:`sysconfig` module, and will be activated in the :mod:`site` module
129+
initialization. This options allow Python distributors to define custom
130+
locations to use for their Python packages.
131+
132+
.. versionadded:: 3.10
133+
119134

120135
Install Options
121136
---------------

Lib/site.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
import builtins
7575
import _sitebuiltins
7676
import io
77+
import sysconfig
7778

7879
# Prefixes for site-packages; add additional prefixes like /usr/local here
7980
PREFIXES = [sys.prefix, sys.exec_prefix]
@@ -87,6 +88,8 @@
8788
USER_SITE = None
8889
USER_BASE = None
8990

91+
_VENDOR_SCHEMES = None
92+
9093

9194
def _trace(message):
9295
if sys.flags.verbose:
@@ -350,6 +353,7 @@ def getsitepackages(prefixes=None):
350353
this function will find its `site-packages` subdirectory depending on the
351354
system environment, and will return a list of full paths.
352355
"""
356+
global _VENDOR_SCHEMES
353357
sitepackages = []
354358
seen = set()
355359

@@ -377,6 +381,23 @@ def getsitepackages(prefixes=None):
377381
for libdir in libdirs:
378382
path = os.path.join(prefix, libdir, "site-packages")
379383
sitepackages.append(path)
384+
385+
if _VENDOR_SCHEMES is None: # delayed execution
386+
try:
387+
import _vendor_config
388+
389+
390+
_VENDOR_SCHEMES = _vendor_config.EXTRA_SITE_INSTALL_SCHEMES.keys()
391+
except (ModuleNotFoundError, AttributeError):
392+
_VENDOR_SCHEMES = []
393+
394+
# vendor site schemes
395+
for scheme in _VENDOR_SCHEMES:
396+
sitepackages += list({
397+
sysconfig.get_path('purelib', scheme),
398+
sysconfig.get_path('platlib', scheme),
399+
})
400+
380401
return sitepackages
381402

382403
def addsitepackages(known_paths, prefixes=None):

Lib/sysconfig.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,23 @@
5959
}
6060

6161

62+
def _load_vendor_schemes():
63+
# add vendor defined schemes
64+
try:
65+
import _vendor_config
66+
67+
_INSTALL_SCHEMES.update({
68+
name: scheme
69+
for name, scheme in _vendor_config.EXTRA_SITE_INSTALL_SCHEMES.items()
70+
if name not in _INSTALL_SCHEMES
71+
})
72+
except (ModuleNotFoundError, AttributeError):
73+
pass
74+
75+
76+
_load_vendor_schemes()
77+
78+
6279
# NOTE: site.py has copy of this function.
6380
# Sync it when modify this function.
6481
def _getuserbase():

Lib/test/test_site.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,11 @@ def test_getusersitepackages(self):
277277
self.assertEqual(site.USER_BASE, site.getuserbase())
278278

279279
def test_getsitepackages(self):
280+
sys.path.append(os.path.abspath(os.path.join(__file__, '..', 'vendor_config')))
281+
# force re-load of vendor schemes with the patched sys.path
282+
site._VENDOR_SCHEMES = None
283+
sysconfig._load_vendor_schemes()
284+
280285
site.PREFIXES = ['xoxo']
281286
dirs = site.getsitepackages()
282287
if os.sep == '/':
@@ -288,17 +293,20 @@ def test_getsitepackages(self):
288293
'site-packages')
289294
self.assertEqual(dirs[0], wanted)
290295
else:
291-
self.assertEqual(len(dirs), 1)
296+
self.assertEqual(len(dirs), 3)
292297
wanted = os.path.join('xoxo', 'lib',
293298
'python%d.%d' % sys.version_info[:2],
294299
'site-packages')
295-
self.assertEqual(dirs[-1], wanted)
300+
self.assertEqual(dirs[-3], wanted)
301+
self.assertEqual(sorted(dirs[-2:]), ['vendor-plat-packages', 'vendor-pure-packages'])
296302
else:
297303
# other platforms
298304
self.assertEqual(len(dirs), 2)
299305
self.assertEqual(dirs[0], 'xoxo')
300306
wanted = os.path.join('xoxo', 'lib', 'site-packages')
301307
self.assertEqual(dirs[1], wanted)
308+
self.assertEqual(dirs[2], 'vendor-pure-packages')
309+
self.assertEqual(dirs[3], 'vendor-plat-packages')
302310

303311
@unittest.skipUnless(HAS_USER_SITE, 'need user site')
304312
def test_no_home_directory(self):

Lib/test/test_sysconfig.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -263,10 +263,14 @@ def test_get_config_h_filename(self):
263263
self.assertTrue(os.path.isfile(config_h), config_h)
264264

265265
def test_get_scheme_names(self):
266-
wanted = ['nt', 'posix_home', 'posix_prefix']
266+
sys.path.append(os.path.abspath(os.path.join(__file__, '..', 'vendor_config')))
267+
# force re-load of vendor schemes with the patched sys.path
268+
sysconfig._load_vendor_schemes()
269+
270+
wanted = ['nt', 'posix_home', 'posix_prefix', 'some_vendor']
267271
if HAS_USER_BASE:
268272
wanted.extend(['nt_user', 'osx_framework_user', 'posix_user'])
269-
self.assertEqual(get_scheme_names(), tuple(sorted(wanted)))
273+
self.assertEqual(sysconfig.get_scheme_names(), tuple(sorted(wanted)))
270274

271275
@skip_unless_symlink
272276
def test_symlink(self): # Issue 7880
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
EXTRA_SITE_INSTALL_SCHEMES = {
2+
'some_vendor': {
3+
'stdlib': '{installed_base}/{platlibdir}/python{py_version_short}',
4+
'platstdlib': '{platbase}/{platlibdir}/python{py_version_short}',
5+
'include':
6+
'{installed_base}/include/python{py_version_short}{abiflags}',
7+
'platinclude':
8+
'{installed_platbase}/include/python{py_version_short}{abiflags}',
9+
'purelib': 'vendor-pure-packages',
10+
'platlib': 'vendor-plat-packages',
11+
'scripts': 'vendor-scripts',
12+
'data': 'vendor-data',
13+
},
14+
}

Makefile.pre.in

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1508,6 +1508,7 @@ TESTSUBDIRS= ctypes/test \
15081508
unittest/test unittest/test/testmock
15091509

15101510
TEST_MODULES=@TEST_MODULES@
1511+
VENDOR_CONFIG='@VENDOR_CONFIG@'
15111512
libinstall: build_all $(srcdir)/Modules/xxmodule.c
15121513
@for i in $(SCRIPTDIR) $(LIBDEST); \
15131514
do \
@@ -1543,6 +1544,9 @@ libinstall: build_all $(srcdir)/Modules/xxmodule.c
15431544
echo $(INSTALL_DATA) $$i $(LIBDEST); \
15441545
fi; \
15451546
done
1547+
@if test ! -z "$(VENDOR_CONFIG)"; then \
1548+
$(INSTALL_SCRIPT) $(VENDOR_CONFIG) $(DESTDIR)$(LIBDEST)/_vendor_config.py \
1549+
fi
15461550
@if test "$(TEST_MODULES)" = yes; then \
15471551
subdirs="$(LIBSUBDIRS) $(TESTSUBDIRS)"; \
15481552
else \
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Introduced support for Python distributors to specify a vendor config, via
2+
--with-vendor-config, which allows them to add custom install schemes.

configure.ac

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5941,6 +5941,27 @@ else
59415941
fi
59425942
AC_SUBST(TEST_MODULES)
59435943

5944+
# --with-vendor-config
5945+
VENDOR_CONFIG=''
5946+
AC_MSG_CHECKING(for --with-vendor-config)
5947+
AC_ARG_WITH(vendor-config,
5948+
AS_HELP_STRING([--with-vendor-config=<config.py>]
5949+
[use a vendor config to customize the certain details of the Python installation]),
5950+
[
5951+
AC_CHECK_FILE("$withval",
5952+
[
5953+
if ( echo "$withval" | grep '.*\.py$' > /dev/null); then
5954+
VENDOR_CONFIG="$withval"
5955+
else
5956+
AC_MSG_ERROR([--with-vendor-config requires a Python file])
5957+
fi
5958+
],
5959+
[
5960+
AC_MSG_ERROR([--with-vendor-config requires a valid file])
5961+
])
5962+
],
5963+
[])
5964+
AC_SUBST(VENDOR_CONFIG)
59445965

59455966
# generate output files
59465967
AC_CONFIG_FILES(Makefile.pre Misc/python.pc Misc/python-embed.pc Misc/python-config.sh)

0 commit comments

Comments
 (0)