Skip to content

Commit e3870ab

Browse files
committed
Merge remote-tracking branch 'upstream/main' into gh-150114-macos
2 parents 03abe46 + dfe7ef6 commit e3870ab

12 files changed

Lines changed: 286 additions & 113 deletions

File tree

Doc/using/configure.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,15 @@ General Options
473473

474474
.. versionadded:: 3.15
475475

476+
.. option:: --with-build-details-suffix=[yes|SUFFIX]
477+
478+
Rename ``build-details.json`` to permit multiple co-located Python
479+
installs. If a custom ``SUFFIX`` is supplied it is used verbatim,
480+
otherwise one will be generated from the ``MULTIARCH`` tag with
481+
``-free-threading`` and ``-debug``, as appropriate.
482+
483+
.. versionadded:: 3.16
484+
476485

477486
C compiler options
478487
------------------

Doc/whatsnew/3.16.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,12 @@ Build changes
227227

228228
.. _libmpdec: https://www.bytereef.org/mpdecimal/
229229

230+
* Add a :option:`--with-build-details-suffix` configure flag to allow
231+
Linux distributions that co-install multiple versions of Python in the
232+
same tree to avoid ``build-details.json`` clashes.
233+
234+
(Contributed by Stefano Rivera in :gh:`131372`.)
235+
230236

231237
C API changes
232238
=============

Lib/test/libregrtest/utils.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -798,12 +798,9 @@ def _get_process_memory_usage_windows(pid: int) -> int | None:
798798

799799

800800
if _get_process_memory_usage is not None:
801-
_testcapi_get_process_memory_usage = _get_process_memory_usage
802-
803801
def get_process_memory_usage(pid: int) -> int | None:
804-
# Worker may exit before we query its memory.
805802
try:
806-
return _testcapi_get_process_memory_usage(pid)
803+
return _get_process_memory_usage(pid)
807804
except ProcessLookupError:
808805
return None
809806
elif _winapi is not None:
@@ -812,7 +809,5 @@ def get_process_memory_usage(pid: int) -> int | None:
812809
get_process_memory_usage = _get_process_memory_usage_linux
813810
else:
814811
def get_process_memory_usage(pid: int) -> int | None:
815-
"""
816-
Get process memory usage in bytes.
817-
"""
818812
return None
813+
get_process_memory_usage.__doc__ = "Get process memory usage in bytes."

Lib/test/test_grp.py

Lines changed: 41 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"""Test script for the grp module."""
22

3+
import random
4+
import string
35
import unittest
46
from test.support import import_helper
57

@@ -50,61 +52,51 @@ def test_values_extended(self):
5052
def test_errors(self):
5153
self.assertRaises(TypeError, grp.getgrgid)
5254
self.assertRaises(TypeError, grp.getgrgid, 3.14)
55+
self.assertRaises(TypeError, grp.getgrgid, 0.0)
56+
self.assertRaises(TypeError, grp.getgrgid, 0, 0)
57+
# should be out of gid_t range
58+
self.assertRaises(OverflowError, grp.getgrgid, 2**128)
59+
self.assertRaises(OverflowError, grp.getgrgid, -2**128)
5360
self.assertRaises(TypeError, grp.getgrnam)
5461
self.assertRaises(TypeError, grp.getgrnam, 42)
55-
self.assertRaises(TypeError, grp.getgrall, 42)
62+
self.assertRaises(TypeError, grp.getgrnam, b'root')
63+
self.assertRaises(TypeError, grp.getgrnam, 'root', 0)
5664
# embedded null character
5765
self.assertRaisesRegex(ValueError, 'null', grp.getgrnam, 'a\x00b')
66+
self.assertRaisesRegex(ValueError, 'null', grp.getgrnam, 'root\x00')
67+
self.assertRaises(UnicodeEncodeError, grp.getgrnam, 'roo\udc74')
68+
self.assertRaises(KeyError, grp.getgrnam, '')
69+
self.assertRaises(TypeError, grp.getgrall, 42)
5870

