Skip to content

Commit 84daea4

Browse files
authored
Merge branch 'main' into thread_name_fix
2 parents 392aa5a + f57be7b commit 84daea4

File tree

12 files changed

+109
-63
lines changed

12 files changed

+109
-63
lines changed

.github/workflows/build.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,10 @@ jobs:
270270
fail-fast: false
271271
matrix:
272272
os: [ubuntu-24.04]
273-
openssl_ver: [3.0.17, 3.2.5, 3.3.4, 3.4.2, 3.5.2]
273+
# Keep 1.1.1w in our list despite it being upstream EOL and otherwise
274+
# unsupported as it most resembles other 1.1.1-work-a-like ssl APIs
275+
# supported by important vendors such as AWS-LC.
276+
openssl_ver: [1.1.1w, 3.0.17, 3.2.5, 3.3.4, 3.4.2, 3.5.2]
274277
# See Tools/ssl/make_ssl_data.py for notes on adding a new version
275278
env:
276279
OPENSSL_VER: ${{ matrix.openssl_ver }}

Doc/extending/extending.rst

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1084,7 +1084,14 @@ references to all its items, so when item 1 is replaced, it has to dispose of
10841084
the original item 1. Now let's suppose the original item 1 was an instance of a
10851085
user-defined class, and let's further suppose that the class defined a
10861086
:meth:`!__del__` method. If this class instance has a reference count of 1,
1087-
disposing of it will call its :meth:`!__del__` method.
1087+
disposing of it will call its :meth:`!__del__` method. Internally,
1088+
:c:func:`PyList_SetItem` calls :c:func:`Py_DECREF` on the replaced item,
1089+
which invokes replaced item's corresponding
1090+
:c:member:`~PyTypeObject.tp_dealloc` function. During
1091+
deallocation, :c:member:`~PyTypeObject.tp_dealloc` calls
1092+
:c:member:`~PyTypeObject.tp_finalize`, which is mapped to the
1093+
:meth:`!__del__` method for class instances (see :pep:`442`). This entire
1094+
sequence happens synchronously within the :c:func:`PyList_SetItem` call.
10881095

10891096
Since it is written in Python, the :meth:`!__del__` method can execute arbitrary
10901097
Python code. Could it perhaps do something to invalidate the reference to

Doc/library/csv.rst

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ The :mod:`csv` module defines the following functions:
113113
spamwriter.writerow(['Spam', 'Lovely Spam', 'Wonderful Spam'])
114114

115115

116-
.. function:: register_dialect(name[, dialect[, **fmtparams]])
116+
.. function:: register_dialect(name, /, dialect='excel', **fmtparams)
117117

118118
Associate *dialect* with *name*. *name* must be a string. The
119119
dialect can be specified either by passing a sub-class of :class:`Dialect`, or
@@ -139,7 +139,8 @@ The :mod:`csv` module defines the following functions:
139139
Return the names of all registered dialects.
140140

141141

142-
.. function:: field_size_limit([new_limit])
142+
.. function:: field_size_limit()
143+
field_size_limit(new_limit)
143144

144145
Returns the current maximum field size allowed by the parser. If *new_limit* is
145146
given, this becomes the new limit.
@@ -526,7 +527,7 @@ out surrounded by parens. This may cause some problems for other programs which
526527
read CSV files (assuming they support complex numbers at all).
527528

528529

529-
.. method:: csvwriter.writerow(row)
530+
.. method:: csvwriter.writerow(row, /)
530531

531532
Write the *row* parameter to the writer's file object, formatted according
532533
to the current :class:`Dialect`. Return the return value of the call to the
@@ -535,7 +536,7 @@ read CSV files (assuming they support complex numbers at all).
535536
.. versionchanged:: 3.5
536537
Added support of arbitrary iterables.
537538

538-
.. method:: csvwriter.writerows(rows)
539+
.. method:: csvwriter.writerows(rows, /)
539540

540541
Write all elements in *rows* (an iterable of *row* objects as described
541542
above) to the writer's file object, formatted according to the current

Doc/library/zipfile.rst

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -830,7 +830,10 @@ Instances have the following methods and attributes:
830830
.. attribute:: ZipInfo.date_time
831831

