Skip to content

Commit 12aaf67

Browse files
committed
Merge branch 'main' into gh-127381-samefile
2 parents 6266d5c + 7f8ec52 commit 12aaf67

21 files changed

+164
-129
lines changed

InternalDocs/garbage_collector.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -518,7 +518,7 @@ Then the above algorithm is repeated, starting from step 2.
518518
Determining how much work to do
519519
-------------------------------
520520

521-
We need to do a certain amount of work to enusre that garbage is collected,
521+
We need to do a certain amount of work to ensure that garbage is collected,
522522
but doing too much work slows down execution.
523523

524524
To work out how much work we need to do, consider a heap with `L` live objects

Lib/inspect.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2943,11 +2943,19 @@ def __init__(self, parameters=None, *, return_annotation=_empty,
29432943
params = OrderedDict()
29442944
top_kind = _POSITIONAL_ONLY
29452945
seen_default = False
2946+
seen_var_parameters = set()
29462947

29472948
for param in parameters:
29482949
kind = param.kind
29492950
name = param.name
29502951

2952+
if kind in (_VAR_POSITIONAL, _VAR_KEYWORD):
2953+
if kind in seen_var_parameters:
2954+
msg = f'more than one {kind.description} parameter'
2955+
raise ValueError(msg)
2956+
2957+
seen_var_parameters.add(kind)
2958+
29512959
if kind < top_kind:
29522960
msg = (
29532961
'wrong parameter order: {} parameter before {} '

Lib/pathlib/_abc.py

Lines changed: 6 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -748,6 +748,12 @@ def copy_into(self, target_dir, *, follow_symlinks=True,
748748
dirs_exist_ok=dirs_exist_ok,
749749
preserve_metadata=preserve_metadata)
750750

751+
def _delete(self):
752+
"""
753+
Delete this file or directory (including all sub-directories).
754+
"""
755+
raise UnsupportedOperation(self._unsupported_msg('_delete()'))
756+
751757
def move(self, target):
752758
"""
753759
Recursively move this file or directory tree to the given destination.
@@ -782,39 +788,6 @@ def lchmod(self, mode):
782788
"""
783789
self.chmod(mode, follow_symlinks=False)
784790

785-
def unlink(self, missing_ok=False):
786-
"""
787-
Remove this file or link.
788-
If the path is a directory, use rmdir() instead.
789-
"""
790-
raise UnsupportedOperation(self._unsupported_msg('unlink()'))
791-
792-
def rmdir(self):
793-
"""
794-
Remove this directory. The directory must be empty.
795-
"""
796-
raise UnsupportedOperation(self._unsupported_msg('rmdir()'))
797-
798-
def _delete(self):
799-
"""
800-
Delete this file or directory (including all sub-directories).
801-
"""
802-
if self.is_dir(follow_symlinks=False):
803-
def on_error(err):
804-
raise err
805-
806-
results = self.walk(
807-
on_error=on_error,
808-
top_down=False, # So we rmdir() empty directories.
809-
follow_symlinks=False)
810-
for dirpath, _, filenames in results:
811-
for filename in filenames:
812-
filepath = dirpath / filename
813-
filepath.unlink()
814-
dirpath.rmdir()
815-
else:
816-
self.unlink()
817-
818791
def owner(self, *, follow_symlinks=True):
819792
"""
820793
Return the login name of the file owner.

Lib/poplib.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,7 @@ def close(self):
309309
# optional commands:
310310

311311
def rpop(self, user):
312-
"""Not sure what this does."""
312+
"""Send RPOP command to access the mailbox with an alternate user."""
313313
return self._shortcmd('RPOP %s' % user)
314314

315315

Lib/sysconfig/__init__.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -173,9 +173,7 @@ def joinuser(*args):
173173
_PY_VERSION = sys.version.split()[0]
174174
_PY_VERSION_SHORT = f'{sys.version_info[0]}.{sys.version_info[1]}'
175175
_PY_VERSION_SHORT_NO_DOT = f'{sys.version_info[0]}{sys.version_info[1]}'
176-
_PREFIX = os.path.normpath(sys.prefix)
177176
_BASE_PREFIX = os.path.normpath(sys.base_prefix)
178-
_EXEC_PREFIX = os.path.normpath(sys.exec_prefix)
179177
_BASE_EXEC_PREFIX = os.path.normpath(sys.base_exec_prefix)
180178
# Mutex guarding initialization of _CONFIG_VARS.
181179
_CONFIG_VARS_LOCK = threading.RLock()
@@ -473,8 +471,8 @@ def _init_config_vars():
473471
global _CONFIG_VARS
474472
_CONFIG_VARS = {}
475473

476-
prefix = _PREFIX
477-
exec_prefix = _EXEC_PREFIX
474+
prefix = os.path.normpath(sys.prefix)
475+
exec_prefix = os.path.normpath(sys.exec_prefix)
478476
base_prefix = _BASE_PREFIX
479477
base_exec_prefix = _BASE_EXEC_PREFIX
480478

@@ -564,9 +562,19 @@ def get_config_vars(*args):
564562
With arguments, return a list of values that result from looking up
565563
each argument in the configuration variable dictionary.
566564
"""
565+
global _CONFIG_VARS_INITIALIZED
567566

568567
# Avoid claiming the lock once initialization is complete.
569-
if not _CONFIG_VARS_INITIALIZED:
568+
if _CONFIG_VARS_INITIALIZED:
569+
# GH-126789: If sys.prefix or sys.exec_prefix were updated, invalidate the cache.
570+
prefix = os.path.normpath(sys.prefix)
571+
exec_prefix = os.path.normpath(sys.exec_prefix)
572+
if _CONFIG_VARS['prefix'] != prefix or _CONFIG_VARS['exec_prefix'] != exec_prefix:
573+
with _CONFIG_VARS_LOCK:
574+
_CONFIG_VARS_INITIALIZED = False
575+
_init_config_vars()
576+
else:
577+
# Initialize the config_vars cache.
570578
with _CONFIG_VARS_LOCK:
571579
# Test again with the lock held to avoid races. Note that
572580
# we test _CONFIG_VARS here, not _CONFIG_VARS_INITIALIZED,

Lib/test/test_inspect/test_inspect.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2992,6 +2992,17 @@ def test2(pod=42, /):
29922992
with self.assertRaisesRegex(ValueError, 'follows default argument'):
29932993
S((pkd, pk))
29942994

2995+
second_args = args.replace(name="second_args")
2996+
with self.assertRaisesRegex(ValueError, 'more than one variadic positional parameter'):
2997+
S((args, second_args))
2998+
2999+
with self.assertRaisesRegex(ValueError, 'more than one variadic positional parameter'):
3000+
S((args, ko, second_args))
3001+
3002+
second_kwargs = kwargs.replace(name="second_kwargs")
3003+
with self.assertRaisesRegex(ValueError, 'more than one variadic keyword parameter'):
3004+
S((kwargs, second_kwargs))
3005+
29953006
def test_signature_object_pickle(self):
29963007
def foo(a, b, *, c:1={}, **kw) -> {42:'ham'}: pass
29973008
foo_partial = functools.partial(foo, a=1)

Lib/test/test_pathlib/test_pathlib.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1352,6 +1352,25 @@ def test_group_no_follow_symlinks(self):
13521352
self.assertEqual(expected_gid, gid_2)
13531353
self.assertEqual(expected_name, link.group(follow_symlinks=False))
13541354

1355+
def test_unlink(self):
1356+
p = self.cls(self.base) / 'fileA'
1357+
p.unlink()
1358+
self.assertFileNotFound(p.stat)
1359+
self.assertFileNotFound(p.unlink)
1360+
1361+
def test_unlink_missing_ok(self):
1362+
p = self.cls(self.base) / 'fileAAA'
1363+
self.assertFileNotFound(p.unlink)
1364+
p.unlink(missing_ok=True)
1365+
1366+
def test_rmdir(self):
1367+
p = self.cls(self.base) / 'dirA'
1368+
for q in p.iterdir():
1369+
q.unlink()
1370+
p.rmdir()
1371+
self.assertFileNotFound(p.stat)
1372+
self.assertFileNotFound(p.unlink)
1373+
13551374
@needs_symlinks
13561375
def test_delete_symlink(self):
13571376
tmp = self.cls(self.base, 'delete')

Lib/test/test_pathlib/test_pathlib_abc.py

Lines changed: 10 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1364,8 +1364,6 @@ def test_unsupported_operation(self):
13641364
self.assertRaises(e, p.touch)
13651365
self.assertRaises(e, p.chmod, 0o755)
13661366
self.assertRaises(e, p.lchmod, 0o755)
1367-
self.assertRaises(e, p.unlink)
1368-
self.assertRaises(e, p.rmdir)
13691367
self.assertRaises(e, p.owner)
13701368
self.assertRaises(e, p.group)
13711369
self.assertRaises(e, p.as_uri)
@@ -1487,31 +1485,18 @@ def mkdir(self, mode=0o777, parents=False, exist_ok=False):
14871485
self.parent.mkdir(parents=True, exist_ok=True)
14881486
self.mkdir(mode, parents=False, exist_ok=exist_ok)
14891487

1490-
def unlink(self, missing_ok=False):
1491-
path = str(self)
1492-
name = self.name
1493-
parent = str(self.parent)
1494-
if path in self._directories:
1495-
raise IsADirectoryError(errno.EISDIR, "Is a directory", path)
1496-
elif path in self._files:
1497-
self._directories[parent].remove(name)
1498-
del self._files[path]
1499-
elif not missing_ok:
1500-
raise FileNotFoundError(errno.ENOENT, "File not found", path)
1501-
1502-
def rmdir(self):
1488+
def _delete(self):
15031489
path = str(self)
15041490
if path in self._files:
1505-
raise NotADirectoryError(errno.ENOTDIR, "Not a directory", path)
1506-
elif path not in self._directories:
1507-
raise FileNotFoundError(errno.ENOENT, "File not found", path)
1508-
elif self._directories[path]:
1509-
raise OSError(errno.ENOTEMPTY, "Directory not empty", path)
1510-
else:
1511-
name = self.name
1512-
parent = str(self.parent)
1513-
self._directories[parent].remove(name)
1491+
del self._files[path]
1492+
elif path in self._directories:
1493+
for name in list(self._directories[path]):
1494+
self.joinpath(name)._delete()
15141495
del self._directories[path]
1496+
else:
1497+
raise FileNotFoundError(errno.ENOENT, "File not found", path)
1498+
parent = str(self.parent)
1499+
self._directories[parent].remove(self.name)
15151500

15161501

15171502
class DummyPathTest(DummyPurePathTest):
@@ -2164,30 +2149,11 @@ def test_is_symlink(self):
21642149
self.assertIs((P / 'linkA\udfff').is_file(), False)
21652150
self.assertIs((P / 'linkA\x00').is_file(), False)
21662151

2167-
def test_unlink(self):
2168-
p = self.cls(self.base) / 'fileA'
2169-
p.unlink()
2170-
self.assertFileNotFound(p.stat)
2171-
self.assertFileNotFound(p.unlink)
2172-
2173-
def test_unlink_missing_ok(self):
2174-
p = self.cls(self.base) / 'fileAAA'
2175-
self.assertFileNotFound(p.unlink)
2176-
p.unlink(missing_ok=True)
2177-
2178-
def test_rmdir(self):
2179-
p = self.cls(self.base) / 'dirA'
2180-
for q in p.iterdir():
2181-
q.unlink()
2182-
p.rmdir()
2183-
self.assertFileNotFound(p.stat)
2184-
self.assertFileNotFound(p.unlink)
2185-
21862152
def test_delete_file(self):
21872153
p = self.cls(self.base) / 'fileA'
21882154
p._delete()
21892155
self.assertFileNotFound(p.stat)
2190-
self.assertFileNotFound(p.unlink)
2156+
self.assertFileNotFound(p._delete)
21912157

21922158
def test_delete_dir(self):
21932159
base = self.cls(self.base)

Lib/test/test_sysconfig.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ def setUp(self):
5353
os.uname = self._get_uname
5454
# saving the environment
5555
self.name = os.name
56+
self.prefix = sys.prefix
57+
self.exec_prefix = sys.exec_prefix
5658
self.platform = sys.platform
5759
self.version = sys.version
5860
self._framework = sys._framework
@@ -77,6 +79,8 @@ def tearDown(self):
7779
else:
7880
del os.uname
7981
os.name = self.name
82+
sys.prefix = self.prefix
83+
sys.exec_prefix = self.exec_prefix
8084
sys.platform = self.platform
8185
sys.version = self.version
8286
sys._framework = self._framework
@@ -653,6 +657,27 @@ def test_sysconfigdata_json(self):
653657

654658
self.assertEqual(system_config_vars, json_config_vars)
655659

660+
def test_sysconfig_config_vars_no_prefix_cache(self):
661+
sys.prefix = 'prefix-AAA'
662+
sys.exec_prefix = 'exec-prefix-AAA'
663+
664+
config_vars = sysconfig.get_config_vars()
665+
666+
self.assertEqual(config_vars['prefix'], sys.prefix)
667+
self.assertEqual(config_vars['base'], sys.prefix)
668+
self.assertEqual(config_vars['exec_prefix'], sys.exec_prefix)
669+
self.assertEqual(config_vars['platbase'], sys.exec_prefix)
670+
671+
sys.prefix = 'prefix-BBB'
672+
sys.exec_prefix = 'exec-prefix-BBB'
673+
674+
config_vars = sysconfig.get_config_vars()
675+
676+
self.assertEqual(config_vars['prefix'], sys.prefix)
677+
self.assertEqual(config_vars['base'], sys.prefix)
678+
self.assertEqual(config_vars['exec_prefix'], sys.exec_prefix)
679+
self.assertEqual(config_vars['platbase'], sys.exec_prefix)
680+
656681

657682
class MakefileTests(unittest.TestCase):
658683

Lib/test/test_urllib.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ def test_headers(self):
156156
self.assertIsInstance(self.returned_obj.headers, email.message.Message)
157157

158158
def test_url(self):
159-
self.assertEqual(self.returned_obj.url, "file://" + self.quoted_pathname)
159+
self.assertEqual(self.returned_obj.url, "file:" + self.quoted_pathname)
160160

161161
def test_status(self):
162162
self.assertIsNone(self.returned_obj.status)
@@ -165,7 +165,7 @@ def test_info(self):
165165
self.assertIsInstance(self.returned_obj.info(), email.message.Message)
166166

167167
def test_geturl(self):
168-
self.assertEqual(self.returned_obj.geturl(), "file://" + self.quoted_pathname)
168+
self.assertEqual(self.returned_obj.geturl(), "file:" + self.quoted_pathname)
169169

170170
def test_getcode(self):
171171
self.assertIsNone(self.returned_obj.getcode())
@@ -471,11 +471,14 @@ def test_missing_localfile(self):
471471

472472
def test_file_notexists(self):
473473
fd, tmp_file = tempfile.mkstemp()
474-
tmp_fileurl = 'file://localhost/' + tmp_file.replace(os.path.sep, '/')
474+
tmp_file_canon_url = 'file:' + urllib.request.pathname2url(tmp_file)
475+
parsed = urllib.parse.urlsplit(tmp_file_canon_url)
476+
tmp_fileurl = parsed._replace(netloc='localhost').geturl()
475477
try:
476478
self.assertTrue(os.path.exists(tmp_file))
477479
with urllib.request.urlopen(tmp_fileurl) as fobj:
478480
self.assertTrue(fobj)
481+
self.assertEqual(fobj.url, tmp_file_canon_url)
479482
finally:
480483
os.close(fd)
481484
os.unlink(tmp_file)
@@ -609,7 +612,7 @@ def tearDown(self):
609612

610613
def constructLocalFileUrl(self, filePath):
611614
filePath = os.path.abspath(filePath)
612-
return "file://%s" % urllib.request.pathname2url(filePath)
615+
return "file:" + urllib.request.pathname2url(filePath)
613616

614617
def createNewTempFile(self, data=b""):
615618
"""Creates a new temporary file containing the specified data,

0 commit comments

Comments
 (0)