diff --git a/Doc/library/concurrent.futures.rst b/Doc/library/concurrent.futures.rst index 9e81b8d25c99d0..6f8043e6cf7735 100644 --- a/Doc/library/concurrent.futures.rst +++ b/Doc/library/concurrent.futures.rst @@ -101,10 +101,10 @@ Executor Objects executor has started running will be completed prior to this method returning. The remaining futures are cancelled. - You can avoid having to call this method explicitly if you use the - :keyword:`with` statement, which will shutdown the :class:`Executor` - (waiting as if :meth:`Executor.shutdown` were called with *wait* set to - ``True``):: + You can avoid having to call this method explicitly if you use the executor + as a :term:`context manager` via the :keyword:`with` statement, which + will shutdown the :class:`Executor` (waiting as if :meth:`Executor.shutdown` + were called with *wait* set to ``True``):: import shutil with ThreadPoolExecutor(max_workers=4) as e: diff --git a/Doc/library/constants.rst b/Doc/library/constants.rst index c0ac4ea8412ebd..04080fd0d865ec 100644 --- a/Doc/library/constants.rst +++ b/Doc/library/constants.rst @@ -97,15 +97,17 @@ should not be used in programs. exit(code=None) Objects that when printed, print a message like "Use quit() or Ctrl-D - (i.e. EOF) to exit", and when called, raise :exc:`SystemExit` with the + (i.e. EOF) to exit", and when accessed directly in the interactive + interpreter or called as functions, raise :exc:`SystemExit` with the specified exit code. .. data:: help :noindex: Object that when printed, prints the message "Type help() for interactive - help, or help(object) for help about object.", and when called, - acts as described :func:`elsewhere `. + help, or help(object) for help about object.", and when accessed directly + in the interactive interpreter, invokes the built-in help system + (see :func:`help`). .. data:: copyright credits diff --git a/Doc/library/pdb.rst b/Doc/library/pdb.rst index f4b51664545be5..90dc6648045f27 100644 --- a/Doc/library/pdb.rst +++ b/Doc/library/pdb.rst @@ -75,6 +75,7 @@ The debugger's prompt is ``(Pdb)``, which is the indicator that you are in debug arguments of the ``p`` command. +.. _pdb-cli: .. program:: pdb You can also invoke :mod:`pdb` from the command line to debug other scripts. For diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 0738f8e8564e7b..80551048e8336b 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -1204,6 +1204,17 @@ Other language changes .. _Jython: https://www.jython.org/ +* The :func:`int` built-in no longer delegates to :meth:`~object.__trunc__`. + Classes that want to support conversion to :func:`!int` must implement + either :meth:`~object.__int__` or :meth:`~object.__index__`. + (Contributed by Mark Dickinson in :gh:`119743`.) + +* Using :data:`NotImplemented` in a boolean context + will now raise a :exc:`TypeError`. + This has raised a :exc:`DeprecationWarning` since Python 3.9. + (Contributed by Jelle Zijlstra in :gh:`118767`.) + + .. _whatsnew314-pep765: PEP 765: Disallow ``return``/``break``/``continue`` that exit a ``finally`` block @@ -1288,12 +1299,11 @@ asyncio :meth:`asyncio.TaskGroup.create_task`. (Contributed by Thomas Grainger in :gh:`128307`.) - -bdb ---- - -* The :mod:`bdb` module now supports the :mod:`sys.monitoring` backend. - (Contributed by Tian Gao in :gh:`124533`.) +* There are two new utility functions for + introspecting and printing a program's call graph: + :func:`~asyncio.capture_call_graph` and :func:`~asyncio.print_call_graph`. + (Contributed by Yury Selivanov, Pablo Galindo Salgado, and Łukasz Langa + in :gh:`91048`.) .. _whatsnew314-color-calendar: @@ -1521,36 +1531,6 @@ functools (Contributed by Sayandip Dutta in :gh:`125916`.) -gc --- - -The cyclic garbage collector is now incremental, -which changes the meaning of the results of -:meth:`~gc.get_threshold` and :meth:`~gc.set_threshold` -as well as :meth:`~gc.get_count` and :meth:`~gc.get_stats`. - -* For backwards compatibility, :meth:`~gc.get_threshold` continues to return - a three-item tuple. - The first value is the threshold for young collections, as before; - the second value determines the rate at which the old collection is scanned - (the default is 10, and higher values mean that the old collection - is scanned more slowly). - The third value is meaningless and is always zero. - -* :meth:`~gc.set_threshold` ignores any items after the second. - -* :meth:`~gc.get_count` and :meth:`~gc.get_stats` continue to return - the same format of results. - The only difference is that instead of the results referring to - the young, aging and old generations, - the results refer to the young generation - and the aging and collecting spaces of the old generation. - -In summary, code that attempted to manipulate the behavior of the cycle GC -may not work exactly as intended, but it is very unlikely to be harmful. -All other code will work just fine. - - getopt ------ @@ -1919,13 +1899,6 @@ pdb * ``$_asynctask`` is added to access the current asyncio task if applicable. (Contributed by Tian Gao in :gh:`124367`.) -* :mod:`pdb` now supports two backends: :func:`sys.settrace` and - :mod:`sys.monitoring`. Using :mod:`pdb` CLI or :func:`breakpoint` will - always use the :mod:`sys.monitoring` backend. Explicitly instantiating - :class:`pdb.Pdb` and its derived classes will use the :func:`sys.settrace` - backend by default, which is configurable. - (Contributed by Tian Gao in :gh:`124533`.) - * :func:`pdb.set_trace_async` is added to support debugging asyncio coroutines. :keyword:`await` statements are supported with this function. @@ -2266,14 +2239,16 @@ zipinfo .. Add improved modules above alphabetically, not here at the end. + Optimizations ============= * The import time for several standard library modules has been improved, - including :mod:`ast`, :mod:`asyncio`, :mod:`base64`, :mod:`cmd`, :mod:`csv`, - :mod:`gettext`, :mod:`importlib.util`, :mod:`locale`, :mod:`mimetypes`, - :mod:`optparse`, :mod:`pickle`, :mod:`pprint`, :mod:`pstats`, :mod:`socket`, - :mod:`subprocess`, :mod:`threading`, :mod:`tomllib`, and :mod:`zipfile`. + including :mod:`annotationlib`, :mod:`ast`, :mod:`asyncio`, :mod:`base64`, + :mod:`cmd`, :mod:`csv`, :mod:`gettext`, :mod:`importlib.util`, :mod:`locale`, + :mod:`mimetypes`, :mod:`optparse`, :mod:`pickle`, :mod:`pprint`, + :mod:`pstats`, :mod:`shlex`, :mod:`socket`, :mod:`string`, :mod:`subprocess`, + :mod:`threading`, :mod:`tomllib`, :mod:`types`, and :mod:`zipfile`. (Contributed by Adam Turner, Bénédikt Tran, Chris Markiewicz, Eli Schwartz, Hugo van Kemenade, Jelle Zijlstra, and others in :gh:`118761`.) @@ -2282,32 +2257,44 @@ Optimizations asyncio ------- -* :mod:`asyncio` has a new per-thread double linked list implementation internally for - :class:`native tasks ` which speeds up execution by 10-20% on standard - pyperformance benchmarks and reduces memory usage. +* Standard benchmark results have improved by 10-20%, following the + implementation of a new per-thread double linked list + for :class:`native tasks `, + also reducing memory usage. This enables external introspection tools such as :ref:`python -m asyncio pstree ` to introspect the call graph of asyncio tasks running in all threads. (Contributed by Kumar Aditya in :gh:`107803`.) -* :mod:`asyncio` has first class support for :term:`free-threading builds `. - This enables parallel execution of multiple event loops across different threads and scales - linearly with the number of threads. +* The module now has first class support for + :term:`free-threading builds `. + This enables parallel execution of multiple event loops across + different threads, scaling linearly with the number of threads. (Contributed by Kumar Aditya in :gh:`128002`.) -* :mod:`asyncio` has new utility functions for introspecting and printing - the program's call graph: :func:`asyncio.capture_call_graph` and - :func:`asyncio.print_call_graph`. - (Contributed by Yury Selivanov, Pablo Galindo Salgado, and Łukasz Langa - in :gh:`91048`.) - base64 ------ -* Improve the performance of :func:`base64.b16decode` by up to ten times, - and reduce the import time of :mod:`base64` by up to six times. - (Contributed by Bénédikt Tran, Chris Markiewicz, and Adam Turner in :gh:`118761`.) +* :func:`~base64.b16decode` is now up to six times faster. + (Contributed by Bénédikt Tran, Chris Markiewicz, and Adam Turner + in :gh:`118761`.) + + +bdb +--- + +* The basic debugger now has a :mod:`sys.monitoring`-based backend, + which can be selected via the passing ``'monitoring'`` + to the :class:`~bdb.Bdb` class's new *backend* parameter. + (Contributed by Tian Gao in :gh:`124533`.) + + +difflib +------- + +* The :func:`~difflib.IS_LINE_JUNK` function is now up to twice as fast. + (Contributed by Adam Turner and Semyon Moroz in :gh:`130167`.) gc @@ -2316,45 +2303,376 @@ gc * The new :ref:`incremental garbage collector ` means that maximum pause times are reduced by an order of magnitude or more for larger heaps. + + Because of this optimization, the meaning of the results of + :meth:`~gc.get_threshold` and :meth:`~gc.set_threshold` have changed, + along with :meth:`~gc.get_count` and :meth:`~gc.get_stats`. + + - For backwards compatibility, :meth:`~gc.get_threshold` continues to return + a three-item tuple. + The first value is the threshold for young collections, as before; + the second value determines the rate at which the old collection is scanned + (the default is 10, and higher values mean that the old collection + is scanned more slowly). + The third value is now meaningless and is always zero. + + - :meth:`~gc.set_threshold` now ignores any items after the second. + + - :meth:`~gc.get_count` and :meth:`~gc.get_stats` continue to return + the same format of results. + The only difference is that instead of the results referring to + the young, aging and old generations, + the results refer to the young generation + and the aging and collecting spaces of the old generation. + + In summary, code that attempted to manipulate the behavior of the cycle GC + may not work exactly as intended, but it is very unlikely to be harmful. + All other code will work just fine. + (Contributed by Mark Shannon in :gh:`108362`.) io --- -* :mod:`io` which provides the built-in :func:`open` makes less system calls - when opening regular files as well as reading whole files. Reading a small - operating system cached file in full is up to 15% faster. - :func:`pathlib.Path.read_bytes` has the most optimizations for reading a - file's bytes in full. (Contributed by Cody Maloney and Victor Stinner in - :gh:`120754` and :gh:`90102`.) + +* Opening and reading files now executes fewer system calls. + Reading a small operating system cached file in full is up to 15% faster. + (Contributed by Cody Maloney and Victor Stinner + in :gh:`120754` and :gh:`90102`.) + + +pathlib +------- + +* :func:`Path.read_bytes ` now uses unbuffered mode + to open files, which is between 9% and 17% faster to read in full. + (Contributed by Cody Maloney in :gh:`120754`.) + + +pdb +--- + +* :mod:`pdb` now supports two backends, based on either + :func:`sys.settrace` or :mod:`sys.monitoring`. + Using the :ref:`pdb CLI ` or :func:`breakpoint` + will always use the :mod:`sys.monitoring` backend. + Explicitly instantiating :class:`pdb.Pdb` and its derived classes + will use the :func:`sys.settrace` backend by default, which is configurable. + (Contributed by Tian Gao in :gh:`124533`.) uuid ---- -* Improve generation of :class:`~uuid.UUID` objects via their dedicated - functions: - - * :func:`~uuid.uuid3` and :func:`~uuid.uuid5` are both roughly 40% faster - for 16-byte names and 20% faster for 1024-byte names. Performance for - longer names remains unchanged. - * :func:`~uuid.uuid4` is 30% faster. +* :func:`~uuid.uuid3` and :func:`~uuid.uuid5` are now both roughly 40% faster + for 16-byte names and 20% faster for 1024-byte names. + Performance for longer names remains unchanged. + (Contributed by Bénédikt Tran in :gh:`128150`.) +* :func:`~uuid.uuid4` is now c. 30% faster. (Contributed by Bénédikt Tran in :gh:`128150`.) zlib ---- -* On Windows, ``zlib-ng`` is now used as the implementation of the - :mod:`zlib` module. This should produce compatible and comparable - results with better performance, though it is worth noting that - ``zlib.Z_BEST_SPEED`` (1) may result in significantly less - compression than the previous implementation (while also significantly - reducing the time taken to compress). +* On Windows, `zlib-ng `__ + is now used as the implementation of the :mod:`zlib` module + in the default binaries. + There are no known incompatabilities between ``zlib-ng`` + and the previously-used ``zlib`` implementation. + This should result in better performance at all compression levels. + + It is worth noting that ``zlib.Z_BEST_SPEED`` (``1``) may result in + significantly less compression than the previous implementation, + whilst also significantly reducing the time taken to compress. + (Contributed by Steve Dower in :gh:`91349`.) +Removed +======= + +argparse +-------- + +* Remove the *type*, *choices*, and *metavar* parameters + of :class:`!BooleanOptionalAction`. + These have been deprecated since Python 3.12. + (Contributed by Nikita Sobolev in :gh:`118805`.) + +* Calling :meth:`~argparse.ArgumentParser.add_argument_group` + on an argument group now raises a :exc:`ValueError`. + Similarly, :meth:`~argparse.ArgumentParser.add_argument_group` + or :meth:`~argparse.ArgumentParser.add_mutually_exclusive_group` + on a mutually exclusive group now both raise :exc:`ValueError`\ s. + This 'nesting' was never supported, often failed to work correctly, + and was unintentionally exposed through inheritance. + This functionality has been deprecated since Python 3.11. + (Contributed by Savannah Ostrowski in :gh:`127186`.) + + +ast +--- + +* Remove the following classes, which have been deprecated aliases of + :class:`~ast.Constant` since Python 3.8 and have emitted + deprecation warnings since Python 3.12: + + * :class:`!Bytes` + * :class:`!Ellipsis` + * :class:`!NameConstant` + * :class:`!Num` + * :class:`!Str` + + As a consequence of these removals, user-defined ``visit_Num``, ``visit_Str``, + ``visit_Bytes``, ``visit_NameConstant`` and ``visit_Ellipsis`` methods + on custom :class:`~ast.NodeVisitor` subclasses will no longer be called + when the :class:`!NodeVisitor` subclass is visiting an AST. + Define a ``visit_Constant`` method instead. + + (Contributed by Alex Waygood in :gh:`119562`.) + +* Remove the following deprecated properties on :class:`ast.Constant`, + which were present for compatibility with the now-removed AST classes: + + * :attr:`!Constant.n` + * :attr:`!Constant.s` + + Use :attr:`!Constant.value` instead. + (Contributed by Alex Waygood in :gh:`119562`.) + + +asyncio +------- + +* Remove the following classes, methods, and functions, + which have been deprecated since Python 3.12: + + * :class:`!AbstractChildWatcher` + * :class:`!FastChildWatcher` + * :class:`!MultiLoopChildWatcher` + * :class:`!PidfdChildWatcher` + * :class:`!SafeChildWatcher` + * :class:`!ThreadedChildWatcher` + * :meth:`!AbstractEventLoopPolicy.get_child_watcher` + * :meth:`!AbstractEventLoopPolicy.set_child_watcher` + * :func:`!get_child_watcher` + * :func:`!set_child_watcher` + + (Contributed by Kumar Aditya in :gh:`120804`.) + +* :func:`asyncio.get_event_loop` now raises a :exc:`RuntimeError` + if there is no current event loop, + and no longer implicitly creates an event loop. + + (Contributed by Kumar Aditya in :gh:`126353`.) + + .. TODO: move these patterns to the asyncio docs? + quite long for What's New + + There's a few patterns that use :func:`asyncio.get_event_loop`, most + of them can be replaced with :func:`asyncio.run`. + + If you're running an async function, simply use :func:`asyncio.run`. + + Before: + + .. code:: python + + async def main(): + ... + + + loop = asyncio.get_event_loop() + try: + loop.run_until_complete(main()) + finally: + loop.close() + + After: + + .. code:: python + + async def main(): + ... + + asyncio.run(main()) + + If you need to start something, for example, a server listening on a socket + and then run forever, use :func:`asyncio.run` and an + :class:`asyncio.Event`. + + Before: + + .. code:: python + + def start_server(loop): ... + + loop = asyncio.get_event_loop() + try: + start_server(loop) + loop.run_forever() + finally: + loop.close() + + After: + + .. code:: python + + def start_server(loop): ... + + async def main(): + start_server(asyncio.get_running_loop()) + await asyncio.Event().wait() + + asyncio.run(main()) + + If you need to run something in an event loop, then run some blocking + code around it, use :class:`asyncio.Runner`. + + Before: + + .. code:: python + + async def operation_one(): ... + def blocking_code(): ... + async def operation_two(): ... + + loop = asyncio.get_event_loop() + try: + loop.run_until_complete(operation_one()) + blocking_code() + loop.run_until_complete(operation_two()) + finally: + loop.close() + + After: + + .. code:: python + + async def operation_one(): ... + def blocking_code(): ... + async def operation_two(): ... + + with asyncio.Runner() as runner: + runner.run(operation_one()) + blocking_code() + runner.run(operation_two()) + + +collections.abc +--------------- + +* Remove :class:`!ByteString`, which has been deprecated since Python 3.12. + (Contributed by Nikita Sobolev in :gh:`118803`.) + + + +email +----- + +* Remove :func:`email.utils.localtime`'s *isdst* parameter, + which was deprecated in and has been ignored since Python 3.12. + (Contributed by Hugo van Kemenade in :gh:`118798`.) + + +importlib.abc +------------- + +* Remove deprecated :mod:`importlib.abc` classes: + + * :class:`!ResourceReader` + (use :class:`~importlib.resources.abc.TraversableResources`) + * :class:`!Traversable` + (use :class:`~importlib.resources.abc.Traversable`) + * :class:`!TraversableResources` + (use :class:`~importlib.resources.abc.TraversableResources`) + + (Contributed by Jason R. Coombs and Hugo van Kemenade in :gh:`93963`.) + + +itertools +--------- + +* Remove support for copy, deepcopy, and pickle operations + from :mod:`itertools` iterators. + These have emitted a :exc:`DeprecationWarning` since Python 3.12. + (Contributed by Raymond Hettinger in :gh:`101588`.) + + +pathlib +------- + +* Remove support for passing additional keyword arguments + to :class:`~pathlib.Path`. + In previous versions, any such arguments are ignored. + (Contributed by Barney Gale in :gh:`74033`.) + +* Remove support for passing additional positional arguments to + :meth:`.PurePath.relative_to` and :meth:`~pathlib.PurePath.is_relative_to`. + In previous versions, any such arguments are joined onto *other*. + (Contributed by Barney Gale in :gh:`78707`.) + + +pkgutil +------- + +* Remove the :func:`!get_loader` and :func:`!find_loader` functions, + which have been deprecated since Python 3.12. + (Contributed by Bénédikt Tran in :gh:`97850`.) + + +pty +--- + +* Remove the :func:`!master_open` and :func:`!slave_open` functions, + which have been deprecated since Python 3.12. + Use :func:`pty.openpty` instead. + (Contributed by Nikita Sobolev in :gh:`118824`.) + + +sqlite3 +------- + +* Remove :data:`!version` and :data:`!version_info` from + the :mod:`sqlite3` module; + use :data:`~sqlite3.sqlite_version` and :data:`~sqlite3.sqlite_version_info` + for the actual version number of the runtime SQLite library. + (Contributed by Hugo van Kemenade in :gh:`118924`.) + +* Using a sequence of parameters with named placeholders now + raises a :exc:`~sqlite3.ProgrammingError`, + having been deprecated since Python 3.12. + (Contributed by Erlend E. Aasland in :gh:`118928` and :gh:`101693`.) + + +typing +------ + +* Remove :class:`!ByteString`, which has been deprecated since Python 3.12. + (Contributed by Nikita Sobolev in :gh:`118803`.) + + +urllib +------ + +* Remove the :class:`!Quoter` class from :mod:`urllib.parse`, + which has been deprecated since Python 3.11. + (Contributed by Nikita Sobolev in :gh:`118827`.) + +* Remove the :class:`!URLopener` and :class:`!FancyURLopener` classes + from :mod:`urllib.request`, + which have been deprecated since Python 3.3. + + ``myopener.open()`` can be replaced with :func:`~urllib.request.urlopen`. + ``myopener.retrieve()`` can be replaced with + :func:`~urllib.request.urlretrieve`. + Customisations to the opener classes can be replaced by passing + customized handlers to :func:`~urllib.request.build_opener`. + (Contributed by Barney Gale in :gh:`84850`.) + + Deprecated ========== @@ -2494,275 +2812,6 @@ Deprecated .. include:: ../deprecations/pending-removal-in-future.rst -Removed -======= - -argparse --------- - -* Remove the *type*, *choices*, and *metavar* parameters - of :class:`!argparse.BooleanOptionalAction`. - They were deprecated since 3.12. - -* Calling :meth:`~argparse.ArgumentParser.add_argument_group` on an argument - group, and calling :meth:`~argparse.ArgumentParser.add_argument_group` or - :meth:`~argparse.ArgumentParser.add_mutually_exclusive_group` on a mutually - exclusive group now raise exceptions. This nesting was never supported, - often failed to work correctly, and was unintentionally exposed through - inheritance. This functionality has been deprecated since Python 3.11. - (Contributed by Savannah Ostrowski in :gh:`127186`.) - -ast ---- - -* Remove the following classes. They were all deprecated since Python 3.8, - and have emitted deprecation warnings since Python 3.12: - - * :class:`!ast.Bytes` - * :class:`!ast.Ellipsis` - * :class:`!ast.NameConstant` - * :class:`!ast.Num` - * :class:`!ast.Str` - - Use :class:`ast.Constant` instead. As a consequence of these removals, - user-defined ``visit_Num``, ``visit_Str``, ``visit_Bytes``, - ``visit_NameConstant`` and ``visit_Ellipsis`` methods on custom - :class:`ast.NodeVisitor` subclasses will no longer be called when the - :class:`!NodeVisitor` subclass is visiting an AST. Define a ``visit_Constant`` - method instead. - - Also, remove the following deprecated properties on :class:`ast.Constant`, - which were present for compatibility with the now-removed AST classes: - - * :attr:`!ast.Constant.n` - * :attr:`!ast.Constant.s` - - Use :attr:`!ast.Constant.value` instead. - (Contributed by Alex Waygood in :gh:`119562`.) - -asyncio -------- - -* Remove the following classes and functions. They were all deprecated and - emitted deprecation warnings since Python 3.12: - - * :func:`!asyncio.get_child_watcher` - * :func:`!asyncio.set_child_watcher` - * :meth:`!asyncio.AbstractEventLoopPolicy.get_child_watcher` - * :meth:`!asyncio.AbstractEventLoopPolicy.set_child_watcher` - * :class:`!asyncio.AbstractChildWatcher` - * :class:`!asyncio.FastChildWatcher` - * :class:`!asyncio.MultiLoopChildWatcher` - * :class:`!asyncio.PidfdChildWatcher` - * :class:`!asyncio.SafeChildWatcher` - * :class:`!asyncio.ThreadedChildWatcher` - - (Contributed by Kumar Aditya in :gh:`120804`.) - -* Removed implicit creation of event loop by :func:`asyncio.get_event_loop`. - It now raises a :exc:`RuntimeError` if there is no current event loop. - (Contributed by Kumar Aditya in :gh:`126353`.) - - There's a few patterns that use :func:`asyncio.get_event_loop`, most - of them can be replaced with :func:`asyncio.run`. - - If you're running an async function, simply use :func:`asyncio.run`. - - Before:: - - async def main(): - ... - - - loop = asyncio.get_event_loop() - try: - loop.run_until_complete(main()) - finally: - loop.close() - - After:: - - async def main(): - ... - - asyncio.run(main()) - - If you need to start something, for example, a server listening on a socket - and then run forever, use :func:`asyncio.run` and an - :class:`asyncio.Event`. - - Before:: - - def start_server(loop): - ... - - loop = asyncio.get_event_loop() - try: - start_server(loop) - loop.run_forever() - finally: - loop.close() - - After:: - - def start_server(loop): - ... - - async def main(): - start_server(asyncio.get_running_loop()) - await asyncio.Event().wait() - - asyncio.run(main()) - - If you need to run something in an event loop, then run some blocking - code around it, use :class:`asyncio.Runner`. - - Before:: - - async def operation_one(): - ... - - def blocking_code(): - ... - - async def operation_two(): - ... - - loop = asyncio.get_event_loop() - try: - loop.run_until_complete(operation_one()) - blocking_code() - loop.run_until_complete(operation_two()) - finally: - loop.close() - - After:: - - async def operation_one(): - ... - - def blocking_code(): - ... - - async def operation_two(): - ... - - with asyncio.Runner() as runner: - runner.run(operation_one()) - blocking_code() - runner.run(operation_two()) - - - -collections.abc ---------------- - -* Remove :class:`!collections.abc.ByteString`. It had previously raised a - :exc:`DeprecationWarning` since Python 3.12. - -email ------ - -* Remove the *isdst* parameter from :func:`email.utils.localtime`. - (Contributed by Hugo van Kemenade in :gh:`118798`.) - -importlib ---------- - -* Remove deprecated :mod:`importlib.abc` classes: - - * :class:`!importlib.abc.ResourceReader` - * :class:`!importlib.abc.Traversable` - * :class:`!importlib.abc.TraversableResources` - - Use :mod:`importlib.resources.abc` classes instead: - - * :class:`importlib.resources.abc.Traversable` - * :class:`importlib.resources.abc.TraversableResources` - - (Contributed by Jason R. Coombs and Hugo van Kemenade in :gh:`93963`.) - -itertools ---------- - -* Remove :mod:`itertools` support for copy, deepcopy, and pickle operations. - These had previously raised a :exc:`DeprecationWarning` since Python 3.12. - (Contributed by Raymond Hettinger in :gh:`101588`.) - -pathlib -------- - -* Remove support for passing additional keyword arguments to - :class:`pathlib.Path`. In previous versions, any such arguments are ignored. -* Remove support for passing additional positional arguments to - :meth:`pathlib.PurePath.relative_to` and - :meth:`~pathlib.PurePath.is_relative_to`. In previous versions, any such - arguments are joined onto *other*. - -pkgutil -------- - -* Remove deprecated :func:`!pkgutil.get_loader` and :func:`!pkgutil.find_loader`. - These had previously raised a :exc:`DeprecationWarning` since Python 3.12. - (Contributed by Bénédikt Tran in :gh:`97850`.) - -pty ---- - -* Remove deprecated :func:`!pty.master_open` and :func:`!pty.slave_open`. - They had previously raised a :exc:`DeprecationWarning` since Python 3.12. - Use :func:`pty.openpty` instead. - (Contributed by Nikita Sobolev in :gh:`118824`.) - -sqlite3 -------- - -* Remove :data:`!version` and :data:`!version_info` from :mod:`sqlite3`; - use :data:`~sqlite3.sqlite_version` and :data:`~sqlite3.sqlite_version_info` - for the actual version number of the runtime SQLite library. - (Contributed by Hugo van Kemenade in :gh:`118924`.) - -* Disallow using a sequence of parameters with named placeholders. - This had previously raised a :exc:`DeprecationWarning` since Python 3.12; - it will now raise a :exc:`sqlite3.ProgrammingError`. - (Contributed by Erlend E. Aasland in :gh:`118928` and :gh:`101693`.) - -typing ------- - -* Remove :class:`!typing.ByteString`. It had previously raised a - :exc:`DeprecationWarning` since Python 3.12. - - -urllib ------- - -* Remove deprecated :class:`!Quoter` class from :mod:`urllib.parse`. - It had previously raised a :exc:`DeprecationWarning` since Python 3.11. - (Contributed by Nikita Sobolev in :gh:`118827`.) -* Remove deprecated :class:`!URLopener` and :class:`!FancyURLopener` classes - from :mod:`urllib.request`. They had previously raised a - :exc:`DeprecationWarning` since Python 3.3. - - ``myopener.open()`` can be replaced with :func:`~urllib.request.urlopen`, - and ``myopener.retrieve()`` can be replaced with - :func:`~urllib.request.urlretrieve`. Customizations to the opener - classes can be replaced by passing customized handlers to - :func:`~urllib.request.build_opener`. - (Contributed by Barney Gale in :gh:`84850`.) - -Others ------- - -* Using :data:`NotImplemented` in a boolean context will now raise a :exc:`TypeError`. - It had previously raised a :exc:`DeprecationWarning` since Python 3.9. (Contributed - by Jelle Zijlstra in :gh:`118767`.) - -* The :func:`int` built-in no longer delegates to - :meth:`~object.__trunc__`. Classes that want to support conversion to - integer must implement either :meth:`~object.__int__` or - :meth:`~object.__index__`. (Contributed by Mark Dickinson in :gh:`119743`.) - CPython bytecode changes ======================== diff --git a/Grammar/python.gram b/Grammar/python.gram index ff54e42111005a..d36d55183ce629 100644 --- a/Grammar/python.gram +++ b/Grammar/python.gram @@ -94,10 +94,12 @@ func_type[mod_ty]: '(' a=[type_expressions] ')' '->' b=expression NEWLINE* ENDMA # GENERAL STATEMENTS # ================== -statements[asdl_stmt_seq*]: a=statement+ { _PyPegen_register_stmts(p, (asdl_stmt_seq*)_PyPegen_seq_flatten(p, a)) } +statements[asdl_stmt_seq*]: a=statement+ { (asdl_stmt_seq*)_PyPegen_seq_flatten(p, a) } statement[asdl_stmt_seq*]: - | a=compound_stmt { (asdl_stmt_seq*)_PyPegen_singleton_seq(p, a) } + | a=compound_stmt { _PyPegen_register_stmts(p , + (asdl_stmt_seq*)_PyPegen_singleton_seq(p, a) + ) } | a[asdl_stmt_seq*]=simple_stmts { a } single_compound_stmt[asdl_stmt_seq*]: diff --git a/Include/internal/pycore_global_objects_fini_generated.h b/Include/internal/pycore_global_objects_fini_generated.h index 672537bfedc9af..6c8ce475c69842 100644 --- a/Include/internal/pycore_global_objects_fini_generated.h +++ b/Include/internal/pycore_global_objects_fini_generated.h @@ -818,6 +818,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(bit_offset)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(bit_size)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(block)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(blocking)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(bound)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(buffer)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(buffer_callback)); diff --git a/Include/internal/pycore_global_strings.h b/Include/internal/pycore_global_strings.h index ed767206ee7322..55e48214728153 100644 --- a/Include/internal/pycore_global_strings.h +++ b/Include/internal/pycore_global_strings.h @@ -309,6 +309,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(bit_offset) STRUCT_FOR_ID(bit_size) STRUCT_FOR_ID(block) + STRUCT_FOR_ID(blocking) STRUCT_FOR_ID(bound) STRUCT_FOR_ID(buffer) STRUCT_FOR_ID(buffer_callback) diff --git a/Include/internal/pycore_runtime_init_generated.h b/Include/internal/pycore_runtime_init_generated.h index 2f24a138fdbf0c..ea188d4f2bf8ed 100644 --- a/Include/internal/pycore_runtime_init_generated.h +++ b/Include/internal/pycore_runtime_init_generated.h @@ -816,6 +816,7 @@ extern "C" { INIT_ID(bit_offset), \ INIT_ID(bit_size), \ INIT_ID(block), \ + INIT_ID(blocking), \ INIT_ID(bound), \ INIT_ID(buffer), \ INIT_ID(buffer_callback), \ diff --git a/Include/internal/pycore_unicodeobject_generated.h b/Include/internal/pycore_unicodeobject_generated.h index 4f8866e729c51e..044fdafef246ed 100644 --- a/Include/internal/pycore_unicodeobject_generated.h +++ b/Include/internal/pycore_unicodeobject_generated.h @@ -1024,6 +1024,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); assert(PyUnicode_GET_LENGTH(string) != 1); + string = &_Py_ID(blocking); + _PyUnicode_InternStatic(interp, &string); + assert(_PyUnicode_CheckConsistency(string, 1)); + assert(PyUnicode_GET_LENGTH(string) != 1); string = &_Py_ID(bound); _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index 11b7f419bddbe4..d45b3b96d2a85f 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -19,7 +19,7 @@ requires_debug_ranges, has_no_debug_ranges, requires_subprocess) from test.support.os_helper import TESTFN, unlink -from test.support.script_helper import assert_python_ok, assert_python_failure +from test.support.script_helper import assert_python_ok, assert_python_failure, make_script from test.support.import_helper import forget from test.support import force_not_colorized, force_not_colorized_test_class @@ -1740,6 +1740,49 @@ def f(): ] self.assertEqual(result_lines, expected) +class TestKeywordTypoSuggestions(unittest.TestCase): + TYPO_CASES = [ + ("with block ad something:\n pass", "and"), + ("fur a in b:\n pass", "for"), + ("for a in b:\n pass\nelso:\n pass", "else"), + ("whille True:\n pass", "while"), + ("iff x > 5:\n pass", "if"), + ("if x:\n pass\nelseif y:\n pass", "elif"), + ("tyo:\n pass\nexcept y:\n pass", "try"), + ("classe MyClass:\n pass", "class"), + ("impor math", "import"), + ("form x import y", "from"), + ("defn calculate_sum(a, b):\n return a + b", "def"), + ("def foo():\n returm result", "return"), + ("lamda x: x ** 2", "lambda"), + ("def foo():\n yeld i", "yield"), + ("def foo():\n globel counter", "global"), + ("frum math import sqrt", "from"), + ("asynch def fetch_data():\n pass", "async"), + ("async def foo():\n awaid fetch_data()", "await"), + ('raisee ValueError("Error")', "raise"), + ("[x for x\nin range(3)\nof x]", "if"), + ("[123 fur x\nin range(3)\nif x]", "for"), + ("for x im n:\n pass", "in"), + ] + + def test_keyword_suggestions_from_file(self): + with tempfile.TemporaryDirectory() as script_dir: + for i, (code, expected_kw) in enumerate(self.TYPO_CASES): + with self.subTest(typo=expected_kw): + source = textwrap.dedent(code).strip() + script_name = make_script(script_dir, f"script_{i}", source) + rc, stdout, stderr = assert_python_failure(script_name) + stderr_text = stderr.decode('utf-8') + self.assertIn(f"Did you mean '{expected_kw}'", stderr_text) + + def test_keyword_suggestions_from_command_string(self): + for code, expected_kw in self.TYPO_CASES: + with self.subTest(typo=expected_kw): + source = textwrap.dedent(code).strip() + rc, stdout, stderr = assert_python_failure('-c', source) + stderr_text = stderr.decode('utf-8') + self.assertIn(f"Did you mean '{expected_kw}'", stderr_text) @requires_debug_ranges() @force_not_colorized_test_class diff --git a/Lib/traceback.py b/Lib/traceback.py index 1fe295add3a6dd..9d40b1df93c645 100644 --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -1322,7 +1322,6 @@ def _find_keyword_typos(self): lines = source.splitlines() error_code = lines[line -1 if line > 0 else 0:end_line] - error_code[0] = error_code[0][offset:] error_code = textwrap.dedent('\n'.join(error_code)) # Do not continue if the source is too large @@ -1338,7 +1337,8 @@ def _find_keyword_typos(self): if token.type != tokenize.NAME: continue # Only consider NAME tokens on the same line as the error - if from_filename and token.start[0]+line != end_line+1: + the_end = end_line if line == 0 else end_line + 1 + if from_filename and token.start[0]+line != the_end: continue wrong_name = token.string if wrong_name in keyword.kwlist: diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-08-15-15-45-26.gh-issue-137079.YEow69.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-08-15-15-45-26.gh-issue-137079.YEow69.rst new file mode 100644 index 00000000000000..5f01a23484594a --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-08-15-15-45-26.gh-issue-137079.YEow69.rst @@ -0,0 +1 @@ +Fix keyword typo recognition when parsing files. Patch by Pablo Galindo. diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c index f82ad6870f850f..0a22907375b0dd 100644 --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -38,6 +38,20 @@ typedef struct { struct llist_node shutdown_handles; } thread_module_state; +typedef struct { + PyObject_HEAD + PyMutex lock; +} lockobject; + +#define lockobject_CAST(op) ((lockobject *)(op)) + +typedef struct { + PyObject_HEAD + _PyRecursiveMutex lock; +} rlockobject; + +#define rlockobject_CAST(op) ((rlockobject *)(op)) + static inline thread_module_state* get_thread_state(PyObject *module) { @@ -753,13 +767,6 @@ static PyType_Spec ThreadHandle_Type_spec = { /* Lock objects */ -typedef struct { - PyObject_HEAD - PyMutex lock; -} lockobject; - -#define lockobject_CAST(op) ((lockobject *)(op)) - static int lock_traverse(PyObject *self, visitproc visit, void *arg) { @@ -779,16 +786,8 @@ lock_dealloc(PyObject *self) static int -lock_acquire_parse_args(PyObject *args, PyObject *kwds, - PyTime_t *timeout) +lock_acquire_parse_timeout(PyObject *timeout_obj, int blocking, PyTime_t *timeout) { - char *kwlist[] = {"blocking", "timeout", NULL}; - int blocking = 1; - PyObject *timeout_obj = NULL; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|pO:acquire", kwlist, - &blocking, &timeout_obj)) - return -1; - // XXX Use PyThread_ParseTimeoutArg(). const PyTime_t unset_timeout = _PyTime_FromSeconds(-1); @@ -823,14 +822,29 @@ lock_acquire_parse_args(PyObject *args, PyObject *kwds, } return 0; } +/*[clinic input] +_thread.lock.acquire + blocking: bool = True + timeout as timeoutobj: object(py_default="-1") = NULL + +Lock the lock. + +Without argument, this blocks if the lock is already +locked (even by the same thread), waiting for another thread to release +the lock, and return True once the lock is acquired. +With an argument, this will only block if the argument is true, +and the return value reflects whether the lock is acquired. +The blocking operation is interruptible. +[clinic start generated code]*/ static PyObject * -lock_PyThread_acquire_lock(PyObject *op, PyObject *args, PyObject *kwds) +_thread_lock_acquire_impl(lockobject *self, int blocking, + PyObject *timeoutobj) +/*[clinic end generated code: output=569d6b25d508bf6f input=13e999649bc1c798]*/ { - lockobject *self = lockobject_CAST(op); - PyTime_t timeout; - if (lock_acquire_parse_args(args, kwds, &timeout) < 0) { + + if (lock_acquire_parse_timeout(timeoutobj, blocking, &timeout) < 0) { return NULL; } @@ -848,33 +862,34 @@ lock_PyThread_acquire_lock(PyObject *op, PyObject *args, PyObject *kwds) return PyBool_FromLong(r == PY_LOCK_ACQUIRED); } -PyDoc_STRVAR(acquire_doc, -"acquire($self, /, blocking=True, timeout=-1)\n\ ---\n\ -\n\ -Lock the lock. Without argument, this blocks if the lock is already\n\ -locked (even by the same thread), waiting for another thread to release\n\ -the lock, and return True once the lock is acquired.\n\ -With an argument, this will only block if the argument is true,\n\ -and the return value reflects whether the lock is acquired.\n\ -The blocking operation is interruptible."); - -PyDoc_STRVAR(acquire_lock_doc, -"acquire_lock($self, /, blocking=True, timeout=-1)\n\ ---\n\ -\n\ -An obsolete synonym of acquire()."); +/*[clinic input] +_thread.lock.acquire_lock = _thread.lock.acquire -PyDoc_STRVAR(enter_doc, -"__enter__($self, /)\n\ ---\n\ -\n\ -Lock the lock."); +An obsolete synonym of acquire(). +[clinic start generated code]*/ static PyObject * -lock_PyThread_release_lock(PyObject *op, PyObject *Py_UNUSED(dummy)) +_thread_lock_acquire_lock_impl(lockobject *self, int blocking, + PyObject *timeoutobj) +/*[clinic end generated code: output=ea6c87ea13b56694 input=5e65bd56327ebe85]*/ +{ + return _thread_lock_acquire_impl(self, blocking, timeoutobj); +} + +/*[clinic input] +_thread.lock.release + +Release the lock. + +Allows another thread that is blocked waiting for +the lock to acquire the lock. The lock must be in the locked state, +but it needn't be locked by the same thread that unlocks it. +[clinic start generated code]*/ + +static PyObject * +_thread_lock_release_impl(lockobject *self) +/*[clinic end generated code: output=a4ab0d75d6e9fb73 input=dfe48f962dfe99b4]*/ { - lockobject *self = lockobject_CAST(op); /* Sanity check: the lock must be locked */ if (_PyMutex_TryUnlock(&self->lock) < 0) { PyErr_SetString(ThreadError, "release unlocked lock"); @@ -884,44 +899,76 @@ lock_PyThread_release_lock(PyObject *op, PyObject *Py_UNUSED(dummy)) Py_RETURN_NONE; } -PyDoc_STRVAR(release_doc, -"release($self, /)\n\ ---\n\ -\n\ -Release the lock, allowing another thread that is blocked waiting for\n\ -the lock to acquire the lock. The lock must be in the locked state,\n\ -but it needn't be locked by the same thread that unlocks it."); +/*[clinic input] +_thread.lock.release_lock -PyDoc_STRVAR(release_lock_doc, -"release_lock($self, /)\n\ ---\n\ -\n\ -An obsolete synonym of release()."); +An obsolete synonym of release(). +[clinic start generated code]*/ -PyDoc_STRVAR(lock_exit_doc, -"__exit__($self, /, *exc_info)\n\ ---\n\ -\n\ -Release the lock."); +static PyObject * +_thread_lock_release_lock_impl(lockobject *self) +/*[clinic end generated code: output=43025044d51789bb input=74d91374fc601433]*/ +{ + return _thread_lock_release_impl(self); +} + +/*[clinic input] +_thread.lock.__enter__ + +Lock the lock. +[clinic start generated code]*/ static PyObject * -lock_locked_lock(PyObject *op, PyObject *Py_UNUSED(dummy)) +_thread_lock___enter___impl(lockobject *self) +/*[clinic end generated code: output=f27725de751ae064 input=8f982991608d38e7]*/ +{ + return _thread_lock_acquire_impl(self, 1, NULL); +} + +/*[clinic input] +_thread.lock.__exit__ + exc_type: object + exc_value: object + exc_tb: object + / + +Release the lock. +[clinic start generated code]*/ + +static PyObject * +_thread_lock___exit___impl(lockobject *self, PyObject *exc_type, + PyObject *exc_value, PyObject *exc_tb) +/*[clinic end generated code: output=c9e8eefa69beed07 input=c74d4abe15a6c037]*/ +{ + return _thread_lock_release_impl(self); +} + + +/*[clinic input] +_thread.lock.locked + +Return whether the lock is in the locked state. +[clinic start generated code]*/ + +static PyObject * +_thread_lock_locked_impl(lockobject *self) +/*[clinic end generated code: output=63bb94e5a9efa382 input=d8e3d64861bbce73]*/ { - lockobject *self = lockobject_CAST(op); return PyBool_FromLong(PyMutex_IsLocked(&self->lock)); } -PyDoc_STRVAR(locked_doc, -"locked($self, /)\n\ ---\n\ -\n\ -Return whether the lock is in the locked state."); +/*[clinic input] +_thread.lock.locked_lock -PyDoc_STRVAR(locked_lock_doc, -"locked_lock($self, /)\n\ ---\n\ -\n\ -An obsolete synonym of locked()."); +An obsolete synonym of locked(). +[clinic start generated code]*/ + +static PyObject * +_thread_lock_locked_lock_impl(lockobject *self) +/*[clinic end generated code: output=f747c8329e905f8e input=500b0c3592f9bf84]*/ +{ + return _thread_lock_locked_impl(self); +} static PyObject * lock_repr(PyObject *op) @@ -932,10 +979,14 @@ lock_repr(PyObject *op) } #ifdef HAVE_FORK +/*[clinic input] +_thread.lock._at_fork_reinit +[clinic start generated code]*/ + static PyObject * -lock__at_fork_reinit(PyObject *op, PyObject *Py_UNUSED(dummy)) +_thread_lock__at_fork_reinit_impl(lockobject *self) +/*[clinic end generated code: output=d8609f2d3bfa1fd5 input=a970cb76e2a0a131]*/ { - lockobject *self = lockobject_CAST(op); _PyMutex_at_fork_reinit(&self->lock); Py_RETURN_NONE; } @@ -960,25 +1011,16 @@ lock_new_impl(PyTypeObject *type) static PyMethodDef lock_methods[] = { - {"acquire_lock", _PyCFunction_CAST(lock_PyThread_acquire_lock), - METH_VARARGS | METH_KEYWORDS, acquire_lock_doc}, - {"acquire", _PyCFunction_CAST(lock_PyThread_acquire_lock), - METH_VARARGS | METH_KEYWORDS, acquire_doc}, - {"release_lock", lock_PyThread_release_lock, - METH_NOARGS, release_lock_doc}, - {"release", lock_PyThread_release_lock, - METH_NOARGS, release_doc}, - {"locked_lock", lock_locked_lock, - METH_NOARGS, locked_lock_doc}, - {"locked", lock_locked_lock, - METH_NOARGS, locked_doc}, - {"__enter__", _PyCFunction_CAST(lock_PyThread_acquire_lock), - METH_VARARGS | METH_KEYWORDS, enter_doc}, - {"__exit__", lock_PyThread_release_lock, - METH_VARARGS, lock_exit_doc}, + _THREAD_LOCK_ACQUIRE_LOCK_METHODDEF + _THREAD_LOCK_ACQUIRE_METHODDEF + _THREAD_LOCK_RELEASE_LOCK_METHODDEF + _THREAD_LOCK_RELEASE_METHODDEF + _THREAD_LOCK_LOCKED_LOCK_METHODDEF + _THREAD_LOCK_LOCKED_METHODDEF + _THREAD_LOCK___ENTER___METHODDEF + _THREAD_LOCK___EXIT___METHODDEF #ifdef HAVE_FORK - {"_at_fork_reinit", lock__at_fork_reinit, - METH_NOARGS, NULL}, + _THREAD_LOCK__AT_FORK_REINIT_METHODDEF #endif {NULL, NULL} /* sentinel */ }; @@ -1018,13 +1060,6 @@ static PyType_Spec lock_type_spec = { /* Recursive lock objects */ -typedef struct { - PyObject_HEAD - _PyRecursiveMutex lock; -} rlockobject; - -#define rlockobject_CAST(op) ((rlockobject *)(op)) - static int rlock_traverse(PyObject *self, visitproc visit, void *arg) { @@ -1048,14 +1083,35 @@ rlock_dealloc(PyObject *self) Py_DECREF(tp); } +/*[clinic input] +_thread.RLock.acquire + blocking: bool = True + timeout as timeoutobj: object(py_default="-1") = NULL + +Lock the lock. + +`blocking` indicates whether we should wait +for the lock to be available or not. If `blocking` is False +and another thread holds the lock, the method will return False +immediately. If `blocking` is True and another thread holds +the lock, the method will wait for the lock to be released, +take it and then return True. +(note: the blocking operation is interruptible.) + +In all other cases, the method will return True immediately. +Precisely, if the current thread already holds the lock, its +internal counter is simply incremented. If nobody holds the lock, +the lock is taken and its internal counter initialized to 1. +[clinic start generated code]*/ static PyObject * -rlock_acquire(PyObject *op, PyObject *args, PyObject *kwds) +_thread_RLock_acquire_impl(rlockobject *self, int blocking, + PyObject *timeoutobj) +/*[clinic end generated code: output=73df5af6f67c1513 input=d55a0f5014522a8d]*/ { - rlockobject *self = rlockobject_CAST(op); PyTime_t timeout; - if (lock_acquire_parse_args(args, kwds, &timeout) < 0) { + if (lock_acquire_parse_timeout(timeoutobj, blocking, &timeout) < 0) { return NULL; } @@ -1073,33 +1129,38 @@ rlock_acquire(PyObject *op, PyObject *args, PyObject *kwds) return PyBool_FromLong(r == PY_LOCK_ACQUIRED); } -PyDoc_STRVAR(rlock_acquire_doc, -"acquire($self, /, blocking=True, timeout=-1)\n\ ---\n\ -\n\ -Lock the lock. `blocking` indicates whether we should wait\n\ -for the lock to be available or not. If `blocking` is False\n\ -and another thread holds the lock, the method will return False\n\ -immediately. If `blocking` is True and another thread holds\n\ -the lock, the method will wait for the lock to be released,\n\ -take it and then return True.\n\ -(note: the blocking operation is interruptible.)\n\ -\n\ -In all other cases, the method will return True immediately.\n\ -Precisely, if the current thread already holds the lock, its\n\ -internal counter is simply incremented. If nobody holds the lock,\n\ -the lock is taken and its internal counter initialized to 1."); +/*[clinic input] +_thread.RLock.__enter__ -PyDoc_STRVAR(rlock_enter_doc, -"__enter__($self, /)\n\ ---\n\ -\n\ -Lock the lock."); +Lock the lock. +[clinic start generated code]*/ static PyObject * -rlock_release(PyObject *op, PyObject *Py_UNUSED(dummy)) +_thread_RLock___enter___impl(rlockobject *self) +/*[clinic end generated code: output=63135898476bf89f input=33be37f459dca390]*/ +{ + return _thread_RLock_acquire_impl(self, 1, NULL); +} + +/*[clinic input] +_thread.RLock.release + +Release the lock. + +Allows another thread that is blocked waiting for +the lock to acquire the lock. The lock must be in the locked state, +and must be locked by the same thread that unlocks it; otherwise a +`RuntimeError` is raised. + +Do note that if the lock was acquire()d several times in a row by the +current thread, release() needs to be called as many times for the lock +to be available for other threads. +[clinic start generated code]*/ + +static PyObject * +_thread_RLock_release_impl(rlockobject *self) +/*[clinic end generated code: output=51f4a013c5fae2c5 input=d425daf1a5782e63]*/ { - rlockobject *self = rlockobject_CAST(op); if (_PyRecursiveMutex_TryUnlock(&self->lock) < 0) { PyErr_SetString(PyExc_RuntimeError, "cannot release un-acquired lock"); @@ -1108,46 +1169,55 @@ rlock_release(PyObject *op, PyObject *Py_UNUSED(dummy)) Py_RETURN_NONE; } -PyDoc_STRVAR(rlock_release_doc, -"release($self, /)\n\ ---\n\ -\n\ -Release the lock, allowing another thread that is blocked waiting for\n\ -the lock to acquire the lock. The lock must be in the locked state,\n\ -and must be locked by the same thread that unlocks it; otherwise a\n\ -`RuntimeError` is raised.\n\ -\n\ -Do note that if the lock was acquire()d several times in a row by the\n\ -current thread, release() needs to be called as many times for the lock\n\ -to be available for other threads."); +/*[clinic input] +_thread.RLock.__exit__ + exc_type: object + exc_value: object + exc_tb: object + / -PyDoc_STRVAR(rlock_exit_doc, -"__exit__($self, /, *exc_info)\n\ ---\n\ -\n\ -Release the lock."); +Release the lock. + +[clinic start generated code]*/ static PyObject * -rlock_locked(PyObject *op, PyObject *Py_UNUSED(ignored)) +_thread_RLock___exit___impl(rlockobject *self, PyObject *exc_type, + PyObject *exc_value, PyObject *exc_tb) +/*[clinic end generated code: output=79bb44d551aedeb5 input=79accf0778d91002]*/ +{ + return _thread_RLock_release_impl(self); +} + +/*[clinic input] +_thread.RLock.locked + +Return a boolean indicating whether this object is locked right now. +[clinic start generated code]*/ + +static PyObject * +_thread_RLock_locked_impl(rlockobject *self) +/*[clinic end generated code: output=e9b6060492b3f94e input=8866d9237ba5391b]*/ { - rlockobject *self = rlockobject_CAST(op); int is_locked = rlock_locked_impl(self); return PyBool_FromLong(is_locked); } -PyDoc_STRVAR(rlock_locked_doc, -"locked()\n\ -\n\ -Return a boolean indicating whether this object is locked right now."); +/*[clinic input] +_thread.RLock._acquire_restore + state: object + / + +For internal use by `threading.Condition`. +[clinic start generated code]*/ static PyObject * -rlock_acquire_restore(PyObject *op, PyObject *args) +_thread_RLock__acquire_restore_impl(rlockobject *self, PyObject *state) +/*[clinic end generated code: output=beb8f2713a35e775 input=c8f2094fde059447]*/ { - rlockobject *self = rlockobject_CAST(op); PyThread_ident_t owner; Py_ssize_t count; - if (!PyArg_ParseTuple(args, "(n" Py_PARSE_THREAD_IDENT_T "):_acquire_restore", + if (!PyArg_Parse(state, "(n" Py_PARSE_THREAD_IDENT_T "):_acquire_restore", &count, &owner)) return NULL; @@ -1157,17 +1227,17 @@ rlock_acquire_restore(PyObject *op, PyObject *args) Py_RETURN_NONE; } -PyDoc_STRVAR(rlock_acquire_restore_doc, -"_acquire_restore($self, state, /)\n\ ---\n\ -\n\ -For internal use by `threading.Condition`."); + +/*[clinic input] +_thread.RLock._release_save + +For internal use by `threading.Condition`. +[clinic start generated code]*/ static PyObject * -rlock_release_save(PyObject *op, PyObject *Py_UNUSED(dummy)) +_thread_RLock__release_save_impl(rlockobject *self) +/*[clinic end generated code: output=d2916487315bea93 input=809d227cfc4a112c]*/ { - rlockobject *self = rlockobject_CAST(op); - if (!_PyRecursiveMutex_IsLockedByCurrentThread(&self->lock)) { PyErr_SetString(PyExc_RuntimeError, "cannot release un-acquired lock"); @@ -1181,42 +1251,38 @@ rlock_release_save(PyObject *op, PyObject *Py_UNUSED(dummy)) return Py_BuildValue("n" Py_PARSE_THREAD_IDENT_T, count, owner); } -PyDoc_STRVAR(rlock_release_save_doc, -"_release_save($self, /)\n\ ---\n\ -\n\ -For internal use by `threading.Condition`."); + +/*[clinic input] +_thread.RLock._recursion_count + +For internal use by reentrancy checks. +[clinic start generated code]*/ static PyObject * -rlock_recursion_count(PyObject *op, PyObject *Py_UNUSED(dummy)) +_thread_RLock__recursion_count_impl(rlockobject *self) +/*[clinic end generated code: output=7993fb9695ef2c4d input=7fd1834cd7a4b044]*/ { - rlockobject *self = rlockobject_CAST(op); if (_PyRecursiveMutex_IsLockedByCurrentThread(&self->lock)) { return PyLong_FromSize_t(self->lock.level + 1); } return PyLong_FromLong(0); } -PyDoc_STRVAR(rlock_recursion_count_doc, -"_recursion_count($self, /)\n\ ---\n\ -\n\ -For internal use by reentrancy checks."); + +/*[clinic input] +_thread.RLock._is_owned + +For internal use by `threading.Condition`. +[clinic start generated code]*/ static PyObject * -rlock_is_owned(PyObject *op, PyObject *Py_UNUSED(dummy)) +_thread_RLock__is_owned_impl(rlockobject *self) +/*[clinic end generated code: output=bf14268a3cabbe07 input=fba6535538deb858]*/ { - rlockobject *self = rlockobject_CAST(op); long owned = _PyRecursiveMutex_IsLockedByCurrentThread(&self->lock); return PyBool_FromLong(owned); } -PyDoc_STRVAR(rlock_is_owned_doc, -"_is_owned($self, /)\n\ ---\n\ -\n\ -For internal use by `threading.Condition`."); - /*[clinic input] @classmethod _thread.RLock.__new__ as rlock_new @@ -1256,10 +1322,14 @@ rlock_repr(PyObject *op) #ifdef HAVE_FORK +/*[clinic input] +_thread.RLock._at_fork_reinit +[clinic start generated code]*/ + static PyObject * -rlock__at_fork_reinit(PyObject *op, PyObject *Py_UNUSED(dummy)) +_thread_RLock__at_fork_reinit_impl(rlockobject *self) +/*[clinic end generated code: output=d77a4ce40351817c input=a3b625b026a8df4f]*/ { - rlockobject *self = rlockobject_CAST(op); self->lock = (_PyRecursiveMutex){0}; Py_RETURN_NONE; } @@ -1267,27 +1337,17 @@ rlock__at_fork_reinit(PyObject *op, PyObject *Py_UNUSED(dummy)) static PyMethodDef rlock_methods[] = { - {"acquire", _PyCFunction_CAST(rlock_acquire), - METH_VARARGS | METH_KEYWORDS, rlock_acquire_doc}, - {"release", rlock_release, - METH_NOARGS, rlock_release_doc}, - {"locked", rlock_locked, - METH_NOARGS, rlock_locked_doc}, - {"_is_owned", rlock_is_owned, - METH_NOARGS, rlock_is_owned_doc}, - {"_acquire_restore", rlock_acquire_restore, - METH_VARARGS, rlock_acquire_restore_doc}, - {"_release_save", rlock_release_save, - METH_NOARGS, rlock_release_save_doc}, - {"_recursion_count", rlock_recursion_count, - METH_NOARGS, rlock_recursion_count_doc}, - {"__enter__", _PyCFunction_CAST(rlock_acquire), - METH_VARARGS | METH_KEYWORDS, rlock_enter_doc}, - {"__exit__", rlock_release, - METH_VARARGS, rlock_exit_doc}, + _THREAD_RLOCK_ACQUIRE_METHODDEF + _THREAD_RLOCK_RELEASE_METHODDEF + _THREAD_RLOCK_LOCKED_METHODDEF + _THREAD_RLOCK__IS_OWNED_METHODDEF + _THREAD_RLOCK__ACQUIRE_RESTORE_METHODDEF + _THREAD_RLOCK__RELEASE_SAVE_METHODDEF + _THREAD_RLOCK__RECURSION_COUNT_METHODDEF + _THREAD_RLOCK___ENTER___METHODDEF + _THREAD_RLOCK___EXIT___METHODDEF #ifdef HAVE_FORK - {"_at_fork_reinit", rlock__at_fork_reinit, - METH_NOARGS, NULL}, + _THREAD_RLOCK__AT_FORK_REINIT_METHODDEF #endif {NULL, NULL} /* sentinel */ }; diff --git a/Modules/clinic/_threadmodule.c.h b/Modules/clinic/_threadmodule.c.h index fd8e32a2261d38..8455f9929babc1 100644 --- a/Modules/clinic/_threadmodule.c.h +++ b/Modules/clinic/_threadmodule.c.h @@ -6,7 +6,310 @@ preserve # include "pycore_gc.h" // PyGC_Head # include "pycore_runtime.h" // _Py_ID() #endif -#include "pycore_modsupport.h" // _PyArg_NoKeywords() +#include "pycore_modsupport.h" // _PyArg_UnpackKeywords() + +PyDoc_STRVAR(_thread_lock_acquire__doc__, +"acquire($self, /, blocking=True, timeout=-1)\n" +"--\n" +"\n" +"Lock the lock.\n" +"\n" +"Without argument, this blocks if the lock is already\n" +"locked (even by the same thread), waiting for another thread to release\n" +"the lock, and return True once the lock is acquired.\n" +"With an argument, this will only block if the argument is true,\n" +"and the return value reflects whether the lock is acquired.\n" +"The blocking operation is interruptible."); + +#define _THREAD_LOCK_ACQUIRE_METHODDEF \ + {"acquire", _PyCFunction_CAST(_thread_lock_acquire), METH_FASTCALL|METH_KEYWORDS, _thread_lock_acquire__doc__}, + +static PyObject * +_thread_lock_acquire_impl(lockobject *self, int blocking, + PyObject *timeoutobj); + +static PyObject * +_thread_lock_acquire(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(blocking), &_Py_ID(timeout), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"blocking", "timeout", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "acquire", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + int blocking = 1; + PyObject *timeoutobj = NULL; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + if (args[0]) { + blocking = PyObject_IsTrue(args[0]); + if (blocking < 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_pos; + } + } + timeoutobj = args[1]; +skip_optional_pos: + return_value = _thread_lock_acquire_impl((lockobject *)self, blocking, timeoutobj); + +exit: + return return_value; +} + +PyDoc_STRVAR(_thread_lock_acquire_lock__doc__, +"acquire_lock($self, /, blocking=True, timeout=-1)\n" +"--\n" +"\n" +"An obsolete synonym of acquire()."); + +#define _THREAD_LOCK_ACQUIRE_LOCK_METHODDEF \ + {"acquire_lock", _PyCFunction_CAST(_thread_lock_acquire_lock), METH_FASTCALL|METH_KEYWORDS, _thread_lock_acquire_lock__doc__}, + +static PyObject * +_thread_lock_acquire_lock_impl(lockobject *self, int blocking, + PyObject *timeoutobj); + +static PyObject * +_thread_lock_acquire_lock(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(blocking), &_Py_ID(timeout), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"blocking", "timeout", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "acquire_lock", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + int blocking = 1; + PyObject *timeoutobj = NULL; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + if (args[0]) { + blocking = PyObject_IsTrue(args[0]); + if (blocking < 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_pos; + } + } + timeoutobj = args[1]; +skip_optional_pos: + return_value = _thread_lock_acquire_lock_impl((lockobject *)self, blocking, timeoutobj); + +exit: + return return_value; +} + +PyDoc_STRVAR(_thread_lock_release__doc__, +"release($self, /)\n" +"--\n" +"\n" +"Release the lock.\n" +"\n" +"Allows another thread that is blocked waiting for\n" +"the lock to acquire the lock. The lock must be in the locked state,\n" +"but it needn\'t be locked by the same thread that unlocks it."); + +#define _THREAD_LOCK_RELEASE_METHODDEF \ + {"release", (PyCFunction)_thread_lock_release, METH_NOARGS, _thread_lock_release__doc__}, + +static PyObject * +_thread_lock_release_impl(lockobject *self); + +static PyObject * +_thread_lock_release(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _thread_lock_release_impl((lockobject *)self); +} + +PyDoc_STRVAR(_thread_lock_release_lock__doc__, +"release_lock($self, /)\n" +"--\n" +"\n" +"An obsolete synonym of release()."); + +#define _THREAD_LOCK_RELEASE_LOCK_METHODDEF \ + {"release_lock", (PyCFunction)_thread_lock_release_lock, METH_NOARGS, _thread_lock_release_lock__doc__}, + +static PyObject * +_thread_lock_release_lock_impl(lockobject *self); + +static PyObject * +_thread_lock_release_lock(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _thread_lock_release_lock_impl((lockobject *)self); +} + +PyDoc_STRVAR(_thread_lock___enter____doc__, +"__enter__($self, /)\n" +"--\n" +"\n" +"Lock the lock."); + +#define _THREAD_LOCK___ENTER___METHODDEF \ + {"__enter__", (PyCFunction)_thread_lock___enter__, METH_NOARGS, _thread_lock___enter____doc__}, + +static PyObject * +_thread_lock___enter___impl(lockobject *self); + +static PyObject * +_thread_lock___enter__(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _thread_lock___enter___impl((lockobject *)self); +} + +PyDoc_STRVAR(_thread_lock___exit____doc__, +"__exit__($self, exc_type, exc_value, exc_tb, /)\n" +"--\n" +"\n" +"Release the lock."); + +#define _THREAD_LOCK___EXIT___METHODDEF \ + {"__exit__", _PyCFunction_CAST(_thread_lock___exit__), METH_FASTCALL, _thread_lock___exit____doc__}, + +static PyObject * +_thread_lock___exit___impl(lockobject *self, PyObject *exc_type, + PyObject *exc_value, PyObject *exc_tb); + +static PyObject * +_thread_lock___exit__(PyObject *self, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *exc_type; + PyObject *exc_value; + PyObject *exc_tb; + + if (!_PyArg_CheckPositional("__exit__", nargs, 3, 3)) { + goto exit; + } + exc_type = args[0]; + exc_value = args[1]; + exc_tb = args[2]; + return_value = _thread_lock___exit___impl((lockobject *)self, exc_type, exc_value, exc_tb); + +exit: + return return_value; +} + +PyDoc_STRVAR(_thread_lock_locked__doc__, +"locked($self, /)\n" +"--\n" +"\n" +"Return whether the lock is in the locked state."); + +#define _THREAD_LOCK_LOCKED_METHODDEF \ + {"locked", (PyCFunction)_thread_lock_locked, METH_NOARGS, _thread_lock_locked__doc__}, + +static PyObject * +_thread_lock_locked_impl(lockobject *self); + +static PyObject * +_thread_lock_locked(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _thread_lock_locked_impl((lockobject *)self); +} + +PyDoc_STRVAR(_thread_lock_locked_lock__doc__, +"locked_lock($self, /)\n" +"--\n" +"\n" +"An obsolete synonym of locked()."); + +#define _THREAD_LOCK_LOCKED_LOCK_METHODDEF \ + {"locked_lock", (PyCFunction)_thread_lock_locked_lock, METH_NOARGS, _thread_lock_locked_lock__doc__}, + +static PyObject * +_thread_lock_locked_lock_impl(lockobject *self); + +static PyObject * +_thread_lock_locked_lock(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _thread_lock_locked_lock_impl((lockobject *)self); +} + +#if defined(HAVE_FORK) + +PyDoc_STRVAR(_thread_lock__at_fork_reinit__doc__, +"_at_fork_reinit($self, /)\n" +"--\n" +"\n"); + +#define _THREAD_LOCK__AT_FORK_REINIT_METHODDEF \ + {"_at_fork_reinit", (PyCFunction)_thread_lock__at_fork_reinit, METH_NOARGS, _thread_lock__at_fork_reinit__doc__}, + +static PyObject * +_thread_lock__at_fork_reinit_impl(lockobject *self); + +static PyObject * +_thread_lock__at_fork_reinit(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _thread_lock__at_fork_reinit_impl((lockobject *)self); +} + +#endif /* defined(HAVE_FORK) */ static PyObject * lock_new_impl(PyTypeObject *type); @@ -31,6 +334,265 @@ lock_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) return return_value; } +PyDoc_STRVAR(_thread_RLock_acquire__doc__, +"acquire($self, /, blocking=True, timeout=-1)\n" +"--\n" +"\n" +"Lock the lock.\n" +"\n" +"`blocking` indicates whether we should wait\n" +"for the lock to be available or not. If `blocking` is False\n" +"and another thread holds the lock, the method will return False\n" +"immediately. If `blocking` is True and another thread holds\n" +"the lock, the method will wait for the lock to be released,\n" +"take it and then return True.\n" +"(note: the blocking operation is interruptible.)\n" +"\n" +"In all other cases, the method will return True immediately.\n" +"Precisely, if the current thread already holds the lock, its\n" +"internal counter is simply incremented. If nobody holds the lock,\n" +"the lock is taken and its internal counter initialized to 1."); + +#define _THREAD_RLOCK_ACQUIRE_METHODDEF \ + {"acquire", _PyCFunction_CAST(_thread_RLock_acquire), METH_FASTCALL|METH_KEYWORDS, _thread_RLock_acquire__doc__}, + +static PyObject * +_thread_RLock_acquire_impl(rlockobject *self, int blocking, + PyObject *timeoutobj); + +static PyObject * +_thread_RLock_acquire(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(blocking), &_Py_ID(timeout), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"blocking", "timeout", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "acquire", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + int blocking = 1; + PyObject *timeoutobj = NULL; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + if (args[0]) { + blocking = PyObject_IsTrue(args[0]); + if (blocking < 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_pos; + } + } + timeoutobj = args[1]; +skip_optional_pos: + return_value = _thread_RLock_acquire_impl((rlockobject *)self, blocking, timeoutobj); + +exit: + return return_value; +} + +PyDoc_STRVAR(_thread_RLock___enter____doc__, +"__enter__($self, /)\n" +"--\n" +"\n" +"Lock the lock."); + +#define _THREAD_RLOCK___ENTER___METHODDEF \ + {"__enter__", (PyCFunction)_thread_RLock___enter__, METH_NOARGS, _thread_RLock___enter____doc__}, + +static PyObject * +_thread_RLock___enter___impl(rlockobject *self); + +static PyObject * +_thread_RLock___enter__(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _thread_RLock___enter___impl((rlockobject *)self); +} + +PyDoc_STRVAR(_thread_RLock_release__doc__, +"release($self, /)\n" +"--\n" +"\n" +"Release the lock.\n" +"\n" +"Allows another thread that is blocked waiting for\n" +"the lock to acquire the lock. The lock must be in the locked state,\n" +"and must be locked by the same thread that unlocks it; otherwise a\n" +"`RuntimeError` is raised.\n" +"\n" +"Do note that if the lock was acquire()d several times in a row by the\n" +"current thread, release() needs to be called as many times for the lock\n" +"to be available for other threads."); + +#define _THREAD_RLOCK_RELEASE_METHODDEF \ + {"release", (PyCFunction)_thread_RLock_release, METH_NOARGS, _thread_RLock_release__doc__}, + +static PyObject * +_thread_RLock_release_impl(rlockobject *self); + +static PyObject * +_thread_RLock_release(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _thread_RLock_release_impl((rlockobject *)self); +} + +PyDoc_STRVAR(_thread_RLock___exit____doc__, +"__exit__($self, exc_type, exc_value, exc_tb, /)\n" +"--\n" +"\n" +"Release the lock."); + +#define _THREAD_RLOCK___EXIT___METHODDEF \ + {"__exit__", _PyCFunction_CAST(_thread_RLock___exit__), METH_FASTCALL, _thread_RLock___exit____doc__}, + +static PyObject * +_thread_RLock___exit___impl(rlockobject *self, PyObject *exc_type, + PyObject *exc_value, PyObject *exc_tb); + +static PyObject * +_thread_RLock___exit__(PyObject *self, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *exc_type; + PyObject *exc_value; + PyObject *exc_tb; + + if (!_PyArg_CheckPositional("__exit__", nargs, 3, 3)) { + goto exit; + } + exc_type = args[0]; + exc_value = args[1]; + exc_tb = args[2]; + return_value = _thread_RLock___exit___impl((rlockobject *)self, exc_type, exc_value, exc_tb); + +exit: + return return_value; +} + +PyDoc_STRVAR(_thread_RLock_locked__doc__, +"locked($self, /)\n" +"--\n" +"\n" +"Return a boolean indicating whether this object is locked right now."); + +#define _THREAD_RLOCK_LOCKED_METHODDEF \ + {"locked", (PyCFunction)_thread_RLock_locked, METH_NOARGS, _thread_RLock_locked__doc__}, + +static PyObject * +_thread_RLock_locked_impl(rlockobject *self); + +static PyObject * +_thread_RLock_locked(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _thread_RLock_locked_impl((rlockobject *)self); +} + +PyDoc_STRVAR(_thread_RLock__acquire_restore__doc__, +"_acquire_restore($self, state, /)\n" +"--\n" +"\n" +"For internal use by `threading.Condition`."); + +#define _THREAD_RLOCK__ACQUIRE_RESTORE_METHODDEF \ + {"_acquire_restore", (PyCFunction)_thread_RLock__acquire_restore, METH_O, _thread_RLock__acquire_restore__doc__}, + +static PyObject * +_thread_RLock__acquire_restore_impl(rlockobject *self, PyObject *state); + +static PyObject * +_thread_RLock__acquire_restore(PyObject *self, PyObject *state) +{ + PyObject *return_value = NULL; + + return_value = _thread_RLock__acquire_restore_impl((rlockobject *)self, state); + + return return_value; +} + +PyDoc_STRVAR(_thread_RLock__release_save__doc__, +"_release_save($self, /)\n" +"--\n" +"\n" +"For internal use by `threading.Condition`."); + +#define _THREAD_RLOCK__RELEASE_SAVE_METHODDEF \ + {"_release_save", (PyCFunction)_thread_RLock__release_save, METH_NOARGS, _thread_RLock__release_save__doc__}, + +static PyObject * +_thread_RLock__release_save_impl(rlockobject *self); + +static PyObject * +_thread_RLock__release_save(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _thread_RLock__release_save_impl((rlockobject *)self); +} + +PyDoc_STRVAR(_thread_RLock__recursion_count__doc__, +"_recursion_count($self, /)\n" +"--\n" +"\n" +"For internal use by reentrancy checks."); + +#define _THREAD_RLOCK__RECURSION_COUNT_METHODDEF \ + {"_recursion_count", (PyCFunction)_thread_RLock__recursion_count, METH_NOARGS, _thread_RLock__recursion_count__doc__}, + +static PyObject * +_thread_RLock__recursion_count_impl(rlockobject *self); + +static PyObject * +_thread_RLock__recursion_count(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _thread_RLock__recursion_count_impl((rlockobject *)self); +} + +PyDoc_STRVAR(_thread_RLock__is_owned__doc__, +"_is_owned($self, /)\n" +"--\n" +"\n" +"For internal use by `threading.Condition`."); + +#define _THREAD_RLOCK__IS_OWNED_METHODDEF \ + {"_is_owned", (PyCFunction)_thread_RLock__is_owned, METH_NOARGS, _thread_RLock__is_owned__doc__}, + +static PyObject * +_thread_RLock__is_owned_impl(rlockobject *self); + +static PyObject * +_thread_RLock__is_owned(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _thread_RLock__is_owned_impl((rlockobject *)self); +} + static PyObject * rlock_new_impl(PyTypeObject *type); @@ -54,6 +616,27 @@ rlock_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) return return_value; } +#if defined(HAVE_FORK) + +PyDoc_STRVAR(_thread_RLock__at_fork_reinit__doc__, +"_at_fork_reinit($self, /)\n" +"--\n" +"\n"); + +#define _THREAD_RLOCK__AT_FORK_REINIT_METHODDEF \ + {"_at_fork_reinit", (PyCFunction)_thread_RLock__at_fork_reinit, METH_NOARGS, _thread_RLock__at_fork_reinit__doc__}, + +static PyObject * +_thread_RLock__at_fork_reinit_impl(rlockobject *self); + +static PyObject * +_thread_RLock__at_fork_reinit(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _thread_RLock__at_fork_reinit_impl((rlockobject *)self); +} + +#endif /* defined(HAVE_FORK) */ + #if (defined(HAVE_PTHREAD_GETNAME_NP) || defined(HAVE_PTHREAD_GET_NAME_NP) || defined(MS_WINDOWS)) PyDoc_STRVAR(_thread__get_name__doc__, @@ -142,6 +725,14 @@ _thread_set_name(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb #endif /* (defined(HAVE_PTHREAD_SETNAME_NP) || defined(HAVE_PTHREAD_SET_NAME_NP) || defined(MS_WINDOWS)) */ +#ifndef _THREAD_LOCK__AT_FORK_REINIT_METHODDEF + #define _THREAD_LOCK__AT_FORK_REINIT_METHODDEF +#endif /* !defined(_THREAD_LOCK__AT_FORK_REINIT_METHODDEF) */ + +#ifndef _THREAD_RLOCK__AT_FORK_REINIT_METHODDEF + #define _THREAD_RLOCK__AT_FORK_REINIT_METHODDEF +#endif /* !defined(_THREAD_RLOCK__AT_FORK_REINIT_METHODDEF) */ + #ifndef _THREAD__GET_NAME_METHODDEF #define _THREAD__GET_NAME_METHODDEF #endif /* !defined(_THREAD__GET_NAME_METHODDEF) */ @@ -149,4 +740,4 @@ _thread_set_name(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb #ifndef _THREAD_SET_NAME_METHODDEF #define _THREAD_SET_NAME_METHODDEF #endif /* !defined(_THREAD_SET_NAME_METHODDEF) */ -/*[clinic end generated code: output=b381ec5e313198e7 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=1255a1520f43f97a input=a9049054013a1b77]*/ diff --git a/Parser/action_helpers.c b/Parser/action_helpers.c index 0763866576733f..57e46b4399c66d 100644 --- a/Parser/action_helpers.c +++ b/Parser/action_helpers.c @@ -1939,6 +1939,9 @@ _PyPegen_register_stmts(Parser *p, asdl_stmt_seq* stmts) { return stmts; } stmt_ty last_stmt = asdl_seq_GET(stmts, len - 1); + if (p->last_stmt_location.lineno > last_stmt->lineno) { + return stmts; + } p->last_stmt_location.lineno = last_stmt->lineno; p->last_stmt_location.col_offset = last_stmt->col_offset; p->last_stmt_location.end_lineno = last_stmt->end_lineno; diff --git a/Parser/parser.c b/Parser/parser.c index de5cdc9b6f7f34..c20c368a089b33 100644 --- a/Parser/parser.c +++ b/Parser/parser.c @@ -1201,7 +1201,7 @@ statements_rule(Parser *p) ) { D(fprintf(stderr, "%*c+ statements[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "statement+")); - _res = _PyPegen_register_stmts ( p , ( asdl_stmt_seq* ) _PyPegen_seq_flatten ( p , a ) ); + _res = ( asdl_stmt_seq* ) _PyPegen_seq_flatten ( p , a ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; p->level--; @@ -1244,7 +1244,7 @@ statement_rule(Parser *p) ) { D(fprintf(stderr, "%*c+ statement[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "compound_stmt")); - _res = ( asdl_stmt_seq* ) _PyPegen_singleton_seq ( p , a ); + _res = _PyPegen_register_stmts ( p , ( asdl_stmt_seq* ) _PyPegen_singleton_seq ( p , a ) ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; p->level--;