832832
The time and date of the last modification to the archive member. This is a
833-
tuple of six values:
833+
tuple of six values representing the "last [modified] file time" and "last [modified] file date"
834+
fields from the ZIP file's central directory.
835+
836+
The tuple contains:
834837

835838
+-------+--------------------------+
836839
| Index | Value |
@@ -850,7 +853,15 @@ Instances have the following methods and attributes:
850853

851854
.. note::
852855

853-
The ZIP file format does not support timestamps before 1980.
856+
The ZIP format supports multiple timestamp fields in different locations
857+
(central directory, extra fields for NTFS/UNIX systems, etc.). This attribute
858+
specifically returns the timestamp from the central directory. The central
859+
directory timestamp format in ZIP files does not support timestamps before
860+
1980. While some extra field formats (such as UNIX timestamps) can represent
861+
earlier dates, this attribute only returns the central directory timestamp.
862+
863+
The central directory timestamp is interpreted as representing local
864+
time, rather than UTC time, to match the behavior of other zip tools.
854865

855866

856867
.. attribute:: ZipInfo.compress_type

Lib/functools.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -580,12 +580,14 @@ def lru_cache(maxsize=128, typed=False):
580580
# Negative maxsize is treated as 0
581581
if maxsize < 0:
582582
maxsize = 0
583+
583584
elif callable(maxsize) and isinstance(typed, bool):
584585
# The user_function was passed in directly via the maxsize argument
585586
user_function, maxsize = maxsize, 128
586587
wrapper = _lru_cache_wrapper(user_function, maxsize, typed, _CacheInfo)
587588
wrapper.cache_parameters = lambda : {'maxsize': maxsize, 'typed': typed}
588589
return update_wrapper(wrapper, user_function)
590+
589591
elif maxsize is not None:
590592
raise TypeError(
591593
'Expected first argument to be an integer, a callable, or None')
@@ -617,6 +619,7 @@ def _lru_cache_wrapper(user_function, maxsize, typed, _CacheInfo):
617619
def wrapper(*args, **kwds):
618620
# No caching -- just a statistics update
619621
nonlocal misses
622+
620623
misses += 1
621624
result = user_function(*args, **kwds)
622625
return result
@@ -626,6 +629,7 @@ def wrapper(*args, **kwds):
626629
def wrapper(*args, **kwds):
627630
# Simple caching without ordering or size limit
628631
nonlocal hits, misses
632+
629633
key = make_key(args, kwds, typed)
630634
result = cache_get(key, sentinel)
631635
if result is not sentinel:
@@ -641,7 +645,9 @@ def wrapper(*args, **kwds):
641645
def wrapper(*args, **kwds):
642646
# Size limited caching that tracks accesses by recency
643647
nonlocal root, hits, misses, full
648+
644649
key = make_key(args, kwds, typed)
650+
645651
with lock:
646652
link = cache_get(key)
647653
if link is not None:
@@ -656,19 +662,23 @@ def wrapper(*args, **kwds):
656662
hits += 1
657663
return result
658664
misses += 1
665+
659666
result = user_function(*args, **kwds)
667+
660668
with lock:
661669
if key in cache:
662670
# Getting here means that this same key was added to the
663671
# cache while the lock was released. Since the link
664672
# update is already done, we need only return the
665673
# computed result and update the count of misses.
666674
pass
675+
667676
elif full:
668677
# Use the old root to store the new key and result.
669678
oldroot = root
670679
oldroot[KEY] = key
671680
oldroot[RESULT] = result
681+
672682
# Empty the oldest link and make it the new root.
673683
# Keep a reference to the old key and old result to
674684
# prevent their ref counts from going to zero during the
@@ -679,20 +689,25 @@ def wrapper(*args, **kwds):
679689
oldkey = root[KEY]
680690
oldresult = root[RESULT]
681691
root[KEY] = root[RESULT] = None
692+
682693
# Now update the cache dictionary.
683694
del cache[oldkey]
695+
684696
# Save the potentially reentrant cache[key] assignment
685697
# for last, after the root and links have been put in
686698
# a consistent state.
687699
cache[key] = oldroot
700+
688701
else:
689702
# Put result in a new link at the front of the queue.
690703
last = root[PREV]
691704
link = [last, root, key, result]
692705
last[NEXT] = root[PREV] = cache[key] = link
706+
693707
# Use the cache_len bound method instead of the len() function
694708
# which could potentially be wrapped in an lru_cache itself.
695709
full = (cache_len() >= maxsize)
710+
696711
return result
697712