59-
# try to get some errors
60-
bynames = {}
61-
bygids = {}
62-
for (n, p, g, mem) in grp.getgrall():
63-
if not n or n == '+':
64-
continue # skip NIS entries etc.
65-
bynames[n] = g
66-
bygids[g] = n
67-
68-
allnames = list(bynames.keys())
69-
namei = 0
70-
fakename = allnames[namei]
71-
while fakename in bynames:
72-
chars = list(fakename)
73-
for i in range(len(chars)):
74-
if chars[i] == 'z':
75-
chars[i] = 'A'
76-
break
77-
elif chars[i] == 'Z':
78-
continue
71+
# Find a non-existent group name.
72+
# getgrall() will not necessarily report all existing groups
73+
# (typical for LDAP based directories in big organizations).
74+
for _ in range(30):
75+
fakename = ''.join(random.choices(string.ascii_lowercase, k=6))
76+
try:
77+
grp.getgrnam(fakename)
78+
except KeyError:
79+
break
80+
else:
81+
self.fail('Cannot find non-existent group name')
82+
83+
# Find a non-existent gid.
84+
maxgid = 2**31
85+
for _ in range(30):
86+
fakegid = random.randrange(maxgid)
87+
try:
88+
grp.getgrgid(fakegid)
89+
except KeyError:
90+
break
91+
except OverflowError:
92+
if maxgid == 2**31:
93+
maxgid = 2**16-1
94+
elif maxgid == 2**16-1:
95+
maxgid = 2**15
7996
else:
80-
chars[i] = chr(ord(chars[i]) + 1)
81-
break
82-
else:
83-
namei = namei + 1
84-
try:
85-
fakename = allnames[namei]
86-
except IndexError:
87-
# should never happen... if so, just forget it
88-
break
89-
fakename = ''.join(chars)
90-
91-
self.assertRaises(KeyError, grp.getgrnam, fakename)
92-
93-
# Choose a non-existent gid.
94-
fakegid = 4127
95-
while fakegid in bygids:
96-
fakegid = (fakegid * 3) % 0x10000
97-
98-
self.assertRaises(KeyError, grp.getgrgid, fakegid)
99-
100-
def test_noninteger_gid(self):
101-
entries = grp.getgrall()
102-
if not entries:
103-
self.skipTest('no groups')
104-
# Choose an existent gid.
105-
gid = entries[0][2]
106-
self.assertRaises(TypeError, grp.getgrgid, float(gid))
107-
self.assertRaises(TypeError, grp.getgrgid, str(gid))
97+
raise
98+
else:
99+
self.fail('Cannot find non-existent gid')
108100

109101

110102
if __name__ == "__main__":

Lib/test/test_pwd.py

Lines changed: 42 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import random
2+
import string
13
import sys
24
import unittest
35
from test.support import import_helper
@@ -56,61 +58,57 @@ def test_values_extended(self):
5658
def test_errors(self):
5759
self.assertRaises(TypeError, pwd.getpwuid)
5860
self.assertRaises(TypeError, pwd.getpwuid, 3.14)
61+
self.assertRaises(TypeError, pwd.getpwuid, 0.0)
62+
self.assertRaises(TypeError, pwd.getpwuid, 0, 0)
63+
# should be out of uid_t range
64+
self.assertRaises(KeyError, pwd.getpwuid, 2**128)
65+
self.assertRaises(KeyError, pwd.getpwuid, -2**128)
5966
self.assertRaises(TypeError, pwd.getpwnam)
6067
self.assertRaises(TypeError, pwd.getpwnam, 42)
61-
self.assertRaises(TypeError, pwd.getpwall, 42)
68+
self.assertRaises(TypeError, pwd.getpwnam, b'root')
69+
self.assertRaises(TypeError, pwd.getpwnam, 'root', 0)
6270
# embedded null character
6371
self.assertRaisesRegex(ValueError, 'null', pwd.getpwnam, 'a\x00b')
72+
self.assertRaisesRegex(ValueError, 'null', pwd.getpwnam, 'root\x00')
73+
self.assertRaises(UnicodeEncodeError, pwd.getpwnam, 'roo\udc74')
74+
self.assertRaises(KeyError, pwd.getpwnam, '')
75+
self.assertRaises(TypeError, pwd.getpwall, 42)
6476

65-
# try to get some errors
66-
bynames = {}
67-
byuids = {}
68-
for (n, p, u, g, gecos, d, s) in pwd.getpwall():
69-
bynames[n] = u
70-
byuids[u] = n
71-
72-
allnames = list(bynames.keys())
73-
namei = 0
74-
fakename = allnames[namei] if allnames else "invaliduser"
75-
while fakename in bynames:
76-
chars = list(fakename)
77-
for i in range(len(chars)):
78-
if chars[i] == 'z':
79-
chars[i] = 'A'
80-
break
81-
elif chars[i] == 'Z':
82-
continue
83-
else:
84-
chars[i] = chr(ord(chars[i]) + 1)
85-
break
86-
else:
87-
namei = namei + 1
88-
try:
89-
fakename = allnames[namei]
90-
except IndexError:
91-
# should never happen... if so, just forget it
92-
break
93-
fakename = ''.join(chars)
94-
95-
self.assertRaises(KeyError, pwd.getpwnam, fakename)
96-
97-
# In some cases, byuids isn't a complete list of all users in the
98-
# system, so if we try to pick a value not in byuids (via a perturbing
99-
# loop, say), pwd.getpwuid() might still be able to find data for that
100-
# uid. Using sys.maxint may provoke the same problems, but hopefully
101-
# it will be a more repeatable failure.
102-
fakeuid = sys.maxsize
103-
self.assertNotIn(fakeuid, byuids)
104-
self.assertRaises(KeyError, pwd.getpwuid, fakeuid)
77+
# Find a non-existent user name.
78+
# getpwall() will not necessarily report all existing users
79+
# (typical for LDAP based directories in big organizations).
80+
for _ in range(30):
81+
fakename = ''.join(random.choices(string.ascii_lowercase, k=6))
82+
try:
83+
pwd.getpwnam(fakename)
84+
except KeyError:
85+
break
86+
else:
87+
self.fail('Cannot find non-existent user name')
88+
89+
# Find a non-existent uid.
90+
maxuid = max(e.pw_uid for e in pwd.getpwall())
91+
if maxuid < 2**15:
92+
maxuid = 2**15
93+
elif maxuid < 2**16:
94+
maxuid = 2**16-1
95+
else:
96+
maxuid = 2**31
97+
for _ in range(30):
98+
fakeuid = random.randrange(maxuid)
99+
try:
100+
pwd.getpwuid(fakeuid)
101+
except KeyError:
102+
break
103+
else:
104+
self.fail('Cannot find non-existent uid')
105105

