diff --git a/.github/workflows/CI_build.yml b/.github/workflows/CI_build.yml
index 7f5dd7bb..7bff746d 100644
--- a/.github/workflows/CI_build.yml
+++ b/.github/workflows/CI_build.yml
@@ -44,9 +44,9 @@ jobs:
if: matrix.build_configuration == 'Release'
working-directory: installer
run: |
- $env:PYTHONBUILDDIR_ARM64='${{ github.workspace }}\packages\pythonarm64.3.14.3\tools'
- $env:PYTHONBUILDDIR_X64='${{ github.workspace }}\packages\python.3.14.3\tools'
- $env:PYTHONBUILDDIR='${{ github.workspace }}\packages\pythonx86.3.14.3\tools'
+ $env:PYTHONBUILDDIR_ARM64='${{ github.workspace }}\packages\pythonarm64.3.14.4\tools'
+ $env:PYTHONBUILDDIR_X64='${{ github.workspace }}\packages\python.3.14.4\tools'
+ $env:PYTHONBUILDDIR='${{ github.workspace }}\packages\pythonx86.3.14.4\tools'
Rename-Item -Path ".\buildPaths.bat.orig" -NewName "buildPaths.bat"
- dotnet tool install --global wix --version 6.0.2
+ dotnet tool install --global wix --version 7.0.0
.\buildAll.bat ${{ matrix.build_platform }}
diff --git a/PythonLib/full/_py_warnings.py b/PythonLib/full/_py_warnings.py
index 55f8c069..36513ba2 100644
--- a/PythonLib/full/_py_warnings.py
+++ b/PythonLib/full/_py_warnings.py
@@ -647,8 +647,8 @@ def __enter__(self):
context = None
self._filters = self._module.filters
self._module.filters = self._filters[:]
- self._showwarning = self._module.showwarning
self._showwarnmsg_impl = self._module._showwarnmsg_impl
+ self._showwarning = self._module.showwarning
self._module._filters_mutated_lock_held()
if self._record:
if _use_context:
@@ -656,9 +656,9 @@ def __enter__(self):
else:
log = []
self._module._showwarnmsg_impl = log.append
- # Reset showwarning() to the default implementation to make sure
- # that _showwarnmsg() calls _showwarnmsg_impl()
- self._module.showwarning = self._module._showwarning_orig
+ # Reset showwarning() to the default implementation to make sure
+ # that _showwarnmsg() calls _showwarnmsg_impl()
+ self._module.showwarning = self._module._showwarning_orig
else:
log = None
if self._filter is not None:
@@ -673,8 +673,8 @@ def __exit__(self, *exc_info):
self._module._warnings_context.set(self._saved_context)
else:
self._module.filters = self._filters
- self._module.showwarning = self._showwarning
self._module._showwarnmsg_impl = self._showwarnmsg_impl
+ self._module.showwarning = self._showwarning
self._module._filters_mutated_lock_held()
diff --git a/PythonLib/full/_pyrepl/windows_console.py b/PythonLib/full/_pyrepl/windows_console.py
index bc137ead..46c60307 100644
--- a/PythonLib/full/_pyrepl/windows_console.py
+++ b/PythonLib/full/_pyrepl/windows_console.py
@@ -283,18 +283,13 @@ def __write_changed_line(
self._erase_to_end()
self.__write(newline[x_pos:])
- if wlen(newline) == self.width:
- # If we wrapped we want to start at the next line
- self._move_relative(0, y + 1)
- self.posxy = 0, y + 1
- else:
- self.posxy = wlen(newline), y
+ self.posxy = min(wlen(newline), self.width - 1), y
- if "\x1b" in newline or y != self.posxy[1] or '\x1a' in newline:
- # ANSI escape characters are present, so we can't assume
- # anything about the position of the cursor. Moving the cursor
- # to the left margin should work to get to a known position.
- self.move_cursor(0, y)
+ if "\x1b" in newline or y != self.posxy[1] or '\x1a' in newline:
+ # ANSI escape characters are present, so we can't assume
+ # anything about the position of the cursor. Moving the cursor
+ # to the left margin should work to get to a known position.
+ self.move_cursor(0, y)
def _scroll(
self, top: int, bottom: int, left: int | None = None, right: int | None = None
diff --git a/PythonLib/full/annotationlib.py b/PythonLib/full/annotationlib.py
index 832d160d..9fee2564 100644
--- a/PythonLib/full/annotationlib.py
+++ b/PythonLib/full/annotationlib.py
@@ -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__"):
diff --git a/PythonLib/full/argparse.py b/PythonLib/full/argparse.py
index 1d7d34f9..8cf85694 100644
--- a/PythonLib/full/argparse.py
+++ b/PythonLib/full/argparse.py
@@ -149,6 +149,10 @@ def _copy_items(items):
return copy.copy(items)
+def _identity(value):
+ return value
+
+
# ===============
# Formatting Help
# ===============
@@ -200,7 +204,7 @@ def _set_color(self, color):
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 @@ def __init__(self,
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)
diff --git a/PythonLib/full/asyncio/base_events.py b/PythonLib/full/asyncio/base_events.py
index 8cbb71f7..b83b8418 100644
--- a/PythonLib/full/asyncio/base_events.py
+++ b/PythonLib/full/asyncio/base_events.py
@@ -1345,6 +1345,17 @@ async def start_tls(self, transport, protocol, sslcontext, *,
# have a chance to get called before "ssl_protocol.connection_made()".
transport.pause_reading()
+ # gh-142352: move buffered StreamReader data to SSLProtocol
+ if server_side:
+ from .streams import StreamReaderProtocol
+ if isinstance(protocol, StreamReaderProtocol):
+ stream_reader = getattr(protocol, '_stream_reader', None)
+ if stream_reader is not None:
+ buffer = stream_reader._buffer
+ if buffer:
+ ssl_protocol._incoming.write(buffer)
+ buffer.clear()
+
transport.set_protocol(ssl_protocol)
conmade_cb = self.call_soon(ssl_protocol.connection_made, transport)
resume_cb = self.call_soon(transport.resume_reading)
diff --git a/PythonLib/full/asyncio/base_subprocess.py b/PythonLib/full/asyncio/base_subprocess.py
index 321a4e5d..224b1883 100644
--- a/PythonLib/full/asyncio/base_subprocess.py
+++ b/PythonLib/full/asyncio/base_subprocess.py
@@ -265,7 +265,7 @@ def _try_finish(self):
# to avoid hanging forever in self._wait as otherwise _exit_waiters
# would never be woken up, we wake them up here.
for waiter in self._exit_waiters:
- if not waiter.cancelled():
+ if not waiter.done():
waiter.set_result(self._returncode)
if all(p is not None and p.disconnected
for p in self._pipes.values()):
@@ -278,7 +278,7 @@ def _call_connection_lost(self, exc):
finally:
# wake up futures waiting for wait()
for waiter in self._exit_waiters:
- if not waiter.cancelled():
+ if not waiter.done():
waiter.set_result(self._returncode)
self._exit_waiters = None
self._loop = None
diff --git a/PythonLib/full/asyncio/queues.py b/PythonLib/full/asyncio/queues.py
index 084fccaa..756216fa 100644
--- a/PythonLib/full/asyncio/queues.py
+++ b/PythonLib/full/asyncio/queues.py
@@ -37,7 +37,7 @@ class Queue(mixins._LoopBoundMixin):
is an integer greater than 0, then "await put()" will block when the
queue reaches maxsize, until an item is removed by get().
- Unlike the standard library Queue, you can reliably know this Queue's size
+ Unlike queue.Queue, you can reliably know this Queue's size
with qsize(), since your single-threaded asyncio application won't be
interrupted between calling qsize() and doing an operation on the Queue.
"""
diff --git a/PythonLib/full/asyncio/windows_utils.py b/PythonLib/full/asyncio/windows_utils.py
index ef277fac..acd49441 100644
--- a/PythonLib/full/asyncio/windows_utils.py
+++ b/PythonLib/full/asyncio/windows_utils.py
@@ -10,7 +10,6 @@
import msvcrt
import os
import subprocess
-import tempfile
import warnings
@@ -24,6 +23,7 @@
PIPE = subprocess.PIPE
STDOUT = subprocess.STDOUT
_mmap_counter = itertools.count()
+_MAX_PIPE_ATTEMPTS = 20
# Replacement for os.pipe() using handles instead of fds
@@ -31,10 +31,6 @@
def pipe(*, duplex=False, overlapped=(True, True), bufsize=BUFSIZE):
"""Like os.pipe() but with overlapped support and using handles not fds."""
- address = tempfile.mktemp(
- prefix=r'\\.\pipe\python-pipe-{:d}-{:d}-'.format(
- os.getpid(), next(_mmap_counter)))
-
if duplex:
openmode = _winapi.PIPE_ACCESS_DUPLEX
access = _winapi.GENERIC_READ | _winapi.GENERIC_WRITE
@@ -56,9 +52,20 @@ def pipe(*, duplex=False, overlapped=(True, True), bufsize=BUFSIZE):
h1 = h2 = None
try:
- h1 = _winapi.CreateNamedPipe(
- address, openmode, _winapi.PIPE_WAIT,
- 1, obsize, ibsize, _winapi.NMPWAIT_WAIT_FOREVER, _winapi.NULL)
+ for attempts in itertools.count():
+ address = r'\\.\pipe\python-pipe-{:d}-{:d}-{}'.format(
+ os.getpid(), next(_mmap_counter), os.urandom(8).hex())
+ try:
+ h1 = _winapi.CreateNamedPipe(
+ address, openmode, _winapi.PIPE_WAIT,
+ 1, obsize, ibsize, _winapi.NMPWAIT_WAIT_FOREVER, _winapi.NULL)
+ break
+ except OSError as e:
+ if attempts >= _MAX_PIPE_ATTEMPTS:
+ raise
+ if e.winerror not in (_winapi.ERROR_PIPE_BUSY,
+ _winapi.ERROR_ACCESS_DENIED):
+ raise
h2 = _winapi.CreateFile(
address, access, 0, _winapi.NULL, _winapi.OPEN_EXISTING,
diff --git a/PythonLib/full/ctypes/__init__.py b/PythonLib/full/ctypes/__init__.py
index 04ec0270..7223fc49 100644
--- a/PythonLib/full/ctypes/__init__.py
+++ b/PythonLib/full/ctypes/__init__.py
@@ -470,6 +470,8 @@ def _load_library(self, name, mode, handle, winmode):
if name and name.endswith(")") and ".a(" in name:
mode |= _os.RTLD_MEMBER | _os.RTLD_NOW
self._name = name
+ if handle is not None:
+ return handle
return _dlopen(name, mode)
def __repr__(self):
diff --git a/PythonLib/full/ctypes/util.py b/PythonLib/full/ctypes/util.py
index 378f1216..3b216584 100644
--- a/PythonLib/full/ctypes/util.py
+++ b/PythonLib/full/ctypes/util.py
@@ -85,15 +85,10 @@ def find_library(name):
wintypes.DWORD,
)
- _psapi = ctypes.WinDLL('psapi', use_last_error=True)
- _enum_process_modules = _psapi["EnumProcessModules"]
- _enum_process_modules.restype = wintypes.BOOL
- _enum_process_modules.argtypes = (
- wintypes.HANDLE,
- ctypes.POINTER(wintypes.HMODULE),
- wintypes.DWORD,
- wintypes.LPDWORD,
- )
+ # gh-145307: We defer loading psapi.dll until _get_module_handles is called.
+ # Loading additional DLLs at startup for functionality that may never be
+ # used is wasteful.
+ _enum_process_modules = None
def _get_module_filename(module: wintypes.HMODULE):
name = (wintypes.WCHAR * 32767)() # UNICODE_STRING_MAX_CHARS
@@ -101,8 +96,19 @@ def _get_module_filename(module: wintypes.HMODULE):
return name.value
return None
-
def _get_module_handles():
+ global _enum_process_modules
+ if _enum_process_modules is None:
+ _psapi = ctypes.WinDLL('psapi', use_last_error=True)
+ _enum_process_modules = _psapi["EnumProcessModules"]
+ _enum_process_modules.restype = wintypes.BOOL
+ _enum_process_modules.argtypes = (
+ wintypes.HANDLE,
+ ctypes.POINTER(wintypes.HMODULE),
+ wintypes.DWORD,
+ wintypes.LPDWORD,
+ )
+
process = _get_current_process()
space_needed = wintypes.DWORD()
n = 1024
diff --git a/PythonLib/full/email/_header_value_parser.py b/PythonLib/full/email/_header_value_parser.py
index 51727688..03fedd99 100644
--- a/PythonLib/full/email/_header_value_parser.py
+++ b/PythonLib/full/email/_header_value_parser.py
@@ -80,7 +80,8 @@
# Useful constants and functions
#
-WSP = set(' \t')
+_WSP = ' \t'
+WSP = set(_WSP)
CFWS_LEADER = WSP | set('(')
SPECIALS = set(r'()<>@,:;.\"[]')
ATOM_ENDS = SPECIALS | WSP
@@ -2831,6 +2832,7 @@ def _steal_trailing_WSP_if_exists(lines):
lines.pop()
return wsp
+
def _refold_parse_tree(parse_tree, *, policy):
"""Return string of contents of parse_tree folded according to RFC rules.
@@ -2839,11 +2841,9 @@ def _refold_parse_tree(parse_tree, *, policy):
maxlen = policy.max_line_length or sys.maxsize
encoding = 'utf-8' if policy.utf8 else 'us-ascii'
lines = [''] # Folded lines to be output
- leading_whitespace = '' # When we have whitespace between two encoded
- # words, we may need to encode the whitespace
- # at the beginning of the second word.
- last_ew = None # Points to the last encoded character if there's an ew on
- # the line
+ last_word_is_ew = False
+ last_ew = None # if there is an encoded word in the last line of lines,
+ # points to the encoded word's first character
last_charset = None
wrap_as_ew_blocked = 0
want_encoding = False # This is set to True if we need to encode this part
@@ -2878,6 +2878,7 @@ def _refold_parse_tree(parse_tree, *, policy):
if part.token_type == 'mime-parameters':
# Mime parameter folding (using RFC2231) is extra special.
_fold_mime_parameters(part, lines, maxlen, encoding)
+ last_word_is_ew = False
continue
if want_encoding and not wrap_as_ew_blocked:
@@ -2894,6 +2895,7 @@ def _refold_parse_tree(parse_tree, *, policy):
# XXX what if encoded_part has no leading FWS?
lines.append(newline)
lines[-1] += encoded_part
+ last_word_is_ew = False
continue
# Either this is not a major syntactic break, so we don't
# want it on a line by itself even if it fits, or it
@@ -2912,11 +2914,16 @@ def _refold_parse_tree(parse_tree, *, policy):
(last_charset == 'unknown-8bit' or
last_charset == 'utf-8' and charset != 'us-ascii')):
last_ew = None
- last_ew = _fold_as_ew(tstr, lines, maxlen, last_ew,
- part.ew_combine_allowed, charset, leading_whitespace)
- # This whitespace has been added to the lines in _fold_as_ew()
- # so clear it now.
- leading_whitespace = ''
+ last_ew = _fold_as_ew(
+ tstr,
+ lines,
+ maxlen,
+ last_ew,
+ part.ew_combine_allowed,
+ charset,
+ last_word_is_ew,
+ )
+ last_word_is_ew = True
last_charset = charset
want_encoding = False
continue
@@ -2929,28 +2936,19 @@ def _refold_parse_tree(parse_tree, *, policy):
if len(tstr) <= maxlen - len(lines[-1]):
lines[-1] += tstr
+ last_word_is_ew = last_word_is_ew and not bool(tstr.strip(_WSP))
continue
# This part is too long to fit. The RFC wants us to break at
# "major syntactic breaks", so unless we don't consider this
# to be one, check if it will fit on the next line by itself.
- leading_whitespace = ''
if (part.syntactic_break and
len(tstr) + 1 <= maxlen):
newline = _steal_trailing_WSP_if_exists(lines)
if newline or part.startswith_fws():
- # We're going to fold the data onto a new line here. Due to
- # the way encoded strings handle continuation lines, we need to
- # be prepared to encode any whitespace if the next line turns
- # out to start with an encoded word.
lines.append(newline + tstr)
-
- whitespace_accumulator = []
- for char in lines[-1]:
- if char not in WSP:
- break
- whitespace_accumulator.append(char)
- leading_whitespace = ''.join(whitespace_accumulator)
+ last_word_is_ew = (last_word_is_ew
+ and not bool(lines[-1].strip(_WSP)))
last_ew = None
continue
if not hasattr(part, 'encode'):
@@ -2990,10 +2988,11 @@ def _refold_parse_tree(parse_tree, *, policy):
else:
# We can't fold it onto the next line either...
lines[-1] += tstr
+ last_word_is_ew = last_word_is_ew and not bool(tstr.strip(_WSP))
return policy.linesep.join(lines) + policy.linesep
-def _fold_as_ew(to_encode, lines, maxlen, last_ew, ew_combine_allowed, charset, leading_whitespace):
+def _fold_as_ew(to_encode, lines, maxlen, last_ew, ew_combine_allowed, charset, last_word_is_ew):
"""Fold string to_encode into lines as encoded word, combining if allowed.
Return the new value for last_ew, or None if ew_combine_allowed is False.
@@ -3008,6 +3007,16 @@ def _fold_as_ew(to_encode, lines, maxlen, last_ew, ew_combine_allowed, charset,
to_encode = str(
get_unstructured(lines[-1][last_ew:] + to_encode))
lines[-1] = lines[-1][:last_ew]
+ elif last_word_is_ew:
+ # If we are following up an encoded word with another encoded word,
+ # any white space between the two will be ignored when decoded.
+ # Therefore, we encode all to-be-displayed whitespace in the second
+ # encoded word.
+ len_without_wsp = len(lines[-1].rstrip(_WSP))
+ leading_whitespace = lines[-1][len_without_wsp:]
+ lines[-1] = (lines[-1][:len_without_wsp]
+ + (' ' if leading_whitespace else ''))
+ to_encode = leading_whitespace + to_encode
elif to_encode[0] in WSP:
# We're joining this to non-encoded text, so don't encode
# the leading blank.
@@ -3036,20 +3045,13 @@ def _fold_as_ew(to_encode, lines, maxlen, last_ew, ew_combine_allowed, charset,
while to_encode:
remaining_space = maxlen - len(lines[-1])
- text_space = remaining_space - chrome_len - len(leading_whitespace)
+ text_space = remaining_space - chrome_len
if text_space <= 0:
- lines.append(' ')
+ newline = _steal_trailing_WSP_if_exists(lines)
+ lines.append(newline or ' ')
+ new_last_ew = len(lines[-1])
continue
- # If we are at the start of a continuation line, prepend whitespace
- # (we only want to do this when the line starts with an encoded word
- # but if we're folding in this helper function, then we know that we
- # are going to be writing out an encoded word.)
- if len(lines) > 1 and len(lines[-1]) == 1 and leading_whitespace:
- encoded_word = _ew.encode(leading_whitespace, charset=encode_as)
- lines[-1] += encoded_word
- leading_whitespace = ''
-
to_encode_word = to_encode[:text_space]
encoded_word = _ew.encode(to_encode_word, charset=encode_as)
excess = len(encoded_word) - remaining_space
@@ -3061,7 +3063,6 @@ def _fold_as_ew(to_encode, lines, maxlen, last_ew, ew_combine_allowed, charset,
excess = len(encoded_word) - remaining_space
lines[-1] += encoded_word
to_encode = to_encode[len(to_encode_word):]
- leading_whitespace = ''
if to_encode:
lines.append(' ')
diff --git a/PythonLib/full/encodings/__init__.py b/PythonLib/full/encodings/__init__.py
index 298177eb..8548bbe0 100644
--- a/PythonLib/full/encodings/__init__.py
+++ b/PythonLib/full/encodings/__init__.py
@@ -33,6 +33,7 @@
from . import aliases
_cache = {}
+_MAXCACHE = 500
_unknown = '--unknown--'
_import_tail = ['*']
_aliases = aliases.aliases
@@ -115,6 +116,8 @@ def search_function(encoding):
if mod is None:
# Cache misses
+ if len(_cache) >= _MAXCACHE:
+ _cache.clear()
_cache[encoding] = None
return None
@@ -136,6 +139,8 @@ def search_function(encoding):
entry = codecs.CodecInfo(*entry)
# Cache the codec registry entry
+ if len(_cache) >= _MAXCACHE:
+ _cache.clear()
_cache[encoding] = entry
# Register its aliases (without overwriting previously registered
diff --git a/PythonLib/full/ensurepip/__init__.py b/PythonLib/full/ensurepip/__init__.py
index 21bbfad0..715389ea 100644
--- a/PythonLib/full/ensurepip/__init__.py
+++ b/PythonLib/full/ensurepip/__init__.py
@@ -10,13 +10,14 @@
__all__ = ["version", "bootstrap"]
-_PIP_VERSION = "25.3"
+_PIP_VERSION = "26.0.1"
# Directory of system wheel packages. Some Linux distribution packaging
# policies recommend against bundling dependencies. For example, Fedora
# installs wheel packages in the /usr/share/python-wheels/ directory and don't
# install the ensurepip._bundled package.
-if (_pkg_dir := sysconfig.get_config_var('WHEEL_PKG_DIR')) is not None:
+_pkg_dir = sysconfig.get_config_var('WHEEL_PKG_DIR')
+if _pkg_dir:
_WHEEL_PKG_DIR = Path(_pkg_dir).resolve()
else:
_WHEEL_PKG_DIR = None
diff --git a/PythonLib/full/ensurepip/_bundled/pip-25.3-py3-none-any.whl b/PythonLib/full/ensurepip/_bundled/pip-26.0.1-py3-none-any.whl
similarity index 70%
rename from PythonLib/full/ensurepip/_bundled/pip-25.3-py3-none-any.whl
rename to PythonLib/full/ensurepip/_bundled/pip-26.0.1-py3-none-any.whl
index 755e1aa0..580d09a9 100644
Binary files a/PythonLib/full/ensurepip/_bundled/pip-25.3-py3-none-any.whl and b/PythonLib/full/ensurepip/_bundled/pip-26.0.1-py3-none-any.whl differ
diff --git a/PythonLib/full/glob.py b/PythonLib/full/glob.py
index f1a87c82..7ce3998c 100644
--- a/PythonLib/full/glob.py
+++ b/PythonLib/full/glob.py
@@ -15,7 +15,7 @@
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)
diff --git a/PythonLib/full/http/cookies.py b/PythonLib/full/http/cookies.py
index e0e2cd4b..d5b8ba93 100644
--- a/PythonLib/full/http/cookies.py
+++ b/PythonLib/full/http/cookies.py
@@ -337,9 +337,16 @@ def update(self, values):
key = key.lower()
if key not in self._reserved:
raise CookieError("Invalid attribute %r" % (key,))
+ if _has_control_character(key, val):
+ raise CookieError("Control characters are not allowed in "
+ f"cookies {key!r} {val!r}")
data[key] = val
dict.update(self, data)
+ def __ior__(self, values):
+ self.update(values)
+ return self
+
def isReservedKey(self, K):
return K.lower() in self._reserved
@@ -365,9 +372,15 @@ def __getstate__(self):
}
def __setstate__(self, state):
- self._key = state['key']
- self._value = state['value']
- self._coded_value = state['coded_value']
+ key = state['key']
+ value = state['value']
+ coded_value = state['coded_value']
+ if _has_control_character(key, value, coded_value):
+ raise CookieError("Control characters are not allowed in cookies "
+ f"{key!r} {value!r} {coded_value!r}")
+ self._key = key
+ self._value = value
+ self._coded_value = coded_value
def output(self, attrs=None, header="Set-Cookie:"):
return "%s %s" % (header, self.OutputString(attrs))
@@ -379,13 +392,16 @@ def __repr__(self):
def js_output(self, attrs=None):
# Print javascript
+ output_string = self.OutputString(attrs)
+ if _has_control_character(output_string):
+ raise CookieError("Control characters are not allowed in cookies")
return """
- """ % (self.OutputString(attrs).replace('"', r'\"'))
+ """ % (output_string.replace('"', r'\"'))
def OutputString(self, attrs=None):
# Build up our result
diff --git a/PythonLib/full/importlib/_bootstrap.py b/PythonLib/full/importlib/_bootstrap.py
index 499da1e0..9d911e1d 100644
--- a/PythonLib/full/importlib/_bootstrap.py
+++ b/PythonLib/full/importlib/_bootstrap.py
@@ -1375,6 +1375,14 @@ def _find_and_load(name, import_):
# NOTE: because of this, initializing must be set *before*
# putting the new module in sys.modules.
_lock_unlock_module(name)
+ else:
+ # Verify the module is still in sys.modules. Another thread may have
+ # removed it (due to import failure) between our sys.modules.get()
+ # above and the _initializing check. If removed, we retry the import
+ # to preserve normal semantics: the caller gets the exception from
+ # the actual import failure rather than a synthetic error.
+ if sys.modules.get(name) is not module:
+ return _find_and_load(name, import_)
if module is None:
message = f'import of {name} halted; None in sys.modules'
diff --git a/PythonLib/full/importlib/_bootstrap_external.py b/PythonLib/full/importlib/_bootstrap_external.py
index 95ce14b2..6a828ae7 100644
--- a/PythonLib/full/importlib/_bootstrap_external.py
+++ b/PythonLib/full/importlib/_bootstrap_external.py
@@ -946,7 +946,7 @@ def get_filename(self, fullname):
def get_data(self, path):
"""Return the data from path as raw bytes."""
- if isinstance(self, (SourceLoader, ExtensionFileLoader)):
+ if isinstance(self, (SourceLoader, SourcelessFileLoader, ExtensionFileLoader)):
with _io.open_code(str(path)) as file:
return file.read()
else:
diff --git a/PythonLib/full/inspect.py b/PythonLib/full/inspect.py
index 3cee85f3..2d229051 100644
--- a/PythonLib/full/inspect.py
+++ b/PythonLib/full/inspect.py
@@ -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')
diff --git a/PythonLib/full/multiprocessing/connection.py b/PythonLib/full/multiprocessing/connection.py
index fc00d286..a6e1b0c7 100644
--- a/PythonLib/full/multiprocessing/connection.py
+++ b/PythonLib/full/multiprocessing/connection.py
@@ -46,6 +46,7 @@
CONNECTION_TIMEOUT = 20.
_mmap_counter = itertools.count()
+_MAX_PIPE_ATTEMPTS = 100
default_family = 'AF_INET'
families = ['AF_INET']
@@ -78,8 +79,8 @@ def arbitrary_address(family):
elif family == 'AF_UNIX':
return tempfile.mktemp(prefix='sock-', dir=util.get_temp_dir())
elif family == 'AF_PIPE':
- return tempfile.mktemp(prefix=r'\\.\pipe\pyc-%d-%d-' %
- (os.getpid(), next(_mmap_counter)), dir="")
+ return (r'\\.\pipe\pyc-%d-%d-%s' %
+ (os.getpid(), next(_mmap_counter), os.urandom(8).hex()))
else:
raise ValueError('unrecognized family')
@@ -472,17 +473,29 @@ class Listener(object):
def __init__(self, address=None, family=None, backlog=1, authkey=None):
family = family or (address and address_type(address)) \
or default_family
- address = address or arbitrary_address(family)
-
_validate_family(family)
+ if authkey is not None and not isinstance(authkey, bytes):
+ raise TypeError('authkey should be a byte string')
+
if family == 'AF_PIPE':
- self._listener = PipeListener(address, backlog)
+ if address:
+ self._listener = PipeListener(address, backlog)
+ else:
+ for attempts in itertools.count():
+ address = arbitrary_address(family)
+ try:
+ self._listener = PipeListener(address, backlog)
+ break
+ except OSError as e:
+ if attempts >= _MAX_PIPE_ATTEMPTS:
+ raise
+ if e.winerror not in (_winapi.ERROR_PIPE_BUSY,
+ _winapi.ERROR_ACCESS_DENIED):
+ raise
else:
+ address = address or arbitrary_address(family)
self._listener = SocketListener(address, family, backlog)
- if authkey is not None and not isinstance(authkey, bytes):
- raise TypeError('authkey should be a byte string')
-
self._authkey = authkey
def accept(self):
@@ -570,7 +583,6 @@ def Pipe(duplex=True):
'''
Returns pair of connection objects at either end of a pipe
'''
- address = arbitrary_address('AF_PIPE')
if duplex:
openmode = _winapi.PIPE_ACCESS_DUPLEX
access = _winapi.GENERIC_READ | _winapi.GENERIC_WRITE
@@ -580,15 +592,25 @@ def Pipe(duplex=True):
access = _winapi.GENERIC_WRITE
obsize, ibsize = 0, BUFSIZE
- h1 = _winapi.CreateNamedPipe(
- address, openmode | _winapi.FILE_FLAG_OVERLAPPED |
- _winapi.FILE_FLAG_FIRST_PIPE_INSTANCE,
- _winapi.PIPE_TYPE_MESSAGE | _winapi.PIPE_READMODE_MESSAGE |
- _winapi.PIPE_WAIT,
- 1, obsize, ibsize, _winapi.NMPWAIT_WAIT_FOREVER,
- # default security descriptor: the handle cannot be inherited
- _winapi.NULL
- )
+ for attempts in itertools.count():
+ address = arbitrary_address('AF_PIPE')
+ try:
+ h1 = _winapi.CreateNamedPipe(
+ address, openmode | _winapi.FILE_FLAG_OVERLAPPED |
+ _winapi.FILE_FLAG_FIRST_PIPE_INSTANCE,
+ _winapi.PIPE_TYPE_MESSAGE | _winapi.PIPE_READMODE_MESSAGE |
+ _winapi.PIPE_WAIT,
+ 1, obsize, ibsize, _winapi.NMPWAIT_WAIT_FOREVER,
+ # default security descriptor: the handle cannot be inherited
+ _winapi.NULL
+ )
+ break
+ except OSError as e:
+ if attempts >= _MAX_PIPE_ATTEMPTS:
+ raise
+ if e.winerror not in (_winapi.ERROR_PIPE_BUSY,
+ _winapi.ERROR_ACCESS_DENIED):
+ raise
h2 = _winapi.CreateFile(
address, access, 0, _winapi.NULL, _winapi.OPEN_EXISTING,
_winapi.FILE_FLAG_OVERLAPPED, _winapi.NULL
diff --git a/PythonLib/full/multiprocessing/context.py b/PythonLib/full/multiprocessing/context.py
index 051d567d..5fa6d7e4 100644
--- a/PythonLib/full/multiprocessing/context.py
+++ b/PythonLib/full/multiprocessing/context.py
@@ -145,7 +145,13 @@ def freeze_support(self):
'''Check whether this is a fake forked process in a frozen executable.
If so then run code specified by commandline and exit.
'''
- if self.get_start_method() == 'spawn' and getattr(sys, 'frozen', False):
+ # gh-140814: allow_none=True avoids locking in the default start
+ # method, which would cause a later set_start_method() to fail.
+ # None is safe to pass through: spawn.freeze_support()
+ # independently detects whether this process is a spawned
+ # child, so the start method check here is only an optimization.
+ if (getattr(sys, 'frozen', False)
+ and self.get_start_method(allow_none=True) in ('spawn', None)):
from .spawn import freeze_support
freeze_support()
diff --git a/PythonLib/full/multiprocessing/forkserver.py b/PythonLib/full/multiprocessing/forkserver.py
index 15c455a5..e431b3f1 100644
--- a/PythonLib/full/multiprocessing/forkserver.py
+++ b/PythonLib/full/multiprocessing/forkserver.py
@@ -142,10 +142,17 @@ def ensure_running(self):
self._forkserver_alive_fd = None
self._forkserver_pid = None
- cmd = ('from multiprocessing.forkserver import main; ' +
- 'main(%d, %d, %r, **%r)')
+ # gh-144503: sys_argv is passed as real argv elements after the
+ # ``-c cmd`` rather than repr'd into main_kws so that a large
+ # parent sys.argv cannot push the single ``-c`` command string
+ # over the OS per-argument length limit (MAX_ARG_STRLEN on Linux).
+ # The child sees them as sys.argv[1:].
+ cmd = ('import sys; '
+ 'from multiprocessing.forkserver import main; '
+ 'main(%d, %d, %r, sys_argv=sys.argv[1:], **%r)')
main_kws = {}
+ sys_argv = None
if self._preload_modules:
data = spawn.get_preparation_data('ignore')
if 'sys_path' in data:
@@ -153,7 +160,7 @@ def ensure_running(self):
if 'init_main_from_path' in data:
main_kws['main_path'] = data['init_main_from_path']
if 'sys_argv' in data:
- main_kws['sys_argv'] = data['sys_argv']
+ sys_argv = data['sys_argv']
with socket.socket(socket.AF_UNIX) as listener:
address = connection.arbitrary_address('AF_UNIX')
@@ -175,6 +182,8 @@ def ensure_running(self):
exe = spawn.get_executable()
args = [exe] + util._args_from_interpreter_flags()
args += ['-c', cmd]
+ if sys_argv is not None:
+ args += sys_argv
pid = util.spawnv_passfds(exe, args, fds_to_pass)
except:
os.close(alive_w)
diff --git a/PythonLib/full/plistlib.py b/PythonLib/full/plistlib.py
index 5b2b4e42..c3aee1e1 100644
--- a/PythonLib/full/plistlib.py
+++ b/PythonLib/full/plistlib.py
@@ -21,7 +21,7 @@
Generate Plist example:
- import datetime
+ import datetime as dt
import plistlib
pl = dict(
@@ -37,7 +37,7 @@
),
someData = b"",
someMoreData = b"" * 10,
- aDate = datetime.datetime.now()
+ aDate = dt.datetime.now()
)
print(plistlib.dumps(pl).decode())
diff --git a/PythonLib/full/pydoc_data/module_docs.py b/PythonLib/full/pydoc_data/module_docs.py
index 2a6ede3a..d6583783 100644
--- a/PythonLib/full/pydoc_data/module_docs.py
+++ b/PythonLib/full/pydoc_data/module_docs.py
@@ -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 = {
diff --git a/PythonLib/full/pydoc_data/topics.py b/PythonLib/full/pydoc_data/topics.py
index 4e31cf08..6dca99ce 100644
--- a/PythonLib/full/pydoc_data/topics.py
+++ b/PythonLib/full/pydoc_data/topics.py
@@ -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.
topics = {
@@ -46,11 +46,10 @@
| "[" [target_list] "]"
| attributeref
| subscription
- | slicing
| "*" target
-(See section Primaries for the syntax definitions for *attributeref*,
-*subscription*, and *slicing*.)
+(See section Primaries for the syntax definitions for *attributeref*
+and *subscription*.)
An assignment statement evaluates the expression list (remember that
this can be a single expression or a comma-separated list, the latter
@@ -59,12 +58,11 @@
Assignment is defined recursively depending on the form of the target
(list). When a target is part of a mutable object (an attribute
-reference, subscription or slicing), the mutable object must
-ultimately perform the assignment and decide about its validity, and
-may raise an exception if the assignment is unacceptable. The rules
-observed by various types and the exceptions raised are given with the
-definition of the object types (see section The standard type
-hierarchy).
+reference or subscription), the mutable object must ultimately perform
+the assignment and decide about its validity, and may raise an
+exception if the assignment is unacceptable. The rules observed by
+various types and the exceptions raised are given with the definition
+of the object types (see section The standard type hierarchy).
Assignment of an object to a target list, optionally enclosed in
parentheses or square brackets, is recursively defined as follows.
@@ -130,9 +128,13 @@ class Cls:
attributes, such as properties created with "property()".
* If the target is a subscription: The primary expression in the
- reference is evaluated. It should yield either a mutable sequence
- object (such as a list) or a mapping object (such as a dictionary).
- Next, the subscript expression is evaluated.
+ reference is evaluated. Next, the subscript expression is evaluated.
+ Then, the primary’s "__setitem__()" method is called with two
+ arguments: the subscript and the assigned object.
+
+ Typically, "__setitem__()" is defined on mutable sequence objects
+ (such as lists) and mapping objects (such as dictionaries), and
+ behaves as follows.
If the primary is a mutable sequence object (such as a list), the
subscript must yield an integer. If it is negative, the sequence’s
@@ -149,27 +151,17 @@ class Cls:
existing key/value pair with the same key value, or insert a new
key/value pair (if no key with the same value existed).
- For user-defined objects, the "__setitem__()" method is called with
- appropriate arguments.
-
-* If the target is a slicing: The primary expression in the reference
- is evaluated. It should yield a mutable sequence object (such as a
- list). The assigned object should be a sequence object of the same
- type. Next, the lower and upper bound expressions are evaluated,
- insofar they are present; defaults are zero and the sequence’s
- length. The bounds should evaluate to integers. If either bound is
- negative, the sequence’s length is added to it. The resulting
- bounds are clipped to lie between zero and the sequence’s length,
- inclusive. Finally, the sequence object is asked to replace the
- slice with the items of the assigned sequence. The length of the
- slice may be different from the length of the assigned sequence,
- thus changing the length of the target sequence, if the target
- sequence allows it.
-
-**CPython implementation detail:** In the current implementation, the
-syntax for targets is taken to be the same as for expressions, and
-invalid syntax is rejected during the code generation phase, causing
-less detailed error messages.
+ If the target is a slicing: The primary expression should evaluate
+ to a mutable sequence object (such as a list). The assigned object
+ should be *iterable*. The slicing’s lower and upper bounds should be
+ integers; if they are "None" (or not present), the defaults are zero
+ and the sequence’s length. If either bound is negative, the
+ sequence’s length is added to it. The resulting bounds are clipped
+ to lie between zero and the sequence’s length, inclusive. Finally,
+ the sequence object is asked to replace the slice with the items of
+ the assigned sequence. The length of the slice may be different
+ from the length of the assigned sequence, thus changing the length
+ of the target sequence, if the target sequence allows it.
Although the definition of assignment implies that overlaps between
the left-hand side and the right-hand side are ‘simultaneous’ (for
@@ -196,7 +188,7 @@ class Cls:
binary operation and an assignment statement:
augmented_assignment_stmt: augtarget augop (expression_list | yield_expression)
- augtarget: identifier | attributeref | subscription | slicing
+ augtarget: identifier | attributeref | subscription
augop: "+=" | "-=" | "*=" | "@=" | "/=" | "//=" | "%=" | "**="
| ">>=" | "<<=" | "&=" | "^=" | "|="
@@ -369,13 +361,12 @@ async def func(param1, param2):
Is semantically equivalent to:
- iter = (ITER)
- iter = type(iter).__aiter__(iter)
+ iter = (ITER).__aiter__()
running = True
while running:
try:
- TARGET = await type(iter).__anext__(iter)
+ TARGET = await iter.__anext__()
except StopAsyncIteration:
running = False
else:
@@ -383,7 +374,8 @@ async def func(param1, param2):
else:
SUITE2
-See also "__aiter__()" and "__anext__()" for details.
+except that implicit special method lookup is used for "__aiter__()"
+and "__anext__()".
It is a "SyntaxError" to use an "async for" statement outside the body
of a coroutine function.
@@ -405,9 +397,9 @@ async def func(param1, param2):
is semantically equivalent to:
manager = (EXPRESSION)
- aenter = type(manager).__aenter__
- aexit = type(manager).__aexit__
- value = await aenter(manager)
+ aenter = manager.__aenter__
+ aexit = manager.__aexit__
+ value = await aenter()
hit_except = False
try:
@@ -415,13 +407,14 @@ async def func(param1, param2):
SUITE
except:
hit_except = True
- if not await aexit(manager, *sys.exc_info()):
+ if not await aexit(*sys.exc_info()):
raise
finally:
if not hit_except:
- await aexit(manager, None, None, None)
+ await aexit(None, None, None)
-See also "__aenter__()" and "__aexit__()" for details.
+except that implicit special method lookup is used for "__aenter__()"
+and "__aexit__()".
It is a "SyntaxError" to use an "async with" statement outside the
body of a coroutine function.
@@ -489,16 +482,34 @@ async def func(param1, param2):
'atom-literals': r'''Literals
********
-Python supports string and bytes literals and various numeric
-literals:
+A *literal* is a textual representation of a value. Python supports
+numeric, string and bytes literals. Format strings and template
+strings are treated as string literals.
+
+Numeric literals consist of a single "NUMBER" token, which names an
+integer, floating-point number, or an imaginary number. See the
+Numeric literals section in Lexical analysis documentation for
+details.
+
+String and bytes literals may consist of several tokens. See section
+String literal concatenation for details.
+
+Note that negative and complex numbers, like "-3" or "3+4.2j", are
+syntactically not literals, but unary or binary arithmetic operations
+involving the "-" or "+" operator.
+
+Evaluation of a literal yields an object of the given type ("int",
+"float", "complex", "str", "bytes", or "Template") with the given
+value. The value may be approximated in the case of floating-point and
+imaginary literals.
+
+The formal grammar for literals is:
literal: strings | NUMBER
-Evaluation of a literal yields an object of the given type (string,
-bytes, integer, floating-point number, complex number) with the given
-value. The value may be approximated in the case of floating-point
-and imaginary (complex) literals. See section Literals for details.
-See section String literal concatenation for details on "strings".
+
+Literals and object identity
+============================
All literals correspond to immutable data types, and hence the
object’s identity is less important than its value. Multiple
@@ -506,21 +517,53 @@ async def func(param1, param2):
occurrence in the program text or a different occurrence) may obtain
the same object or a different object with the same value.
+CPython implementation detail: For example, in CPython, *small*
+integers with the same value evaluate to the same object:
+
+ >>> x = 7
+ >>> y = 7
+ >>> x is y
+ True
+
+However, large integers evaluate to different objects:
+
+ >>> x = 123456789
+ >>> y = 123456789
+ >>> x is y
+ False
+
+This behavior may change in future versions of CPython. In particular,
+the boundary between “small” and “large” integers has already changed
+in the past.CPython will emit a "SyntaxWarning" when you compare
+literals using "is":
+
+ >>> x = 7
+ >>> x is 7
+ :1: SyntaxWarning: "is" with 'int' literal. Did you mean "=="?
+ True
+
+See When can I rely on identity tests with the is operator? for more
+information.
+
+Template strings are immutable but may reference mutable objects as
+"Interpolation" values. For the purposes of this section, two
+t-strings have the “same value” if both their structure and the
+*identity* of the values match.
+
+**CPython implementation detail:** Currently, each evaluation of a
+template string results in a different object.
+
String literal concatenation
============================
-Multiple adjacent string or bytes literals (delimited by whitespace),
-possibly using different quoting conventions, are allowed, and their
-meaning is the same as their concatenation:
+Multiple adjacent string or bytes literals, possibly using different
+quoting conventions, are allowed, and their meaning is the same as
+their concatenation:
>>> "hello" 'world'
"helloworld"
-Formally:
-
- strings: ( STRING | fstring)+ | tstring+
-
This feature is defined at the syntactical level, so it only works
with literals. To concatenate string expressions at run time, the ‘+’
operator may be used:
@@ -551,6 +594,10 @@ async def func(param1, param2):
>>> t"Hello" t"{name}!"
Template(strings=('Hello', '!'), interpolations=(...))
+
+Formally:
+
+ strings: (STRING | fstring)+ | tstring+
''',
'attribute-access': r'''Customizing attribute access
****************************
@@ -916,7 +963,7 @@ class derived from a ""variable-length" built-in type" such as
binary operation and an assignment statement:
augmented_assignment_stmt: augtarget augop (expression_list | yield_expression)
- augtarget: identifier | attributeref | subscription | slicing
+ augtarget: identifier | attributeref | subscription
augop: "+=" | "-=" | "*=" | "@=" | "/=" | "//=" | "%=" | "**="
| ">>=" | "<<=" | "&=" | "^=" | "|="
@@ -1010,7 +1057,7 @@ class and instance attributes applies as for regular assignments.
The "%" (modulo) operator yields the remainder from the division of
the first argument by the second. The numeric arguments are first
-converted to a common type. A zero right argument raises the
+converted to a common type. A zero right argument raises the
"ZeroDivisionError" exception. The arguments may be floating-point
numbers, e.g., "3.14%0.7" equals "0.34" (since "3.14" equals "4*0.7 +
0.34".) The modulo operator always yields a result with the same sign
@@ -2120,9 +2167,9 @@ def foo():
is semantically equivalent to:
manager = (EXPRESSION)
- enter = type(manager).__enter__
- exit = type(manager).__exit__
- value = enter(manager)
+ enter = manager.__enter__
+ exit = manager.__exit__
+ value = enter()
hit_except = False
try:
@@ -2130,11 +2177,14 @@ def foo():
SUITE
except:
hit_except = True
- if not exit(manager, *sys.exc_info()):
+ if not exit(*sys.exc_info()):
raise
finally:
if not hit_except:
- exit(manager, None, None, None)
+ exit(None, None, None)
+
+except that implicit special method lookup is used for "__enter__()"
+and "__exit__()".
With more than one item, the context managers are processed as if
multiple "with" statements were nested:
@@ -3066,13 +3116,12 @@ async def func(param1, param2):
Is semantically equivalent to:
- iter = (ITER)
- iter = type(iter).__aiter__(iter)
+ iter = (ITER).__aiter__()
running = True
while running:
try:
- TARGET = await type(iter).__anext__(iter)
+ TARGET = await iter.__anext__()
except StopAsyncIteration:
running = False
else:
@@ -3080,7 +3129,8 @@ async def func(param1, param2):
else:
SUITE2
-See also "__aiter__()" and "__anext__()" for details.
+except that implicit special method lookup is used for "__aiter__()"
+and "__anext__()".
It is a "SyntaxError" to use an "async for" statement outside the body
of a coroutine function.
@@ -3102,9 +3152,9 @@ async def func(param1, param2):
is semantically equivalent to:
manager = (EXPRESSION)
- aenter = type(manager).__aenter__
- aexit = type(manager).__aexit__
- value = await aenter(manager)
+ aenter = manager.__aenter__
+ aexit = manager.__aexit__
+ value = await aenter()
hit_except = False
try:
@@ -3112,13 +3162,14 @@ async def func(param1, param2):
SUITE
except:
hit_except = True
- if not await aexit(manager, *sys.exc_info()):
+ if not await aexit(*sys.exc_info()):
raise
finally:
if not hit_except:
- await aexit(manager, None, None, None)
+ await aexit(None, None, None)
-See also "__aenter__()" and "__aexit__()" for details.
+except that implicit special method lookup is used for "__aenter__()"
+and "__aexit__()".
It is a "SyntaxError" to use an "async with" statement outside the
body of a coroutine function.
@@ -3529,19 +3580,13 @@ def f() -> annotation: ...
When a description of an arithmetic operator below uses the phrase
“the numeric arguments are converted to a common real type”, this
-means that the operator implementation for built-in types works as
-follows:
-
-* If both arguments are complex numbers, no conversion is performed;
-
-* if either argument is a complex or a floating-point number, the
- other is converted to a floating-point number;
+means that the operator implementation for built-in numeric types
+works as described in the Numeric Types section of the standard
+library documentation.
-* otherwise, both must be integers and no conversion is necessary.
-
-Some additional rules apply for certain operators (e.g., a string as a
-left argument to the ‘%’ operator). Extensions must define their own
-conversion behavior.
+Some additional rules apply for certain operators and non-numeric
+operands (for example, a string as a left argument to the "%"
+operator). Extensions must define their own conversion behavior.
''',
'customization': r'''Basic customization
*******************
@@ -3698,7 +3743,7 @@ def f() -> annotation: ...
formatting to one of the built-in types, or use a similar
formatting option syntax.
- See Format Specification Mini-Language for a description of the
+ See Format specification mini-language for a description of the
standard formatting syntax.
The return value must be a string object.
@@ -3835,7 +3880,7 @@ def __hash__(self):
intended to provide protection against a denial-of-service caused
by carefully chosen inputs that exploit the worst case
performance of a dict insertion, *O*(*n*^2) complexity. See
- http://ocert.org/advisories/ocert-2011-003.html for
+ https://ocert.org/advisories/ocert-2011-003.html for
details.Changing hash values affects the iteration order of sets.
Python has never made guarantees about this ordering (and it
typically varies between 32-bit and 64-bit builds).See also
@@ -4685,8 +4730,8 @@ def inner(x):
statement in the same code block. Trying to delete an unbound name
raises a "NameError" exception.
-Deletion of attribute references, subscriptions and slicings is passed
-to the primary object involved; deletion of a slicing is in general
+Deletion of attribute references and subscriptions is passed to the
+primary object involved; deletion of a slicing is in general
equivalent to assignment of an empty slice of the right type (but even
this is determined by the sliced object).
@@ -5481,7 +5526,7 @@ class of the instance or a *non-virtual base class* thereof. The
Changed in version 3.11: Starred elements are now allowed in the
expression list.
''',
- 'formatstrings': r'''Format String Syntax
+ 'formatstrings': r'''Format string syntax
********************
The "str.format()" method and the "Formatter" class share the same
@@ -5516,7 +5561,7 @@ class of the instance or a *non-virtual base class* thereof. The
preceded by a colon "':'". These specify a non-default format for the
replacement value.
-See also the Format Specification Mini-Language section.
+See also the Format specification mini-language section.
The *field_name* itself begins with an *arg_name* that is either a
number or a keyword. If it’s a number, it refers to a positional
@@ -5584,12 +5629,12 @@ class of the instance or a *non-virtual base class* thereof. The
See the Format examples section for some examples.
-Format Specification Mini-Language
+Format specification mini-language
==================================
“Format specifications” are used within replacement fields contained
within a format string to define how individual values are presented
-(see Format String Syntax, f-strings, and t-strings). They can also be
+(see Format string syntax, f-strings, and t-strings). They can also be
passed directly to the built-in "format()" function. Each formattable
type may define how the format specification is to be interpreted.
@@ -5996,8 +6041,8 @@ class of the instance or a *non-virtual base class* thereof. The
Using type-specific formatting:
- >>> import datetime
- >>> d = datetime.datetime(2010, 7, 4, 12, 15, 58)
+ >>> import datetime as dt
+ >>> d = dt.datetime(2010, 7, 4, 12, 15, 58)
>>> '{:%Y-%m-%d %H:%M:%S}'.format(d)
'2010-07-04 12:15:58'
@@ -6405,8 +6450,8 @@ def whats_on_the_telly(penguin=None):
remaining characters must be in the “letter- and digit-like” set
"xid_continue".
-These sets based on the *XID_Start* and *XID_Continue* sets as defined
-by the Unicode standard annex UAX-31. Python’s "xid_start"
+These sets are based on the *XID_Start* and *XID_Continue* sets as
+defined by the Unicode standard annex UAX-31. Python’s "xid_start"
additionally includes the underscore ("_"). Note that Python does not
necessarily conform to UAX-31.
@@ -6614,7 +6659,9 @@ def whats_on_the_telly(penguin=None):
The *public names* defined by a module are determined by checking the
module’s namespace for a variable named "__all__"; if defined, it must
be a sequence of strings which are names defined or imported by that
-module. The names given in "__all__" are all considered public and
+module. Names containing non-ASCII characters must be in the
+normalization form NFKC; see Non-ASCII characters in names for
+details. The names given in "__all__" are all considered public and
are required to exist. If "__all__" is not defined, the set of public
names includes all names found in the module’s namespace which do not
begin with an underscore character ("'_'"). "__all__" should contain
@@ -7620,8 +7667,8 @@ class that has an "__rsub__()" method, "type(y).__rsub__(y, x)" is
| value...}", "{expressions...}" | list display, dictionary display, set |
| | display |
+-------------------------------------------------+---------------------------------------+
-| "x[index]", "x[index:index]", | Subscription, slicing, call, |
-| "x(arguments...)", "x.attribute" | attribute reference |
+| "x[index]", "x[index:index]" "x(arguments...)", | Subscription (including slicing), |
+| "x.attribute" | call, attribute reference |
+-------------------------------------------------+---------------------------------------+
| "await x" | Await expression |
+-------------------------------------------------+---------------------------------------+
@@ -7738,8 +7785,8 @@ class C: pass # a class with no methods (yet)
The power operator has the same semantics as the built-in "pow()"
function, when called with two arguments: it yields its left argument
-raised to the power of its right argument. The numeric arguments are
-first converted to a common type, and the result is of that type.
+raised to the power of its right argument. Numeric arguments are first
+converted to a common type, and the result is of that type.
For int operands, the result has the same type as the operands unless
the second argument is negative; in that case, all arguments are
@@ -7945,35 +7992,46 @@ class C: pass # a class with no methods (yet)
Added in version 3.4.
-Note:
+object.__getitem__(self, subscript)
- Slicing is done exclusively with the following three methods. A
- call like
+ Called to implement *subscription*, that is, "self[subscript]". See
+ Subscriptions and slicings for details on the syntax.
- a[1:2] = b
+ There are two types of built-in objects that support subscription
+ via "__getitem__()":
- is translated to
+ * **sequences**, where *subscript* (also called *index*) should be
+ an integer or a "slice" object. See the sequence documentation
+ for the expected behavior, including handling "slice" objects and
+ negative indices.
- a[slice(1, 2, None)] = b
+ * **mappings**, where *subscript* is also called the *key*. See
+ mapping documentation for the expected behavior.
- and so forth. Missing slice items are always filled in with "None".
+ If *subscript* is of an inappropriate type, "__getitem__()" should
+ raise "TypeError". If *subscript* has an inappropriate value,
+ "__getitem__()" should raise an "LookupError" or one of its
+ subclasses ("IndexError" for sequences; "KeyError" for mappings).
+
+ Note:
-object.__getitem__(self, key)
+ Slicing is handled by "__getitem__()", "__setitem__()", and
+ "__delitem__()". A call like
- Called to implement evaluation of "self[key]". For *sequence*
- types, the accepted keys should be integers. Optionally, they may
- support "slice" objects as well. Negative index support is also
- optional. If *key* is of an inappropriate type, "TypeError" may be
- raised; if *key* is a value outside the set of indexes for the
- sequence (after any special interpretation of negative values),
- "IndexError" should be raised. For *mapping* types, if *key* is
- missing (not in the container), "KeyError" should be raised.
+ a[1:2] = b
+
+ is translated to
+
+ a[slice(1, 2, None)] = b
+
+ and so forth. Missing slice items are always filled in with
+ "None".
Note:
- "for" loops expect that an "IndexError" will be raised for
- illegal indexes to allow proper detection of the end of the
- sequence.
+ The sequence iteration protocol (used, for example, in "for"
+ loops), expects that an "IndexError" will be raised for illegal
+ indexes to allow proper detection of the end of a sequence.
Note:
@@ -8063,37 +8121,40 @@ class C: pass # a class with no methods (yet)
'slicings': r'''Slicings
********
-A slicing selects a range of items in a sequence object (e.g., a
-string, tuple or list). Slicings may be used as expressions or as
-targets in assignment or "del" statements. The syntax for a slicing:
-
- slicing: primary "[" slice_list "]"
- slice_list: slice_item ("," slice_item)* [","]
- slice_item: expression | proper_slice
- proper_slice: [lower_bound] ":" [upper_bound] [ ":" [stride] ]
- lower_bound: expression
- upper_bound: expression
- stride: expression
-
-There is ambiguity in the formal syntax here: anything that looks like
-an expression list also looks like a slice list, so any subscription
-can be interpreted as a slicing. Rather than further complicating the
-syntax, this is disambiguated by defining that in this case the
-interpretation as a subscription takes priority over the
-interpretation as a slicing (this is the case if the slice list
-contains no proper slice).
-
-The semantics for a slicing are as follows. The primary is indexed
-(using the same "__getitem__()" method as normal subscription) with a
-key that is constructed from the slice list, as follows. If the slice
-list contains at least one comma, the key is a tuple containing the
-conversion of the slice items; otherwise, the conversion of the lone
-slice item is the key. The conversion of a slice item that is an
-expression is that expression. The conversion of a proper slice is a
-slice object (see section The standard type hierarchy) whose "start",
-"stop" and "step" attributes are the values of the expressions given
-as lower bound, upper bound and stride, respectively, substituting
-"None" for missing expressions.
+A more advanced form of subscription, *slicing*, is commonly used to
+extract a portion of a sequence. In this form, the subscript is a
+*slice*: up to three expressions separated by colons. Any of the
+expressions may be omitted, but a slice must contain at least one
+colon:
+
+ >>> number_names = ['zero', 'one', 'two', 'three', 'four', 'five']
+ >>> number_names[1:3]
+ ['one', 'two']
+ >>> number_names[1:]
+ ['one', 'two', 'three', 'four', 'five']
+ >>> number_names[:3]
+ ['zero', 'one', 'two']
+ >>> number_names[:]
+ ['zero', 'one', 'two', 'three', 'four', 'five']
+ >>> number_names[::2]
+ ['zero', 'two', 'four']
+ >>> number_names[:-3]
+ ['zero', 'one', 'two']
+ >>> del number_names[4:]
+ >>> number_names
+ ['zero', 'one', 'two', 'three']
+
+When a slice is evaluated, the interpreter constructs a "slice" object
+whose "start", "stop" and "step" attributes, respectively, are the
+results of the expressions between the colons. Any missing expression
+evaluates to "None". This "slice" object is then passed to the
+"__getitem__()" or "__class_getitem__()" *special method*, as above.
+
+ # continuing with the SubscriptionDemo instance defined above:
+ >>> demo[2:3]
+ subscripted with: slice(2, 3, None)
+ >>> demo[::'spam']
+ subscripted with: slice(None, None, 'spam')
''',
'specialattrs': r'''Special Attributes
******************
@@ -8314,7 +8375,7 @@ class C: pass # a class with no methods (yet)
formatting to one of the built-in types, or use a similar
formatting option syntax.
- See Format Specification Mini-Language for a description of the
+ See Format specification mini-language for a description of the
standard formatting syntax.
The return value must be a string object.
@@ -8451,7 +8512,7 @@ def __hash__(self):
intended to provide protection against a denial-of-service caused
by carefully chosen inputs that exploit the worst case
performance of a dict insertion, *O*(*n*^2) complexity. See
- http://ocert.org/advisories/ocert-2011-003.html for
+ https://ocert.org/advisories/ocert-2011-003.html for
details.Changing hash values affects the iteration order of sets.
Python has never made guarantees about this ordering (and it
typically varies between 32-bit and 64-bit builds).See also
@@ -9310,35 +9371,46 @@ class of a class is known as that class’s *metaclass*, and most
Added in version 3.4.
-Note:
+object.__getitem__(self, subscript)
+
+ Called to implement *subscription*, that is, "self[subscript]". See
+ Subscriptions and slicings for details on the syntax.
- Slicing is done exclusively with the following three methods. A
- call like
+ There are two types of built-in objects that support subscription
+ via "__getitem__()":
- a[1:2] = b
+ * **sequences**, where *subscript* (also called *index*) should be
+ an integer or a "slice" object. See the sequence documentation
+ for the expected behavior, including handling "slice" objects and
+ negative indices.
+
+ * **mappings**, where *subscript* is also called the *key*. See
+ mapping documentation for the expected behavior.
+
+ If *subscript* is of an inappropriate type, "__getitem__()" should
+ raise "TypeError". If *subscript* has an inappropriate value,
+ "__getitem__()" should raise an "LookupError" or one of its
+ subclasses ("IndexError" for sequences; "KeyError" for mappings).
+
+ Note:
- is translated to
+ Slicing is handled by "__getitem__()", "__setitem__()", and
+ "__delitem__()". A call like
- a[slice(1, 2, None)] = b
+ a[1:2] = b
- and so forth. Missing slice items are always filled in with "None".
+ is translated to
-object.__getitem__(self, key)
+ a[slice(1, 2, None)] = b
- Called to implement evaluation of "self[key]". For *sequence*
- types, the accepted keys should be integers. Optionally, they may
- support "slice" objects as well. Negative index support is also
- optional. If *key* is of an inappropriate type, "TypeError" may be
- raised; if *key* is a value outside the set of indexes for the
- sequence (after any special interpretation of negative values),
- "IndexError" should be raised. For *mapping* types, if *key* is
- missing (not in the container), "KeyError" should be raised.
+ and so forth. Missing slice items are always filled in with
+ "None".
Note:
- "for" loops expect that an "IndexError" will be raised for
- illegal indexes to allow proper detection of the end of the
- sequence.
+ The sequence iteration protocol (used, for example, in "for"
+ loops), expects that an "IndexError" will be raised for illegal
+ indexes to allow proper detection of the end of a sequence.
Note:
@@ -9656,14 +9728,27 @@ class is used in a class pattern with positional arguments, each
"inspect.BufferFlags" provides a convenient way to interpret the
flags. The method must return a "memoryview" object.
+ **Thread safety:** In *free-threaded* Python, implementations must
+ manage any internal export counter using atomic operations. The
+ method must be safe to call concurrently from multiple threads, and
+ the returned buffer’s underlying data must remain valid until the
+ corresponding "__release_buffer__()" call completes. See Thread
+ safety for memoryview objects for details.
+
object.__release_buffer__(self, buffer)
Called when a buffer is no longer needed. The *buffer* argument is
a "memoryview" object that was previously returned by
"__buffer__()". The method must release any resources associated
- with the buffer. This method should return "None". Buffer objects
- that do not need to perform any cleanup are not required to
- implement this method.
+ with the buffer. This method should return "None".
+
+ **Thread safety:** In *free-threaded* Python, any export counter
+ decrement must use atomic operations. Resource cleanup must be
+ thread-safe, as the final release may race with concurrent releases
+ from other threads.
+
+ Buffer objects that do not need to perform any cleanup are not
+ required to implement this method.
Added in version 3.12.
@@ -9804,7 +9889,7 @@ class is used in a class pattern with positional arguments, each
Strings also support two styles of string formatting, one providing a
large degree of flexibility and customization (see "str.format()",
-Format String Syntax and Custom String Formatting) and the other based
+Format string syntax and Custom string formatting) and the other based
on C "printf" style formatting that handles a narrower range of types
and is slightly harder to use correctly, but is often faster for the
cases it can handle (printf-style String Formatting).
@@ -9990,7 +10075,7 @@ class is used in a class pattern with positional arguments, each
>>> "{1} expects the {0} Inquisition!".format("Spanish", "Nobody")
'Nobody expects the Spanish Inquisition!'
- See Format String Syntax for a description of the various
+ See Format string syntax for a description of the various
formatting options that can be specified in format strings.
Note:
@@ -10045,6 +10130,16 @@ class is used in a class pattern with positional arguments, each
there is at least one character, "False" otherwise. A character
"c" is alphanumeric if one of the following returns "True":
"c.isalpha()", "c.isdecimal()", "c.isdigit()", or "c.isnumeric()".
+ For example:
+
+ >>> 'abc123'.isalnum()
+ True
+ >>> 'abc123!@#'.isalnum()
+ False
+ >>> ''.isalnum()
+ False
+ >>> ' '.isalnum()
+ False
str.isalpha()
@@ -10173,16 +10268,31 @@ class is used in a class pattern with positional arguments, each
>>> '\t'.isprintable(), '\n'.isprintable()
(False, False)
+ See also "isspace()".
+
str.isspace()
Return "True" if there are only whitespace characters in the string
and there is at least one character, "False" otherwise.
+ For example:
+
+ >>> ''.isspace()
+ False
+ >>> ' '.isspace()
+ True
+ >>> '\t\n'.isspace() # TAB and BREAK LINE
+ True
+ >>> '\u3000'.isspace() # IDEOGRAPHIC SPACE
+ True
+
A character is *whitespace* if in the Unicode character database
(see "unicodedata"), either its general category is "Zs"
(“Separator, space”), or its bidirectional class is one of "WS",
"B", or "S".
+ See also "isprintable()".
+
str.istitle()
Return "True" if the string is a titlecased string and there is at
@@ -10306,6 +10416,17 @@ class is used in a class pattern with positional arguments, each
found, return a 3-tuple containing the string itself, followed by
two empty strings.
+ For example:
+
+ >>> 'Monty Python'.partition(' ')
+ ('Monty', ' ', 'Python')
+ >>> "Monty Python's Flying Circus".partition(' ')
+ ('Monty', ' ', "Python's Flying Circus")
+ >>> 'Monty Python'.partition('-')
+ ('Monty Python', '', '')
+
+ See also "rpartition()".
+
str.removeprefix(prefix, /)
If the string starts with the *prefix* string, return
@@ -10388,6 +10509,17 @@ class is used in a class pattern with positional arguments, each
space). The original string is returned if *width* is less than or
equal to "len(s)".
+ For example:
+
+ >>> 'Python'.rjust(10)
+ ' Python'
+ >>> 'Python'.rjust(10, '.')
+ '....Python'
+ >>> 'Monty Python'.rjust(10, '.')
+ 'Monty Python'
+
+ See also "ljust()" and "zfill()".
+
str.rpartition(sep, /)
Split the string at the last occurrence of *sep*, and return a
@@ -10422,21 +10554,23 @@ class is used in a class pattern with positional arguments, each
*chars* argument is a string specifying the set of characters to be
removed. If omitted or "None", the *chars* argument defaults to
removing whitespace. The *chars* argument is not a suffix; rather,
- all combinations of its values are stripped:
+ all combinations of its values are stripped. For example:
>>> ' spacious '.rstrip()
' spacious'
>>> 'mississippi'.rstrip('ipz')
'mississ'
- See "str.removesuffix()" for a method that will remove a single
- suffix string rather than all of a set of characters. For example:
+ See "removesuffix()" for a method that will remove a single suffix
+ string rather than all of a set of characters. For example:
>>> 'Monty Python'.rstrip(' Python')
'M'
>>> 'Monty Python'.removesuffix(' Python')
'Monty'
+ See also "strip()".
+
str.split(sep=None, maxsplit=-1)
Return a list of the words in the string, using *sep* as the
@@ -10561,6 +10695,17 @@ class is used in a class pattern with positional arguments, each
With optional *start*, test string beginning at that position.
With optional *end*, stop comparing string at that position.
+ For example:
+
+ >>> 'Python'.startswith('Py')
+ True
+ >>> 'a tuple of prefixes'.startswith(('at', 'a'))
+ True
+ >>> 'Python is amazing'.startswith('is', 7)
+ True
+
+ See also "endswith()" and "removeprefix()".
+
str.strip(chars=None, /)
Return a copy of the string with the leading and trailing
@@ -10568,7 +10713,9 @@ class is used in a class pattern with positional arguments, each
set of characters to be removed. If omitted or "None", the *chars*
argument defaults to removing whitespace. The *chars* argument is
not a prefix or suffix; rather, all combinations of its values are
- stripped:
+ stripped.
+
+ For example:
>>> ' spacious '.strip()
'spacious'
@@ -10579,12 +10726,16 @@ class is used in a class pattern with positional arguments, each
stripped from the string. Characters are removed from the leading
end until reaching a string character that is not contained in the
set of characters in *chars*. A similar action takes place on the
- trailing end. For example:
+ trailing end.
+
+ For example:
>>> comment_string = '#....... Section 3.2.1 Issue #32 .......'
>>> comment_string.strip('.#! ')
'Section 3.2.1 Issue #32'
+ See also "rstrip()".
+
str.swapcase()
Return a copy of the string with uppercase characters converted to
@@ -10669,6 +10820,8 @@ class is used in a class pattern with positional arguments, each
'00042'
>>> "-42".zfill(5)
'-0042'
+
+ See also "rjust()".
''',
'strings': '''String and Bytes literals
*************************
@@ -11235,62 +11388,168 @@ class is used in a class pattern with positional arguments, each
''',
- 'subscriptions': r'''Subscriptions
-*************
+ 'subscriptions': r'''Subscriptions and slicings
+**************************
+
+The *subscription* syntax is usually used for selecting an element
+from a container – for example, to get a value from a "dict":
-The subscription of an instance of a container class will generally
-select an element from the container. The subscription of a *generic
-class* will generally return a GenericAlias object.
+ >>> digits_by_name = {'one': 1, 'two': 2}
+ >>> digits_by_name['two'] # Subscripting a dictionary using the key 'two'
+ 2
- subscription: primary "[" flexible_expression_list "]"
+In the subscription syntax, the object being subscribed – a primary –
+is followed by a *subscript* in square brackets. In the simplest case,
+the subscript is a single expression.
-When an object is subscripted, the interpreter will evaluate the
-primary and the expression list.
+Depending on the type of the object being subscribed, the subscript is
+sometimes called a *key* (for mappings), *index* (for sequences), or
+*type argument* (for *generic types*). Syntactically, these are all
+equivalent:
-The primary must evaluate to an object that supports subscription. An
-object may support subscription through defining one or both of
-"__getitem__()" and "__class_getitem__()". When the primary is
-subscripted, the evaluated result of the expression list will be
-passed to one of these methods. For more details on when
-"__class_getitem__" is called instead of "__getitem__", see
+ >>> colors = ['red', 'blue', 'green', 'black']
+ >>> colors[3] # Subscripting a list using the index 3
+ 'black'
+
+ >>> list[str] # Parameterizing the list type using the type argument str
+ list[str]
+
+At runtime, the interpreter will evaluate the primary and the
+subscript, and call the primary’s "__getitem__()" or
+"__class_getitem__()" *special method* with the subscript as argument.
+For more details on which of these methods is called, see
__class_getitem__ versus __getitem__.
-If the expression list contains at least one comma, or if any of the
-expressions are starred, the expression list will evaluate to a
-"tuple" containing the items of the expression list. Otherwise, the
-expression list will evaluate to the value of the list’s sole member.
-
-Changed in version 3.11: Expressions in an expression list may be
-starred. See **PEP 646**.
-
-For built-in objects, there are two types of objects that support
-subscription via "__getitem__()":
-
-1. Mappings. If the primary is a *mapping*, the expression list must
- evaluate to an object whose value is one of the keys of the
- mapping, and the subscription selects the value in the mapping that
- corresponds to that key. An example of a builtin mapping class is
- the "dict" class.
-
-2. Sequences. If the primary is a *sequence*, the expression list must
- evaluate to an "int" or a "slice" (as discussed in the following
- section). Examples of builtin sequence classes include the "str",
- "list" and "tuple" classes.
-
-The formal syntax makes no special provision for negative indices in
-*sequences*. However, built-in sequences all provide a "__getitem__()"
-method that interprets negative indices by adding the length of the
-sequence to the index so that, for example, "x[-1]" selects the last
-item of "x". The resulting value must be a nonnegative integer less
-than the number of items in the sequence, and the subscription selects
-the item whose index is that value (counting from zero). Since the
-support for negative indices and slicing occurs in the object’s
-"__getitem__()" method, subclasses overriding this method will need to
-explicitly add that support.
-
-A "string" is a special kind of sequence whose items are *characters*.
-A character is not a separate data type but a string of exactly one
-character.
+To show how subscription works, we can define a custom object that
+implements "__getitem__()" and prints out the value of the subscript:
+
+ >>> class SubscriptionDemo:
+ ... def __getitem__(self, key):
+ ... print(f'subscripted with: {key!r}')
+ ...
+ >>> demo = SubscriptionDemo()
+ >>> demo[1]
+ subscripted with: 1
+ >>> demo['a' * 3]
+ subscripted with: 'aaa'
+
+See "__getitem__()" documentation for how built-in types handle
+subscription.
+
+Subscriptions may also be used as targets in assignment or deletion
+statements. In these cases, the interpreter will call the subscripted
+object’s "__setitem__()" or "__delitem__()" *special method*,
+respectively, instead of "__getitem__()".
+
+ >>> colors = ['red', 'blue', 'green', 'black']
+ >>> colors[3] = 'white' # Setting item at index
+ >>> colors
+ ['red', 'blue', 'green', 'white']
+ >>> del colors[3] # Deleting item at index 3
+ >>> colors
+ ['red', 'blue', 'green']
+
+All advanced forms of *subscript* documented in the following sections
+are also usable for assignment and deletion.
+
+
+Slicings
+========
+
+A more advanced form of subscription, *slicing*, is commonly used to
+extract a portion of a sequence. In this form, the subscript is a
+*slice*: up to three expressions separated by colons. Any of the
+expressions may be omitted, but a slice must contain at least one
+colon:
+
+ >>> number_names = ['zero', 'one', 'two', 'three', 'four', 'five']
+ >>> number_names[1:3]
+ ['one', 'two']
+ >>> number_names[1:]
+ ['one', 'two', 'three', 'four', 'five']
+ >>> number_names[:3]
+ ['zero', 'one', 'two']
+ >>> number_names[:]
+ ['zero', 'one', 'two', 'three', 'four', 'five']
+ >>> number_names[::2]
+ ['zero', 'two', 'four']
+ >>> number_names[:-3]
+ ['zero', 'one', 'two']
+ >>> del number_names[4:]
+ >>> number_names
+ ['zero', 'one', 'two', 'three']
+
+When a slice is evaluated, the interpreter constructs a "slice" object
+whose "start", "stop" and "step" attributes, respectively, are the
+results of the expressions between the colons. Any missing expression
+evaluates to "None". This "slice" object is then passed to the
+"__getitem__()" or "__class_getitem__()" *special method*, as above.
+
+ # continuing with the SubscriptionDemo instance defined above:
+ >>> demo[2:3]
+ subscripted with: slice(2, 3, None)
+ >>> demo[::'spam']
+ subscripted with: slice(None, None, 'spam')
+
+
+Comma-separated subscripts
+==========================
+
+The subscript can also be given as two or more comma-separated
+expressions or slices:
+
+ # continuing with the SubscriptionDemo instance defined above:
+ >>> demo[1, 2, 3]
+ subscripted with: (1, 2, 3)
+ >>> demo[1:2, 3]
+ subscripted with: (slice(1, 2, None), 3)
+
+This form is commonly used with numerical libraries for slicing multi-
+dimensional data. In this case, the interpreter constructs a "tuple"
+of the results of the expressions or slices, and passes this tuple to
+the "__getitem__()" or "__class_getitem__()" *special method*, as
+above.
+
+The subscript may also be given as a single expression or slice
+followed by a comma, to specify a one-element tuple:
+
+ >>> demo['spam',]
+ subscripted with: ('spam',)
+
+
+“Starred” subscriptions
+=======================
+
+Added in version 3.11: Expressions in *tuple_slices* may be starred.
+See **PEP 646**.
+
+The subscript can also contain a starred expression. In this case, the
+interpreter unpacks the result into a tuple, and passes this tuple to
+"__getitem__()" or "__class_getitem__()":
+
+ # continuing with the SubscriptionDemo instance defined above:
+ >>> demo[*range(10)]
+ subscripted with: (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
+
+Starred expressions may be combined with comma-separated expressions
+and slices:
+
+ >>> demo['a', 'b', *range(3), 'c']
+ subscripted with: ('a', 'b', 0, 1, 2, 'c')
+
+
+Formal subscription grammar
+===========================
+
+ subscription: primary '[' subscript ']'
+ subscript: single_subscript | tuple_subscript
+ single_subscript: proper_slice | assignment_expression
+ proper_slice: [expression] ":" [expression] [ ":" [expression] ]
+ tuple_subscript: ','.(single_subscript | starred_expression)+ [',']
+
+Recall that the "|" operator denotes ordered choice. Specifically, in
+"subscript", if both alternatives would match, the first
+("single_subscript") has priority.
''',
'truth': r'''Truth Value Testing
*******************
@@ -11696,10 +11955,19 @@ def foo():
"a[-2]" equals "a[n-2]", the second to last item of sequence a with
length "n".
-Sequences also support slicing: "a[i:j]" selects all items with index
-*k* such that *i* "<=" *k* "<" *j*. When used as an expression, a
-slice is a sequence of the same type. The comment above about negative
-indexes also applies to negative slice positions.
+The resulting value must be a nonnegative integer less than the number
+of items in the sequence. If it is not, an "IndexError" is raised.
+
+Sequences also support slicing: "a[start:stop]" selects all items with
+index *k* such that *start* "<=" *k* "<" *stop*. When used as an
+expression, a slice is a sequence of the same type. The comment above
+about negative subscripts also applies to negative slice positions.
+Note that no error is raised if a slice position is less than zero or
+larger than the length of the sequence.
+
+If *start* is missing or "None", slicing behaves as if *start* was
+zero. If *stop* is missing or "None", slicing behaves as if *stop* was
+equal to the length of the sequence.
Some sequences also support “extended slicing” with a third “step”
parameter: "a[i:j:k]" selects all items of *a* with index *x* where "x
@@ -11720,27 +11988,33 @@ def foo():
The following types are immutable sequences:
Strings
- A string is a sequence of values that represent Unicode code
- points. All the code points in the range "U+0000 - U+10FFFF" can be
- represented in a string. Python doesn’t have a char type; instead,
- every code point in the string is represented as a string object
- with length "1". The built-in function "ord()" converts a code
- point from its string form to an integer in the range "0 - 10FFFF";
- "chr()" converts an integer in the range "0 - 10FFFF" to the
- corresponding length "1" string object. "str.encode()" can be used
- to convert a "str" to "bytes" using the given text encoding, and
+ A string ("str") is a sequence of values that represent
+ *characters*, or more formally, *Unicode code points*. All the code
+ points in the range "0" to "0x10FFFF" can be represented in a
+ string.
+
+ Python doesn’t have a dedicated *character* type. Instead, every
+ code point in the string is represented as a string object with
+ length "1".
+
+ The built-in function "ord()" converts a code point from its string
+ form to an integer in the range "0" to "0x10FFFF"; "chr()" converts
+ an integer in the range "0" to "0x10FFFF" to the corresponding
+ length "1" string object. "str.encode()" can be used to convert a
+ "str" to "bytes" using the given text encoding, and
"bytes.decode()" can be used to achieve the opposite.
Tuples
- The items of a tuple are arbitrary Python objects. Tuples of two or
- more items are formed by comma-separated lists of expressions. A
- tuple of one item (a ‘singleton’) can be formed by affixing a comma
- to an expression (an expression by itself does not create a tuple,
- since parentheses must be usable for grouping of expressions). An
- empty tuple can be formed by an empty pair of parentheses.
+ The items of a "tuple" are arbitrary Python objects. Tuples of two
+ or more items are formed by comma-separated lists of expressions.
+ A tuple of one item (a ‘singleton’) can be formed by affixing a
+ comma to an expression (an expression by itself does not create a
+ tuple, since parentheses must be usable for grouping of
+ expressions). An empty tuple can be formed by an empty pair of
+ parentheses.
Bytes
- A bytes object is an immutable array. The items are 8-bit bytes,
+ A "bytes" object is an immutable array. The items are 8-bit bytes,
represented by integers in the range 0 <= x < 256. Bytes literals
(like "b'abc'") and the built-in "bytes()" constructor can be used
to create bytes objects. Also, bytes objects can be decoded to
@@ -12510,11 +12784,28 @@ class instance has a namespace implemented as a dictionary which is
socket objects (and perhaps by other functions or methods provided by
extension modules).
+File objects implement common methods, listed below, to simplify usage
+in generic code. They are expected to be With Statement Context
+Managers.
+
The objects "sys.stdin", "sys.stdout" and "sys.stderr" are initialized
to file objects corresponding to the interpreter’s standard input,
output and error streams; they are all open in text mode and therefore
follow the interface defined by the "io.TextIOBase" abstract class.
+file.read(size=-1, /)
+
+ Retrieve up to *size* data from the file. As a convenience if
+ *size* is unspecified or -1 retrieve all data available.
+
+file.write(data, /)
+
+ Store *data* to the file.
+
+file.close()
+
+ Flush any buffers and close the underlying file.
+
Internal types
==============
@@ -13202,6 +13493,11 @@ class dict(iterable, /, **kwargs)
"types.MappingProxyType" can be used to create a read-only view of a
"dict".
+See also:
+
+ For detailed information on thread-safety guarantees for "dict"
+ objects, see Thread safety for dict objects.
+
Dictionary view objects
=======================
@@ -13535,7 +13831,7 @@ class dict(iterable, /, **kwargs)
Return the total number of occurrences of *value* in *sequence*.
-sequence.index(value[, start[, stop])
+sequence.index(value[, start[, stop]])
Return the index of the first occurrence of *value* in *sequence*.
@@ -13625,7 +13921,7 @@ class dict(iterable, /, **kwargs)
sequence.append(value, /)
- Append *value* to the end of the sequence This is equivalent to
+ Append *value* to the end of the sequence. This is equivalent to
writing "seq[len(seq):len(seq)] = [value]".
sequence.clear()
@@ -13754,75 +14050,10 @@ class list(iterable=(), /)
empty for the duration, and raises "ValueError" if it can detect
that the list has been mutated during a sort.
-Thread safety: Reading a single element from a "list" is *atomic*:
-
- lst[i] # list.__getitem__
-
-The following methods traverse the list and use *atomic* reads of each
-item to perform their function. That means that they may return
-results affected by concurrent modifications:
-
- item in lst
- lst.index(item)
- lst.count(item)
-
-All of the above methods/operations are also lock-free. They do not
-block concurrent modifications. Other operations that hold a lock will
-not block these from observing intermediate states.All other
-operations from here on block using the per-object lock.Writing a
-single item via "lst[i] = x" is safe to call from multiple threads and
-will not corrupt the list.The following operations return new objects
-and appear *atomic* to other threads:
-
- lst1 + lst2 # concatenates two lists into a new list
- x * lst # repeats lst x times into a new list
- lst.copy() # returns a shallow copy of the list
-
-Methods that only operate on a single elements with no shifting
-required are *atomic*:
-
- lst.append(x) # append to the end of the list, no shifting required
- lst.pop() # pop element from the end of the list, no shifting required
-
-The "clear()" method is also *atomic*. Other threads cannot observe
-elements being removed.The "sort()" method is not *atomic*. Other
-threads cannot observe intermediate states during sorting, but the
-list appears empty for the duration of the sort.The following
-operations may allow lock-free operations to observe intermediate
-states since they modify multiple elements in place:
-
- lst.insert(idx, item) # shifts elements
- lst.pop(idx) # idx not at the end of the list, shifts elements
- lst *= x # copies elements in place
-
-The "remove()" method may allow concurrent modifications since element
-comparison may execute arbitrary Python code (via
-"__eq__()")."extend()" is safe to call from multiple threads.
-However, its guarantees depend on the iterable passed to it. If it is
-a "list", a "tuple", a "set", a "frozenset", a "dict" or a dictionary
-view object (but not their subclasses), the "extend" operation is safe
-from concurrent modifications to the iterable. Otherwise, an iterator
-is created which can be concurrently modified by another thread. The
-same applies to inplace concatenation of a list with other iterables
-when using "lst += iterable".Similarly, assigning to a list slice with
-"lst[i:j] = iterable" is safe to call from multiple threads, but
-"iterable" is only locked when it is also a "list" (but not its
-subclasses).Operations that involve multiple accesses, as well as
-iteration, are never atomic. For example:
-
- # NOT atomic: read-modify-write
- lst[i] = lst[i] + 1
-
- # NOT atomic: check-then-act
- if lst:
- item = lst.pop()
-
- # NOT thread-safe: iteration while modifying
- for item in lst:
- process(item) # another thread may modify lst
-
-Consider external synchronization when sharing "list" instances across
-threads. See Python support for free threading for more information.
+See also:
+
+ For detailed information on thread-safety guarantees for "list"
+ objects, see Thread safety for list objects.
Tuples
@@ -14043,7 +14274,7 @@ class range(start, stop, step=1, /)
sequence.append(value, /)
- Append *value* to the end of the sequence This is equivalent to
+ Append *value* to the end of the sequence. This is equivalent to
writing "seq[len(seq):len(seq)] = [value]".
sequence.clear()
@@ -14194,9 +14425,9 @@ class range(start, stop, step=1, /)
is semantically equivalent to:
manager = (EXPRESSION)
- enter = type(manager).__enter__
- exit = type(manager).__exit__
- value = enter(manager)
+ enter = manager.__enter__
+ exit = manager.__exit__
+ value = enter()
hit_except = False
try:
@@ -14204,11 +14435,14 @@ class range(start, stop, step=1, /)
SUITE
except:
hit_except = True
- if not exit(manager, *sys.exc_info()):
+ if not exit(*sys.exc_info()):
raise
finally:
if not hit_except:
- exit(manager, None, None, None)
+ exit(None, None, None)
+
+except that implicit special method lookup is used for "__enter__()"
+and "__exit__()".
With more than one item, the context managers are processed as if
multiple "with" statements were nested:
diff --git a/PythonLib/full/subprocess.py b/PythonLib/full/subprocess.py
index 578d7b95..52b7b711 100644
--- a/PythonLib/full/subprocess.py
+++ b/PythonLib/full/subprocess.py
@@ -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
diff --git a/PythonLib/full/sysconfig/__init__.py b/PythonLib/full/sysconfig/__init__.py
index 2ecbff22..faf8273b 100644
--- a/PythonLib/full/sysconfig/__init__.py
+++ b/PythonLib/full/sysconfig/__init__.py
@@ -698,11 +698,19 @@ def get_platform():
release = get_config_var("ANDROID_API_LEVEL")
# Wheel tags use the ABI names from Android's own tools.
+ # When Python is running on 32-bit ARM Android on a 64-bit ARM kernel,
+ # 'os.uname().machine' is 'armv8l'. Such devices run the same userspace
+ # code as 'armv7l' devices.
+ # During the build process of the Android testbed when targeting 32-bit ARM,
+ # '_PYTHON_HOST_PLATFORM' is 'arm-linux-androideabi', so 'machine' becomes
+ # 'arm'.
machine = {
- "x86_64": "x86_64",
- "i686": "x86",
"aarch64": "arm64_v8a",
+ "arm": "armeabi_v7a",
"armv7l": "armeabi_v7a",
+ "armv8l": "armeabi_v7a",
+ "i686": "x86",
+ "x86_64": "x86_64",
}[machine]
elif osname == "linux":
# At least on Linux/Intel, 'machine' is the processor --
diff --git a/PythonLib/full/tarfile.py b/PythonLib/full/tarfile.py
index c7e9f7d6..414aefe9 100644
--- a/PythonLib/full/tarfile.py
+++ b/PythonLib/full/tarfile.py
@@ -1278,6 +1278,20 @@ def _create_pax_generic_header(cls, pax_headers, type, encoding):
@classmethod
def frombuf(cls, buf, encoding, errors):
"""Construct a TarInfo object from a 512 byte bytes object.
+
+ To support the old v7 tar format AREGTYPE headers are
+ transformed to DIRTYPE headers if their name ends in '/'.
+ """
+ return cls._frombuf(buf, encoding, errors)
+
+ @classmethod
+ def _frombuf(cls, buf, encoding, errors, *, dircheck=True):
+ """Construct a TarInfo object from a 512 byte bytes object.
+
+ If ``dircheck`` is set to ``True`` then ``AREGTYPE`` headers will
+ be normalized to ``DIRTYPE`` if the name ends in a trailing slash.
+ ``dircheck`` must be set to ``False`` if this function is called
+ on a follow-up header such as ``GNUTYPE_LONGNAME``.
"""
if len(buf) == 0:
raise EmptyHeaderError("empty header")
@@ -1308,7 +1322,7 @@ def frombuf(cls, buf, encoding, errors):
# Old V7 tar format represents a directory as a regular
# file with a trailing slash.
- if obj.type == AREGTYPE and obj.name.endswith("/"):
+ if dircheck and obj.type == AREGTYPE and obj.name.endswith("/"):
obj.type = DIRTYPE
# The old GNU sparse format occupies some of the unused
@@ -1343,8 +1357,15 @@ def fromtarfile(cls, tarfile):
"""Return the next TarInfo object from TarFile object
tarfile.
"""
+ return cls._fromtarfile(tarfile)
+
+ @classmethod
+ def _fromtarfile(cls, tarfile, *, dircheck=True):
+ """
+ See dircheck documentation in _frombuf().
+ """
buf = tarfile.fileobj.read(BLOCKSIZE)
- obj = cls.frombuf(buf, tarfile.encoding, tarfile.errors)
+ obj = cls._frombuf(buf, tarfile.encoding, tarfile.errors, dircheck=dircheck)
obj.offset = tarfile.fileobj.tell() - BLOCKSIZE
return obj._proc_member(tarfile)
@@ -1402,7 +1423,7 @@ def _proc_gnulong(self, tarfile):
# Fetch the next header and process it.
try:
- next = self.fromtarfile(tarfile)
+ next = self._fromtarfile(tarfile, dircheck=False)
except HeaderError as e:
raise SubsequentHeaderError(str(e)) from None
@@ -1537,7 +1558,7 @@ def _proc_pax(self, tarfile):
# Fetch the next header.
try:
- next = self.fromtarfile(tarfile)
+ next = self._fromtarfile(tarfile, dircheck=False)
except HeaderError as e:
raise SubsequentHeaderError(str(e)) from None
diff --git a/PythonLib/full/tempfile.py b/PythonLib/full/tempfile.py
index 5e3ccab5..a34e062f 100644
--- a/PythonLib/full/tempfile.py
+++ b/PythonLib/full/tempfile.py
@@ -57,10 +57,11 @@
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
diff --git a/PythonLib/full/traceback.py b/PythonLib/full/traceback.py
index 5a34a2b8..79f67b98 100644
--- a/PythonLib/full/traceback.py
+++ b/PythonLib/full/traceback.py
@@ -1589,17 +1589,23 @@ def _substitution_cost(ch_a, ch_b):
return _MOVE_COST
+def _get_safe___dir__(obj):
+ # Use obj.__dir__() to avoid a TypeError when calling dir(obj).
+ # See gh-131001 and gh-139933.
+ try:
+ d = obj.__dir__()
+ except TypeError: # when obj is a class
+ d = type(obj).__dir__(obj)
+ return sorted(x for x in d if isinstance(x, str))
+
+
def _compute_suggestion_error(exc_value, tb, wrong_name):
if wrong_name is None or not isinstance(wrong_name, str):
return None
if isinstance(exc_value, AttributeError):
obj = exc_value.obj
try:
- try:
- d = dir(obj)
- except TypeError: # Attributes are unsortable, e.g. int and str
- d = list(obj.__class__.__dict__.keys()) + list(obj.__dict__.keys())
- d = sorted([x for x in d if isinstance(x, str)])
+ d = _get_safe___dir__(obj)
hide_underscored = (wrong_name[:1] != '_')
if hide_underscored and tb is not None:
while tb.tb_next is not None:
@@ -1614,11 +1620,7 @@ def _compute_suggestion_error(exc_value, tb, wrong_name):
elif isinstance(exc_value, ImportError):
try:
mod = __import__(exc_value.name)
- try:
- d = dir(mod)
- except TypeError: # Attributes are unsortable, e.g. int and str
- d = list(mod.__dict__.keys())
- d = sorted([x for x in d if isinstance(x, str)])
+ d = _get_safe___dir__(mod)
if wrong_name[:1] != '_':
d = [x for x in d if x[:1] != '_']
except Exception:
diff --git a/PythonLib/full/turtledemo/__main__.py b/PythonLib/full/turtledemo/__main__.py
index b49c0bea..7c2d753f 100644
--- a/PythonLib/full/turtledemo/__main__.py
+++ b/PythonLib/full/turtledemo/__main__.py
@@ -136,7 +136,7 @@ def __init__(self, filename=None):
# so that our menu bar appears.
subprocess.run(
[
- 'osascript',
+ '/usr/bin/osascript',
'-e', 'tell application "System Events"',
'-e', 'set frontmost of the first process whose '
'unix id is {} to true'.format(os.getpid()),
diff --git a/PythonLib/full/unittest/mock.py b/PythonLib/full/unittest/mock.py
index feb9730d..92b81d15 100644
--- a/PythonLib/full/unittest/mock.py
+++ b/PythonLib/full/unittest/mock.py
@@ -34,6 +34,7 @@
import pkgutil
from inspect import iscoroutinefunction
import threading
+from annotationlib import Format
from dataclasses import fields, is_dataclass
from types import CodeType, ModuleType, MethodType
from unittest.util import safe_repr
@@ -119,7 +120,7 @@ def _get_signature_object(func, as_instance, eat_self):
else:
sig_func = func
try:
- return func, inspect.signature(sig_func)
+ return func, inspect.signature(sig_func, annotation_format=Format.FORWARDREF)
except ValueError:
# Certain callable types are not supported by inspect.signature()
return None
@@ -1184,10 +1185,16 @@ def _increment_mock_call(self, /, *args, **kwargs):
# handle call_args
# needs to be set here so assertions on call arguments pass before
# execution in the case of awaited calls
- _call = _Call((args, kwargs), two=True)
- self.call_args = _call
- self.call_args_list.append(_call)
- self.call_count = len(self.call_args_list)
+ with NonCallableMock._lock:
+ # Lock is used here so that call_args_list and call_count are
+ # set atomically otherwise it is possible that by the time call_count
+ # is set another thread may have appended to call_args_list.
+ # The rest of this function relies on list.append being atomic and
+ # skips locking.
+ _call = _Call((args, kwargs), two=True)
+ self.call_args = _call
+ self.call_args_list.append(_call)
+ self.call_count = len(self.call_args_list)
# initial stuff for method_calls:
do_method_calls = self._mock_parent is not None
diff --git a/PythonLib/full/urllib/parse.py b/PythonLib/full/urllib/parse.py
index 67d9bbea..a651e815 100644
--- a/PythonLib/full/urllib/parse.py
+++ b/PythonLib/full/urllib/parse.py
@@ -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 @@
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)
diff --git a/PythonLib/full/venv/__init__.py b/PythonLib/full/venv/__init__.py
index 17ee28e8..88f3340a 100644
--- a/PythonLib/full/venv/__init__.py
+++ b/PythonLib/full/venv/__init__.py
@@ -588,7 +588,7 @@ def skip_file(f):
'may be binary: %s', srcfile, e)
continue
if new_data == data:
- shutil.copy2(srcfile, dstfile)
+ shutil.copy(srcfile, dstfile)
else:
with open(dstfile, 'wb') as f:
f.write(new_data)
diff --git a/PythonLib/full/venv/scripts/common/Activate.ps1 b/PythonLib/full/venv/scripts/common/Activate.ps1
index d75b8fbc..2cc90919 100644
--- a/PythonLib/full/venv/scripts/common/Activate.ps1
+++ b/PythonLib/full/venv/scripts/common/Activate.ps1
@@ -246,3 +246,302 @@ if (Test-Path -Path Env:PYTHONHOME) {
# Add the venv to the PATH
Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH
$Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH"
+
+# SIG # Begin signature block
+# MII3YgYJKoZIhvcNAQcCoII3UzCCN08CAQExDzANBglghkgBZQMEAgEFADB5Bgor
+# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
+# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBALKwKRFIhr2RY
+# IW/WJLd9pc8a9sj/IoThKU92fTfKsKCCG9IwggXMMIIDtKADAgECAhBUmNLR1FsZ
+# lUgTecgRwIeZMA0GCSqGSIb3DQEBDAUAMHcxCzAJBgNVBAYTAlVTMR4wHAYDVQQK
+# ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xSDBGBgNVBAMTP01pY3Jvc29mdCBJZGVu
+# dGl0eSBWZXJpZmljYXRpb24gUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAy
+# MDAeFw0yMDA0MTYxODM2MTZaFw00NTA0MTYxODQ0NDBaMHcxCzAJBgNVBAYTAlVT
+# MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xSDBGBgNVBAMTP01pY3Jv
+# c29mdCBJZGVudGl0eSBWZXJpZmljYXRpb24gUm9vdCBDZXJ0aWZpY2F0ZSBBdXRo
+# b3JpdHkgMjAyMDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALORKgeD
+# Bmf9np3gx8C3pOZCBH8Ppttf+9Va10Wg+3cL8IDzpm1aTXlT2KCGhFdFIMeiVPvH
+# or+Kx24186IVxC9O40qFlkkN/76Z2BT2vCcH7kKbK/ULkgbk/WkTZaiRcvKYhOuD
+# PQ7k13ESSCHLDe32R0m3m/nJxxe2hE//uKya13NnSYXjhr03QNAlhtTetcJtYmrV
+# qXi8LW9J+eVsFBT9FMfTZRY33stuvF4pjf1imxUs1gXmuYkyM6Nix9fWUmcIxC70
+# ViueC4fM7Ke0pqrrBc0ZV6U6CwQnHJFnni1iLS8evtrAIMsEGcoz+4m+mOJyoHI1
+# vnnhnINv5G0Xb5DzPQCGdTiO0OBJmrvb0/gwytVXiGhNctO/bX9x2P29Da6SZEi3
+# W295JrXNm5UhhNHvDzI9e1eM80UHTHzgXhgONXaLbZ7LNnSrBfjgc10yVpRnlyUK
+# xjU9lJfnwUSLgP3B+PR0GeUw9gb7IVc+BhyLaxWGJ0l7gpPKWeh1R+g/OPTHU3mg
+# trTiXFHvvV84wRPmeAyVWi7FQFkozA8kwOy6CXcjmTimthzax7ogttc32H83rwjj
+# O3HbbnMbfZlysOSGM1l0tRYAe1BtxoYT2v3EOYI9JACaYNq6lMAFUSw0rFCZE4e7
+# swWAsk0wAly4JoNdtGNz764jlU9gKL431VulAgMBAAGjVDBSMA4GA1UdDwEB/wQE
+# AwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTIftJqhSobyhmYBAcnz1AQ
+# T2ioojAQBgkrBgEEAYI3FQEEAwIBADANBgkqhkiG9w0BAQwFAAOCAgEAr2rd5hnn
+# LZRDGU7L6VCVZKUDkQKL4jaAOxWiUsIWGbZqWl10QzD0m/9gdAmxIR6QFm3FJI9c
+# Zohj9E/MffISTEAQiwGf2qnIrvKVG8+dBetJPnSgaFvlVixlHIJ+U9pW2UYXeZJF
+# xBA2CFIpF8svpvJ+1Gkkih6PsHMNzBxKq7Kq7aeRYwFkIqgyuH4yKLNncy2RtNwx
+# AQv3Rwqm8ddK7VZgxCwIo3tAsLx0J1KH1r6I3TeKiW5niB31yV2g/rarOoDXGpc8
+# FzYiQR6sTdWD5jw4vU8w6VSp07YEwzJ2YbuwGMUrGLPAgNW3lbBeUU0i/OxYqujY
+# lLSlLu2S3ucYfCFX3VVj979tzR/SpncocMfiWzpbCNJbTsgAlrPhgzavhgplXHT2
+# 6ux6anSg8Evu75SjrFDyh+3XOjCDyft9V77l4/hByuVkrrOj7FjshZrM77nq81YY
+# uVxzmq/FdxeDWds3GhhyVKVB0rYjdaNDmuV3fJZ5t0GNv+zcgKCf0Xd1WF81E+Al
+# GmcLfc4l+gcK5GEh2NQc5QfGNpn0ltDGFf5Ozdeui53bFv0ExpK91IjmqaOqu/dk
+# ODtfzAzQNb50GQOmxapMomE2gj4d8yu8l13bS3g7LfU772Aj6PXsCyM2la+YZr9T
+# 03u4aUoqlmZpxJTG9F9urJh4iIAGXKKy7aIwggb+MIIE5qADAgECAhMzAAfqVHr/
+# 4Q/aDzAcAAAAB+pUMA0GCSqGSIb3DQEBDAUAMFoxCzAJBgNVBAYTAlVTMR4wHAYD
+# VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKzApBgNVBAMTIk1pY3Jvc29mdCBJ
+# RCBWZXJpZmllZCBDUyBFT0MgQ0EgMDIwHhcNMjYwNDA3MDcyODM1WhcNMjYwNDEw
+# MDcyODM1WjB8MQswCQYDVQQGEwJVUzEPMA0GA1UECBMGT3JlZ29uMRIwEAYDVQQH
+# EwlCZWF2ZXJ0b24xIzAhBgNVBAoTGlB5dGhvbiBTb2Z0d2FyZSBGb3VuZGF0aW9u
+# MSMwIQYDVQQDExpQeXRob24gU29mdHdhcmUgRm91bmRhdGlvbjCCAaIwDQYJKoZI
+# hvcNAQEBBQADggGPADCCAYoCggGBAND/lHfn3OCIvUzMUIL6OdsKJrpnvuRtahV1
+# 6NCf0YSqOQemwQw2bTIyTkgSFwY4WaCvfHzcliURiPidXiqy56OmeC19A95BarKA
+# UmKRv3bVpM0XEK7OLvMyRFNg9aPUi1nmdF3Vx02RI9p88wBHQR5nNIpOTXlwfONQ
+# klggyEZSxkBf+dCL6jtz4jiqoreiEmRwesOrtQxKNsRuezbumpmVMZGxrMQVLBIX
+# OWG9a3GS6Sqfi+cJgxQhSKa9JENPRojyxOyVG8vdwJQiMqSjm2ZMFAkIkSWBQSfx
+# WjrRmw8/20WaBENattpqb7/cjX7zwimJ86uV48D8AQIGzAxfYAySG6NG9iMfU5S5
+# wzDFpiCuXyfrlgAbZu4fnBIyOmGcq01XxruzJ3FcdLMif5YXZU+n30XOaJfgY9/x
+# Gq2HiEIQF5MeuxknfD+vYi/GXGtC/nlKS0Tx91+YXt6RctxgJEwpZCGzFZmmaiUa
+# Y0GBp4jzXXwLqX8T15lgxAGoqoPvvwIDAQABo4ICGTCCAhUwDAYDVR0TAQH/BAIw
+# ADAOBgNVHQ8BAf8EBAMCB4AwPAYDVR0lBDUwMwYKKwYBBAGCN2EBAAYIKwYBBQUH
+# AwMGGysGAQQBgjdhgqKNuwqmkohkgZH0oEWCk/3hbzAdBgNVHQ4EFgQUy3N6DzeS
+# y91jju8Ihmm3r+5AO58wHwYDVR0jBBgwFoAUZZ9RzoVofy+KRYiq3acxux4NAF4w
+# ZwYDVR0fBGAwXjBcoFqgWIZWaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9w
+# cy9jcmwvTWljcm9zb2Z0JTIwSUQlMjBWZXJpZmllZCUyMENTJTIwRU9DJTIwQ0El
+# MjAwMi5jcmwwgaUGCCsGAQUFBwEBBIGYMIGVMGQGCCsGAQUFBzAChlhodHRwOi8v
+# d3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY3Jvc29mdCUyMElEJTIw
+# VmVyaWZpZWQlMjBDUyUyMEVPQyUyMENBJTIwMDIuY3J0MC0GCCsGAQUFBzABhiFo
+# dHRwOi8vb25lb2NzcC5taWNyb3NvZnQuY29tL29jc3AwZgYDVR0gBF8wXTBRBgwr
+# BgEEAYI3TIN9AQEwQTA/BggrBgEFBQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQu
+# Y29tL3BraW9wcy9Eb2NzL1JlcG9zaXRvcnkuaHRtMAgGBmeBDAEEATANBgkqhkiG
+# 9w0BAQwFAAOCAgEAPPwJPfkrkQMH39/iTBbir6tGnQpLCpOuP1A6mmKp22GxCG0/
+# 1IPx4QK1qXpy8hYd/G9ySDSYu3DSg22/icSmGSxdcI3zoRsj9vdJeesQrxtK8v9y
+# 4zMxN5TaLV5CmatSUZPyX1t7Tee9wiLBUeZIj+3Lg2gNUsdvavywRYxSYkWGuGaM
+# jGtJrs4PoJW3f4KkOc5mShCpUgl4Mo9ZO+ChcQpKEP99UJ9CXB9wrNzXnEOTyGnR
+# f1sYklPqBifC7hrnKIPZiJte1efmGeExmspWewmUSNXCIGenDAN8XDut2yi1iSSQ
+# n1VtL6deCRhS1cTn+FAzy2q7a/8Jhhq+HUlcJwRGtrxgKZHrwEvGRvIWNK5l1rKl
+# Q+WQ7RqRrH6PpSfR/xoptfpJX9LNUoHS0m114HcE2xk2hbv+U/5ZgxUtSd4MbF7/
+# C8eShz4Os8CznYXJ/d+kfvoyEqKE9VCbc4BUC+w1iufQOPo4tRvK4TFJu1N4IqJk
+# NsChWXUef7lIT5CoaJw4np0dVS2NosmRCxi1dMyADzqFNDXGKQxq5k6MpnXbevL5
+# JdcznhhxgwRUcwNK/3f9WSaU2mnI+6tHrnATteL7Ct6FzZWjqWDbURkU66bRqrBh
+# +u5KyLZAAQXTfdsaDUfxtElQJf5wROgYvwnW1dGvujgc+XKVvf1VT3GSFRIwggda
+# MIIFQqADAgECAhMzAAAABft6XDITYd9dAAAAAAAFMA0GCSqGSIb3DQEBDAUAMGMx
+# CzAJBgNVBAYTAlVTMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xNDAy
+# BgNVBAMTK01pY3Jvc29mdCBJRCBWZXJpZmllZCBDb2RlIFNpZ25pbmcgUENBIDIw
+# MjEwHhcNMjEwNDEzMTczMTUzWhcNMjYwNDEzMTczMTUzWjBaMQswCQYDVQQGEwJV
+# UzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSswKQYDVQQDEyJNaWNy
+# b3NvZnQgSUQgVmVyaWZpZWQgQ1MgRU9DIENBIDAyMIICIjANBgkqhkiG9w0BAQEF
+# AAOCAg8AMIICCgKCAgEA0hqZfD8ykKTA6CDbWvshmBpDoBf7Lv132RVuSqVwQO3a
+# ALLkuRnnTIoRmMGo0fIMQrtwR6UHB06xdqOkAfqB6exubXTHu44+duHUCdE4ngjE
+# LBQyluMuSOnHaEdveIbt31OhMEX/4nQkph4+Ah0eR4H2sTRrVKmKrlOoQlhia73Q
+# g2dHoitcX1uT1vW3Knpt9Mt76H7ZHbLNspMZLkWBabKMl6BdaWZXYpPGdS+qY80g
+# DaNCvFq0d10UMu7xHesIqXpTDT3Q3AeOxSylSTc/74P3og9j3OuemEFauFzL55t1
+# MvpadEhQmD8uFMxFv/iZOjwvcdY1zhanVLLyplz13/NzSoU3QjhPdqAGhRIwh/YD
+# zo3jCdVJgWQRrW83P3qWFFkxNiME2iO4IuYgj7RwseGwv7I9cxOyaHihKMdT9Neo
+# SjpSNzVnKKGcYMtOdMtKFqoV7Cim2m84GmIYZTBorR/Po9iwlasTYKFpGZqdWKyY
+# nJO2FV8oMmWkIK1iagLLgEt6ZaR0rk/1jUYssyTiRqWr84Qs3XL/V5KUBEtUEQfQ
+# /4RtnI09uFFUIGJZV9mD/xOUksWodGrCQSem6Hy261xMJAHqTqMuDKgwi8xk/mfl
+# r7yhXPL73SOULmu1Aqu4I7Gpe6QwNW2TtQBxM3vtSTmdPW6rK5y0gED51RjsyK0C
+# AwEAAaOCAg4wggIKMA4GA1UdDwEB/wQEAwIBhjAQBgkrBgEEAYI3FQEEAwIBADAd
+# BgNVHQ4EFgQUZZ9RzoVofy+KRYiq3acxux4NAF4wVAYDVR0gBE0wSzBJBgRVHSAA
+# MEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMv
+# RG9jcy9SZXBvc2l0b3J5Lmh0bTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTAS
+# BgNVHRMBAf8ECDAGAQH/AgEAMB8GA1UdIwQYMBaAFNlBKbAPD2Ns72nX9c0pnqRI
+# ajDmMHAGA1UdHwRpMGcwZaBjoGGGX2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9w
+# a2lvcHMvY3JsL01pY3Jvc29mdCUyMElEJTIwVmVyaWZpZWQlMjBDb2RlJTIwU2ln
+# bmluZyUyMFBDQSUyMDIwMjEuY3JsMIGuBggrBgEFBQcBAQSBoTCBnjBtBggrBgEF
+# BQcwAoZhaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNy
+# b3NvZnQlMjBJRCUyMFZlcmlmaWVkJTIwQ29kZSUyMFNpZ25pbmclMjBQQ0ElMjAy
+# MDIxLmNydDAtBggrBgEFBQcwAYYhaHR0cDovL29uZW9jc3AubWljcm9zb2Z0LmNv
+# bS9vY3NwMA0GCSqGSIb3DQEBDAUAA4ICAQBFSWDUd08X4g5HzvVfrB1SiV8pk6XP
+# HT9jPkCmvU/uvBzmZRAjYk2gKYR3pXoStRJaJ/lhjC5Dq/2R7P1YRZHCDYyK0zvS
+# RMdE6YQtgGjmsdhzD0nCS6hVVcgfmNQscPJ1WHxbvG5EQgYQ0ZED1FN0MOPQzWe1
+# zbH5Va0dSxtnodBVRjnyDYEm7sNEcvJHTG3eXzAyd00E5KDCsEl4z5O0mvXqwaH2
+# PS0200E6P4WqLwgs/NmUu5+Aa8Lw/2En2VkIW7Pkir4Un1jG6+tj/ehuqgFyUPPC
+# h6kbnvk48bisi/zPjAVkj7qErr7fSYICCzJ4s4YUNVVHgdoFn2xbW7ZfBT3QA9zf
+# hq9u4ExXbrVD5rxXSTFEUg2gzQq9JHxsdHyMfcCKLFQOXODSzcYeLpCd+r6GcoDB
+# ToyPdKccjC6mAq6+/hiMDnpvKUIHpyYEzWUeattyKXtMf+QrJeQ+ny5jBL+xqdOO
+# PEz3dg7qn8/oprUrUbGLBv9fWm18fWXdAv1PCtLL/acMLtHoyeSVMKQYqDHb3Qm0
+# uQ+NQ0YE4kUxSQa+W/cCzYAI32uN0nb9M4Mr1pj4bJZidNkM4JyYqezohILxYkgH
+# bboJQISrQWrm5RYdyhKBpptJ9JJn0Z63LjdnzlOUxjlsAbQir2Wmz/OJE703BbHm
+# QZRwzPx1vu7S5zCCB54wggWGoAMCAQICEzMAAAAHh6M0o3uljhwAAAAAAAcwDQYJ
+# KoZIhvcNAQEMBQAwdzELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1pY3Jvc29mdCBD
+# b3Jwb3JhdGlvbjFIMEYGA1UEAxM/TWljcm9zb2Z0IElkZW50aXR5IFZlcmlmaWNh
+# dGlvbiBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAyMDIwMB4XDTIxMDQwMTIw
+# MDUyMFoXDTM2MDQwMTIwMTUyMFowYzELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1p
+# Y3Jvc29mdCBDb3Jwb3JhdGlvbjE0MDIGA1UEAxMrTWljcm9zb2Z0IElEIFZlcmlm
+# aWVkIENvZGUgU2lnbmluZyBQQ0EgMjAyMTCCAiIwDQYJKoZIhvcNAQEBBQADggIP
+# ADCCAgoCggIBALLwwK8ZiCji3VR6TElsaQhVCbRS/3pK+MHrJSj3Zxd3KU3rlfL3
+# qrZilYKJNqztA9OQacr1AwoNcHbKBLbsQAhBnIB34zxf52bDpIO3NJlfIaTE/xrw
+# eLoQ71lzCHkD7A4As1Bs076Iu+mA6cQzsYYH/Cbl1icwQ6C65rU4V9NQhNUwgrx9
+# rGQ//h890Q8JdjLLw0nV+ayQ2Fbkd242o9kH82RZsH3HEyqjAB5a8+Ae2nPIPc8s
+# ZU6ZE7iRrRZywRmrKDp5+TcmJX9MRff241UaOBs4NmHOyke8oU1TYrkxh+YeHgfW
+# o5tTgkoSMoayqoDpHOLJs+qG8Tvh8SnifW2Jj3+ii11TS8/FGngEaNAWrbyfNrC6
+# 9oKpRQXY9bGH6jn9NEJv9weFxhTwyvx9OJLXmRGbAUXN1U9nf4lXezky6Uh/cgjk
+# Vd6CGUAf0K+Jw+GE/5VpIVbcNr9rNE50Sbmy/4RTCEGvOq3GhjITbCa4crCzTTHg
+# YYjHs1NbOc6brH+eKpWLtr+bGecy9CrwQyx7S/BfYJ+ozst7+yZtG2wR461uckFu
+# 0t+gCwLdN0A6cFtSRtR8bvxVFyWwTtgMMFRuBa3vmUOTnfKLsLefRaQcVTgRnzeL
+# zdpt32cdYKp+dhr2ogc+qM6K4CBI5/j4VFyC4QFeUP2YAidLtvpXRRo3AgMBAAGj
+# ggI1MIICMTAOBgNVHQ8BAf8EBAMCAYYwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0O
+# BBYEFNlBKbAPD2Ns72nX9c0pnqRIajDmMFQGA1UdIARNMEswSQYEVR0gADBBMD8G
+# CCsGAQUFBwIBFjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL0RvY3Mv
+# UmVwb3NpdG9yeS5odG0wGQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwDwYDVR0T
+# AQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTIftJqhSobyhmYBAcnz1AQT2ioojCBhAYD
+# VR0fBH0wezB5oHegdYZzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9j
+# cmwvTWljcm9zb2Z0JTIwSWRlbnRpdHklMjBWZXJpZmljYXRpb24lMjBSb290JTIw
+# Q2VydGlmaWNhdGUlMjBBdXRob3JpdHklMjAyMDIwLmNybDCBwwYIKwYBBQUHAQEE
+# gbYwgbMwgYEGCCsGAQUFBzAChnVodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtp
+# b3BzL2NlcnRzL01pY3Jvc29mdCUyMElkZW50aXR5JTIwVmVyaWZpY2F0aW9uJTIw
+# Um9vdCUyMENlcnRpZmljYXRlJTIwQXV0aG9yaXR5JTIwMjAyMC5jcnQwLQYIKwYB
+# BQUHMAGGIWh0dHA6Ly9vbmVvY3NwLm1pY3Jvc29mdC5jb20vb2NzcDANBgkqhkiG
+# 9w0BAQwFAAOCAgEAfyUqnv7Uq+rdZgrbVyNMul5skONbhls5fccPlmIbzi+OwVdP
+# Q4H55v7VOInnmezQEeW4LqK0wja+fBznANbXLB0KrdMCbHQpbLvG6UA/Xv2pfpVI
+# E1CRFfNF4XKO8XYEa3oW8oVH+KZHgIQRIwAbyFKQ9iyj4aOWeAzwk+f9E5StNp5T
+# 8FG7/VEURIVWArbAzPt9ThVN3w1fAZkF7+YU9kbq1bCR2YD+MtunSQ1Rft6XG7b4
+# e0ejRA7mB2IoX5hNh3UEauY0byxNRG+fT2MCEhQl9g2i2fs6VOG19CNep7SquKaB
+# jhWmirYyANb0RJSLWjinMLXNOAga10n8i9jqeprzSMU5ODmrMCJE12xS/NWShg/t
+# uLjAsKP6SzYZ+1Ry358ZTFcx0FS/mx2vSoU8s8HRvy+rnXqyUJ9HBqS0DErVLjQw
+# K8VtsBdekBmdTbQVoCgPCqr+PDPB3xajYnzevs7eidBsM71PINK2BoE2UfMwxCCX
+# 3mccFgx6UsQeRSdVVVNSyALQe6PT12418xon2iDGE81OGCreLzDcMAZnrUAx4XQL
+# Uz6ZTl65yPUiOh3k7Yww94lDf+8oG2oZmDh5O1Qe38E+M3vhKwmzIeoB1dVLlz4i
+# 3IpaDcR+iuGjH2TdaC1ZOmBXiCRKJLj4DT2uhJ04ji+tHD6n58vhavFIrmcxghrm
+# MIIa4gIBATBxMFoxCzAJBgNVBAYTAlVTMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y
+# cG9yYXRpb24xKzApBgNVBAMTIk1pY3Jvc29mdCBJRCBWZXJpZmllZCBDUyBFT0Mg
+# Q0EgMDICEzMAB+pUev/hD9oPMBwAAAAH6lQwDQYJYIZIAWUDBAIBBQCggbIwGQYJ
+# KoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQB
+# gjcCARUwLwYJKoZIhvcNAQkEMSIEICpXe3RS3b2coD0CJveEHlglqtPUYZ2FqSrO
+# UfP6C6Y4MEYGCisGAQQBgjcCAQwxODA2oDCALgBQAHkAdABoAG8AbgAgADMALgAx
+# ADQALgA0ACAAKAAyADMAMQAxADYAZgA5ACmhAoAAMA0GCSqGSIb3DQEBAQUABIIB
+# gHaAK9dRSQxQvAiBXu8BOjm/3WL7Hdh4vVPDdI7TVKrNk9GE/8isBY5v3SDISaGV
+# VzilkWjgUJX1tp5Wq3Ix9zJVToVG3kaHlNrEjb+cK8oqkMJqIS0GTrS70Xs1UPKk
+# PNSzUfi1ddmCW9Up3bmvR7e5SotcgAKysucyRPHmhDZKdC0tM3FdzqbFMs0QV1QL
+# gUzdgWEMqRQp7PN9Y3uHeO7/FUUGkEGBHuKq9kGXbnYGwZEazzy6Uxx2Nd47iCsu
+# cEiOdpecA13fGE6lnM+uTZGOvshUVQeTIkr5pb2NS+lXF+CEq6uqMntdAfblYzCs
+# gTCp8OflGXHVnbo4p8SsrGaBV7KnbzCk7uuEqW3SJiQfMrkQjvjc1cRwQvOVVaPj
+# F1Qknn7KmxukU7FEIwyGXPKQ5OG8oUugOTS/5aqSbpmY6HuTbbWH/7MULJlfikOq
+# zuteCVjvd18Y6LMk+mK1x9PAtoHmZuMIedP6rHNE8admogur39k1IhRJIcG9S8Kk
+# 3aGCGBEwghgNBgorBgEEAYI3AwMBMYIX/TCCF/kGCSqGSIb3DQEHAqCCF+owghfm
+# AgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFiBgsqhkiG9w0BCRABBKCCAVEEggFNMIIB
+# SQIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFlAwQCAQUABCASSpbbgjgopvaqW5Og
+# e0ZpH9C7TBmrsY+qwoqXJMywiwIGacJyyM2cGBMyMDI2MDQwNzE2MTkwNy44NjJa
+# MASAAgH0oIHhpIHeMIHbMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv
+# bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0
+# aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25zMScwJQYD
+# VQQLEx5uU2hpZWxkIFRTUyBFU046N0QwMC0wNUUwLUQ5NDcxNTAzBgNVBAMTLE1p
+# Y3Jvc29mdCBQdWJsaWMgUlNBIFRpbWUgU3RhbXBpbmcgQXV0aG9yaXR5oIIPITCC
+# B4IwggVqoAMCAQICEzMAAAAF5c8P/2YuyYcAAAAAAAUwDQYJKoZIhvcNAQEMBQAw
+# dzELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjFI
+# MEYGA1UEAxM/TWljcm9zb2Z0IElkZW50aXR5IFZlcmlmaWNhdGlvbiBSb290IENl
+# cnRpZmljYXRlIEF1dGhvcml0eSAyMDIwMB4XDTIwMTExOTIwMzIzMVoXDTM1MTEx
+# OTIwNDIzMVowYTELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw
+# b3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFB1YmxpYyBSU0EgVGltZXN0YW1w
+# aW5nIENBIDIwMjAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCefOdS
+# Y/3gxZ8FfWO1BiKjHB7X55cz0RMFvWVGR3eRwV1wb3+yq0OXDEqhUhxqoNv6iYWK
+# jkMcLhEFxvJAeNcLAyT+XdM5i2CgGPGcb95WJLiw7HzLiBKrxmDj1EQB/mG5eEiR
+# BEp7dDGzxKCnTYocDOcRr9KxqHydajmEkzXHOeRGwU+7qt8Md5l4bVZrXAhK+WSk
+# 5CihNQsWbzT1nRliVDwunuLkX1hyIWXIArCfrKM3+RHh+Sq5RZ8aYyik2r8HxT+l
+# 2hmRllBvE2Wok6IEaAJanHr24qoqFM9WLeBUSudz+qL51HwDYyIDPSQ3SeHtKog0
+# ZubDk4hELQSxnfVYXdTGncaBnB60QrEuazvcob9n4yR65pUNBCF5qeA4QwYnilBk
+# fnmeAjRN3LVuLr0g0FXkqfYdUmj1fFFhH8k8YBozrEaXnsSL3kdTD01X+4LfIWOu
+# FzTzuoslBrBILfHNj8RfOxPgjuwNvE6YzauXi4orp4Sm6tF245DaFOSYbWFK5ZgG
+# 6cUY2/bUq3g3bQAqZt65KcaewEJ3ZyNEobv35Nf6xN6FrA6jF9447+NHvCjeWLCQ
+# Z3M8lgeCcnnhTFtyQX3XgCoc6IRXvFOcPVrr3D9RPHCMS6Ckg8wggTrtIVnY8yjb
+# vGOUsAdZbeXUIQAWMs0d3cRDv09SvwVRd61evQIDAQABo4ICGzCCAhcwDgYDVR0P
+# AQH/BAQDAgGGMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRraSg6NS9IY0DP
+# e9ivSek+2T3bITBUBgNVHSAETTBLMEkGBFUdIAAwQTA/BggrBgEFBQcCARYzaHR0
+# cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9Eb2NzL1JlcG9zaXRvcnkuaHRt
+# MBMGA1UdJQQMMAoGCCsGAQUFBwMIMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBB
+# MA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUyH7SaoUqG8oZmAQHJ89QEE9o
+# qKIwgYQGA1UdHwR9MHsweaB3oHWGc2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9w
+# a2lvcHMvY3JsL01pY3Jvc29mdCUyMElkZW50aXR5JTIwVmVyaWZpY2F0aW9uJTIw
+# Um9vdCUyMENlcnRpZmljYXRlJTIwQXV0aG9yaXR5JTIwMjAyMC5jcmwwgZQGCCsG
+# AQUFBwEBBIGHMIGEMIGBBggrBgEFBQcwAoZ1aHR0cDovL3d3dy5taWNyb3NvZnQu
+# Y29tL3BraW9wcy9jZXJ0cy9NaWNyb3NvZnQlMjBJZGVudGl0eSUyMFZlcmlmaWNh
+# dGlvbiUyMFJvb3QlMjBDZXJ0aWZpY2F0ZSUyMEF1dGhvcml0eSUyMDIwMjAuY3J0
+# MA0GCSqGSIb3DQEBDAUAA4ICAQBfiHbHfm21WhV150x4aPpO4dhEmSUVpbixNDmv
+# 6TvuIHv1xIs174bNGO/ilWMm+Jx5boAXrJxagRhHQtiFprSjMktTliL4sKZyt2i+
+# SXncM23gRezzsoOiBhv14YSd1Klnlkzvgs29XNjT+c8hIfPRe9rvVCMPiH7zPZcw
+# 5nNjthDQ+zD563I1nUJ6y59TbXWsuyUsqw7wXZoGzZwijWT5oc6GvD3HDokJY401
+# uhnj3ubBhbkR83RbfMvmzdp3he2bvIUztSOuFzRqrLfEvsPkVHYnvH1wtYyrt5vS
+# hiKheGpXa2AWpsod4OJyT4/y0dggWi8g/tgbhmQlZqDUf3UqUQsZaLdIu/XSjgoZ
+# qDjamzCPJtOLi2hBwL+KsCh0Nbwc21f5xvPSwym0Ukr4o5sCcMUcSy6TEP7uMV8R
+# X0eH/4JLEpGyae6Ki8JYg5v4fsNGif1OXHJ2IWG+7zyjTDfkmQ1snFOTgyEX8qBp
+# efQbF0fx6URrYiarjmBprwP6ZObwtZXJ23jK3Fg/9uqM3j0P01nzVygTppBabzxP
+# Ah/hHhhls6kwo3QLJ6No803jUsZcd4JQxiYHHc+Q/wAMcPUnYKv/q2O444LO1+n6
+# j01z5mggCSlRwD9faBIySAcA9S8h22hIAcRQqIGEjolCK9F6nK9ZyX4lhthsGHum
+# aABdWzCCB5cwggV/oAMCAQICEzMAAABV2d1pJij5+OIAAAAAAFUwDQYJKoZIhvcN
+# AQEMBQAwYTELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3Jh
+# dGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFB1YmxpYyBSU0EgVGltZXN0YW1waW5n
+# IENBIDIwMjAwHhcNMjUxMDIzMjA0NjQ5WhcNMjYxMDIyMjA0NjQ5WjCB2zELMAkG
+# A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx
+# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjElMCMGA1UECxMcTWljcm9z
+# b2Z0IEFtZXJpY2EgT3BlcmF0aW9uczEnMCUGA1UECxMeblNoaWVsZCBUU1MgRVNO
+# OjdEMDAtMDVFMC1EOTQ3MTUwMwYDVQQDEyxNaWNyb3NvZnQgUHVibGljIFJTQSBU
+# aW1lIFN0YW1waW5nIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC
+# AgoCggIBAL25H5IeWUiz9DAlFmn2sPymaFWbvYkMfK+ScIWb3a1IvOlIwghUDjY0
+# Gp6yMRhfYURiGS0GedIB6ywvuH6VBCX3+bdOFcAclgtv21jrpOjZmk4fSaT2Q3Bs
+# zUfeUJa8o3xI7ZfoMY9dszTxHQAz6ZVX87fHGEVhQcfxW33IdPJOj/ae419qtYxT
+# 21MVmCfsTshgtWioQxmOW/vMC9/b+qgtBxSMf798vm3qfmhF6KCvFaHlivrM32hY
+# 16PGE3L0PFC+LM7vRxU7mTb+r76CeybvqOWk4+dbKYftPhV1t/E5S/6wwXeYmu/Y
+# 7JC7Tnh2w45G5Y4pcM3oHMb/YuPRdOWa0v+RC2QgmNVWqjuxDiylWscXQDuaMtb2
+# 9AcdGUVV9ZsRY2M2sthAtOdZOshiR5ufMtaHtiCkWv0jNfgUxrHurxzYuUNneWZ6
+# EfQDgFAw8CSCKkSOK2c9jEop4ddVq10xvbqxdrqMneVXvvIcXrPQAXj9j2ECpV2E
+# wMb3Wnmpw00P78JpzPsk3Fs61ZvOGd/F1RcOBu6f2TWdp7HL7+rq7tgHr13Mldbf
+# IWu4lpoYYE1gTQa1Yrg5XN4j7zs9klT2z3qocmPzV8DWQgIHNh+aTs7bujMEMQyI
+# 7Xt1zPxZCgcR6H0tmmzU/9BxvsWbRalCQ2sYGyWupTdc4e7KY7kPAgMBAAGjggHL
+# MIIBxzAdBgNVHQ4EFgQUVgRfEG3cCAPwyL+pyRbKwdesZbYwHwYDVR0jBBgwFoAU
+# a2koOjUvSGNAz3vYr0npPtk92yEwbAYDVR0fBGUwYzBhoF+gXYZbaHR0cDovL3d3
+# dy5taWNyb3NvZnQuY29tL3BraW9wcy9jcmwvTWljcm9zb2Z0JTIwUHVibGljJTIw
+# UlNBJTIwVGltZXN0YW1waW5nJTIwQ0ElMjAyMDIwLmNybDB5BggrBgEFBQcBAQRt
+# MGswaQYIKwYBBQUHMAKGXWh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMv
+# Y2VydHMvTWljcm9zb2Z0JTIwUHVibGljJTIwUlNBJTIwVGltZXN0YW1waW5nJTIw
+# Q0ElMjAyMDIwLmNydDAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB/wQMMAoGCCsGAQUF
+# BwMIMA4GA1UdDwEB/wQEAwIHgDBmBgNVHSAEXzBdMFEGDCsGAQQBgjdMg30BATBB
+# MD8GCCsGAQUFBwIBFjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL0Rv
+# Y3MvUmVwb3NpdG9yeS5odG0wCAYGZ4EMAQQCMA0GCSqGSIb3DQEBDAUAA4ICAQBS
+# HuGSVHvalCnFnlsqXIQefH1xP2SFr9g+Vz+f5P7QeywjfQb5jUlSmd1XnJUDPe/M
+# HxL7r3TEElL+mNtG6CDPAytStSFPXD9tTBtBMYh8Wqo64pH9qm361yIqeBH979mz
+# WCkMQsTd0nM6dUl9B+7qiti+ToXwxIl39eYqLuYYfhD2mqqePXMzUKSQzkf73yYI
+# VHP6nLJQz4aAmaWcfG9jg78sBkDV8KpW7JgktuLhphJEN1B+SVHjenPdcmrFXIUu
+# /K4jK5ukfWaQIjuaXzSjBlNjC5tQN6adPfA3GxUwHPeR4ekL5If/9vBf13tmzBW+
+# gy+0sNGTveb9IL9GU8iX8UvywsX62nhCCPRUhTigDBKdczRUrNrntBhowbfchBDF
+# ML8avRMRc9Gmc2JvIryX336SFQ51//q1UU2HMSJEMhWLJSIWJVhfUowsOa+PampI
+# zETYfFvTu2mqKJUlWZXkGYxrdCvCczJcqeoadpW1ul6kcdnDh228SQ8ZhDc6IRlM
+# 4iNd5SNoNgX+aom3wuGyjUaSaPZWxPB1G2NKiYhPLt0lPHg0Gskj1zhISY8UQkMM
+# Dr3o2JgRuT+wnJEDQUp55ddvhSkSoD6I9DL/s+TjIY/c9jLaW5xywJHqdKHUApRM
+# sghv7kebSua1upmR+TquelFktDSOjVdSRkuya4uoxTGCB0Mwggc/AgEBMHgwYTEL
+# MAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAG
+# A1UEAxMpTWljcm9zb2Z0IFB1YmxpYyBSU0EgVGltZXN0YW1waW5nIENBIDIwMjAC
+# EzMAAABV2d1pJij5+OIAAAAAAFUwDQYJYIZIAWUDBAIBBQCgggScMBEGCyqGSIb3
+# DQEJEAIPMQIFADAaBgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwHAYJKoZIhvcN
+# AQkFMQ8XDTI2MDQwNzE2MTkwN1owLwYJKoZIhvcNAQkEMSIEIO1tbnR5rJvq+GXf
+# 4bgeV3HDcP+8Ud48R8sLBQBLI+rBMIG5BgsqhkiG9w0BCRACLzGBqTCBpjCBozCB
+# oAQg2Lk8l2SGYru/ff7+D2qrJnkswcYdK6pGKu7GGGr4/s0wfDBlpGMwYTELMAkG
+# A1UEBhMCVVMxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UE
+# AxMpTWljcm9zb2Z0IFB1YmxpYyBSU0EgVGltZXN0YW1waW5nIENBIDIwMjACEzMA
+# AABV2d1pJij5+OIAAAAAAFUwggNeBgsqhkiG9w0BCRACEjGCA00wggNJoYIDRTCC
+# A0EwggIpAgEBMIIBCaGB4aSB3jCB2zELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldh
+# c2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBD
+# b3Jwb3JhdGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJpY2EgT3BlcmF0aW9u
+# czEnMCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOjdEMDAtMDVFMC1EOTQ3MTUwMwYD
+# VQQDEyxNaWNyb3NvZnQgUHVibGljIFJTQSBUaW1lIFN0YW1waW5nIEF1dGhvcml0
+# eaIjCgEBMAcGBSsOAwIaAxUAHTtUAYJlv7bgWVeRBo4X7FeHDeqgZzBlpGMwYTEL
+# MAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAG
+# A1UEAxMpTWljcm9zb2Z0IFB1YmxpYyBSU0EgVGltZXN0YW1waW5nIENBIDIwMjAw
+# DQYJKoZIhvcNAQELBQACBQDtf2YdMCIYDzIwMjYwNDA3MTExNjQ1WhgPMjAyNjA0
+# MDgxMTE2NDVaMHQwOgYKKwYBBAGEWQoEATEsMCowCgIFAO1/Zh0CAQAwBwIBAAIC
+# Ew4wBwIBAAICHEcwCgIFAO2At50CAQAwNgYKKwYBBAGEWQoEAjEoMCYwDAYKKwYB
+# BAGEWQoDAqAKMAgCAQACAwehIKEKMAgCAQACAwGGoDANBgkqhkiG9w0BAQsFAAOC
+# AQEAj/DYTzrEaV8hMcUsbVdSBihFoM0FgJoy99LL0e/ztow4UwgL27kPtwlbyVfW
+# Onw+tLBT2sbsS4yIj+QnTUoHeqNA/Ue9FPkPH801qthzRDZ4UTwz09KgVfKchw0Y
+# jGTSpL8GQL6cAYgFuf/Zh0qEVwYW4OxiyKEMKpzYdqoNwRx9LrCgE0km4WZ3bDBw
+# VFCKcd176gCpV5Yle8MbDWFizSUrylp7v7q/Knx0BrWs8Q17RUHnYAyehZCD0d0H
+# zXEClh+sKxScyk8TjI2o7MKau8YRHzeLDN9wj24hJ9URkNThjDhY3ruNMvves5xS
+# fKmhpIqWsb5ZvZW4hmbqdxwWbDANBgkqhkiG9w0BAQEFAASCAgA6C6+LOeV/wer0
+# SB0XI0QO89oeTrCbxFNxmKegd8rz7ERBKLcxLh9WFOfDf4t5oi0RlPP8cFwByVIb
+# TIgtggkIq5uN6/qJ9+iF+w7a4123dTuQSAi35d7VMC/q8rtHL6ev5zeKeYVnov5C
+# JqmlUyVL5NPRxrUaWeXNXOmOMZwmNTqZspf/eadKTiBwDVJZ4DLXkrZNoR3Gd7Wq
+# w6N/gt5MDhL26NU2xnysk2pXRdTna4Fh545XD2YGqZAJdJ6nzv/uSQBDnAL7uOV1
+# U5I+hEBgbCMLAkNzTJUprQFaz1X9taVTbYQOE8IGkseOg1nw8kDc5nYga3kNZQ/k
+# u4gbx3opZuxUL43teYm17AJwHphWgQojwdvi+OozR0bS27xVK5O5Y+5yReg4U6DG
+# XjuaFzxxA8efdy7DDETfFWhO6fSfREWcf+Nb9uA7i+qPOJvK4ls5p3yTwJgG1KUr
+# UNFhIw5LZZxn+yAArVY712VcCDsdBQiJ3ZbS90/AXjtsgFP/Cr99YhoKi7WNOtJ9
+# CHXI8Y5RMy3AAGBXmSJ2f1bEVX7Ya1Wdi7UlwgYbchhfL25XRKonczGNxAzymO6t
+# MCAweDvuELomErT+70YfQXd1kye4j5KiCQXYtpedFgb9sDqGv5j7c5ACHVyAsvPA
+# Wx3T3b6yi2pK9C6xC8g5cmjf8c7Rnw==
+# SIG # End signature block
diff --git a/PythonLib/full/webbrowser.py b/PythonLib/full/webbrowser.py
index f2e23940..0e0b5034 100644
--- a/PythonLib/full/webbrowser.py
+++ b/PythonLib/full/webbrowser.py
@@ -163,6 +163,12 @@ def open_new(self, url):
def open_new_tab(self, url):
return self.open(url, 2)
+ @staticmethod
+ def _check_url(url):
+ """Ensures that the URL is safe to pass to subprocesses as a parameter"""
+ if url and url.lstrip().startswith("-"):
+ raise ValueError(f"Invalid URL (leading dash disallowed): {url!r}")
+
class GenericBrowser(BaseBrowser):
"""Class for all browsers started with a command
@@ -180,6 +186,7 @@ def __init__(self, name):
def open(self, url, new=0, autoraise=True):
sys.audit("webbrowser.open", url)
+ self._check_url(url)
cmdline = [self.name] + [arg.replace("%s", url)
for arg in self.args]
try:
@@ -200,6 +207,7 @@ def open(self, url, new=0, autoraise=True):
cmdline = [self.name] + [arg.replace("%s", url)
for arg in self.args]
sys.audit("webbrowser.open", url)
+ self._check_url(url)
try:
if sys.platform[:3] == 'win':
p = subprocess.Popen(cmdline)
@@ -266,6 +274,7 @@ def _invoke(self, args, remote, autoraise, url=None):
def open(self, url, new=0, autoraise=True):
sys.audit("webbrowser.open", url)
+ self._check_url(url)
if new == 0:
action = self.remote_action
elif new == 1:
@@ -357,6 +366,7 @@ class Konqueror(BaseBrowser):
def open(self, url, new=0, autoraise=True):
sys.audit("webbrowser.open", url)
+ self._check_url(url)
# XXX Currently I know no way to prevent KFM from opening a new win.
if new == 2:
action = "newTab"
@@ -588,6 +598,7 @@ def register_standard_browsers():
class WindowsDefault(BaseBrowser):
def open(self, url, new=0, autoraise=True):
sys.audit("webbrowser.open", url)
+ self._check_url(url)
try:
os.startfile(url)
except OSError:
@@ -608,6 +619,7 @@ def __init__(self, name='default'):
def open(self, url, new=0, autoraise=True):
sys.audit("webbrowser.open", url)
+ self._check_url(url)
url = url.replace('"', '%22')
if self.name == 'default':
proto, _sep, _rest = url.partition(":")
@@ -644,7 +656,7 @@ def open(self, url, new=0, autoraise=True):
end
'''
- osapipe = os.popen("osascript", "w")
+ osapipe = os.popen("/usr/bin/osascript", "w")
if osapipe is None:
return False
@@ -664,6 +676,7 @@ def open(self, url, new=0, autoraise=True):
class IOSBrowser(BaseBrowser):
def open(self, url, new=0, autoraise=True):
sys.audit("webbrowser.open", url)
+ self._check_url(url)
# If ctypes isn't available, we can't open a browser
if objc is None:
return False
diff --git a/PythonLib/full/wsgiref/handlers.py b/PythonLib/full/wsgiref/handlers.py
index cafe872c..f8fe89f6 100644
--- a/PythonLib/full/wsgiref/handlers.py
+++ b/PythonLib/full/wsgiref/handlers.py
@@ -1,7 +1,7 @@
"""Base classes for server/gateway implementations"""
from .util import FileWrapper, guess_scheme, is_hop_by_hop
-from .headers import Headers
+from .headers import Headers, _name_disallowed_re
import sys, os, time
@@ -249,6 +249,8 @@ def start_response(self, status, headers,exc_info=None):
return self.write
def _validate_status(self, status):
+ if _name_disallowed_re.search(status):
+ raise ValueError("Control characters are not allowed in status")
if len(status) < 4:
raise AssertionError("Status must be at least 4 characters")
if not status[:3].isdigit():
diff --git a/PythonLib/full/wsgiref/headers.py b/PythonLib/full/wsgiref/headers.py
index e180a623..eb6ea6a4 100644
--- a/PythonLib/full/wsgiref/headers.py
+++ b/PythonLib/full/wsgiref/headers.py
@@ -9,7 +9,11 @@
# existence of which force quoting of the parameter value.
import re
tspecials = re.compile(r'[ \(\)<>@,;:\\"/\[\]\?=]')
-_control_chars_re = re.compile(r'[\x00-\x1F\x7F]')
+# Disallowed characters for headers and values.
+# HTAB (\x09) is allowed in header values, but
+# not in header names. (RFC 9110 Section 5.5)
+_name_disallowed_re = re.compile(r'[\x00-\x1F\x7F]')
+_value_disallowed_re = re.compile(r'[\x00-\x08\x0A-\x1F\x7F]')
def _formatparam(param, value=None, quote=1):
"""Convenience function to format and return a key=value pair.
@@ -36,13 +40,14 @@ def __init__(self, headers=None):
self._headers = headers
if __debug__:
for k, v in headers:
- self._convert_string_type(k)
- self._convert_string_type(v)
+ self._convert_string_type(k, name=True)
+ self._convert_string_type(v, name=False)
- def _convert_string_type(self, value):
+ def _convert_string_type(self, value, *, name):
"""Convert/check value type."""
if type(value) is str:
- if _control_chars_re.search(value):
+ regex = (_name_disallowed_re if name else _value_disallowed_re)
+ if regex.search(value):
raise ValueError("Control characters not allowed in headers")
return value
raise AssertionError("Header names/values must be"
@@ -56,14 +61,14 @@ def __setitem__(self, name, val):
"""Set the value of a header."""
del self[name]
self._headers.append(
- (self._convert_string_type(name), self._convert_string_type(val)))
+ (self._convert_string_type(name, name=True), self._convert_string_type(val, name=False)))
def __delitem__(self,name):
"""Delete all occurrences of a header, if present.
Does *not* raise an exception if the header is missing.
"""
- name = self._convert_string_type(name.lower())
+ name = self._convert_string_type(name.lower(), name=True)
self._headers[:] = [kv for kv in self._headers if kv[0].lower() != name]
def __getitem__(self,name):
@@ -90,13 +95,13 @@ def get_all(self, name):
fields deleted and re-inserted are always appended to the header list.
If no fields exist with the given name, returns an empty list.
"""
- name = self._convert_string_type(name.lower())
+ name = self._convert_string_type(name.lower(), name=True)
return [kv[1] for kv in self._headers if kv[0].lower()==name]
def get(self,name,default=None):
"""Get the first header value for 'name', or return 'default'"""
- name = self._convert_string_type(name.lower())
+ name = self._convert_string_type(name.lower(), name=True)
for k,v in self._headers:
if k.lower()==name:
return v
@@ -151,8 +156,8 @@ def setdefault(self,name,value):
and value 'value'."""
result = self.get(name)
if result is None:
- self._headers.append((self._convert_string_type(name),
- self._convert_string_type(value)))
+ self._headers.append((self._convert_string_type(name, name=True),
+ self._convert_string_type(value, name=False)))
return value
else:
return result
@@ -175,13 +180,13 @@ def add_header(self, _name, _value, **_params):
"""
parts = []
if _value is not None:
- _value = self._convert_string_type(_value)
+ _value = self._convert_string_type(_value, name=False)
parts.append(_value)
for k, v in _params.items():
- k = self._convert_string_type(k)
+ k = self._convert_string_type(k, name=True)
if v is None:
parts.append(k.replace('_', '-'))
else:
- v = self._convert_string_type(v)
+ v = self._convert_string_type(v, name=False)
parts.append(_formatparam(k.replace('_', '-'), v))
- self._headers.append((self._convert_string_type(_name), "; ".join(parts)))
+ self._headers.append((self._convert_string_type(_name, name=True), "; ".join(parts)))
diff --git a/PythonLib/full/zipfile/__init__.py b/PythonLib/full/zipfile/__init__.py
index ac2332e5..19aea290 100644
--- a/PythonLib/full/zipfile/__init__.py
+++ b/PythonLib/full/zipfile/__init__.py
@@ -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
diff --git a/PythonLib/full/zoneinfo/_common.py b/PythonLib/full/zoneinfo/_common.py
index 59f3f0ce..98668c15 100644
--- a/PythonLib/full/zoneinfo/_common.py
+++ b/PythonLib/full/zoneinfo/_common.py
@@ -67,6 +67,10 @@ def load_data(fobj):
f">{timecnt}{time_type}", fobj.read(timecnt * time_size)
)
trans_idx = struct.unpack(f">{timecnt}B", fobj.read(timecnt))
+
+ if max(trans_idx) >= typecnt:
+ raise ValueError("Invalid transition index found while reading TZif: "
+ f"{max(trans_idx)}")
else:
trans_list_utc = ()
trans_idx = ()
diff --git a/PythonLib/full/zoneinfo/_zoneinfo.py b/PythonLib/full/zoneinfo/_zoneinfo.py
index 3ffdb4c8..7063eb6a 100644
--- a/PythonLib/full/zoneinfo/_zoneinfo.py
+++ b/PythonLib/full/zoneinfo/_zoneinfo.py
@@ -47,7 +47,11 @@ def __new__(cls, key):
cls._strong_cache[key] = cls._strong_cache.pop(key, instance)
if len(cls._strong_cache) > cls._strong_cache_size:
- cls._strong_cache.popitem(last=False)
+ try:
+ cls._strong_cache.popitem(last=False)
+ except KeyError:
+ # another thread may have already emptied the cache
+ pass
return instance
@@ -334,7 +338,7 @@ def _utcoff_to_dstoff(trans_idx, utcoffsets, isdsts):
if not isdsts[comp_idx]:
dstoff = utcoff - utcoffsets[comp_idx]
- if not dstoff and idx < (typecnt - 1):
+ if not dstoff and idx < (typecnt - 1) and i + 1 < len(trans_idx):
comp_idx = trans_idx[i + 1]
# If the following transition is also DST and we couldn't
diff --git a/PythonLib/full_dll/_asyncio.pyd b/PythonLib/full_dll/_asyncio.pyd
index 3abaa9f8..a3a6fe5f 100644
Binary files a/PythonLib/full_dll/_asyncio.pyd and b/PythonLib/full_dll/_asyncio.pyd differ
diff --git a/PythonLib/full_dll/_bz2.pyd b/PythonLib/full_dll/_bz2.pyd
index a66178fa..6f75125f 100644
Binary files a/PythonLib/full_dll/_bz2.pyd and b/PythonLib/full_dll/_bz2.pyd differ
diff --git a/PythonLib/full_dll/_ctypes.pyd b/PythonLib/full_dll/_ctypes.pyd
index daf9b0fa..57c0a75b 100644
Binary files a/PythonLib/full_dll/_ctypes.pyd and b/PythonLib/full_dll/_ctypes.pyd differ
diff --git a/PythonLib/full_dll/_decimal.pyd b/PythonLib/full_dll/_decimal.pyd
index 5c32d377..e0590bbf 100644
Binary files a/PythonLib/full_dll/_decimal.pyd and b/PythonLib/full_dll/_decimal.pyd differ
diff --git a/PythonLib/full_dll/_elementtree.pyd b/PythonLib/full_dll/_elementtree.pyd
index bbcc1a27..63bfe097 100644
Binary files a/PythonLib/full_dll/_elementtree.pyd and b/PythonLib/full_dll/_elementtree.pyd differ
diff --git a/PythonLib/full_dll/_hashlib.pyd b/PythonLib/full_dll/_hashlib.pyd
index 0625c606..bd85c598 100644
Binary files a/PythonLib/full_dll/_hashlib.pyd and b/PythonLib/full_dll/_hashlib.pyd differ
diff --git a/PythonLib/full_dll/_lzma.pyd b/PythonLib/full_dll/_lzma.pyd
index 0c730651..138d6f74 100644
Binary files a/PythonLib/full_dll/_lzma.pyd and b/PythonLib/full_dll/_lzma.pyd differ
diff --git a/PythonLib/full_dll/_multiprocessing.pyd b/PythonLib/full_dll/_multiprocessing.pyd
index 4768a00e..15267ce4 100644
Binary files a/PythonLib/full_dll/_multiprocessing.pyd and b/PythonLib/full_dll/_multiprocessing.pyd differ
diff --git a/PythonLib/full_dll/_overlapped.pyd b/PythonLib/full_dll/_overlapped.pyd
index feaede17..59c17903 100644
Binary files a/PythonLib/full_dll/_overlapped.pyd and b/PythonLib/full_dll/_overlapped.pyd differ
diff --git a/PythonLib/full_dll/_queue.pyd b/PythonLib/full_dll/_queue.pyd
index 1579ee1e..32a55017 100644
Binary files a/PythonLib/full_dll/_queue.pyd and b/PythonLib/full_dll/_queue.pyd differ
diff --git a/PythonLib/full_dll/_remote_debugging.pyd b/PythonLib/full_dll/_remote_debugging.pyd
index 11e1ae4e..1abffb6d 100644
Binary files a/PythonLib/full_dll/_remote_debugging.pyd and b/PythonLib/full_dll/_remote_debugging.pyd differ
diff --git a/PythonLib/full_dll/_socket.pyd b/PythonLib/full_dll/_socket.pyd
index 2eae9658..92958a9b 100644
Binary files a/PythonLib/full_dll/_socket.pyd and b/PythonLib/full_dll/_socket.pyd differ
diff --git a/PythonLib/full_dll/_sqlite3.pyd b/PythonLib/full_dll/_sqlite3.pyd
index ea818a4a..65a28b3b 100644
Binary files a/PythonLib/full_dll/_sqlite3.pyd and b/PythonLib/full_dll/_sqlite3.pyd differ
diff --git a/PythonLib/full_dll/_ssl.pyd b/PythonLib/full_dll/_ssl.pyd
index c3f7509d..46f3cd22 100644
Binary files a/PythonLib/full_dll/_ssl.pyd and b/PythonLib/full_dll/_ssl.pyd differ
diff --git a/PythonLib/full_dll/_uuid.pyd b/PythonLib/full_dll/_uuid.pyd
index cdb12e75..0e83509b 100644
Binary files a/PythonLib/full_dll/_uuid.pyd and b/PythonLib/full_dll/_uuid.pyd differ
diff --git a/PythonLib/full_dll/_wmi.pyd b/PythonLib/full_dll/_wmi.pyd
index c47ad4f8..2f35f8b1 100644
Binary files a/PythonLib/full_dll/_wmi.pyd and b/PythonLib/full_dll/_wmi.pyd differ
diff --git a/PythonLib/full_dll/_zoneinfo.pyd b/PythonLib/full_dll/_zoneinfo.pyd
index 1f7b9afe..fdc0bbbf 100644
Binary files a/PythonLib/full_dll/_zoneinfo.pyd and b/PythonLib/full_dll/_zoneinfo.pyd differ
diff --git a/PythonLib/full_dll/_zstd.pyd b/PythonLib/full_dll/_zstd.pyd
index d91855e8..02786fcd 100644
Binary files a/PythonLib/full_dll/_zstd.pyd and b/PythonLib/full_dll/_zstd.pyd differ
diff --git a/PythonLib/full_dll/libcrypto-3.dll b/PythonLib/full_dll/libcrypto-3.dll
index f88c2305..eb01f1f2 100644
Binary files a/PythonLib/full_dll/libcrypto-3.dll and b/PythonLib/full_dll/libcrypto-3.dll differ
diff --git a/PythonLib/full_dll/libssl-3.dll b/PythonLib/full_dll/libssl-3.dll
index 96b2d192..b7bf1531 100644
Binary files a/PythonLib/full_dll/libssl-3.dll and b/PythonLib/full_dll/libssl-3.dll differ
diff --git a/PythonLib/full_dll/pyexpat.pyd b/PythonLib/full_dll/pyexpat.pyd
index ca460740..3a42a5e1 100644
Binary files a/PythonLib/full_dll/pyexpat.pyd and b/PythonLib/full_dll/pyexpat.pyd differ
diff --git a/PythonLib/full_dll/select.pyd b/PythonLib/full_dll/select.pyd
index ae7e0282..fcb6037b 100644
Binary files a/PythonLib/full_dll/select.pyd and b/PythonLib/full_dll/select.pyd differ
diff --git a/PythonLib/full_dll/sqlite3.dll b/PythonLib/full_dll/sqlite3.dll
index e1bab7b0..28310f41 100644
Binary files a/PythonLib/full_dll/sqlite3.dll and b/PythonLib/full_dll/sqlite3.dll differ
diff --git a/PythonLib/full_dll/unicodedata.pyd b/PythonLib/full_dll/unicodedata.pyd
index bc97dde6..65e60f80 100644
Binary files a/PythonLib/full_dll/unicodedata.pyd and b/PythonLib/full_dll/unicodedata.pyd differ
diff --git a/PythonLib/full_dll/winsound.pyd b/PythonLib/full_dll/winsound.pyd
index c5b5eaad..88c9a70c 100644
Binary files a/PythonLib/full_dll/winsound.pyd and b/PythonLib/full_dll/winsound.pyd differ
diff --git a/PythonLib/full_dll_arm64/_asyncio.pyd b/PythonLib/full_dll_arm64/_asyncio.pyd
index 88818579..416d8815 100644
Binary files a/PythonLib/full_dll_arm64/_asyncio.pyd and b/PythonLib/full_dll_arm64/_asyncio.pyd differ
diff --git a/PythonLib/full_dll_arm64/_bz2.pyd b/PythonLib/full_dll_arm64/_bz2.pyd
index 727b3db8..6ac60100 100644
Binary files a/PythonLib/full_dll_arm64/_bz2.pyd and b/PythonLib/full_dll_arm64/_bz2.pyd differ
diff --git a/PythonLib/full_dll_arm64/_ctypes.pyd b/PythonLib/full_dll_arm64/_ctypes.pyd
index 29eb2d54..194cdda9 100644
Binary files a/PythonLib/full_dll_arm64/_ctypes.pyd and b/PythonLib/full_dll_arm64/_ctypes.pyd differ
diff --git a/PythonLib/full_dll_arm64/_decimal.pyd b/PythonLib/full_dll_arm64/_decimal.pyd
index 01c986be..bdb09e67 100644
Binary files a/PythonLib/full_dll_arm64/_decimal.pyd and b/PythonLib/full_dll_arm64/_decimal.pyd differ
diff --git a/PythonLib/full_dll_arm64/_elementtree.pyd b/PythonLib/full_dll_arm64/_elementtree.pyd
index 1ee151a0..92c084ef 100644
Binary files a/PythonLib/full_dll_arm64/_elementtree.pyd and b/PythonLib/full_dll_arm64/_elementtree.pyd differ
diff --git a/PythonLib/full_dll_arm64/_hashlib.pyd b/PythonLib/full_dll_arm64/_hashlib.pyd
index 54636c54..562b3a37 100644
Binary files a/PythonLib/full_dll_arm64/_hashlib.pyd and b/PythonLib/full_dll_arm64/_hashlib.pyd differ
diff --git a/PythonLib/full_dll_arm64/_lzma.pyd b/PythonLib/full_dll_arm64/_lzma.pyd
index 89856501..88a72ca4 100644
Binary files a/PythonLib/full_dll_arm64/_lzma.pyd and b/PythonLib/full_dll_arm64/_lzma.pyd differ
diff --git a/PythonLib/full_dll_arm64/_multiprocessing.pyd b/PythonLib/full_dll_arm64/_multiprocessing.pyd
index 284356ec..e12d7211 100644
Binary files a/PythonLib/full_dll_arm64/_multiprocessing.pyd and b/PythonLib/full_dll_arm64/_multiprocessing.pyd differ
diff --git a/PythonLib/full_dll_arm64/_overlapped.pyd b/PythonLib/full_dll_arm64/_overlapped.pyd
index 65e7eeba..628d23dd 100644
Binary files a/PythonLib/full_dll_arm64/_overlapped.pyd and b/PythonLib/full_dll_arm64/_overlapped.pyd differ
diff --git a/PythonLib/full_dll_arm64/_queue.pyd b/PythonLib/full_dll_arm64/_queue.pyd
index c2a19b60..09dc9eb0 100644
Binary files a/PythonLib/full_dll_arm64/_queue.pyd and b/PythonLib/full_dll_arm64/_queue.pyd differ
diff --git a/PythonLib/full_dll_arm64/_remote_debugging.pyd b/PythonLib/full_dll_arm64/_remote_debugging.pyd
index 96a44c96..33304ac0 100644
Binary files a/PythonLib/full_dll_arm64/_remote_debugging.pyd and b/PythonLib/full_dll_arm64/_remote_debugging.pyd differ
diff --git a/PythonLib/full_dll_arm64/_socket.pyd b/PythonLib/full_dll_arm64/_socket.pyd
index 36a0804f..58ea8417 100644
Binary files a/PythonLib/full_dll_arm64/_socket.pyd and b/PythonLib/full_dll_arm64/_socket.pyd differ
diff --git a/PythonLib/full_dll_arm64/_sqlite3.pyd b/PythonLib/full_dll_arm64/_sqlite3.pyd
index 05724188..886c0645 100644
Binary files a/PythonLib/full_dll_arm64/_sqlite3.pyd and b/PythonLib/full_dll_arm64/_sqlite3.pyd differ
diff --git a/PythonLib/full_dll_arm64/_ssl.pyd b/PythonLib/full_dll_arm64/_ssl.pyd
index b6e4b874..c9ccdb16 100644
Binary files a/PythonLib/full_dll_arm64/_ssl.pyd and b/PythonLib/full_dll_arm64/_ssl.pyd differ
diff --git a/PythonLib/full_dll_arm64/_uuid.pyd b/PythonLib/full_dll_arm64/_uuid.pyd
index 67e6378f..bda6bd43 100644
Binary files a/PythonLib/full_dll_arm64/_uuid.pyd and b/PythonLib/full_dll_arm64/_uuid.pyd differ
diff --git a/PythonLib/full_dll_arm64/_wmi.pyd b/PythonLib/full_dll_arm64/_wmi.pyd
index 3ba7174c..ae49e31a 100644
Binary files a/PythonLib/full_dll_arm64/_wmi.pyd and b/PythonLib/full_dll_arm64/_wmi.pyd differ
diff --git a/PythonLib/full_dll_arm64/_zoneinfo.pyd b/PythonLib/full_dll_arm64/_zoneinfo.pyd
index afcb3173..55f1277e 100644
Binary files a/PythonLib/full_dll_arm64/_zoneinfo.pyd and b/PythonLib/full_dll_arm64/_zoneinfo.pyd differ
diff --git a/PythonLib/full_dll_arm64/_zstd.pyd b/PythonLib/full_dll_arm64/_zstd.pyd
index ef6885bf..809443bb 100644
Binary files a/PythonLib/full_dll_arm64/_zstd.pyd and b/PythonLib/full_dll_arm64/_zstd.pyd differ
diff --git a/PythonLib/full_dll_arm64/libcrypto-3-arm64.dll b/PythonLib/full_dll_arm64/libcrypto-3-arm64.dll
index a337b7ef..cf6d03be 100644
Binary files a/PythonLib/full_dll_arm64/libcrypto-3-arm64.dll and b/PythonLib/full_dll_arm64/libcrypto-3-arm64.dll differ
diff --git a/PythonLib/full_dll_arm64/libssl-3-arm64.dll b/PythonLib/full_dll_arm64/libssl-3-arm64.dll
index 0d089e5b..85762095 100644
Binary files a/PythonLib/full_dll_arm64/libssl-3-arm64.dll and b/PythonLib/full_dll_arm64/libssl-3-arm64.dll differ
diff --git a/PythonLib/full_dll_arm64/pyexpat.pyd b/PythonLib/full_dll_arm64/pyexpat.pyd
index 858539d8..7a580370 100644
Binary files a/PythonLib/full_dll_arm64/pyexpat.pyd and b/PythonLib/full_dll_arm64/pyexpat.pyd differ
diff --git a/PythonLib/full_dll_arm64/select.pyd b/PythonLib/full_dll_arm64/select.pyd
index 4b0df8d6..0f272593 100644
Binary files a/PythonLib/full_dll_arm64/select.pyd and b/PythonLib/full_dll_arm64/select.pyd differ
diff --git a/PythonLib/full_dll_arm64/sqlite3.dll b/PythonLib/full_dll_arm64/sqlite3.dll
index e67c3bfc..fa6f9c5d 100644
Binary files a/PythonLib/full_dll_arm64/sqlite3.dll and b/PythonLib/full_dll_arm64/sqlite3.dll differ
diff --git a/PythonLib/full_dll_arm64/unicodedata.pyd b/PythonLib/full_dll_arm64/unicodedata.pyd
index 75418346..16e2e908 100644
Binary files a/PythonLib/full_dll_arm64/unicodedata.pyd and b/PythonLib/full_dll_arm64/unicodedata.pyd differ
diff --git a/PythonLib/full_dll_arm64/winsound.pyd b/PythonLib/full_dll_arm64/winsound.pyd
index b6ffebc4..0ac02ca5 100644
Binary files a/PythonLib/full_dll_arm64/winsound.pyd and b/PythonLib/full_dll_arm64/winsound.pyd differ
diff --git a/PythonLib/full_dll_x64/_asyncio.pyd b/PythonLib/full_dll_x64/_asyncio.pyd
index 1ec499e6..53c9fabb 100644
Binary files a/PythonLib/full_dll_x64/_asyncio.pyd and b/PythonLib/full_dll_x64/_asyncio.pyd differ
diff --git a/PythonLib/full_dll_x64/_bz2.pyd b/PythonLib/full_dll_x64/_bz2.pyd
index fc576304..5b13a5a1 100644
Binary files a/PythonLib/full_dll_x64/_bz2.pyd and b/PythonLib/full_dll_x64/_bz2.pyd differ
diff --git a/PythonLib/full_dll_x64/_ctypes.pyd b/PythonLib/full_dll_x64/_ctypes.pyd
index 117a6b26..a91dc05b 100644
Binary files a/PythonLib/full_dll_x64/_ctypes.pyd and b/PythonLib/full_dll_x64/_ctypes.pyd differ
diff --git a/PythonLib/full_dll_x64/_decimal.pyd b/PythonLib/full_dll_x64/_decimal.pyd
index c8a177db..a7f01b95 100644
Binary files a/PythonLib/full_dll_x64/_decimal.pyd and b/PythonLib/full_dll_x64/_decimal.pyd differ
diff --git a/PythonLib/full_dll_x64/_elementtree.pyd b/PythonLib/full_dll_x64/_elementtree.pyd
index a5f2b6ed..d940c438 100644
Binary files a/PythonLib/full_dll_x64/_elementtree.pyd and b/PythonLib/full_dll_x64/_elementtree.pyd differ
diff --git a/PythonLib/full_dll_x64/_hashlib.pyd b/PythonLib/full_dll_x64/_hashlib.pyd
index cfef22d1..64b1f256 100644
Binary files a/PythonLib/full_dll_x64/_hashlib.pyd and b/PythonLib/full_dll_x64/_hashlib.pyd differ
diff --git a/PythonLib/full_dll_x64/_lzma.pyd b/PythonLib/full_dll_x64/_lzma.pyd
index 3b6a67c6..23db27c1 100644
Binary files a/PythonLib/full_dll_x64/_lzma.pyd and b/PythonLib/full_dll_x64/_lzma.pyd differ
diff --git a/PythonLib/full_dll_x64/_multiprocessing.pyd b/PythonLib/full_dll_x64/_multiprocessing.pyd
index 85593e6d..3e59a953 100644
Binary files a/PythonLib/full_dll_x64/_multiprocessing.pyd and b/PythonLib/full_dll_x64/_multiprocessing.pyd differ
diff --git a/PythonLib/full_dll_x64/_overlapped.pyd b/PythonLib/full_dll_x64/_overlapped.pyd
index 5991c48c..ee3aaa20 100644
Binary files a/PythonLib/full_dll_x64/_overlapped.pyd and b/PythonLib/full_dll_x64/_overlapped.pyd differ
diff --git a/PythonLib/full_dll_x64/_queue.pyd b/PythonLib/full_dll_x64/_queue.pyd
index 5e9dbcbf..d7323ec6 100644
Binary files a/PythonLib/full_dll_x64/_queue.pyd and b/PythonLib/full_dll_x64/_queue.pyd differ
diff --git a/PythonLib/full_dll_x64/_remote_debugging.pyd b/PythonLib/full_dll_x64/_remote_debugging.pyd
index ab435e24..c2ac66a6 100644
Binary files a/PythonLib/full_dll_x64/_remote_debugging.pyd and b/PythonLib/full_dll_x64/_remote_debugging.pyd differ
diff --git a/PythonLib/full_dll_x64/_socket.pyd b/PythonLib/full_dll_x64/_socket.pyd
index 2590c3ee..20126303 100644
Binary files a/PythonLib/full_dll_x64/_socket.pyd and b/PythonLib/full_dll_x64/_socket.pyd differ
diff --git a/PythonLib/full_dll_x64/_sqlite3.pyd b/PythonLib/full_dll_x64/_sqlite3.pyd
index 65334460..6cdef78d 100644
Binary files a/PythonLib/full_dll_x64/_sqlite3.pyd and b/PythonLib/full_dll_x64/_sqlite3.pyd differ
diff --git a/PythonLib/full_dll_x64/_ssl.pyd b/PythonLib/full_dll_x64/_ssl.pyd
index 471da7e2..4b892ec7 100644
Binary files a/PythonLib/full_dll_x64/_ssl.pyd and b/PythonLib/full_dll_x64/_ssl.pyd differ
diff --git a/PythonLib/full_dll_x64/_uuid.pyd b/PythonLib/full_dll_x64/_uuid.pyd
index 118add73..cc31d95d 100644
Binary files a/PythonLib/full_dll_x64/_uuid.pyd and b/PythonLib/full_dll_x64/_uuid.pyd differ
diff --git a/PythonLib/full_dll_x64/_wmi.pyd b/PythonLib/full_dll_x64/_wmi.pyd
index 252e574f..4b6d7ead 100644
Binary files a/PythonLib/full_dll_x64/_wmi.pyd and b/PythonLib/full_dll_x64/_wmi.pyd differ
diff --git a/PythonLib/full_dll_x64/_zoneinfo.pyd b/PythonLib/full_dll_x64/_zoneinfo.pyd
index 651a9e68..4bd6423d 100644
Binary files a/PythonLib/full_dll_x64/_zoneinfo.pyd and b/PythonLib/full_dll_x64/_zoneinfo.pyd differ
diff --git a/PythonLib/full_dll_x64/_zstd.pyd b/PythonLib/full_dll_x64/_zstd.pyd
index d7284e8b..99897568 100644
Binary files a/PythonLib/full_dll_x64/_zstd.pyd and b/PythonLib/full_dll_x64/_zstd.pyd differ
diff --git a/PythonLib/full_dll_x64/libcrypto-3.dll b/PythonLib/full_dll_x64/libcrypto-3.dll
index 66f64d08..cc05043d 100644
Binary files a/PythonLib/full_dll_x64/libcrypto-3.dll and b/PythonLib/full_dll_x64/libcrypto-3.dll differ
diff --git a/PythonLib/full_dll_x64/libssl-3.dll b/PythonLib/full_dll_x64/libssl-3.dll
index 0a9394cc..608fc42e 100644
Binary files a/PythonLib/full_dll_x64/libssl-3.dll and b/PythonLib/full_dll_x64/libssl-3.dll differ
diff --git a/PythonLib/full_dll_x64/pyexpat.pyd b/PythonLib/full_dll_x64/pyexpat.pyd
index c328d5e9..c96cd0db 100644
Binary files a/PythonLib/full_dll_x64/pyexpat.pyd and b/PythonLib/full_dll_x64/pyexpat.pyd differ
diff --git a/PythonLib/full_dll_x64/select.pyd b/PythonLib/full_dll_x64/select.pyd
index e677d3de..0ff5305d 100644
Binary files a/PythonLib/full_dll_x64/select.pyd and b/PythonLib/full_dll_x64/select.pyd differ
diff --git a/PythonLib/full_dll_x64/sqlite3.dll b/PythonLib/full_dll_x64/sqlite3.dll
index 64a95da2..d669911e 100644
Binary files a/PythonLib/full_dll_x64/sqlite3.dll and b/PythonLib/full_dll_x64/sqlite3.dll differ
diff --git a/PythonLib/full_dll_x64/unicodedata.pyd b/PythonLib/full_dll_x64/unicodedata.pyd
index 19171f47..cda45130 100644
Binary files a/PythonLib/full_dll_x64/unicodedata.pyd and b/PythonLib/full_dll_x64/unicodedata.pyd differ
diff --git a/PythonLib/full_dll_x64/winsound.pyd b/PythonLib/full_dll_x64/winsound.pyd
index c1a7a15a..3d426f2c 100644
Binary files a/PythonLib/full_dll_x64/winsound.pyd and b/PythonLib/full_dll_x64/winsound.pyd differ
diff --git a/PythonLib/min/_py_warnings.py b/PythonLib/min/_py_warnings.py
index 55f8c069..36513ba2 100644
--- a/PythonLib/min/_py_warnings.py
+++ b/PythonLib/min/_py_warnings.py
@@ -647,8 +647,8 @@ def __enter__(self):
context = None
self._filters = self._module.filters
self._module.filters = self._filters[:]
- self._showwarning = self._module.showwarning
self._showwarnmsg_impl = self._module._showwarnmsg_impl
+ self._showwarning = self._module.showwarning
self._module._filters_mutated_lock_held()
if self._record:
if _use_context:
@@ -656,9 +656,9 @@ def __enter__(self):
else:
log = []
self._module._showwarnmsg_impl = log.append
- # Reset showwarning() to the default implementation to make sure
- # that _showwarnmsg() calls _showwarnmsg_impl()
- self._module.showwarning = self._module._showwarning_orig
+ # Reset showwarning() to the default implementation to make sure
+ # that _showwarnmsg() calls _showwarnmsg_impl()
+ self._module.showwarning = self._module._showwarning_orig
else:
log = None
if self._filter is not None:
@@ -673,8 +673,8 @@ def __exit__(self, *exc_info):
self._module._warnings_context.set(self._saved_context)
else:
self._module.filters = self._filters
- self._module.showwarning = self._showwarning
self._module._showwarnmsg_impl = self._showwarnmsg_impl
+ self._module.showwarning = self._showwarning
self._module._filters_mutated_lock_held()
diff --git a/PythonLib/min/annotationlib.py b/PythonLib/min/annotationlib.py
index 832d160d..9fee2564 100644
--- a/PythonLib/min/annotationlib.py
+++ b/PythonLib/min/annotationlib.py
@@ -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__"):
diff --git a/PythonLib/min/argparse.py b/PythonLib/min/argparse.py
index 1d7d34f9..8cf85694 100644
--- a/PythonLib/min/argparse.py
+++ b/PythonLib/min/argparse.py
@@ -149,6 +149,10 @@ def _copy_items(items):
return copy.copy(items)
+def _identity(value):
+ return value
+
+
# ===============
# Formatting Help
# ===============
@@ -200,7 +204,7 @@ def _set_color(self, color):
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 @@ def __init__(self,
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)
diff --git a/PythonLib/min/encodings/__init__.py b/PythonLib/min/encodings/__init__.py
index 298177eb..8548bbe0 100644
--- a/PythonLib/min/encodings/__init__.py
+++ b/PythonLib/min/encodings/__init__.py
@@ -33,6 +33,7 @@
from . import aliases
_cache = {}
+_MAXCACHE = 500
_unknown = '--unknown--'
_import_tail = ['*']
_aliases = aliases.aliases
@@ -115,6 +116,8 @@ def search_function(encoding):
if mod is None:
# Cache misses
+ if len(_cache) >= _MAXCACHE:
+ _cache.clear()
_cache[encoding] = None
return None
@@ -136,6 +139,8 @@ def search_function(encoding):
entry = codecs.CodecInfo(*entry)
# Cache the codec registry entry
+ if len(_cache) >= _MAXCACHE:
+ _cache.clear()
_cache[encoding] = entry
# Register its aliases (without overwriting previously registered
diff --git a/PythonLib/min/glob.py b/PythonLib/min/glob.py
index f1a87c82..7ce3998c 100644
--- a/PythonLib/min/glob.py
+++ b/PythonLib/min/glob.py
@@ -15,7 +15,7 @@
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)
diff --git a/PythonLib/min/inspect.py b/PythonLib/min/inspect.py
index 3cee85f3..2d229051 100644
--- a/PythonLib/min/inspect.py
+++ b/PythonLib/min/inspect.py
@@ -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')
diff --git a/PythonLib/min/plistlib.py b/PythonLib/min/plistlib.py
index 5b2b4e42..c3aee1e1 100644
--- a/PythonLib/min/plistlib.py
+++ b/PythonLib/min/plistlib.py
@@ -21,7 +21,7 @@
Generate Plist example:
- import datetime
+ import datetime as dt
import plistlib
pl = dict(
@@ -37,7 +37,7 @@
),
someData = b"",
someMoreData = b"" * 10,
- aDate = datetime.datetime.now()
+ aDate = dt.datetime.now()
)
print(plistlib.dumps(pl).decode())
diff --git a/PythonLib/min/subprocess.py b/PythonLib/min/subprocess.py
index 578d7b95..52b7b711 100644
--- a/PythonLib/min/subprocess.py
+++ b/PythonLib/min/subprocess.py
@@ -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
diff --git a/PythonLib/min/tarfile.py b/PythonLib/min/tarfile.py
index c7e9f7d6..414aefe9 100644
--- a/PythonLib/min/tarfile.py
+++ b/PythonLib/min/tarfile.py
@@ -1278,6 +1278,20 @@ def _create_pax_generic_header(cls, pax_headers, type, encoding):
@classmethod
def frombuf(cls, buf, encoding, errors):
"""Construct a TarInfo object from a 512 byte bytes object.
+
+ To support the old v7 tar format AREGTYPE headers are
+ transformed to DIRTYPE headers if their name ends in '/'.
+ """
+ return cls._frombuf(buf, encoding, errors)
+
+ @classmethod
+ def _frombuf(cls, buf, encoding, errors, *, dircheck=True):
+ """Construct a TarInfo object from a 512 byte bytes object.
+
+ If ``dircheck`` is set to ``True`` then ``AREGTYPE`` headers will
+ be normalized to ``DIRTYPE`` if the name ends in a trailing slash.
+ ``dircheck`` must be set to ``False`` if this function is called
+ on a follow-up header such as ``GNUTYPE_LONGNAME``.
"""
if len(buf) == 0:
raise EmptyHeaderError("empty header")
@@ -1308,7 +1322,7 @@ def frombuf(cls, buf, encoding, errors):
# Old V7 tar format represents a directory as a regular
# file with a trailing slash.
- if obj.type == AREGTYPE and obj.name.endswith("/"):
+ if dircheck and obj.type == AREGTYPE and obj.name.endswith("/"):
obj.type = DIRTYPE
# The old GNU sparse format occupies some of the unused
@@ -1343,8 +1357,15 @@ def fromtarfile(cls, tarfile):
"""Return the next TarInfo object from TarFile object
tarfile.
"""
+ return cls._fromtarfile(tarfile)
+
+ @classmethod
+ def _fromtarfile(cls, tarfile, *, dircheck=True):
+ """
+ See dircheck documentation in _frombuf().
+ """
buf = tarfile.fileobj.read(BLOCKSIZE)
- obj = cls.frombuf(buf, tarfile.encoding, tarfile.errors)
+ obj = cls._frombuf(buf, tarfile.encoding, tarfile.errors, dircheck=dircheck)
obj.offset = tarfile.fileobj.tell() - BLOCKSIZE
return obj._proc_member(tarfile)
@@ -1402,7 +1423,7 @@ def _proc_gnulong(self, tarfile):
# Fetch the next header and process it.
try:
- next = self.fromtarfile(tarfile)
+ next = self._fromtarfile(tarfile, dircheck=False)
except HeaderError as e:
raise SubsequentHeaderError(str(e)) from None
@@ -1537,7 +1558,7 @@ def _proc_pax(self, tarfile):
# Fetch the next header.
try:
- next = self.fromtarfile(tarfile)
+ next = self._fromtarfile(tarfile, dircheck=False)
except HeaderError as e:
raise SubsequentHeaderError(str(e)) from None
diff --git a/PythonLib/min/tempfile.py b/PythonLib/min/tempfile.py
index 5e3ccab5..a34e062f 100644
--- a/PythonLib/min/tempfile.py
+++ b/PythonLib/min/tempfile.py
@@ -57,10 +57,11 @@
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
diff --git a/PythonLib/min/traceback.py b/PythonLib/min/traceback.py
index 5a34a2b8..79f67b98 100644
--- a/PythonLib/min/traceback.py
+++ b/PythonLib/min/traceback.py
@@ -1589,17 +1589,23 @@ def _substitution_cost(ch_a, ch_b):
return _MOVE_COST
+def _get_safe___dir__(obj):
+ # Use obj.__dir__() to avoid a TypeError when calling dir(obj).
+ # See gh-131001 and gh-139933.
+ try:
+ d = obj.__dir__()
+ except TypeError: # when obj is a class
+ d = type(obj).__dir__(obj)
+ return sorted(x for x in d if isinstance(x, str))
+
+
def _compute_suggestion_error(exc_value, tb, wrong_name):
if wrong_name is None or not isinstance(wrong_name, str):
return None
if isinstance(exc_value, AttributeError):
obj = exc_value.obj
try:
- try:
- d = dir(obj)
- except TypeError: # Attributes are unsortable, e.g. int and str
- d = list(obj.__class__.__dict__.keys()) + list(obj.__dict__.keys())
- d = sorted([x for x in d if isinstance(x, str)])
+ d = _get_safe___dir__(obj)
hide_underscored = (wrong_name[:1] != '_')
if hide_underscored and tb is not None:
while tb.tb_next is not None:
@@ -1614,11 +1620,7 @@ def _compute_suggestion_error(exc_value, tb, wrong_name):
elif isinstance(exc_value, ImportError):
try:
mod = __import__(exc_value.name)
- try:
- d = dir(mod)
- except TypeError: # Attributes are unsortable, e.g. int and str
- d = list(mod.__dict__.keys())
- d = sorted([x for x in d if isinstance(x, str)])
+ d = _get_safe___dir__(mod)
if wrong_name[:1] != '_':
d = [x for x in d if x[:1] != '_']
except Exception:
diff --git a/PythonLib/min/webbrowser.py b/PythonLib/min/webbrowser.py
index f2e23940..0e0b5034 100644
--- a/PythonLib/min/webbrowser.py
+++ b/PythonLib/min/webbrowser.py
@@ -163,6 +163,12 @@ def open_new(self, url):
def open_new_tab(self, url):
return self.open(url, 2)
+ @staticmethod
+ def _check_url(url):
+ """Ensures that the URL is safe to pass to subprocesses as a parameter"""
+ if url and url.lstrip().startswith("-"):
+ raise ValueError(f"Invalid URL (leading dash disallowed): {url!r}")
+
class GenericBrowser(BaseBrowser):
"""Class for all browsers started with a command
@@ -180,6 +186,7 @@ def __init__(self, name):
def open(self, url, new=0, autoraise=True):
sys.audit("webbrowser.open", url)
+ self._check_url(url)
cmdline = [self.name] + [arg.replace("%s", url)
for arg in self.args]
try:
@@ -200,6 +207,7 @@ def open(self, url, new=0, autoraise=True):
cmdline = [self.name] + [arg.replace("%s", url)
for arg in self.args]
sys.audit("webbrowser.open", url)
+ self._check_url(url)
try:
if sys.platform[:3] == 'win':
p = subprocess.Popen(cmdline)
@@ -266,6 +274,7 @@ def _invoke(self, args, remote, autoraise, url=None):
def open(self, url, new=0, autoraise=True):
sys.audit("webbrowser.open", url)
+ self._check_url(url)
if new == 0:
action = self.remote_action
elif new == 1:
@@ -357,6 +366,7 @@ class Konqueror(BaseBrowser):
def open(self, url, new=0, autoraise=True):
sys.audit("webbrowser.open", url)
+ self._check_url(url)
# XXX Currently I know no way to prevent KFM from opening a new win.
if new == 2:
action = "newTab"
@@ -588,6 +598,7 @@ def register_standard_browsers():
class WindowsDefault(BaseBrowser):
def open(self, url, new=0, autoraise=True):
sys.audit("webbrowser.open", url)
+ self._check_url(url)
try:
os.startfile(url)
except OSError:
@@ -608,6 +619,7 @@ def __init__(self, name='default'):
def open(self, url, new=0, autoraise=True):
sys.audit("webbrowser.open", url)
+ self._check_url(url)
url = url.replace('"', '%22')
if self.name == 'default':
proto, _sep, _rest = url.partition(":")
@@ -644,7 +656,7 @@ def open(self, url, new=0, autoraise=True):
end
'''
- osapipe = os.popen("osascript", "w")
+ osapipe = os.popen("/usr/bin/osascript", "w")
if osapipe is None:
return False
@@ -664,6 +676,7 @@ def open(self, url, new=0, autoraise=True):
class IOSBrowser(BaseBrowser):
def open(self, url, new=0, autoraise=True):
sys.audit("webbrowser.open", url)
+ self._check_url(url)
# If ctypes isn't available, we can't open a browser
if objc is None:
return False
diff --git a/PythonLib/tcl_dll/_tkinter.pyd b/PythonLib/tcl_dll/_tkinter.pyd
index 2c59b941..cbc8941b 100644
Binary files a/PythonLib/tcl_dll/_tkinter.pyd and b/PythonLib/tcl_dll/_tkinter.pyd differ
diff --git a/PythonLib/tcl_dll_arm64/_tkinter.pyd b/PythonLib/tcl_dll_arm64/_tkinter.pyd
index 33ef2b44..c3c94404 100644
Binary files a/PythonLib/tcl_dll_arm64/_tkinter.pyd and b/PythonLib/tcl_dll_arm64/_tkinter.pyd differ
diff --git a/PythonLib/tcl_dll_x64/_tkinter.pyd b/PythonLib/tcl_dll_x64/_tkinter.pyd
index 3cb5a4e2..0d7a2c33 100644
Binary files a/PythonLib/tcl_dll_x64/_tkinter.pyd and b/PythonLib/tcl_dll_x64/_tkinter.pyd differ
diff --git a/PythonScript/project/PythonSettings.props b/PythonScript/project/PythonSettings.props
index cba03fe4..d21fabb2 100644
--- a/PythonScript/project/PythonSettings.props
+++ b/PythonScript/project/PythonSettings.props
@@ -4,9 +4,9 @@
$(SolutionDir)boost\$(SolutionDir)boost\lib
- $(SolutionDir)packages\pythonx86.3.14.3\tools
- $(SolutionDir)packages\python.3.14.3\tools
- $(SolutionDir)packages\pythonarm64.3.14.3\tools
+ $(SolutionDir)packages\pythonx86.3.14.4\tools
+ $(SolutionDir)packages\python.3.14.4\tools
+ $(SolutionDir)packages\pythonarm64.3.14.4\tools$(PythonBase)\libs$(PythonBaseX64)\libs$(PythonBaseARM64)\libs
diff --git a/PythonScript/project/packages.config b/PythonScript/project/packages.config
index c5955d68..077023ab 100644
--- a/PythonScript/project/packages.config
+++ b/PythonScript/project/packages.config
@@ -1,7 +1,7 @@
-
-
-
-
+
+
+
+
\ No newline at end of file
diff --git a/PythonScript/project/packages_local_pydebug.config b/PythonScript/project/packages_local_pydebug.config
index 4ee1a730..d074c89d 100644
--- a/PythonScript/project/packages_local_pydebug.config
+++ b/PythonScript/project/packages_local_pydebug.config
@@ -1,4 +1,4 @@
-
+
\ No newline at end of file
diff --git a/appveyor.yml b/appveyor.yml
index 64694a1a..e0f3a891 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -46,9 +46,9 @@ after_build:
- cd "%APPVEYOR_BUILD_FOLDER%"\installer
- dotnet tool install --global wix
- - SET PYTHONBUILDDIR_ARM64="%APPVEYOR_BUILD_FOLDER%"\packages\pythonarm64.3.14.3\tools
- - SET PYTHONBUILDDIR_X64="%APPVEYOR_BUILD_FOLDER%"\packages\python.3.14.3\tools
- - SET PYTHONBUILDDIR="%APPVEYOR_BUILD_FOLDER%"\packages\pythonx86.3.14.3\tools
+ - SET PYTHONBUILDDIR_ARM64="%APPVEYOR_BUILD_FOLDER%"\packages\pythonarm64.3.14.4\tools
+ - SET PYTHONBUILDDIR_X64="%APPVEYOR_BUILD_FOLDER%"\packages\python.3.14.4\tools
+ - SET PYTHONBUILDDIR="%APPVEYOR_BUILD_FOLDER%"\packages\pythonx86.3.14.4\tools
- copy "%APPVEYOR_BUILD_FOLDER%"\installer\buildPaths.bat.orig "%APPVEYOR_BUILD_FOLDER%"\installer\buildPaths.bat
- if "%configuration%"=="Release" buildAll.bat %platform_input%
- cd "%APPVEYOR_BUILD_FOLDER%"