698713
def cache_info():
@@ -703,6 +718,7 @@ def cache_info():
703718
def cache_clear():
704719
"""Clear the cache and cache statistics"""
705720
nonlocal hits, misses, full
721+
706722
with lock:
707723
cache.clear()
708724
root[:] = [root, root, None, None]

Lib/smtplib.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -734,6 +734,7 @@ def login(self, user, password, *, initial_response_ok=True):
734734
preferred_auths = ['CRAM-MD5', 'PLAIN', 'LOGIN']
735735
else:
736736
preferred_auths = ['PLAIN', 'LOGIN']
737+
# We try the supported authentications in our preferred order, if
737738
# the server supports them.
738739
authlist = [auth for auth in preferred_auths
739740
if auth in advertised_authlist]

Lib/test/test_zoneinfo/test_zoneinfo.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
from test.test_zoneinfo import _support as test_support
2323
from test.test_zoneinfo._support import TZPATH_TEST_LOCK, ZoneInfoTestBase
2424
from test.support.import_helper import import_module, CleanImport
25+
from test.support.script_helper import assert_python_ok
2526

2627
lzma = import_module('lzma')
2728
py_zoneinfo, c_zoneinfo = test_support.get_modules()
@@ -1946,6 +1947,26 @@ class CTestModule(TestModule):
19461947
module = c_zoneinfo
19471948

19481949

1950+
class MiscTests(unittest.TestCase):
1951+
def test_pydatetime(self):
1952+
# Test that zoneinfo works if the C implementation of datetime
1953+
# is not available and the Python implementation of datetime is used.
1954+
# The Python implementation of zoneinfo should be used in thet case.
1955+
#
1956+
# Run the test in a subprocess, as importing _zoneinfo with
1957+
# _datettime disabled causes crash in the previously imported
1958+
# _zoneinfo.
1959+
assert_python_ok('-c', '''if 1:
1960+
import sys
1961+
sys.modules['_datetime'] = None
1962+
import datetime
1963+
import zoneinfo
1964+
tzinfo = zoneinfo.ZoneInfo('Europe/London')
1965+
datetime.datetime(2025, 10, 26, 2, 0, tzinfo=tzinfo)
1966+
''',
1967+
PYTHONTZPATH=str(ZONEINFO_DATA.tzpath))
1968+
1969+
19491970
class ExtensionBuiltTest(unittest.TestCase):
19501971
"""Smoke test to ensure that the C and Python extensions are both tested.
19511972

Lib/typing.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -453,12 +453,6 @@ def _deprecation_warning_for_no_type_params_passed(funcname: str) -> None:
453453
warnings.warn(depr_message, category=DeprecationWarning, stacklevel=3)
454454

455455

456-
class _Sentinel:
457-
__slots__ = ()
458-
def __repr__(self):
459-
return '<sentinel>'
460-
461-
462456
def _eval_type(t, globalns, localns, type_params, *, recursive_guard=frozenset(),
463457
format=None, owner=None, parent_fwdref=None):
464458
"""Evaluate all forward references in the given type t.

Lib/zoneinfo/__init__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@
1212

1313
try:
1414
from _zoneinfo import ZoneInfo
15-
except ImportError: # pragma: nocover
15+
except (ImportError, AttributeError): # pragma: nocover
16+
# AttributeError: module 'datetime' has no attribute 'datetime_CAPI'.
17+
# This happens when the '_datetime' module is not available and the
18+
# pure Python implementation is used instead.
1619
from ._zoneinfo import ZoneInfo
1720

1821
reset_tzpath = _tzpath.reset_tzpath
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix import of the :mod:`zoneinfo` module if the C implementation of the
2+
:mod:`datetime` module is not available.

0 commit comments

Comments
 (0)