Update some libs and tests from 3.14.3 to 3.14.4 (#7576)

* Update some libs and tests to 3.14.4

* Update `pydoc_data`
This commit is contained in:
Shahar Naveh authored and GitHub committed 2026-04-10 09:16:10 +09:00
1 parent f937cde7af
commit dcae8f0d53
15 files changed
+722 -377

No files matched your search

+7
View File
@@ -168,6 +168,13 @@ class Logcat:
# message.
message = message.replace(b"\x00", b"\xc0\x80")
# On API level 30 and higher, Logcat will strip any number of leading
# newlines. This is visible in all `logcat` modes, even --binary. Work
# around this by adding a leading space, which shouldn't make any
# difference to the log's usability.
if message.startswith(b"\n"):
message = b" " + message
with self._lock:
now = time()
self._bucket_level += (
+16 -3
View File
@@ -919,7 +919,7 @@ def get_annotations(
does not exist, the __annotate__ function is called. The
FORWARDREF format uses __annotations__ if it exists and can be
evaluated, and otherwise falls back to calling the __annotate__ function.
The SOURCE format tries __annotate__ first, and falls back to
The STRING format tries __annotate__ first, and falls back to
using __annotations__, stringified using annotations_to_string().
This function handles several details for you:
@@ -1037,13 +1037,26 @@ def get_annotations(
obj_globals = obj_locals = unwrap = None
if unwrap is not None:
# Use an id-based visited set to detect cycles in the __wrapped__
# and functools.partial.func chain (e.g. f.__wrapped__ = f).
# On cycle detection we stop and use whatever __globals__ we have
# found so far, mirroring the approach of inspect.unwrap().
_seen_ids = {id(unwrap)}
while True:
if hasattr(unwrap, "__wrapped__"):
unwrap = unwrap.__wrapped__
candidate = unwrap.__wrapped__
if id(candidate) in _seen_ids:
break
_seen_ids.add(id(candidate))
unwrap = candidate
continue
if functools := sys.modules.get("functools"):
if isinstance(unwrap, functools.partial):
unwrap = unwrap.func
candidate = unwrap.func
if id(candidate) in _seen_ids:
break
_seen_ids.add(id(candidate))
unwrap = candidate
continue
break
if hasattr(unwrap, "__globals__"):
+6 -4
View File
@@ -149,6 +149,10 @@ def _copy_items(items):
return copy.copy(items)
def _identity(value):
return value
# ===============
# Formatting Help
# ===============
@@ -200,7 +204,7 @@ class HelpFormatter(object):
self._decolor = decolor
else:
self._theme = get_theme(force_no_color=True).argparse
self._decolor = lambda text: text
self._decolor = _identity
# ===============================
# Section and indentation methods
@@ -1903,9 +1907,7 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
self._subparsers = None
# register types
def identity(string):
return string
self.register('type', None, identity)
self.register('type', None, _identity)
# add help argument if necessary
# (using explicit default to override global argument_default)
Vendored
+24 -3
View File
@@ -15,7 +15,7 @@ __all__ = ["glob", "iglob", "escape", "translate"]
def glob(pathname, *, root_dir=None, dir_fd=None, recursive=False,
include_hidden=False):
"""Return a list of paths matching a pathname pattern.
"""Return a list of paths matching a `pathname` pattern.
The pattern may contain simple shell-style wildcards a la
fnmatch. Unlike fnmatch, filenames starting with a
@@ -25,6 +25,15 @@ def glob(pathname, *, root_dir=None, dir_fd=None, recursive=False,
The order of the returned list is undefined. Sort it if you need a
particular order.
If `root_dir` is not None, it should be a path-like object specifying the
root directory for searching. It has the same effect as changing the
current directory before calling it (without actually
changing it). If pathname is relative, the result will contain
paths relative to `root_dir`.
If `dir_fd` is not None, it should be a file descriptor referring to a
directory, and paths will then be relative to that directory.
If `include_hidden` is true, the patterns '*', '?', '**' will match hidden
directories.
@@ -36,7 +45,7 @@ def glob(pathname, *, root_dir=None, dir_fd=None, recursive=False,
def iglob(pathname, *, root_dir=None, dir_fd=None, recursive=False,
include_hidden=False):
"""Return an iterator which yields the paths matching a pathname pattern.
"""Return an iterator which yields the paths matching a `pathname` pattern.
The pattern may contain simple shell-style wildcards a la
fnmatch. However, unlike fnmatch, filenames starting with a
@@ -46,7 +55,19 @@ def iglob(pathname, *, root_dir=None, dir_fd=None, recursive=False,
The order of the returned paths is undefined. Sort them if you need a
particular order.
If recursive is true, the pattern '**' will match any files and
If `root_dir` is not None, it should be a path-like object specifying
the root directory for searching. It has the same effect as changing
the current directory before calling it (without actually
changing it). If pathname is relative, the result will contain
paths relative to `root_dir`.
If `dir_fd` is not None, it should be a file descriptor referring to a
directory, and paths will then be relative to that directory.
If `include_hidden` is true, the patterns '*', '?', '**' will match hidden
directories.
If `recursive` is true, the pattern '**' will match any files and
zero or more directories and subdirectories.
"""
sys.audit("glob.glob", pathname, recursive)
+2 -1
View File
@@ -2660,11 +2660,12 @@ class Parameter:
The annotation for the parameter if specified. If the
parameter has no annotation, this attribute is set to
`Parameter.empty`.
* kind : str
* kind
Describes how argument values are bound to the parameter.
Possible values: `Parameter.POSITIONAL_ONLY`,
`Parameter.POSITIONAL_OR_KEYWORD`, `Parameter.VAR_POSITIONAL`,
`Parameter.KEYWORD_ONLY`, `Parameter.VAR_KEYWORD`.
Every value has a `description` attribute describing meaning.
"""
__slots__ = ('_name', '_kind', '_default', '_annotation')
+1 -1
View File
@@ -1,4 +1,4 @@
# Autogenerated by Sphinx on Tue Feb 3 17:32:13 2026
# Autogenerated by Sphinx on Tue Apr 7 16:13:12 2026
# as part of the release process.
module_docs = {
+561 -327
View File
File diff suppressed because it is too large. Load diff
+10 -9
View File
@@ -351,15 +351,16 @@ def _args_from_interpreter_flags():
# -X options
if dev_mode:
args.extend(('-X', 'dev'))
for opt in ('faulthandler', 'tracemalloc', 'importtime',
'frozen_modules', 'showrefcount', 'utf8', 'gil'):
if opt in xoptions:
value = xoptions[opt]
if value is True:
arg = opt
else:
arg = '%s=%s' % (opt, value)
args.extend(('-X', arg))
for opt in sorted(xoptions):
if opt == 'dev':
# handled above via sys.flags.dev_mode
continue
value = xoptions[opt]
if value is True:
arg = opt
else:
arg = '%s=%s' % (opt, value)
args.extend(('-X', arg))
return args
+18 -18
View File
@@ -57,10 +57,11 @@ _bin_openflags = _text_openflags
if hasattr(_os, 'O_BINARY'):
_bin_openflags |= _os.O_BINARY
if hasattr(_os, 'TMP_MAX'):
TMP_MAX = _os.TMP_MAX
else:
TMP_MAX = 10000
# This is more than enough.
# Each name contains over 40 random bits. Even with a million temporary
# files, the chance of a conflict is less than 1 in a million, and with
# 20 attempts, it is less than 1e-120.
TMP_MAX = 20
# This variable _was_ unused for legacy reasons, see issue 10354.
# But as of 3.5 we actually use it at runtime so changing it would
@@ -196,8 +197,7 @@ def _get_default_tempdir(dirlist=None):
for dir in dirlist:
if dir != _os.curdir:
dir = _os.path.abspath(dir)
# Try only a few names per directory.
for seq in range(100):
for seq in range(TMP_MAX):
name = next(namer)
filename = _os.path.join(dir, name)
try:
@@ -213,10 +213,8 @@ def _get_default_tempdir(dirlist=None):
except FileExistsError:
pass
except PermissionError:
# This exception is thrown when a directory with the chosen name
# already exists on windows.
if (_os.name == 'nt' and _os.path.isdir(dir) and
_os.access(dir, _os.W_OK)):
# See the comment in mkdtemp().
if _os.name == 'nt' and _os.path.isdir(dir):
continue
break # no point trying more names in this directory
except OSError:
@@ -258,10 +256,8 @@ def _mkstemp_inner(dir, pre, suf, flags, output_type):
except FileExistsError:
continue # try again
except PermissionError:
# This exception is thrown when a directory with the chosen name
# already exists on windows.
if (_os.name == 'nt' and _os.path.isdir(dir) and
_os.access(dir, _os.W_OK)):
# See the comment in mkdtemp().
if _os.name == 'nt' and _os.path.isdir(dir) and seq < TMP_MAX - 1:
continue
else:
raise
@@ -386,10 +382,14 @@ def mkdtemp(suffix=None, prefix=None, dir=None):
except FileExistsError:
continue # try again
except PermissionError:
# This exception is thrown when a directory with the chosen name
# already exists on windows.
if (_os.name == 'nt' and _os.path.isdir(dir) and
_os.access(dir, _os.W_OK)):
# On Posix, this exception is raised when the user has no
# write access to the parent directory.
# On Windows, it is also raised when a directory with
# the chosen name already exists, or if the parent directory
# is not a directory.
# We cannot distinguish between "directory-exists-error" and
# "access-denied-error".
if _os.name == 'nt' and _os.path.isdir(dir) and seq < TMP_MAX - 1:
continue
else:
raise
+25
View File
@@ -646,6 +646,31 @@ class TestGetAnnotations(unittest.TestCase):
get_annotations(foo, format=Format.FORWARDREF, eval_str=True)
get_annotations(foo, format=Format.STRING, eval_str=True)
def test_eval_str_wrapped_cycle_self(self):
# gh-146556: self-referential __wrapped__ cycle must not hang.
def f(x: 'int') -> 'str': ...
f.__wrapped__ = f
# Cycle is detected and broken; globals from f itself are used.
result = get_annotations(f, eval_str=True)
self.assertEqual(result, {'x': int, 'return': str})
def test_eval_str_wrapped_cycle_mutual(self):
# gh-146556: mutual __wrapped__ cycle (a -> b -> a) must not hang.
def a(x: 'int'): ...
def b(): ...
a.__wrapped__ = b
b.__wrapped__ = a
result = get_annotations(a, eval_str=True)
self.assertEqual(result, {'x': int})
def test_eval_str_wrapped_chain_no_cycle(self):
# gh-146556: a valid (non-cyclic) __wrapped__ chain must still work.
def inner(x: 'int'): ...
def outer(x: 'int'): ...
outer.__wrapped__ = inner
result = get_annotations(outer, eval_str=True)
self.assertEqual(result, {'x': int})
def test_stock_annotations(self):
def foo(a: int, b: str):
pass
+21
View File
@@ -81,6 +81,27 @@ class StdStreamTest(unittest.TestCase):
self.assertRegex(mocked_stderr.getvalue(), r'usage:')
class TestArgumentParserPickleable(unittest.TestCase):
@force_not_colorized
def test_pickle_roundtrip(self):
import pickle
parser = argparse.ArgumentParser(exit_on_error=False)
parser.add_argument('--foo', type=int, default=42)
parser.add_argument('bar', nargs='?', default='baz')
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
with self.subTest(protocol=proto):
# Try to pickle and unpickle the parser
parser2 = pickle.loads(pickle.dumps(parser, protocol=proto))
# Check that the round-tripped parser still works
ns = parser2.parse_args(['--foo', '123', 'quux'])
self.assertEqual(ns.foo, 123)
self.assertEqual(ns.bar, 'quux')
ns2 = parser2.parse_args([])
self.assertEqual(ns2.foo, 42)
self.assertEqual(ns2.bar, 'baz')
class TestCase(unittest.TestCase):
def setUp(self):
+1 -2
View File
@@ -6234,8 +6234,7 @@ class TestSignatureDefinitions(unittest.TestCase):
def test_os_module_has_signatures(self):
unsupported_signature = {'chmod', 'utime'}
unsupported_signature |= {name for name in
['get_terminal_size', 'link', 'posix_spawn', 'posix_spawnp',
'register_at_fork', 'startfile']
['get_terminal_size', 'link', 'register_at_fork', 'startfile']
if hasattr(os, name)}
self._test_module_has_signatures(os, unsupported_signature=unsupported_signature)
+25 -6
View File
@@ -330,17 +330,36 @@ def _mock_candidate_names(*names):
class TestBadTempdir:
def test_read_only_directory(self):
with _inside_empty_temp_dir():
oldmode = mode = os.stat(tempfile.tempdir).st_mode
mode &= ~(stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH)
os.chmod(tempfile.tempdir, mode)
probe = os.path.join(tempfile.tempdir, 'probe')
if os.name == 'nt':
cmd = ['icacls', tempfile.tempdir, '/deny', 'Everyone:(W)']
stdout = None if support.verbose > 1 else subprocess.DEVNULL
subprocess.run(cmd, check=True, stdout=stdout)
else:
oldmode = mode = os.stat(tempfile.tempdir).st_mode
mode &= ~(stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH)
mode = stat.S_IREAD
os.chmod(tempfile.tempdir, mode)
try:
if os.access(tempfile.tempdir, os.W_OK):
# Check that the directory is read-only.
try:
os.mkdir(probe)
except PermissionError:
pass
else:
os.rmdir(probe)
self.skipTest("can't set the directory read-only")
# gh-66305: Now it takes a split second, but previously
# it took about 10 days on Windows.
with self.assertRaises(PermissionError):
self.make_temp()
self.assertEqual(os.listdir(tempfile.tempdir), [])
finally:
os.chmod(tempfile.tempdir, oldmode)
if os.name == 'nt':
cmd = ['icacls', tempfile.tempdir, '/grant:r', 'Everyone:(M)']
subprocess.run(cmd, check=True, stdout=stdout)
else:
os.chmod(tempfile.tempdir, oldmode)
self.assertEqual(os.listdir(tempfile.tempdir), [])
def test_nonexisting_directory(self):
with _inside_empty_temp_dir():
+4 -2
View File
@@ -1,6 +1,6 @@
"""Parse (absolute and relative) URLs.
urlparse module is based upon the following RFC specifications.
urllib.parse module is based upon the following RFC specifications.
RFC 3986 (STD66): "Uniform Resource Identifiers" by T. Berners-Lee, R. Fielding
and L. Masinter, January 2005.
@@ -20,7 +20,7 @@ RFC 1738: "Uniform Resource Locators (URL)" by T. Berners-Lee, L. Masinter, M.
McCahill, December 1994
RFC 3986 is considered the current standard and any future changes to
urlparse module should conform with it. The urlparse module is
urllib.parse module should conform with it. The urllib.parse module is
currently not entirely compliant with this RFC due to defacto
scenarios for parsing, and for backward compatibility purposes, some
parsing quirks from older RFCs are retained. The testcases in
@@ -390,6 +390,8 @@ def urlparse(url, scheme='', allow_fragments=True):
path or query.
Note that % escapes are not expanded.
urlsplit() should generally be used instead of urlparse().
"""
url, scheme, _coerce_result = _coerce_args(url, scheme)
scheme, netloc, url, params, query, fragment = _urlparse(url, scheme, allow_fragments)
+1 -1
View File
@@ -950,7 +950,7 @@ class ZipExtFile(io.BufferedIOBase):
"""
# Max size supported by decompressor.
MAX_N = 1 << 31 - 1
MAX_N = (1 << 31) - 1
# Read from compressed files in 4k blocks.
MIN_READ_SIZE = 4096