aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--Doc/howto/free-threading-extensions.rst8
-rw-r--r--Doc/library/multiprocessing.rst12
-rw-r--r--Doc/library/re.rst16
-rw-r--r--Doc/library/socketserver.rst2
-rw-r--r--Doc/whatsnew/3.14.rst6
-rw-r--r--Doc/whatsnew/3.15.rst9
-rw-r--r--Include/Python.h13
-rw-r--r--Include/internal/pycore_opcode_metadata.h33
-rw-r--r--Include/internal/pycore_uop_ids.h393
-rw-r--r--Include/internal/pycore_uop_metadata.h12
-rw-r--r--Include/py_curses.h7
-rw-r--r--Lib/_pydatetime.py44
-rw-r--r--Lib/_pyrepl/_module_completer.py5
-rw-r--r--Lib/_pyrepl/simple_interact.py9
-rw-r--r--Lib/_pyrepl/utils.py8
-rw-r--r--Lib/http/server.py8
-rw-r--r--Lib/ntpath.py3
-rw-r--r--Lib/pydoc.py4
-rw-r--r--Lib/socketserver.py2
-rw-r--r--Lib/sysconfig/__init__.py2
-rw-r--r--Lib/tarfile.py4
-rw-r--r--Lib/test/datetimetester.py9
-rw-r--r--Lib/test/lock_tests.py5
-rw-r--r--Lib/test/pythoninfo.py11
-rw-r--r--Lib/test/test_capi/test_opt.py136
-rw-r--r--Lib/test/test_curses.py3
-rw-r--r--Lib/test/test_httpservers.py171
-rw-r--r--Lib/test/test_ntpath.py179
-rw-r--r--Lib/test/test_posixpath.py74
-rw-r--r--Lib/test/test_pyrepl/test_pyrepl.py47
-rw-r--r--Lib/test/test_pyrepl/test_reader.py31
-rw-r--r--Lib/test/test_sysconfig.py5
-rw-r--r--Lib/test/test_tarfile.py49
-rw-r--r--Lib/test/test_threading.py6
-rw-r--r--Lib/threading.py13
-rw-r--r--Misc/ACKS2
-rw-r--r--Misc/NEWS.d/next/Core_and_Builtins/2025-04-26-17-50-01.gh-issue-131798.XiOgw5.rst2
-rw-r--r--Misc/NEWS.d/next/Core_and_Builtins/2025-05-17-20-44-51.gh-issue-134158.ewLNLp.rst1
-rw-r--r--Misc/NEWS.d/next/Core_and_Builtins/2025-05-19-15-15-58.gh-issue-131798.PCP71j.rst1
-rw-r--r--Misc/NEWS.d/next/Core_and_Builtins/2025-05-19-20-52-53.gh-issue-134268.HPKX1e.rst2
-rw-r--r--Misc/NEWS.d/next/Library/2024-10-22-16-21-55.gh-issue-125843.2ttzYo.rst2
-rw-r--r--Misc/NEWS.d/next/Library/2024-11-25-10-22-08.gh-issue-126883.MAEF7g.rst3
-rw-r--r--Misc/NEWS.d/next/Library/2025-05-17-13-46-20.gh-issue-134097.fgkjE1.rst1
-rw-r--r--Misc/NEWS.d/next/Library/2025-05-17-18-08-35.gh-issue-133890.onn9_X.rst2
-rw-r--r--Misc/NEWS.d/next/Library/2025-05-18-12-23-07.gh-issue-134087.HilZWl.rst3
-rw-r--r--Misc/NEWS.d/next/Library/2025-05-19-15-05-24.gh-issue-134235.pz9PwV.rst2
-rw-r--r--Misc/NEWS.d/next/Library/2025-05-19-15-30-00.gh-issue-132983.asdsfs.rst1
-rw-r--r--Misc/NEWS.d/next/Library/2025-05-19-17-27-21.gh-issue-80184.LOkbaw.rst1
-rw-r--r--Misc/NEWS.d/next/Library/2025-05-19-18-12-42.gh-issue-88994.7avvVu.rst3
-rw-r--r--Misc/NEWS.d/next/Windows/2025-05-13-13-25-27.gh-issue-133779.-YcTBz.rst6
-rw-r--r--Misc/NEWS.d/next/Windows/2025-05-19-03-02-04.gh-issue-76023.vHOf6M.rst1
-rw-r--r--Modules/_cursesmodule.c535
-rw-r--r--Modules/_datetimemodule.c18
-rw-r--r--Modules/_io/bytesio.c2
-rw-r--r--Modules/_remote_debugging_module.c2
-rw-r--r--Modules/_threadmodule.c82
-rw-r--r--Modules/clinic/_threadmodule.c.h50
-rw-r--r--PC/pyconfig.h (renamed from PC/pyconfig.h.in)17
-rw-r--r--PCbuild/_freeze_module.vcxproj27
-rw-r--r--PCbuild/pyproject.props8
-rw-r--r--PCbuild/pythoncore.vcxproj31
-rw-r--r--PCbuild/regen.targets14
-rw-r--r--Python/bytecodes.c36
-rw-r--r--Python/executor_cases.c.h69
-rw-r--r--Python/generated_cases.c.h113
-rw-r--r--Python/optimizer_bytecodes.c39
-rw-r--r--Python/optimizer_cases.c.h47
-rw-r--r--Python/pystate.c6
-rw-r--r--Tools/cases_generator/opcode_metadata_generator.py7
-rw-r--r--Tools/msi/dev/dev_files.wxs2
-rw-r--r--Tools/peg_generator/pegen/build.py2
71 files changed, 1853 insertions, 626 deletions
diff --git a/Doc/howto/free-threading-extensions.rst b/Doc/howto/free-threading-extensions.rst
index 3f6ee517050..5a3970f15d5 100644
--- a/Doc/howto/free-threading-extensions.rst
+++ b/Doc/howto/free-threading-extensions.rst
@@ -23,6 +23,14 @@ You can use it to enable code that only runs under the free-threaded build::
/* code that only runs in the free-threaded build */
#endif
+.. note::
+
+ On Windows, this macro is not defined automatically, but must be specified
+ to the compiler when building. The :func:`sysconfig.get_config_var` function
+ can be used to determine whether the current running interpreter had the
+ macro defined.
+
+
Module Initialization
=====================
diff --git a/Doc/library/multiprocessing.rst b/Doc/library/multiprocessing.rst
index 6c43d5fe166..80e33c4a1df 100644
--- a/Doc/library/multiprocessing.rst
+++ b/Doc/library/multiprocessing.rst
@@ -1369,6 +1369,12 @@ object -- see :ref:`multiprocessing-managers`.
A solitary difference from its close analog exists: its ``acquire`` method's
first argument is named *block*, as is consistent with :meth:`Lock.acquire`.
+ .. method:: locked()
+
+ Return a boolean indicating whether this object is locked right now.
+
+ .. versionadded:: 3.14
+
.. note::
On macOS, this is indistinguishable from :class:`Semaphore` because
``sem_getvalue()`` is not implemented on that platform.
@@ -1521,6 +1527,12 @@ object -- see :ref:`multiprocessing-managers`.
A solitary difference from its close analog exists: its ``acquire`` method's
first argument is named *block*, as is consistent with :meth:`Lock.acquire`.
+ .. method:: locked()
+
+ Return a boolean indicating whether this object is locked right now.
+
+ .. versionadded:: 3.14
+
.. note::
On macOS, ``sem_timedwait`` is unsupported, so calling ``acquire()`` with
diff --git a/Doc/library/re.rst b/Doc/library/re.rst
index eb3b1e5549c..75ebbf11c8e 100644
--- a/Doc/library/re.rst
+++ b/Doc/library/re.rst
@@ -991,8 +991,8 @@ Functions
That way, separator components are always found at the same relative
indices within the result list.
- Empty matches for the pattern split the string only when not adjacent
- to a previous empty match.
+ Adjacent empty matches are not possible, but an empty match can occur
+ immediately after a non-empty match.
.. code:: pycon
@@ -1095,9 +1095,12 @@ Functions
The optional argument *count* is the maximum number of pattern occurrences to be
replaced; *count* must be a non-negative integer. If omitted or zero, all
- occurrences will be replaced. Empty matches for the pattern are replaced only
- when not adjacent to a previous empty match, so ``sub('x*', '-', 'abxd')`` returns
- ``'-a-b--d-'``.
+ occurrences will be replaced.
+
+ Adjacent empty matches are not possible, but an empty match can occur
+ immediately after a non-empty match.
+ As a result, ``sub('x*', '-', 'abxd')`` returns ``'-a-b--d-'``
+ instead of ``'-a-b-d-'``.
.. index:: single: \g; in regular expressions
@@ -1128,8 +1131,7 @@ Functions
.. versionchanged:: 3.7
Unknown escapes in *repl* consisting of ``'\'`` and an ASCII letter
now are errors.
- Empty matches for the pattern are replaced when adjacent to a previous
- non-empty match.
+ An empty match can occur immediately after a non-empty match.
.. versionchanged:: 3.12
Group *id* can only contain ASCII digits.
diff --git a/Doc/library/socketserver.rst b/Doc/library/socketserver.rst
index 59cfa136a3b..753f12460b8 100644
--- a/Doc/library/socketserver.rst
+++ b/Doc/library/socketserver.rst
@@ -24,6 +24,8 @@ There are four basic concrete server classes:
:meth:`~BaseServer.server_activate`. The other parameters are passed to
the :class:`BaseServer` base class.
+ .. versionchanged:: next
+ The default queue size is now ``socket.SOMAXCONN`` for :class:`socketserver.TCPServer`.
.. class:: UDPServer(server_address, RequestHandlerClass, bind_and_activate=True)
diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst
index 11361289874..8f39b99e38e 100644
--- a/Doc/whatsnew/3.14.rst
+++ b/Doc/whatsnew/3.14.rst
@@ -829,6 +829,12 @@ Kumar Aditya, Edgar Margffoy, and many others.
Some of these contributors are employed by Meta, which has continued to provide
significant engineering resources to support this project.
+From 3.14, when compiling extension modules for the free-threaded build of
+CPython on Windows, the preprocessor variable ``Py_GIL_DISABLED`` now needs to
+be specified by the build backend, as it will no longer be determined
+automatically by the C compiler. For a running interpreter, the setting that
+was used at compile time can be found using :func:`sysconfig.get_config_var`.
+
.. _whatsnew314-pyrepl-highlighting:
diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst
index 987cf944972..bf186c191b0 100644
--- a/Doc/whatsnew/3.15.rst
+++ b/Doc/whatsnew/3.15.rst
@@ -145,6 +145,15 @@ sysconfig
(Contributed by Filipe Laíns in :gh:`92897`.)
+threading
+---------
+
+* Remove support for arbitrary positional or keyword arguments in the C
+ implementation of :class:`~threading.RLock` objects. This was deprecated
+ in Python 3.14.
+ (Contributed by Bénédikt Tran in :gh:`134087`.)
+
+
typing
------
diff --git a/Include/Python.h b/Include/Python.h
index 64be8014589..f34d581f0b4 100644
--- a/Include/Python.h
+++ b/Include/Python.h
@@ -59,6 +59,14 @@
# include <intrin.h> // __readgsqword()
#endif
+// Suppress known warnings in Python header files.
+#if defined(_MSC_VER)
+// Warning that alignas behaviour has changed. Doesn't affect us, because we
+// never relied on the old behaviour.
+#pragma warning(push)
+#pragma warning(disable: 5274)
+#endif
+
// Include Python header files
#include "pyport.h"
#include "pymacro.h"
@@ -138,4 +146,9 @@
#include "cpython/pyfpe.h"
#include "cpython/tracemalloc.h"
+// Restore warning filter
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
#endif /* !Py_PYTHON_H */
diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h
index e55e26783a6..33e9804e593 100644
--- a/Include/internal/pycore_opcode_metadata.h
+++ b/Include/internal/pycore_opcode_metadata.h
@@ -1121,7 +1121,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[267] = {
[CALL_KW_NON_PY] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
[CALL_KW_PY] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
[CALL_LEN] = { true, INSTR_FMT_IXC00, HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG },
- [CALL_LIST_APPEND] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
+ [CALL_LIST_APPEND] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
[CALL_METHOD_DESCRIPTOR_FAST] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
[CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
[CALL_METHOD_DESCRIPTOR_NOARGS] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
@@ -1368,7 +1368,7 @@ _PyOpcode_macro_expansion[256] = {
[CALL_KW_NON_PY] = { .nuops = 3, .uops = { { _CHECK_IS_NOT_PY_CALLABLE_KW, OPARG_SIMPLE, 3 }, { _CALL_KW_NON_PY, OPARG_SIMPLE, 3 }, { _CHECK_PERIODIC, OPARG_SIMPLE, 3 } } },
[CALL_KW_PY] = { .nuops = 5, .uops = { { _CHECK_PEP_523, OPARG_SIMPLE, 1 }, { _CHECK_FUNCTION_VERSION_KW, 2, 1 }, { _PY_FRAME_KW, OPARG_SIMPLE, 3 }, { _SAVE_RETURN_OFFSET, OPARG_SAVE_RETURN_OFFSET, 3 }, { _PUSH_FRAME, OPARG_SIMPLE, 3 } } },
[CALL_LEN] = { .nuops = 3, .uops = { { _GUARD_NOS_NULL, OPARG_SIMPLE, 3 }, { _GUARD_CALLABLE_LEN, OPARG_SIMPLE, 3 }, { _CALL_LEN, OPARG_SIMPLE, 3 } } },
- [CALL_LIST_APPEND] = { .nuops = 1, .uops = { { _CALL_LIST_APPEND, OPARG_SIMPLE, 3 } } },
+ [CALL_LIST_APPEND] = { .nuops = 4, .uops = { { _GUARD_CALLABLE_LIST_APPEND, OPARG_SIMPLE, 3 }, { _GUARD_NOS_NOT_NULL, OPARG_SIMPLE, 3 }, { _GUARD_NOS_LIST, OPARG_SIMPLE, 3 }, { _CALL_LIST_APPEND, OPARG_SIMPLE, 3 } } },
[CALL_METHOD_DESCRIPTOR_FAST] = { .nuops = 2, .uops = { { _CALL_METHOD_DESCRIPTOR_FAST, OPARG_SIMPLE, 3 }, { _CHECK_PERIODIC, OPARG_SIMPLE, 3 } } },
[CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = { .nuops = 2, .uops = { { _CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS, OPARG_SIMPLE, 3 }, { _CHECK_PERIODIC, OPARG_SIMPLE, 3 } } },
[CALL_METHOD_DESCRIPTOR_NOARGS] = { .nuops = 2, .uops = { { _CALL_METHOD_DESCRIPTOR_NOARGS, OPARG_SIMPLE, 3 }, { _CHECK_PERIODIC, OPARG_SIMPLE, 3 } } },
@@ -1787,6 +1787,35 @@ const uint8_t _PyOpcode_Caches[256] = {
extern const uint8_t _PyOpcode_Deopt[256];
#ifdef NEED_OPCODE_METADATA
const uint8_t _PyOpcode_Deopt[256] = {
+ [121] = 121,
+ [122] = 122,
+ [123] = 123,
+ [124] = 124,
+ [125] = 125,
+ [126] = 126,
+ [127] = 127,
+ [212] = 212,
+ [213] = 213,
+ [214] = 214,
+ [215] = 215,
+ [216] = 216,
+ [217] = 217,
+ [218] = 218,
+ [219] = 219,
+ [220] = 220,
+ [221] = 221,
+ [222] = 222,
+ [223] = 223,
+ [224] = 224,
+ [225] = 225,
+ [226] = 226,
+ [227] = 227,
+ [228] = 228,
+ [229] = 229,
+ [230] = 230,
+ [231] = 231,
+ [232] = 232,
+ [233] = 233,
[BINARY_OP] = BINARY_OP,
[BINARY_OP_ADD_FLOAT] = BINARY_OP,
[BINARY_OP_ADD_INT] = BINARY_OP,
diff --git a/Include/internal/pycore_uop_ids.h b/Include/internal/pycore_uop_ids.h
index 71a288a3a39..322f3cc093f 100644
--- a/Include/internal/pycore_uop_ids.h
+++ b/Include/internal/pycore_uop_ids.h
@@ -46,124 +46,126 @@ extern "C" {
#define _CALL_ISINSTANCE 324
#define _CALL_KW_NON_PY 325
#define _CALL_LEN 326
-#define _CALL_LIST_APPEND CALL_LIST_APPEND
-#define _CALL_METHOD_DESCRIPTOR_FAST 327
-#define _CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS 328
-#define _CALL_METHOD_DESCRIPTOR_NOARGS 329
-#define _CALL_METHOD_DESCRIPTOR_O 330
-#define _CALL_NON_PY_GENERAL 331
-#define _CALL_STR_1 332
-#define _CALL_TUPLE_1 333
-#define _CALL_TYPE_1 334
-#define _CHECK_AND_ALLOCATE_OBJECT 335
-#define _CHECK_ATTR_CLASS 336
-#define _CHECK_ATTR_METHOD_LAZY_DICT 337
-#define _CHECK_CALL_BOUND_METHOD_EXACT_ARGS 338
+#define _CALL_LIST_APPEND 327
+#define _CALL_METHOD_DESCRIPTOR_FAST 328
+#define _CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS 329
+#define _CALL_METHOD_DESCRIPTOR_NOARGS 330
+#define _CALL_METHOD_DESCRIPTOR_O 331
+#define _CALL_NON_PY_GENERAL 332
+#define _CALL_STR_1 333
+#define _CALL_TUPLE_1 334
+#define _CALL_TYPE_1 335
+#define _CHECK_AND_ALLOCATE_OBJECT 336
+#define _CHECK_ATTR_CLASS 337
+#define _CHECK_ATTR_METHOD_LAZY_DICT 338
+#define _CHECK_CALL_BOUND_METHOD_EXACT_ARGS 339
#define _CHECK_EG_MATCH CHECK_EG_MATCH
#define _CHECK_EXC_MATCH CHECK_EXC_MATCH
-#define _CHECK_FUNCTION 339
-#define _CHECK_FUNCTION_EXACT_ARGS 340
-#define _CHECK_FUNCTION_VERSION 341
-#define _CHECK_FUNCTION_VERSION_INLINE 342
-#define _CHECK_FUNCTION_VERSION_KW 343
-#define _CHECK_IS_NOT_PY_CALLABLE 344
-#define _CHECK_IS_NOT_PY_CALLABLE_KW 345
-#define _CHECK_MANAGED_OBJECT_HAS_VALUES 346
-#define _CHECK_METHOD_VERSION 347
-#define _CHECK_METHOD_VERSION_KW 348
-#define _CHECK_PEP_523 349
-#define _CHECK_PERIODIC 350
-#define _CHECK_PERIODIC_IF_NOT_YIELD_FROM 351
-#define _CHECK_RECURSION_REMAINING 352
-#define _CHECK_STACK_SPACE 353
-#define _CHECK_STACK_SPACE_OPERAND 354
-#define _CHECK_VALIDITY 355
-#define _COMPARE_OP 356
-#define _COMPARE_OP_FLOAT 357
-#define _COMPARE_OP_INT 358
-#define _COMPARE_OP_STR 359
-#define _CONTAINS_OP 360
-#define _CONTAINS_OP_DICT 361
-#define _CONTAINS_OP_SET 362
+#define _CHECK_FUNCTION 340
+#define _CHECK_FUNCTION_EXACT_ARGS 341
+#define _CHECK_FUNCTION_VERSION 342
+#define _CHECK_FUNCTION_VERSION_INLINE 343
+#define _CHECK_FUNCTION_VERSION_KW 344
+#define _CHECK_IS_NOT_PY_CALLABLE 345
+#define _CHECK_IS_NOT_PY_CALLABLE_KW 346
+#define _CHECK_MANAGED_OBJECT_HAS_VALUES 347
+#define _CHECK_METHOD_VERSION 348
+#define _CHECK_METHOD_VERSION_KW 349
+#define _CHECK_PEP_523 350
+#define _CHECK_PERIODIC 351
+#define _CHECK_PERIODIC_IF_NOT_YIELD_FROM 352
+#define _CHECK_RECURSION_REMAINING 353
+#define _CHECK_STACK_SPACE 354
+#define _CHECK_STACK_SPACE_OPERAND 355
+#define _CHECK_VALIDITY 356
+#define _COMPARE_OP 357
+#define _COMPARE_OP_FLOAT 358
+#define _COMPARE_OP_INT 359
+#define _COMPARE_OP_STR 360
+#define _CONTAINS_OP 361
+#define _CONTAINS_OP_DICT 362
+#define _CONTAINS_OP_SET 363
#define _CONVERT_VALUE CONVERT_VALUE
#define _COPY COPY
#define _COPY_FREE_VARS COPY_FREE_VARS
-#define _CREATE_INIT_FRAME 363
+#define _CREATE_INIT_FRAME 364
#define _DELETE_ATTR DELETE_ATTR
#define _DELETE_DEREF DELETE_DEREF
#define _DELETE_FAST DELETE_FAST
#define _DELETE_GLOBAL DELETE_GLOBAL
#define _DELETE_NAME DELETE_NAME
#define _DELETE_SUBSCR DELETE_SUBSCR
-#define _DEOPT 364
+#define _DEOPT 365
#define _DICT_MERGE DICT_MERGE
#define _DICT_UPDATE DICT_UPDATE
-#define _DO_CALL 365
-#define _DO_CALL_FUNCTION_EX 366
-#define _DO_CALL_KW 367
+#define _DO_CALL 366
+#define _DO_CALL_FUNCTION_EX 367
+#define _DO_CALL_KW 368
#define _END_FOR END_FOR
#define _END_SEND END_SEND
-#define _ERROR_POP_N 368
+#define _ERROR_POP_N 369
#define _EXIT_INIT_CHECK EXIT_INIT_CHECK
-#define _EXPAND_METHOD 369
-#define _EXPAND_METHOD_KW 370
-#define _FATAL_ERROR 371
+#define _EXPAND_METHOD 370
+#define _EXPAND_METHOD_KW 371
+#define _FATAL_ERROR 372
#define _FORMAT_SIMPLE FORMAT_SIMPLE
#define _FORMAT_WITH_SPEC FORMAT_WITH_SPEC
-#define _FOR_ITER 372
-#define _FOR_ITER_GEN_FRAME 373
-#define _FOR_ITER_TIER_TWO 374
+#define _FOR_ITER 373
+#define _FOR_ITER_GEN_FRAME 374
+#define _FOR_ITER_TIER_TWO 375
#define _GET_AITER GET_AITER
#define _GET_ANEXT GET_ANEXT
#define _GET_AWAITABLE GET_AWAITABLE
#define _GET_ITER GET_ITER
#define _GET_LEN GET_LEN
#define _GET_YIELD_FROM_ITER GET_YIELD_FROM_ITER
-#define _GUARD_BINARY_OP_EXTEND 375
-#define _GUARD_CALLABLE_ISINSTANCE 376
-#define _GUARD_CALLABLE_LEN 377
-#define _GUARD_CALLABLE_STR_1 378
-#define _GUARD_CALLABLE_TUPLE_1 379
-#define _GUARD_CALLABLE_TYPE_1 380
-#define _GUARD_DORV_NO_DICT 381
-#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT 382
-#define _GUARD_GLOBALS_VERSION 383
-#define _GUARD_IS_FALSE_POP 384
-#define _GUARD_IS_NONE_POP 385
-#define _GUARD_IS_NOT_NONE_POP 386
-#define _GUARD_IS_TRUE_POP 387
-#define _GUARD_KEYS_VERSION 388
-#define _GUARD_NOS_DICT 389
-#define _GUARD_NOS_FLOAT 390
-#define _GUARD_NOS_INT 391
-#define _GUARD_NOS_LIST 392
-#define _GUARD_NOS_NULL 393
-#define _GUARD_NOS_TUPLE 394
-#define _GUARD_NOS_UNICODE 395
-#define _GUARD_NOT_EXHAUSTED_LIST 396
-#define _GUARD_NOT_EXHAUSTED_RANGE 397
-#define _GUARD_NOT_EXHAUSTED_TUPLE 398
-#define _GUARD_THIRD_NULL 399
-#define _GUARD_TOS_ANY_SET 400
-#define _GUARD_TOS_DICT 401
-#define _GUARD_TOS_FLOAT 402
-#define _GUARD_TOS_INT 403
-#define _GUARD_TOS_LIST 404
-#define _GUARD_TOS_SLICE 405
-#define _GUARD_TOS_TUPLE 406
-#define _GUARD_TOS_UNICODE 407
-#define _GUARD_TYPE_VERSION 408
-#define _GUARD_TYPE_VERSION_AND_LOCK 409
+#define _GUARD_BINARY_OP_EXTEND 376
+#define _GUARD_CALLABLE_ISINSTANCE 377
+#define _GUARD_CALLABLE_LEN 378
+#define _GUARD_CALLABLE_LIST_APPEND 379
+#define _GUARD_CALLABLE_STR_1 380
+#define _GUARD_CALLABLE_TUPLE_1 381
+#define _GUARD_CALLABLE_TYPE_1 382
+#define _GUARD_DORV_NO_DICT 383
+#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT 384
+#define _GUARD_GLOBALS_VERSION 385
+#define _GUARD_IS_FALSE_POP 386
+#define _GUARD_IS_NONE_POP 387
+#define _GUARD_IS_NOT_NONE_POP 388
+#define _GUARD_IS_TRUE_POP 389
+#define _GUARD_KEYS_VERSION 390
+#define _GUARD_NOS_DICT 391
+#define _GUARD_NOS_FLOAT 392
+#define _GUARD_NOS_INT 393
+#define _GUARD_NOS_LIST 394
+#define _GUARD_NOS_NOT_NULL 395
+#define _GUARD_NOS_NULL 396
+#define _GUARD_NOS_TUPLE 397
+#define _GUARD_NOS_UNICODE 398
+#define _GUARD_NOT_EXHAUSTED_LIST 399
+#define _GUARD_NOT_EXHAUSTED_RANGE 400
+#define _GUARD_NOT_EXHAUSTED_TUPLE 401
+#define _GUARD_THIRD_NULL 402
+#define _GUARD_TOS_ANY_SET 403
+#define _GUARD_TOS_DICT 404
+#define _GUARD_TOS_FLOAT 405
+#define _GUARD_TOS_INT 406
+#define _GUARD_TOS_LIST 407
+#define _GUARD_TOS_SLICE 408
+#define _GUARD_TOS_TUPLE 409
+#define _GUARD_TOS_UNICODE 410
+#define _GUARD_TYPE_VERSION 411
+#define _GUARD_TYPE_VERSION_AND_LOCK 412
#define _IMPORT_FROM IMPORT_FROM
#define _IMPORT_NAME IMPORT_NAME
-#define _INIT_CALL_BOUND_METHOD_EXACT_ARGS 410
-#define _INIT_CALL_PY_EXACT_ARGS 411
-#define _INIT_CALL_PY_EXACT_ARGS_0 412
-#define _INIT_CALL_PY_EXACT_ARGS_1 413
-#define _INIT_CALL_PY_EXACT_ARGS_2 414
-#define _INIT_CALL_PY_EXACT_ARGS_3 415
-#define _INIT_CALL_PY_EXACT_ARGS_4 416
-#define _INSERT_NULL 417
+#define _INIT_CALL_BOUND_METHOD_EXACT_ARGS 413
+#define _INIT_CALL_PY_EXACT_ARGS 414
+#define _INIT_CALL_PY_EXACT_ARGS_0 415
+#define _INIT_CALL_PY_EXACT_ARGS_1 416
+#define _INIT_CALL_PY_EXACT_ARGS_2 417
+#define _INIT_CALL_PY_EXACT_ARGS_3 418
+#define _INIT_CALL_PY_EXACT_ARGS_4 419
+#define _INSERT_NULL 420
#define _INSTRUMENTED_FOR_ITER INSTRUMENTED_FOR_ITER
#define _INSTRUMENTED_INSTRUCTION INSTRUMENTED_INSTRUCTION
#define _INSTRUMENTED_JUMP_FORWARD INSTRUMENTED_JUMP_FORWARD
@@ -173,163 +175,164 @@ extern "C" {
#define _INSTRUMENTED_POP_JUMP_IF_NONE INSTRUMENTED_POP_JUMP_IF_NONE
#define _INSTRUMENTED_POP_JUMP_IF_NOT_NONE INSTRUMENTED_POP_JUMP_IF_NOT_NONE
#define _INSTRUMENTED_POP_JUMP_IF_TRUE INSTRUMENTED_POP_JUMP_IF_TRUE
-#define _IS_NONE 418
+#define _IS_NONE 421
#define _IS_OP IS_OP
-#define _ITER_CHECK_LIST 419
-#define _ITER_CHECK_RANGE 420
-#define _ITER_CHECK_TUPLE 421
-#define _ITER_JUMP_LIST 422
-#define _ITER_JUMP_RANGE 423
-#define _ITER_JUMP_TUPLE 424
-#define _ITER_NEXT_LIST 425
-#define _ITER_NEXT_LIST_TIER_TWO 426
-#define _ITER_NEXT_RANGE 427
-#define _ITER_NEXT_TUPLE 428
-#define _JUMP_TO_TOP 429
+#define _ITER_CHECK_LIST 422
+#define _ITER_CHECK_RANGE 423
+#define _ITER_CHECK_TUPLE 424
+#define _ITER_JUMP_LIST 425
+#define _ITER_JUMP_RANGE 426
+#define _ITER_JUMP_TUPLE 427
+#define _ITER_NEXT_LIST 428
+#define _ITER_NEXT_LIST_TIER_TWO 429
+#define _ITER_NEXT_RANGE 430
+#define _ITER_NEXT_TUPLE 431
+#define _JUMP_TO_TOP 432
#define _LIST_APPEND LIST_APPEND
#define _LIST_EXTEND LIST_EXTEND
-#define _LOAD_ATTR 430
-#define _LOAD_ATTR_CLASS 431
+#define _LOAD_ATTR 433
+#define _LOAD_ATTR_CLASS 434
#define _LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN
-#define _LOAD_ATTR_INSTANCE_VALUE 432
-#define _LOAD_ATTR_METHOD_LAZY_DICT 433
-#define _LOAD_ATTR_METHOD_NO_DICT 434
-#define _LOAD_ATTR_METHOD_WITH_VALUES 435
-#define _LOAD_ATTR_MODULE 436
-#define _LOAD_ATTR_NONDESCRIPTOR_NO_DICT 437
-#define _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES 438
-#define _LOAD_ATTR_PROPERTY_FRAME 439
-#define _LOAD_ATTR_SLOT 440
-#define _LOAD_ATTR_WITH_HINT 441
+#define _LOAD_ATTR_INSTANCE_VALUE 435
+#define _LOAD_ATTR_METHOD_LAZY_DICT 436
+#define _LOAD_ATTR_METHOD_NO_DICT 437
+#define _LOAD_ATTR_METHOD_WITH_VALUES 438
+#define _LOAD_ATTR_MODULE 439
+#define _LOAD_ATTR_NONDESCRIPTOR_NO_DICT 440
+#define _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES 441
+#define _LOAD_ATTR_PROPERTY_FRAME 442
+#define _LOAD_ATTR_SLOT 443
+#define _LOAD_ATTR_WITH_HINT 444
#define _LOAD_BUILD_CLASS LOAD_BUILD_CLASS
-#define _LOAD_BYTECODE 442
+#define _LOAD_BYTECODE 445
#define _LOAD_COMMON_CONSTANT LOAD_COMMON_CONSTANT
#define _LOAD_CONST LOAD_CONST
#define _LOAD_CONST_IMMORTAL LOAD_CONST_IMMORTAL
-#define _LOAD_CONST_INLINE 443
-#define _LOAD_CONST_INLINE_BORROW 444
+#define _LOAD_CONST_INLINE 446
+#define _LOAD_CONST_INLINE_BORROW 447
#define _LOAD_CONST_MORTAL LOAD_CONST_MORTAL
#define _LOAD_DEREF LOAD_DEREF
-#define _LOAD_FAST 445
-#define _LOAD_FAST_0 446
-#define _LOAD_FAST_1 447
-#define _LOAD_FAST_2 448
-#define _LOAD_FAST_3 449
-#define _LOAD_FAST_4 450
-#define _LOAD_FAST_5 451
-#define _LOAD_FAST_6 452
-#define _LOAD_FAST_7 453
+#define _LOAD_FAST 448
+#define _LOAD_FAST_0 449
+#define _LOAD_FAST_1 450
+#define _LOAD_FAST_2 451
+#define _LOAD_FAST_3 452
+#define _LOAD_FAST_4 453
+#define _LOAD_FAST_5 454
+#define _LOAD_FAST_6 455
+#define _LOAD_FAST_7 456
#define _LOAD_FAST_AND_CLEAR LOAD_FAST_AND_CLEAR
-#define _LOAD_FAST_BORROW 454
-#define _LOAD_FAST_BORROW_0 455
-#define _LOAD_FAST_BORROW_1 456
-#define _LOAD_FAST_BORROW_2 457
-#define _LOAD_FAST_BORROW_3 458
-#define _LOAD_FAST_BORROW_4 459
-#define _LOAD_FAST_BORROW_5 460
-#define _LOAD_FAST_BORROW_6 461
-#define _LOAD_FAST_BORROW_7 462
+#define _LOAD_FAST_BORROW 457
+#define _LOAD_FAST_BORROW_0 458
+#define _LOAD_FAST_BORROW_1 459
+#define _LOAD_FAST_BORROW_2 460
+#define _LOAD_FAST_BORROW_3 461
+#define _LOAD_FAST_BORROW_4 462
+#define _LOAD_FAST_BORROW_5 463
+#define _LOAD_FAST_BORROW_6 464
+#define _LOAD_FAST_BORROW_7 465
#define _LOAD_FAST_BORROW_LOAD_FAST_BORROW LOAD_FAST_BORROW_LOAD_FAST_BORROW
#define _LOAD_FAST_CHECK LOAD_FAST_CHECK
#define _LOAD_FAST_LOAD_FAST LOAD_FAST_LOAD_FAST
#define _LOAD_FROM_DICT_OR_DEREF LOAD_FROM_DICT_OR_DEREF
#define _LOAD_FROM_DICT_OR_GLOBALS LOAD_FROM_DICT_OR_GLOBALS
-#define _LOAD_GLOBAL 463
-#define _LOAD_GLOBAL_BUILTINS 464
-#define _LOAD_GLOBAL_MODULE 465
+#define _LOAD_GLOBAL 466
+#define _LOAD_GLOBAL_BUILTINS 467
+#define _LOAD_GLOBAL_MODULE 468
#define _LOAD_LOCALS LOAD_LOCALS
#define _LOAD_NAME LOAD_NAME
-#define _LOAD_SMALL_INT 466
-#define _LOAD_SMALL_INT_0 467
-#define _LOAD_SMALL_INT_1 468
-#define _LOAD_SMALL_INT_2 469
-#define _LOAD_SMALL_INT_3 470
-#define _LOAD_SPECIAL 471
+#define _LOAD_SMALL_INT 469
+#define _LOAD_SMALL_INT_0 470
+#define _LOAD_SMALL_INT_1 471
+#define _LOAD_SMALL_INT_2 472
+#define _LOAD_SMALL_INT_3 473
+#define _LOAD_SPECIAL 474
#define _LOAD_SUPER_ATTR_ATTR LOAD_SUPER_ATTR_ATTR
#define _LOAD_SUPER_ATTR_METHOD LOAD_SUPER_ATTR_METHOD
-#define _MAKE_CALLARGS_A_TUPLE 472
+#define _MAKE_CALLARGS_A_TUPLE 475
#define _MAKE_CELL MAKE_CELL
#define _MAKE_FUNCTION MAKE_FUNCTION
-#define _MAKE_WARM 473
+#define _MAKE_WARM 476
#define _MAP_ADD MAP_ADD
#define _MATCH_CLASS MATCH_CLASS
#define _MATCH_KEYS MATCH_KEYS
#define _MATCH_MAPPING MATCH_MAPPING
#define _MATCH_SEQUENCE MATCH_SEQUENCE
-#define _MAYBE_EXPAND_METHOD 474
-#define _MAYBE_EXPAND_METHOD_KW 475
-#define _MONITOR_CALL 476
-#define _MONITOR_CALL_KW 477
-#define _MONITOR_JUMP_BACKWARD 478
-#define _MONITOR_RESUME 479
+#define _MAYBE_EXPAND_METHOD 477
+#define _MAYBE_EXPAND_METHOD_KW 478
+#define _MONITOR_CALL 479
+#define _MONITOR_CALL_KW 480
+#define _MONITOR_JUMP_BACKWARD 481
+#define _MONITOR_RESUME 482
#define _NOP NOP
+#define _POP_CALL_TWO_LOAD_CONST_INLINE_BORROW 483
#define _POP_EXCEPT POP_EXCEPT
-#define _POP_JUMP_IF_FALSE 480
-#define _POP_JUMP_IF_TRUE 481
+#define _POP_JUMP_IF_FALSE 484
+#define _POP_JUMP_IF_TRUE 485
#define _POP_TOP POP_TOP
-#define _POP_TOP_LOAD_CONST_INLINE 482
-#define _POP_TOP_LOAD_CONST_INLINE_BORROW 483
-#define _POP_TWO_LOAD_CONST_INLINE_BORROW 484
+#define _POP_TOP_LOAD_CONST_INLINE 486
+#define _POP_TOP_LOAD_CONST_INLINE_BORROW 487
+#define _POP_TWO_LOAD_CONST_INLINE_BORROW 488
#define _PUSH_EXC_INFO PUSH_EXC_INFO
-#define _PUSH_FRAME 485
+#define _PUSH_FRAME 489
#define _PUSH_NULL PUSH_NULL
-#define _PUSH_NULL_CONDITIONAL 486
-#define _PY_FRAME_GENERAL 487
-#define _PY_FRAME_KW 488
-#define _QUICKEN_RESUME 489
-#define _REPLACE_WITH_TRUE 490
+#define _PUSH_NULL_CONDITIONAL 490
+#define _PY_FRAME_GENERAL 491
+#define _PY_FRAME_KW 492
+#define _QUICKEN_RESUME 493
+#define _REPLACE_WITH_TRUE 494
#define _RESUME_CHECK RESUME_CHECK
#define _RETURN_GENERATOR RETURN_GENERATOR
#define _RETURN_VALUE RETURN_VALUE
-#define _SAVE_RETURN_OFFSET 491
-#define _SEND 492
-#define _SEND_GEN_FRAME 493
+#define _SAVE_RETURN_OFFSET 495
+#define _SEND 496
+#define _SEND_GEN_FRAME 497
#define _SETUP_ANNOTATIONS SETUP_ANNOTATIONS
#define _SET_ADD SET_ADD
#define _SET_FUNCTION_ATTRIBUTE SET_FUNCTION_ATTRIBUTE
#define _SET_UPDATE SET_UPDATE
-#define _START_EXECUTOR 494
-#define _STORE_ATTR 495
-#define _STORE_ATTR_INSTANCE_VALUE 496
-#define _STORE_ATTR_SLOT 497
-#define _STORE_ATTR_WITH_HINT 498
+#define _START_EXECUTOR 498
+#define _STORE_ATTR 499
+#define _STORE_ATTR_INSTANCE_VALUE 500
+#define _STORE_ATTR_SLOT 501
+#define _STORE_ATTR_WITH_HINT 502
#define _STORE_DEREF STORE_DEREF
-#define _STORE_FAST 499
-#define _STORE_FAST_0 500
-#define _STORE_FAST_1 501
-#define _STORE_FAST_2 502
-#define _STORE_FAST_3 503
-#define _STORE_FAST_4 504
-#define _STORE_FAST_5 505
-#define _STORE_FAST_6 506
-#define _STORE_FAST_7 507
+#define _STORE_FAST 503
+#define _STORE_FAST_0 504
+#define _STORE_FAST_1 505
+#define _STORE_FAST_2 506
+#define _STORE_FAST_3 507
+#define _STORE_FAST_4 508
+#define _STORE_FAST_5 509
+#define _STORE_FAST_6 510
+#define _STORE_FAST_7 511
#define _STORE_FAST_LOAD_FAST STORE_FAST_LOAD_FAST
#define _STORE_FAST_STORE_FAST STORE_FAST_STORE_FAST
#define _STORE_GLOBAL STORE_GLOBAL
#define _STORE_NAME STORE_NAME
-#define _STORE_SLICE 508
-#define _STORE_SUBSCR 509
-#define _STORE_SUBSCR_DICT 510
-#define _STORE_SUBSCR_LIST_INT 511
+#define _STORE_SLICE 512
+#define _STORE_SUBSCR 513
+#define _STORE_SUBSCR_DICT 514
+#define _STORE_SUBSCR_LIST_INT 515
#define _SWAP SWAP
-#define _TIER2_RESUME_CHECK 512
-#define _TO_BOOL 513
+#define _TIER2_RESUME_CHECK 516
+#define _TO_BOOL 517
#define _TO_BOOL_BOOL TO_BOOL_BOOL
#define _TO_BOOL_INT TO_BOOL_INT
-#define _TO_BOOL_LIST 514
+#define _TO_BOOL_LIST 518
#define _TO_BOOL_NONE TO_BOOL_NONE
-#define _TO_BOOL_STR 515
+#define _TO_BOOL_STR 519
#define _UNARY_INVERT UNARY_INVERT
#define _UNARY_NEGATIVE UNARY_NEGATIVE
#define _UNARY_NOT UNARY_NOT
#define _UNPACK_EX UNPACK_EX
-#define _UNPACK_SEQUENCE 516
-#define _UNPACK_SEQUENCE_LIST 517
-#define _UNPACK_SEQUENCE_TUPLE 518
-#define _UNPACK_SEQUENCE_TWO_TUPLE 519
+#define _UNPACK_SEQUENCE 520
+#define _UNPACK_SEQUENCE_LIST 521
+#define _UNPACK_SEQUENCE_TUPLE 522
+#define _UNPACK_SEQUENCE_TWO_TUPLE 523
#define _WITH_EXCEPT_START WITH_EXCEPT_START
#define _YIELD_VALUE YIELD_VALUE
-#define MAX_UOP_ID 519
+#define MAX_UOP_ID 523
#ifdef __cplusplus
}
diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h
index 88a2e538447..b334dd840c8 100644
--- a/Include/internal/pycore_uop_metadata.h
+++ b/Include/internal/pycore_uop_metadata.h
@@ -247,6 +247,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = {
[_INIT_CALL_PY_EXACT_ARGS] = HAS_ARG_FLAG | HAS_PURE_FLAG,
[_PUSH_FRAME] = 0,
[_GUARD_NOS_NULL] = HAS_DEOPT_FLAG,
+ [_GUARD_NOS_NOT_NULL] = HAS_EXIT_FLAG,
[_GUARD_THIRD_NULL] = HAS_DEOPT_FLAG,
[_GUARD_CALLABLE_TYPE_1] = HAS_DEOPT_FLAG,
[_CALL_TYPE_1] = HAS_ARG_FLAG | HAS_ESCAPES_FLAG,
@@ -265,6 +266,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = {
[_CALL_LEN] = HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG,
[_GUARD_CALLABLE_ISINSTANCE] = HAS_DEOPT_FLAG,
[_CALL_ISINSTANCE] = HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG,
+ [_GUARD_CALLABLE_LIST_APPEND] = HAS_DEOPT_FLAG,
[_CALL_LIST_APPEND] = HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG,
[_CALL_METHOD_DESCRIPTOR_O] = HAS_ARG_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG,
[_CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = HAS_ARG_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG,
@@ -303,6 +305,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = {
[_LOAD_CONST_INLINE_BORROW] = HAS_PURE_FLAG,
[_POP_TOP_LOAD_CONST_INLINE_BORROW] = HAS_ESCAPES_FLAG | HAS_PURE_FLAG,
[_POP_TWO_LOAD_CONST_INLINE_BORROW] = HAS_ESCAPES_FLAG | HAS_PURE_FLAG,
+ [_POP_CALL_TWO_LOAD_CONST_INLINE_BORROW] = HAS_ESCAPES_FLAG | HAS_PURE_FLAG,
[_CHECK_FUNCTION] = HAS_DEOPT_FLAG,
[_START_EXECUTOR] = 0,
[_MAKE_WARM] = 0,
@@ -429,6 +432,7 @@ const char *const _PyOpcode_uop_name[MAX_UOP_ID+1] = {
[_GUARD_BINARY_OP_EXTEND] = "_GUARD_BINARY_OP_EXTEND",
[_GUARD_CALLABLE_ISINSTANCE] = "_GUARD_CALLABLE_ISINSTANCE",
[_GUARD_CALLABLE_LEN] = "_GUARD_CALLABLE_LEN",
+ [_GUARD_CALLABLE_LIST_APPEND] = "_GUARD_CALLABLE_LIST_APPEND",
[_GUARD_CALLABLE_STR_1] = "_GUARD_CALLABLE_STR_1",
[_GUARD_CALLABLE_TUPLE_1] = "_GUARD_CALLABLE_TUPLE_1",
[_GUARD_CALLABLE_TYPE_1] = "_GUARD_CALLABLE_TYPE_1",
@@ -444,6 +448,7 @@ const char *const _PyOpcode_uop_name[MAX_UOP_ID+1] = {
[_GUARD_NOS_FLOAT] = "_GUARD_NOS_FLOAT",
[_GUARD_NOS_INT] = "_GUARD_NOS_INT",
[_GUARD_NOS_LIST] = "_GUARD_NOS_LIST",
+ [_GUARD_NOS_NOT_NULL] = "_GUARD_NOS_NOT_NULL",
[_GUARD_NOS_NULL] = "_GUARD_NOS_NULL",
[_GUARD_NOS_TUPLE] = "_GUARD_NOS_TUPLE",
[_GUARD_NOS_UNICODE] = "_GUARD_NOS_UNICODE",
@@ -549,6 +554,7 @@ const char *const _PyOpcode_uop_name[MAX_UOP_ID+1] = {
[_MAYBE_EXPAND_METHOD] = "_MAYBE_EXPAND_METHOD",
[_MAYBE_EXPAND_METHOD_KW] = "_MAYBE_EXPAND_METHOD_KW",
[_NOP] = "_NOP",
+ [_POP_CALL_TWO_LOAD_CONST_INLINE_BORROW] = "_POP_CALL_TWO_LOAD_CONST_INLINE_BORROW",
[_POP_EXCEPT] = "_POP_EXCEPT",
[_POP_TOP] = "_POP_TOP",
[_POP_TOP_LOAD_CONST_INLINE] = "_POP_TOP_LOAD_CONST_INLINE",
@@ -1072,6 +1078,8 @@ int _PyUop_num_popped(int opcode, int oparg)
return 1;
case _GUARD_NOS_NULL:
return 0;
+ case _GUARD_NOS_NOT_NULL:
+ return 0;
case _GUARD_THIRD_NULL:
return 0;
case _GUARD_CALLABLE_TYPE_1:
@@ -1108,6 +1116,8 @@ int _PyUop_num_popped(int opcode, int oparg)
return 0;
case _CALL_ISINSTANCE:
return 4;
+ case _GUARD_CALLABLE_LIST_APPEND:
+ return 0;
case _CALL_LIST_APPEND:
return 3;
case _CALL_METHOD_DESCRIPTOR_O:
@@ -1184,6 +1194,8 @@ int _PyUop_num_popped(int opcode, int oparg)
return 1;
case _POP_TWO_LOAD_CONST_INLINE_BORROW:
return 2;
+ case _POP_CALL_TWO_LOAD_CONST_INLINE_BORROW:
+ return 4;
case _CHECK_FUNCTION:
return 0;
case _START_EXECUTOR:
diff --git a/Include/py_curses.h b/Include/py_curses.h
index 49fc3c9d127..0948aabedd4 100644
--- a/Include/py_curses.h
+++ b/Include/py_curses.h
@@ -109,6 +109,13 @@ static void **PyCurses_API;
static const char catchall_ERR[] = "curses function returned ERR";
static const char catchall_NULL[] = "curses function returned NULL";
+#if defined(CURSES_MODULE) || defined(CURSES_PANEL_MODULE)
+/* Error messages shared by the curses package */
+# define CURSES_ERROR_FORMAT "%s() returned %s"
+# define CURSES_ERROR_VERBOSE_FORMAT "%s() (called by %s()) returned %s"
+# define CURSES_ERROR_MUST_CALL_FORMAT "must call %s() first"
+#endif
+
#ifdef __cplusplus
}
#endif
diff --git a/Lib/_pydatetime.py b/Lib/_pydatetime.py
index 471e89c16a1..71f619024e5 100644
--- a/Lib/_pydatetime.py
+++ b/Lib/_pydatetime.py
@@ -467,6 +467,7 @@ def _parse_isoformat_time(tstr):
hour, minute, second, microsecond = time_comps
became_next_day = False
error_from_components = False
+ error_from_tz = None
if (hour == 24):
if all(time_comp == 0 for time_comp in time_comps[1:]):
hour = 0
@@ -500,14 +501,22 @@ def _parse_isoformat_time(tstr):
else:
tzsign = -1 if tstr[tz_pos - 1] == '-' else 1
- td = timedelta(hours=tz_comps[0], minutes=tz_comps[1],
- seconds=tz_comps[2], microseconds=tz_comps[3])
-
- tzi = timezone(tzsign * td)
+ try:
+ # This function is intended to validate datetimes, but because
+ # we restrict time zones to ±24h, it serves here as well.
+ _check_time_fields(hour=tz_comps[0], minute=tz_comps[1],
+ second=tz_comps[2], microsecond=tz_comps[3],
+ fold=0)
+ except ValueError as e:
+ error_from_tz = e
+ else:
+ td = timedelta(hours=tz_comps[0], minutes=tz_comps[1],
+ seconds=tz_comps[2], microseconds=tz_comps[3])
+ tzi = timezone(tzsign * td)
time_comps.append(tzi)
- return time_comps, became_next_day, error_from_components
+ return time_comps, became_next_day, error_from_components, error_from_tz
# tuple[int, int, int] -> tuple[int, int, int] version of date.fromisocalendar
def _isoweek_to_gregorian(year, week, day):
@@ -1633,9 +1642,21 @@ class time:
time_string = time_string.removeprefix('T')
try:
- return cls(*_parse_isoformat_time(time_string)[0])
- except Exception:
- raise ValueError(f'Invalid isoformat string: {time_string!r}')
+ time_components, _, error_from_components, error_from_tz = (
+ _parse_isoformat_time(time_string)
+ )
+ except ValueError:
+ raise ValueError(
+ f'Invalid isoformat string: {time_string!r}') from None
+ else:
+ if error_from_tz:
+ raise error_from_tz
+ if error_from_components:
+ raise ValueError(
+ "Minute, second, and microsecond must be 0 when hour is 24"
+ )
+
+ return cls(*time_components)
def strftime(self, format):
"""Format using strftime(). The date part of the timestamp passed
@@ -1947,11 +1968,16 @@ class datetime(date):
if tstr:
try:
- time_components, became_next_day, error_from_components = _parse_isoformat_time(tstr)
+ (time_components,
+ became_next_day,
+ error_from_components,
+ error_from_tz) = _parse_isoformat_time(tstr)
except ValueError:
raise ValueError(
f'Invalid isoformat string: {date_string!r}') from None
else:
+ if error_from_tz:
+ raise error_from_tz
if error_from_components:
raise ValueError("minute, second, and microsecond must be 0 when hour is 24")
diff --git a/Lib/_pyrepl/_module_completer.py b/Lib/_pyrepl/_module_completer.py
index 347f05607c7..0606797226d 100644
--- a/Lib/_pyrepl/_module_completer.py
+++ b/Lib/_pyrepl/_module_completer.py
@@ -81,8 +81,9 @@ class ModuleCompleter:
def _find_modules(self, path: str, prefix: str) -> list[str]:
if not path:
# Top-level import (e.g. `import foo<tab>`` or `from foo<tab>`)`
- return [name for _, name, _ in self.global_cache
- if name.startswith(prefix)]
+ builtin_modules = [name for name in sys.builtin_module_names if name.startswith(prefix)]
+ third_party_modules = [name for _, name, _ in self.global_cache if name.startswith(prefix)]
+ return sorted(builtin_modules + third_party_modules)
if path.startswith('.'):
# Convert relative path to absolute path
diff --git a/Lib/_pyrepl/simple_interact.py b/Lib/_pyrepl/simple_interact.py
index b3848833e14..c23894a34b8 100644
--- a/Lib/_pyrepl/simple_interact.py
+++ b/Lib/_pyrepl/simple_interact.py
@@ -110,6 +110,10 @@ def run_multiline_interactive_console(
more_lines = functools.partial(_more_lines, console)
input_n = 0
+ _is_x_showrefcount_set = sys._xoptions.get("showrefcount")
+ _is_pydebug_build = hasattr(sys, "gettotalrefcount")
+ show_ref_count = _is_x_showrefcount_set and _is_pydebug_build
+
def maybe_run_command(statement: str) -> bool:
statement = statement.strip()
if statement in console.locals or statement not in REPL_COMMANDS:
@@ -167,3 +171,8 @@ def run_multiline_interactive_console(
except:
console.showtraceback()
console.resetbuffer()
+ if show_ref_count:
+ console.write(
+ f"[{sys.gettotalrefcount()} refs,"
+ f" {sys.getallocatedblocks()} blocks]\n"
+ )
diff --git a/Lib/_pyrepl/utils.py b/Lib/_pyrepl/utils.py
index 752049ac05a..e04fbdc6c8a 100644
--- a/Lib/_pyrepl/utils.py
+++ b/Lib/_pyrepl/utils.py
@@ -41,9 +41,15 @@ class Span(NamedTuple):
@classmethod
def from_token(cls, token: TI, line_len: list[int]) -> Self:
+ end_offset = -1
+ if (token.type in {T.FSTRING_MIDDLE, T.TSTRING_MIDDLE}
+ and token.string.endswith(("{", "}"))):
+ # gh-134158: a visible trailing brace comes from a double brace in input
+ end_offset += 1
+
return cls(
line_len[token.start[0] - 1] + token.start[1],
- line_len[token.end[0] - 1] + token.end[1] - 1,
+ line_len[token.end[0] - 1] + token.end[1] + end_offset,
)
diff --git a/Lib/http/server.py b/Lib/http/server.py
index abf9f87a1fc..f6d1b998f42 100644
--- a/Lib/http/server.py
+++ b/Lib/http/server.py
@@ -1000,7 +1000,7 @@ def test(HandlerClass=BaseHTTPRequestHandler,
sys.exit(0)
-if __name__ == '__main__':
+def _main(args=None):
import argparse
import contextlib
@@ -1024,7 +1024,7 @@ if __name__ == '__main__':
parser.add_argument('port', default=8000, type=int, nargs='?',
help='bind to this port '
'(default: %(default)s)')
- args = parser.parse_args()
+ args = parser.parse_args(args)
if not args.tls_cert and args.tls_key:
parser.error("--tls-key requires --tls-cert to be set")
@@ -1064,3 +1064,7 @@ if __name__ == '__main__':
tls_key=args.tls_key,
tls_password=tls_key_password,
)
+
+
+if __name__ == '__main__':
+ _main()
diff --git a/Lib/ntpath.py b/Lib/ntpath.py
index 5481bb8888e..52ff2af743a 100644
--- a/Lib/ntpath.py
+++ b/Lib/ntpath.py
@@ -661,9 +661,10 @@ else:
# 87: ERROR_INVALID_PARAMETER
# 123: ERROR_INVALID_NAME
# 161: ERROR_BAD_PATHNAME
+ # 1005: ERROR_UNRECOGNIZED_VOLUME
# 1920: ERROR_CANT_ACCESS_FILE
# 1921: ERROR_CANT_RESOLVE_FILENAME (implies unfollowable symlink)
- allowed_winerror = 1, 2, 3, 5, 21, 32, 50, 53, 65, 67, 87, 123, 161, 1920, 1921
+ allowed_winerror = 1, 2, 3, 5, 21, 32, 50, 53, 65, 67, 87, 123, 161, 1005, 1920, 1921
# Non-strict algorithm is to find as much of the target directory
# as we can and join the rest.
diff --git a/Lib/pydoc.py b/Lib/pydoc.py
index def76d076a2..7528178fdca 100644
--- a/Lib/pydoc.py
+++ b/Lib/pydoc.py
@@ -2110,7 +2110,7 @@ has the same effect as typing a particular string at the help> prompt.
self.output.write(_introdoc())
def list(self, items, columns=4, width=80):
- items = list(sorted(items))
+ items = sorted(items)
colw = width // columns
rows = (len(items) + columns - 1) // columns
for row in range(rows):
@@ -2142,7 +2142,7 @@ to. Enter any symbol to get more help.
Here is a list of available topics. Enter any topic name to get more help.
''')
- self.list(self.topics.keys())
+ self.list(self.topics.keys(), columns=3)
def showtopic(self, topic, more_xrefs=''):
try:
diff --git a/Lib/socketserver.py b/Lib/socketserver.py
index 35b2723de3b..93b0a23be27 100644
--- a/Lib/socketserver.py
+++ b/Lib/socketserver.py
@@ -441,7 +441,7 @@ class TCPServer(BaseServer):
socket_type = socket.SOCK_STREAM
- request_queue_size = 5
+ request_queue_size = getattr(socket, "SOMAXCONN", 5)
allow_reuse_address = False
diff --git a/Lib/sysconfig/__init__.py b/Lib/sysconfig/__init__.py
index 68890b45b82..49e0986517c 100644
--- a/Lib/sysconfig/__init__.py
+++ b/Lib/sysconfig/__init__.py
@@ -457,7 +457,7 @@ def get_config_h_filename():
"""Return the path of pyconfig.h."""
if _PYTHON_BUILD:
if os.name == "nt":
- inc_dir = os.path.dirname(sys._base_executable)
+ inc_dir = os.path.join(_PROJECT_BASE, 'PC')
else:
inc_dir = _PROJECT_BASE
else:
diff --git a/Lib/tarfile.py b/Lib/tarfile.py
index 13889d76802..212b71f6509 100644
--- a/Lib/tarfile.py
+++ b/Lib/tarfile.py
@@ -2439,7 +2439,7 @@ class TarFile(object):
unfiltered = tarinfo
try:
tarinfo = filter_function(tarinfo, path)
- except (OSError, FilterError) as e:
+ except (OSError, UnicodeEncodeError, FilterError) as e:
self._handle_fatal_error(e)
except ExtractError as e:
self._handle_nonfatal_error(e)
@@ -2460,7 +2460,7 @@ class TarFile(object):
self._extract_member(tarinfo, os.path.join(path, tarinfo.name),
set_attrs=set_attrs,
numeric_owner=numeric_owner)
- except OSError as e:
+ except (OSError, UnicodeEncodeError) as e:
self._handle_fatal_error(e)
except ExtractError as e:
self._handle_nonfatal_error(e)
diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py
index d1882a310bb..345698cfb5f 100644
--- a/Lib/test/datetimetester.py
+++ b/Lib/test/datetimetester.py
@@ -3571,6 +3571,10 @@ class TestDateTime(TestDate):
'2009-04-19T12:30:45.400 +02:30', # Space between ms and timezone (gh-130959)
'2009-04-19T12:30:45.400 ', # Trailing space (gh-130959)
'2009-04-19T12:30:45. 400', # Space before fraction (gh-130959)
+ '2009-04-19T12:30:45+00:90:00', # Time zone field out from range
+ '2009-04-19T12:30:45+00:00:90', # Time zone field out from range
+ '2009-04-19T12:30:45-00:90:00', # Time zone field out from range
+ '2009-04-19T12:30:45-00:00:90', # Time zone field out from range
]
for bad_str in bad_strs:
@@ -4795,6 +4799,11 @@ class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
'12:30:45.400 +02:30', # Space between ms and timezone (gh-130959)
'12:30:45.400 ', # Trailing space (gh-130959)
'12:30:45. 400', # Space before fraction (gh-130959)
+ '24:00:00.000001', # Has non-zero microseconds on 24:00
+ '24:00:01.000000', # Has non-zero seconds on 24:00
+ '24:01:00.000000', # Has non-zero minutes on 24:00
+ '12:30:45+00:90:00', # Time zone field out from range
+ '12:30:45+00:00:90', # Time zone field out from range
]
for bad_str in bad_strs:
diff --git a/Lib/test/lock_tests.py b/Lib/test/lock_tests.py
index 009e04e9c0b..d64b2b9fe28 100644
--- a/Lib/test/lock_tests.py
+++ b/Lib/test/lock_tests.py
@@ -124,6 +124,11 @@ class BaseLockTests(BaseTestCase):
lock = self.locktype()
del lock
+ def test_constructor_noargs(self):
+ self.assertRaises(TypeError, self.locktype, 1)
+ self.assertRaises(TypeError, self.locktype, x=1)
+ self.assertRaises(TypeError, self.locktype, 1, x=2)
+
def test_repr(self):
lock = self.locktype()
self.assertRegex(repr(lock), "<unlocked .* object (.*)?at .*>")
diff --git a/Lib/test/pythoninfo.py b/Lib/test/pythoninfo.py
index 682815c3fdd..e1830f2e6eb 100644
--- a/Lib/test/pythoninfo.py
+++ b/Lib/test/pythoninfo.py
@@ -658,6 +658,16 @@ def collect_zlib(info_add):
copy_attributes(info_add, zlib, 'zlib.%s', attributes)
+def collect_zstd(info_add):
+ try:
+ import _zstd
+ except ImportError:
+ return
+
+ attributes = ('zstd_version',)
+ copy_attributes(info_add, _zstd, 'zstd.%s', attributes)
+
+
def collect_expat(info_add):
try:
from xml.parsers import expat
@@ -1051,6 +1061,7 @@ def collect_info(info):
collect_tkinter,
collect_windows,
collect_zlib,
+ collect_zstd,
collect_libregrtest_utils,
# Collecting from tests should be last as they have side effects.
diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py
index 651148336f7..f7a4200e1ee 100644
--- a/Lib/test/test_capi/test_opt.py
+++ b/Lib/test/test_capi/test_opt.py
@@ -1955,9 +1955,143 @@ class TestUopsOptimization(unittest.TestCase):
self.assertEqual(res, TIER2_THRESHOLD)
self.assertIsNotNone(ex)
uops = get_opnames(ex)
- self.assertIn("_CALL_ISINSTANCE", uops)
+ self.assertNotIn("_CALL_ISINSTANCE", uops)
self.assertNotIn("_GUARD_THIRD_NULL", uops)
self.assertNotIn("_GUARD_CALLABLE_ISINSTANCE", uops)
+ self.assertIn("_POP_CALL_TWO_LOAD_CONST_INLINE_BORROW", uops)
+
+ def test_call_list_append(self):
+ def testfunc(n):
+ a = []
+ for i in range(n):
+ a.append(i)
+ return sum(a)
+
+ res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
+ self.assertEqual(res, sum(range(TIER2_THRESHOLD)))
+ uops = get_opnames(ex)
+ self.assertIn("_CALL_LIST_APPEND", uops)
+ # We should remove these in the future
+ self.assertIn("_GUARD_NOS_LIST", uops)
+ self.assertIn("_GUARD_CALLABLE_LIST_APPEND", uops)
+
+ def test_call_isinstance_is_true(self):
+ def testfunc(n):
+ x = 0
+ for _ in range(n):
+ y = isinstance(42, int)
+ if y:
+ x += 1
+ return x
+
+ res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
+ self.assertEqual(res, TIER2_THRESHOLD)
+ self.assertIsNotNone(ex)
+ uops = get_opnames(ex)
+ self.assertNotIn("_CALL_ISINSTANCE", uops)
+ self.assertNotIn("_TO_BOOL_BOOL", uops)
+ self.assertNotIn("_GUARD_IS_TRUE_POP", uops)
+ self.assertIn("_POP_CALL_TWO_LOAD_CONST_INLINE_BORROW", uops)
+
+ def test_call_isinstance_is_false(self):
+ def testfunc(n):
+ x = 0
+ for _ in range(n):
+ y = isinstance(42, str)
+ if not y:
+ x += 1
+ return x
+
+ res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
+ self.assertEqual(res, TIER2_THRESHOLD)
+ self.assertIsNotNone(ex)
+ uops = get_opnames(ex)
+ self.assertNotIn("_CALL_ISINSTANCE", uops)
+ self.assertNotIn("_TO_BOOL_BOOL", uops)
+ self.assertNotIn("_GUARD_IS_FALSE_POP", uops)
+ self.assertIn("_POP_CALL_TWO_LOAD_CONST_INLINE_BORROW", uops)
+
+ def test_call_isinstance_subclass(self):
+ def testfunc(n):
+ x = 0
+ for _ in range(n):
+ y = isinstance(True, int)
+ if y:
+ x += 1
+ return x
+
+ res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
+ self.assertEqual(res, TIER2_THRESHOLD)
+ self.assertIsNotNone(ex)
+ uops = get_opnames(ex)
+ self.assertNotIn("_CALL_ISINSTANCE", uops)
+ self.assertNotIn("_TO_BOOL_BOOL", uops)
+ self.assertNotIn("_GUARD_IS_TRUE_POP", uops)
+ self.assertIn("_POP_CALL_TWO_LOAD_CONST_INLINE_BORROW", uops)
+
+ def test_call_isinstance_unknown_object(self):
+ def testfunc(n):
+ x = 0
+ for _ in range(n):
+ # The optimizer doesn't know the return type here:
+ bar = eval("42")
+ # This will only narrow to bool:
+ y = isinstance(bar, int)
+ if y:
+ x += 1
+ return x
+
+ res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
+ self.assertEqual(res, TIER2_THRESHOLD)
+ self.assertIsNotNone(ex)
+ uops = get_opnames(ex)
+ self.assertIn("_CALL_ISINSTANCE", uops)
+ self.assertNotIn("_TO_BOOL_BOOL", uops)
+ self.assertIn("_GUARD_IS_TRUE_POP", uops)
+
+ def test_call_isinstance_tuple_of_classes(self):
+ def testfunc(n):
+ x = 0
+ for _ in range(n):
+ # A tuple of classes is currently not optimized,
+ # so this is only narrowed to bool:
+ y = isinstance(42, (int, str))
+ if y:
+ x += 1
+ return x
+
+ res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
+ self.assertEqual(res, TIER2_THRESHOLD)
+ self.assertIsNotNone(ex)
+ uops = get_opnames(ex)
+ self.assertIn("_CALL_ISINSTANCE", uops)
+ self.assertNotIn("_TO_BOOL_BOOL", uops)
+ self.assertIn("_GUARD_IS_TRUE_POP", uops)
+
+ def test_call_isinstance_metaclass(self):
+ class EvenNumberMeta(type):
+ def __instancecheck__(self, number):
+ return number % 2 == 0
+
+ class EvenNumber(metaclass=EvenNumberMeta):
+ pass
+
+ def testfunc(n):
+ x = 0
+ for _ in range(n):
+ # Only narrowed to bool
+ y = isinstance(42, EvenNumber)
+ if y:
+ x += 1
+ return x
+
+ res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
+ self.assertEqual(res, TIER2_THRESHOLD)
+ self.assertIsNotNone(ex)
+ uops = get_opnames(ex)
+ self.assertIn("_CALL_ISINSTANCE", uops)
+ self.assertNotIn("_TO_BOOL_BOOL", uops)
+ self.assertIn("_GUARD_IS_TRUE_POP", uops)
def global_identity(x):
diff --git a/Lib/test/test_curses.py b/Lib/test/test_curses.py
index c307258e565..feca82681de 100644
--- a/Lib/test/test_curses.py
+++ b/Lib/test/test_curses.py
@@ -130,6 +130,9 @@ class TestCurses(unittest.TestCase):
curses.use_env(False)
curses.use_env(True)
+ def test_error(self):
+ self.assertIsSubclass(curses.error, Exception)
+
def test_create_windows(self):
win = curses.newwin(5, 10)
self.assertEqual(win.getbegyx(), (0, 0))
diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py
index df5d2a7bedc..0af1c45ecb2 100644
--- a/Lib/test/test_httpservers.py
+++ b/Lib/test/test_httpservers.py
@@ -8,6 +8,7 @@ from http.server import BaseHTTPRequestHandler, HTTPServer, HTTPSServer, \
SimpleHTTPRequestHandler
from http import server, HTTPStatus
+import contextlib
import os
import socket
import sys
@@ -1281,6 +1282,176 @@ class ScriptTestCase(unittest.TestCase):
self.assertEqual(mock_server.address_family, socket.AF_INET)
+class CommandLineTestCase(unittest.TestCase):
+ default_port = 8000
+ default_bind = None
+ default_protocol = 'HTTP/1.0'
+ default_handler = SimpleHTTPRequestHandler
+ default_server = unittest.mock.ANY
+ tls_cert = certdata_file('ssl_cert.pem')
+ tls_key = certdata_file('ssl_key.pem')
+ tls_password = 'somepass'
+ tls_cert_options = ['--tls-cert']
+ tls_key_options = ['--tls-key']
+ tls_password_options = ['--tls-password-file']
+ args = {
+ 'HandlerClass': default_handler,
+ 'ServerClass': default_server,
+ 'protocol': default_protocol,
+ 'port': default_port,
+ 'bind': default_bind,
+ 'tls_cert': None,
+ 'tls_key': None,
+ 'tls_password': None,
+ }
+
+ def setUp(self):
+ super().setUp()
+ self.tls_password_file = tempfile.mktemp()
+ with open(self.tls_password_file, 'wb') as f:
+ f.write(self.tls_password.encode())
+ self.addCleanup(os_helper.unlink, self.tls_password_file)
+
+ def invoke_httpd(self, *args, stdout=None, stderr=None):
+ stdout = StringIO() if stdout is None else stdout
+ stderr = StringIO() if stderr is None else stderr
+ with contextlib.redirect_stdout(stdout), \
+ contextlib.redirect_stderr(stderr):
+ server._main(args)
+ return stdout.getvalue(), stderr.getvalue()
+
+ @mock.patch('http.server.test')
+ def test_port_flag(self, mock_func):
+ ports = [8000, 65535]
+ for port in ports:
+ with self.subTest(port=port):
+ self.invoke_httpd(str(port))
+ call_args = self.args | dict(port=port)
+ mock_func.assert_called_once_with(**call_args)
+ mock_func.reset_mock()
+
+ @mock.patch('http.server.test')
+ def test_directory_flag(self, mock_func):
+ options = ['-d', '--directory']
+ directories = ['.', '/foo', '\\bar', '/',
+ 'C:\\', 'C:\\foo', 'C:\\bar',
+ '/home/user', './foo/foo2', 'D:\\foo\\bar']
+ for flag in options:
+ for directory in directories:
+ with self.subTest(flag=flag, directory=directory):
+ self.invoke_httpd(flag, directory)
+ mock_func.assert_called_once_with(**self.args)
+ mock_func.reset_mock()
+
+ @mock.patch('http.server.test')
+ def test_bind_flag(self, mock_func):
+ options = ['-b', '--bind']
+ bind_addresses = ['localhost', '127.0.0.1', '::1',
+ '0.0.0.0', '8.8.8.8']
+ for flag in options:
+ for bind_address in bind_addresses:
+ with self.subTest(flag=flag, bind_address=bind_address):
+ self.invoke_httpd(flag, bind_address)
+ call_args = self.args | dict(bind=bind_address)
+ mock_func.assert_called_once_with(**call_args)
+ mock_func.reset_mock()
+
+ @mock.patch('http.server.test')
+ def test_protocol_flag(self, mock_func):
+ options = ['-p', '--protocol']
+ protocols = ['HTTP/1.0', 'HTTP/1.1', 'HTTP/2.0', 'HTTP/3.0']
+ for flag in options:
+ for protocol in protocols:
+ with self.subTest(flag=flag, protocol=protocol):
+ self.invoke_httpd(flag, protocol)
+ call_args = self.args | dict(protocol=protocol)
+ mock_func.assert_called_once_with(**call_args)
+ mock_func.reset_mock()
+
+ @unittest.skipIf(ssl is None, "requires ssl")
+ @mock.patch('http.server.test')
+ def test_tls_cert_and_key_flags(self, mock_func):
+ for tls_cert_option in self.tls_cert_options:
+ for tls_key_option in self.tls_key_options:
+ self.invoke_httpd(tls_cert_option, self.tls_cert,
+ tls_key_option, self.tls_key)
+ call_args = self.args | {
+ 'tls_cert': self.tls_cert,
+ 'tls_key': self.tls_key,
+ }
+ mock_func.assert_called_once_with(**call_args)
+ mock_func.reset_mock()
+
+ @unittest.skipIf(ssl is None, "requires ssl")
+ @mock.patch('http.server.test')
+ def test_tls_cert_and_key_and_password_flags(self, mock_func):
+ for tls_cert_option in self.tls_cert_options:
+ for tls_key_option in self.tls_key_options:
+ for tls_password_option in self.tls_password_options:
+ self.invoke_httpd(tls_cert_option,
+ self.tls_cert,
+ tls_key_option,
+ self.tls_key,
+ tls_password_option,
+ self.tls_password_file)
+ call_args = self.args | {
+ 'tls_cert': self.tls_cert,
+ 'tls_key': self.tls_key,
+ 'tls_password': self.tls_password,
+ }
+ mock_func.assert_called_once_with(**call_args)
+ mock_func.reset_mock()
+
+ @unittest.skipIf(ssl is None, "requires ssl")
+ @mock.patch('http.server.test')
+ def test_missing_tls_cert_flag(self, mock_func):
+ for tls_key_option in self.tls_key_options:
+ with self.assertRaises(SystemExit):
+ self.invoke_httpd(tls_key_option, self.tls_key)
+ mock_func.reset_mock()
+
+ for tls_password_option in self.tls_password_options:
+ with self.assertRaises(SystemExit):
+ self.invoke_httpd(tls_password_option, self.tls_password)
+ mock_func.reset_mock()
+
+ @unittest.skipIf(ssl is None, "requires ssl")
+ @mock.patch('http.server.test')
+ def test_invalid_password_file(self, mock_func):
+ non_existent_file = 'non_existent_file'
+ for tls_password_option in self.tls_password_options:
+ for tls_cert_option in self.tls_cert_options:
+ with self.assertRaises(SystemExit):
+ self.invoke_httpd(tls_cert_option,
+ self.tls_cert,
+ tls_password_option,
+ non_existent_file)
+
+ @mock.patch('http.server.test')
+ def test_no_arguments(self, mock_func):
+ self.invoke_httpd()
+ mock_func.assert_called_once_with(**self.args)
+ mock_func.reset_mock()
+
+ @mock.patch('http.server.test')
+ def test_help_flag(self, _):
+ options = ['-h', '--help']
+ for option in options:
+ stdout, stderr = StringIO(), StringIO()
+ with self.assertRaises(SystemExit):
+ self.invoke_httpd(option, stdout=stdout, stderr=stderr)
+ self.assertIn('usage', stdout.getvalue())
+ self.assertEqual(stderr.getvalue(), '')
+
+ @mock.patch('http.server.test')
+ def test_unknown_flag(self, _):
+ stdout, stderr = StringIO(), StringIO()
+ with self.assertRaises(SystemExit):
+ self.invoke_httpd('--unknown-flag', stdout=stdout, stderr=stderr)
+ self.assertEqual(stdout.getvalue(), '')
+ self.assertIn('error', stderr.getvalue())
+
+
def setUpModule():
unittest.addModuleCleanup(os.chdir, os.getcwd())
diff --git a/Lib/test/test_ntpath.py b/Lib/test/test_ntpath.py
index c10387b58e3..f83ef225a6e 100644
--- a/Lib/test/test_ntpath.py
+++ b/Lib/test/test_ntpath.py
@@ -124,6 +124,22 @@ class TestNtpath(NtpathTestCase):
tester('ntpath.splitdrive("//?/UNC/server/share/dir")',
("//?/UNC/server/share", "/dir"))
+ def test_splitdrive_invalid_paths(self):
+ splitdrive = ntpath.splitdrive
+ self.assertEqual(splitdrive('\\\\ser\x00ver\\sha\x00re\\di\x00r'),
+ ('\\\\ser\x00ver\\sha\x00re', '\\di\x00r'))
+ self.assertEqual(splitdrive(b'\\\\ser\x00ver\\sha\x00re\\di\x00r'),
+ (b'\\\\ser\x00ver\\sha\x00re', b'\\di\x00r'))
+ self.assertEqual(splitdrive("\\\\\udfff\\\udffe\\\udffd"),
+ ('\\\\\udfff\\\udffe', '\\\udffd'))
+ if sys.platform == 'win32':
+ self.assertRaises(UnicodeDecodeError, splitdrive, b'\\\\\xff\\share\\dir')
+ self.assertRaises(UnicodeDecodeError, splitdrive, b'\\\\server\\\xff\\dir')
+ self.assertRaises(UnicodeDecodeError, splitdrive, b'\\\\server\\share\\\xff')
+ else:
+ self.assertEqual(splitdrive(b'\\\\\xff\\\xfe\\\xfd'),
+ (b'\\\\\xff\\\xfe', b'\\\xfd'))
+
def test_splitroot(self):
tester("ntpath.splitroot('')", ('', '', ''))
tester("ntpath.splitroot('foo')", ('', '', 'foo'))
@@ -214,6 +230,22 @@ class TestNtpath(NtpathTestCase):
tester('ntpath.splitroot(" :/foo")', (" :", "/", "foo"))
tester('ntpath.splitroot("/:/foo")', ("", "/", ":/foo"))
+ def test_splitroot_invalid_paths(self):
+ splitroot = ntpath.splitroot
+ self.assertEqual(splitroot('\\\\ser\x00ver\\sha\x00re\\di\x00r'),
+ ('\\\\ser\x00ver\\sha\x00re', '\\', 'di\x00r'))
+ self.assertEqual(splitroot(b'\\\\ser\x00ver\\sha\x00re\\di\x00r'),
+ (b'\\\\ser\x00ver\\sha\x00re', b'\\', b'di\x00r'))
+ self.assertEqual(splitroot("\\\\\udfff\\\udffe\\\udffd"),
+ ('\\\\\udfff\\\udffe', '\\', '\udffd'))
+ if sys.platform == 'win32':
+ self.assertRaises(UnicodeDecodeError, splitroot, b'\\\\\xff\\share\\dir')
+ self.assertRaises(UnicodeDecodeError, splitroot, b'\\\\server\\\xff\\dir')
+ self.assertRaises(UnicodeDecodeError, splitroot, b'\\\\server\\share\\\xff')
+ else:
+ self.assertEqual(splitroot(b'\\\\\xff\\\xfe\\\xfd'),
+ (b'\\\\\xff\\\xfe', b'\\', b'\xfd'))
+
def test_split(self):
tester('ntpath.split("c:\\foo\\bar")', ('c:\\foo', 'bar'))
tester('ntpath.split("\\\\conky\\mountpoint\\foo\\bar")',
@@ -226,6 +258,21 @@ class TestNtpath(NtpathTestCase):
tester('ntpath.split("c:/")', ('c:/', ''))
tester('ntpath.split("//conky/mountpoint/")', ('//conky/mountpoint/', ''))
+ def test_split_invalid_paths(self):
+ split = ntpath.split
+ self.assertEqual(split('c:\\fo\x00o\\ba\x00r'),
+ ('c:\\fo\x00o', 'ba\x00r'))
+ self.assertEqual(split(b'c:\\fo\x00o\\ba\x00r'),
+ (b'c:\\fo\x00o', b'ba\x00r'))
+ self.assertEqual(split('c:\\\udfff\\\udffe'),
+ ('c:\\\udfff', '\udffe'))
+ if sys.platform == 'win32':
+ self.assertRaises(UnicodeDecodeError, split, b'c:\\\xff\\bar')
+ self.assertRaises(UnicodeDecodeError, split, b'c:\\foo\\\xff')
+ else:
+ self.assertEqual(split(b'c:\\\xff\\\xfe'),
+ (b'c:\\\xff', b'\xfe'))
+
def test_isabs(self):
tester('ntpath.isabs("foo\\bar")', 0)
tester('ntpath.isabs("foo/bar")', 0)
@@ -333,6 +380,30 @@ class TestNtpath(NtpathTestCase):
tester("ntpath.join('D:a', './c:b')", 'D:a\\.\\c:b')
tester("ntpath.join('D:/a', './c:b')", 'D:\\a\\.\\c:b')
+ def test_normcase(self):
+ normcase = ntpath.normcase
+ self.assertEqual(normcase(''), '')
+ self.assertEqual(normcase(b''), b'')
+ self.assertEqual(normcase('ABC'), 'abc')
+ self.assertEqual(normcase(b'ABC'), b'abc')
+ self.assertEqual(normcase('\xc4\u0141\u03a8'), '\xe4\u0142\u03c8')
+ expected = '\u03c9\u2126' if sys.platform == 'win32' else '\u03c9\u03c9'
+ self.assertEqual(normcase('\u03a9\u2126'), expected)
+ if sys.platform == 'win32' or sys.getfilesystemencoding() == 'utf-8':
+ self.assertEqual(normcase('\xc4\u0141\u03a8'.encode()),
+ '\xe4\u0142\u03c8'.encode())
+ self.assertEqual(normcase('\u03a9\u2126'.encode()),
+ expected.encode())
+
+ def test_normcase_invalid_paths(self):
+ normcase = ntpath.normcase
+ self.assertEqual(normcase('abc\x00def'), 'abc\x00def')
+ self.assertEqual(normcase(b'abc\x00def'), b'abc\x00def')
+ self.assertEqual(normcase('\udfff'), '\udfff')
+ if sys.platform == 'win32':
+ path = b'ABC' + bytes(range(128, 256))
+ self.assertEqual(normcase(path), path.lower())
+
def test_normpath(self):
tester("ntpath.normpath('A//////././//.//B')", r'A\B')
tester("ntpath.normpath('A/./B')", r'A\B')
@@ -381,6 +452,21 @@ class TestNtpath(NtpathTestCase):
tester("ntpath.normpath('\\\\')", '\\\\')
tester("ntpath.normpath('//?/UNC/server/share/..')", '\\\\?\\UNC\\server\\share\\')
+ def test_normpath_invalid_paths(self):
+ normpath = ntpath.normpath
+ self.assertEqual(normpath('fo\x00o'), 'fo\x00o')
+ self.assertEqual(normpath(b'fo\x00o'), b'fo\x00o')
+ self.assertEqual(normpath('fo\x00o\\..\\bar'), 'bar')
+ self.assertEqual(normpath(b'fo\x00o\\..\\bar'), b'bar')
+ self.assertEqual(normpath('\udfff'), '\udfff')
+ self.assertEqual(normpath('\udfff\\..\\foo'), 'foo')
+ if sys.platform == 'win32':
+ self.assertRaises(UnicodeDecodeError, normpath, b'\xff')
+ self.assertRaises(UnicodeDecodeError, normpath, b'\xff\\..\\foo')
+ else:
+ self.assertEqual(normpath(b'\xff'), b'\xff')
+ self.assertEqual(normpath(b'\xff\\..\\foo'), b'foo')
+
def test_realpath_curdir(self):
expected = ntpath.normpath(os.getcwd())
tester("ntpath.realpath('.')", expected)
@@ -420,10 +506,6 @@ class TestNtpath(NtpathTestCase):
d = drives.pop().encode()
self.assertEqual(ntpath.realpath(d), d)
- # gh-106242: Embedded nulls and non-strict fallback to abspath
- self.assertEqual(ABSTFN + "\0spam",
- ntpath.realpath(os_helper.TESTFN + "\0spam", strict=False))
-
@os_helper.skip_unless_symlink
@unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname')
def test_realpath_strict(self):
@@ -434,8 +516,51 @@ class TestNtpath(NtpathTestCase):
self.addCleanup(os_helper.unlink, ABSTFN)
self.assertRaises(FileNotFoundError, ntpath.realpath, ABSTFN, strict=True)
self.assertRaises(FileNotFoundError, ntpath.realpath, ABSTFN + "2", strict=True)
+
+ @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname')
+ def test_realpath_invalid_paths(self):
+ realpath = ntpath.realpath
+ ABSTFN = ntpath.abspath(os_helper.TESTFN)
+ ABSTFNb = os.fsencode(ABSTFN)
+ path = ABSTFN + '\x00'
+ # gh-106242: Embedded nulls and non-strict fallback to abspath
+ self.assertEqual(realpath(path, strict=False), path)
# gh-106242: Embedded nulls should raise OSError (not ValueError)
- self.assertRaises(OSError, ntpath.realpath, ABSTFN + "\0spam", strict=True)
+ self.assertRaises(OSError, realpath, path, strict=True)
+ path = ABSTFNb + b'\x00'
+ self.assertEqual(realpath(path, strict=False), path)
+ self.assertRaises(OSError, realpath, path, strict=True)
+ path = ABSTFN + '\\nonexistent\\x\x00'
+ self.assertEqual(realpath(path, strict=False), path)
+ self.assertRaises(OSError, realpath, path, strict=True)
+ path = ABSTFNb + b'\\nonexistent\\x\x00'
+ self.assertEqual(realpath(path, strict=False), path)
+ self.assertRaises(OSError, realpath, path, strict=True)
+ path = ABSTFN + '\x00\\..'
+ self.assertEqual(realpath(path, strict=False), os.getcwd())
+ self.assertEqual(realpath(path, strict=True), os.getcwd())
+ path = ABSTFNb + b'\x00\\..'
+ self.assertEqual(realpath(path, strict=False), os.getcwdb())
+ self.assertEqual(realpath(path, strict=True), os.getcwdb())
+ path = ABSTFN + '\\nonexistent\\x\x00\\..'
+ self.assertEqual(realpath(path, strict=False), ABSTFN + '\\nonexistent')
+ self.assertRaises(OSError, realpath, path, strict=True)
+ path = ABSTFNb + b'\\nonexistent\\x\x00\\..'
+ self.assertEqual(realpath(path, strict=False), ABSTFNb + b'\\nonexistent')
+ self.assertRaises(OSError, realpath, path, strict=True)
+
+ path = ABSTFNb + b'\xff'
+ self.assertRaises(UnicodeDecodeError, realpath, path, strict=False)
+ self.assertRaises(UnicodeDecodeError, realpath, path, strict=True)
+ path = ABSTFNb + b'\\nonexistent\\\xff'
+ self.assertRaises(UnicodeDecodeError, realpath, path, strict=False)
+ self.assertRaises(UnicodeDecodeError, realpath, path, strict=True)
+ path = ABSTFNb + b'\xff\\..'
+ self.assertRaises(UnicodeDecodeError, realpath, path, strict=False)
+ self.assertRaises(UnicodeDecodeError, realpath, path, strict=True)
+ path = ABSTFNb + b'\\nonexistent\\\xff\\..'
+ self.assertRaises(UnicodeDecodeError, realpath, path, strict=False)
+ self.assertRaises(UnicodeDecodeError, realpath, path, strict=True)
@os_helper.skip_unless_symlink
@unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname')
@@ -812,8 +937,6 @@ class TestNtpath(NtpathTestCase):
tester('ntpath.abspath("C:/nul")', "\\\\.\\nul")
tester('ntpath.abspath("C:\\nul")', "\\\\.\\nul")
self.assertTrue(ntpath.isabs(ntpath.abspath("C:spam")))
- self.assertEqual(ntpath.abspath("C:\x00"), ntpath.join(ntpath.abspath("C:"), "\x00"))
- self.assertEqual(ntpath.abspath("\x00:spam"), "\x00:\\spam")
tester('ntpath.abspath("//..")', "\\\\")
tester('ntpath.abspath("//../")', "\\\\..\\")
tester('ntpath.abspath("//../..")', "\\\\..\\")
@@ -847,6 +970,26 @@ class TestNtpath(NtpathTestCase):
drive, _ = ntpath.splitdrive(cwd_dir)
tester('ntpath.abspath("/abc/")', drive + "\\abc")
+ def test_abspath_invalid_paths(self):
+ abspath = ntpath.abspath
+ if sys.platform == 'win32':
+ self.assertEqual(abspath("C:\x00"), ntpath.join(abspath("C:"), "\x00"))
+ self.assertEqual(abspath(b"C:\x00"), ntpath.join(abspath(b"C:"), b"\x00"))
+ self.assertEqual(abspath("\x00:spam"), "\x00:\\spam")
+ self.assertEqual(abspath(b"\x00:spam"), b"\x00:\\spam")
+ self.assertEqual(abspath('c:\\fo\x00o'), 'c:\\fo\x00o')
+ self.assertEqual(abspath(b'c:\\fo\x00o'), b'c:\\fo\x00o')
+ self.assertEqual(abspath('c:\\fo\x00o\\..\\bar'), 'c:\\bar')
+ self.assertEqual(abspath(b'c:\\fo\x00o\\..\\bar'), b'c:\\bar')
+ self.assertEqual(abspath('c:\\\udfff'), 'c:\\\udfff')
+ self.assertEqual(abspath('c:\\\udfff\\..\\foo'), 'c:\\foo')
+ if sys.platform == 'win32':
+ self.assertRaises(UnicodeDecodeError, abspath, b'c:\\\xff')
+ self.assertRaises(UnicodeDecodeError, abspath, b'c:\\\xff\\..\\foo')
+ else:
+ self.assertEqual(abspath(b'c:\\\xff'), b'c:\\\xff')
+ self.assertEqual(abspath(b'c:\\\xff\\..\\foo'), b'c:\\foo')
+
def test_relpath(self):
tester('ntpath.relpath("a")', 'a')
tester('ntpath.relpath(ntpath.abspath("a"))', 'a')
@@ -989,6 +1132,18 @@ class TestNtpath(NtpathTestCase):
self.assertTrue(ntpath.ismount(b"\\\\localhost\\c$"))
self.assertTrue(ntpath.ismount(b"\\\\localhost\\c$\\"))
+ def test_ismount_invalid_paths(self):
+ ismount = ntpath.ismount
+ self.assertFalse(ismount("c:\\\udfff"))
+ if sys.platform == 'win32':
+ self.assertRaises(ValueError, ismount, "c:\\\x00")
+ self.assertRaises(ValueError, ismount, b"c:\\\x00")
+ self.assertRaises(UnicodeDecodeError, ismount, b"c:\\\xff")
+ else:
+ self.assertFalse(ismount("c:\\\x00"))
+ self.assertFalse(ismount(b"c:\\\x00"))
+ self.assertFalse(ismount(b"c:\\\xff"))
+
def test_isreserved(self):
self.assertFalse(ntpath.isreserved(''))
self.assertFalse(ntpath.isreserved('.'))
@@ -1095,6 +1250,13 @@ class TestNtpath(NtpathTestCase):
self.assertFalse(ntpath.isjunction('tmpdir'))
self.assertPathEqual(ntpath.realpath('testjunc'), ntpath.realpath('tmpdir'))
+ def test_isfile_invalid_paths(self):
+ isfile = ntpath.isfile
+ self.assertIs(isfile('/tmp\udfffabcds'), False)
+ self.assertIs(isfile(b'/tmp\xffabcds'), False)
+ self.assertIs(isfile('/tmp\x00abcds'), False)
+ self.assertIs(isfile(b'/tmp\x00abcds'), False)
+
@unittest.skipIf(sys.platform != 'win32', "drive letters are a windows concept")
def test_isfile_driveletter(self):
drive = os.environ.get('SystemDrive')
@@ -1195,9 +1357,6 @@ class PathLikeTests(NtpathTestCase):
def test_path_normcase(self):
self._check_function(self.path.normcase)
- if sys.platform == 'win32':
- self.assertEqual(ntpath.normcase('\u03a9\u2126'), 'ωΩ')
- self.assertEqual(ntpath.normcase('abc\x00def'), 'abc\x00def')
def test_path_isabs(self):
self._check_function(self.path.isabs)
diff --git a/Lib/test/test_posixpath.py b/Lib/test/test_posixpath.py
index fa19d549c26..c70688ff5e9 100644
--- a/Lib/test/test_posixpath.py
+++ b/Lib/test/test_posixpath.py
@@ -229,6 +229,7 @@ class PosixPathTest(unittest.TestCase):
finally:
safe_rmdir(ABSTFN)
+ def test_ismount_invalid_paths(self):
self.assertIs(posixpath.ismount('/\udfff'), False)
self.assertIs(posixpath.ismount(b'/\xff'), False)
self.assertIs(posixpath.ismount('/\x00'), False)
@@ -489,6 +490,79 @@ class PosixPathTest(unittest.TestCase):
finally:
os_helper.unlink(ABSTFN)
+ def test_realpath_invalid_paths(self):
+ path = '/\x00'
+ self.assertRaises(ValueError, realpath, path, strict=False)
+ self.assertRaises(ValueError, realpath, path, strict=True)
+ path = b'/\x00'
+ self.assertRaises(ValueError, realpath, path, strict=False)
+ self.assertRaises(ValueError, realpath, path, strict=True)
+ path = '/nonexistent/x\x00'
+ self.assertRaises(ValueError, realpath, path, strict=False)
+ self.assertRaises(FileNotFoundError, realpath, path, strict=True)
+ path = b'/nonexistent/x\x00'
+ self.assertRaises(ValueError, realpath, path, strict=False)
+ self.assertRaises(FileNotFoundError, realpath, path, strict=True)
+ path = '/\x00/..'
+ self.assertRaises(ValueError, realpath, path, strict=False)
+ self.assertRaises(ValueError, realpath, path, strict=True)
+ path = b'/\x00/..'
+ self.assertRaises(ValueError, realpath, path, strict=False)
+ self.assertRaises(ValueError, realpath, path, strict=True)
+ path = '/nonexistent/x\x00/..'
+ self.assertRaises(ValueError, realpath, path, strict=False)
+ self.assertRaises(FileNotFoundError, realpath, path, strict=True)
+ path = b'/nonexistent/x\x00/..'
+ self.assertRaises(ValueError, realpath, path, strict=False)
+ self.assertRaises(FileNotFoundError, realpath, path, strict=True)
+
+ path = '/\udfff'
+ if sys.platform == 'win32':
+ self.assertEqual(realpath(path, strict=False), path)
+ self.assertRaises(FileNotFoundError, realpath, path, strict=True)
+ else:
+ self.assertRaises(UnicodeEncodeError, realpath, path, strict=False)
+ self.assertRaises(UnicodeEncodeError, realpath, path, strict=True)
+ path = '/nonexistent/\udfff'
+ if sys.platform == 'win32':
+ self.assertEqual(realpath(path, strict=False), path)
+ else:
+ self.assertRaises(UnicodeEncodeError, realpath, path, strict=False)
+ self.assertRaises(FileNotFoundError, realpath, path, strict=True)
+ path = '/\udfff/..'
+ if sys.platform == 'win32':
+ self.assertEqual(realpath(path, strict=False), '/')
+ self.assertRaises(FileNotFoundError, realpath, path, strict=True)
+ else:
+ self.assertRaises(UnicodeEncodeError, realpath, path, strict=False)
+ self.assertRaises(UnicodeEncodeError, realpath, path, strict=True)
+ path = '/nonexistent/\udfff/..'
+ if sys.platform == 'win32':
+ self.assertEqual(realpath(path, strict=False), '/nonexistent')
+ else:
+ self.assertRaises(UnicodeEncodeError, realpath, path, strict=False)
+ self.assertRaises(FileNotFoundError, realpath, path, strict=True)
+
+ path = b'/\xff'
+ if sys.platform == 'win32':
+ self.assertRaises(UnicodeDecodeError, realpath, path, strict=False)
+ self.assertRaises(UnicodeDecodeError, realpath, path, strict=True)
+ else:
+ self.assertEqual(realpath(path, strict=False), path)
+ if support.is_wasi:
+ self.assertRaises(OSError, realpath, path, strict=True)
+ else:
+ self.assertRaises(FileNotFoundError, realpath, path, strict=True)
+ path = b'/nonexistent/\xff'
+ if sys.platform == 'win32':
+ self.assertRaises(UnicodeDecodeError, realpath, path, strict=False)
+ else:
+ self.assertEqual(realpath(path, strict=False), path)
+ if support.is_wasi:
+ self.assertRaises(OSError, realpath, path, strict=True)
+ else:
+ self.assertRaises(FileNotFoundError, realpath, path, strict=True)
+
@os_helper.skip_unless_symlink
@skip_if_ABSTFN_contains_backslash
def test_realpath_relative(self):
diff --git a/Lib/test/test_pyrepl/test_pyrepl.py b/Lib/test/test_pyrepl/test_pyrepl.py
index fc8114891d1..59f5d1f893f 100644
--- a/Lib/test/test_pyrepl/test_pyrepl.py
+++ b/Lib/test/test_pyrepl/test_pyrepl.py
@@ -10,7 +10,7 @@ import sys
import tempfile
from unittest import TestCase, skipUnless, skipIf
from unittest.mock import patch
-from test.support import force_not_colorized, make_clean_env
+from test.support import force_not_colorized, make_clean_env, Py_DEBUG
from test.support import SHORT_TIMEOUT, STDLIB_DIR
from test.support.import_helper import import_module
from test.support.os_helper import EnvironmentVarGuard, unlink
@@ -959,6 +959,26 @@ class TestPyReplModuleCompleter(TestCase):
output = reader.readline()
self.assertEqual(output, expected)
+ def test_builtin_completion_top_level(self):
+ import importlib
+ # Make iter_modules() search only the standard library.
+ # This makes the test more reliable in case there are
+ # other user packages/scripts on PYTHONPATH which can
+ # intefere with the completions.
+ lib_path = os.path.dirname(importlib.__path__[0])
+ sys.path = [lib_path]
+
+ cases = (
+ ("import bui\t\n", "import builtins"),
+ ("from bui\t\n", "from builtins"),
+ )
+ for code, expected in cases:
+ with self.subTest(code=code):
+ events = code_to_events(code)
+ reader = self.prepare_reader(events, namespace={})
+ output = reader.readline()
+ self.assertEqual(output, expected)
+
def test_relative_import_completions(self):
cases = (
("from .readl\t\n", "from .readline"),
@@ -1055,11 +1075,15 @@ class TestPyReplModuleCompleter(TestCase):
self.assertEqual(actual, parsed)
# The parser should not get tripped up by any
# other preceding statements
- code = f'import xyz\n{code}'
- with self.subTest(code=code):
+ _code = f'import xyz\n{code}'
+ parser = ImportParser(_code)
+ actual = parser.parse()
+ with self.subTest(code=_code):
self.assertEqual(actual, parsed)
- code = f'import xyz;{code}'
- with self.subTest(code=code):
+ _code = f'import xyz;{code}'
+ parser = ImportParser(_code)
+ actual = parser.parse()
+ with self.subTest(code=_code):
self.assertEqual(actual, parsed)
def test_parse_error(self):
@@ -1610,3 +1634,16 @@ class TestMain(ReplTestCase):
# Extra stuff (newline and `exit` rewrites) are necessary
# because of how run_repl works.
self.assertNotIn(">>> \n>>> >>>", cleaned_output)
+
+ @skipUnless(Py_DEBUG, '-X showrefcount requires a Python debug build')
+ def test_showrefcount(self):
+ env = os.environ.copy()
+ env.pop("PYTHON_BASIC_REPL", "")
+ output, _ = self.run_repl("1\n1+2\nexit()\n", cmdline_args=['-Xshowrefcount'], env=env)
+ matches = re.findall(r'\[-?\d+ refs, \d+ blocks\]', output)
+ self.assertEqual(len(matches), 3)
+
+ env["PYTHON_BASIC_REPL"] = "1"
+ output, _ = self.run_repl("1\n1+2\nexit()\n", cmdline_args=['-Xshowrefcount'], env=env)
+ matches = re.findall(r'\[-?\d+ refs, \d+ blocks\]', output)
+ self.assertEqual(len(matches), 3)
diff --git a/Lib/test/test_pyrepl/test_reader.py b/Lib/test/test_pyrepl/test_reader.py
index 57526f88f93..1f655264f1c 100644
--- a/Lib/test/test_pyrepl/test_reader.py
+++ b/Lib/test/test_pyrepl/test_reader.py
@@ -517,6 +517,37 @@ class TestReaderInColor(ScreenEqualMixin, TestCase):
self.assert_screen_equal(reader, code, clean=True)
self.assert_screen_equal(reader, expected)
+ def test_syntax_highlighting_literal_brace_in_fstring_or_tstring(self):
+ code = dedent(
+ """\
+ f"{{"
+ f"}}"
+ f"a{{b"
+ f"a}}b"
+ f"a{{b}}c"
+ t"a{{b}}c"
+ f"{{{0}}}"
+ f"{ {0} }"
+ """
+ )
+ expected = dedent(
+ """\
+ {s}f"{z}{s}<<{z}{s}"{z}
+ {s}f"{z}{s}>>{z}{s}"{z}
+ {s}f"{z}{s}a<<{z}{s}b{z}{s}"{z}
+ {s}f"{z}{s}a>>{z}{s}b{z}{s}"{z}
+ {s}f"{z}{s}a<<{z}{s}b>>{z}{s}c{z}{s}"{z}
+ {s}t"{z}{s}a<<{z}{s}b>>{z}{s}c{z}{s}"{z}
+ {s}f"{z}{s}<<{z}{o}<{z}{n}0{z}{o}>{z}{s}>>{z}{s}"{z}
+ {s}f"{z}{o}<{z} {o}<{z}{n}0{z}{o}>{z} {o}>{z}{s}"{z}
+ """
+ ).format(**colors).replace("<", "{").replace(">", "}")
+ events = code_to_events(code)
+ reader, _ = handle_all_events(events)
+ self.assert_screen_equal(reader, code, clean=True)
+ self.maxDiff=None
+ self.assert_screen_equal(reader, expected)
+
def test_control_characters(self):
code = 'flag = "🏳️‍🌈"'
events = code_to_events(code)
diff --git a/Lib/test/test_sysconfig.py b/Lib/test/test_sysconfig.py
index 53e55383bf9..963cf753ce6 100644
--- a/Lib/test/test_sysconfig.py
+++ b/Lib/test/test_sysconfig.py
@@ -531,13 +531,10 @@ class TestSysConfig(unittest.TestCase, VirtualEnvironmentMixin):
Python_h = os.path.join(srcdir, 'Include', 'Python.h')
self.assertTrue(os.path.exists(Python_h), Python_h)
# <srcdir>/PC/pyconfig.h.in always exists even if unused
- pyconfig_h = os.path.join(srcdir, 'PC', 'pyconfig.h.in')
- self.assertTrue(os.path.exists(pyconfig_h), pyconfig_h)
pyconfig_h_in = os.path.join(srcdir, 'pyconfig.h.in')
self.assertTrue(os.path.exists(pyconfig_h_in), pyconfig_h_in)
if os.name == 'nt':
- # <executable dir>/pyconfig.h exists on Windows in a build tree
- pyconfig_h = os.path.join(sys.executable, '..', 'pyconfig.h')
+ pyconfig_h = os.path.join(srcdir, 'PC', 'pyconfig.h')
self.assertTrue(os.path.exists(pyconfig_h), pyconfig_h)
elif os.name == 'posix':
makefile_dir = os.path.dirname(sysconfig.get_makefile_filename())
diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py
index 2d9649237a9..2018a20afd1 100644
--- a/Lib/test/test_tarfile.py
+++ b/Lib/test/test_tarfile.py
@@ -3490,11 +3490,12 @@ class ArchiveMaker:
with t.open() as tar:
... # `tar` is now a TarFile with 'filename' in it!
"""
- def __init__(self):
+ def __init__(self, **kwargs):
self.bio = io.BytesIO()
+ self.tar_kwargs = dict(kwargs)
def __enter__(self):
- self.tar_w = tarfile.TarFile(mode='w', fileobj=self.bio)
+ self.tar_w = tarfile.TarFile(mode='w', fileobj=self.bio, **self.tar_kwargs)
return self
def __exit__(self, *exc):
@@ -4073,7 +4074,10 @@ class TestExtractionFilters(unittest.TestCase):
# that in the test archive.)
with tarfile.TarFile.open(tarname) as tar:
for tarinfo in tar.getmembers():
- filtered = tarfile.tar_filter(tarinfo, '')
+ try:
+ filtered = tarfile.tar_filter(tarinfo, '')
+ except UnicodeEncodeError:
+ continue
self.assertIs(filtered.name, tarinfo.name)
self.assertIs(filtered.type, tarinfo.type)
@@ -4084,11 +4088,48 @@ class TestExtractionFilters(unittest.TestCase):
for tarinfo in tar.getmembers():
try:
filtered = tarfile.data_filter(tarinfo, '')
- except tarfile.FilterError:
+ except (tarfile.FilterError, UnicodeEncodeError):
continue
self.assertIs(filtered.name, tarinfo.name)
self.assertIs(filtered.type, tarinfo.type)
+ @unittest.skipIf(sys.platform == 'win32', 'requires native bytes paths')
+ def test_filter_unencodable(self):
+ # Sanity check using a valid path.
+ tarinfo = tarfile.TarInfo(os_helper.TESTFN)
+ filtered = tarfile.tar_filter(tarinfo, '')
+ self.assertIs(filtered.name, tarinfo.name)
+ filtered = tarfile.data_filter(tarinfo, '')
+ self.assertIs(filtered.name, tarinfo.name)
+
+ tarinfo = tarfile.TarInfo('test\x00')
+ self.assertRaises(ValueError, tarfile.tar_filter, tarinfo, '')
+ self.assertRaises(ValueError, tarfile.data_filter, tarinfo, '')
+ tarinfo = tarfile.TarInfo('\ud800')
+ self.assertRaises(UnicodeEncodeError, tarfile.tar_filter, tarinfo, '')
+ self.assertRaises(UnicodeEncodeError, tarfile.data_filter, tarinfo, '')
+
+ @unittest.skipIf(sys.platform == 'win32', 'requires native bytes paths')
+ def test_extract_unencodable(self):
+ # Create a member with name \xed\xa0\x80 which is UTF-8 encoded
+ # lone surrogate \ud800.
+ with ArchiveMaker(encoding='ascii', errors='surrogateescape') as arc:
+ arc.add('\udced\udca0\udc80')
+ with os_helper.temp_cwd() as tmp:
+ tar = arc.open(encoding='utf-8', errors='surrogatepass',
+ errorlevel=1)
+ self.assertEqual(tar.getnames(), ['\ud800'])
+ with self.assertRaises(UnicodeEncodeError):
+ tar.extractall()
+ self.assertEqual(os.listdir(), [])
+
+ tar = arc.open(encoding='utf-8', errors='surrogatepass',
+ errorlevel=0, debug=1)
+ with support.captured_stderr() as stderr:
+ tar.extractall()
+ self.assertEqual(os.listdir(), [])
+ self.assertIn('tarfile: UnicodeEncodeError ', stderr.getvalue())
+
def test_change_default_filter_on_instance(self):
tar = tarfile.TarFile(tarname, 'r')
def strict_filter(tarinfo, path):
diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py
index abe63c10c0a..b6e2d419019 100644
--- a/Lib/test/test_threading.py
+++ b/Lib/test/test_threading.py
@@ -2137,8 +2137,7 @@ class CRLockTests(lock_tests.RLockTests):
]
for args, kwargs in arg_types:
with self.subTest(args=args, kwargs=kwargs):
- with self.assertWarns(DeprecationWarning):
- threading.RLock(*args, **kwargs)
+ self.assertRaises(TypeError, threading.RLock, *args, **kwargs)
# Subtypes with custom `__init__` are allowed (but, not recommended):
class CustomRLock(self.locktype):
@@ -2156,6 +2155,9 @@ class ConditionAsRLockTests(lock_tests.RLockTests):
# Condition uses an RLock by default and exports its API.
locktype = staticmethod(threading.Condition)
+ def test_constructor_noargs(self):
+ self.skipTest("Condition allows positional arguments")
+
def test_recursion_count(self):
self.skipTest("Condition does not expose _recursion_count()")
diff --git a/Lib/threading.py b/Lib/threading.py
index 39a1a7f4cdf..9feada3b8bb 100644
--- a/Lib/threading.py
+++ b/Lib/threading.py
@@ -123,7 +123,7 @@ def gettrace():
Lock = _LockType
-def RLock(*args, **kwargs):
+def RLock():
"""Factory function that returns a new reentrant lock.
A reentrant lock must be released by the thread that acquired it. Once a
@@ -132,16 +132,9 @@ def RLock(*args, **kwargs):
acquired it.
"""
- if args or kwargs:
- import warnings
- warnings.warn(
- 'Passing arguments to RLock is deprecated and will be removed in 3.15',
- DeprecationWarning,
- stacklevel=2,
- )
if _CRLock is None:
- return _PyRLock(*args, **kwargs)
- return _CRLock(*args, **kwargs)
+ return _PyRLock()
+ return _CRLock()
class _RLock:
"""This class implements reentrant lock objects.
diff --git a/Misc/ACKS b/Misc/ACKS
index 610dcf9f423..1b500870dec 100644
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -795,6 +795,7 @@ Albert Hofkamp
Chris Hogan
Tomas Hoger
Jonathan Hogg
+John Keith Hohm
Vladyslav Hoi
Kamilla Holanda
Steve Holden
@@ -1288,6 +1289,7 @@ Paul Moore
Ross Moore
Ben Morgan
Emily Morehouse
+Semyon Moroz
Derek Morr
James A Morrison
Martin Morrison
diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-04-26-17-50-01.gh-issue-131798.XiOgw5.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-04-26-17-50-01.gh-issue-131798.XiOgw5.rst
new file mode 100644
index 00000000000..45ab1bea6b1
--- /dev/null
+++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-04-26-17-50-01.gh-issue-131798.XiOgw5.rst
@@ -0,0 +1,2 @@
+Narrow the return type and constant-evaluate ``CALL_ISINSTANCE`` for a
+subset of known values in the JIT. Patch by Tomas Roun
diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-17-20-44-51.gh-issue-134158.ewLNLp.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-17-20-44-51.gh-issue-134158.ewLNLp.rst
new file mode 100644
index 00000000000..7b8bab739c3
--- /dev/null
+++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-17-20-44-51.gh-issue-134158.ewLNLp.rst
@@ -0,0 +1 @@
+Fix coloring of double braces in f-strings and t-strings in the :term:`REPL`.
diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-19-15-15-58.gh-issue-131798.PCP71j.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-19-15-15-58.gh-issue-131798.PCP71j.rst
new file mode 100644
index 00000000000..c816a0afad4
--- /dev/null
+++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-19-15-15-58.gh-issue-131798.PCP71j.rst
@@ -0,0 +1 @@
+Split ``CALL_LIST_APPEND`` into several uops. Patch by Diego Russo.
diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-19-20-52-53.gh-issue-134268.HPKX1e.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-19-20-52-53.gh-issue-134268.HPKX1e.rst
new file mode 100644
index 00000000000..98d770cf054
--- /dev/null
+++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-19-20-52-53.gh-issue-134268.HPKX1e.rst
@@ -0,0 +1,2 @@
+Add ``_POP_CALL_TWO_LOAD_CONST_INLINE_BORROW`` and use it to further
+optimize ``CALL_ISINSTANCE``.
diff --git a/Misc/NEWS.d/next/Library/2024-10-22-16-21-55.gh-issue-125843.2ttzYo.rst b/Misc/NEWS.d/next/Library/2024-10-22-16-21-55.gh-issue-125843.2ttzYo.rst
new file mode 100644
index 00000000000..ec8f3a75006
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-10-22-16-21-55.gh-issue-125843.2ttzYo.rst
@@ -0,0 +1,2 @@
+If possible, indicate which :mod:`curses` C function or macro is responsible
+for raising a :exc:`curses.error` exception. Patch by Bénédikt Tran.
diff --git a/Misc/NEWS.d/next/Library/2024-11-25-10-22-08.gh-issue-126883.MAEF7g.rst b/Misc/NEWS.d/next/Library/2024-11-25-10-22-08.gh-issue-126883.MAEF7g.rst
new file mode 100644
index 00000000000..5e3fa39acf1
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-11-25-10-22-08.gh-issue-126883.MAEF7g.rst
@@ -0,0 +1,3 @@
+Add check that timezone fields are in range for
+:meth:`datetime.datetime.fromisoformat` and
+:meth:`datetime.time.fromisoformat`. Patch by Semyon Moroz.
diff --git a/Misc/NEWS.d/next/Library/2025-05-17-13-46-20.gh-issue-134097.fgkjE1.rst b/Misc/NEWS.d/next/Library/2025-05-17-13-46-20.gh-issue-134097.fgkjE1.rst
new file mode 100644
index 00000000000..0b388d9db38
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-05-17-13-46-20.gh-issue-134097.fgkjE1.rst
@@ -0,0 +1 @@
+Fix interaction of the new :term:`REPL` and :option:`-X showrefcount <-X>` command line option.
diff --git a/Misc/NEWS.d/next/Library/2025-05-17-18-08-35.gh-issue-133890.onn9_X.rst b/Misc/NEWS.d/next/Library/2025-05-17-18-08-35.gh-issue-133890.onn9_X.rst
new file mode 100644
index 00000000000..44565a5424e
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-05-17-18-08-35.gh-issue-133890.onn9_X.rst
@@ -0,0 +1,2 @@
+The :mod:`tarfile` module now handles :exc:`UnicodeEncodeError` in the same
+way as :exc:`OSError` when cannot extract a member.
diff --git a/Misc/NEWS.d/next/Library/2025-05-18-12-23-07.gh-issue-134087.HilZWl.rst b/Misc/NEWS.d/next/Library/2025-05-18-12-23-07.gh-issue-134087.HilZWl.rst
new file mode 100644
index 00000000000..c4a05965f73
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-05-18-12-23-07.gh-issue-134087.HilZWl.rst
@@ -0,0 +1,3 @@
+Remove support for arbitrary positional or keyword arguments in the C
+implementation of :class:`threading.RLock` objects. This was deprecated
+since Python 3.14. Patch by Bénédikt Tran.
diff --git a/Misc/NEWS.d/next/Library/2025-05-19-15-05-24.gh-issue-134235.pz9PwV.rst b/Misc/NEWS.d/next/Library/2025-05-19-15-05-24.gh-issue-134235.pz9PwV.rst
new file mode 100644
index 00000000000..a65df886919
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-05-19-15-05-24.gh-issue-134235.pz9PwV.rst
@@ -0,0 +1,2 @@
+Updated tab completion on REPL to include builtin modules. Contributed by
+Tom Wang, Hunter Young
diff --git a/Misc/NEWS.d/next/Library/2025-05-19-15-30-00.gh-issue-132983.asdsfs.rst b/Misc/NEWS.d/next/Library/2025-05-19-15-30-00.gh-issue-132983.asdsfs.rst
new file mode 100644
index 00000000000..3893eeafa9c
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-05-19-15-30-00.gh-issue-132983.asdsfs.rst
@@ -0,0 +1 @@
+Add :mod:`!compression.zstd` version information to ``test.pythoninfo``.
diff --git a/Misc/NEWS.d/next/Library/2025-05-19-17-27-21.gh-issue-80184.LOkbaw.rst b/Misc/NEWS.d/next/Library/2025-05-19-17-27-21.gh-issue-80184.LOkbaw.rst
new file mode 100644
index 00000000000..089268dc4c3
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-05-19-17-27-21.gh-issue-80184.LOkbaw.rst
@@ -0,0 +1 @@
+The default queue size is now ``socket.SOMAXCONN`` for :class:`socketserver.TCPServer`.
diff --git a/Misc/NEWS.d/next/Library/2025-05-19-18-12-42.gh-issue-88994.7avvVu.rst b/Misc/NEWS.d/next/Library/2025-05-19-18-12-42.gh-issue-88994.7avvVu.rst
new file mode 100644
index 00000000000..554a0c3bcb2
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-05-19-18-12-42.gh-issue-88994.7avvVu.rst
@@ -0,0 +1,3 @@
+Change :func:`datetime.datetime.now` to half-even rounding for
+consistency with :func:`datetime.datetime.fromtimestamp`. Patch by
+John Keith Hohm.
diff --git a/Misc/NEWS.d/next/Windows/2025-05-13-13-25-27.gh-issue-133779.-YcTBz.rst b/Misc/NEWS.d/next/Windows/2025-05-13-13-25-27.gh-issue-133779.-YcTBz.rst
new file mode 100644
index 00000000000..550600d5eeb
--- /dev/null
+++ b/Misc/NEWS.d/next/Windows/2025-05-13-13-25-27.gh-issue-133779.-YcTBz.rst
@@ -0,0 +1,6 @@
+Reverts the change to generate different :file:`pyconfig.h` files based on
+compiler settings, as it was frequently causing extension builds to break.
+In particular, the ``Py_GIL_DISABLED`` preprocessor variable must now always
+be defined explicitly when compiling for the experimental free-threaded
+runtime. The :func:`sysconfig.get_config_var` function can be used to
+determine whether the current runtime was compiled with that flag or not.
diff --git a/Misc/NEWS.d/next/Windows/2025-05-19-03-02-04.gh-issue-76023.vHOf6M.rst b/Misc/NEWS.d/next/Windows/2025-05-19-03-02-04.gh-issue-76023.vHOf6M.rst
new file mode 100644
index 00000000000..958f4f4a440
--- /dev/null
+++ b/Misc/NEWS.d/next/Windows/2025-05-19-03-02-04.gh-issue-76023.vHOf6M.rst
@@ -0,0 +1 @@
+Make :func:`os.path.realpath` ignore Windows error 1005 when in non-strict mode.
diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c
index cd185bc2b02..ab63fdbe45d 100644
--- a/Modules/_cursesmodule.c
+++ b/Modules/_cursesmodule.c
@@ -211,18 +211,116 @@ static int curses_start_color_called = FALSE;
static const char *curses_screen_encoding = NULL;
+/* Utility Error Procedures */
+
+static void
+_curses_format_error(cursesmodule_state *state,
+ const char *curses_funcname,
+ const char *python_funcname,
+ const char *return_value,
+ const char *default_message)
+{
+ assert(!PyErr_Occurred());
+ if (python_funcname == NULL && curses_funcname == NULL) {
+ PyErr_SetString(state->error, default_message);
+ }
+ else if (python_funcname == NULL) {
+ (void)PyErr_Format(state->error, CURSES_ERROR_FORMAT,
+ curses_funcname, return_value);
+ }
+ else {
+ assert(python_funcname != NULL);
+ (void)PyErr_Format(state->error, CURSES_ERROR_VERBOSE_FORMAT,
+ curses_funcname, python_funcname, return_value);
+ }
+}
+
+/*
+ * Format a curses error for a function that returned ERR.
+ *
+ * Specify a non-NULL 'python_funcname' when the latter differs from
+ * 'curses_funcname'. If both names are NULL, uses the 'catchall_ERR'
+ * message instead.
+ */
+static void
+_curses_set_error(cursesmodule_state *state,
+ const char *curses_funcname,
+ const char *python_funcname)
+{
+ _curses_format_error(state, curses_funcname, python_funcname,
+ "ERR", catchall_ERR);
+}
+
+/*
+ * Format a curses error for a function that returned NULL.
+ *
+ * Specify a non-NULL 'python_funcname' when the latter differs from
+ * 'curses_funcname'. If both names are NULL, uses the 'catchall_NULL'
+ * message instead.
+ */
+static inline void
+_curses_set_null_error(cursesmodule_state *state,
+ const char *curses_funcname,
+ const char *python_funcname)
+{
+ _curses_format_error(state, curses_funcname, python_funcname,
+ "NULL", catchall_NULL);
+}
+
+/* Same as _curses_set_error() for a module object. */
+static void
+curses_set_error(PyObject *module,
+ const char *curses_funcname,
+ const char *python_funcname)
+{
+ cursesmodule_state *state = get_cursesmodule_state(module);
+ _curses_set_error(state, curses_funcname, python_funcname);
+}
+
+/* Same as _curses_set_null_error() for a module object. */
+static void
+curses_set_null_error(PyObject *module,
+ const char *curses_funcname,
+ const char *python_funcname)
+{
+ cursesmodule_state *state = get_cursesmodule_state(module);
+ _curses_set_null_error(state, curses_funcname, python_funcname);
+}
+
+/* Same as _curses_set_error() for a Window object. */
+static void
+curses_window_set_error(PyCursesWindowObject *win,
+ const char *curses_funcname,
+ const char *python_funcname)
+{
+ cursesmodule_state *state = get_cursesmodule_state_by_win(win);
+ _curses_set_error(state, curses_funcname, python_funcname);
+}
+
+/* Same as _curses_set_null_error() for a Window object. */
+static void
+curses_window_set_null_error(PyCursesWindowObject *win,
+ const char *curses_funcname,
+ const char *python_funcname)
+{
+ cursesmodule_state *state = get_cursesmodule_state_by_win(win);
+ _curses_set_null_error(state, curses_funcname, python_funcname);
+}
+
/* Utility Checking Procedures */
/*
* Function to check that 'funcname' has been called by testing
- * the 'called' boolean. If an error occurs, a PyCursesError is
+ * the 'called' boolean. If an error occurs, an exception is
* set and this returns 0. Otherwise, this returns 1.
*
* Since this function can be called in functions that do not
* have a direct access to the module's state, '_curses.error'
* is imported on demand.
+ *
+ * Use _PyCursesStatefulCheckFunction() if the module is given.
*/
-static inline int
+static int
_PyCursesCheckFunction(int called, const char *funcname)
{
if (called == TRUE) {
@@ -230,7 +328,7 @@ _PyCursesCheckFunction(int called, const char *funcname)
}
PyObject *exc = PyImport_ImportModuleAttrString("_curses", "error");
if (exc != NULL) {
- PyErr_Format(exc, "must call %s() first", funcname);
+ PyErr_Format(exc, CURSES_ERROR_MUST_CALL_FORMAT, funcname);
Py_DECREF(exc);
}
assert(PyErr_Occurred());
@@ -244,14 +342,15 @@ _PyCursesCheckFunction(int called, const char *funcname)
*
* The exception type is obtained from the 'module' state.
*/
-static inline int
-_PyCursesStatefulCheckFunction(PyObject *module, int called, const char *funcname)
+static int
+_PyCursesStatefulCheckFunction(PyObject *module,
+ int called, const char *funcname)
{
if (called == TRUE) {
return 1;
}
cursesmodule_state *state = get_cursesmodule_state(module);
- PyErr_Format(state->error, "must call %s() first", funcname);
+ PyErr_Format(state->error, CURSES_ERROR_MUST_CALL_FORMAT, funcname);
return 0;
}
@@ -287,44 +386,41 @@ _PyCursesStatefulCheckFunction(PyObject *module, int called, const char *funcnam
/* Utility Functions */
-static inline void
-_PyCursesSetError(cursesmodule_state *state, const char *funcname)
-{
- if (funcname == NULL) {
- PyErr_SetString(state->error, catchall_ERR);
- }
- else {
- PyErr_Format(state->error, "%s() returned ERR", funcname);
- }
-}
-
/*
- * Check the return code from a curses function and return None
- * or raise an exception as appropriate.
+ * Check the return code from a curses function, returning None
+ * on success and setting an exception on error.
*/
+/*
+ * Return None if 'code' is OK. Otherwise, set an exception
+ * using curses_set_error() and the remaining arguments, and
+ * return NULL.
+ */
static PyObject *
-PyCursesCheckERR(PyObject *module, int code, const char *fname)
+curses_check_err(PyObject *module, int code,
+ const char *curses_funcname,
+ const char *python_funcname)
{
if (code != ERR) {
+ assert(code == OK);
Py_RETURN_NONE;
- } else {
- cursesmodule_state *state = get_cursesmodule_state(module);
- _PyCursesSetError(state, fname);
- return NULL;
}
+ curses_set_error(module, curses_funcname, python_funcname);
+ return NULL;
}
+/* Same as curses_check_err() for a Window object. */
static PyObject *
-PyCursesCheckERR_ForWin(PyCursesWindowObject *win, int code, const char *fname)
+curses_window_check_err(PyCursesWindowObject *win, int code,
+ const char *curses_funcname,
+ const char *python_funcname)
{
if (code != ERR) {
+ assert(code == OK);
Py_RETURN_NONE;
- } else {
- cursesmodule_state *state = get_cursesmodule_state_by_win(win);
- _PyCursesSetError(state, fname);
- return NULL;
}
+ curses_window_set_error(win, curses_funcname, python_funcname);
+ return NULL;
}
/* Convert an object to a byte (an integer of type chtype):
@@ -650,13 +746,16 @@ class component_converter(CConverter):
The Window Object
******************************************************************************/
-/* Function prototype macros for Window object
-
- X - function name
- TYPE - parameter Type
- ERGSTR - format string for construction of the return value
- PARSESTR - format string for argument parsing
-*/
+/*
+ * Macros for creating a PyCursesWindowObject object's method.
+ *
+ * Parameters
+ *
+ * X The name of the curses C function or macro to invoke.
+ * TYPE The function parameter(s) type.
+ * ERGSTR The format string for construction of the return value.
+ * PARSESTR The format string for argument parsing.
+ */
#define Window_NoArgNoReturnFunction(X) \
static PyObject *PyCursesWindow_ ## X \
@@ -664,7 +763,7 @@ class component_converter(CConverter):
{ \
PyCursesWindowObject *self = _PyCursesWindowObject_CAST(op); \
int code = X(self->win); \
- return PyCursesCheckERR_ForWin(self, code, # X); \
+ return curses_window_check_err(self, code, # X, NULL); \
}
#define Window_NoArgTrueFalseFunction(X) \
@@ -717,7 +816,7 @@ class component_converter(CConverter):
} \
PyCursesWindowObject *self = _PyCursesWindowObject_CAST(op); \
int code = X(self->win, arg1); \
- return PyCursesCheckERR_ForWin(self, code, # X); \
+ return curses_window_check_err(self, code, # X, NULL); \
}
#define Window_TwoArgNoReturnFunction(X, TYPE, PARSESTR) \
@@ -730,7 +829,7 @@ class component_converter(CConverter):
} \
PyCursesWindowObject *self = _PyCursesWindowObject_CAST(op); \
int code = X(self->win, arg1, arg2); \
- return PyCursesCheckERR_ForWin(self, code, # X); \
+ return curses_window_check_err(self, code, # X, NULL); \
}
/* ------------- WINDOW routines --------------- */
@@ -903,13 +1002,15 @@ _curses_window_addch_impl(PyCursesWindowObject *self, int group_left_1,
#ifdef HAVE_NCURSESW
type = PyCurses_ConvertToCchar_t(self, ch, &cch, wstr);
if (type == 2) {
- funcname = "add_wch";
wstr[1] = L'\0';
setcchar(&wcval, wstr, attr, PAIR_NUMBER(attr), NULL);
- if (coordinates_group)
+ if (coordinates_group) {
rtn = mvwadd_wch(self->win,y,x, &wcval);
+ funcname = "mvwadd_wch";
+ }
else {
rtn = wadd_wch(self->win, &wcval);
+ funcname = "wadd_wch";
}
}
else
@@ -917,17 +1018,19 @@ _curses_window_addch_impl(PyCursesWindowObject *self, int group_left_1,
type = PyCurses_ConvertToCchar_t(self, ch, &cch);
#endif
if (type == 1) {
- funcname = "addch";
- if (coordinates_group)
+ if (coordinates_group) {
rtn = mvwaddch(self->win,y,x, cch | (attr_t) attr);
+ funcname = "mvwaddch";
+ }
else {
rtn = waddch(self->win, cch | (attr_t) attr);
+ funcname = "waddch";
}
}
else {
return NULL;
}
- return PyCursesCheckERR_ForWin(self, rtn, funcname);
+ return curses_window_check_err(self, rtn, funcname, "addch");
}
/*[clinic input]
@@ -987,27 +1090,33 @@ _curses_window_addstr_impl(PyCursesWindowObject *self, int group_left_1,
}
#ifdef HAVE_NCURSESW
if (strtype == 2) {
- funcname = "addwstr";
- if (use_xy)
+ if (use_xy) {
rtn = mvwaddwstr(self->win,y,x,wstr);
- else
+ funcname = "mvwaddwstr";
+ }
+ else {
rtn = waddwstr(self->win,wstr);
+ funcname = "waddwstr";
+ }
PyMem_Free(wstr);
}
else
#endif
{
const char *str = PyBytes_AS_STRING(bytesobj);
- funcname = "addstr";
- if (use_xy)
+ if (use_xy) {
rtn = mvwaddstr(self->win,y,x,str);
- else
+ funcname = "mvwaddstr";
+ }
+ else {
rtn = waddstr(self->win,str);
+ funcname = "waddstr";
+ }
Py_DECREF(bytesobj);
}
if (use_attr)
(void)wattrset(self->win,attr_old);
- return PyCursesCheckERR_ForWin(self, rtn, funcname);
+ return curses_window_check_err(self, rtn, funcname, "addstr");
}
/*[clinic input]
@@ -1070,27 +1179,33 @@ _curses_window_addnstr_impl(PyCursesWindowObject *self, int group_left_1,
}
#ifdef HAVE_NCURSESW
if (strtype == 2) {
- funcname = "addnwstr";
- if (use_xy)
+ if (use_xy) {
rtn = mvwaddnwstr(self->win,y,x,wstr,n);
- else
+ funcname = "mvwaddnwstr";
+ }
+ else {
rtn = waddnwstr(self->win,wstr,n);
+ funcname = "waddnwstr";
+ }
PyMem_Free(wstr);
}
else
#endif
{
const char *str = PyBytes_AS_STRING(bytesobj);
- funcname = "addnstr";
- if (use_xy)
+ if (use_xy) {
rtn = mvwaddnstr(self->win,y,x,str,n);
- else
+ funcname = "mvwaddnstr";
+ }
+ else {
rtn = waddnstr(self->win,str,n);
+ funcname = "waddnstr";
+ }
Py_DECREF(bytesobj);
}
if (use_attr)
(void)wattrset(self->win,attr_old);
- return PyCursesCheckERR_ForWin(self, rtn, funcname);
+ return curses_window_check_err(self, rtn, funcname, "addnstr");
}
/*[clinic input]
@@ -1114,7 +1229,8 @@ _curses_window_bkgd_impl(PyCursesWindowObject *self, PyObject *ch, long attr)
if (!PyCurses_ConvertToChtype(self, ch, &bkgd))
return NULL;
- return PyCursesCheckERR_ForWin(self, wbkgd(self->win, bkgd | attr), "bkgd");
+ int rtn = wbkgd(self->win, bkgd | attr);
+ return curses_window_check_err(self, rtn, "wbkgd", "bkgd");
}
/*[clinic input]
@@ -1130,7 +1246,8 @@ static PyObject *
_curses_window_attroff_impl(PyCursesWindowObject *self, long attr)
/*[clinic end generated code: output=8a2fcd4df682fc64 input=786beedf06a7befe]*/
{
- return PyCursesCheckERR_ForWin(self, wattroff(self->win, (attr_t)attr), "attroff");
+ int rtn = wattroff(self->win, (attr_t)attr);
+ return curses_window_check_err(self, rtn, "wattroff", "attroff");
}
/*[clinic input]
@@ -1146,7 +1263,8 @@ static PyObject *
_curses_window_attron_impl(PyCursesWindowObject *self, long attr)
/*[clinic end generated code: output=7afea43b237fa870 input=5a88fba7b1524f32]*/
{
- return PyCursesCheckERR_ForWin(self, wattron(self->win, (attr_t)attr), "attron");
+ int rtn = wattron(self->win, (attr_t)attr);
+ return curses_window_check_err(self, rtn, "wattron", "attron");
}
/*[clinic input]
@@ -1162,7 +1280,8 @@ static PyObject *
_curses_window_attrset_impl(PyCursesWindowObject *self, long attr)
/*[clinic end generated code: output=84e379bff20c0433 input=42e400c0d0154ab5]*/
{
- return PyCursesCheckERR_ForWin(self, wattrset(self->win, (attr_t)attr), "attrset");
+ int rtn = wattrset(self->win, (attr_t)attr);
+ return curses_window_check_err(self, rtn, "wattrset", "attrset");
}
/*[clinic input]
@@ -1188,7 +1307,7 @@ _curses_window_bkgdset_impl(PyCursesWindowObject *self, PyObject *ch,
return NULL;
wbkgdset(self->win, bkgd | attr);
- return PyCursesCheckERR_ForWin(self, 0, "bkgdset");
+ Py_RETURN_NONE;
}
/*[clinic input]
@@ -1344,6 +1463,7 @@ PyCursesWindow_ChgAt(PyObject *op, PyObject *args)
PyCursesWindowObject *self = _PyCursesWindowObject_CAST(op);
int rtn;
+ const char *funcname;
int x, y;
int num = -1;
short color;
@@ -1385,12 +1505,14 @@ PyCursesWindow_ChgAt(PyObject *op, PyObject *args)
if (use_xy) {
rtn = mvwchgat(self->win,y,x,num,attr,color,NULL);
touchline(self->win,y,1);
+ funcname = "mvwchgat";
} else {
getyx(self->win,y,x);
rtn = wchgat(self->win,num,attr,color,NULL);
touchline(self->win,y,1);
+ funcname = "wchgat";
}
- return PyCursesCheckERR_ForWin(self, rtn, "chgat");
+ return curses_window_check_err(self, rtn, funcname, "chgat");
}
#endif
@@ -1413,12 +1535,17 @@ _curses_window_delch_impl(PyCursesWindowObject *self, int group_right_1,
int y, int x)
/*[clinic end generated code: output=22e77bb9fa11b461 input=d2f79e630a4fc6d0]*/
{
+ int rtn;
+ const char *funcname;
if (!group_right_1) {
- return PyCursesCheckERR_ForWin(self, wdelch(self->win), "wdelch");
+ rtn = wdelch(self->win);
+ funcname = "wdelch";
}
else {
- return PyCursesCheckERR_ForWin(self, py_mvwdelch(self->win, y, x), "mvwdelch");
+ rtn = py_mvwdelch(self->win, y, x);
+ funcname = "mvwdelch";
}
+ return curses_window_check_err(self, rtn, funcname, "delch");
}
/*[clinic input]
@@ -1453,8 +1580,7 @@ _curses_window_derwin_impl(PyCursesWindowObject *self, int group_left_1,
win = derwin(self->win,nlines,ncols,begin_y,begin_x);
if (win == NULL) {
- cursesmodule_state *state = get_cursesmodule_state_by_win(self);
- PyErr_SetString(state->error, catchall_NULL);
+ curses_window_set_error(self, "derwin", NULL);
return NULL;
}
@@ -1485,17 +1611,20 @@ _curses_window_echochar_impl(PyCursesWindowObject *self, PyObject *ch,
if (!PyCurses_ConvertToChtype(self, ch, &ch_))
return NULL;
+ int rtn;
+ const char *funcname;
#ifdef py_is_pad
if (py_is_pad(self->win)) {
- return PyCursesCheckERR_ForWin(self,
- pechochar(self->win, ch_ | (attr_t)attr),
- "echochar");
+ rtn = pechochar(self->win, ch_ | (attr_t)attr);
+ funcname = "pechochar";
}
else
#endif
- return PyCursesCheckERR_ForWin(self,
- wechochar(self->win, ch_ | (attr_t)attr),
- "echochar");
+ {
+ rtn = wechochar(self->win, ch_ | (attr_t)attr);
+ funcname = "wechochar";
+ }
+ return curses_window_check_err(self, rtn, funcname, "echochar");
}
#ifdef NCURSES_MOUSE_VERSION
@@ -1608,7 +1737,8 @@ _curses_window_getkey_impl(PyCursesWindowObject *self, int group_right_1,
PyErr_CheckSignals();
if (!PyErr_Occurred()) {
cursesmodule_state *state = get_cursesmodule_state_by_win(self);
- PyErr_SetString(state->error, "no input");
+ const char *funcname = group_right_1 ? "mvwgetch" : "wgetch";
+ PyErr_Format(state->error, "getkey(): %s(): no input", funcname);
}
return NULL;
} else if (rtn <= 255) {
@@ -1668,7 +1798,8 @@ _curses_window_get_wch_impl(PyCursesWindowObject *self, int group_right_1,
/* get_wch() returns ERR in nodelay mode */
cursesmodule_state *state = get_cursesmodule_state_by_win(self);
- PyErr_SetString(state->error, "no input");
+ const char *funcname = group_right_1 ? "mvwget_wch" : "wget_wch";
+ PyErr_Format(state->error, "get_wch(): %s(): no input", funcname);
return NULL;
}
if (ct == KEY_CODE_YES)
@@ -1794,10 +1925,12 @@ _curses_window_hline_impl(PyCursesWindowObject *self, int group_left_1,
return NULL;
if (group_left_1) {
if (wmove(self->win, y, x) == ERR) {
- return PyCursesCheckERR_ForWin(self, ERR, "wmove");
+ curses_window_set_error(self, "wmove", "hline");
+ return NULL;
}
}
- return PyCursesCheckERR_ForWin(self, whline(self->win, ch_ | (attr_t)attr, n), "hline");
+ int rtn = whline(self->win, ch_ | (attr_t)attr, n);
+ return curses_window_check_err(self, rtn, "whline", "hline");
}
/*[clinic input]
@@ -1837,14 +1970,17 @@ _curses_window_insch_impl(PyCursesWindowObject *self, int group_left_1,
if (!PyCurses_ConvertToChtype(self, ch, &ch_))
return NULL;
+ const char *funcname;
if (!group_left_1) {
rtn = winsch(self->win, ch_ | (attr_t)attr);
+ funcname = "winsch";
}
else {
rtn = mvwinsch(self->win, y, x, ch_ | (attr_t)attr);
+ funcname = "mvwwinsch";
}
- return PyCursesCheckERR_ForWin(self, rtn, "insch");
+ return curses_window_check_err(self, rtn, funcname, "insch");
}
/*[clinic input]
@@ -2003,27 +2139,33 @@ _curses_window_insstr_impl(PyCursesWindowObject *self, int group_left_1,
}
#ifdef HAVE_NCURSESW
if (strtype == 2) {
- funcname = "inswstr";
- if (use_xy)
+ if (use_xy) {
rtn = mvwins_wstr(self->win,y,x,wstr);
- else
+ funcname = "mvwins_wstr";
+ }
+ else {
rtn = wins_wstr(self->win,wstr);
+ funcname = "wins_wstr";
+ }
PyMem_Free(wstr);
}
else
#endif
{
const char *str = PyBytes_AS_STRING(bytesobj);
- funcname = "insstr";
- if (use_xy)
+ if (use_xy) {
rtn = mvwinsstr(self->win,y,x,str);
- else
+ funcname = "mvwinsstr";
+ }
+ else {
rtn = winsstr(self->win,str);
+ funcname = "winsstr";
+ }
Py_DECREF(bytesobj);
}
if (use_attr)
(void)wattrset(self->win,attr_old);
- return PyCursesCheckERR_ForWin(self, rtn, funcname);
+ return curses_window_check_err(self, rtn, funcname, "insstr");
}
/*[clinic input]
@@ -2088,27 +2230,33 @@ _curses_window_insnstr_impl(PyCursesWindowObject *self, int group_left_1,
}
#ifdef HAVE_NCURSESW
if (strtype == 2) {
- funcname = "insn_wstr";
- if (use_xy)
+ if (use_xy) {
rtn = mvwins_nwstr(self->win,y,x,wstr,n);
- else
+ funcname = "mvwins_nwstr";
+ }
+ else {
rtn = wins_nwstr(self->win,wstr,n);
+ funcname = "wins_nwstr";
+ }
PyMem_Free(wstr);
}
else
#endif
{
const char *str = PyBytes_AS_STRING(bytesobj);
- funcname = "insnstr";
- if (use_xy)
+ if (use_xy) {
rtn = mvwinsnstr(self->win,y,x,str,n);
- else
+ funcname = "mvwinsnstr";
+ }
+ else {
rtn = winsnstr(self->win,str,n);
+ funcname = "winsnstr";
+ }
Py_DECREF(bytesobj);
}
if (use_attr)
(void)wattrset(self->win,attr_old);
- return PyCursesCheckERR_ForWin(self, rtn, funcname);
+ return curses_window_check_err(self, rtn, funcname, "insnstr");
}
/*[clinic input]
@@ -2130,8 +2278,7 @@ _curses_window_is_linetouched_impl(PyCursesWindowObject *self, int line)
int erg;
erg = is_linetouched(self->win, line);
if (erg == ERR) {
- PyErr_SetString(PyExc_TypeError,
- "is_linetouched: line number outside of boundaries");
+ curses_window_set_error(self, "is_linetouched", NULL);
return NULL;
}
return PyBool_FromLong(erg);
@@ -2195,7 +2342,8 @@ _curses_window_noutrefresh_impl(PyCursesWindowObject *self)
rtn = pnoutrefresh(self->win, pminrow, pmincol,
sminrow, smincol, smaxrow, smaxcol);
Py_END_ALLOW_THREADS
- return PyCursesCheckERR_ForWin(self, rtn, "pnoutrefresh");
+ return curses_window_check_err(self, rtn,
+ "pnoutrefresh", "noutrefresh");
}
if (group_right_1) {
PyErr_SetString(PyExc_TypeError,
@@ -2206,7 +2354,7 @@ _curses_window_noutrefresh_impl(PyCursesWindowObject *self)
Py_BEGIN_ALLOW_THREADS
rtn = wnoutrefresh(self->win);
Py_END_ALLOW_THREADS
- return PyCursesCheckERR_ForWin(self, rtn, "wnoutrefresh");
+ return curses_window_check_err(self, rtn, "wnoutrefresh", "noutrefresh");
}
/*[clinic input]
@@ -2248,11 +2396,11 @@ _curses_window_overlay_impl(PyCursesWindowObject *self,
if (group_right_1) {
rtn = copywin(self->win, destwin->win, sminrow, smincol,
dminrow, dmincol, dmaxrow, dmaxcol, TRUE);
- return PyCursesCheckERR_ForWin(self, rtn, "copywin");
+ return curses_window_check_err(self, rtn, "copywin", "overlay");
}
else {
rtn = overlay(self->win, destwin->win);
- return PyCursesCheckERR_ForWin(self, rtn, "overlay");
+ return curses_window_check_err(self, rtn, "overlay", NULL);
}
}
@@ -2296,11 +2444,11 @@ _curses_window_overwrite_impl(PyCursesWindowObject *self,
if (group_right_1) {
rtn = copywin(self->win, destwin->win, sminrow, smincol,
dminrow, dmincol, dmaxrow, dmaxcol, FALSE);
- return PyCursesCheckERR_ForWin(self, rtn, "copywin");
+ return curses_window_check_err(self, rtn, "copywin", "overwrite");
}
else {
rtn = overwrite(self->win, destwin->win);
- return PyCursesCheckERR_ForWin(self, rtn, "overwrite");
+ return curses_window_check_err(self, rtn, "overwrite", NULL);
}
}
@@ -2329,7 +2477,7 @@ _curses_window_putwin_impl(PyCursesWindowObject *self, PyObject *file)
return PyErr_SetFromErrno(PyExc_OSError);
if (_Py_set_inheritable(fileno(fp), 0, NULL) < 0)
goto exit;
- res = PyCursesCheckERR_ForWin(self, putwin(self->win, fp), "putwin");
+ res = curses_window_check_err(self, putwin(self->win, fp), "putwin", NULL);
if (res == NULL)
goto exit;
fseek(fp, 0, 0);
@@ -2368,7 +2516,8 @@ static PyObject *
_curses_window_redrawln_impl(PyCursesWindowObject *self, int beg, int num)
/*[clinic end generated code: output=ea216e334f9ce1b4 input=152155e258a77a7a]*/
{
- return PyCursesCheckERR_ForWin(self, wredrawln(self->win,beg,num), "redrawln");
+ int rtn = wredrawln(self->win,beg, num);
+ return curses_window_check_err(self, rtn, "wredrawln", "redrawln");
}
/*[clinic input]
@@ -2419,7 +2568,7 @@ _curses_window_refresh_impl(PyCursesWindowObject *self, int group_right_1,
rtn = prefresh(self->win, pminrow, pmincol,
sminrow, smincol, smaxrow, smaxcol);
Py_END_ALLOW_THREADS
- return PyCursesCheckERR_ForWin(self, rtn, "prefresh");
+ return curses_window_check_err(self, rtn, "prefresh", "refresh");
}
#endif
if (group_right_1) {
@@ -2430,7 +2579,7 @@ _curses_window_refresh_impl(PyCursesWindowObject *self, int group_right_1,
Py_BEGIN_ALLOW_THREADS
rtn = wrefresh(self->win);
Py_END_ALLOW_THREADS
- return PyCursesCheckERR_ForWin(self, rtn, "prefresh");
+ return curses_window_check_err(self, rtn, "wrefresh", "refresh");
}
/*[clinic input]
@@ -2452,7 +2601,8 @@ _curses_window_setscrreg_impl(PyCursesWindowObject *self, int top,
int bottom)
/*[clinic end generated code: output=486ab5db218d2b1a input=1b517b986838bf0e]*/
{
- return PyCursesCheckERR_ForWin(self, wsetscrreg(self->win, top, bottom), "wsetscrreg");
+ int rtn = wsetscrreg(self->win, top, bottom);
+ return curses_window_check_err(self, rtn, "wsetscrreg", "setscrreg");
}
/*[clinic input]
@@ -2482,19 +2632,23 @@ _curses_window_subwin_impl(PyCursesWindowObject *self, int group_left_1,
/*[clinic end generated code: output=93e898afc348f59a input=2129fa47fd57721c]*/
{
WINDOW *win;
+ const char *funcname;
/* printf("Subwin: %i %i %i %i \n", nlines, ncols, begin_y, begin_x); */
#ifdef py_is_pad
if (py_is_pad(self->win)) {
win = subpad(self->win, nlines, ncols, begin_y, begin_x);
+ funcname = "subpad";
}
else
#endif
+ {
win = subwin(self->win, nlines, ncols, begin_y, begin_x);
+ funcname = "subwin";
+ }
if (win == NULL) {
- cursesmodule_state *state = get_cursesmodule_state_by_win(self);
- PyErr_SetString(state->error, catchall_NULL);
+ curses_window_set_null_error(self, funcname, "subwin");
return NULL;
}
@@ -2521,12 +2675,17 @@ _curses_window_scroll_impl(PyCursesWindowObject *self, int group_right_1,
int lines)
/*[clinic end generated code: output=4541a8a11852d360 input=c969ca0cfabbdbec]*/
{
+ int rtn;
+ const char *funcname;
if (!group_right_1) {
- return PyCursesCheckERR_ForWin(self, scroll(self->win), "scroll");
+ rtn = scroll(self->win);
+ funcname = "scroll";
}
else {
- return PyCursesCheckERR_ForWin(self, wscrl(self->win, lines), "scroll");
+ rtn = wscrl(self->win, lines);
+ funcname = "wscrl";
}
+ return curses_window_check_err(self, rtn, funcname, "scroll");
}
/*[clinic input]
@@ -2550,12 +2709,17 @@ _curses_window_touchline_impl(PyCursesWindowObject *self, int start,
int count, int group_right_1, int changed)
/*[clinic end generated code: output=65d05b3f7438c61d input=a98aa4f79b6be845]*/
{
+ int rtn;
+ const char *funcname;
if (!group_right_1) {
- return PyCursesCheckERR_ForWin(self, touchline(self->win, start, count), "touchline");
+ rtn = touchline(self->win, start, count);
+ funcname = "touchline";
}
else {
- return PyCursesCheckERR_ForWin(self, wtouchln(self->win, start, count, changed), "touchline");
+ rtn = wtouchln(self->win, start, count, changed);
+ funcname = "wtouchln";
}
+ return curses_window_check_err(self, rtn, funcname, "touchline");
}
/*[clinic input]
@@ -2593,10 +2757,13 @@ _curses_window_vline_impl(PyCursesWindowObject *self, int group_left_1,
if (!PyCurses_ConvertToChtype(self, ch, &ch_))
return NULL;
if (group_left_1) {
- if (wmove(self->win, y, x) == ERR)
- return PyCursesCheckERR_ForWin(self, ERR, "wmove");
+ if (wmove(self->win, y, x) == ERR) {
+ curses_window_set_error(self, "wmove", "vline");
+ return NULL;
+ }
}
- return PyCursesCheckERR_ForWin(self, wvline(self->win, ch_ | (attr_t)attr, n), "vline");
+ int rtn = wvline(self->win, ch_ | (attr_t)attr, n);
+ return curses_window_check_err(self, rtn, "wvline", "vline");
}
static PyObject *
@@ -2761,26 +2928,39 @@ static PyType_Spec PyCursesWindow_Type_spec = {
/* -------------------------------------------------------*/
-/* Function Body Macros - They are ugly but very, very useful. ;-)
-
- X - function name
- TYPE - parameter Type
- ERGSTR - format string for construction of the return value
- PARSESTR - format string for argument parsing
- */
+/*
+ * Macros for implementing simple module's methods.
+ *
+ * Parameters
+ *
+ * X The name of the curses C function or macro to invoke.
+ * FLAG When false, prefixes the function name with 'no' at runtime,
+ * This parameter is present in the signature and auto-generated
+ * by Argument Clinic.
+ *
+ * These macros should only be used for generating the body of
+ * the module's methods since they need a module reference.
+ */
#define NoArgNoReturnFunctionBody(X) \
{ \
PyCursesStatefulInitialised(module); \
- return PyCursesCheckERR(module, X(), # X); }
-
-#define NoArgOrFlagNoReturnFunctionBody(X, flag) \
-{ \
- PyCursesStatefulInitialised(module); \
- if (flag) \
- return PyCursesCheckERR(module, X(), # X); \
- else \
- return PyCursesCheckERR(module, no ## X(), # X); \
+ return curses_check_err(module, X(), # X, NULL); }
+
+#define NoArgOrFlagNoReturnFunctionBody(X, FLAG) \
+{ \
+ PyCursesStatefulInitialised(module); \
+ int rtn; \
+ const char *funcname; \
+ if (FLAG) { \
+ rtn = X(); \
+ funcname = # X; \
+ } \
+ else { \
+ rtn = no ## X(); \
+ funcname = "no" # X; \
+ } \
+ return curses_check_err(module, rtn, funcname, # X); \
}
#define NoArgReturnIntFunctionBody(X) \
@@ -2788,7 +2968,6 @@ static PyType_Spec PyCursesWindow_Type_spec = {
PyCursesStatefulInitialised(module); \
return PyLong_FromLong((long) X()); }
-
#define NoArgReturnStringFunctionBody(X) \
{ \
PyCursesStatefulInitialised(module); \
@@ -2903,9 +3082,8 @@ _curses_color_content_impl(PyObject *module, int color_number)
PyCursesStatefulInitialisedColor(module);
if (_COLOR_CONTENT_FUNC(color_number, &r, &g, &b) == ERR) {
- cursesmodule_state *state = get_cursesmodule_state(module);
- PyErr_Format(state->error, "%s() returned ERR",
- Py_STRINGIFY(_COLOR_CONTENT_FUNC));
+ const char *funcname = Py_STRINGIFY(_COLOR_CONTENT_FUNC);
+ curses_set_error(module, funcname, "color_content");
return NULL;
}
@@ -2959,7 +3137,10 @@ _curses_curs_set_impl(PyObject *module, int visibility)
PyCursesStatefulInitialised(module);
erg = curs_set(visibility);
- if (erg == ERR) return PyCursesCheckERR(module, erg, "curs_set");
+ if (erg == ERR) {
+ curses_set_error(module, "curs_set", NULL);
+ return NULL;
+ }
return PyLong_FromLong((long) erg);
}
@@ -3010,7 +3191,7 @@ _curses_delay_output_impl(PyObject *module, int ms)
{
PyCursesStatefulInitialised(module);
- return PyCursesCheckERR(module, delay_output(ms), "delay_output");
+ return curses_check_err(module, delay_output(ms), "delay_output", NULL);
}
/*[clinic input]
@@ -3143,8 +3324,7 @@ _curses_getmouse_impl(PyObject *module)
rtn = getmouse( &event );
if (rtn == ERR) {
- cursesmodule_state *state = get_cursesmodule_state(module);
- PyErr_SetString(state->error, "getmouse() returned ERR");
+ curses_set_error(module, "getmouse", NULL);
return NULL;
}
return Py_BuildValue("(hiiik)",
@@ -3182,7 +3362,7 @@ _curses_ungetmouse_impl(PyObject *module, short id, int x, int y, int z,
event.y = y;
event.z = z;
event.bstate = bstate;
- return PyCursesCheckERR(module, ungetmouse(&event), "ungetmouse");
+ return curses_check_err(module, ungetmouse(&event), "ungetmouse", NULL);
}
#endif
@@ -3238,8 +3418,7 @@ _curses_getwin(PyObject *module, PyObject *file)
fseek(fp, 0, 0);
win = getwin(fp);
if (win == NULL) {
- cursesmodule_state *state = get_cursesmodule_state(module);
- PyErr_SetString(state->error, catchall_NULL);
+ curses_set_null_error(module, "getwin", NULL);
goto error;
}
cursesmodule_state *state = get_cursesmodule_state(module);
@@ -3268,7 +3447,7 @@ _curses_halfdelay_impl(PyObject *module, unsigned char tenths)
{
PyCursesStatefulInitialised(module);
- return PyCursesCheckERR(module, halfdelay(tenths), "halfdelay");
+ return curses_check_err(module, halfdelay(tenths), "halfdelay", NULL);
}
/*[clinic input]
@@ -3353,9 +3532,10 @@ _curses_init_color_impl(PyObject *module, int color_number, short r, short g,
PyCursesStatefulInitialised(module);
PyCursesStatefulInitialisedColor(module);
- return PyCursesCheckERR(module,
+ return curses_check_err(module,
_CURSES_INIT_COLOR_FUNC(color_number, r, g, b),
- Py_STRINGIFY(_CURSES_INIT_COLOR_FUNC));
+ Py_STRINGIFY(_CURSES_INIT_COLOR_FUNC),
+ NULL);
}
/*[clinic input]
@@ -3389,9 +3569,8 @@ _curses_init_pair_impl(PyObject *module, int pair_number, int fg, int bg)
COLOR_PAIRS - 1);
}
else {
- cursesmodule_state *state = get_cursesmodule_state(module);
- PyErr_Format(state->error, "%s() returned ERR",
- Py_STRINGIFY(_CURSES_INIT_PAIR_FUNC));
+ const char *funcname = Py_STRINGIFY(_CURSES_INIT_PAIR_FUNC);
+ curses_set_error(module, funcname, "init_pair");
}
return NULL;
}
@@ -3422,8 +3601,7 @@ _curses_initscr_impl(PyObject *module)
win = initscr();
if (win == NULL) {
- cursesmodule_state *state = get_cursesmodule_state(module);
- PyErr_SetString(state->error, catchall_NULL);
+ curses_set_null_error(module, "initscr", NULL);
return NULL;
}
@@ -3628,7 +3806,7 @@ _curses_set_escdelay_impl(PyObject *module, int ms)
return NULL;
}
- return PyCursesCheckERR(module, set_escdelay(ms), "set_escdelay");
+ return curses_check_err(module, set_escdelay(ms), "set_escdelay", NULL);
}
/*[clinic input]
@@ -3667,7 +3845,7 @@ _curses_set_tabsize_impl(PyObject *module, int size)
return NULL;
}
- return PyCursesCheckERR(module, set_tabsize(size), "set_tabsize");
+ return curses_check_err(module, set_tabsize(size), "set_tabsize", NULL);
}
#endif
@@ -3685,7 +3863,7 @@ _curses_intrflush_impl(PyObject *module, int flag)
{
PyCursesStatefulInitialised(module);
- return PyCursesCheckERR(module, intrflush(NULL, flag), "intrflush");
+ return curses_check_err(module, intrflush(NULL, flag), "intrflush", NULL);
}
/*[clinic input]
@@ -3798,7 +3976,7 @@ _curses_meta_impl(PyObject *module, int yes)
{
PyCursesStatefulInitialised(module);
- return PyCursesCheckERR(module, meta(stdscr, yes), "meta");
+ return curses_check_err(module, meta(stdscr, yes), "meta", NULL);
}
#ifdef NCURSES_MOUSE_VERSION
@@ -3821,8 +3999,12 @@ _curses_mouseinterval_impl(PyObject *module, int interval)
/*[clinic end generated code: output=c4f5ff04354634c5 input=75aaa3f0db10ac4e]*/
{
PyCursesStatefulInitialised(module);
-
- return PyCursesCheckERR(module, mouseinterval(interval), "mouseinterval");
+ int value = mouseinterval(interval);
+ if (value == ERR) {
+ curses_set_error(module, "mouseinterval", NULL);
+ return NULL;
+ }
+ return PyLong_FromLong(value);
}
/*[clinic input]
@@ -3898,8 +4080,7 @@ _curses_newpad_impl(PyObject *module, int nlines, int ncols)
win = newpad(nlines, ncols);
if (win == NULL) {
- cursesmodule_state *state = get_cursesmodule_state(module);
- PyErr_SetString(state->error, catchall_NULL);
+ curses_set_null_error(module, "newpad", NULL);
return NULL;
}
@@ -3939,8 +4120,7 @@ _curses_newwin_impl(PyObject *module, int nlines, int ncols,
win = newwin(nlines,ncols,begin_y,begin_x);
if (win == NULL) {
- cursesmodule_state *state = get_cursesmodule_state(module);
- PyErr_SetString(state->error, catchall_NULL);
+ curses_set_null_error(module, "newwin", NULL);
return NULL;
}
@@ -4059,9 +4239,8 @@ _curses_pair_content_impl(PyObject *module, int pair_number)
COLOR_PAIRS - 1);
}
else {
- cursesmodule_state *state = get_cursesmodule_state(module);
- PyErr_Format(state->error, "%s() returned ERR",
- Py_STRINGIFY(_CURSES_PAIR_CONTENT_FUNC));
+ const char *funcname = Py_STRINGIFY(_CURSES_PAIR_CONTENT_FUNC);
+ curses_set_error(module, funcname, "pair_content");
}
return NULL;
}
@@ -4105,7 +4284,7 @@ static PyObject *
_curses_putp_impl(PyObject *module, const char *string)
/*[clinic end generated code: output=e98081d1b8eb5816 input=1601faa828b44cb3]*/
{
- return PyCursesCheckERR(module, putp(string), "putp");
+ return curses_check_err(module, putp(string), "putp", NULL);
}
/*[clinic input]
@@ -4279,10 +4458,12 @@ _curses_resizeterm_impl(PyObject *module, short nlines, short ncols)
/*[clinic end generated code: output=4de3abab50c67f02 input=414e92a63e3e9899]*/
{
PyObject *result;
+ int code;
PyCursesStatefulInitialised(module);
- result = PyCursesCheckERR(module, resizeterm(nlines, ncols), "resizeterm");
+ code = resizeterm(nlines, ncols);
+ result = curses_check_err(module, code, "resizeterm", NULL);
if (!result)
return NULL;
if (!update_lines_cols(module)) {
@@ -4318,10 +4499,12 @@ _curses_resize_term_impl(PyObject *module, short nlines, short ncols)
/*[clinic end generated code: output=46c6d749fa291dbd input=276afa43d8ea7091]*/
{
PyObject *result;
+ int code;
PyCursesStatefulInitialised(module);
- result = PyCursesCheckERR(module, resize_term(nlines, ncols), "resize_term");
+ code = resize_term(nlines, ncols);
+ result = curses_check_err(module, code, "resize_term", NULL);
if (!result)
return NULL;
if (!update_lines_cols(module)) {
@@ -4390,8 +4573,7 @@ _curses_start_color_impl(PyObject *module)
PyCursesStatefulInitialised(module);
if (start_color() == ERR) {
- cursesmodule_state *state = get_cursesmodule_state(module);
- PyErr_SetString(state->error, "start_color() returned ERR");
+ curses_set_error(module, "start_color", NULL);
return NULL;
}
@@ -4543,8 +4725,7 @@ _curses_tparm_impl(PyObject *module, const char *str, int i1, int i2, int i3,
result = tparm((char *)str,i1,i2,i3,i4,i5,i6,i7,i8,i9);
if (!result) {
- cursesmodule_state *state = get_cursesmodule_state(module);
- PyErr_SetString(state->error, "tparm() returned NULL");
+ curses_set_null_error(module, "tparm", NULL);
return NULL;
}
@@ -4570,7 +4751,7 @@ _curses_typeahead_impl(PyObject *module, int fd)
{
PyCursesStatefulInitialised(module);
- return PyCursesCheckERR(module, typeahead( fd ), "typeahead");
+ return curses_check_err(module, typeahead(fd), "typeahead", NULL);
}
#endif
@@ -4620,7 +4801,7 @@ _curses_ungetch(PyObject *module, PyObject *ch)
if (!PyCurses_ConvertToChtype(NULL, ch, &ch_))
return NULL;
- return PyCursesCheckERR(module, ungetch(ch_), "ungetch");
+ return curses_check_err(module, ungetch(ch_), "ungetch", NULL);
}
#ifdef HAVE_NCURSESW
@@ -4690,7 +4871,7 @@ _curses_unget_wch(PyObject *module, PyObject *ch)
if (!PyCurses_ConvertToWchar_t(ch, &wch))
return NULL;
- return PyCursesCheckERR(module, unget_wch(wch), "unget_wch");
+ return curses_check_err(module, unget_wch(wch), "unget_wch", NULL);
}
#endif
@@ -4739,13 +4920,7 @@ _curses_use_default_colors_impl(PyObject *module)
PyCursesStatefulInitialisedColor(module);
code = use_default_colors();
- if (code != ERR) {
- Py_RETURN_NONE;
- } else {
- cursesmodule_state *state = get_cursesmodule_state(module);
- PyErr_SetString(state->error, "use_default_colors() returned ERR");
- return NULL;
- }
+ return curses_check_err(module, code, "use_default_colors", NULL);
}
/*[clinic input]
diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c
index 9bba0e3354b..eb90be81c8d 100644
--- a/Modules/_datetimemodule.c
+++ b/Modules/_datetimemodule.c
@@ -1088,6 +1088,7 @@ parse_isoformat_time(const char *dtstr, size_t dtlen, int *hour, int *minute,
// -3: Failed to parse time component
// -4: Failed to parse time separator
// -5: Malformed timezone string
+ // -6: Timezone fields are not in range
const char *p = dtstr;
const char *p_end = dtstr + dtlen;
@@ -1134,6 +1135,11 @@ parse_isoformat_time(const char *dtstr, size_t dtlen, int *hour, int *minute,
rv = parse_hh_mm_ss_ff(tzinfo_pos, p_end, &tzhour, &tzminute, &tzsecond,
tzmicrosecond);
+ // Check if timezone fields are in range
+ if (check_time_args(tzhour, tzminute, tzsecond, *tzmicrosecond, 0) < 0) {
+ return -6;
+ }
+
*tzoffset = tzsign * ((tzhour * 3600) + (tzminute * 60) + tzsecond);
*tzmicrosecond *= tzsign;
@@ -5039,6 +5045,9 @@ time_fromisoformat(PyObject *cls, PyObject *tstr) {
&tzoffset, &tzimicrosecond);
if (rv < 0) {
+ if (rv == -6) {
+ goto error;
+ }
goto invalid_string_error;
}
@@ -5075,6 +5084,9 @@ invalid_iso_midnight:
invalid_string_error:
PyErr_Format(PyExc_ValueError, "Invalid isoformat string: %R", tstr);
return NULL;
+
+error:
+ return NULL;
}
@@ -5539,8 +5551,9 @@ datetime_best_possible(PyObject *cls, TM_FUNC f, PyObject *tzinfo)
time_t secs;
int us;
- if (_PyTime_AsTimevalTime_t(ts, &secs, &us, _PyTime_ROUND_FLOOR) < 0)
+ if (_PyTime_AsTimevalTime_t(ts, &secs, &us, _PyTime_ROUND_HALF_EVEN) < 0) {
return NULL;
+ }
assert(0 <= us && us <= 999999);
return datetime_from_timet_and_us(cls, f, secs, us, tzinfo);
@@ -5927,6 +5940,9 @@ datetime_fromisoformat(PyObject *cls, PyObject *dtstr)
len -= (p - dt_ptr);
rv = parse_isoformat_time(p, len, &hour, &minute, &second,
&microsecond, &tzoffset, &tzusec);
+ if (rv == -6) {
+ goto error;
+ }
}
if (rv < 0) {
goto invalid_string_error;
diff --git a/Modules/_io/bytesio.c b/Modules/_io/bytesio.c
index 4cde5f87032..61cfec435fe 100644
--- a/Modules/_io/bytesio.c
+++ b/Modules/_io/bytesio.c
@@ -607,9 +607,9 @@ _io_BytesIO_readinto_impl(bytesio *self, Py_buffer *buffer)
len = 0;
}
- memcpy(buffer->buf, PyBytes_AS_STRING(self->buf) + self->pos, len);
assert(self->pos + len < PY_SSIZE_T_MAX);
assert(len >= 0);
+ memcpy(buffer->buf, PyBytes_AS_STRING(self->buf) + self->pos, len);
self->pos += len;
return PyLong_FromSsize_t(len);
diff --git a/Modules/_remote_debugging_module.c b/Modules/_remote_debugging_module.c
index 9314ddd9bed..42db93bb5ea 100644
--- a/Modules/_remote_debugging_module.c
+++ b/Modules/_remote_debugging_module.c
@@ -1765,7 +1765,7 @@ result_err:
static PyMethodDef methods[] = {
{"get_stack_trace", get_stack_trace, METH_VARARGS,
- "Get the Python stack from a given pod"},
+ "Get the Python stack from a given pid"},
{"get_async_stack_trace", get_async_stack_trace, METH_VARARGS,
"Get the asyncio stack from a given pid"},
{"get_all_awaited_by", get_all_awaited_by, METH_VARARGS,
diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c
index 9776a32755d..77286ed2a74 100644
--- a/Modules/_threadmodule.c
+++ b/Modules/_threadmodule.c
@@ -19,8 +19,6 @@
# include <signal.h> // SIGINT
#endif
-#include "clinic/_threadmodule.c.h"
-
// ThreadError is just an alias to PyExc_RuntimeError
#define ThreadError PyExc_RuntimeError
@@ -31,6 +29,7 @@ static struct PyModuleDef thread_module;
typedef struct {
PyTypeObject *excepthook_type;
PyTypeObject *lock_type;
+ PyTypeObject *rlock_type;
PyTypeObject *local_type;
PyTypeObject *local_dummy_type;
PyTypeObject *thread_handle_type;
@@ -48,6 +47,17 @@ get_thread_state(PyObject *module)
return (thread_module_state *)state;
}
+static inline thread_module_state*
+get_thread_state_by_cls(PyTypeObject *cls)
+{
+ // Use PyType_GetModuleByDef() to handle (R)Lock subclasses.
+ PyObject *module = PyType_GetModuleByDef(cls, &thread_module);
+ if (module == NULL) {
+ return NULL;
+ }
+ return get_thread_state(module);
+}
+
#ifdef MS_WINDOWS
typedef HRESULT (WINAPI *PF_GET_THREAD_DESCRIPTION)(HANDLE, PCWSTR*);
@@ -59,9 +69,14 @@ static PF_SET_THREAD_DESCRIPTION pSetThreadDescription = NULL;
/*[clinic input]
module _thread
+class _thread.lock "lockobject *" "clinic_state()->lock_type"
+class _thread.RLock "rlockobject *" "clinic_state()->rlock_type"
[clinic start generated code]*/
-/*[clinic end generated code: output=da39a3ee5e6b4b0d input=be8dbe5cc4b16df7]*/
+/*[clinic end generated code: output=da39a3ee5e6b4b0d input=c5a0f8c492a0c263]*/
+#define clinic_state() get_thread_state_by_cls(type)
+#include "clinic/_threadmodule.c.h"
+#undef clinic_state
// _ThreadHandle type
@@ -916,25 +931,21 @@ lock__at_fork_reinit(PyObject *op, PyObject *Py_UNUSED(dummy))
}
#endif /* HAVE_FORK */
-static lockobject *newlockobject(PyObject *module);
+/*[clinic input]
+@classmethod
+_thread.lock.__new__ as lock_new
+[clinic start generated code]*/
static PyObject *
-lock_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
+lock_new_impl(PyTypeObject *type)
+/*[clinic end generated code: output=eab660d5a4c05c8a input=260208a4e277d250]*/
{
- // convert to AC?
- if (!_PyArg_NoKeywords("lock", kwargs)) {
- goto error;
- }
- if (!_PyArg_CheckPositional("lock", PyTuple_GET_SIZE(args), 0, 0)) {
- goto error;
+ lockobject *self = (lockobject *)type->tp_alloc(type, 0);
+ if (self == NULL) {
+ return NULL;
}
-
- PyObject *module = PyType_GetModuleByDef(type, &thread_module);
- assert(module != NULL);
- return (PyObject *)newlockobject(module);
-
-error:
- return NULL;
+ self->lock = (PyMutex){0};
+ return (PyObject *)self;
}
@@ -1186,8 +1197,14 @@ PyDoc_STRVAR(rlock_is_owned_doc,
\n\
For internal use by `threading.Condition`.");
+/*[clinic input]
+@classmethod
+_thread.RLock.__new__ as rlock_new
+[clinic start generated code]*/
+
static PyObject *
-rlock_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+rlock_new_impl(PyTypeObject *type)
+/*[clinic end generated code: output=bb4fb1edf6818df5 input=013591361bf1ac6e]*/
{
rlockobject *self = (rlockobject *) type->tp_alloc(type, 0);
if (self == NULL) {
@@ -1267,20 +1284,6 @@ static PyType_Spec rlock_type_spec = {
.slots = rlock_type_slots,
};
-static lockobject *
-newlockobject(PyObject *module)
-{
- thread_module_state *state = get_thread_state(module);
-
- PyTypeObject *type = state->lock_type;
- lockobject *self = (lockobject *)type->tp_alloc(type, 0);
- if (self == NULL) {
- return NULL;
- }
- self->lock = (PyMutex){0};
- return self;
-}
-
/* Thread-local objects */
/* Quick overview:
@@ -2035,7 +2038,8 @@ Note: the default signal handler for SIGINT raises ``KeyboardInterrupt``."
static PyObject *
thread_PyThread_allocate_lock(PyObject *module, PyObject *Py_UNUSED(ignored))
{
- return (PyObject *) newlockobject(module);
+ thread_module_state *state = get_thread_state(module);
+ return lock_new_impl(state->lock_type);
}
PyDoc_STRVAR(allocate_lock_doc,
@@ -2645,15 +2649,13 @@ thread_module_exec(PyObject *module)
}
// RLock
- PyTypeObject *rlock_type = (PyTypeObject *)PyType_FromSpec(&rlock_type_spec);
- if (rlock_type == NULL) {
+ state->rlock_type = (PyTypeObject *)PyType_FromModuleAndSpec(module, &rlock_type_spec, NULL);
+ if (state->rlock_type == NULL) {
return -1;
}
- if (PyModule_AddType(module, rlock_type) < 0) {
- Py_DECREF(rlock_type);
+ if (PyModule_AddType(module, state->rlock_type) < 0) {
return -1;
}
- Py_DECREF(rlock_type);
// Local dummy
state->local_dummy_type = (PyTypeObject *)PyType_FromSpec(&local_dummy_type_spec);
@@ -2740,6 +2742,7 @@ thread_module_traverse(PyObject *module, visitproc visit, void *arg)
thread_module_state *state = get_thread_state(module);
Py_VISIT(state->excepthook_type);
Py_VISIT(state->lock_type);
+ Py_VISIT(state->rlock_type);
Py_VISIT(state->local_type);
Py_VISIT(state->local_dummy_type);
Py_VISIT(state->thread_handle_type);
@@ -2752,6 +2755,7 @@ thread_module_clear(PyObject *module)
thread_module_state *state = get_thread_state(module);
Py_CLEAR(state->excepthook_type);
Py_CLEAR(state->lock_type);
+ Py_CLEAR(state->rlock_type);
Py_CLEAR(state->local_type);
Py_CLEAR(state->local_dummy_type);
Py_CLEAR(state->thread_handle_type);
diff --git a/Modules/clinic/_threadmodule.c.h b/Modules/clinic/_threadmodule.c.h
index 8930e54170c..fd8e32a2261 100644
--- a/Modules/clinic/_threadmodule.c.h
+++ b/Modules/clinic/_threadmodule.c.h
@@ -6,7 +6,53 @@ preserve
# include "pycore_gc.h" // PyGC_Head
# include "pycore_runtime.h" // _Py_ID()
#endif
-#include "pycore_modsupport.h" // _PyArg_UnpackKeywords()
+#include "pycore_modsupport.h" // _PyArg_NoKeywords()
+
+static PyObject *
+lock_new_impl(PyTypeObject *type);
+
+static PyObject *
+lock_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
+{
+ PyObject *return_value = NULL;
+ PyTypeObject *base_tp = clinic_state()->lock_type;
+
+ if ((type == base_tp || type->tp_init == base_tp->tp_init) &&
+ !_PyArg_NoPositional("lock", args)) {
+ goto exit;
+ }
+ if ((type == base_tp || type->tp_init == base_tp->tp_init) &&
+ !_PyArg_NoKeywords("lock", kwargs)) {
+ goto exit;
+ }
+ return_value = lock_new_impl(type);
+
+exit:
+ return return_value;
+}
+
+static PyObject *
+rlock_new_impl(PyTypeObject *type);
+
+static PyObject *
+rlock_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
+{
+ PyObject *return_value = NULL;
+ PyTypeObject *base_tp = clinic_state()->rlock_type;
+
+ if ((type == base_tp || type->tp_init == base_tp->tp_init) &&
+ !_PyArg_NoPositional("RLock", args)) {
+ goto exit;
+ }
+ if ((type == base_tp || type->tp_init == base_tp->tp_init) &&
+ !_PyArg_NoKeywords("RLock", kwargs)) {
+ goto exit;
+ }
+ return_value = rlock_new_impl(type);
+
+exit:
+ return return_value;
+}
#if (defined(HAVE_PTHREAD_GETNAME_NP) || defined(HAVE_PTHREAD_GET_NAME_NP) || defined(MS_WINDOWS))
@@ -103,4 +149,4 @@ exit:
#ifndef _THREAD_SET_NAME_METHODDEF
#define _THREAD_SET_NAME_METHODDEF
#endif /* !defined(_THREAD_SET_NAME_METHODDEF) */
-/*[clinic end generated code: output=e978dc4615b9bc35 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=b381ec5e313198e7 input=a9049054013a1b77]*/
diff --git a/PC/pyconfig.h.in b/PC/pyconfig.h
index 1d659e7cee6..0e8379387cd 100644
--- a/PC/pyconfig.h.in
+++ b/PC/pyconfig.h
@@ -99,12 +99,17 @@ WIN32 is still required for the locale module.
# define Py_DEBUG 1
#endif
-/* Define to 1 if you want to disable the GIL */
-/* Uncomment the definition for free-threaded builds, or define it manually
- * when compiling extension modules. Note that we test with #ifdef, so
- * defining as 0 will still disable the GIL. */
-#ifndef Py_GIL_DISABLED
-/* #define Py_GIL_DISABLED 1 */
+/* Define to 1 when compiling for experimental free-threaded builds */
+#ifdef Py_GIL_DISABLED
+/* We undefine if it was set to zero because all later checks are #ifdef.
+ * Note that non-Windows builds do not do this, and so every effort should
+ * be made to avoid defining the variable at all when not desired. However,
+ * sysconfig.get_config_var always returns a 1 or a 0, and so it seems likely
+ * that a build backend will define it with the value.
+ */
+#if Py_GIL_DISABLED == 0
+#undef Py_GIL_DISABLED
+#endif
#endif
/* Compiler specific defines */
diff --git a/PCbuild/_freeze_module.vcxproj b/PCbuild/_freeze_module.vcxproj
index 8c161835af9..efff6a58d89 100644
--- a/PCbuild/_freeze_module.vcxproj
+++ b/PCbuild/_freeze_module.vcxproj
@@ -276,7 +276,7 @@
<ClCompile Include="..\Python\uniqueid.c" />
</ItemGroup>
<ItemGroup>
- <ClInclude Include="..\PC\pyconfig.h.in" />
+ <ClInclude Include="..\PC\pyconfig.h" />
</ItemGroup>
<ItemGroup>
<!-- BEGIN frozen modules -->
@@ -436,31 +436,6 @@
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
- <!-- Direct copy from pythoncore.vcxproj, but this one is only used for our
- own build. All other extension modules will use the copy that pythoncore
- generates. -->
- <Target Name="_UpdatePyconfig" BeforeTargets="PrepareForBuild">
- <MakeDir Directories="$(IntDir)" Condition="!Exists($(IntDir))" />
- <ItemGroup>
- <PyConfigH Remove="@(PyConfigH)" />
- <PyConfigH Include="@(ClInclude)" Condition="'%(Filename)%(Extension)' == 'pyconfig.h.in'" />
- </ItemGroup>
- <Error Text="Did not find pyconfig.h" Condition="@(ClInclude) == ''" />
- <PropertyGroup>
- <PyConfigH>@(PyConfigH->'%(FullPath)', ';')</PyConfigH>
- <PyConfigHText>$([System.IO.File]::ReadAllText($(PyConfigH)))</PyConfigHText>
- <OldPyConfigH Condition="Exists('$(IntDir)pyconfig.h')">$([System.IO.File]::ReadAllText('$(IntDir)pyconfig.h'))</OldPyConfigH>
- </PropertyGroup>
- <PropertyGroup Condition="$(DisableGil) == 'true'">
- <PyConfigHText>$(PyConfigHText.Replace('/* #define Py_GIL_DISABLED 1 */', '#define Py_GIL_DISABLED 1'))</PyConfigHText>
- </PropertyGroup>
- <Message Text="Updating pyconfig.h" Condition="$(PyConfigHText.TrimEnd()) != $(OldPyConfigH.TrimEnd())" />
- <WriteLinesToFile File="$(IntDir)pyconfig.h"
- Lines="$(PyConfigHText)"
- Overwrite="true"
- Condition="$(PyConfigHText.TrimEnd()) != $(OldPyConfigH.TrimEnd())" />
- </Target>
-
<Target Name="_RebuildGetPath" AfterTargets="_RebuildFrozen" Condition="$(Configuration) != 'PGUpdate'">
<Exec Command='"$(TargetPath)" "%(GetPath.ModName)" "%(GetPath.FullPath)" "%(GetPath.IntFile)"' />
diff --git a/PCbuild/pyproject.props b/PCbuild/pyproject.props
index 4e414dc913b..7272542e13a 100644
--- a/PCbuild/pyproject.props
+++ b/PCbuild/pyproject.props
@@ -10,10 +10,9 @@
<Py_IntDir Condition="'$(Py_IntDir)' == ''">$(MSBuildThisFileDirectory)obj\</Py_IntDir>
<IntDir>$(Py_IntDir)\$(MajorVersionNumber)$(MinorVersionNumber)$(ArchName)_$(Configuration)\$(ProjectName)\</IntDir>
<IntDir>$(IntDir.Replace(`\\`, `\`))</IntDir>
- <!-- pyconfig.h is updated by pythoncore.vcxproj, so it's always in pythoncore's IntDir -->
- <GeneratedPyConfigDir>$(Py_IntDir)\$(MajorVersionNumber)$(MinorVersionNumber)$(ArchName)_$(Configuration)\pythoncore\</GeneratedPyConfigDir>
<GeneratedFrozenModulesDir>$(Py_IntDir)\$(MajorVersionNumber)$(MinorVersionNumber)_frozen\</GeneratedFrozenModulesDir>
<GeneratedZlibNgDir>$(Py_IntDir)\$(MajorVersionNumber)$(MinorVersionNumber)$(ArchName)_$(Configuration)\zlib-ng\</GeneratedZlibNgDir>
+ <GeneratedJitStencilsDir>$(Py_IntDir)\$(MajorVersionNumber)$(MinorVersionNumber)_$(Configuration)</GeneratedJitStencilsDir>
<TargetName Condition="'$(TargetName)' == ''">$(ProjectName)</TargetName>
<TargetName>$(TargetName)$(PyDebugExt)</TargetName>
<GenerateManifest>false</GenerateManifest>
@@ -49,11 +48,12 @@
<_PlatformPreprocessorDefinition Condition="$(Platform) == 'x64'">_WIN64;</_PlatformPreprocessorDefinition>
<_PlatformPreprocessorDefinition Condition="$(Platform) == 'x64' and $(PlatformToolset) != 'ClangCL'">_M_X64;$(_PlatformPreprocessorDefinition)</_PlatformPreprocessorDefinition>
<_Py3NamePreprocessorDefinition>PY3_DLLNAME=L"$(Py3DllName)$(PyDebugExt)";</_Py3NamePreprocessorDefinition>
+ <_FreeThreadedPreprocessorDefinition Condition="$(DisableGil) == 'true'">Py_GIL_DISABLED=1;</_FreeThreadedPreprocessorDefinition>
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
- <AdditionalIncludeDirectories>$(PySourcePath)Include;$(PySourcePath)Include\internal;$(PySourcePath)Include\internal\mimalloc;$(GeneratedPyConfigDir);$(PySourcePath)PC;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
- <PreprocessorDefinitions>WIN32;$(_Py3NamePreprocessorDefinition);$(_PlatformPreprocessorDefinition)$(_DebugPreprocessorDefinition)$(_PyStatsPreprocessorDefinition)$(_PydPreprocessorDefinition)%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(PySourcePath)Include;$(PySourcePath)Include\internal;$(PySourcePath)Include\internal\mimalloc;$(PySourcePath)PC;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;$(_Py3NamePreprocessorDefinition)$(_PlatformPreprocessorDefinition)$(_DebugPreprocessorDefinition)$(_PyStatsPreprocessorDefinition)$(_PydPreprocessorDefinition)$(_FreeThreadedPreprocessorDefinition)%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions Condition="'$(SupportPGO)' and ($(Configuration) == 'PGInstrument' or $(Configuration) == 'PGUpdate')">_Py_USING_PGO=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Optimization>MaxSpeed</Optimization>
diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj
index 549d6284972..32a8f2dbad3 100644
--- a/PCbuild/pythoncore.vcxproj
+++ b/PCbuild/pythoncore.vcxproj
@@ -102,6 +102,7 @@
<AdditionalOptions>/Zm200 %(AdditionalOptions)</AdditionalOptions>
<AdditionalIncludeDirectories>$(PySourcePath)Modules\_hacl;$(PySourcePath)Modules\_hacl\include;$(PySourcePath)Python;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories Condition="$(IncludeExternals)">$(zlibNgDir);$(GeneratedZlibNgDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <AdditionalIncludeDirectories Condition="'$(UseJIT)' == 'true'">$(GeneratedJitStencilsDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>_USRDLL;Py_BUILD_CORE;Py_BUILD_CORE_BUILTIN;Py_ENABLE_SHARED;MS_DLL_ID="$(SysWinVer)";ZLIB_COMPAT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions Condition="$(IncludeExternals)">_Py_HAVE_ZLIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions Condition="'$(UseJIT)' == 'true'">_Py_JIT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
@@ -409,7 +410,7 @@
<ClInclude Include="..\Parser\string_parser.h" />
<ClInclude Include="..\Parser\pegen.h" />
<ClInclude Include="..\PC\errmap.h" />
- <ClInclude Include="..\PC\pyconfig.h.in" />
+ <ClInclude Include="..\PC\pyconfig.h" />
<ClInclude Include="..\Python\condvar.h" />
<ClInclude Include="..\Python\stdlib_module_names.h" />
<ClInclude Include="..\Python\thread_nt.h" />
@@ -688,34 +689,6 @@
</ImportGroup>
<Target Name="_TriggerRegen" BeforeTargets="PrepareForBuild" DependsOnTargets="Regen" />
- <Target Name="_UpdatePyconfig" BeforeTargets="PrepareForBuild">
- <MakeDir Directories="$(IntDir)" Condition="!Exists($(IntDir))" />
- <ItemGroup>
- <PyConfigH Remove="@(PyConfigH)" />
- <PyConfigH Include="@(ClInclude)" Condition="'%(Filename)%(Extension)' == 'pyconfig.h.in'" />
- </ItemGroup>
- <Error Text="Did not find pyconfig.h" Condition="@(ClInclude) == ''" />
- <PropertyGroup>
- <PyConfigH>@(PyConfigH->'%(FullPath)', ';')</PyConfigH>
- <PyConfigHText>$([System.IO.File]::ReadAllText($(PyConfigH)))</PyConfigHText>
- <OldPyConfigH Condition="Exists('$(IntDir)pyconfig.h')">$([System.IO.File]::ReadAllText('$(IntDir)pyconfig.h'))</OldPyConfigH>
- </PropertyGroup>
- <PropertyGroup Condition="$(DisableGil) == 'true'">
- <PyConfigHText>$(PyConfigHText.Replace('/* #define Py_GIL_DISABLED 1 */', '#define Py_GIL_DISABLED 1'))</PyConfigHText>
- </PropertyGroup>
- <Message Text="Updating pyconfig.h" Condition="$(PyConfigHText.TrimEnd()) != $(OldPyConfigH.TrimEnd())" />
- <WriteLinesToFile File="$(IntDir)pyconfig.h"
- Lines="$(PyConfigHText)"
- Overwrite="true"
- Condition="$(PyConfigHText.TrimEnd()) != $(OldPyConfigH.TrimEnd())" />
- </Target>
- <Target Name="_CopyPyconfig" Inputs="$(IntDir)pyconfig.h" Outputs="$(OutDir)pyconfig.h" AfterTargets="Build" DependsOnTargets="_UpdatePyconfig">
- <Copy SourceFiles="$(IntDir)pyconfig.h" DestinationFolder="$(OutDir)" />
- </Target>
- <Target Name="_CleanPyconfig" AfterTargets="Clean">
- <Delete Files="$(IntDir)pyconfig.h;$(OutDir)pyconfig.h" />
- </Target>
-
<Target Name="_GetBuildInfo" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<GIT Condition="$(GIT) == ''">git</GIT>
diff --git a/PCbuild/regen.targets b/PCbuild/regen.targets
index 3ad17737807..21de614e71d 100644
--- a/PCbuild/regen.targets
+++ b/PCbuild/regen.targets
@@ -29,12 +29,12 @@
<_KeywordSources Include="$(PySourcePath)Grammar\python.gram;$(PySourcePath)Grammar\Tokens" />
<_KeywordOutputs Include="$(PySourcePath)Lib\keyword.py" />
<!-- Taken from _Target._compute_digest in Tools\jit\_targets.py: -->
- <_JITSources Include="$(PySourcePath)Python\executor_cases.c.h;$(GeneratedPyConfigDir)pyconfig.h;$(PySourcePath)Tools\jit\**"/>
+ <_JITSources Include="$(PySourcePath)Python\executor_cases.c.h;$(PySourcePath)PC\pyconfig.h;$(PySourcePath)Tools\jit\**"/>
<!-- Need to explicitly enumerate these, since globbing doesn't work for missing outputs: -->
- <_JITOutputs Include="$(GeneratedPyConfigDir)jit_stencils.h"/>
- <_JITOutputs Include="$(GeneratedPyConfigDir)jit_stencils-aarch64-pc-windows-msvc.h" Condition="$(Platform) == 'ARM64'"/>
- <_JITOutputs Include="$(GeneratedPyConfigDir)jit_stencils-i686-pc-windows-msvc.h" Condition="$(Platform) == 'Win32'"/>
- <_JITOutputs Include="$(GeneratedPyConfigDir)jit_stencils-x86_64-pc-windows-msvc.h" Condition="$(Platform) == 'x64'"/>
+ <_JITOutputs Include="$(GeneratedJitStencilsDir)jit_stencils.h"/>
+ <_JITOutputs Include="$(GeneratedJitStencilsDir)jit_stencils-aarch64-pc-windows-msvc.h" Condition="$(Platform) == 'ARM64'"/>
+ <_JITOutputs Include="$(GeneratedJitStencilsDir)jit_stencils-i686-pc-windows-msvc.h" Condition="$(Platform) == 'Win32'"/>
+ <_JITOutputs Include="$(GeneratedJitStencilsDir)jit_stencils-x86_64-pc-windows-msvc.h" Condition="$(Platform) == 'x64'"/>
<_CasesSources Include="$(PySourcePath)Python\bytecodes.c;$(PySourcePath)Python\optimizer_bytecodes.c;"/>
<_CasesOutputs Include="$(PySourcePath)Python\generated_cases.c.h;$(PySourcePath)Include\opcode_ids.h;$(PySourcePath)Include\internal\pycore_uop_ids.h;$(PySourcePath)Python\opcode_targets.h;$(PySourcePath)Include\internal\pycore_opcode_metadata.h;$(PySourcePath)Include\internal\pycore_uop_metadata.h;$(PySourcePath)Python\optimizer_cases.c.h;$(PySourcePath)Lib\_opcode_metadata.py"/>
<_SbomSources Include="$(PySourcePath)PCbuild\get_externals.bat" />
@@ -116,7 +116,7 @@
<Target Name="_RegenJIT"
Condition="'$(UseJIT)' == 'true'"
- DependsOnTargets="_UpdatePyconfig;FindPythonForBuild"
+ DependsOnTargets="FindPythonForBuild"
Inputs="@(_JITSources)"
Outputs="@(_JITOutputs)">
<PropertyGroup>
@@ -126,7 +126,7 @@
<JITArgs Condition="$(Configuration) == 'Debug'">$(JITArgs) --debug</JITArgs>
</PropertyGroup>
<Exec Command='$(PythonForBuild) "$(PySourcePath)Tools\jit\build.py" $(JITArgs)'
- WorkingDirectory="$(GeneratedPyConfigDir)"/>
+ WorkingDirectory="$(GeneratedJitStencilsDir)"/>
</Target>
<Target Name="_CleanJIT" AfterTargets="Clean">
<Delete Files="@(_JITOutputs)"/>
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 42e4f581894..477a84b170a 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -4041,6 +4041,11 @@ dummy_func(
DEOPT_IF(!PyStackRef_IsNull(null));
}
+ op(_GUARD_NOS_NOT_NULL, (nos, unused -- nos, unused)) {
+ PyObject *o = PyStackRef_AsPyObjectBorrow(nos);
+ EXIT_IF(o == NULL);
+ }
+
op(_GUARD_THIRD_NULL, (null, unused, unused -- null, unused, unused)) {
DEOPT_IF(!PyStackRef_IsNull(null));
}
@@ -4394,16 +4399,26 @@ dummy_func(
_GUARD_CALLABLE_ISINSTANCE +
_CALL_ISINSTANCE;
+ macro(CALL_LIST_APPEND) =
+ unused/1 +
+ unused/2 +
+ _GUARD_CALLABLE_LIST_APPEND +
+ _GUARD_NOS_NOT_NULL +
+ _GUARD_NOS_LIST +
+ _CALL_LIST_APPEND;
+
+ op(_GUARD_CALLABLE_LIST_APPEND, (callable, unused, unused -- callable, unused, unused)){
+ PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable);
+ PyInterpreterState *interp = tstate->interp;
+ DEOPT_IF(callable_o != interp->callable_cache.list_append);
+ }
+
// This is secretly a super-instruction
- inst(CALL_LIST_APPEND, (unused/1, unused/2, callable, self, arg -- )) {
+ op(_CALL_LIST_APPEND, (callable, self, arg -- )) {
assert(oparg == 1);
- PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable);
PyObject *self_o = PyStackRef_AsPyObjectBorrow(self);
- PyInterpreterState *interp = tstate->interp;
- DEOPT_IF(callable_o != interp->callable_cache.list_append);
- DEOPT_IF(self_o == NULL);
- DEOPT_IF(!PyList_Check(self_o));
+ DEOPT_IF(!PyList_CheckExact(self_o));
DEOPT_IF(!LOCK_OBJECT(self_o));
STAT_INC(CALL, hit);
int err = _PyList_AppendTakeRef((PyListObject *)self_o, PyStackRef_AsPyObjectSteal(arg));
@@ -5327,6 +5342,15 @@ dummy_func(
value = PyStackRef_FromPyObjectImmortal(ptr);
}
+ tier2 pure op(_POP_CALL_TWO_LOAD_CONST_INLINE_BORROW, (ptr/4, callable, null, pop1, pop2 -- value)) {
+ PyStackRef_CLOSE(pop2);
+ PyStackRef_CLOSE(pop1);
+ (void)null; // Silence compiler warnings about unused variables
+ DEAD(null);
+ PyStackRef_CLOSE(callable);
+ value = PyStackRef_FromPyObjectImmortal(ptr);
+ }
+
tier2 op(_CHECK_FUNCTION, (func_version/2 -- )) {
assert(PyStackRef_FunctionCheck(frame->f_funcobj));
PyFunctionObject *func = (PyFunctionObject *)PyStackRef_AsPyObjectBorrow(frame->f_funcobj);
diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h
index 41c9bd5ba70..c3da7a63b9e 100644
--- a/Python/executor_cases.c.h
+++ b/Python/executor_cases.c.h
@@ -5276,6 +5276,17 @@
break;
}
+ case _GUARD_NOS_NOT_NULL: {
+ _PyStackRef nos;
+ nos = stack_pointer[-2];
+ PyObject *o = PyStackRef_AsPyObjectBorrow(nos);
+ if (o == NULL) {
+ UOP_STAT_INC(uopcode, miss);
+ JUMP_TO_JUMP_TARGET();
+ }
+ break;
+ }
+
case _GUARD_THIRD_NULL: {
_PyStackRef null;
null = stack_pointer[-3];
@@ -5920,6 +5931,18 @@
break;
}
+ case _GUARD_CALLABLE_LIST_APPEND: {
+ _PyStackRef callable;
+ callable = stack_pointer[-3];
+ PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable);
+ PyInterpreterState *interp = tstate->interp;
+ if (callable_o != interp->callable_cache.list_append) {
+ UOP_STAT_INC(uopcode, miss);
+ JUMP_TO_JUMP_TARGET();
+ }
+ break;
+ }
+
case _CALL_LIST_APPEND: {
_PyStackRef arg;
_PyStackRef self;
@@ -5929,18 +5952,8 @@
self = stack_pointer[-2];
callable = stack_pointer[-3];
assert(oparg == 1);
- PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable);
PyObject *self_o = PyStackRef_AsPyObjectBorrow(self);
- PyInterpreterState *interp = tstate->interp;
- if (callable_o != interp->callable_cache.list_append) {
- UOP_STAT_INC(uopcode, miss);
- JUMP_TO_JUMP_TARGET();
- }
- if (self_o == NULL) {
- UOP_STAT_INC(uopcode, miss);
- JUMP_TO_JUMP_TARGET();
- }
- if (!PyList_Check(self_o)) {
+ if (!PyList_CheckExact(self_o)) {
UOP_STAT_INC(uopcode, miss);
JUMP_TO_JUMP_TARGET();
}
@@ -7090,6 +7103,40 @@
break;
}
+ case _POP_CALL_TWO_LOAD_CONST_INLINE_BORROW: {
+ _PyStackRef pop2;
+ _PyStackRef pop1;
+ _PyStackRef null;
+ _PyStackRef callable;
+ _PyStackRef value;
+ pop2 = stack_pointer[-1];
+ pop1 = stack_pointer[-2];
+ null = stack_pointer[-3];
+ callable = stack_pointer[-4];
+ PyObject *ptr = (PyObject *)CURRENT_OPERAND0();
+ stack_pointer += -1;
+ assert(WITHIN_STACK_BOUNDS());
+ _PyFrame_SetStackPointer(frame, stack_pointer);
+ PyStackRef_CLOSE(pop2);
+ stack_pointer = _PyFrame_GetStackPointer(frame);
+ stack_pointer += -1;
+ assert(WITHIN_STACK_BOUNDS());
+ _PyFrame_SetStackPointer(frame, stack_pointer);
+ PyStackRef_CLOSE(pop1);
+ stack_pointer = _PyFrame_GetStackPointer(frame);
+ (void)null;
+ stack_pointer += -2;
+ assert(WITHIN_STACK_BOUNDS());
+ _PyFrame_SetStackPointer(frame, stack_pointer);
+ PyStackRef_CLOSE(callable);
+ stack_pointer = _PyFrame_GetStackPointer(frame);
+ value = PyStackRef_FromPyObjectImmortal(ptr);
+ stack_pointer[0] = value;
+ stack_pointer += 1;
+ assert(WITHIN_STACK_BOUNDS());
+ break;
+ }
+
case _CHECK_FUNCTION: {
uint32_t func_version = (uint32_t)CURRENT_OPERAND0();
assert(PyStackRef_FunctionCheck(frame->f_funcobj));
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index b3f2a2067f7..cb9bfcbf2a7 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -3475,58 +3475,79 @@
INSTRUCTION_STATS(CALL_LIST_APPEND);
static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size");
_PyStackRef callable;
+ _PyStackRef nos;
_PyStackRef self;
_PyStackRef arg;
/* Skip 1 cache entry */
/* Skip 2 cache entries */
- arg = stack_pointer[-1];
- self = stack_pointer[-2];
- callable = stack_pointer[-3];
- assert(oparg == 1);
- PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable);
- PyObject *self_o = PyStackRef_AsPyObjectBorrow(self);
- PyInterpreterState *interp = tstate->interp;
- if (callable_o != interp->callable_cache.list_append) {
- UPDATE_MISS_STATS(CALL);
- assert(_PyOpcode_Deopt[opcode] == (CALL));
- JUMP_TO_PREDICTED(CALL);
- }
- if (self_o == NULL) {
- UPDATE_MISS_STATS(CALL);
- assert(_PyOpcode_Deopt[opcode] == (CALL));
- JUMP_TO_PREDICTED(CALL);
- }
- if (!PyList_Check(self_o)) {
- UPDATE_MISS_STATS(CALL);
- assert(_PyOpcode_Deopt[opcode] == (CALL));
- JUMP_TO_PREDICTED(CALL);
- }
- if (!LOCK_OBJECT(self_o)) {
- UPDATE_MISS_STATS(CALL);
- assert(_PyOpcode_Deopt[opcode] == (CALL));
- JUMP_TO_PREDICTED(CALL);
- }
- STAT_INC(CALL, hit);
- int err = _PyList_AppendTakeRef((PyListObject *)self_o, PyStackRef_AsPyObjectSteal(arg));
- UNLOCK_OBJECT(self_o);
- stack_pointer += -2;
- assert(WITHIN_STACK_BOUNDS());
- _PyFrame_SetStackPointer(frame, stack_pointer);
- PyStackRef_CLOSE(self);
- stack_pointer = _PyFrame_GetStackPointer(frame);
- stack_pointer += -1;
- assert(WITHIN_STACK_BOUNDS());
- _PyFrame_SetStackPointer(frame, stack_pointer);
- PyStackRef_CLOSE(callable);
- stack_pointer = _PyFrame_GetStackPointer(frame);
- if (err) {
- JUMP_TO_LABEL(error);
+ // _GUARD_CALLABLE_LIST_APPEND
+ {
+ callable = stack_pointer[-3];
+ PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable);
+ PyInterpreterState *interp = tstate->interp;
+ if (callable_o != interp->callable_cache.list_append) {
+ UPDATE_MISS_STATS(CALL);
+ assert(_PyOpcode_Deopt[opcode] == (CALL));
+ JUMP_TO_PREDICTED(CALL);
+ }
}
- #if TIER_ONE
+ // _GUARD_NOS_NOT_NULL
+ {
+ nos = stack_pointer[-2];
+ PyObject *o = PyStackRef_AsPyObjectBorrow(nos);
+ if (o == NULL) {
+ UPDATE_MISS_STATS(CALL);
+ assert(_PyOpcode_Deopt[opcode] == (CALL));
+ JUMP_TO_PREDICTED(CALL);
+ }
+ }
+ // _GUARD_NOS_LIST
+ {
+ PyObject *o = PyStackRef_AsPyObjectBorrow(nos);
+ if (!PyList_CheckExact(o)) {
+ UPDATE_MISS_STATS(CALL);
+ assert(_PyOpcode_Deopt[opcode] == (CALL));
+ JUMP_TO_PREDICTED(CALL);
+ }
+ }
+ // _CALL_LIST_APPEND
+ {
+ arg = stack_pointer[-1];
+ self = nos;
+ assert(oparg == 1);
+ PyObject *self_o = PyStackRef_AsPyObjectBorrow(self);
+ if (!PyList_CheckExact(self_o)) {
+ UPDATE_MISS_STATS(CALL);
+ assert(_PyOpcode_Deopt[opcode] == (CALL));
+ JUMP_TO_PREDICTED(CALL);
+ }
+ if (!LOCK_OBJECT(self_o)) {
+ UPDATE_MISS_STATS(CALL);
+ assert(_PyOpcode_Deopt[opcode] == (CALL));
+ JUMP_TO_PREDICTED(CALL);
+ }
+ STAT_INC(CALL, hit);
+ int err = _PyList_AppendTakeRef((PyListObject *)self_o, PyStackRef_AsPyObjectSteal(arg));
+ UNLOCK_OBJECT(self_o);
+ stack_pointer += -2;
+ assert(WITHIN_STACK_BOUNDS());
+ _PyFrame_SetStackPointer(frame, stack_pointer);
+ PyStackRef_CLOSE(self);
+ stack_pointer = _PyFrame_GetStackPointer(frame);
+ stack_pointer += -1;
+ assert(WITHIN_STACK_BOUNDS());
+ _PyFrame_SetStackPointer(frame, stack_pointer);
+ PyStackRef_CLOSE(callable);
+ stack_pointer = _PyFrame_GetStackPointer(frame);
+ if (err) {
+ JUMP_TO_LABEL(error);
+ }
+ #if TIER_ONE
- assert(next_instr->op.code == POP_TOP);
- SKIP_OVER(1);
- #endif
+ assert(next_instr->op.code == POP_TOP);
+ SKIP_OVER(1);
+ #endif
+ }
DISPATCH();
}
diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c
index 7c160cdcb0c..9811e9f348c 100644
--- a/Python/optimizer_bytecodes.c
+++ b/Python/optimizer_bytecodes.c
@@ -550,6 +550,10 @@ dummy_func(void) {
value = sym_new_const(ctx, ptr);
}
+ op(_POP_CALL_TWO_LOAD_CONST_INLINE_BORROW, (ptr/4, unused, unused, unused, unused -- value)) {
+ value = sym_new_const(ctx, ptr);
+ }
+
op(_COPY, (bottom, unused[oparg-1] -- bottom, unused[oparg-1], top)) {
assert(oparg > 0);
top = bottom;
@@ -890,6 +894,26 @@ dummy_func(void) {
}
}
+ op(_CALL_ISINSTANCE, (unused, unused, instance, cls -- res)) {
+ // the result is always a bool, but sometimes we can
+ // narrow it down to True or False
+ res = sym_new_type(ctx, &PyBool_Type);
+ PyTypeObject *inst_type = sym_get_type(instance);
+ PyTypeObject *cls_o = (PyTypeObject *)sym_get_const(ctx, cls);
+ if (inst_type && cls_o && sym_matches_type(cls, &PyType_Type)) {
+ // isinstance(inst, cls) where both inst and cls have
+ // known types, meaning we can deduce either True or False
+
+ // The below check is equivalent to PyObject_TypeCheck(inst, cls)
+ PyObject *out = Py_False;
+ if (inst_type == cls_o || PyType_IsSubtype(inst_type, cls_o)) {
+ out = Py_True;
+ }
+ sym_set_const(res, out);
+ REPLACE_OP(this_instr, _POP_CALL_TWO_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)out);
+ }
+ }
+
op(_GUARD_IS_TRUE_POP, (flag -- )) {
if (sym_is_const(ctx, flag)) {
PyObject *value = sym_get_const(ctx, flag);
@@ -1067,6 +1091,13 @@ dummy_func(void) {
sym_set_null(null);
}
+ op(_GUARD_NOS_NOT_NULL, (nos, unused -- nos, unused)) {
+ if (sym_is_not_null(nos)) {
+ REPLACE_OP(this_instr, _NOP, 0, 0);
+ }
+ sym_set_non_null(nos);
+ }
+
op(_GUARD_THIRD_NULL, (null, unused, unused -- null, unused, unused)) {
if (sym_is_null(null)) {
REPLACE_OP(this_instr, _NOP, 0, 0);
@@ -1115,6 +1146,14 @@ dummy_func(void) {
sym_set_const(callable, isinstance);
}
+ op(_GUARD_CALLABLE_LIST_APPEND, (callable, unused, unused -- callable, unused, unused)) {
+ PyObject *list_append = _PyInterpreterState_GET()->callable_cache.list_append;
+ if (sym_get_const(ctx, callable) == list_append) {
+ REPLACE_OP(this_instr, _NOP, 0, 0);
+ }
+ sym_set_const(callable, list_append);
+ }
+
// END BYTECODES //
}
diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h
index deb912662e4..6e19319fbae 100644
--- a/Python/optimizer_cases.c.h
+++ b/Python/optimizer_cases.c.h
@@ -1935,6 +1935,16 @@
break;
}
+ case _GUARD_NOS_NOT_NULL: {
+ JitOptSymbol *nos;
+ nos = stack_pointer[-2];
+ if (sym_is_not_null(nos)) {
+ REPLACE_OP(this_instr, _NOP, 0, 0);
+ }
+ sym_set_non_null(nos);
+ break;
+ }
+
case _GUARD_THIRD_NULL: {
JitOptSymbol *null;
null = stack_pointer[-3];
@@ -2124,14 +2134,39 @@
}
case _CALL_ISINSTANCE: {
+ JitOptSymbol *cls;
+ JitOptSymbol *instance;
JitOptSymbol *res;
- res = sym_new_not_null(ctx);
+ cls = stack_pointer[-1];
+ instance = stack_pointer[-2];
+ res = sym_new_type(ctx, &PyBool_Type);
+ PyTypeObject *inst_type = sym_get_type(instance);
+ PyTypeObject *cls_o = (PyTypeObject *)sym_get_const(ctx, cls);
+ if (inst_type && cls_o && sym_matches_type(cls, &PyType_Type)) {
+ PyObject *out = Py_False;
+ if (inst_type == cls_o || PyType_IsSubtype(inst_type, cls_o)) {
+ out = Py_True;
+ }
+ sym_set_const(res, out);
+ REPLACE_OP(this_instr, _POP_CALL_TWO_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)out);
+ }
stack_pointer[-4] = res;
stack_pointer += -3;
assert(WITHIN_STACK_BOUNDS());
break;
}
+ case _GUARD_CALLABLE_LIST_APPEND: {
+ JitOptSymbol *callable;
+ callable = stack_pointer[-3];
+ PyObject *list_append = _PyInterpreterState_GET()->callable_cache.list_append;
+ if (sym_get_const(ctx, callable) == list_append) {
+ REPLACE_OP(this_instr, _NOP, 0, 0);
+ }
+ sym_set_const(callable, list_append);
+ break;
+ }
+
case _CALL_LIST_APPEND: {
stack_pointer += -3;
assert(WITHIN_STACK_BOUNDS());
@@ -2521,6 +2556,16 @@
break;
}
+ case _POP_CALL_TWO_LOAD_CONST_INLINE_BORROW: {
+ JitOptSymbol *value;
+ PyObject *ptr = (PyObject *)this_instr->operand0;
+ value = sym_new_const(ctx, ptr);
+ stack_pointer[-4] = value;
+ stack_pointer += -3;
+ assert(WITHIN_STACK_BOUNDS());
+ break;
+ }
+
case _CHECK_FUNCTION: {
break;
}
diff --git a/Python/pystate.c b/Python/pystate.c
index 14ae2748b0b..4757a8c3d14 100644
--- a/Python/pystate.c
+++ b/Python/pystate.c
@@ -1393,10 +1393,8 @@ interp_look_up_id(_PyRuntimeState *runtime, int64_t requested_id)
{
PyInterpreterState *interp = runtime->interpreters.head;
while (interp != NULL) {
- int64_t id = PyInterpreterState_GetID(interp);
- if (id < 0) {
- return NULL;
- }
+ int64_t id = interp->id;
+ assert(id >= 0);
if (requested_id == id) {
return interp;
}
diff --git a/Tools/cases_generator/opcode_metadata_generator.py b/Tools/cases_generator/opcode_metadata_generator.py
index 620e4b6f1f4..10567204dcc 100644
--- a/Tools/cases_generator/opcode_metadata_generator.py
+++ b/Tools/cases_generator/opcode_metadata_generator.py
@@ -157,6 +157,13 @@ def generate_deopt_table(analysis: Analysis, out: CWriter) -> None:
if inst.family is not None:
deopt = inst.family.name
deopts.append((inst.name, deopt))
+ defined = set(analysis.opmap.values())
+ for i in range(256):
+ if i not in defined:
+ deopts.append((f'{i}', f'{i}'))
+
+ assert len(deopts) == 256
+ assert len(set(x[0] for x in deopts)) == 256
for name, deopt in sorted(deopts):
out.emit(f"[{name}] = {deopt},\n")
out.emit("};\n\n")
diff --git a/Tools/msi/dev/dev_files.wxs b/Tools/msi/dev/dev_files.wxs
index 4357dc86d9d..21f9c848cc6 100644
--- a/Tools/msi/dev/dev_files.wxs
+++ b/Tools/msi/dev/dev_files.wxs
@@ -3,7 +3,7 @@
<Fragment>
<ComponentGroup Id="dev_pyconfig">
<Component Id="include_pyconfig.h" Directory="include" Guid="*">
- <File Id="include_pyconfig.h" Name="pyconfig.h" Source="pyconfig.h" KeyPath="yes" />
+ <File Id="include_pyconfig.h" Name="pyconfig.h" Source="!(bindpath.src)PC\pyconfig.h" KeyPath="yes" />
</Component>
</ComponentGroup>
</Fragment>
diff --git a/Tools/peg_generator/pegen/build.py b/Tools/peg_generator/pegen/build.py
index 41338c29bdd..be289c352de 100644
--- a/Tools/peg_generator/pegen/build.py
+++ b/Tools/peg_generator/pegen/build.py
@@ -108,6 +108,8 @@ def compile_c_extension(
extra_compile_args.append("-DPy_BUILD_CORE_MODULE")
# Define _Py_TEST_PEGEN to not call PyAST_Validate() in Parser/pegen.c
extra_compile_args.append("-D_Py_TEST_PEGEN")
+ if sys.platform == "win32" and sysconfig.get_config_var("Py_GIL_DISABLED"):
+ extra_compile_args.append("-DPy_GIL_DISABLED")
extra_link_args = get_extra_flags("LDFLAGS", "PY_LDFLAGS_NODIST")
if keep_asserts:
extra_compile_args.append("-UNDEBUG")