diff options
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, µsecond, &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") |