106106
# On Cygwin, getpwuid(-1) returns 'Unknown+User' user
107107
if sys.platform != 'cygwin':
108108
# -1 shouldn't be a valid uid because it has a special meaning in many
109109
# uid-related functions
110110
self.assertRaises(KeyError, pwd.getpwuid, -1)
111-
# should be out of uid_t range
112-
self.assertRaises(KeyError, pwd.getpwuid, 2**128)
113-
self.assertRaises(KeyError, pwd.getpwuid, -2**128)
111+
114112

115113
if __name__ == "__main__":
116114
unittest.main()

Makefile.pre.in

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,8 @@ MACOSX_DEPLOYMENT_TARGET=@CONFIGURE_MACOSX_DEPLOYMENT_TARGET@
215215
# the build, and is only listed here so it will be included in sysconfigdata.
216216
IPHONEOS_DEPLOYMENT_TARGET=@IPHONEOS_DEPLOYMENT_TARGET@
217217

218+
BUILD_DETAILS=@BUILD_DETAILS@
219+
218220
# Option to install to strip binaries
219221
STRIPFLAG=-s
220222

@@ -774,11 +776,11 @@ list-targets:
774776

775777
.PHONY: build_all
776778
build_all: check-clean-src check-app-store-compliance $(BUILDPYTHON) platform sharedmods \
777-
gdbhooks Programs/_testembed scripts checksharedmods rundsymutil build-details.json
779+
gdbhooks Programs/_testembed scripts checksharedmods rundsymutil $(BUILD_DETAILS)
778780

779781
.PHONY: build_wasm
780782
build_wasm: check-clean-src $(BUILDPYTHON) platform sharedmods \
781-
python-config checksharedmods build-details.json
783+
python-config checksharedmods $(BUILD_DETAILS)
782784

783785
.PHONY: build_emscripten
784786
build_emscripten: build_wasm web_example web_example_pyrepl_jspi
@@ -979,8 +981,8 @@ pybuilddir.txt: $(PYTHON_FOR_BUILD_DEPS)
979981
exit 1 ; \
980982
fi
981983

982-
build-details.json: pybuilddir.txt
983-
$(RUNSHARED) $(PYTHON_FOR_BUILD) $(srcdir)/Tools/build/generate-build-details.py `cat pybuilddir.txt`/build-details.json
984+
$(BUILD_DETAILS): pybuilddir.txt
985+
$(RUNSHARED) $(PYTHON_FOR_BUILD) $(srcdir)/Tools/build/generate-build-details.py `cat pybuilddir.txt`/$(BUILD_DETAILS)
984986

985987
# Build static library
986988
$(LIBRARY): $(LIBRARY_OBJS)
@@ -2349,7 +2351,7 @@ multissltest: all
23492351
# Only the main install gets a build-details.json.
23502352
.PHONY: install
23512353
install: @FRAMEWORKINSTALLFIRST@ @INSTALLTARGETS@ @FRAMEWORKINSTALLLAST@
2352-
$(INSTALL_DATA) `cat pybuilddir.txt`/build-details.json $(DESTDIR)$(LIBDEST); \
2354+
$(INSTALL_DATA) `cat pybuilddir.txt`/$(BUILD_DETAILS) $(DESTDIR)$(LIBDEST); \
23532355
if test "x$(ENSUREPIP)" != "xno" ; then \
23542356
case $(ENSUREPIP) in \
23552357
upgrade) ensurepip="--upgrade" ;; \
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Add a :option:`--with-build-details-suffix` configure flag to allow Linux
2+
distributions that co-install multiple versions of Python in the same tree
3+
to avoid ``build-details.json`` clashes.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix an intermittent crash after :func:`os.fork` when perf trampoline
2+
profiling is enabled and the child returns through trampoline frames
3+
inherited from the parent process.

0 commit comments

Comments
 (0)