diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7ac12cd9c8aa8c..1d97c0a60928e3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,7 +28,7 @@ permissions: contents: read concurrency: - group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}-reusable cancel-in-progress: true jobs: @@ -37,8 +37,10 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 10 outputs: + run-docs: ${{ steps.docs-changes.outputs.run-docs || false }} run_tests: ${{ steps.check.outputs.run_tests }} run_hypothesis: ${{ steps.check.outputs.run_hypothesis }} + config_hash: ${{ steps.config_hash.outputs.hash }} steps: - uses: actions/checkout@v3 - name: Check for source changes @@ -74,6 +76,32 @@ jobs: echo "Run hypothesis tests" echo "run_hypothesis=true" >> $GITHUB_OUTPUT fi + - name: Compute hash for config cache key + id: config_hash + run: | + echo "hash=${{ hashFiles('configure', 'configure.ac', '.github/workflows/build.yml') }}" >> $GITHUB_OUTPUT + - name: Get a list of the changed documentation-related files + if: github.event_name == 'pull_request' + id: changed-docs-files + uses: Ana06/get-changed-files@v2.2.0 + with: + filter: | + Doc/** + Misc/** + .github/workflows/reusable-docs.yml + - name: Check for docs changes + if: >- + github.event_name == 'pull_request' + && steps.changed-docs-files.outputs.added_modified_renamed != '' + id: docs-changes + run: | + echo "run-docs=true" >> "${GITHUB_OUTPUT}" + + check-docs: + name: Docs + needs: check_source + if: fromJSON(needs.check_source.outputs.run-docs) + uses: ./.github/workflows/reusable-docs.yml check_generated_files: name: 'Check if generated files are up to date' @@ -87,7 +115,7 @@ jobs: uses: actions/cache@v3 with: path: config.cache - key: ${{ github.job }}-${{ runner.os }}-${{ hashFiles('configure', 'configure.ac', '.github/workflows/build.yml') }} + key: ${{ github.job }}-${{ runner.os }}-${{ needs.check_source.outputs.config_hash }} - uses: actions/setup-python@v3 - name: Install Dependencies run: sudo ./.github/workflows/posix-deps-apt.sh @@ -189,7 +217,7 @@ jobs: uses: actions/cache@v3 with: path: config.cache - key: ${{ github.job }}-${{ runner.os }}-${{ hashFiles('configure', 'configure.ac', '.github/workflows/build.yml') }} + key: ${{ github.job }}-${{ runner.os }}-${{ needs.check_source.outputs.config_hash }} - name: Install Homebrew dependencies run: brew install pkg-config openssl@1.1 xz gdbm tcl-tk - name: Configure CPython @@ -255,7 +283,7 @@ jobs: uses: actions/cache@v3 with: path: ${{ env.CPYTHON_BUILDDIR }}/config.cache - key: ${{ github.job }}-${{ runner.os }}-${{ hashFiles('configure', 'configure.ac', '.github/workflows/build.yml') }} + key: ${{ github.job }}-${{ runner.os }}-${{ needs.check_source.outputs.config_hash }} - name: Configure CPython out-of-tree working-directory: ${{ env.CPYTHON_BUILDDIR }} run: | @@ -297,7 +325,7 @@ jobs: uses: actions/cache@v3 with: path: config.cache - key: ${{ github.job }}-${{ runner.os }}-${{ hashFiles('configure', 'configure.ac', '.github/workflows/build.yml') }} + key: ${{ github.job }}-${{ runner.os }}-${{ needs.check_source.outputs.config_hash }} - name: Register gcc problem matcher run: echo "::add-matcher::.github/problem-matchers/gcc.json" - name: Install Dependencies @@ -376,7 +404,7 @@ jobs: uses: actions/cache@v3 with: path: ${{ env.CPYTHON_BUILDDIR }}/config.cache - key: ${{ github.job }}-${{ runner.os }}-${{ hashFiles('configure', 'configure.ac', '.github/workflows/build.yml') }} + key: ${{ github.job }}-${{ runner.os }}-${{ needs.check_source.outputs.config_hash }} - name: Configure CPython out-of-tree working-directory: ${{ env.CPYTHON_BUILDDIR }} run: | @@ -455,7 +483,7 @@ jobs: uses: actions/cache@v3 with: path: config.cache - key: ${{ github.job }}-${{ runner.os }}-${{ hashFiles('configure', 'configure.ac', '.github/workflows/build.yml') }} + key: ${{ github.job }}-${{ runner.os }}-${{ needs.check_source.outputs.config_hash }} - name: Register gcc problem matcher run: echo "::add-matcher::.github/problem-matchers/gcc.json" - name: Install Dependencies diff --git a/.github/workflows/doc.yml b/.github/workflows/reusable-docs.yml similarity index 91% rename from .github/workflows/doc.yml rename to .github/workflows/reusable-docs.yml index ec900ce68a1dde..8a271e867c8b4d 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/reusable-docs.yml @@ -1,31 +1,8 @@ name: Docs on: + workflow_call: workflow_dispatch: - #push: - # branches: - # - 'main' - # - '3.12' - # - '3.11' - # - '3.10' - # - '3.9' - # - '3.8' - # - '3.7' - # paths: - # - 'Doc/**' - pull_request: - branches: - - 'main' - - '3.12' - - '3.11' - - '3.10' - - '3.9' - - '3.8' - - '3.7' - paths: - - 'Doc/**' - - 'Misc/**' - - '.github/workflows/doc.yml' permissions: contents: read diff --git a/Doc/c-api/structures.rst b/Doc/c-api/structures.rst index aae1b951804491..7ce3578f250792 100644 --- a/Doc/c-api/structures.rst +++ b/Doc/c-api/structures.rst @@ -395,7 +395,7 @@ Accessing attributes of extension types The string should be static, no copy is made of it. - .. c:member:: Py_ssize_t PyMemberDef.offset + .. c:member:: Py_ssize_t offset The offset in bytes that the member is located on the type’s object struct. @@ -625,23 +625,23 @@ Defining Getters and Setters Structure to define property-like access for a type. See also description of the :c:member:`PyTypeObject.tp_getset` slot. - .. c:member:: const char* PyGetSetDef.name + .. c:member:: const char* name attribute name - .. c:member:: getter PyGetSetDef.get + .. c:member:: getter get C function to get the attribute. - .. c:member:: setter PyGetSetDef.set + .. c:member:: setter set Optional C function to set or delete the attribute, if omitted the attribute is readonly. - .. c:member:: const char* PyGetSetDef.doc + .. c:member:: const char* doc optional docstring - .. c:member:: void* PyGetSetDef.closure + .. c:member:: void* closure Optional function pointer, providing additional data for getter and setter. diff --git a/Doc/c-api/type.rst b/Doc/c-api/type.rst index fb38935e003336..89cd74335fd770 100644 --- a/Doc/c-api/type.rst +++ b/Doc/c-api/type.rst @@ -349,6 +349,15 @@ The following functions and structs are used to create :c:member:`~PyTypeObject.tp_new` is deprecated and in Python 3.14+ it will be no longer allowed. +.. raw:: html + + + + + + + + .. c:type:: PyType_Spec Structure defining a type's behavior. @@ -410,12 +419,18 @@ The following functions and structs are used to create Each slot ID should be specified at most once. +.. raw:: html + + + + + .. c:type:: PyType_Slot Structure defining optional functionality of a type, containing a slot ID and a value pointer. - .. c:member:: int PyType_Slot.slot + .. c:member:: int slot A slot ID. @@ -459,7 +474,7 @@ The following functions and structs are used to create :c:member:`~PyBufferProcs.bf_releasebuffer` are now available under the limited API. - .. c:member:: void *PyType_Slot.pfunc + .. c:member:: void *pfunc The desired value of the slot. In most cases, this is a pointer to a function. diff --git a/Doc/conf.py b/Doc/conf.py index 485c0bdf84df2e..09e12e245891d2 100644 --- a/Doc/conf.py +++ b/Doc/conf.py @@ -235,7 +235,6 @@ # match any of the following regexes (using re.match). coverage_ignore_modules = [ r'[T|t][k|K]', - r'Tix', ] coverage_ignore_functions = [ diff --git a/Doc/faq/gui.rst b/Doc/faq/gui.rst index 023ffdf0db510a..0a372342862d2f 100644 --- a/Doc/faq/gui.rst +++ b/Doc/faq/gui.rst @@ -46,15 +46,8 @@ One solution is to ship the application with the Tcl and Tk libraries, and point to them at run-time using the :envvar:`TCL_LIBRARY` and :envvar:`TK_LIBRARY` environment variables. -To get truly stand-alone applications, the Tcl scripts that form the library -have to be integrated into the application as well. One tool supporting that is -SAM (stand-alone modules), which is part of the Tix distribution -(https://tix.sourceforge.net/). - -Build Tix with SAM enabled, perform the appropriate call to -:c:func:`Tclsam_init`, etc. inside Python's -:file:`Modules/tkappinit.c`, and link with libtclsam and libtksam (you -might include the Tix libraries as well). +Various third-party freeze libraries such as py2exe and cx_Freeze have +handling for Tkinter applications built-in. Can I have Tk events handled while waiting for I/O? diff --git a/Doc/library/ast.rst b/Doc/library/ast.rst index b6b1e076c9f08c..17d1de9fbb8f11 100644 --- a/Doc/library/ast.rst +++ b/Doc/library/ast.rst @@ -481,7 +481,7 @@ Expressions Comparison operator tokens. -.. class:: Call(func, args, keywords, starargs, kwargs) +.. class:: Call(func, args, keywords) A function call. ``func`` is the function, which will often be a :class:`Name` or :class:`Attribute` object. Of the arguments: @@ -491,7 +491,7 @@ Expressions arguments passed by keyword. When creating a ``Call`` node, ``args`` and ``keywords`` are required, but - they can be empty lists. ``starargs`` and ``kwargs`` are optional. + they can be empty lists. .. doctest:: @@ -917,6 +917,25 @@ Statements type_ignores=[]) +.. class:: TypeAlias(name, type_params, value) + + A :ref:`type alias ` created through the :keyword:`type` + statement. ``name`` is the name of the alias, ``type_params`` is a list of + :ref:`type parameters `, and ``value`` is the value of the + type alias. + + .. doctest:: + + >>> print(ast.dump(ast.parse('type Alias = int'), indent=4)) + Module( + body=[ + TypeAlias( + name=Name(id='Alias', ctx=Store()), + type_params=[], + value=Name(id='int', ctx=Load()))], + type_ignores=[]) + + Other statements which are only applicable inside functions or loops are described in other sections. @@ -1644,15 +1663,93 @@ Pattern matching value=Constant(value=Ellipsis))])])], type_ignores=[]) +.. _ast-type-params: + +Type parameters +^^^^^^^^^^^^^^^ + +:ref:`Type parameters ` can exist on classes, functions, and type +aliases. + +.. class:: TypeVar(name, bound) + + A :class:`typing.TypeVar`. ``name`` is the name of the type variable. + ``bound`` is the bound or constraints, if any. If ``bound`` is a :class:`Tuple`, + it represents constraints; otherwise it represents the bound. + + .. doctest:: + + >>> print(ast.dump(ast.parse("type Alias[T: int] = list[T]"), indent=4)) + Module( + body=[ + TypeAlias( + name=Name(id='Alias', ctx=Store()), + type_params=[ + TypeVar( + name='T', + bound=Name(id='int', ctx=Load()))], + value=Subscript( + value=Name(id='list', ctx=Load()), + slice=Name(id='T', ctx=Load()), + ctx=Load()))], + type_ignores=[]) + +.. class:: ParamSpec(name) + + A :class:`typing.ParamSpec`. ``name`` is the name of the parameter specification. + + .. doctest:: + + >>> print(ast.dump(ast.parse("type Alias[**P] = Callable[P, int]"), indent=4)) + Module( + body=[ + TypeAlias( + name=Name(id='Alias', ctx=Store()), + type_params=[ + ParamSpec(name='P')], + value=Subscript( + value=Name(id='Callable', ctx=Load()), + slice=Tuple( + elts=[ + Name(id='P', ctx=Load()), + Name(id='int', ctx=Load())], + ctx=Load()), + ctx=Load()))], + type_ignores=[]) + +.. class:: TypeVarTuple(name) + + A :class:`typing.TypeVarTuple`. ``name`` is the name of the type variable tuple. + + .. doctest:: + + >>> print(ast.dump(ast.parse("type Alias[*Ts] = tuple[*Ts]"), indent=4)) + Module( + body=[ + TypeAlias( + name=Name(id='Alias', ctx=Store()), + type_params=[ + TypeVarTuple(name='Ts')], + value=Subscript( + value=Name(id='tuple', ctx=Load()), + slice=Tuple( + elts=[ + Starred( + value=Name(id='Ts', ctx=Load()), + ctx=Load())], + ctx=Load()), + ctx=Load()))], + type_ignores=[]) Function and class definitions ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. class:: FunctionDef(name, args, body, decorator_list, returns, type_comment) +.. class:: FunctionDef(name, type_params, args, body, decorator_list, returns, type_comment) A function definition. * ``name`` is a raw string of the function name. + * ``type_params`` is a list of :ref:`type parameters `. * ``args`` is an :class:`arguments` node. * ``body`` is the list of nodes inside the function. * ``decorator_list`` is the list of decorators to be applied, stored outermost @@ -1724,7 +1821,6 @@ Function and class definitions body=[ FunctionDef( name='f', - type_params=[], args=arguments( posonlyargs=[], args=[ @@ -1749,7 +1845,8 @@ Function and class definitions decorator_list=[ Name(id='decorator1', ctx=Load()), Name(id='decorator2', ctx=Load())], - returns=Constant(value='return annotation'))], + returns=Constant(value='return annotation'), + type_params=[])], type_ignores=[]) @@ -1820,18 +1917,16 @@ Function and class definitions type_ignores=[]) -.. class:: ClassDef(name, bases, keywords, starargs, kwargs, body, decorator_list) +.. class:: ClassDef(name, type_params, bases, keywords, body, decorator_list) A class definition. * ``name`` is a raw string for the class name + * ``type_params`` is a list of :ref:`type parameters `. * ``bases`` is a list of nodes for explicitly specified base classes. * ``keywords`` is a list of :class:`keyword` nodes, principally for 'metaclass'. Other keywords will be passed to the metaclass, as per `PEP-3115 `_. - * ``starargs`` and ``kwargs`` are each a single node, as in a function call. - starargs will be expanded to join the list of base classes, and kwargs will - be passed to the metaclass. * ``body`` is a list of nodes representing the code within the class definition. * ``decorator_list`` is a list of nodes, as in :class:`FunctionDef`. @@ -1848,7 +1943,6 @@ Function and class definitions body=[ ClassDef( name='Foo', - type_params=[], bases=[ Name(id='base1', ctx=Load()), Name(id='base2', ctx=Load())], @@ -1860,7 +1954,8 @@ Function and class definitions Pass()], decorator_list=[ Name(id='decorator1', ctx=Load()), - Name(id='decorator2', ctx=Load())])], + Name(id='decorator2', ctx=Load())], + type_params=[])], type_ignores=[]) Async and await @@ -1887,7 +1982,6 @@ Async and await body=[ AsyncFunctionDef( name='f', - type_params=[], args=arguments( posonlyargs=[], args=[], @@ -1901,7 +1995,8 @@ Async and await func=Name(id='other_func', ctx=Load()), args=[], keywords=[])))], - decorator_list=[])], + decorator_list=[], + type_params=[])], type_ignores=[]) diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst index 4762fb50437460..9b90f1ef23d92c 100644 --- a/Doc/library/dis.rst +++ b/Doc/library/dis.rst @@ -188,9 +188,9 @@ operation is being performed, so the intermediate analysis object isn't useful: For a module, it disassembles all functions. For a class, it disassembles all methods (including class and static methods). For a code object or sequence of raw bytecode, it prints one line per bytecode instruction. - It also recursively disassembles nested code objects (the code of - comprehensions, generator expressions and nested functions, and the code - used for building nested classes). + It also recursively disassembles nested code objects. These can include + generator expressions, nested functions, the bodies of nested classes, + and the code objects used for :ref:`annotation scopes `. Strings are first compiled to code objects with the :func:`compile` built-in function before being disassembled. If no object is provided, this function disassembles the last traceback. @@ -926,6 +926,27 @@ iterations of the loop. .. opcode:: LOAD_NAME (namei) Pushes the value associated with ``co_names[namei]`` onto the stack. + The name is looked up within the locals, then the globals, then the builtins. + + +.. opcode:: LOAD_LOCALS + + Pushes a reference to the locals dictionary onto the stack. This is used + to prepare namespace dictionaries for :opcode:`LOAD_FROM_DICT_OR_DEREF` + and :opcode:`LOAD_FROM_DICT_OR_GLOBALS`. + + .. versionadded:: 3.12 + + +.. opcode:: LOAD_FROM_DICT_OR_GLOBALS (i) + + Pops a mapping off the stack and looks up the value for ``co_names[namei]``. + If the name is not found there, looks it up in the globals and then the builtins, + similar to :opcode:`LOAD_GLOBAL`. + This is used for loading global variables in + :ref:`annotation scopes ` within class bodies. + + .. versionadded:: 3.12 .. opcode:: BUILD_TUPLE (count) @@ -1243,16 +1264,17 @@ iterations of the loop. ``i`` is no longer offset by the length of ``co_varnames``. -.. opcode:: LOAD_CLASSDEREF (i) +.. opcode:: LOAD_FROM_DICT_OR_DEREF (i) - Much like :opcode:`LOAD_DEREF` but first checks the locals dictionary before - consulting the cell. This is used for loading free variables in class - bodies. - - .. versionadded:: 3.4 + Pops a mapping off the stack and looks up the name associated with + slot ``i`` of the "fast locals" storage in this mapping. + If the name is not found there, loads it from the cell contained in + slot ``i``, similar to :opcode:`LOAD_DEREF`. This is used for loading + free variables in class bodies (which previously used + :opcode:`!LOAD_CLASSDEREF`) and in + :ref:`annotation scopes ` within class bodies. - .. versionchanged:: 3.11 - ``i`` is no longer offset by the length of ``co_varnames``. + .. versionadded:: 3.12 .. opcode:: STORE_DEREF (i) @@ -1504,13 +1526,45 @@ iterations of the loop. The operand determines which intrinsic function is called: - * ``0`` Not valid - * ``1`` Prints the argument to standard out. Used in the REPL. - * ``2`` Performs ``import *`` for the named module. - * ``3`` Extracts the return value from a ``StopIteration`` exception. - * ``4`` Wraps an aync generator value - * ``5`` Performs the unary ``+`` operation - * ``6`` Converts a list to a tuple + +-----------------------------------+-----------------------------------+ + | Operand | Description | + +===================================+===================================+ + | ``INTRINSIC_1_INVALID`` | Not valid | + +-----------------------------------+-----------------------------------+ + | ``INTRINSIC_PRINT`` | Prints the argument to standard | + | | out. Used in the REPL. | + +-----------------------------------+-----------------------------------+ + | ``INTRINSIC_IMPORT_STAR`` | Performs ``import *`` for the | + | | named module. | + +-----------------------------------+-----------------------------------+ + | ``INTRINSIC_STOPITERATION_ERROR`` | Extracts the return value from a | + | | ``StopIteration`` exception. | + +-----------------------------------+-----------------------------------+ + | ``INTRINSIC_ASYNC_GEN_WRAP`` | Wraps an aync generator value | + +-----------------------------------+-----------------------------------+ + | ``INTRINSIC_UNARY_POSITIVE`` | Performs the unary ``+`` | + | | operation | + +-----------------------------------+-----------------------------------+ + | ``INTRINSIC_LIST_TO_TUPLE`` | Converts a list to a tuple | + +-----------------------------------+-----------------------------------+ + | ``INTRINSIC_TYPEVAR`` | Creates a :class:`typing.TypeVar` | + +-----------------------------------+-----------------------------------+ + | ``INTRINSIC_PARAMSPEC`` | Creates a | + | | :class:`typing.ParamSpec` | + +-----------------------------------+-----------------------------------+ + | ``INTRINSIC_TYPEVARTUPLE`` | Creates a | + | | :class:`typing.TypeVarTuple` | + +-----------------------------------+-----------------------------------+ + | ``INTRINSIC_SUBSCRIPT_GENERIC`` | Returns :class:`typing.Generic` | + | | subscripted with the argument | + +-----------------------------------+-----------------------------------+ + | ``INTRINSIC_TYPEALIAS`` | Creates a | + | | :class:`typing.TypeAliasType`; | + | | used in the :keyword:`type` | + | | statement. The argument is a tuple| + | | of the type alias's name, | + | | type parameters, and value. | + +-----------------------------------+-----------------------------------+ .. versionadded:: 3.12 @@ -1522,8 +1576,25 @@ iterations of the loop. The operand determines which intrinsic function is called: - * ``0`` Not valid - * ``1`` Calculates the :exc:`ExceptionGroup` to raise from a ``try-except*``. + +----------------------------------------+-----------------------------------+ + | Operand | Description | + +========================================+===================================+ + | ``INTRINSIC_2_INVALID`` | Not valid | + +----------------------------------------+-----------------------------------+ + | ``INTRINSIC_PREP_RERAISE_STAR`` | Calculates the | + | | :exc:`ExceptionGroup` to raise | + | | from a ``try-except*``. | + +----------------------------------------+-----------------------------------+ + | ``INTRINSIC_TYPEVAR_WITH_BOUND`` | Creates a :class:`typing.TypeVar` | + | | with a bound. | + +----------------------------------------+-----------------------------------+ + | ``INTRINSIC_TYPEVAR_WITH_CONSTRAINTS`` | Creates a | + | | :class:`typing.TypeVar` with | + | | constraints. | + +----------------------------------------+-----------------------------------+ + | ``INTRINSIC_SET_FUNCTION_TYPE_PARAMS`` | Sets the ``__type_params__`` | + | | attribute of a function. | + +----------------------------------------+-----------------------------------+ .. versionadded:: 3.12 diff --git a/Doc/library/imghdr.rst b/Doc/library/imghdr.rst deleted file mode 100644 index 630fd7019f94de..00000000000000 --- a/Doc/library/imghdr.rst +++ /dev/null @@ -1,86 +0,0 @@ -:mod:`imghdr` --- Determine the type of an image -================================================ - -.. module:: imghdr - :synopsis: Determine the type of image contained in a file or byte stream. - :deprecated: - -**Source code:** :source:`Lib/imghdr.py` - -.. deprecated-removed:: 3.11 3.13 - The :mod:`imghdr` module is deprecated - (see :pep:`PEP 594 <594#imghdr>` for details and alternatives). - --------------- - -The :mod:`imghdr` module determines the type of image contained in a file or -byte stream. - -The :mod:`imghdr` module defines the following function: - - -.. function:: what(file, h=None) - - Test the image data contained in the file named *file* and return a - string describing the image type. If *h* is provided, the *file* - argument is ignored and *h* is assumed to contain the byte stream to test. - - .. versionchanged:: 3.6 - Accepts a :term:`path-like object`. - -The following image types are recognized, as listed below with the return value -from :func:`what`: - -+------------+-----------------------------------+ -| Value | Image format | -+============+===================================+ -| ``'rgb'`` | SGI ImgLib Files | -+------------+-----------------------------------+ -| ``'gif'`` | GIF 87a and 89a Files | -+------------+-----------------------------------+ -| ``'pbm'`` | Portable Bitmap Files | -+------------+-----------------------------------+ -| ``'pgm'`` | Portable Graymap Files | -+------------+-----------------------------------+ -| ``'ppm'`` | Portable Pixmap Files | -+------------+-----------------------------------+ -| ``'tiff'`` | TIFF Files | -+------------+-----------------------------------+ -| ``'rast'`` | Sun Raster Files | -+------------+-----------------------------------+ -| ``'xbm'`` | X Bitmap Files | -+------------+-----------------------------------+ -| ``'jpeg'`` | JPEG data in JFIF or Exif formats | -+------------+-----------------------------------+ -| ``'bmp'`` | BMP files | -+------------+-----------------------------------+ -| ``'png'`` | Portable Network Graphics | -+------------+-----------------------------------+ -| ``'webp'`` | WebP files | -+------------+-----------------------------------+ -| ``'exr'`` | OpenEXR Files | -+------------+-----------------------------------+ - -.. versionadded:: 3.5 - The *exr* and *webp* formats were added. - - -You can extend the list of file types :mod:`imghdr` can recognize by appending -to this variable: - - -.. data:: tests - - A list of functions performing the individual tests. Each function takes two - arguments: the byte-stream and an open file-like object. When :func:`what` is - called with a byte-stream, the file-like object will be ``None``. - - The test function should return a string describing the image type if the test - succeeded, or ``None`` if it failed. - -Example:: - - >>> import imghdr - >>> imghdr.what('bass.gif') - 'gif' - diff --git a/Doc/library/os.path.rst b/Doc/library/os.path.rst index 7881c52db87090..3a668e28f2e268 100644 --- a/Doc/library/os.path.rst +++ b/Doc/library/os.path.rst @@ -304,6 +304,24 @@ the :mod:`glob` module.) Accepts a :term:`path-like object`. +.. function:: isdevdrive(path) + + Return ``True`` if pathname *path* is located on a Windows Dev Drive. + A Dev Drive is optimized for developer scenarios, and offers faster + performance for reading and writing files. It is recommended for use for + source code, temporary build directories, package caches, and other + IO-intensive operations. + + May raise an error for an invalid path, for example, one without a + recognizable drive, but returns ``False`` on platforms that do not support + Dev Drives. See `the Windows documentation `_ + for information on enabling and creating Dev Drives. + + .. availability:: Windows. + + .. versionadded:: 3.12 + + .. function:: join(path, *paths) Join one or more path segments intelligently. The return value is the diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst index 627f2df9263dec..ee3330f44f47d0 100644 --- a/Doc/library/pathlib.rst +++ b/Doc/library/pathlib.rst @@ -885,7 +885,7 @@ call fails (for example because the path doesn't exist). .. versionadded:: 3.5 -.. method:: Path.glob(pattern, *, case_sensitive=None) +.. method:: Path.glob(pattern, *, case_sensitive=None, follow_symlinks=None) Glob the given relative *pattern* in the directory represented by this path, yielding all matching files (of any kind):: @@ -911,6 +911,11 @@ call fails (for example because the path doesn't exist). typically, case-sensitive on POSIX, and case-insensitive on Windows. Set *case_sensitive* to ``True`` or ``False`` to override this behaviour. + By default, or when the *follow_symlinks* keyword-only argument is set to + ``None``, this method follows symlinks except when expanding "``**``" + wildcards. Set *follow_symlinks* to ``True`` to always follow symlinks, or + ``False`` to treat all symlinks as files. + .. note:: Using the "``**``" pattern in large directory trees may consume an inordinate amount of time. @@ -924,6 +929,9 @@ call fails (for example because the path doesn't exist). .. versionadded:: 3.12 The *case_sensitive* argument. + .. versionadded:: 3.13 + The *follow_symlinks* argument. + .. method:: Path.group() Return the name of the group owning the file. :exc:`KeyError` is raised @@ -1309,7 +1317,7 @@ call fails (for example because the path doesn't exist). .. versionadded:: 3.6 The *strict* argument (pre-3.6 behavior is strict). -.. method:: Path.rglob(pattern, *, case_sensitive=None) +.. method:: Path.rglob(pattern, *, case_sensitive=None, follow_symlinks=None) Glob the given relative *pattern* recursively. This is like calling :func:`Path.glob` with "``**/``" added in front of the *pattern*, where @@ -1327,6 +1335,11 @@ call fails (for example because the path doesn't exist). typically, case-sensitive on POSIX, and case-insensitive on Windows. Set *case_sensitive* to ``True`` or ``False`` to override this behaviour. + By default, or when the *follow_symlinks* keyword-only argument is set to + ``None``, this method follows symlinks except when expanding "``**``" + wildcards. Set *follow_symlinks* to ``True`` to always follow symlinks, or + ``False`` to treat all symlinks as files. + .. audit-event:: pathlib.Path.rglob self,pattern pathlib.Path.rglob .. versionchanged:: 3.11 @@ -1336,6 +1349,9 @@ call fails (for example because the path doesn't exist). .. versionadded:: 3.12 The *case_sensitive* argument. + .. versionadded:: 3.13 + The *follow_symlinks* argument. + .. method:: Path.rmdir() Remove this directory. The directory must be empty. diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 9203afbf6a4e8a..fdef5314b9a4ef 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -5476,6 +5476,14 @@ types, where they are relevant. Some of these are not reported by the .. versionadded:: 3.3 +.. attribute:: definition.__type_params__ + + The :ref:`type parameters ` of generic classes, functions, + and :ref:`type aliases `. + + .. versionadded:: 3.12 + + .. attribute:: class.__mro__ This attribute is a tuple of classes that are considered when looking for diff --git a/Doc/library/superseded.rst b/Doc/library/superseded.rst index 24e74110a319f9..bd17039bc6f6b6 100644 --- a/Doc/library/superseded.rst +++ b/Doc/library/superseded.rst @@ -10,5 +10,4 @@ backwards compatibility. They have been superseded by other modules. .. toctree:: - imghdr.rst optparse.rst diff --git a/Doc/library/tk.rst b/Doc/library/tk.rst index 3dc2130539c2cf..0593f8b73ea545 100644 --- a/Doc/library/tk.rst +++ b/Doc/library/tk.rst @@ -12,8 +12,7 @@ Graphical User Interfaces with Tk Tk/Tcl has long been an integral part of Python. It provides a robust and platform independent windowing toolkit, that is available to Python programmers -using the :mod:`tkinter` package, and its extension, the :mod:`tkinter.tix` and -the :mod:`tkinter.ttk` modules. +using the :mod:`tkinter` package, and its extension, the :mod:`tkinter.ttk` module. The :mod:`tkinter` package is a thin object-oriented layer on top of Tcl/Tk. To use :mod:`tkinter`, you don't need to write Tcl code, but you will need to @@ -39,7 +38,6 @@ alternative `GUI frameworks and tools - -**Source code:** :source:`Lib/tkinter/tix.py` - -.. index:: single: Tix - -.. deprecated:: 3.6 - This Tk extension is unmaintained and should not be used in new code. Use - :mod:`tkinter.ttk` instead. - --------------- - -The :mod:`tkinter.tix` (Tk Interface Extension) module provides an additional -rich set of widgets. Although the standard Tk library has many useful widgets, -they are far from complete. The :mod:`tkinter.tix` library provides most of the -commonly needed widgets that are missing from standard Tk: :class:`HList`, -:class:`ComboBox`, :class:`Control` (a.k.a. SpinBox) and an assortment of -scrollable widgets. -:mod:`tkinter.tix` also includes many more widgets that are generally useful in -a wide range of applications: :class:`NoteBook`, :class:`FileEntry`, -:class:`PanedWindow`, etc; there are more than 40 of them. - -With all these new widgets, you can introduce new interaction techniques into -applications, creating more useful and more intuitive user interfaces. You can -design your application by choosing the most appropriate widgets to match the -special needs of your application and users. - -.. seealso:: - - `Tix Homepage `_ - The home page for :mod:`Tix`. This includes links to additional documentation - and downloads. - - `Tix Man Pages `_ - On-line version of the man pages and reference material. - - `Tix Programming Guide `_ - On-line version of the programmer's reference material. - - `Tix Development Applications `_ - Tix applications for development of Tix and Tkinter programs. Tide applications - work under Tk or Tkinter, and include :program:`TixInspect`, an inspector to - remotely modify and debug Tix/Tk/Tkinter applications. - - -Using Tix ---------- - - -.. class:: Tk(screenName=None, baseName=None, className='Tix') - - Toplevel widget of Tix which represents mostly the main window of an - application. It has an associated Tcl interpreter. - - Classes in the :mod:`tkinter.tix` module subclasses the classes in the - :mod:`tkinter`. The former imports the latter, so to use :mod:`tkinter.tix` - with Tkinter, all you need to do is to import one module. In general, you - can just import :mod:`tkinter.tix`, and replace the toplevel call to - :class:`tkinter.Tk` with :class:`tix.Tk`:: - - from tkinter import tix - from tkinter.constants import * - root = tix.Tk() - -To use :mod:`tkinter.tix`, you must have the Tix widgets installed, usually -alongside your installation of the Tk widgets. To test your installation, try -the following:: - - from tkinter import tix - root = tix.Tk() - root.tk.eval('package require Tix') - - -Tix Widgets ------------ - -`Tix `_ -introduces over 40 widget classes to the :mod:`tkinter` repertoire. - - -Basic Widgets -^^^^^^^^^^^^^ - - -.. class:: Balloon() - - A `Balloon - `_ that - pops up over a widget to provide help. When the user moves the cursor inside a - widget to which a Balloon widget has been bound, a small pop-up window with a - descriptive message will be shown on the screen. - -.. Python Demo of: -.. \ulink{Balloon}{https://tix.sourceforge.net/dist/current/demos/samples/Balloon.tcl} - - -.. class:: ButtonBox() - - The `ButtonBox - `_ - widget creates a box of buttons, such as is commonly used for ``Ok Cancel``. - -.. Python Demo of: -.. \ulink{ButtonBox}{https://tix.sourceforge.net/dist/current/demos/samples/BtnBox.tcl} - - -.. class:: ComboBox() - - The `ComboBox - `_ - widget is similar to the combo box control in MS Windows. The user can select a - choice by either typing in the entry subwidget or selecting from the listbox - subwidget. - -.. Python Demo of: -.. \ulink{ComboBox}{https://tix.sourceforge.net/dist/current/demos/samples/ComboBox.tcl} - - -.. class:: Control() - - The `Control - `_ - widget is also known as the :class:`SpinBox` widget. The user can adjust the - value by pressing the two arrow buttons or by entering the value directly into - the entry. The new value will be checked against the user-defined upper and - lower limits. - -.. Python Demo of: -.. \ulink{Control}{https://tix.sourceforge.net/dist/current/demos/samples/Control.tcl} - - -.. class:: LabelEntry() - - The `LabelEntry - `_ - widget packages an entry widget and a label into one mega widget. It can - be used to simplify the creation of "entry-form" type of interface. - -.. Python Demo of: -.. \ulink{LabelEntry}{https://tix.sourceforge.net/dist/current/demos/samples/LabEntry.tcl} - - -.. class:: LabelFrame() - - The `LabelFrame - `_ - widget packages a frame widget and a label into one mega widget. To create - widgets inside a LabelFrame widget, one creates the new widgets relative to the - :attr:`frame` subwidget and manage them inside the :attr:`frame` subwidget. - -.. Python Demo of: -.. \ulink{LabelFrame}{https://tix.sourceforge.net/dist/current/demos/samples/LabFrame.tcl} - - -.. class:: Meter() - - The `Meter - `_ widget - can be used to show the progress of a background job which may take a long time - to execute. - -.. Python Demo of: -.. \ulink{Meter}{https://tix.sourceforge.net/dist/current/demos/samples/Meter.tcl} - - -.. class:: OptionMenu() - - The `OptionMenu - `_ - creates a menu button of options. - -.. Python Demo of: -.. \ulink{OptionMenu}{https://tix.sourceforge.net/dist/current/demos/samples/OptMenu.tcl} - - -.. class:: PopupMenu() - - The `PopupMenu - `_ - widget can be used as a replacement of the ``tk_popup`` command. The advantage - of the :mod:`Tix` :class:`PopupMenu` widget is it requires less application code - to manipulate. - -.. Python Demo of: -.. \ulink{PopupMenu}{https://tix.sourceforge.net/dist/current/demos/samples/PopMenu.tcl} - - -.. class:: Select() - - The `Select - `_ widget - is a container of button subwidgets. It can be used to provide radio-box or - check-box style of selection options for the user. - -.. Python Demo of: -.. \ulink{Select}{https://tix.sourceforge.net/dist/current/demos/samples/Select.tcl} - - -.. class:: StdButtonBox() - - The `StdButtonBox - `_ - widget is a group of standard buttons for Motif-like dialog boxes. - -.. Python Demo of: -.. \ulink{StdButtonBox}{https://tix.sourceforge.net/dist/current/demos/samples/StdBBox.tcl} - - -File Selectors -^^^^^^^^^^^^^^ - - -.. class:: DirList() - - The `DirList - `_ - widget displays a list view of a directory, its previous directories and its - sub-directories. The user can choose one of the directories displayed in the - list or change to another directory. - -.. Python Demo of: -.. \ulink{DirList}{https://tix.sourceforge.net/dist/current/demos/samples/DirList.tcl} - - -.. class:: DirTree() - - The `DirTree - `_ - widget displays a tree view of a directory, its previous directories and its - sub-directories. The user can choose one of the directories displayed in the - list or change to another directory. - -.. Python Demo of: -.. \ulink{DirTree}{https://tix.sourceforge.net/dist/current/demos/samples/DirTree.tcl} - - -.. class:: DirSelectDialog() - - The `DirSelectDialog - `_ - widget presents the directories in the file system in a dialog window. The user - can use this dialog window to navigate through the file system to select the - desired directory. - -.. Python Demo of: -.. \ulink{DirSelectDialog}{https://tix.sourceforge.net/dist/current/demos/samples/DirDlg.tcl} - - -.. class:: DirSelectBox() - - The :class:`DirSelectBox` is similar to the standard Motif(TM) - directory-selection box. It is generally used for the user to choose a - directory. DirSelectBox stores the directories mostly recently selected into - a ComboBox widget so that they can be quickly selected again. - - -.. class:: ExFileSelectBox() - - The `ExFileSelectBox - `_ - widget is usually embedded in a tixExFileSelectDialog widget. It provides a - convenient method for the user to select files. The style of the - :class:`ExFileSelectBox` widget is very similar to the standard file dialog on - MS Windows 3.1. - -.. Python Demo of: -.. \ulink{ExFileSelectDialog}{https://tix.sourceforge.net/dist/current/demos/samples/EFileDlg.tcl} - - -.. class:: FileSelectBox() - - The `FileSelectBox - `_ - is similar to the standard Motif(TM) file-selection box. It is generally used - for the user to choose a file. FileSelectBox stores the files mostly recently - selected into a :class:`ComboBox` widget so that they can be quickly selected - again. - -.. Python Demo of: -.. \ulink{FileSelectDialog}{https://tix.sourceforge.net/dist/current/demos/samples/FileDlg.tcl} - - -.. class:: FileEntry() - - The `FileEntry - `_ - widget can be used to input a filename. The user can type in the filename - manually. Alternatively, the user can press the button widget that sits next to - the entry, which will bring up a file selection dialog. - -.. Python Demo of: -.. \ulink{FileEntry}{https://tix.sourceforge.net/dist/current/demos/samples/FileEnt.tcl} - - -Hierarchical ListBox -^^^^^^^^^^^^^^^^^^^^ - - -.. class:: HList() - - The `HList - `_ widget - can be used to display any data that have a hierarchical structure, for example, - file system directory trees. The list entries are indented and connected by - branch lines according to their places in the hierarchy. - -.. Python Demo of: -.. \ulink{HList}{https://tix.sourceforge.net/dist/current/demos/samples/HList1.tcl} - - -.. class:: CheckList() - - The `CheckList - `_ - widget displays a list of items to be selected by the user. CheckList acts - similarly to the Tk checkbutton or radiobutton widgets, except it is capable of - handling many more items than checkbuttons or radiobuttons. - -.. Python Demo of: -.. \ulink{ CheckList}{https://tix.sourceforge.net/dist/current/demos/samples/ChkList.tcl} -.. Python Demo of: -.. \ulink{ScrolledHList (1)}{https://tix.sourceforge.net/dist/current/demos/samples/SHList.tcl} -.. Python Demo of: -.. \ulink{ScrolledHList (2)}{https://tix.sourceforge.net/dist/current/demos/samples/SHList2.tcl} - - -.. class:: Tree() - - The `Tree - `_ widget - can be used to display hierarchical data in a tree form. The user can adjust the - view of the tree by opening or closing parts of the tree. - -.. Python Demo of: -.. \ulink{Tree}{https://tix.sourceforge.net/dist/current/demos/samples/Tree.tcl} -.. Python Demo of: -.. \ulink{Tree (Dynamic)}{https://tix.sourceforge.net/dist/current/demos/samples/DynTree.tcl} - - -Tabular ListBox -^^^^^^^^^^^^^^^ - - -.. class:: TList() - - The `TList - `_ widget - can be used to display data in a tabular format. The list entries of a - :class:`TList` widget are similar to the entries in the Tk listbox widget. The - main differences are (1) the :class:`TList` widget can display the list entries - in a two dimensional format and (2) you can use graphical images as well as - multiple colors and fonts for the list entries. - -.. Python Demo of: -.. \ulink{ScrolledTList (1)}{https://tix.sourceforge.net/dist/current/demos/samples/STList1.tcl} -.. Python Demo of: -.. \ulink{ScrolledTList (2)}{https://tix.sourceforge.net/dist/current/demos/samples/STList2.tcl} -.. Grid has yet to be added to Python -.. \subsubsection{Grid Widget} -.. Python Demo of: -.. \ulink{Simple Grid}{https://tix.sourceforge.net/dist/current/demos/samples/SGrid0.tcl} -.. Python Demo of: -.. \ulink{ScrolledGrid}{https://tix.sourceforge.net/dist/current/demos/samples/SGrid1.tcl} -.. Python Demo of: -.. \ulink{Editable Grid}{https://tix.sourceforge.net/dist/current/demos/samples/EditGrid.tcl} - - -Manager Widgets -^^^^^^^^^^^^^^^ - - -.. class:: PanedWindow() - - The `PanedWindow - `_ - widget allows the user to interactively manipulate the sizes of several panes. - The panes can be arranged either vertically or horizontally. The user changes - the sizes of the panes by dragging the resize handle between two panes. - -.. Python Demo of: -.. \ulink{PanedWindow}{https://tix.sourceforge.net/dist/current/demos/samples/PanedWin.tcl} - - -.. class:: ListNoteBook() - - The `ListNoteBook - `_ - widget is very similar to the :class:`TixNoteBook` widget: it can be used to - display many windows in a limited space using a notebook metaphor. The notebook - is divided into a stack of pages (windows). At one time only one of these pages - can be shown. The user can navigate through these pages by choosing the name of - the desired page in the :attr:`hlist` subwidget. - -.. Python Demo of: -.. \ulink{ListNoteBook}{https://tix.sourceforge.net/dist/current/demos/samples/ListNBK.tcl} - - -.. class:: NoteBook() - - The `NoteBook - `_ - widget can be used to display many windows in a limited space using a notebook - metaphor. The notebook is divided into a stack of pages. At one time only one of - these pages can be shown. The user can navigate through these pages by choosing - the visual "tabs" at the top of the NoteBook widget. - -.. Python Demo of: -.. \ulink{NoteBook}{https://tix.sourceforge.net/dist/current/demos/samples/NoteBook.tcl} - -.. \subsubsection{Scrolled Widgets} -.. Python Demo of: -.. \ulink{ScrolledListBox}{https://tix.sourceforge.net/dist/current/demos/samples/SListBox.tcl} -.. Python Demo of: -.. \ulink{ScrolledText}{https://tix.sourceforge.net/dist/current/demos/samples/SText.tcl} -.. Python Demo of: -.. \ulink{ScrolledWindow}{https://tix.sourceforge.net/dist/current/demos/samples/SWindow.tcl} -.. Python Demo of: -.. \ulink{Canvas Object View}{https://tix.sourceforge.net/dist/current/demos/samples/CObjView.tcl} - - -Image Types -^^^^^^^^^^^ - -The :mod:`tkinter.tix` module adds: - -* `pixmap `_ - capabilities to all :mod:`tkinter.tix` and :mod:`tkinter` widgets to create - color images from XPM files. - - .. Python Demo of: - .. \ulink{XPM Image In Button}{https://tix.sourceforge.net/dist/current/demos/samples/Xpm.tcl} - .. Python Demo of: - .. \ulink{XPM Image In Menu}{https://tix.sourceforge.net/dist/current/demos/samples/Xpm1.tcl} - -* `Compound - `_ image - types can be used to create images that consists of multiple horizontal lines; - each line is composed of a series of items (texts, bitmaps, images or spaces) - arranged from left to right. For example, a compound image can be used to - display a bitmap and a text string simultaneously in a Tk :class:`Button` - widget. - - .. Python Demo of: - .. \ulink{Compound Image In Buttons}{https://tix.sourceforge.net/dist/current/demos/samples/CmpImg.tcl} - .. Python Demo of: - .. \ulink{Compound Image In NoteBook}{https://tix.sourceforge.net/dist/current/demos/samples/CmpImg2.tcl} - .. Python Demo of: - .. \ulink{Compound Image Notebook Color Tabs}{https://tix.sourceforge.net/dist/current/demos/samples/CmpImg4.tcl} - .. Python Demo of: - .. \ulink{Compound Image Icons}{https://tix.sourceforge.net/dist/current/demos/samples/CmpImg3.tcl} - - -Miscellaneous Widgets -^^^^^^^^^^^^^^^^^^^^^ - - -.. class:: InputOnly() - - The `InputOnly - `_ - widgets are to accept inputs from the user, which can be done with the ``bind`` - command (Unix only). - - -Form Geometry Manager -^^^^^^^^^^^^^^^^^^^^^ - -In addition, :mod:`tkinter.tix` augments :mod:`tkinter` by providing: - - -.. class:: Form() - - The `Form - `_ geometry - manager based on attachment rules for all Tk widgets. - - -Tix Commands ------------- - - -.. class:: tixCommand() - - The `tix commands - `_ provide - access to miscellaneous elements of :mod:`Tix`'s internal state and the - :mod:`Tix` application context. Most of the information manipulated by these - methods pertains to the application as a whole, or to a screen or display, - rather than to a particular window. - - To view the current settings, the common usage is:: - - from tkinter import tix - root = tix.Tk() - print(root.tix_configure()) - - -.. method:: tixCommand.tix_configure(cnf=None, **kw) - - Query or modify the configuration options of the Tix application context. If no - option is specified, returns a dictionary all of the available options. If - option is specified with no value, then the method returns a list describing the - one named option (this list will be identical to the corresponding sublist of - the value returned if no option is specified). If one or more option-value - pairs are specified, then the method modifies the given option(s) to have the - given value(s); in this case the method returns an empty string. Option may be - any of the configuration options. - - -.. method:: tixCommand.tix_cget(option) - - Returns the current value of the configuration option given by *option*. Option - may be any of the configuration options. - - -.. method:: tixCommand.tix_getbitmap(name) - - Locates a bitmap file of the name ``name.xpm`` or ``name`` in one of the bitmap - directories (see the :meth:`tix_addbitmapdir` method). By using - :meth:`tix_getbitmap`, you can avoid hard coding the pathnames of the bitmap - files in your application. When successful, it returns the complete pathname of - the bitmap file, prefixed with the character ``@``. The returned value can be - used to configure the ``bitmap`` option of the Tk and Tix widgets. - - -.. method:: tixCommand.tix_addbitmapdir(directory) - - Tix maintains a list of directories under which the :meth:`tix_getimage` and - :meth:`tix_getbitmap` methods will search for image files. The standard bitmap - directory is :file:`$TIX_LIBRARY/bitmaps`. The :meth:`tix_addbitmapdir` method - adds *directory* into this list. By using this method, the image files of an - applications can also be located using the :meth:`tix_getimage` or - :meth:`tix_getbitmap` method. - - -.. method:: tixCommand.tix_filedialog([dlgclass]) - - Returns the file selection dialog that may be shared among different calls from - this application. This method will create a file selection dialog widget when - it is called the first time. This dialog will be returned by all subsequent - calls to :meth:`tix_filedialog`. An optional dlgclass parameter can be passed - as a string to specified what type of file selection dialog widget is desired. - Possible options are ``tix``, ``FileSelectDialog`` or ``tixExFileSelectDialog``. - - -.. method:: tixCommand.tix_getimage(self, name) - - Locates an image file of the name :file:`name.xpm`, :file:`name.xbm` or - :file:`name.ppm` in one of the bitmap directories (see the - :meth:`tix_addbitmapdir` method above). If more than one file with the same name - (but different extensions) exist, then the image type is chosen according to the - depth of the X display: xbm images are chosen on monochrome displays and color - images are chosen on color displays. By using :meth:`tix_getimage`, you can - avoid hard coding the pathnames of the image files in your application. When - successful, this method returns the name of the newly created image, which can - be used to configure the ``image`` option of the Tk and Tix widgets. - - -.. method:: tixCommand.tix_option_get(name) - - Gets the options maintained by the Tix scheme mechanism. - - -.. method:: tixCommand.tix_resetoptions(newScheme, newFontSet[, newScmPrio]) - - Resets the scheme and fontset of the Tix application to *newScheme* and - *newFontSet*, respectively. This affects only those widgets created after this - call. Therefore, it is best to call the resetoptions method before the creation - of any widgets in a Tix application. - - The optional parameter *newScmPrio* can be given to reset the priority level of - the Tk options set by the Tix schemes. - - Because of the way Tk handles the X option database, after Tix has been has - imported and inited, it is not possible to reset the color schemes and font sets - using the :meth:`tix_config` method. Instead, the :meth:`tix_resetoptions` - method must be used. diff --git a/Doc/library/traceback.rst b/Doc/library/traceback.rst index 5c0e261b90763c..9a04b56947a1bb 100644 --- a/Doc/library/traceback.rst +++ b/Doc/library/traceback.rst @@ -218,7 +218,7 @@ The module also defines the following classes: :class:`TracebackException` objects are created from actual exceptions to capture data for later printing in a lightweight fashion. -.. class:: TracebackException(exc_type, exc_value, exc_traceback, *, limit=None, lookup_lines=True, capture_locals=False, compact=False) +.. class:: TracebackException(exc_type, exc_value, exc_traceback, *, limit=None, lookup_lines=True, capture_locals=False, compact=False, max_group_width=15, max_group_depth=10) Capture an exception for later rendering. *limit*, *lookup_lines* and *capture_locals* are as for the :class:`StackSummary` class. @@ -230,6 +230,12 @@ capture data for later printing in a lightweight fashion. Note that when locals are captured, they are also shown in the traceback. + *max_group_width* and *max_group_depth* control the formatting of exception + groups (see :exc:`BaseExceptionGroup`). The depth refers to the nesting + level of the group, and the width refers to the size of a single exception + group's exceptions array. The formatted output is truncated when either + limit is exceeded. + .. attribute:: __cause__ A :class:`TracebackException` of the original ``__cause__``. @@ -238,6 +244,14 @@ capture data for later printing in a lightweight fashion. A :class:`TracebackException` of the original ``__context__``. + .. attribute:: exceptions + + If ``self`` represents an :exc:`ExceptionGroup`, this field holds a list of + :class:`TracebackException` instances representing the nested exceptions. + Otherwise it is ``None``. + + .. versionadded:: 3.11 + .. attribute:: __suppress_context__ The ``__suppress_context__`` value from the original exception. @@ -323,6 +337,9 @@ capture data for later printing in a lightweight fashion. .. versionchanged:: 3.10 Added the *compact* parameter. + .. versionchanged:: 3.11 + Added the *max_group_width* and *max_group_depth* parameters. + :class:`StackSummary` Objects ----------------------------- diff --git a/Doc/library/turtle.rst b/Doc/library/turtle.rst index c656f6d9cfdaad..c9ce955a6d2ba4 100644 --- a/Doc/library/turtle.rst +++ b/Doc/library/turtle.rst @@ -166,7 +166,6 @@ Turtle state | :func:`resizemode` | :func:`shapesize` | :func:`turtlesize` | :func:`shearfactor` - | :func:`settiltangle` | :func:`tiltangle` | :func:`tilt` | :func:`shapetransform` @@ -1301,28 +1300,6 @@ Appearance >>> turtle.fd(50) -.. function:: settiltangle(angle) - - :param angle: a number - - Rotate the turtleshape to point in the direction specified by *angle*, - regardless of its current tilt-angle. *Do not* change the turtle's heading - (direction of movement). - - .. doctest:: - :skipif: _tkinter is None or 'always; deprecated method' - - >>> turtle.reset() - >>> turtle.shape("circle") - >>> turtle.shapesize(5,2) - >>> turtle.settiltangle(45) - >>> turtle.fd(50) - >>> turtle.settiltangle(-45) - >>> turtle.fd(50) - - .. deprecated:: 3.1 - - .. function:: tiltangle(angle=None) :param angle: a number (optional) @@ -2529,8 +2506,7 @@ Changes since Python 3.0 :func:`get_shapepoly` have been added. Thus the full range of regular linear transforms is now available for transforming turtle shapes. :func:`tiltangle` has been enhanced in functionality: it now can - be used to get or set the tilt angle. :func:`settiltangle` has been - deprecated. + be used to get or set the tilt angle. - The :class:`Screen` method :func:`onkeypress` has been added as a complement to :func:`onkey`. As the latter binds actions to the key release event, diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index eb7ee5a03b6b92..a9ea4b9fee3ad4 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -101,6 +101,8 @@ annotations. These include: * :pep:`692`: Using ``TypedDict`` for more precise ``**kwargs`` typing *Introducing* a new way of typing ``**kwargs`` with :data:`Unpack` and :data:`TypedDict` +* :pep:`695`: Type Parameter Syntax + *Introducing* builtin syntax for creating generic functions, classes, and type aliases. * :pep:`698`: Adding an override decorator to typing *Introducing* the :func:`@override` decorator @@ -109,10 +111,12 @@ annotations. These include: Type aliases ============ -A type alias is defined by assigning the type to the alias. In this example, -``Vector`` and ``list[float]`` will be treated as interchangeable synonyms:: +A type alias is defined using the :keyword:`type` statement, which creates +an instance of :class:`TypeAliasType`. In this example, +``Vector`` and ``list[float]`` will be treated equivalently by static type +checkers:: - Vector = list[float] + type Vector = list[float] def scale(scalar: float, vector: Vector) -> Vector: return [scalar * num for num in vector] @@ -124,9 +128,9 @@ Type aliases are useful for simplifying complex type signatures. For example:: from collections.abc import Sequence - ConnectionOptions = dict[str, str] - Address = tuple[str, int] - Server = tuple[Address, ConnectionOptions] + type ConnectionOptions = dict[str, str] + type Address = tuple[str, int] + type Server = tuple[Address, ConnectionOptions] def broadcast_message(message: str, servers: Sequence[Server]) -> None: ... @@ -141,6 +145,18 @@ Type aliases are useful for simplifying complex type signatures. For example:: Note that ``None`` as a type hint is a special case and is replaced by ``type(None)``. +The :keyword:`type` statement is new in Python 3.12. For backwards +compatibility, type aliases can also be created through simple assignment:: + + Vector = list[float] + +Or marked with :data:`TypeAlias` to make it explicit that this is a type alias, +not a normal variable assignment:: + + from typing import TypeAlias + + Vector: TypeAlias = list[float] + .. _distinct: NewType @@ -206,7 +222,7 @@ See :pep:`484` for more details. .. note:: Recall that the use of a type alias declares two types to be *equivalent* to - one another. Doing ``Alias = Original`` will make the static type checker + one another. Doing ``type Alias = Original`` will make the static type checker treat ``Alias`` as being *exactly equivalent* to ``Original`` in all cases. This is useful when you want to simplify complex type signatures. @@ -282,19 +298,26 @@ subscription to denote expected types for container elements. def notify_by_email(employees: Sequence[Employee], overrides: Mapping[str, str]) -> None: ... -Generics can be parameterized by using a factory available in typing -called :class:`TypeVar`. +Generics can be parameterized by using :ref:`type parameter syntax `:: -:: + from collections.abc import Sequence + + def first[T](l: Sequence[T]) -> T: # Generic function + return l[0] + +Or by using the :class:`TypeVar` factory directly:: from collections.abc import Sequence from typing import TypeVar - T = TypeVar('T') # Declare type variable + U = TypeVar('U') # Declare type variable - def first(l: Sequence[T]) -> T: # Generic function + def first(l: Sequence[U]) -> U: # Generic function return l[0] +.. versionchanged:: 3.12 + Syntactic support for generics is new in Python 3.12. + .. _user-defined-generics: User-defined generic types @@ -304,12 +327,9 @@ A user-defined class can be defined as a generic class. :: - from typing import TypeVar, Generic from logging import Logger - T = TypeVar('T') - - class LoggedVar(Generic[T]): + class LoggedVar[T]: def __init__(self, value: T, name: str, logger: Logger) -> None: self.name = name self.logger = logger @@ -326,12 +346,23 @@ A user-defined class can be defined as a generic class. def log(self, message: str) -> None: self.logger.info('%s: %s', self.name, message) -``Generic[T]`` as a base class defines that the class ``LoggedVar`` takes a -single type parameter ``T`` . This also makes ``T`` valid as a type within the -class body. +This syntax indicates that the class ``LoggedVar`` is parameterised around a +single :class:`type variable ` ``T`` . This also makes ``T`` valid as +a type within the class body. + +Generic classes implicitly inherit from :class:`Generic`. For compatibility +with Python 3.11 and lower, it is also possible to inherit explicitly from +:class:`Generic` to indicate a generic class:: + + from typing import TypeVar, Generic + + T = TypeVar('T') + + class LoggedVar(Generic[T]): + ... -The :class:`Generic` base class defines :meth:`~object.__class_getitem__` so -that ``LoggedVar[T]`` is valid as a type:: +Generic classes have :meth:`~object.__class_getitem__` methods, meaning they +can be parameterised at runtime (e.g. ``LoggedVar[int]`` below):: from collections.abc import Iterable @@ -344,11 +375,14 @@ A generic type can have any number of type variables. All varieties of from typing import TypeVar, Generic, Sequence - T = TypeVar('T', contravariant=True) - B = TypeVar('B', bound=Sequence[bytes], covariant=True) - S = TypeVar('S', int, str) + class WeirdTrio[T, B: Sequence[bytes], S: (int, str)]: + ... + + OldT = TypeVar('OldT', contravariant=True) + OldB = TypeVar('OldB', bound=Sequence[bytes], covariant=True) + OldS = TypeVar('OldS', int, str) - class WeirdTrio(Generic[T, B, S]): + class OldWeirdTrio(Generic[OldT, OldB, OldS]): ... Each type variable argument to :class:`Generic` must be distinct. @@ -357,29 +391,26 @@ This is thus invalid:: from typing import TypeVar, Generic ... + class Pair[M, M]: # SyntaxError + ... + T = TypeVar('T') class Pair(Generic[T, T]): # INVALID ... -You can use multiple inheritance with :class:`Generic`:: +Generic classes can also inherit from other classes:: from collections.abc import Sized - from typing import TypeVar, Generic - T = TypeVar('T') - - class LinkedList(Sized, Generic[T]): + class LinkedList[T](Sized): ... -When inheriting from generic classes, some type variables could be fixed:: +When inheriting from generic classes, some type parameters could be fixed:: from collections.abc import Mapping - from typing import TypeVar - - T = TypeVar('T') - class MyDict(Mapping[str, T]): + class MyDict[T](Mapping[str, T]): ... In this case ``MyDict`` has a single parameter, ``T``. @@ -392,49 +423,66 @@ not generic but implicitly inherits from ``Iterable[Any]``:: class MyIterable(Iterable): # Same as Iterable[Any] -User defined generic type aliases are also supported. Examples:: +User-defined generic type aliases are also supported. Examples:: from collections.abc import Iterable - from typing import TypeVar - S = TypeVar('S') - Response = Iterable[S] | int + + type Response[S] = Iterable[S] | int # Return type here is same as Iterable[str] | int def response(query: str) -> Response[str]: ... - T = TypeVar('T', int, float, complex) - Vec = Iterable[tuple[T, T]] + type Vec[T] = Iterable[tuple[T, T]] - def inproduct(v: Vec[T]) -> T: # Same as Iterable[tuple[T, T]] + def inproduct[T: (int, float, complex)](v: Vec[T]) -> T: # Same as Iterable[tuple[T, T]] return sum(x*y for x, y in v) +For backward compatibility, generic type aliases can also be created +through a simple assignment:: + + from collections.abc import Iterable + from typing import TypeVar + + S = TypeVar("S") + Response = Iterable[S] | int + .. versionchanged:: 3.7 :class:`Generic` no longer has a custom metaclass. +.. versionchanged:: 3.12 + Syntactic support for generics and type aliases is new in version 3.12. + Previously, generic classes had to explicitly inherit from :class:`Generic` + or contain a type variable in one of their bases. + User-defined generics for parameter expressions are also supported via parameter -specification variables in the form ``Generic[P]``. The behavior is consistent +specification variables in the form ``[**P]``. The behavior is consistent with type variables' described above as parameter specification variables are treated by the typing module as a specialized type variable. The one exception to this is that a list of types can be used to substitute a :class:`ParamSpec`:: - >>> from typing import Generic, ParamSpec, TypeVar - - >>> T = TypeVar('T') - >>> P = ParamSpec('P') - - >>> class Z(Generic[T, P]): ... + >>> class Z[T, **P]: ... # T is a TypeVar; P is a ParamSpec ... >>> Z[int, [dict, float]] __main__.Z[int, [dict, float]] +Classes generic over a :class:`ParamSpec` can also be created using explicit +inheritance from :class:`Generic`. In this case, ``**`` is not used:: -Furthermore, a generic with only one parameter specification variable will accept + from typing import ParamSpec, Generic + + P = ParamSpec('P') + + class Z(Generic[P]): + ... + +Another difference between :class:`TypeVar` and :class:`ParamSpec` is that a +generic with only one parameter specification variable will accept parameter lists in the forms ``X[[Type1, Type2, ...]]`` and also ``X[Type1, Type2, ...]`` for aesthetic reasons. Internally, the latter is converted to the former, so the following are equivalent:: - >>> class X(Generic[P]): ... + >>> class X[**P]: ... ... >>> X[int, str] __main__.X[[int, str]] @@ -670,20 +718,20 @@ These can be used as types in annotations and do not support ``[]``. This can be used to define a function that should never be called, or a function that never returns:: - from typing import Never + from typing import Never - def never_call_me(arg: Never) -> None: - pass + def never_call_me(arg: Never) -> None: + pass - def int_or_str(arg: int | str) -> None: - never_call_me(arg) # type checker error - match arg: - case int(): - print("It's an int") - case str(): - print("It's a str") - case _: - never_call_me(arg) # ok, arg is of type Never + def int_or_str(arg: int | str) -> None: + never_call_me(arg) # type checker error + match arg: + case int(): + print("It's an int") + case str(): + print("It's a str") + case _: + never_call_me(arg) # ok, arg is of type Never .. versionadded:: 3.11 @@ -717,9 +765,9 @@ These can be used as types in annotations and do not support ``[]``. from typing import Self class Foo: - def return_self(self) -> Self: - ... - return self + def return_self(self) -> Self: + ... + return self This annotation is semantically equivalent to the following, @@ -730,16 +778,16 @@ These can be used as types in annotations and do not support ``[]``. Self = TypeVar("Self", bound="Foo") class Foo: - def return_self(self: Self) -> Self: - ... - return self + def return_self(self: Self) -> Self: + ... + return self In general if something currently follows the pattern of:: class Foo: - def return_self(self) -> "Foo": - ... - return self + def return_self(self) -> "Foo": + ... + return self You should use :data:`Self` as calls to ``SubclassOfFoo.return_self`` would have ``Foo`` as the return type and not ``SubclassOfFoo``. @@ -767,6 +815,15 @@ These can be used as types in annotations and do not support ``[]``. .. versionadded:: 3.10 + .. deprecated:: 3.12 + :data:`TypeAlias` is deprecated in favor of the :keyword:`type` statement, + which creates instances of :class:`TypeAliasType`. + Note that while :data:`TypeAlias` and :class:`TypeAliasType` serve + similar purposes and have similar names, they are distinct and the + latter is not the type of the former. + Removal of :data:`TypeAlias` is not currently planned, but users + are encouraged to migrate to :keyword:`type` statements. + Special forms """"""""""""" @@ -1255,8 +1312,8 @@ These can be used as types in annotations using ``[]``, each having a unique syn from typing import TypedDict, Unpack class Movie(TypedDict): - name: str - year: int + name: str + year: int # This function expects two keyword arguments - `name` of type `str` # and `year` of type `int`. @@ -1266,213 +1323,327 @@ These can be used as types in annotations using ``[]``, each having a unique syn .. versionadded:: 3.11 -Building generic types -"""""""""""""""""""""" +Building generic types and type aliases +""""""""""""""""""""""""""""""""""""""" + +The following objects are not used directly in annotations. Instead, they are building blocks +for creating generic types and type aliases. -These are not used in annotations. They are building blocks for creating generic types. +These objects can be created through special syntax +(:ref:`type parameter lists ` and the :keyword:`type` statement). +For compatibility with Python 3.11 and earlier, they can also be created +without the dedicated syntax, as documented below. .. class:: Generic Abstract base class for generic types. - A generic type is typically declared by inheriting from an - instantiation of this class with one or more type variables. - For example, a generic mapping type might be defined as:: + A generic type is typically declared by adding a list of type parameters + after the class name:: - class Mapping(Generic[KT, VT]): + class Mapping[KT, VT]: def __getitem__(self, key: KT) -> VT: ... # Etc. - This class can then be used as follows:: + Such a class implicitly inherits from ``Generic``. + The runtime semantics of this syntax are discussed in the + :ref:`Language Reference `. - X = TypeVar('X') - Y = TypeVar('Y') + This class can then be used as follows:: - def lookup_name(mapping: Mapping[X, Y], key: X, default: Y) -> Y: + def lookup_name[X, Y](mapping: Mapping[X, Y], key: X, default: Y) -> Y: try: return mapping[key] except KeyError: return default -.. class:: TypeVar + Here the brackets after the function name indicate a + :ref:`generic function `. - Type variable. + For backwards compatibility, generic classes can also be + declared by explicitly inheriting from + ``Generic``. In this case, the type parameters must be declared + separately:: - Usage:: + KT = TypeVar('KT') + VT = TypeVar('VT') + + class Mapping(Generic[KT, VT]): + def __getitem__(self, key: KT) -> VT: + ... + # Etc. + +.. class:: TypeVar(name, *constraints, bound=None, covariant=False, contravariant=False, infer_variance=False) + + Type variable. + + The preferred way to construct a type variable is via the dedicated syntax + for :ref:`generic functions `, + :ref:`generic classes `, and + :ref:`generic type aliases `:: + + class Sequence[T]: # T is a TypeVar + ... + + This syntax can also be used to create bound and constrained type + variables:: + + class StrSequence[S: str]: # S is a TypeVar bound to str + ... + + + class StrOrBytesSequence[A: (str, bytes)]: # A is a TypeVar constrained to str or bytes + ... + + However, if desired, reusable type variables can also be constructed manually, like so:: T = TypeVar('T') # Can be anything S = TypeVar('S', bound=str) # Can be any subtype of str A = TypeVar('A', str, bytes) # Must be exactly str or bytes - Type variables exist primarily for the benefit of static type - checkers. They serve as the parameters for generic types as well - as for generic function definitions. See :class:`Generic` for more - information on generic types. Generic functions work as follows:: + Type variables exist primarily for the benefit of static type + checkers. They serve as the parameters for generic types as well + as for generic function and type alias definitions. + See :class:`Generic` for more + information on generic types. Generic functions work as follows:: + + def repeat[T](x: T, n: int) -> Sequence[T]: + """Return a list containing n references to x.""" + return [x]*n + + + def print_capitalized[S: str](x: S) -> S: + """Print x capitalized, and return x.""" + print(x.capitalize()) + return x - def repeat(x: T, n: int) -> Sequence[T]: - """Return a list containing n references to x.""" - return [x]*n + def concatenate[A: (str, bytes)](x: A, y: A) -> A: + """Add two strings or bytes objects together.""" + return x + y + + Note that type variables can be *bound*, *constrained*, or neither, but + cannot be both bound *and* constrained. + + The variance of type variables is inferred by type checkers when they are created + through the :ref:`type parameter syntax ` or when + ``infer_variance=True`` is passed. + Manually created type variables may be explicitly marked covariant or contravariant by passing + ``covariant=True`` or ``contravariant=True``. + By default, manually created type variables are invariant. + See :pep:`484` and :pep:`695` for more details. + + Bound type variables and constrained type variables have different + semantics in several important ways. Using a *bound* type variable means + that the ``TypeVar`` will be solved using the most specific type possible:: + + x = print_capitalized('a string') + reveal_type(x) # revealed type is str + + class StringSubclass(str): + pass + + y = print_capitalized(StringSubclass('another string')) + reveal_type(y) # revealed type is StringSubclass + + z = print_capitalized(45) # error: int is not a subtype of str + + Type variables can be bound to concrete types, abstract types (ABCs or + protocols), and even unions of types:: + + # Can be anything with an __abs__ method + def print_abs[T: SupportsAbs](arg: T) -> None: + print("Absolute value:", abs(arg)) + + U = TypeVar('U', bound=str|bytes) # Can be any subtype of the union str|bytes + V = TypeVar('V', bound=SupportsAbs) # Can be anything with an __abs__ method + + .. _typing-constrained-typevar: + + Using a *constrained* type variable, however, means that the ``TypeVar`` + can only ever be solved as being exactly one of the constraints given:: + + a = concatenate('one', 'two') + reveal_type(a) # revealed type is str + + b = concatenate(StringSubclass('one'), StringSubclass('two')) + reveal_type(b) # revealed type is str, despite StringSubclass being passed in + + c = concatenate('one', b'two') # error: type variable 'A' can be either str or bytes in a function call, but not both + + At runtime, ``isinstance(x, T)`` will raise :exc:`TypeError`. + + .. attribute:: __name__ + + The name of the type variable. - def print_capitalized(x: S) -> S: - """Print x capitalized, and return x.""" - print(x.capitalize()) - return x + .. attribute:: __covariant__ + Whether the type var has been explicitly marked as covariant. - def concatenate(x: A, y: A) -> A: - """Add two strings or bytes objects together.""" - return x + y + .. attribute:: __contravariant__ - Note that type variables can be *bound*, *constrained*, or neither, but - cannot be both bound *and* constrained. + Whether the type var has been explicitly marked as contravariant. - Bound type variables and constrained type variables have different - semantics in several important ways. Using a *bound* type variable means - that the ``TypeVar`` will be solved using the most specific type possible:: + .. attribute:: __infer_variance__ - x = print_capitalized('a string') - reveal_type(x) # revealed type is str + Whether the type variable's variance should be inferred by type checkers. - class StringSubclass(str): - pass + .. versionadded:: 3.12 - y = print_capitalized(StringSubclass('another string')) - reveal_type(y) # revealed type is StringSubclass + .. attribute:: __bound__ - z = print_capitalized(45) # error: int is not a subtype of str + The bound of the type variable, if any. - Type variables can be bound to concrete types, abstract types (ABCs or - protocols), and even unions of types:: + .. versionchanged:: 3.12 - U = TypeVar('U', bound=str|bytes) # Can be any subtype of the union str|bytes - V = TypeVar('V', bound=SupportsAbs) # Can be anything with an __abs__ method + For type variables created through :ref:`type parameter syntax `, + the bound is evaluated only when the attribute is accessed, not when + the type variable is created (see :ref:`lazy-evaluation`). -.. _typing-constrained-typevar: + .. attribute:: __constraints__ - Using a *constrained* type variable, however, means that the ``TypeVar`` - can only ever be solved as being exactly one of the constraints given:: + A tuple containing the constraints of the type variable, if any. - a = concatenate('one', 'two') - reveal_type(a) # revealed type is str + .. versionchanged:: 3.12 - b = concatenate(StringSubclass('one'), StringSubclass('two')) - reveal_type(b) # revealed type is str, despite StringSubclass being passed in + For type variables created through :ref:`type parameter syntax `, + the constraints are evaluated only when the attribute is accessed, not when + the type variable is created (see :ref:`lazy-evaluation`). - c = concatenate('one', b'two') # error: type variable 'A' can be either str or bytes in a function call, but not both + .. versionchanged:: 3.12 + + Type variables can now be declared using the + :ref:`type parameter ` syntax introduced by :pep:`695`. + The ``infer_variance`` parameter was added. + +.. class:: TypeVarTuple(name) + + Type variable tuple. A specialized form of :class:`type variable ` + that enables *variadic* generics. + + Type variable tuples can be declared in :ref:`type parameter lists ` + using a single asterisk (``*``) before the name:: - At runtime, ``isinstance(x, T)`` will raise :exc:`TypeError`. In general, - :func:`isinstance` and :func:`issubclass` should not be used with types. + def move_first_element_to_last[T, *Ts](tup: tuple[T, *Ts]) -> tuple[*Ts, T]: + return (*tup[1:], tup[0]) - Type variables may be marked covariant or contravariant by passing - ``covariant=True`` or ``contravariant=True``. See :pep:`484` for more - details. By default, type variables are invariant. + Or by explicitly invoking the ``TypeVarTuple`` constructor:: -.. class:: TypeVarTuple + T = TypeVar("T") + Ts = TypeVarTuple("Ts") + + def move_first_element_to_last(tup: tuple[T, *Ts]) -> tuple[*Ts, T]: + return (*tup[1:], tup[0]) - Type variable tuple. A specialized form of :class:`type variable ` - that enables *variadic* generics. + A normal type variable enables parameterization with a single type. A type + variable tuple, in contrast, allows parameterization with an + *arbitrary* number of types by acting like an *arbitrary* number of type + variables wrapped in a tuple. For example:: - A normal type variable enables parameterization with a single type. A type - variable tuple, in contrast, allows parameterization with an - *arbitrary* number of types by acting like an *arbitrary* number of type - variables wrapped in a tuple. For example:: + # T is bound to int, Ts is bound to () + # Return value is (1,), which has type tuple[int] + move_first_element_to_last(tup=(1,)) - T = TypeVar('T') - Ts = TypeVarTuple('Ts') + # T is bound to int, Ts is bound to (str,) + # Return value is ('spam', 1), which has type tuple[str, int] + move_first_element_to_last(tup=(1, 'spam')) - def move_first_element_to_last(tup: tuple[T, *Ts]) -> tuple[*Ts, T]: - return (*tup[1:], tup[0]) + # T is bound to int, Ts is bound to (str, float) + # Return value is ('spam', 3.0, 1), which has type tuple[str, float, int] + move_first_element_to_last(tup=(1, 'spam', 3.0)) - # T is bound to int, Ts is bound to () - # Return value is (1,), which has type tuple[int] - move_first_element_to_last(tup=(1,)) + # This fails to type check (and fails at runtime) + # because tuple[()] is not compatible with tuple[T, *Ts] + # (at least one element is required) + move_first_element_to_last(tup=()) - # T is bound to int, Ts is bound to (str,) - # Return value is ('spam', 1), which has type tuple[str, int] - move_first_element_to_last(tup=(1, 'spam')) + Note the use of the unpacking operator ``*`` in ``tuple[T, *Ts]``. + Conceptually, you can think of ``Ts`` as a tuple of type variables + ``(T1, T2, ...)``. ``tuple[T, *Ts]`` would then become + ``tuple[T, *(T1, T2, ...)]``, which is equivalent to + ``tuple[T, T1, T2, ...]``. (Note that in older versions of Python, you might + see this written using :data:`Unpack ` instead, as + ``Unpack[Ts]``.) - # T is bound to int, Ts is bound to (str, float) - # Return value is ('spam', 3.0, 1), which has type tuple[str, float, int] - move_first_element_to_last(tup=(1, 'spam', 3.0)) + Type variable tuples must *always* be unpacked. This helps distinguish type + variable tuples from normal type variables:: - # This fails to type check (and fails at runtime) - # because tuple[()] is not compatible with tuple[T, *Ts] - # (at least one element is required) - move_first_element_to_last(tup=()) + x: Ts # Not valid + x: tuple[Ts] # Not valid + x: tuple[*Ts] # The correct way to do it - Note the use of the unpacking operator ``*`` in ``tuple[T, *Ts]``. - Conceptually, you can think of ``Ts`` as a tuple of type variables - ``(T1, T2, ...)``. ``tuple[T, *Ts]`` would then become - ``tuple[T, *(T1, T2, ...)]``, which is equivalent to - ``tuple[T, T1, T2, ...]``. (Note that in older versions of Python, you might - see this written using :data:`Unpack ` instead, as - ``Unpack[Ts]``.) + Type variable tuples can be used in the same contexts as normal type + variables. For example, in class definitions, arguments, and return types:: - Type variable tuples must *always* be unpacked. This helps distinguish type - variable tuples from normal type variables:: + class Array[*Shape]: + def __getitem__(self, key: tuple[*Shape]) -> float: ... + def __abs__(self) -> "Array[*Shape]": ... + def get_shape(self) -> tuple[*Shape]: ... - x: Ts # Not valid - x: tuple[Ts] # Not valid - x: tuple[*Ts] # The correct way to do it + Type variable tuples can be happily combined with normal type variables:: - Type variable tuples can be used in the same contexts as normal type - variables. For example, in class definitions, arguments, and return types:: + DType = TypeVar('DType') - Shape = TypeVarTuple('Shape') - class Array(Generic[*Shape]): - def __getitem__(self, key: tuple[*Shape]) -> float: ... - def __abs__(self) -> "Array[*Shape]": ... - def get_shape(self) -> tuple[*Shape]: ... + class Array[DType, *Shape]: # This is fine + pass - Type variable tuples can be happily combined with normal type variables:: + class Array2[*Shape, DType]: # This would also be fine + pass - DType = TypeVar('DType') + float_array_1d: Array[float, Height] = Array() # Totally fine + int_array_2d: Array[int, Height, Width] = Array() # Yup, fine too - class Array(Generic[DType, *Shape]): # This is fine - pass + However, note that at most one type variable tuple may appear in a single + list of type arguments or type parameters:: - class Array2(Generic[*Shape, DType]): # This would also be fine - pass + x: tuple[*Ts, *Ts] # Not valid + class Array[*Shape, *Shape]: # Not valid + pass - float_array_1d: Array[float, Height] = Array() # Totally fine - int_array_2d: Array[int, Height, Width] = Array() # Yup, fine too + Finally, an unpacked type variable tuple can be used as the type annotation + of ``*args``:: - However, note that at most one type variable tuple may appear in a single - list of type arguments or type parameters:: + def call_soon[*Ts]( + callback: Callable[[*Ts], None], + *args: *Ts + ) -> None: + ... + callback(*args) - x: tuple[*Ts, *Ts] # Not valid - class Array(Generic[*Shape, *Shape]): # Not valid - pass + In contrast to non-unpacked annotations of ``*args`` - e.g. ``*args: int``, + which would specify that *all* arguments are ``int`` - ``*args: *Ts`` + enables reference to the types of the *individual* arguments in ``*args``. + Here, this allows us to ensure the types of the ``*args`` passed + to ``call_soon`` match the types of the (positional) arguments of + ``callback``. - Finally, an unpacked type variable tuple can be used as the type annotation - of ``*args``:: + See :pep:`646` for more details on type variable tuples. + + .. attribute:: __name__ - def call_soon( - callback: Callable[[*Ts], None], - *args: *Ts - ) -> None: - ... - callback(*args) + The name of the type variable tuple. - In contrast to non-unpacked annotations of ``*args`` - e.g. ``*args: int``, - which would specify that *all* arguments are ``int`` - ``*args: *Ts`` - enables reference to the types of the *individual* arguments in ``*args``. - Here, this allows us to ensure the types of the ``*args`` passed - to ``call_soon`` match the types of the (positional) arguments of - ``callback``. + .. versionadded:: 3.11 - See :pep:`646` for more details on type variable tuples. + .. versionchanged:: 3.12 - .. versionadded:: 3.11 + Type variable tuples can now be declared using the + :ref:`type parameter ` syntax introduced by :pep:`695`. .. class:: ParamSpec(name, *, bound=None, covariant=False, contravariant=False) Parameter specification variable. A specialized version of :class:`type variables `. - Usage:: + In :ref:`type parameter lists `, parameter specifications + can be declared with two asterisks (``**``):: + + type IntFunc[**P] = Callable[P, int] + + For compatibility with Python 3.11 and earlier, ``ParamSpec`` objects + can also be created as follows:: P = ParamSpec('P') @@ -1489,13 +1660,9 @@ These are not used in annotations. They are building blocks for creating generic new callable returned by it have inter-dependent type parameters:: from collections.abc import Callable - from typing import TypeVar, ParamSpec import logging - T = TypeVar('T') - P = ParamSpec('P') - - def add_logging(f: Callable[P, T]) -> Callable[P, T]: + def add_logging[T, **P](f: Callable[P, T]) -> Callable[P, T]: '''A type-safe decorator to add logging to a function.''' def inner(*args: P.args, **kwargs: P.kwargs) -> T: logging.info(f'{f.__name__} was called') @@ -1530,6 +1697,10 @@ These are not used in annotations. They are building blocks for creating generic ``P.args`` and ``P.kwargs`` are instances respectively of :class:`ParamSpecArgs` and :class:`ParamSpecKwargs`. + .. attribute:: __name__ + + The name of the parameter specification. + Parameter specification variables created with ``covariant=True`` or ``contravariant=True`` can be used to declare covariant or contravariant generic types. The ``bound`` argument is also accepted, similar to @@ -1538,6 +1709,11 @@ These are not used in annotations. They are building blocks for creating generic .. versionadded:: 3.10 + .. versionchanged:: 3.12 + + Parameter specifications can now be declared using the + :ref:`type parameter ` syntax introduced by :pep:`695`. + .. note:: Only parameter specification variables defined in global scope can be pickled. @@ -1565,6 +1741,67 @@ These are not used in annotations. They are building blocks for creating generic .. versionadded:: 3.10 +.. class:: TypeAliasType(name, value, *, type_params=()) + + The type of type aliases created through the :keyword:`type` statement. + + Example:: + + >>> type Alias = int + >>> type(Alias) + + + .. versionadded:: 3.12 + + .. attribute:: __name__ + + The name of the type alias:: + + >>> type Alias = int + >>> Alias.__name__ + 'Alias' + + .. attribute:: __module__ + + The module in which the type alias was defined:: + + >>> type Alias = int + >>> Alias.__module__ + '__main__' + + .. attribute:: __type_params__ + + The type parameters of the type alias, or an empty tuple if the alias is + not generic: + + .. doctest:: + + >>> type ListOrSet[T] = list[T] | set[T] + >>> ListOrSet.__type_params__ + (T,) + >>> type NotGeneric = int + >>> NotGeneric.__type_params__ + () + + .. attribute:: __value__ + + The type alias's value. This is :ref:`lazily evaluated `, + so names used in the definition of the alias are not resolved until the + ``__value__`` attribute is accessed: + + .. doctest:: + + >>> type Mutually = Recursive + >>> type Recursive = Mutually + >>> Mutually + Mutually + >>> Recursive + Recursive + >>> Mutually.__value__ + Recursive + >>> Recursive.__value__ + Mutually + Other special directives """""""""""""""""""""""" @@ -1613,12 +1850,18 @@ These are not used in annotations. They are building blocks for declaring types. ``NamedTuple`` subclasses can be generic:: - class Group(NamedTuple, Generic[T]): + class Group[T](NamedTuple): key: T group: list[T] Backward-compatible usage:: + # For creating a generic NamedTuple on Python 3.11 or lower + class Group(NamedTuple, Generic[T]): + key: T + group: list[T] + + # A functional syntax is also supported Employee = NamedTuple('Employee', [('name', str), ('id', int)]) .. versionchanged:: 3.6 @@ -1692,6 +1935,15 @@ These are not used in annotations. They are building blocks for declaring types. Protocol classes can be generic, for example:: + class GenProto[T](Protocol): + def meth(self) -> T: + ... + + In code that needs to be compatible with Python 3.11 or older, generic + Protocols can be written as follows:: + + T = TypeVar("T") + class GenProto(Protocol[T]): def meth(self) -> T: ... @@ -1876,6 +2128,13 @@ These are not used in annotations. They are building blocks for declaring types. A ``TypedDict`` can be generic:: + class Group[T](TypedDict): + key: T + group: list[T] + + To create a generic ``TypedDict`` that is compatible with Python 3.11 + or lower, inherit from :class:`Generic` explicitly:: + class Group(TypedDict, Generic[T]): key: T group: list[T] @@ -1977,12 +2236,10 @@ Corresponding to built-in types This type may be used as follows:: - T = TypeVar('T', int, float) - - def vec2(x: T, y: T) -> List[T]: + def vec2[T: (int, float)](x: T, y: T) -> List[T]: return [x, y] - def keep_positives(vector: Sequence[T]) -> List[T]: + def keep_positives[T: (int, float)](vector: Sequence[T]) -> List[T]: return [item for item in vector if item > 0] .. deprecated:: 3.9 @@ -2173,8 +2430,8 @@ Corresponding to collections in :mod:`collections.abc` A generic version of :class:`collections.abc.Mapping`. This type can be used as follows:: - def get_position_in_index(word_list: Mapping[str, int], word: str) -> int: - return word_list[word] + def get_position_in_index(word_list: Mapping[str, int], word: str) -> int: + return word_list[word] .. deprecated:: 3.9 :class:`collections.abc.Mapping` now supports subscripting (``[]``). @@ -2987,3 +3244,5 @@ convenience. This is subject to change, and not all deprecations are listed. | ``typing.Hashable`` and | 3.12 | Undecided | :gh:`94309` | | ``typing.Sized`` | | | | +----------------------------------+---------------+-------------------+----------------+ +| ``typing.TypeAlias`` | 3.12 | Undecided | :pep:`695` | ++----------------------------------+---------------+-------------------+----------------+ diff --git a/Doc/library/webbrowser.rst b/Doc/library/webbrowser.rst index 61db8042093627..2f0b5feb8cbc75 100644 --- a/Doc/library/webbrowser.rst +++ b/Doc/library/webbrowser.rst @@ -20,7 +20,7 @@ will be used if graphical browsers are not available or an X11 display isn't available. If text-mode browsers are used, the calling process will block until the user exits the browser. -If the environment variable :envvar:`BROWSER` exists, it is interpreted as the +If the environment variable :envvar:`!BROWSER` exists, it is interpreted as the :data:`os.pathsep`-separated list of browsers to try ahead of the platform defaults. When the value of a list part contains the string ``%s``, then it is interpreted as a literal browser command line to be used with the argument URL @@ -97,7 +97,7 @@ The following functions are defined: Setting *preferred* to ``True`` makes this browser a preferred result for a :func:`get` call with no argument. Otherwise, this entry point is only - useful if you plan to either set the :envvar:`BROWSER` variable or call + useful if you plan to either set the :envvar:`!BROWSER` variable or call :func:`get` with a nonempty argument matching the name of a handler you declare. @@ -111,41 +111,41 @@ for the controller classes, all defined in this module. +------------------------+-----------------------------------------+-------+ | Type Name | Class Name | Notes | +========================+=========================================+=======+ -| ``'mozilla'`` | :class:`Mozilla('mozilla')` | | +| ``'mozilla'`` | ``Mozilla('mozilla')`` | | +------------------------+-----------------------------------------+-------+ -| ``'firefox'`` | :class:`Mozilla('mozilla')` | | +| ``'firefox'`` | ``Mozilla('mozilla')`` | | +------------------------+-----------------------------------------+-------+ -| ``'epiphany'`` | :class:`Epiphany('epiphany')` | | +| ``'epiphany'`` | ``Epiphany('epiphany')`` | | +------------------------+-----------------------------------------+-------+ -| ``'kfmclient'`` | :class:`Konqueror()` | \(1) | +| ``'kfmclient'`` | ``Konqueror()`` | \(1) | +------------------------+-----------------------------------------+-------+ -| ``'konqueror'`` | :class:`Konqueror()` | \(1) | +| ``'konqueror'`` | ``Konqueror()`` | \(1) | +------------------------+-----------------------------------------+-------+ -| ``'kfm'`` | :class:`Konqueror()` | \(1) | +| ``'kfm'`` | ``Konqueror()`` | \(1) | +------------------------+-----------------------------------------+-------+ -| ``'opera'`` | :class:`Opera()` | | +| ``'opera'`` | ``Opera()`` | | +------------------------+-----------------------------------------+-------+ -| ``'links'`` | :class:`GenericBrowser('links')` | | +| ``'links'`` | ``GenericBrowser('links')`` | | +------------------------+-----------------------------------------+-------+ -| ``'elinks'`` | :class:`Elinks('elinks')` | | +| ``'elinks'`` | ``Elinks('elinks')`` | | +------------------------+-----------------------------------------+-------+ -| ``'lynx'`` | :class:`GenericBrowser('lynx')` | | +| ``'lynx'`` | ``GenericBrowser('lynx')`` | | +------------------------+-----------------------------------------+-------+ -| ``'w3m'`` | :class:`GenericBrowser('w3m')` | | +| ``'w3m'`` | ``GenericBrowser('w3m')`` | | +------------------------+-----------------------------------------+-------+ -| ``'windows-default'`` | :class:`WindowsDefault` | \(2) | +| ``'windows-default'`` | ``WindowsDefault`` | \(2) | +------------------------+-----------------------------------------+-------+ -| ``'macosx'`` | :class:`MacOSXOSAScript('default')` | \(3) | +| ``'macosx'`` | ``MacOSXOSAScript('default')`` | \(3) | +------------------------+-----------------------------------------+-------+ -| ``'safari'`` | :class:`MacOSXOSAScript('safari')` | \(3) | +| ``'safari'`` | ``MacOSXOSAScript('safari')`` | \(3) | +------------------------+-----------------------------------------+-------+ -| ``'google-chrome'`` | :class:`Chrome('google-chrome')` | | +| ``'google-chrome'`` | ``Chrome('google-chrome')`` | | +------------------------+-----------------------------------------+-------+ -| ``'chrome'`` | :class:`Chrome('chrome')` | | +| ``'chrome'`` | ``Chrome('chrome')`` | | +------------------------+-----------------------------------------+-------+ -| ``'chromium'`` | :class:`Chromium('chromium')` | | +| ``'chromium'`` | ``Chromium('chromium')`` | | +------------------------+-----------------------------------------+-------+ -| ``'chromium-browser'`` | :class:`Chromium('chromium-browser')` | | +| ``'chromium-browser'`` | ``Chromium('chromium-browser')`` | | +------------------------+-----------------------------------------+-------+ Notes: @@ -153,7 +153,7 @@ Notes: (1) "Konqueror" is the file manager for the KDE desktop environment for Unix, and only makes sense to use if KDE is running. Some way of reliably detecting KDE - would be nice; the :envvar:`KDEDIR` variable is not sufficient. Note also that + would be nice; the :envvar:`!KDEDIR` variable is not sufficient. Note also that the name "kfm" is used even when using the :program:`konqueror` command with KDE 2 --- the implementation selects the best strategy for running Konqueror. @@ -163,6 +163,11 @@ Notes: (3) Only on macOS platform. +.. versionadded:: 3.2 + A new :class:`!MacOSXOSAScript` class has been added + and is used on Mac instead of the previous :class:`!MacOSX` class. + This adds support for opening browsers not currently set as the OS default. + .. versionadded:: 3.3 Support for Chrome/Chromium has been added. @@ -171,9 +176,6 @@ Notes: Removed browsers include Grail, Mosaic, Netscape, Galeon, Skipstone, Iceape, and Firefox versions 35 and below. -.. deprecated-removed:: 3.11 3.13 - :class:`MacOSX` is deprecated, use :class:`MacOSXOSAScript` instead. - Here are some simple examples:: url = 'https://docs.python.org/' @@ -222,4 +224,4 @@ module-level convenience functions: .. rubric:: Footnotes .. [1] Executables named here without a full path will be searched in the - directories given in the :envvar:`PATH` environment variable. + directories given in the :envvar:`!PATH` environment variable. diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst index 9d1e5b6c596d9f..6d30eccab1990f 100644 --- a/Doc/reference/compound_stmts.rst +++ b/Doc/reference/compound_stmts.rst @@ -1206,7 +1206,7 @@ A function definition defines a user-defined function object (see section :ref:`types`): .. productionlist:: python-grammar - funcdef: [`decorators`] "def" `funcname` "(" [`parameter_list`] ")" + funcdef: [`decorators`] "def" `funcname` [`type_params`] "(" [`parameter_list`] ")" : ["->" `expression`] ":" `suite` decorators: `decorator`+ decorator: "@" `assignment_expression` NEWLINE @@ -1256,6 +1256,15 @@ except that the original function is not temporarily bound to the name ``func``. :token:`~python-grammar:assignment_expression`. Previously, the grammar was much more restrictive; see :pep:`614` for details. +A list of :ref:`type parameters ` may be given in square brackets +between the function's name and the opening parenthesis for its parameter list. +This indicates to static type checkers that the function is generic. At runtime, +the type parameters can be retrieved from the function's ``__type_params__`` +attribute. See :ref:`generic-functions` for more. + +.. versionchanged:: 3.12 + Type parameter lists are new in Python 3.12. + .. index:: triple: default; parameter; value single: argument; function definition @@ -1378,7 +1387,7 @@ Class definitions A class definition defines a class object (see section :ref:`types`): .. productionlist:: python-grammar - classdef: [`decorators`] "class" `classname` [`inheritance`] ":" `suite` + classdef: [`decorators`] "class" `classname` [`type_params`] [`inheritance`] ":" `suite` inheritance: "(" [`argument_list`] ")" classname: `identifier` @@ -1434,6 +1443,15 @@ decorators. The result is then bound to the class name. :token:`~python-grammar:assignment_expression`. Previously, the grammar was much more restrictive; see :pep:`614` for details. +A list of :ref:`type parameters ` may be given in square brackets +immediately after the class's name. +This indicates to static type checkers that the class is generic. At runtime, +the type parameters can be retrieved from the class's ``__type_params__`` +attribute. See :ref:`generic-classes` for more. + +.. versionchanged:: 3.12 + Type parameter lists are new in Python 3.12. + **Programmer's note:** Variables defined in the class definition are class attributes; they are shared by instances. Instance attributes can be set in a method with ``self.name = value``. Both class and instance attributes are @@ -1589,6 +1607,228 @@ body of a coroutine function. The proposal that made coroutines a proper standalone concept in Python, and added supporting syntax. +.. _type-params: + +Type parameter lists +==================== + +.. versionadded:: 3.12 + +.. index:: + single: type parameters + +.. productionlist:: python-grammar + type_params: "[" `type_param` ("," `type_param`)* "]" + type_param: `typevar` | `typevartuple` | `paramspec` + typevar: `identifier` (":" `expression`)? + typevartuple: "*" `identifier` + paramspec: "**" `identifier` + +:ref:`Functions ` (including :ref:`coroutines `), +:ref:`classes ` and :ref:`type aliases ` may +contain a type parameter list:: + + def max[T](args: list[T]) -> T: + ... + + async def amax[T](args: list[T]) -> T: + ... + + class Bag[T]: + def __iter__(self) -> Iterator[T]: + ... + + def add(self, arg: T) -> None: + ... + + type ListOrSet[T] = list[T] | set[T] + +Semantically, this indicates that the function, class, or type alias is +generic over a type variable. This information is primarily used by static +type checkers, and at runtime, generic objects behave much like their +non-generic counterparts. + +Type parameters are declared in square brackets (``[]``) immediately +after the name of the function, class, or type alias. The type parameters +are accessible within the scope of the generic object, but not elsewhere. +Thus, after a declaration ``def func[T](): pass``, the name ``T`` is not available in +the module scope. Below, the semantics of generic objects are described +with more precision. The scope of type parameters is modeled with a special +function (technically, an :ref:`annotation scope `) that +wraps the creation of the generic object. + +Generic functions, classes, and type aliases have a :attr:`!__type_params__` +attribute listing their type parameters. + +Type parameters come in three kinds: + +* :data:`typing.TypeVar`, introduced by a plain name (e.g., ``T``). Semantically, this + represents a single type to a type checker. +* :data:`typing.TypeVarTuple`, introduced by a name prefixed with a single + asterisk (e.g., ``*Ts``). Semantically, this stands for a tuple of any + number of types. +* :data:`typing.ParamSpec`, introduced by a name prefixed with two asterisks + (e.g., ``**P``). Semantically, this stands for the parameters of a callable. + +:data:`typing.TypeVar` declarations can define *bounds* and *constraints* with +a colon (``:``) followed by an expression. A single expression after the colon +indicates a bound (e.g. ``T: int``). Semantically, this means +that the :data:`!typing.TypeVar` can only represent types that are a subtype of +this bound. A parenthesized tuple of expressions after the colon indicates a +set of constraints (e.g. ``T: (str, bytes)``). Each member of the tuple should be a +type (again, this is not enforced at runtime). Constrained type variables can only +take on one of the types in the list of constraints. + +For :data:`!typing.TypeVar`\ s declared using the type parameter list syntax, +the bound and constraints are not evaluated when the generic object is created, +but only when the value is explicitly accessed through the attributes ``__bound__`` +and ``__constraints__``. To accomplish this, the bounds or constraints are +evaluated in a separate :ref:`annotation scope `. + +:data:`typing.TypeVarTuple`\ s and :data:`typing.ParamSpec`\ s cannot have bounds +or constraints. + +The following example indicates the full set of allowed type parameter declarations:: + + def overly_generic[ + SimpleTypeVar, + TypeVarWithBound: int, + TypeVarWithConstraints: (str, bytes), + *SimpleTypeVarTuple, + **SimpleParamSpec, + ]( + a: SimpleTypeVar, + b: TypeVarWithBound, + c: Callable[SimpleParamSpec, TypeVarWithConstraints], + *d: SimpleTypeVarTuple, + ): ... + +.. _generic-functions: + +Generic functions +----------------- + +Generic functions are declared as follows:: + + def func[T](arg: T): ... + +This syntax is equivalent to:: + + annotation-def TYPE_PARAMS_OF_func(): + T = typing.TypeVar("T") + def func(arg: T): ... + func.__type_params__ = (T,) + return func + func = TYPE_PARAMS_OF_func() + +Here ``annotation-def`` indicates an :ref:`annotation scope `, +which is not actually bound to any name at runtime. (One +other liberty is taken in the translation: the syntax does not go through +attribute access on the :mod:`typing` module, but creates an instance of +:data:`typing.TypeVar` directly.) + +The annotations of generic functions are evaluated within the annotation scope +used for declaring the type parameters, but the function's defaults and +decorators are not. + +The following example illustrates the scoping rules for these cases, +as well as for additional flavors of type parameters:: + + @decorator + def func[T: int, *Ts, **P](*args: *Ts, arg: Callable[P, T] = some_default): + ... + +Except for the :ref:`lazy evaluation ` of the +:class:`~typing.TypeVar` bound, this is equivalent to:: + + DEFAULT_OF_arg = some_default + + annotation-def TYPE_PARAMS_OF_func(): + + annotation-def BOUND_OF_T(): + return int + # In reality, BOUND_OF_T() is evaluated only on demand. + T = typing.TypeVar("T", bound=BOUND_OF_T()) + + Ts = typing.TypeVarTuple("Ts") + P = typing.ParamSpec("P") + + def func(*args: *Ts, arg: Callable[P, T] = DEFAULT_OF_arg): + ... + + func.__type_params__ = (T, Ts, P) + return func + func = decorator(TYPE_PARAMS_OF_func()) + +The capitalized names like ``DEFAULT_OF_arg`` are not actually +bound at runtime. + +.. _generic-classes: + +Generic classes +--------------- + +Generic classes are declared as follows:: + + class Bag[T]: ... + +This syntax is equivalent to:: + + annotation-def TYPE_PARAMS_OF_Bag(): + T = typing.TypeVar("T") + class Bag(typing.Generic[T]): + __type_params__ = (T,) + ... + return Bag + Bag = TYPE_PARAMS_OF_Bag() + +Here again ``annotation-def`` (not a real keyword) indicates an +:ref:`annotation scope `, and the name +``TYPE_PARAMS_OF_Bag`` is not actually bound at runtime. + +Generic classes implicitly inherit from :data:`typing.Generic`. +The base classes and keyword arguments of generic classes are +evaluated within the type scope for the type parameters, +and decorators are evaluated outside that scope. This is illustrated +by this example:: + + @decorator + class Bag(Base[T], arg=T): ... + +This is equivalent to:: + + annotation-def TYPE_PARAMS_OF_Bag(): + T = typing.TypeVar("T") + class Bag(Base[T], typing.Generic[T], arg=T): + __type_params__ = (T,) + ... + return Bag + Bag = decorator(TYPE_PARAMS_OF_Bag()) + +.. _generic-type-aliases: + +Generic type aliases +-------------------- + +The :keyword:`type` statement can also be used to create a generic type alias:: + + type ListOrSet[T] = list[T] | set[T] + +Except for the :ref:`lazy evaluation ` of the value, +this is equivalent to:: + + annotation-def TYPE_PARAMS_OF_ListOrSet(): + T = typing.TypeVar("T") + + annotation-def VALUE_OF_ListOrSet(): + return list[T] | set[T] + # In reality, the value is lazily evaluated + return typing.TypeAliasType("ListOrSet", VALUE_OF_ListOrSet(), type_params=(T,)) + ListOrSet = TYPE_PARAMS_OF_ListOrSet() + +Here, ``annotation-def`` (not a real keyword) indicates an +:ref:`annotation scope `. The capitalized names +like ``TYPE_PARAMS_OF_ListOrSet`` are not actually bound at runtime. .. rubric:: Footnotes diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index c0734e49f29192..e8f9775dd33ce1 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -499,6 +499,7 @@ Callable types single: __globals__ (function attribute) single: __annotations__ (function attribute) single: __kwdefaults__ (function attribute) + single: __type_params__ (function attribute) pair: global; namespace +-------------------------+-------------------------------+-----------+ @@ -561,6 +562,12 @@ Callable types | :attr:`__kwdefaults__` | A dict containing defaults | Writable | | | for keyword-only parameters. | | +-------------------------+-------------------------------+-----------+ + | :attr:`__type_params__` | A tuple containing the | Writable | + | | :ref:`type parameters | | + | | ` of a | | + | | :ref:`generic function | | + | | `. | | + +-------------------------+-------------------------------+-----------+ Most of the attributes labelled "Writable" check the type of the assigned value. @@ -837,6 +844,7 @@ Custom classes single: __bases__ (class attribute) single: __doc__ (class attribute) single: __annotations__ (class attribute) + single: __type_params__ (class attribute) Special attributes: @@ -863,6 +871,10 @@ Custom classes working with :attr:`__annotations__`, please see :ref:`annotations-howto`. + :attr:`__type_params__` + A tuple containing the :ref:`type parameters ` of + a :ref:`generic class `. + Class instances .. index:: pair: object; class instance diff --git a/Doc/reference/executionmodel.rst b/Doc/reference/executionmodel.rst index 8917243999d399..cea3a56ba51644 100644 --- a/Doc/reference/executionmodel.rst +++ b/Doc/reference/executionmodel.rst @@ -71,6 +71,8 @@ The following constructs bind names: + in a capture pattern in structural pattern matching * :keyword:`import` statements. +* :keyword:`type` statements. +* :ref:`type parameter lists `. The :keyword:`!import` statement of the form ``from ... import *`` binds all names defined in the imported module, except those beginning with an underscore. @@ -149,7 +151,8 @@ a global statement, the free variable is treated as a global. The :keyword:`nonlocal` statement causes corresponding names to refer to previously bound variables in the nearest enclosing function scope. :exc:`SyntaxError` is raised at compile time if the given name does not -exist in any enclosing function scope. +exist in any enclosing function scope. :ref:`Type parameters ` +cannot be rebound with the :keyword:`!nonlocal` statement. .. index:: pair: module; __main__ @@ -163,14 +166,119 @@ These references follow the normal rules for name resolution with an exception that unbound local variables are looked up in the global namespace. The namespace of the class definition becomes the attribute dictionary of the class. The scope of names defined in a class block is limited to the -class block; it does not extend to the code blocks of methods -- this includes -comprehensions and generator expressions since they are implemented using a -function scope. This means that the following will fail:: +class block; it does not extend to the code blocks of methods. This includes +comprehensions and generator expressions, but it does not include +:ref:`annotation scopes `, +which have access to their enclosing class scopes. +This means that the following will fail:: class A: a = 42 b = list(a + i for i in range(10)) +However, the following will succeed:: + + class A: + type Alias = Nested + class Nested: pass + + print(A.Alias.__value__) # + +.. _annotation-scopes: + +Annotation scopes +----------------- + +:ref:`Type parameter lists ` and :keyword:`type` statements +introduce *annotation scopes*, which behave mostly like function scopes, +but with some exceptions discussed below. :term:`Annotations ` +currently do not use annotation scopes, but they are expected to use +annotation scopes in Python 3.13 when :pep:`649` is implemented. + +Annotation scopes are used in the following contexts: + +* Type parameter lists for :ref:`generic type aliases `. +* Type parameter lists for :ref:`generic functions `. + A generic function's annotations are + executed within the annotation scope, but its defaults and decorators are not. +* Type parameter lists for :ref:`generic classes `. + A generic class's base classes and + keyword arguments are executed within the annotation scope, but its decorators are not. +* The bounds and constraints for type variables + (:ref:`lazily evaluated `). +* The value of type aliases (:ref:`lazily evaluated `). + +Annotation scopes differ from function scopes in the following ways: + +* Annotation scopes have access to their enclosing class namespace. + If an annotation scope is immediately within a class scope, or within another + annotation scope that is immediately within a class scope, the code in the + annotation scope can use names defined in the class scope as if it were + executed directly within the class body. This contrasts with regular + functions defined within classes, which cannot access names defined in the class scope. +* Expressions in annotation scopes cannot contain :keyword:`yield`, ``yield from``, + :keyword:`await`, or :token:`:= ` + expressions. (These expressions are allowed in other scopes contained within the + annotation scope.) +* Names defined in annotation scopes cannot be rebound with :keyword:`nonlocal` + statements in inner scopes. This includes only type parameters, as no other + syntactic elements that can appear within annotation scopes can introduce new names. +* While annotation scopes have an internal name, that name is not reflected in the + :term:`__qualname__ ` of objects defined within the scope. + Instead, the :attr:`!__qualname__` + of such objects is as if the object were defined in the enclosing scope. + +.. versionadded:: 3.12 + Annotation scopes were introduced in Python 3.12 as part of :pep:`695`. + +.. _lazy-evaluation: + +Lazy evaluation +--------------- + +The values of type aliases created through the :keyword:`type` statement are +*lazily evaluated*. The same applies to the bounds and constraints of type +variables created through the :ref:`type parameter syntax `. +This means that they are not evaluated when the type alias or type variable is +created. Instead, they are only evaluated when doing so is necessary to resolve +an attribute access. + +Example: + +.. doctest:: + + >>> type Alias = 1/0 + >>> Alias.__value__ + Traceback (most recent call last): + ... + ZeroDivisionError: division by zero + >>> def func[T: 1/0](): pass + >>> T = func.__type_params__[0] + >>> T.__bound__ + Traceback (most recent call last): + ... + ZeroDivisionError: division by zero + +Here the exception is raised only when the ``__value__`` attribute +of the type alias or the ``__bound__`` attribute of the type variable +is accessed. + +This behavior is primarily useful for references to types that have not +yet been defined when the type alias or type variable is created. For example, +lazy evaluation enables creation of mutually recursive type aliases:: + + from typing import Literal + + type SimpleExpr = int | Parenthesized + type Parenthesized = tuple[Literal["("], Expr, Literal[")"]] + type Expr = SimpleExpr | tuple[SimpleExpr, Literal["+", "-"], Expr] + +Lazily evaluated values are evaluated in :ref:`annotation scope `, +which means that names that appear inside the lazily evaluated value are looked up +as if they were used in the immediately enclosing scope. + +.. versionadded:: 3.12 + .. _restrict_exec: Builtins and restricted execution diff --git a/Doc/reference/lexical_analysis.rst b/Doc/reference/lexical_analysis.rst index 757f887caa4029..47062f86810e91 100644 --- a/Doc/reference/lexical_analysis.rst +++ b/Doc/reference/lexical_analysis.rst @@ -361,15 +361,19 @@ Soft Keywords .. versionadded:: 3.10 Some identifiers are only reserved under specific contexts. These are known as -*soft keywords*. The identifiers ``match``, ``case`` and ``_`` can -syntactically act as keywords in contexts related to the pattern matching -statement, but this distinction is done at the parser level, not when -tokenizing. +*soft keywords*. The identifiers ``match``, ``case``, ``type`` and ``_`` can +syntactically act as keywords in certain contexts, +but this distinction is done at the parser level, not when tokenizing. -As soft keywords, their use with pattern matching is possible while still -preserving compatibility with existing code that uses ``match``, ``case`` and ``_`` as +As soft keywords, their use in the grammar is possible while still +preserving compatibility with existing code that uses these names as identifier names. +``match``, ``case``, and ``_`` are used in the :keyword:`match` statement. +``type`` is used in the :keyword:`type` statement. + +.. versionchanged:: 3.12 + ``type`` is now a soft keyword. .. index:: single: _, identifiers diff --git a/Doc/reference/simple_stmts.rst b/Doc/reference/simple_stmts.rst index f7a8b44d195417..662a4b643c4378 100644 --- a/Doc/reference/simple_stmts.rst +++ b/Doc/reference/simple_stmts.rst @@ -28,6 +28,7 @@ simple statements is: : | `future_stmt` : | `global_stmt` : | `nonlocal_stmt` + : | `type_stmt` .. _exprstmts: @@ -1012,3 +1013,48 @@ pre-existing bindings in the local scope. :pep:`3104` - Access to Names in Outer Scopes The specification for the :keyword:`nonlocal` statement. + +.. _type: + +The :keyword:`!type` statement +============================== + +.. index:: pair: statement; type + +.. productionlist:: python-grammar + type_stmt: 'type' `identifier` [`type_params`] "=" `expression` + +The :keyword:`!type` statement declares a type alias, which is an instance +of :class:`typing.TypeAliasType`. + +For example, the following statement creates a type alias:: + + type Point = tuple[float, float] + +This code is roughly equivalent to:: + + annotation-def VALUE_OF_Point(): + return tuple[float, float] + Point = typing.TypeAliasType("Point", VALUE_OF_Point()) + +``annotation-def`` indicates an :ref:`annotation scope `, which behaves +mostly like a function, but with several small differences. + +The value of the +type alias is evaluated in the annotation scope. It is not evaluated when the +type alias is created, but only when the value is accessed through the type alias's +:attr:`!__value__` attribute (see :ref:`lazy-evaluation`). +This allows the type alias to refer to names that are not yet defined. + +Type aliases may be made generic by adding a :ref:`type parameter list ` +after the name. See :ref:`generic-type-aliases` for more. + +:keyword:`!type` is a :ref:`soft keyword `. + +.. versionadded:: 3.12 + +.. seealso:: + + :pep:`695` - Type Parameter Syntax + Introduced the :keyword:`!type` statement and syntax for + generic classes and functions. diff --git a/Doc/requirements.txt b/Doc/requirements.txt index 9cbd15c2209dc6..d3fa6ce6dabc60 100644 --- a/Doc/requirements.txt +++ b/Doc/requirements.txt @@ -6,7 +6,8 @@ # Sphinx version is pinned so that new versions that introduce new warnings # won't suddenly cause build failures. Updating the version is fine as long # as no warnings are raised by doing so. -sphinx==4.5.0 +# PR #104777: Sphinx 6.2 no longer uses imghdr, removed in Python 3.13. +sphinx==6.2.0 blurb diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index fb2969028f084f..2b1fc2bdce4647 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -215,7 +215,6 @@ Doc/library/threading.rst Doc/library/time.rst Doc/library/tkinter.rst Doc/library/tkinter.scrolledtext.rst -Doc/library/tkinter.tix.rst Doc/library/tkinter.ttk.rst Doc/library/traceback.rst Doc/library/tty.rst @@ -229,7 +228,6 @@ Doc/library/urllib.request.rst Doc/library/uuid.rst Doc/library/wave.rst Doc/library/weakref.rst -Doc/library/webbrowser.rst Doc/library/winreg.rst Doc/library/winsound.rst Doc/library/wsgiref.rst diff --git a/Doc/tutorial/floatingpoint.rst b/Doc/tutorial/floatingpoint.rst index 306b1eba3c45b8..b88055a41fd1ff 100644 --- a/Doc/tutorial/floatingpoint.rst +++ b/Doc/tutorial/floatingpoint.rst @@ -148,7 +148,7 @@ Binary floating-point arithmetic holds many surprises like this. The problem with "0.1" is explained in precise detail below, in the "Representation Error" section. See `Examples of Floating Point Problems `_ for -a pleasant summary of how binary floating point works and the kinds of +a pleasant summary of how binary floating-point works and the kinds of problems commonly encountered in practice. Also see `The Perils of Floating Point `_ for a more complete account of other common surprises. @@ -174,7 +174,7 @@ Another form of exact arithmetic is supported by the :mod:`fractions` module which implements arithmetic based on rational numbers (so the numbers like 1/3 can be represented exactly). -If you are a heavy user of floating point operations you should take a look +If you are a heavy user of floating-point operations you should take a look at the NumPy package and many other packages for mathematical and statistical operations supplied by the SciPy project. See . @@ -268,12 +268,14 @@ decimal fractions cannot be represented exactly as binary (base 2) fractions. This is the chief reason why Python (or Perl, C, C++, Java, Fortran, and many others) often won't display the exact decimal number you expect. -Why is that? 1/10 is not exactly representable as a binary fraction. Almost all -machines today (November 2000) use IEEE-754 floating point arithmetic, and -almost all platforms map Python floats to IEEE-754 "double precision". 754 -doubles contain 53 bits of precision, so on input the computer strives to -convert 0.1 to the closest fraction it can of the form *J*/2**\ *N* where *J* is -an integer containing exactly 53 bits. Rewriting :: +Why is that? 1/10 is not exactly representable as a binary fraction. Since at +least 2000, almost all machines use IEEE 754 binary floating-point arithmetic, +and almost all platforms map Python floats to IEEE 754 binary64 "double +precision" values. IEEE 754 binary64 values contain 53 bits of precision, so +on input the computer strives to convert 0.1 to the closest fraction it can of +the form *J*/2**\ *N* where *J* is an integer containing exactly 53 bits. +Rewriting +:: 1 / 10 ~= J / (2**N) @@ -308,7 +310,8 @@ by rounding up: >>> q+1 7205759403792794 -Therefore the best possible approximation to 1/10 in 754 double precision is:: +Therefore the best possible approximation to 1/10 in IEEE 754 double precision +is:: 7205759403792794 / 2 ** 56 @@ -321,7 +324,7 @@ if we had not rounded up, the quotient would have been a little bit smaller than 1/10. But in no case can it be *exactly* 1/10! So the computer never "sees" 1/10: what it sees is the exact fraction given -above, the best 754 double approximation it can get: +above, the best IEEE 754 double approximation it can get: .. doctest:: diff --git a/Doc/whatsnew/2.3.rst b/Doc/whatsnew/2.3.rst index 3848c85145ed32..43bf3fa46a29f1 100644 --- a/Doc/whatsnew/2.3.rst +++ b/Doc/whatsnew/2.3.rst @@ -1555,7 +1555,7 @@ complete list of changes, or look through the CVS logs for all the details. # [0.36831796169281006, 0.37441694736480713, 0.35304892063140869] # [0.17574405670166016, 0.18193507194519043, 0.17565798759460449] -* The :mod:`Tix` module has received various bug fixes and updates for the +* The :mod:`!Tix` module has received various bug fixes and updates for the current version of the Tix package. * The :mod:`Tkinter` module now works with a thread-enabled version of Tcl. diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index ccdf3fc34682be..a734233cff7dc8 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -1735,7 +1735,7 @@ Modules +---------------------+---------------------+---------------------+---------------------+---------------------+ | :mod:`!audioop` | :mod:`!crypt` | :mod:`!nis` | :mod:`!sndhdr` | :mod:`!uu` | +---------------------+---------------------+---------------------+---------------------+---------------------+ - | :mod:`!cgi` | :mod:`imghdr` | :mod:`!nntplib` | :mod:`!spwd` | :mod:`!xdrlib` | + | :mod:`!cgi` | :mod:`!imghdr` | :mod:`!nntplib` | :mod:`!spwd` | :mod:`!xdrlib` | +---------------------+---------------------+---------------------+---------------------+---------------------+ | :mod:`!cgitb` | :mod:`!mailcap` | :mod:`!ossaudiodev` | :mod:`!sunau` | | +---------------------+---------------------+---------------------+---------------------+---------------------+ @@ -1817,7 +1817,7 @@ Standard Library They will be removed in Python 3.13. (Contributed by Serhiy Storchaka and Miro Hrončok in :gh:`92728`.) -* :func:`turtle.settiltangle` has been deprecated since Python 3.1; +* :func:`!turtle.settiltangle` has been deprecated since Python 3.1; it now emits a deprecation warning and will be removed in Python 3.13. Use :func:`turtle.tiltangle` instead (it was earlier incorrectly marked as deprecated, and its docstring is now corrected). @@ -1856,6 +1856,10 @@ Standard Library (Contributed by Erlend E. Aasland in :issue:`5846`.) +* :meth:`~!unittest.TestProgram.usageExit` is marked deprecated, to be removed + in 3.13. + (Contributed by Carlos Damázio in :gh:`67048`.) + .. _whatsnew311-pending-removal: .. _whatsnew311-python-api-pending-removal: diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index 5266f5ffb93737..f23a733e0c611c 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -74,6 +74,8 @@ New typing features: * :pep:`688`: Making the buffer protocol accessible in Python +* :ref:`whatsnew312-pep695` + * :ref:`whatsnew312-pep692` * :pep:`698`: Override Decorator for Static Typing @@ -270,7 +272,100 @@ typed dictionaries:: See :pep:`692` for more details. -(PEP written by Franek Magiera) +(Contributed by Franek Magiera in :gh:`103629`.) + +PEP 698: Override Decorator for Static Typing +--------------------------------------------- + +A new decorator :func:`typing.override` has been added to the :mod:`typing` +module. It indicates to type checkers that the method is intended to override +a method in a superclass. This allows type checkers to catch mistakes where +a method that is intended to override something in a base class +does not in fact do so. + +Example:: + + from typing import override + + class Base: + def get_color(self) -> str: + return "blue" + + class GoodChild(Base): + @override # ok: overrides Base.get_color + def get_color(self) -> str: + return "yellow" + + class BadChild(Base): + @override # type checker error: does not override Base.get_color + def get_colour(self) -> str: + return "red" + +(Contributed by Steven Troxler in :gh:`101561`.) + +.. _whatsnew312-pep695: + +PEP 695: Type Parameter Syntax +------------------------------ + +Generic classes and functions under :pep:`484` were declared using a verbose syntax +that left the scope of type parameters unclear and required explicit declarations of +variance. + +:pep:`695` introduces a new, more compact and explicit way to create +:ref:`generic classes ` and :ref:`functions `:: + + def max[T](args: Iterable[T]) -> T: + ... + + class list[T]: + def __getitem__(self, index: int, /) -> T: + ... + + def append(self, element: T) -> None: + ... + +In addition, the PEP introduces a new way to declare :ref:`type aliases ` +using the :keyword:`type` statement, which creates an instance of +:class:`~typing.TypeAliasType`:: + + type Point = tuple[float, float] + +Type aliases can also be :ref:`generic `:: + + type Point[T] = tuple[T, T] + +The new syntax allows declaring :class:`~typing.TypeVarTuple` +and :class:`~typing.ParamSpec` parameters, as well as :class:`~typing.TypeVar` +parameters with bounds or constraints:: + + type IntFunc[**P] = Callable[P, int] # ParamSpec + type LabeledTuple[*Ts] = tuple[str, *Ts] # TypeVarTuple + type HashableSequence[T: Hashable] = Sequence[T] # TypeVar with bound + type IntOrStrSequence[T: (int, str)] = Sequence[T] # TypeVar with constraints + +The value of type aliases and the bound and constraints of type variables +created through this syntax are evaluated only on demand (see +:ref:`lazy-evaluation`). This means type aliases are able to refer to other +types defined later in the file. + +Type parameters declared through a type parameter list are visible within the +scope of the declaration and any nested scopes, but not in the outer scope. For +example, they can be used in the type annotations for the methods of a generic +class or in the class body. However, they cannot be used in the module scope after +the class is defined. See :ref:`type-params` for a detailed description of the +runtime semantics of type parameters. + +In order to support these scoping semantics, a new kind of scope is introduced, +the :ref:`annotation scope `. Annotation scopes behave for the +most part like function scopes, but interact differently with enclosing class scopes. +In Python 3.13, :term:`annotations ` will also be evaluated in +annotation scopes. + +See :pep:`695` for more details. + +(PEP written by Eric Traut. Implementation by Jelle Zijlstra, Eric Traut, +and others in :gh:`103764`.) Other Language Changes ====================== @@ -706,11 +801,6 @@ tempfile typing ------ -* Add :func:`typing.override`, an override decorator telling to static type - checkers to verify that a method overrides some method or attribute of the - same name on a base class, as per :pep:`698`. (Contributed by Steven Troxler in - :gh:`101564`.) - * :func:`isinstance` checks against :func:`runtime-checkable protocols ` now use :func:`inspect.getattr_static` rather than :func:`hasattr` to lookup whether @@ -755,6 +845,13 @@ typing or more members may be slower than in Python 3.11. (Contributed by Alex Waygood in :gh:`74690` and :gh:`103193`.) +* All :data:`typing.TypedDict` and :data:`typing.NamedTuple` classes now have the + ``__orig_bases__`` attribute. (Contributed by Adrian Garcia Badaracco in + :gh:`103699`.) + +* Add ``frozen_default`` parameter to :func:`typing.dataclass_transform`. + (Contributed by Erik De Bonte in :gh:`99957`.) + sys --- @@ -806,14 +903,19 @@ Optimizations CPython bytecode changes ======================== -* Removed the :opcode:`LOAD_METHOD` instruction. It has been merged into +* Remove the :opcode:`LOAD_METHOD` instruction. It has been merged into :opcode:`LOAD_ATTR`. :opcode:`LOAD_ATTR` will now behave like the old :opcode:`LOAD_METHOD` instruction if the low bit of its oparg is set. (Contributed by Ken Jin in :gh:`93429`.) -* Removed the :opcode:`!JUMP_IF_FALSE_OR_POP` and :opcode:`!JUMP_IF_TRUE_OR_POP` +* Remove the :opcode:`!JUMP_IF_FALSE_OR_POP` and :opcode:`!JUMP_IF_TRUE_OR_POP` instructions. (Contributed by Irit Katriel in :gh:`102859`.) +* Add the :opcode:`LOAD_FROM_DICT_OR_DEREF`, :opcode:`LOAD_FROM_DICT_OR_GLOBALS`, + and :opcode:`LOAD_LOCALS` opcodes as part of the implementation of :pep:`695`. + Remove the :opcode:`!LOAD_CLASSDEREF` opcode, which can be replaced with + :opcode:`LOAD_LOCALS` plus :opcode:`LOAD_FROM_DICT_OR_DEREF`. (Contributed + by Jelle Zijlstra in :gh:`103764`.) Demos and Tools =============== @@ -924,7 +1026,7 @@ Modules (see :pep:`594`): * :mod:`!cgitb` * :mod:`!chunk` * :mod:`!crypt` -* :mod:`imghdr` +* :mod:`!imghdr` * :mod:`!mailcap` * :mod:`!msilib` * :mod:`!nis` @@ -944,8 +1046,9 @@ APIs: * :func:`locale.getdefaultlocale` (:gh:`90817`) * :meth:`!turtle.RawTurtle.settiltangle` (:gh:`50096`) * :func:`!unittest.findTestCases` (:gh:`50096`) -* :func:`!unittest.makeSuite` (:gh:`50096`) * :func:`!unittest.getTestCaseNames` (:gh:`50096`) +* :func:`!unittest.makeSuite` (:gh:`50096`) +* :meth:`!unittest.TestProgram.usageExit` (:gh:`67048`) * :class:`!webbrowser.MacOSX` (:gh:`86421`) * :class:`classmethod` descriptor chaining (:gh:`89519`) @@ -998,6 +1101,12 @@ Pending Removal in Python 3.14 functions that have been deprecated since Python 2 but only gained a proper :exc:`DeprecationWarning` in 3.12. Remove them in 3.14. +* :mod:`itertools` had undocumented, inefficient, historically buggy, + and inconsistent support for copy, deepcopy, and pickle operations. + This will be removed in 3.14 for a significant reduction in code + volume and maintenance burden. + (Contributed by Raymond Hettinger in :gh:`101588`.) + * Accessing ``co_lnotab`` was deprecated in :pep:`626` since 3.10 and was planned to be removed in 3.12 but it only got a proper :exc:`DeprecationWarning` in 3.12. diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index caf09668e824e0..8c81ac76a56b46 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -87,6 +87,12 @@ New Modules Improved Modules ================ +pathlib +------- + +* Add *follow_symlinks* keyword-only argument to :meth:`pathlib.Path.glob` and + :meth:`~pathlib.Path.rglob`. + (Contributed by Barney Gale in :gh:`77609`.) Optimizations ============= @@ -115,9 +121,24 @@ Removed are now removed. The items in those namespaces can be imported directly from :mod:`typing`. (Contributed by Sebastian Rittau in :gh:`92871`.) +* Remove the untested and undocumented :mod:`webbrowser` :class:`!MacOSX` class, + deprecated in Python 3.11. + Use the :class:`!MacOSXOSAScript` class (introduced in Python 3.2) instead. + (Contributed by Hugo van Kemenade in :gh:`104804`.) + * Remove support for using :class:`pathlib.Path` objects as context managers. This functionality was deprecated and made a no-op in Python 3.9. +* Remove the undocumented :class:`!configparser.LegacyInterpolation` class, + deprecated in the docstring since Python 3.2, + and with a deprecation warning since Python 3.11. + (Contributed by Hugo van Kemenade in :gh:`104886`.) + +* Remove the :meth:`!turtle.RawTurtle.settiltangle` method, + deprecated in docs since Python 3.1 + and with a deprecation warning since Python 3.11. + (Contributed by Hugo van Kemenade in :gh:`104876`.) + * Removed the following :mod:`unittest` functions, deprecated in Python 3.11: * :func:`!unittest.findTestCases` @@ -234,6 +255,23 @@ Removed :class:`typing.TypedDict` types, deprecated in Python 3.11. (Contributed by Tomas Roun in :gh:`104786`.) +* :pep:`594`: Remove the :mod:`!imghdr` module, deprecated in Python 3.11: + use the projects + `filetype `_, + `puremagic `_, + or `python-magic `_ instead. + (Contributed by Victor Stinner in :gh:`104773`.) + +* Remove the untested and undocumented :meth:`!unittest.TestProgram.usageExit` + method, deprecated in Python 3.11. + (Contributed by Hugo van Kemenade in :gh:`104992`.) + +* Remove the :mod:`!tkinter.tix` module, deprecated in Python 3.6. The + third-party Tix library which the module wrapped is unmaintained. + (Contributed by Zachary Ware in :gh:`75552`.) + + + Porting to Python 3.13 ====================== diff --git a/Doc/whatsnew/3.5.rst b/Doc/whatsnew/3.5.rst index cd8f6e2cd9b595..ccf71bf08e8608 100644 --- a/Doc/whatsnew/3.5.rst +++ b/Doc/whatsnew/3.5.rst @@ -1252,7 +1252,7 @@ Oberkirch in :issue:`21800`.) imghdr ------ -The :func:`~imghdr.what` function now recognizes the +The :func:`~!imghdr.what` function now recognizes the `OpenEXR `_ format (contributed by Martin Vignali and Claudiu Popa in :issue:`20295`), and the `WebP `_ format diff --git a/Doc/whatsnew/3.6.rst b/Doc/whatsnew/3.6.rst index b45dc80820c9d3..7d293c634f237b 100644 --- a/Doc/whatsnew/3.6.rst +++ b/Doc/whatsnew/3.6.rst @@ -2049,7 +2049,7 @@ of OpenSSL. Other features are deprecated in favor of a different API. tkinter ~~~~~~~ -The :mod:`tkinter.tix` module is now deprecated. :mod:`tkinter` users +The :mod:`!tkinter.tix` module is now deprecated. :mod:`tkinter` users should use :mod:`tkinter.ttk` instead. .. _whatsnew36-venv: diff --git a/Grammar/python.gram b/Grammar/python.gram index e6a983429e39d8..9131835f7421bc 100644 --- a/Grammar/python.gram +++ b/Grammar/python.gram @@ -254,10 +254,10 @@ class_def[stmt_ty]: class_def_raw[stmt_ty]: | invalid_class_def_raw | 'class' a=NAME t=[type_params] b=['(' z=[arguments] ')' { z }] ':' c=block { - _PyAST_ClassDef(a->v.Name.id, t, + _PyAST_ClassDef(a->v.Name.id, (b) ? ((expr_ty) b)->v.Call.args : NULL, (b) ? ((expr_ty) b)->v.Call.keywords : NULL, - c, NULL, EXTRA) } + c, NULL, t, EXTRA) } # Function definitions # -------------------- @@ -269,17 +269,17 @@ function_def[stmt_ty]: function_def_raw[stmt_ty]: | invalid_def_raw | 'def' n=NAME t=[type_params] &&'(' params=[params] ')' a=['->' z=expression { z }] &&':' tc=[func_type_comment] b=block { - _PyAST_FunctionDef(n->v.Name.id, t, + _PyAST_FunctionDef(n->v.Name.id, (params) ? params : CHECK(arguments_ty, _PyPegen_empty_arguments(p)), - b, NULL, a, NEW_TYPE_COMMENT(p, tc), EXTRA) } + b, NULL, a, NEW_TYPE_COMMENT(p, tc), t, EXTRA) } | ASYNC 'def' n=NAME t=[type_params] &&'(' params=[params] ')' a=['->' z=expression { z }] &&':' tc=[func_type_comment] b=block { CHECK_VERSION( stmt_ty, 5, "Async functions are", - _PyAST_AsyncFunctionDef(n->v.Name.id, t, + _PyAST_AsyncFunctionDef(n->v.Name.id, (params) ? params : CHECK(arguments_ty, _PyPegen_empty_arguments(p)), - b, NULL, a, NEW_TYPE_COMMENT(p, tc), EXTRA) + b, NULL, a, NEW_TYPE_COMMENT(p, tc), t, EXTRA) ) } # Function parameters diff --git a/Include/internal/pycore_ast.h b/Include/internal/pycore_ast.h index 06a40239a2473a..b568902bb1e381 100644 --- a/Include/internal/pycore_ast.h +++ b/Include/internal/pycore_ast.h @@ -198,31 +198,31 @@ struct _stmt { union { struct { identifier name; - asdl_type_param_seq *type_params; arguments_ty args; asdl_stmt_seq *body; asdl_expr_seq *decorator_list; expr_ty returns; string type_comment; + asdl_type_param_seq *type_params; } FunctionDef; struct { identifier name; - asdl_type_param_seq *type_params; arguments_ty args; asdl_stmt_seq *body; asdl_expr_seq *decorator_list; expr_ty returns; string type_comment; + asdl_type_param_seq *type_params; } AsyncFunctionDef; struct { identifier name; - asdl_type_param_seq *type_params; asdl_expr_seq *bases; asdl_keyword_seq *keywords; asdl_stmt_seq *body; asdl_expr_seq *decorator_list; + asdl_type_param_seq *type_params; } ClassDef; struct { @@ -682,22 +682,22 @@ mod_ty _PyAST_Interactive(asdl_stmt_seq * body, PyArena *arena); mod_ty _PyAST_Expression(expr_ty body, PyArena *arena); mod_ty _PyAST_FunctionType(asdl_expr_seq * argtypes, expr_ty returns, PyArena *arena); -stmt_ty _PyAST_FunctionDef(identifier name, asdl_type_param_seq * type_params, - arguments_ty args, asdl_stmt_seq * body, - asdl_expr_seq * decorator_list, expr_ty returns, - string type_comment, int lineno, int col_offset, int +stmt_ty _PyAST_FunctionDef(identifier name, arguments_ty args, asdl_stmt_seq * + body, asdl_expr_seq * decorator_list, expr_ty + returns, string type_comment, asdl_type_param_seq * + type_params, int lineno, int col_offset, int end_lineno, int end_col_offset, PyArena *arena); -stmt_ty _PyAST_AsyncFunctionDef(identifier name, asdl_type_param_seq * - type_params, arguments_ty args, asdl_stmt_seq * - body, asdl_expr_seq * decorator_list, expr_ty - returns, string type_comment, int lineno, int - col_offset, int end_lineno, int end_col_offset, - PyArena *arena); -stmt_ty _PyAST_ClassDef(identifier name, asdl_type_param_seq * type_params, - asdl_expr_seq * bases, asdl_keyword_seq * keywords, - asdl_stmt_seq * body, asdl_expr_seq * decorator_list, - int lineno, int col_offset, int end_lineno, int - end_col_offset, PyArena *arena); +stmt_ty _PyAST_AsyncFunctionDef(identifier name, arguments_ty args, + asdl_stmt_seq * body, asdl_expr_seq * + decorator_list, expr_ty returns, string + type_comment, asdl_type_param_seq * + type_params, int lineno, int col_offset, int + end_lineno, int end_col_offset, PyArena *arena); +stmt_ty _PyAST_ClassDef(identifier name, asdl_expr_seq * bases, + asdl_keyword_seq * keywords, asdl_stmt_seq * body, + asdl_expr_seq * decorator_list, asdl_type_param_seq * + type_params, int lineno, int col_offset, int + end_lineno, int end_col_offset, PyArena *arena); stmt_ty _PyAST_Return(expr_ty value, int lineno, int col_offset, int end_lineno, int end_col_offset, PyArena *arena); stmt_ty _PyAST_Delete(asdl_expr_seq * targets, int lineno, int col_offset, int diff --git a/Include/internal/pycore_global_objects_fini_generated.h b/Include/internal/pycore_global_objects_fini_generated.h index 5a1993eac23a8a..a83f8fc49fc5ef 100644 --- a/Include/internal/pycore_global_objects_fini_generated.h +++ b/Include/internal/pycore_global_objects_fini_generated.h @@ -781,6 +781,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(a)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(abs_tol)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(access)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(aclose)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(add)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(add_done_callback)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(after_in_child)); @@ -794,7 +795,9 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(arguments)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(argv)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(as_integer_ratio)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(asend)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(ast)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(athrow)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(attribute)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(authorizer_callback)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(autocommit)); diff --git a/Include/internal/pycore_global_strings.h b/Include/internal/pycore_global_strings.h index 61967877ab4ac8..dd6a62f53a9989 100644 --- a/Include/internal/pycore_global_strings.h +++ b/Include/internal/pycore_global_strings.h @@ -269,6 +269,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(a) STRUCT_FOR_ID(abs_tol) STRUCT_FOR_ID(access) + STRUCT_FOR_ID(aclose) STRUCT_FOR_ID(add) STRUCT_FOR_ID(add_done_callback) STRUCT_FOR_ID(after_in_child) @@ -282,7 +283,9 @@ struct _Py_global_strings { STRUCT_FOR_ID(arguments) STRUCT_FOR_ID(argv) STRUCT_FOR_ID(as_integer_ratio) + STRUCT_FOR_ID(asend) STRUCT_FOR_ID(ast) + STRUCT_FOR_ID(athrow) STRUCT_FOR_ID(attribute) STRUCT_FOR_ID(authorizer_callback) STRUCT_FOR_ID(autocommit) diff --git a/Include/internal/pycore_runtime_init_generated.h b/Include/internal/pycore_runtime_init_generated.h index 59ec49af358f2e..d689f717eaf94f 100644 --- a/Include/internal/pycore_runtime_init_generated.h +++ b/Include/internal/pycore_runtime_init_generated.h @@ -775,6 +775,7 @@ extern "C" { INIT_ID(a), \ INIT_ID(abs_tol), \ INIT_ID(access), \ + INIT_ID(aclose), \ INIT_ID(add), \ INIT_ID(add_done_callback), \ INIT_ID(after_in_child), \ @@ -788,7 +789,9 @@ extern "C" { INIT_ID(arguments), \ INIT_ID(argv), \ INIT_ID(as_integer_ratio), \ + INIT_ID(asend), \ INIT_ID(ast), \ + INIT_ID(athrow), \ INIT_ID(attribute), \ INIT_ID(authorizer_callback), \ INIT_ID(autocommit), \ diff --git a/Include/internal/pycore_unicodeobject_generated.h b/Include/internal/pycore_unicodeobject_generated.h index 8f8a067e4c1808..db6a157ee7afbf 100644 --- a/Include/internal/pycore_unicodeobject_generated.h +++ b/Include/internal/pycore_unicodeobject_generated.h @@ -648,6 +648,9 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { string = &_Py_ID(access); assert(_PyUnicode_CheckConsistency(string, 1)); _PyUnicode_InternInPlace(interp, &string); + string = &_Py_ID(aclose); + assert(_PyUnicode_CheckConsistency(string, 1)); + _PyUnicode_InternInPlace(interp, &string); string = &_Py_ID(add); assert(_PyUnicode_CheckConsistency(string, 1)); _PyUnicode_InternInPlace(interp, &string); @@ -687,9 +690,15 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { string = &_Py_ID(as_integer_ratio); assert(_PyUnicode_CheckConsistency(string, 1)); _PyUnicode_InternInPlace(interp, &string); + string = &_Py_ID(asend); + assert(_PyUnicode_CheckConsistency(string, 1)); + _PyUnicode_InternInPlace(interp, &string); string = &_Py_ID(ast); assert(_PyUnicode_CheckConsistency(string, 1)); _PyUnicode_InternInPlace(interp, &string); + string = &_Py_ID(athrow); + assert(_PyUnicode_CheckConsistency(string, 1)); + _PyUnicode_InternInPlace(interp, &string); string = &_Py_ID(attribute); assert(_PyUnicode_CheckConsistency(string, 1)); _PyUnicode_InternInPlace(interp, &string); diff --git a/Include/internal/pycore_warnings.h b/Include/internal/pycore_warnings.h index efb4f1cd7eac80..452d6b96ce4f1c 100644 --- a/Include/internal/pycore_warnings.h +++ b/Include/internal/pycore_warnings.h @@ -22,6 +22,7 @@ extern int _PyWarnings_InitState(PyInterpreterState *interp); PyAPI_FUNC(PyObject*) _PyWarnings_Init(void); extern void _PyErr_WarnUnawaitedCoroutine(PyObject *coro); +extern void _PyErr_WarnUnawaitedAgenMethod(PyAsyncGenObject *agen, PyObject *method); #ifdef __cplusplus } diff --git a/Lib/_compat_pickle.py b/Lib/_compat_pickle.py index 65a94b6b1bdfd5..439f8c02f4b586 100644 --- a/Lib/_compat_pickle.py +++ b/Lib/_compat_pickle.py @@ -22,7 +22,6 @@ 'tkMessageBox': 'tkinter.messagebox', 'ScrolledText': 'tkinter.scrolledtext', 'Tkconstants': 'tkinter.constants', - 'Tix': 'tkinter.tix', 'ttk': 'tkinter.ttk', 'Tkinter': 'tkinter', 'markupbase': '_markupbase', diff --git a/Lib/concurrent/futures/thread.py b/Lib/concurrent/futures/thread.py index 51c942f51abd37..3b3a36a5093336 100644 --- a/Lib/concurrent/futures/thread.py +++ b/Lib/concurrent/futures/thread.py @@ -43,7 +43,7 @@ def _python_exit(): after_in_parent=_global_shutdown_lock.release) -class _WorkItem(object): +class _WorkItem: def __init__(self, future, fn, args, kwargs): self.future = future self.fn = fn @@ -78,17 +78,20 @@ def _worker(executor_reference, work_queue, initializer, initargs): return try: while True: - work_item = work_queue.get(block=True) - if work_item is not None: - work_item.run() - # Delete references to object. See issue16284 - del work_item - - # attempt to increment idle count + try: + work_item = work_queue.get_nowait() + except queue.Empty: + # attempt to increment idle count if queue is empty executor = executor_reference() if executor is not None: executor._idle_semaphore.release() del executor + work_item = work_queue.get(block=True) + + if work_item is not None: + work_item.run() + # Delete references to object. See GH-60488 + del work_item continue executor = executor_reference() diff --git a/Lib/configparser.py b/Lib/configparser.py index dee5a0db7e7ddc..9640f71adc7718 100644 --- a/Lib/configparser.py +++ b/Lib/configparser.py @@ -155,7 +155,7 @@ "ParsingError", "MissingSectionHeaderError", "ConfigParser", "RawConfigParser", "Interpolation", "BasicInterpolation", "ExtendedInterpolation", - "LegacyInterpolation", "SectionProxy", "ConverterMapping", + "SectionProxy", "ConverterMapping", "DEFAULTSECT", "MAX_INTERPOLATION_DEPTH") _default_dict = dict @@ -491,53 +491,6 @@ def _interpolate_some(self, parser, option, accum, rest, section, map, "found: %r" % (rest,)) -class LegacyInterpolation(Interpolation): - """Deprecated interpolation used in old versions of ConfigParser. - Use BasicInterpolation or ExtendedInterpolation instead.""" - - _KEYCRE = re.compile(r"%\(([^)]*)\)s|.") - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - warnings.warn( - "LegacyInterpolation has been deprecated since Python 3.2 " - "and will be removed from the configparser module in Python 3.13. " - "Use BasicInterpolation or ExtendedInterpolation instead.", - DeprecationWarning, stacklevel=2 - ) - - def before_get(self, parser, section, option, value, vars): - rawval = value - depth = MAX_INTERPOLATION_DEPTH - while depth: # Loop through this until it's done - depth -= 1 - if value and "%(" in value: - replace = functools.partial(self._interpolation_replace, - parser=parser) - value = self._KEYCRE.sub(replace, value) - try: - value = value % vars - except KeyError as e: - raise InterpolationMissingOptionError( - option, section, rawval, e.args[0]) from None - else: - break - if value and "%(" in value: - raise InterpolationDepthError(option, section, rawval) - return value - - def before_set(self, parser, section, option, value): - return value - - @staticmethod - def _interpolation_replace(match, parser): - s = match.group(1) - if s is None: - return match.group() - else: - return "%%(%s)s" % parser.optionxform(s) - - class RawConfigParser(MutableMapping): """ConfigParser that does not do interpolation.""" diff --git a/Lib/http/client.py b/Lib/http/client.py index 59a9fd72b4722f..3d98e4eb54bb45 100644 --- a/Lib/http/client.py +++ b/Lib/http/client.py @@ -1024,7 +1024,7 @@ def send(self, data): print("send:", repr(data)) if hasattr(data, "read") : if self.debuglevel > 0: - print("sendIng a read()able") + print("sending a readable") encode = self._is_textIO(data) if encode and self.debuglevel > 0: print("encoding file using iso-8859-1") @@ -1054,7 +1054,7 @@ def _output(self, s): def _read_readable(self, readable): if self.debuglevel > 0: - print("sendIng a read()able") + print("reading a readable") encode = self._is_textIO(readable) if encode and self.debuglevel > 0: print("encoding file using iso-8859-1") diff --git a/Lib/idlelib/idle_test/test_editor.py b/Lib/idlelib/idle_test/test_editor.py index ba59c40dc6dde5..9296a6d235fbbe 100644 --- a/Lib/idlelib/idle_test/test_editor.py +++ b/Lib/idlelib/idle_test/test_editor.py @@ -201,8 +201,8 @@ def test_searcher(self): test_info = (# text, (block, indent)) ("", (None, None)), ("[1,", (None, None)), # TokenError - ("if 1:\n", ('if 1:', None)), - ("if 1:\n 2\n 3\n", ('if 1:', ' 2')), + ("if 1:\n", ('if 1:\n', None)), + ("if 1:\n 2\n 3\n", ('if 1:\n', ' 2\n')), ) for code, expected_pair in test_info: with self.subTest(code=code): diff --git a/Lib/imghdr.py b/Lib/imghdr.py deleted file mode 100644 index 33868883470764..00000000000000 --- a/Lib/imghdr.py +++ /dev/null @@ -1,180 +0,0 @@ -"""Recognize image file formats based on their first few bytes.""" - -from os import PathLike -import warnings - -__all__ = ["what"] - - -warnings._deprecated(__name__, remove=(3, 13)) - - -#-------------------------# -# Recognize image headers # -#-------------------------# - -def what(file, h=None): - """Return the type of image contained in a file or byte stream.""" - f = None - try: - if h is None: - if isinstance(file, (str, PathLike)): - f = open(file, 'rb') - h = f.read(32) - else: - location = file.tell() - h = file.read(32) - file.seek(location) - for tf in tests: - res = tf(h, f) - if res: - return res - finally: - if f: f.close() - return None - - -#---------------------------------# -# Subroutines per image file type # -#---------------------------------# - -tests = [] - -def test_jpeg(h, f): - """Test for JPEG data with JFIF or Exif markers; and raw JPEG.""" - if h[6:10] in (b'JFIF', b'Exif'): - return 'jpeg' - elif h[:4] == b'\xff\xd8\xff\xdb': - return 'jpeg' - -tests.append(test_jpeg) - -def test_png(h, f): - """Verify if the image is a PNG.""" - if h.startswith(b'\211PNG\r\n\032\n'): - return 'png' - -tests.append(test_png) - -def test_gif(h, f): - """Verify if the image is a GIF ('87 or '89 variants).""" - if h[:6] in (b'GIF87a', b'GIF89a'): - return 'gif' - -tests.append(test_gif) - -def test_tiff(h, f): - """Verify if the image is a TIFF (can be in Motorola or Intel byte order).""" - if h[:2] in (b'MM', b'II'): - return 'tiff' - -tests.append(test_tiff) - -def test_rgb(h, f): - """test for the SGI image library.""" - if h.startswith(b'\001\332'): - return 'rgb' - -tests.append(test_rgb) - -def test_pbm(h, f): - """Verify if the image is a PBM (portable bitmap).""" - if len(h) >= 3 and \ - h[0] == ord(b'P') and h[1] in b'14' and h[2] in b' \t\n\r': - return 'pbm' - -tests.append(test_pbm) - -def test_pgm(h, f): - """Verify if the image is a PGM (portable graymap).""" - if len(h) >= 3 and \ - h[0] == ord(b'P') and h[1] in b'25' and h[2] in b' \t\n\r': - return 'pgm' - -tests.append(test_pgm) - -def test_ppm(h, f): - """Verify if the image is a PPM (portable pixmap).""" - if len(h) >= 3 and \ - h[0] == ord(b'P') and h[1] in b'36' and h[2] in b' \t\n\r': - return 'ppm' - -tests.append(test_ppm) - -def test_rast(h, f): - """test for the Sun raster file.""" - if h.startswith(b'\x59\xA6\x6A\x95'): - return 'rast' - -tests.append(test_rast) - -def test_xbm(h, f): - """Verify if the image is a X bitmap (X10 or X11).""" - if h.startswith(b'#define '): - return 'xbm' - -tests.append(test_xbm) - -def test_bmp(h, f): - """Verify if the image is a BMP file.""" - if h.startswith(b'BM'): - return 'bmp' - -tests.append(test_bmp) - -def test_webp(h, f): - """Verify if the image is a WebP.""" - if h.startswith(b'RIFF') and h[8:12] == b'WEBP': - return 'webp' - -tests.append(test_webp) - -def test_exr(h, f): - """verify is the image ia a OpenEXR fileOpenEXR.""" - if h.startswith(b'\x76\x2f\x31\x01'): - return 'exr' - -tests.append(test_exr) - -#--------------------# -# Small test program # -#--------------------# - -def test(): - import sys - recursive = 0 - if sys.argv[1:] and sys.argv[1] == '-r': - del sys.argv[1:2] - recursive = 1 - try: - if sys.argv[1:]: - testall(sys.argv[1:], recursive, 1) - else: - testall(['.'], recursive, 1) - except KeyboardInterrupt: - sys.stderr.write('\n[Interrupted]\n') - sys.exit(1) - -def testall(list, recursive, toplevel): - import sys - import os - for filename in list: - if os.path.isdir(filename): - print(filename + '/:', end=' ') - if recursive or toplevel: - print('recursing down:') - import glob - names = glob.glob(os.path.join(glob.escape(filename), '*')) - testall(names, recursive, 0) - else: - print('*** directory (use -r) ***') - else: - print(filename + ':', end=' ') - sys.stdout.flush() - try: - print(what(filename)) - except OSError: - print('*** not found ***') - -if __name__ == '__main__': - test() diff --git a/Lib/inspect.py b/Lib/inspect.py index 7709a95003efbd..55530fc780b35c 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -1242,6 +1242,14 @@ def getblock(lines): blockfinder.tokeneater(*_token) except (EndOfBlock, IndentationError): pass + except SyntaxError as e: + if "unmatched" not in e.msg: + raise e from None + _, *_token_info = _token + try: + blockfinder.tokeneater(tokenize.NEWLINE, *_token_info) + except (EndOfBlock, IndentationError): + pass return lines[:blockfinder.last] def getsourcelines(object): diff --git a/Lib/ntpath.py b/Lib/ntpath.py index 0f3674fe11eecd..dadcdc0c495da1 100644 --- a/Lib/ntpath.py +++ b/Lib/ntpath.py @@ -867,3 +867,19 @@ def commonpath(paths): except ImportError: # Use genericpath.* as imported above pass + + +try: + from nt import _path_isdevdrive +except ImportError: + def isdevdrive(path): + """Determines whether the specified path is on a Windows Dev Drive.""" + # Never a Dev Drive + return False +else: + def isdevdrive(path): + """Determines whether the specified path is on a Windows Dev Drive.""" + try: + return _path_isdevdrive(abspath(path)) + except OSError: + return False diff --git a/Lib/pathlib.py b/Lib/pathlib.py index c530f237fee050..229588fdf5b412 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -105,19 +105,19 @@ def __init__(self, child_parts, flavour, case_sensitive): self.successor = _TerminatingSelector() self.dironly = False - def select_from(self, parent_path): + def select_from(self, parent_path, follow_symlinks): """Iterate over all child paths of `parent_path` matched by this selector. This can contain parent_path itself.""" path_cls = type(parent_path) scandir = path_cls._scandir if not parent_path.is_dir(): return iter([]) - return self._select_from(parent_path, scandir) + return self._select_from(parent_path, scandir, follow_symlinks) class _TerminatingSelector: - def _select_from(self, parent_path, scandir): + def _select_from(self, parent_path, scandir, follow_symlinks): yield parent_path @@ -126,9 +126,9 @@ class _ParentSelector(_Selector): def __init__(self, name, child_parts, flavour, case_sensitive): _Selector.__init__(self, child_parts, flavour, case_sensitive) - def _select_from(self, parent_path, scandir): + def _select_from(self, parent_path, scandir, follow_symlinks): path = parent_path._make_child_relpath('..') - for p in self.successor._select_from(path, scandir): + for p in self.successor._select_from(path, scandir, follow_symlinks): yield p @@ -141,7 +141,8 @@ def __init__(self, pat, child_parts, flavour, case_sensitive): case_sensitive = _is_case_sensitive(flavour) self.match = _compile_pattern(pat, case_sensitive) - def _select_from(self, parent_path, scandir): + def _select_from(self, parent_path, scandir, follow_symlinks): + follow_dirlinks = True if follow_symlinks is None else follow_symlinks try: # We must close the scandir() object before proceeding to # avoid exhausting file descriptors when globbing deep trees. @@ -153,14 +154,14 @@ def _select_from(self, parent_path, scandir): for entry in entries: if self.dironly: try: - if not entry.is_dir(): + if not entry.is_dir(follow_symlinks=follow_dirlinks): continue except OSError: continue name = entry.name if self.match(name): path = parent_path._make_child_relpath(name) - for p in self.successor._select_from(path, scandir): + for p in self.successor._select_from(path, scandir, follow_symlinks): yield p @@ -169,16 +170,17 @@ class _RecursiveWildcardSelector(_Selector): def __init__(self, pat, child_parts, flavour, case_sensitive): _Selector.__init__(self, child_parts, flavour, case_sensitive) - def _iterate_directories(self, parent_path): + def _iterate_directories(self, parent_path, follow_symlinks): yield parent_path - for dirpath, dirnames, _ in parent_path.walk(): + for dirpath, dirnames, _ in parent_path.walk(follow_symlinks=follow_symlinks): for dirname in dirnames: yield dirpath._make_child_relpath(dirname) - def _select_from(self, parent_path, scandir): + def _select_from(self, parent_path, scandir, follow_symlinks): + follow_dirlinks = False if follow_symlinks is None else follow_symlinks successor_select = self.successor._select_from - for starting_point in self._iterate_directories(parent_path): - for p in successor_select(starting_point, scandir): + for starting_point in self._iterate_directories(parent_path, follow_dirlinks): + for p in successor_select(starting_point, scandir, follow_symlinks): yield p @@ -189,10 +191,10 @@ class _DoubleRecursiveWildcardSelector(_RecursiveWildcardSelector): multiple non-adjacent '**' segments. """ - def _select_from(self, parent_path, scandir): + def _select_from(self, parent_path, scandir, follow_symlinks): yielded = set() try: - for p in super()._select_from(parent_path, scandir): + for p in super()._select_from(parent_path, scandir, follow_symlinks): if p not in yielded: yield p yielded.add(p) @@ -286,6 +288,9 @@ def __init__(self, *args): for arg in args: if isinstance(arg, _BasePurePath): path = arg._raw_path + if arg._flavour is ntpath and self._flavour is posixpath: + # GH-103631: Convert separators for backwards compatibility. + path = path.replace('\\', '/') else: try: path = os.fspath(arg) @@ -379,7 +384,10 @@ def _str_normcase(self): try: return self._str_normcase_cached except AttributeError: - self._str_normcase_cached = self._flavour.normcase(str(self)) + if _is_case_sensitive(self._flavour): + self._str_normcase_cached = str(self) + else: + self._str_normcase_cached = str(self).lower() return self._str_normcase_cached @property @@ -716,6 +724,11 @@ def as_uri(self): return prefix + urlquote_from_bytes(os.fsencode(path)) +# Subclassing os.PathLike makes isinstance() checks slower, +# which in turn makes Path construction slower. Register instead! +os.PathLike.register(PurePath) + + class PurePosixPath(PurePath): """PurePath subclass for non-Windows systems. @@ -996,7 +1009,7 @@ def _make_child_relpath(self, name): path._tail_cached = tail + [name] return path - def glob(self, pattern, *, case_sensitive=None): + def glob(self, pattern, *, case_sensitive=None, follow_symlinks=None): """Iterate over this subtree and yield all existing files (of any kind, including directories) matching the given relative pattern. """ @@ -1009,10 +1022,10 @@ def glob(self, pattern, *, case_sensitive=None): if pattern[-1] in (self._flavour.sep, self._flavour.altsep): pattern_parts.append('') selector = _make_selector(tuple(pattern_parts), self._flavour, case_sensitive) - for p in selector.select_from(self): + for p in selector.select_from(self, follow_symlinks): yield p - def rglob(self, pattern, *, case_sensitive=None): + def rglob(self, pattern, *, case_sensitive=None, follow_symlinks=None): """Recursively yield all existing files (of any kind, including directories) matching the given relative pattern, anywhere in this subtree. @@ -1024,7 +1037,7 @@ def rglob(self, pattern, *, case_sensitive=None): if pattern and pattern[-1] in (self._flavour.sep, self._flavour.altsep): pattern_parts.append('') selector = _make_selector(("**",) + tuple(pattern_parts), self._flavour, case_sensitive) - for p in selector.select_from(self): + for p in selector.select_from(self, follow_symlinks): yield p def walk(self, top_down=True, on_error=None, follow_symlinks=False): diff --git a/Lib/test/imghdrdata/python-raw.jpg b/Lib/test/imghdrdata/python-raw.jpg deleted file mode 100644 index 11940b3410ddf0..00000000000000 Binary files a/Lib/test/imghdrdata/python-raw.jpg and /dev/null differ diff --git a/Lib/test/imghdrdata/python.bmp b/Lib/test/imghdrdata/python.bmp deleted file mode 100644 index 675f95191a45fd..00000000000000 Binary files a/Lib/test/imghdrdata/python.bmp and /dev/null differ diff --git a/Lib/test/imghdrdata/python.exr b/Lib/test/imghdrdata/python.exr deleted file mode 100644 index 773c81ee1fb850..00000000000000 Binary files a/Lib/test/imghdrdata/python.exr and /dev/null differ diff --git a/Lib/test/imghdrdata/python.jpg b/Lib/test/imghdrdata/python.jpg deleted file mode 100644 index 21222c09f5a71d..00000000000000 Binary files a/Lib/test/imghdrdata/python.jpg and /dev/null differ diff --git a/Lib/test/imghdrdata/python.pbm b/Lib/test/imghdrdata/python.pbm deleted file mode 100644 index 1848ba7ff064e7..00000000000000 --- a/Lib/test/imghdrdata/python.pbm +++ /dev/null @@ -1,3 +0,0 @@ -P4 -16 16 -[a_X? \ No newline at end of file diff --git a/Lib/test/imghdrdata/python.ras b/Lib/test/imghdrdata/python.ras deleted file mode 100644 index 130e96f817ed9d..00000000000000 Binary files a/Lib/test/imghdrdata/python.ras and /dev/null differ diff --git a/Lib/test/imghdrdata/python.sgi b/Lib/test/imghdrdata/python.sgi deleted file mode 100644 index ffe9081c7a5b67..00000000000000 Binary files a/Lib/test/imghdrdata/python.sgi and /dev/null differ diff --git a/Lib/test/imghdrdata/python.tiff b/Lib/test/imghdrdata/python.tiff deleted file mode 100644 index 39d0bfcec02533..00000000000000 Binary files a/Lib/test/imghdrdata/python.tiff and /dev/null differ diff --git a/Lib/test/imghdrdata/python.webp b/Lib/test/imghdrdata/python.webp deleted file mode 100644 index e824ec7fb1c7fa..00000000000000 Binary files a/Lib/test/imghdrdata/python.webp and /dev/null differ diff --git a/Lib/test/inspect_fodder2.py b/Lib/test/inspect_fodder2.py index 2dc49817087c44..03464613694605 100644 --- a/Lib/test/inspect_fodder2.py +++ b/Lib/test/inspect_fodder2.py @@ -273,3 +273,20 @@ def wrapper(*a, **kwd): @deco_factory(foo=(1 + 2), bar=lambda: 1) def complex_decorated(foo=0, bar=lambda: 0): return foo + bar() + +# line 276 +parenthesized_lambda = ( + lambda: ()) +parenthesized_lambda2 = [ + lambda: ()][0] +parenthesized_lambda3 = {0: + lambda: ()}[0] + +# line 285 +post_line_parenthesized_lambda1 = (lambda: () +) + +# line 289 +nested_lambda = ( + lambda right: [].map( + lambda length: ())) diff --git a/Lib/test/pythoninfo.py b/Lib/test/pythoninfo.py index adc211b3e2169c..b84c14400d42f0 100644 --- a/Lib/test/pythoninfo.py +++ b/Lib/test/pythoninfo.py @@ -296,7 +296,6 @@ def format_groups(groups): "TEMP", "TERM", "TILE_LIBRARY", - "TIX_LIBRARY", "TMP", "TMPDIR", "TRAVIS", diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index d555c53fee50a2..7f8b1d71dbd227 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -1865,15 +1865,16 @@ def missing_compiler_executable(cmd_names=[]): missing. """ - # TODO (PEP 632): alternate check without using distutils - from distutils import ccompiler, sysconfig, spawn, errors + from setuptools._distutils import ccompiler, sysconfig, spawn + from setuptools import errors + compiler = ccompiler.new_compiler() sysconfig.customize_compiler(compiler) if compiler.compiler_type == "msvc": # MSVC has no executables, so check whether initialization succeeds try: compiler.initialize() - except errors.DistutilsPlatformError: + except errors.PlatformError: return "msvc" for name in compiler.executables: if cmd_names and name not in cmd_names: @@ -2270,6 +2271,42 @@ def requires_venv_with_pip(): return unittest.skipUnless(ctypes, 'venv: pip requires ctypes') +# Context manager that creates a virtual environment, install setuptools and wheel in it +# and returns the path to the venv directory and the path to the python executable +@contextlib.contextmanager +def setup_venv_with_pip_setuptools_wheel(venv_dir): + import subprocess + from .os_helper import temp_cwd + + with temp_cwd() as temp_dir: + # Create virtual environment to get setuptools + cmd = [sys.executable, '-X', 'dev', '-m', 'venv', venv_dir] + if verbose: + print() + print('Run:', ' '.join(cmd)) + subprocess.run(cmd, check=True) + + venv = os.path.join(temp_dir, venv_dir) + + # Get the Python executable of the venv + python_exe = os.path.basename(sys.executable) + if sys.platform == 'win32': + python = os.path.join(venv, 'Scripts', python_exe) + else: + python = os.path.join(venv, 'bin', python_exe) + + cmd = [python, '-X', 'dev', + '-m', 'pip', 'install', + findfile('setuptools-67.6.1-py3-none-any.whl'), + findfile('wheel-0.40.0-py3-none-any.whl')] + if verbose: + print() + print('Run:', ' '.join(cmd)) + subprocess.run(cmd, check=True) + + yield python + + # True if Python is built with the Py_DEBUG macro defined: if # Python is built in debug mode (./configure --with-pydebug). Py_DEBUG = hasattr(sys, 'gettotalrefcount') diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py index cf128e1e8cd04c..76edbf5121c806 100644 --- a/Lib/test/test_ast.py +++ b/Lib/test/test_ast.py @@ -4,6 +4,7 @@ import enum import os import sys +import textwrap import types import unittest import warnings @@ -1583,20 +1584,41 @@ def arguments(args=None, posonlyargs=None, vararg=None, def test_funcdef(self): a = ast.arguments([], [], None, [], [], None, []) - f = ast.FunctionDef("x", [], a, [], [], None) + f = ast.FunctionDef("x", a, [], [], None, None, []) self.stmt(f, "empty body on FunctionDef") - f = ast.FunctionDef("x", [], a, [ast.Pass()], [ast.Name("x", ast.Store())], - None) + f = ast.FunctionDef("x", a, [ast.Pass()], [ast.Name("x", ast.Store())], None, None, []) self.stmt(f, "must have Load context") - f = ast.FunctionDef("x", [], a, [ast.Pass()], [], - ast.Name("x", ast.Store())) + f = ast.FunctionDef("x", a, [ast.Pass()], [], + ast.Name("x", ast.Store()), None, []) self.stmt(f, "must have Load context") def fac(args): - return ast.FunctionDef("x", [], args, [ast.Pass()], [], None) + return ast.FunctionDef("x", args, [ast.Pass()], [], None, None, []) self._check_arguments(fac, self.stmt) + def test_funcdef_pattern_matching(self): + # gh-104799: New fields on FunctionDef should be added at the end + def matcher(node): + match node: + case ast.FunctionDef("foo", ast.arguments(args=[ast.arg("bar")]), + [ast.Pass()], + [ast.Name("capybara", ast.Load())], + ast.Name("pacarana", ast.Load())): + return True + case _: + return False + + code = """ + @capybara + def foo(bar) -> pacarana: + pass + """ + source = ast.parse(textwrap.dedent(code)) + funcdef = source.body[0] + self.assertIsInstance(funcdef, ast.FunctionDef) + self.assertTrue(matcher(funcdef)) + def test_classdef(self): - def cls(bases=None, keywords=None, body=None, decorator_list=None): + def cls(bases=None, keywords=None, body=None, decorator_list=None, type_params=None): if bases is None: bases = [] if keywords is None: @@ -1605,8 +1627,10 @@ def cls(bases=None, keywords=None, body=None, decorator_list=None): body = [ast.Pass()] if decorator_list is None: decorator_list = [] - return ast.ClassDef("myclass", [], bases, keywords, - body, decorator_list) + if type_params is None: + type_params = [] + return ast.ClassDef("myclass", bases, keywords, + body, decorator_list, type_params) self.stmt(cls(bases=[ast.Name("x", ast.Store())]), "must have Load context") self.stmt(cls(keywords=[ast.keyword("x", ast.Name("x", ast.Store()))]), @@ -2895,23 +2919,23 @@ def main(): exec_results = [ ('Module', [('Expr', (1, 0, 1, 4), ('Constant', (1, 0, 1, 4), None, None))], []), ('Module', [('Expr', (1, 0, 1, 18), ('Constant', (1, 0, 1, 18), 'module docstring', None))], []), -('Module', [('FunctionDef', (1, 0, 1, 13), 'f', [], ('arguments', [], [], None, [], [], None, []), [('Pass', (1, 9, 1, 13))], [], None, None)], []), -('Module', [('FunctionDef', (1, 0, 1, 29), 'f', [], ('arguments', [], [], None, [], [], None, []), [('Expr', (1, 9, 1, 29), ('Constant', (1, 9, 1, 29), 'function docstring', None))], [], None, None)], []), -('Module', [('FunctionDef', (1, 0, 1, 14), 'f', [], ('arguments', [], [('arg', (1, 6, 1, 7), 'a', None, None)], None, [], [], None, []), [('Pass', (1, 10, 1, 14))], [], None, None)], []), -('Module', [('FunctionDef', (1, 0, 1, 16), 'f', [], ('arguments', [], [('arg', (1, 6, 1, 7), 'a', None, None)], None, [], [], None, [('Constant', (1, 8, 1, 9), 0, None)]), [('Pass', (1, 12, 1, 16))], [], None, None)], []), -('Module', [('FunctionDef', (1, 0, 1, 18), 'f', [], ('arguments', [], [], ('arg', (1, 7, 1, 11), 'args', None, None), [], [], None, []), [('Pass', (1, 14, 1, 18))], [], None, None)], []), -('Module', [('FunctionDef', (1, 0, 1, 23), 'f', [], ('arguments', [], [], ('arg', (1, 7, 1, 16), 'args', ('Starred', (1, 13, 1, 16), ('Name', (1, 14, 1, 16), 'Ts', ('Load',)), ('Load',)), None), [], [], None, []), [('Pass', (1, 19, 1, 23))], [], None, None)], []), -('Module', [('FunctionDef', (1, 0, 1, 36), 'f', [], ('arguments', [], [], ('arg', (1, 7, 1, 29), 'args', ('Starred', (1, 13, 1, 29), ('Subscript', (1, 14, 1, 29), ('Name', (1, 14, 1, 19), 'tuple', ('Load',)), ('Tuple', (1, 20, 1, 28), [('Name', (1, 20, 1, 23), 'int', ('Load',)), ('Constant', (1, 25, 1, 28), Ellipsis, None)], ('Load',)), ('Load',)), ('Load',)), None), [], [], None, []), [('Pass', (1, 32, 1, 36))], [], None, None)], []), -('Module', [('FunctionDef', (1, 0, 1, 36), 'f', [], ('arguments', [], [], ('arg', (1, 7, 1, 29), 'args', ('Starred', (1, 13, 1, 29), ('Subscript', (1, 14, 1, 29), ('Name', (1, 14, 1, 19), 'tuple', ('Load',)), ('Tuple', (1, 20, 1, 28), [('Name', (1, 20, 1, 23), 'int', ('Load',)), ('Starred', (1, 25, 1, 28), ('Name', (1, 26, 1, 28), 'Ts', ('Load',)), ('Load',))], ('Load',)), ('Load',)), ('Load',)), None), [], [], None, []), [('Pass', (1, 32, 1, 36))], [], None, None)], []), -('Module', [('FunctionDef', (1, 0, 1, 21), 'f', [], ('arguments', [], [], None, [], [], ('arg', (1, 8, 1, 14), 'kwargs', None, None), []), [('Pass', (1, 17, 1, 21))], [], None, None)], []), -('Module', [('FunctionDef', (1, 0, 1, 71), 'f', [], ('arguments', [], [('arg', (1, 6, 1, 7), 'a', None, None), ('arg', (1, 9, 1, 10), 'b', None, None), ('arg', (1, 14, 1, 15), 'c', None, None), ('arg', (1, 22, 1, 23), 'd', None, None), ('arg', (1, 28, 1, 29), 'e', None, None)], ('arg', (1, 35, 1, 39), 'args', None, None), [('arg', (1, 41, 1, 42), 'f', None, None)], [('Constant', (1, 43, 1, 45), 42, None)], ('arg', (1, 49, 1, 55), 'kwargs', None, None), [('Constant', (1, 11, 1, 12), 1, None), ('Constant', (1, 16, 1, 20), None, None), ('List', (1, 24, 1, 26), [], ('Load',)), ('Dict', (1, 30, 1, 32), [], [])]), [('Expr', (1, 58, 1, 71), ('Constant', (1, 58, 1, 71), 'doc for f()', None))], [], None, None)], []), -('Module', [('FunctionDef', (1, 0, 1, 27), 'f', [], ('arguments', [], [], None, [], [], None, []), [('Pass', (1, 23, 1, 27))], [], ('Subscript', (1, 11, 1, 21), ('Name', (1, 11, 1, 16), 'tuple', ('Load',)), ('Tuple', (1, 17, 1, 20), [('Starred', (1, 17, 1, 20), ('Name', (1, 18, 1, 20), 'Ts', ('Load',)), ('Load',))], ('Load',)), ('Load',)), None)], []), -('Module', [('FunctionDef', (1, 0, 1, 32), 'f', [], ('arguments', [], [], None, [], [], None, []), [('Pass', (1, 28, 1, 32))], [], ('Subscript', (1, 11, 1, 26), ('Name', (1, 11, 1, 16), 'tuple', ('Load',)), ('Tuple', (1, 17, 1, 25), [('Name', (1, 17, 1, 20), 'int', ('Load',)), ('Starred', (1, 22, 1, 25), ('Name', (1, 23, 1, 25), 'Ts', ('Load',)), ('Load',))], ('Load',)), ('Load',)), None)], []), -('Module', [('FunctionDef', (1, 0, 1, 45), 'f', [], ('arguments', [], [], None, [], [], None, []), [('Pass', (1, 41, 1, 45))], [], ('Subscript', (1, 11, 1, 39), ('Name', (1, 11, 1, 16), 'tuple', ('Load',)), ('Tuple', (1, 17, 1, 38), [('Name', (1, 17, 1, 20), 'int', ('Load',)), ('Starred', (1, 22, 1, 38), ('Subscript', (1, 23, 1, 38), ('Name', (1, 23, 1, 28), 'tuple', ('Load',)), ('Tuple', (1, 29, 1, 37), [('Name', (1, 29, 1, 32), 'int', ('Load',)), ('Constant', (1, 34, 1, 37), Ellipsis, None)], ('Load',)), ('Load',)), ('Load',))], ('Load',)), ('Load',)), None)], []), -('Module', [('ClassDef', (1, 0, 1, 12), 'C', [], [], [], [('Pass', (1, 8, 1, 12))], [])], []), -('Module', [('ClassDef', (1, 0, 1, 32), 'C', [], [], [], [('Expr', (1, 9, 1, 32), ('Constant', (1, 9, 1, 32), 'docstring for class C', None))], [])], []), -('Module', [('ClassDef', (1, 0, 1, 21), 'C', [], [('Name', (1, 8, 1, 14), 'object', ('Load',))], [], [('Pass', (1, 17, 1, 21))], [])], []), -('Module', [('FunctionDef', (1, 0, 1, 16), 'f', [], ('arguments', [], [], None, [], [], None, []), [('Return', (1, 8, 1, 16), ('Constant', (1, 15, 1, 16), 1, None))], [], None, None)], []), +('Module', [('FunctionDef', (1, 0, 1, 13), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (1, 9, 1, 13))], [], None, None, [])], []), +('Module', [('FunctionDef', (1, 0, 1, 29), 'f', ('arguments', [], [], None, [], [], None, []), [('Expr', (1, 9, 1, 29), ('Constant', (1, 9, 1, 29), 'function docstring', None))], [], None, None, [])], []), +('Module', [('FunctionDef', (1, 0, 1, 14), 'f', ('arguments', [], [('arg', (1, 6, 1, 7), 'a', None, None)], None, [], [], None, []), [('Pass', (1, 10, 1, 14))], [], None, None, [])], []), +('Module', [('FunctionDef', (1, 0, 1, 16), 'f', ('arguments', [], [('arg', (1, 6, 1, 7), 'a', None, None)], None, [], [], None, [('Constant', (1, 8, 1, 9), 0, None)]), [('Pass', (1, 12, 1, 16))], [], None, None, [])], []), +('Module', [('FunctionDef', (1, 0, 1, 18), 'f', ('arguments', [], [], ('arg', (1, 7, 1, 11), 'args', None, None), [], [], None, []), [('Pass', (1, 14, 1, 18))], [], None, None, [])], []), +('Module', [('FunctionDef', (1, 0, 1, 23), 'f', ('arguments', [], [], ('arg', (1, 7, 1, 16), 'args', ('Starred', (1, 13, 1, 16), ('Name', (1, 14, 1, 16), 'Ts', ('Load',)), ('Load',)), None), [], [], None, []), [('Pass', (1, 19, 1, 23))], [], None, None, [])], []), +('Module', [('FunctionDef', (1, 0, 1, 36), 'f', ('arguments', [], [], ('arg', (1, 7, 1, 29), 'args', ('Starred', (1, 13, 1, 29), ('Subscript', (1, 14, 1, 29), ('Name', (1, 14, 1, 19), 'tuple', ('Load',)), ('Tuple', (1, 20, 1, 28), [('Name', (1, 20, 1, 23), 'int', ('Load',)), ('Constant', (1, 25, 1, 28), Ellipsis, None)], ('Load',)), ('Load',)), ('Load',)), None), [], [], None, []), [('Pass', (1, 32, 1, 36))], [], None, None, [])], []), +('Module', [('FunctionDef', (1, 0, 1, 36), 'f', ('arguments', [], [], ('arg', (1, 7, 1, 29), 'args', ('Starred', (1, 13, 1, 29), ('Subscript', (1, 14, 1, 29), ('Name', (1, 14, 1, 19), 'tuple', ('Load',)), ('Tuple', (1, 20, 1, 28), [('Name', (1, 20, 1, 23), 'int', ('Load',)), ('Starred', (1, 25, 1, 28), ('Name', (1, 26, 1, 28), 'Ts', ('Load',)), ('Load',))], ('Load',)), ('Load',)), ('Load',)), None), [], [], None, []), [('Pass', (1, 32, 1, 36))], [], None, None, [])], []), +('Module', [('FunctionDef', (1, 0, 1, 21), 'f', ('arguments', [], [], None, [], [], ('arg', (1, 8, 1, 14), 'kwargs', None, None), []), [('Pass', (1, 17, 1, 21))], [], None, None, [])], []), +('Module', [('FunctionDef', (1, 0, 1, 71), 'f', ('arguments', [], [('arg', (1, 6, 1, 7), 'a', None, None), ('arg', (1, 9, 1, 10), 'b', None, None), ('arg', (1, 14, 1, 15), 'c', None, None), ('arg', (1, 22, 1, 23), 'd', None, None), ('arg', (1, 28, 1, 29), 'e', None, None)], ('arg', (1, 35, 1, 39), 'args', None, None), [('arg', (1, 41, 1, 42), 'f', None, None)], [('Constant', (1, 43, 1, 45), 42, None)], ('arg', (1, 49, 1, 55), 'kwargs', None, None), [('Constant', (1, 11, 1, 12), 1, None), ('Constant', (1, 16, 1, 20), None, None), ('List', (1, 24, 1, 26), [], ('Load',)), ('Dict', (1, 30, 1, 32), [], [])]), [('Expr', (1, 58, 1, 71), ('Constant', (1, 58, 1, 71), 'doc for f()', None))], [], None, None, [])], []), +('Module', [('FunctionDef', (1, 0, 1, 27), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (1, 23, 1, 27))], [], ('Subscript', (1, 11, 1, 21), ('Name', (1, 11, 1, 16), 'tuple', ('Load',)), ('Tuple', (1, 17, 1, 20), [('Starred', (1, 17, 1, 20), ('Name', (1, 18, 1, 20), 'Ts', ('Load',)), ('Load',))], ('Load',)), ('Load',)), None, [])], []), +('Module', [('FunctionDef', (1, 0, 1, 32), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (1, 28, 1, 32))], [], ('Subscript', (1, 11, 1, 26), ('Name', (1, 11, 1, 16), 'tuple', ('Load',)), ('Tuple', (1, 17, 1, 25), [('Name', (1, 17, 1, 20), 'int', ('Load',)), ('Starred', (1, 22, 1, 25), ('Name', (1, 23, 1, 25), 'Ts', ('Load',)), ('Load',))], ('Load',)), ('Load',)), None, [])], []), +('Module', [('FunctionDef', (1, 0, 1, 45), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (1, 41, 1, 45))], [], ('Subscript', (1, 11, 1, 39), ('Name', (1, 11, 1, 16), 'tuple', ('Load',)), ('Tuple', (1, 17, 1, 38), [('Name', (1, 17, 1, 20), 'int', ('Load',)), ('Starred', (1, 22, 1, 38), ('Subscript', (1, 23, 1, 38), ('Name', (1, 23, 1, 28), 'tuple', ('Load',)), ('Tuple', (1, 29, 1, 37), [('Name', (1, 29, 1, 32), 'int', ('Load',)), ('Constant', (1, 34, 1, 37), Ellipsis, None)], ('Load',)), ('Load',)), ('Load',))], ('Load',)), ('Load',)), None, [])], []), +('Module', [('ClassDef', (1, 0, 1, 12), 'C', [], [], [('Pass', (1, 8, 1, 12))], [], [])], []), +('Module', [('ClassDef', (1, 0, 1, 32), 'C', [], [], [('Expr', (1, 9, 1, 32), ('Constant', (1, 9, 1, 32), 'docstring for class C', None))], [], [])], []), +('Module', [('ClassDef', (1, 0, 1, 21), 'C', [('Name', (1, 8, 1, 14), 'object', ('Load',))], [], [('Pass', (1, 17, 1, 21))], [], [])], []), +('Module', [('FunctionDef', (1, 0, 1, 16), 'f', ('arguments', [], [], None, [], [], None, []), [('Return', (1, 8, 1, 16), ('Constant', (1, 15, 1, 16), 1, None))], [], None, None, [])], []), ('Module', [('Delete', (1, 0, 1, 5), [('Name', (1, 4, 1, 5), 'v', ('Del',))])], []), ('Module', [('Assign', (1, 0, 1, 5), [('Name', (1, 0, 1, 1), 'v', ('Store',))], ('Constant', (1, 4, 1, 5), 1, None), None)], []), ('Module', [('Assign', (1, 0, 1, 7), [('Tuple', (1, 0, 1, 3), [('Name', (1, 0, 1, 1), 'a', ('Store',)), ('Name', (1, 2, 1, 3), 'b', ('Store',))], ('Store',))], ('Name', (1, 6, 1, 7), 'c', ('Load',)), None)], []), @@ -2948,41 +2972,41 @@ def main(): ('Module', [('Expr', (1, 0, 1, 20), ('DictComp', (1, 0, 1, 20), ('Name', (1, 1, 1, 2), 'a', ('Load',)), ('Name', (1, 5, 1, 6), 'b', ('Load',)), [('comprehension', ('Tuple', (1, 11, 1, 14), [('Name', (1, 11, 1, 12), 'v', ('Store',)), ('Name', (1, 13, 1, 14), 'w', ('Store',))], ('Store',)), ('Name', (1, 18, 1, 19), 'x', ('Load',)), [], 0)]))], []), ('Module', [('Expr', (1, 0, 1, 19), ('SetComp', (1, 0, 1, 19), ('Name', (1, 1, 1, 2), 'r', ('Load',)), [('comprehension', ('Name', (1, 7, 1, 8), 'l', ('Store',)), ('Name', (1, 12, 1, 13), 'x', ('Load',)), [('Name', (1, 17, 1, 18), 'g', ('Load',))], 0)]))], []), ('Module', [('Expr', (1, 0, 1, 16), ('SetComp', (1, 0, 1, 16), ('Name', (1, 1, 1, 2), 'r', ('Load',)), [('comprehension', ('Tuple', (1, 7, 1, 10), [('Name', (1, 7, 1, 8), 'l', ('Store',)), ('Name', (1, 9, 1, 10), 'm', ('Store',))], ('Store',)), ('Name', (1, 14, 1, 15), 'x', ('Load',)), [], 0)]))], []), -('Module', [('AsyncFunctionDef', (1, 0, 3, 18), 'f', [], ('arguments', [], [], None, [], [], None, []), [('Expr', (2, 1, 2, 17), ('Constant', (2, 1, 2, 17), 'async function', None)), ('Expr', (3, 1, 3, 18), ('Await', (3, 1, 3, 18), ('Call', (3, 7, 3, 18), ('Name', (3, 7, 3, 16), 'something', ('Load',)), [], [])))], [], None, None)], []), -('Module', [('AsyncFunctionDef', (1, 0, 3, 8), 'f', [], ('arguments', [], [], None, [], [], None, []), [('AsyncFor', (2, 1, 3, 8), ('Name', (2, 11, 2, 12), 'e', ('Store',)), ('Name', (2, 16, 2, 17), 'i', ('Load',)), [('Expr', (2, 19, 2, 20), ('Constant', (2, 19, 2, 20), 1, None))], [('Expr', (3, 7, 3, 8), ('Constant', (3, 7, 3, 8), 2, None))], None)], [], None, None)], []), -('Module', [('AsyncFunctionDef', (1, 0, 2, 21), 'f', [], ('arguments', [], [], None, [], [], None, []), [('AsyncWith', (2, 1, 2, 21), [('withitem', ('Name', (2, 12, 2, 13), 'a', ('Load',)), ('Name', (2, 17, 2, 18), 'b', ('Store',)))], [('Expr', (2, 20, 2, 21), ('Constant', (2, 20, 2, 21), 1, None))], None)], [], None, None)], []), +('Module', [('AsyncFunctionDef', (1, 0, 3, 18), 'f', ('arguments', [], [], None, [], [], None, []), [('Expr', (2, 1, 2, 17), ('Constant', (2, 1, 2, 17), 'async function', None)), ('Expr', (3, 1, 3, 18), ('Await', (3, 1, 3, 18), ('Call', (3, 7, 3, 18), ('Name', (3, 7, 3, 16), 'something', ('Load',)), [], [])))], [], None, None, [])], []), +('Module', [('AsyncFunctionDef', (1, 0, 3, 8), 'f', ('arguments', [], [], None, [], [], None, []), [('AsyncFor', (2, 1, 3, 8), ('Name', (2, 11, 2, 12), 'e', ('Store',)), ('Name', (2, 16, 2, 17), 'i', ('Load',)), [('Expr', (2, 19, 2, 20), ('Constant', (2, 19, 2, 20), 1, None))], [('Expr', (3, 7, 3, 8), ('Constant', (3, 7, 3, 8), 2, None))], None)], [], None, None, [])], []), +('Module', [('AsyncFunctionDef', (1, 0, 2, 21), 'f', ('arguments', [], [], None, [], [], None, []), [('AsyncWith', (2, 1, 2, 21), [('withitem', ('Name', (2, 12, 2, 13), 'a', ('Load',)), ('Name', (2, 17, 2, 18), 'b', ('Store',)))], [('Expr', (2, 20, 2, 21), ('Constant', (2, 20, 2, 21), 1, None))], None)], [], None, None, [])], []), ('Module', [('Expr', (1, 0, 1, 14), ('Dict', (1, 0, 1, 14), [None, ('Constant', (1, 10, 1, 11), 2, None)], [('Dict', (1, 3, 1, 8), [('Constant', (1, 4, 1, 5), 1, None)], [('Constant', (1, 6, 1, 7), 2, None)]), ('Constant', (1, 12, 1, 13), 3, None)]))], []), ('Module', [('Expr', (1, 0, 1, 12), ('Set', (1, 0, 1, 12), [('Starred', (1, 1, 1, 8), ('Set', (1, 2, 1, 8), [('Constant', (1, 3, 1, 4), 1, None), ('Constant', (1, 6, 1, 7), 2, None)]), ('Load',)), ('Constant', (1, 10, 1, 11), 3, None)]))], []), -('Module', [('AsyncFunctionDef', (1, 0, 2, 21), 'f', [], ('arguments', [], [], None, [], [], None, []), [('Expr', (2, 1, 2, 21), ('ListComp', (2, 1, 2, 21), ('Name', (2, 2, 2, 3), 'i', ('Load',)), [('comprehension', ('Name', (2, 14, 2, 15), 'b', ('Store',)), ('Name', (2, 19, 2, 20), 'c', ('Load',)), [], 1)]))], [], None, None)], []), -('Module', [('FunctionDef', (4, 0, 4, 13), 'f', [], ('arguments', [], [], None, [], [], None, []), [('Pass', (4, 9, 4, 13))], [('Name', (1, 1, 1, 6), 'deco1', ('Load',)), ('Call', (2, 1, 2, 8), ('Name', (2, 1, 2, 6), 'deco2', ('Load',)), [], []), ('Call', (3, 1, 3, 9), ('Name', (3, 1, 3, 6), 'deco3', ('Load',)), [('Constant', (3, 7, 3, 8), 1, None)], [])], None, None)], []), -('Module', [('AsyncFunctionDef', (4, 0, 4, 19), 'f', [], ('arguments', [], [], None, [], [], None, []), [('Pass', (4, 15, 4, 19))], [('Name', (1, 1, 1, 6), 'deco1', ('Load',)), ('Call', (2, 1, 2, 8), ('Name', (2, 1, 2, 6), 'deco2', ('Load',)), [], []), ('Call', (3, 1, 3, 9), ('Name', (3, 1, 3, 6), 'deco3', ('Load',)), [('Constant', (3, 7, 3, 8), 1, None)], [])], None, None)], []), -('Module', [('ClassDef', (4, 0, 4, 13), 'C', [], [], [], [('Pass', (4, 9, 4, 13))], [('Name', (1, 1, 1, 6), 'deco1', ('Load',)), ('Call', (2, 1, 2, 8), ('Name', (2, 1, 2, 6), 'deco2', ('Load',)), [], []), ('Call', (3, 1, 3, 9), ('Name', (3, 1, 3, 6), 'deco3', ('Load',)), [('Constant', (3, 7, 3, 8), 1, None)], [])])], []), -('Module', [('FunctionDef', (2, 0, 2, 13), 'f', [], ('arguments', [], [], None, [], [], None, []), [('Pass', (2, 9, 2, 13))], [('Call', (1, 1, 1, 19), ('Name', (1, 1, 1, 5), 'deco', ('Load',)), [('GeneratorExp', (1, 5, 1, 19), ('Name', (1, 6, 1, 7), 'a', ('Load',)), [('comprehension', ('Name', (1, 12, 1, 13), 'a', ('Store',)), ('Name', (1, 17, 1, 18), 'b', ('Load',)), [], 0)])], [])], None, None)], []), -('Module', [('FunctionDef', (2, 0, 2, 13), 'f', [], ('arguments', [], [], None, [], [], None, []), [('Pass', (2, 9, 2, 13))], [('Attribute', (1, 1, 1, 6), ('Attribute', (1, 1, 1, 4), ('Name', (1, 1, 1, 2), 'a', ('Load',)), 'b', ('Load',)), 'c', ('Load',))], None, None)], []), +('Module', [('AsyncFunctionDef', (1, 0, 2, 21), 'f', ('arguments', [], [], None, [], [], None, []), [('Expr', (2, 1, 2, 21), ('ListComp', (2, 1, 2, 21), ('Name', (2, 2, 2, 3), 'i', ('Load',)), [('comprehension', ('Name', (2, 14, 2, 15), 'b', ('Store',)), ('Name', (2, 19, 2, 20), 'c', ('Load',)), [], 1)]))], [], None, None, [])], []), +('Module', [('FunctionDef', (4, 0, 4, 13), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (4, 9, 4, 13))], [('Name', (1, 1, 1, 6), 'deco1', ('Load',)), ('Call', (2, 1, 2, 8), ('Name', (2, 1, 2, 6), 'deco2', ('Load',)), [], []), ('Call', (3, 1, 3, 9), ('Name', (3, 1, 3, 6), 'deco3', ('Load',)), [('Constant', (3, 7, 3, 8), 1, None)], [])], None, None, [])], []), +('Module', [('AsyncFunctionDef', (4, 0, 4, 19), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (4, 15, 4, 19))], [('Name', (1, 1, 1, 6), 'deco1', ('Load',)), ('Call', (2, 1, 2, 8), ('Name', (2, 1, 2, 6), 'deco2', ('Load',)), [], []), ('Call', (3, 1, 3, 9), ('Name', (3, 1, 3, 6), 'deco3', ('Load',)), [('Constant', (3, 7, 3, 8), 1, None)], [])], None, None, [])], []), +('Module', [('ClassDef', (4, 0, 4, 13), 'C', [], [], [('Pass', (4, 9, 4, 13))], [('Name', (1, 1, 1, 6), 'deco1', ('Load',)), ('Call', (2, 1, 2, 8), ('Name', (2, 1, 2, 6), 'deco2', ('Load',)), [], []), ('Call', (3, 1, 3, 9), ('Name', (3, 1, 3, 6), 'deco3', ('Load',)), [('Constant', (3, 7, 3, 8), 1, None)], [])], [])], []), +('Module', [('FunctionDef', (2, 0, 2, 13), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (2, 9, 2, 13))], [('Call', (1, 1, 1, 19), ('Name', (1, 1, 1, 5), 'deco', ('Load',)), [('GeneratorExp', (1, 5, 1, 19), ('Name', (1, 6, 1, 7), 'a', ('Load',)), [('comprehension', ('Name', (1, 12, 1, 13), 'a', ('Store',)), ('Name', (1, 17, 1, 18), 'b', ('Load',)), [], 0)])], [])], None, None, [])], []), +('Module', [('FunctionDef', (2, 0, 2, 13), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (2, 9, 2, 13))], [('Attribute', (1, 1, 1, 6), ('Attribute', (1, 1, 1, 4), ('Name', (1, 1, 1, 2), 'a', ('Load',)), 'b', ('Load',)), 'c', ('Load',))], None, None, [])], []), ('Module', [('Expr', (1, 0, 1, 8), ('NamedExpr', (1, 1, 1, 7), ('Name', (1, 1, 1, 2), 'a', ('Store',)), ('Constant', (1, 6, 1, 7), 1, None)))], []), -('Module', [('FunctionDef', (1, 0, 1, 18), 'f', [], ('arguments', [('arg', (1, 6, 1, 7), 'a', None, None)], [], None, [], [], None, []), [('Pass', (1, 14, 1, 18))], [], None, None)], []), -('Module', [('FunctionDef', (1, 0, 1, 26), 'f', [], ('arguments', [('arg', (1, 6, 1, 7), 'a', None, None)], [('arg', (1, 12, 1, 13), 'c', None, None), ('arg', (1, 15, 1, 16), 'd', None, None), ('arg', (1, 18, 1, 19), 'e', None, None)], None, [], [], None, []), [('Pass', (1, 22, 1, 26))], [], None, None)], []), -('Module', [('FunctionDef', (1, 0, 1, 29), 'f', [], ('arguments', [('arg', (1, 6, 1, 7), 'a', None, None)], [('arg', (1, 12, 1, 13), 'c', None, None)], None, [('arg', (1, 18, 1, 19), 'd', None, None), ('arg', (1, 21, 1, 22), 'e', None, None)], [None, None], None, []), [('Pass', (1, 25, 1, 29))], [], None, None)], []), -('Module', [('FunctionDef', (1, 0, 1, 39), 'f', [], ('arguments', [('arg', (1, 6, 1, 7), 'a', None, None)], [('arg', (1, 12, 1, 13), 'c', None, None)], None, [('arg', (1, 18, 1, 19), 'd', None, None), ('arg', (1, 21, 1, 22), 'e', None, None)], [None, None], ('arg', (1, 26, 1, 32), 'kwargs', None, None), []), [('Pass', (1, 35, 1, 39))], [], None, None)], []), -('Module', [('FunctionDef', (1, 0, 1, 20), 'f', [], ('arguments', [('arg', (1, 6, 1, 7), 'a', None, None)], [], None, [], [], None, [('Constant', (1, 8, 1, 9), 1, None)]), [('Pass', (1, 16, 1, 20))], [], None, None)], []), -('Module', [('FunctionDef', (1, 0, 1, 29), 'f', [], ('arguments', [('arg', (1, 6, 1, 7), 'a', None, None)], [('arg', (1, 14, 1, 15), 'b', None, None), ('arg', (1, 19, 1, 20), 'c', None, None)], None, [], [], None, [('Constant', (1, 8, 1, 9), 1, None), ('Constant', (1, 16, 1, 17), 2, None), ('Constant', (1, 21, 1, 22), 4, None)]), [('Pass', (1, 25, 1, 29))], [], None, None)], []), -('Module', [('FunctionDef', (1, 0, 1, 32), 'f', [], ('arguments', [('arg', (1, 6, 1, 7), 'a', None, None)], [('arg', (1, 14, 1, 15), 'b', None, None)], None, [('arg', (1, 22, 1, 23), 'c', None, None)], [('Constant', (1, 24, 1, 25), 4, None)], None, [('Constant', (1, 8, 1, 9), 1, None), ('Constant', (1, 16, 1, 17), 2, None)]), [('Pass', (1, 28, 1, 32))], [], None, None)], []), -('Module', [('FunctionDef', (1, 0, 1, 30), 'f', [], ('arguments', [('arg', (1, 6, 1, 7), 'a', None, None)], [('arg', (1, 14, 1, 15), 'b', None, None)], None, [('arg', (1, 22, 1, 23), 'c', None, None)], [None], None, [('Constant', (1, 8, 1, 9), 1, None), ('Constant', (1, 16, 1, 17), 2, None)]), [('Pass', (1, 26, 1, 30))], [], None, None)], []), -('Module', [('FunctionDef', (1, 0, 1, 42), 'f', [], ('arguments', [('arg', (1, 6, 1, 7), 'a', None, None)], [('arg', (1, 14, 1, 15), 'b', None, None)], None, [('arg', (1, 22, 1, 23), 'c', None, None)], [('Constant', (1, 24, 1, 25), 4, None)], ('arg', (1, 29, 1, 35), 'kwargs', None, None), [('Constant', (1, 8, 1, 9), 1, None), ('Constant', (1, 16, 1, 17), 2, None)]), [('Pass', (1, 38, 1, 42))], [], None, None)], []), -('Module', [('FunctionDef', (1, 0, 1, 40), 'f', [], ('arguments', [('arg', (1, 6, 1, 7), 'a', None, None)], [('arg', (1, 14, 1, 15), 'b', None, None)], None, [('arg', (1, 22, 1, 23), 'c', None, None)], [None], ('arg', (1, 27, 1, 33), 'kwargs', None, None), [('Constant', (1, 8, 1, 9), 1, None), ('Constant', (1, 16, 1, 17), 2, None)]), [('Pass', (1, 36, 1, 40))], [], None, None)], []), +('Module', [('FunctionDef', (1, 0, 1, 18), 'f', ('arguments', [('arg', (1, 6, 1, 7), 'a', None, None)], [], None, [], [], None, []), [('Pass', (1, 14, 1, 18))], [], None, None, [])], []), +('Module', [('FunctionDef', (1, 0, 1, 26), 'f', ('arguments', [('arg', (1, 6, 1, 7), 'a', None, None)], [('arg', (1, 12, 1, 13), 'c', None, None), ('arg', (1, 15, 1, 16), 'd', None, None), ('arg', (1, 18, 1, 19), 'e', None, None)], None, [], [], None, []), [('Pass', (1, 22, 1, 26))], [], None, None, [])], []), +('Module', [('FunctionDef', (1, 0, 1, 29), 'f', ('arguments', [('arg', (1, 6, 1, 7), 'a', None, None)], [('arg', (1, 12, 1, 13), 'c', None, None)], None, [('arg', (1, 18, 1, 19), 'd', None, None), ('arg', (1, 21, 1, 22), 'e', None, None)], [None, None], None, []), [('Pass', (1, 25, 1, 29))], [], None, None, [])], []), +('Module', [('FunctionDef', (1, 0, 1, 39), 'f', ('arguments', [('arg', (1, 6, 1, 7), 'a', None, None)], [('arg', (1, 12, 1, 13), 'c', None, None)], None, [('arg', (1, 18, 1, 19), 'd', None, None), ('arg', (1, 21, 1, 22), 'e', None, None)], [None, None], ('arg', (1, 26, 1, 32), 'kwargs', None, None), []), [('Pass', (1, 35, 1, 39))], [], None, None, [])], []), +('Module', [('FunctionDef', (1, 0, 1, 20), 'f', ('arguments', [('arg', (1, 6, 1, 7), 'a', None, None)], [], None, [], [], None, [('Constant', (1, 8, 1, 9), 1, None)]), [('Pass', (1, 16, 1, 20))], [], None, None, [])], []), +('Module', [('FunctionDef', (1, 0, 1, 29), 'f', ('arguments', [('arg', (1, 6, 1, 7), 'a', None, None)], [('arg', (1, 14, 1, 15), 'b', None, None), ('arg', (1, 19, 1, 20), 'c', None, None)], None, [], [], None, [('Constant', (1, 8, 1, 9), 1, None), ('Constant', (1, 16, 1, 17), 2, None), ('Constant', (1, 21, 1, 22), 4, None)]), [('Pass', (1, 25, 1, 29))], [], None, None, [])], []), +('Module', [('FunctionDef', (1, 0, 1, 32), 'f', ('arguments', [('arg', (1, 6, 1, 7), 'a', None, None)], [('arg', (1, 14, 1, 15), 'b', None, None)], None, [('arg', (1, 22, 1, 23), 'c', None, None)], [('Constant', (1, 24, 1, 25), 4, None)], None, [('Constant', (1, 8, 1, 9), 1, None), ('Constant', (1, 16, 1, 17), 2, None)]), [('Pass', (1, 28, 1, 32))], [], None, None, [])], []), +('Module', [('FunctionDef', (1, 0, 1, 30), 'f', ('arguments', [('arg', (1, 6, 1, 7), 'a', None, None)], [('arg', (1, 14, 1, 15), 'b', None, None)], None, [('arg', (1, 22, 1, 23), 'c', None, None)], [None], None, [('Constant', (1, 8, 1, 9), 1, None), ('Constant', (1, 16, 1, 17), 2, None)]), [('Pass', (1, 26, 1, 30))], [], None, None, [])], []), +('Module', [('FunctionDef', (1, 0, 1, 42), 'f', ('arguments', [('arg', (1, 6, 1, 7), 'a', None, None)], [('arg', (1, 14, 1, 15), 'b', None, None)], None, [('arg', (1, 22, 1, 23), 'c', None, None)], [('Constant', (1, 24, 1, 25), 4, None)], ('arg', (1, 29, 1, 35), 'kwargs', None, None), [('Constant', (1, 8, 1, 9), 1, None), ('Constant', (1, 16, 1, 17), 2, None)]), [('Pass', (1, 38, 1, 42))], [], None, None, [])], []), +('Module', [('FunctionDef', (1, 0, 1, 40), 'f', ('arguments', [('arg', (1, 6, 1, 7), 'a', None, None)], [('arg', (1, 14, 1, 15), 'b', None, None)], None, [('arg', (1, 22, 1, 23), 'c', None, None)], [None], ('arg', (1, 27, 1, 33), 'kwargs', None, None), [('Constant', (1, 8, 1, 9), 1, None), ('Constant', (1, 16, 1, 17), 2, None)]), [('Pass', (1, 36, 1, 40))], [], None, None, [])], []), ('Module', [('TypeAlias', (1, 0, 1, 12), ('Name', (1, 5, 1, 6), 'X', ('Store',)), [], ('Name', (1, 9, 1, 12), 'int', ('Load',)))], []), ('Module', [('TypeAlias', (1, 0, 1, 15), ('Name', (1, 5, 1, 6), 'X', ('Store',)), [('TypeVar', (1, 7, 1, 8), 'T', None)], ('Name', (1, 12, 1, 15), 'int', ('Load',)))], []), ('Module', [('TypeAlias', (1, 0, 1, 32), ('Name', (1, 5, 1, 6), 'X', ('Store',)), [('TypeVar', (1, 7, 1, 8), 'T', None), ('TypeVarTuple', (1, 10, 1, 13), 'Ts'), ('ParamSpec', (1, 15, 1, 18), 'P')], ('Tuple', (1, 22, 1, 32), [('Name', (1, 23, 1, 24), 'T', ('Load',)), ('Name', (1, 26, 1, 28), 'Ts', ('Load',)), ('Name', (1, 30, 1, 31), 'P', ('Load',))], ('Load',)))], []), ('Module', [('TypeAlias', (1, 0, 1, 37), ('Name', (1, 5, 1, 6), 'X', ('Store',)), [('TypeVar', (1, 7, 1, 13), 'T', ('Name', (1, 10, 1, 13), 'int', ('Load',))), ('TypeVarTuple', (1, 15, 1, 18), 'Ts'), ('ParamSpec', (1, 20, 1, 23), 'P')], ('Tuple', (1, 27, 1, 37), [('Name', (1, 28, 1, 29), 'T', ('Load',)), ('Name', (1, 31, 1, 33), 'Ts', ('Load',)), ('Name', (1, 35, 1, 36), 'P', ('Load',))], ('Load',)))], []), ('Module', [('TypeAlias', (1, 0, 1, 44), ('Name', (1, 5, 1, 6), 'X', ('Store',)), [('TypeVar', (1, 7, 1, 20), 'T', ('Tuple', (1, 10, 1, 20), [('Name', (1, 11, 1, 14), 'int', ('Load',)), ('Name', (1, 16, 1, 19), 'str', ('Load',))], ('Load',))), ('TypeVarTuple', (1, 22, 1, 25), 'Ts'), ('ParamSpec', (1, 27, 1, 30), 'P')], ('Tuple', (1, 34, 1, 44), [('Name', (1, 35, 1, 36), 'T', ('Load',)), ('Name', (1, 38, 1, 40), 'Ts', ('Load',)), ('Name', (1, 42, 1, 43), 'P', ('Load',))], ('Load',)))], []), -('Module', [('ClassDef', (1, 0, 1, 16), 'X', [('TypeVar', (1, 8, 1, 9), 'T', None)], [], [], [('Pass', (1, 12, 1, 16))], [])], []), -('Module', [('ClassDef', (1, 0, 1, 26), 'X', [('TypeVar', (1, 8, 1, 9), 'T', None), ('TypeVarTuple', (1, 11, 1, 14), 'Ts'), ('ParamSpec', (1, 16, 1, 19), 'P')], [], [], [('Pass', (1, 22, 1, 26))], [])], []), -('Module', [('ClassDef', (1, 0, 1, 31), 'X', [('TypeVar', (1, 8, 1, 14), 'T', ('Name', (1, 11, 1, 14), 'int', ('Load',))), ('TypeVarTuple', (1, 16, 1, 19), 'Ts'), ('ParamSpec', (1, 21, 1, 24), 'P')], [], [], [('Pass', (1, 27, 1, 31))], [])], []), -('Module', [('ClassDef', (1, 0, 1, 38), 'X', [('TypeVar', (1, 8, 1, 21), 'T', ('Tuple', (1, 11, 1, 21), [('Name', (1, 12, 1, 15), 'int', ('Load',)), ('Name', (1, 17, 1, 20), 'str', ('Load',))], ('Load',))), ('TypeVarTuple', (1, 23, 1, 26), 'Ts'), ('ParamSpec', (1, 28, 1, 31), 'P')], [], [], [('Pass', (1, 34, 1, 38))], [])], []), -('Module', [('FunctionDef', (1, 0, 1, 16), 'f', [('TypeVar', (1, 6, 1, 7), 'T', None)], ('arguments', [], [], None, [], [], None, []), [('Pass', (1, 12, 1, 16))], [], None, None)], []), -('Module', [('FunctionDef', (1, 0, 1, 26), 'f', [('TypeVar', (1, 6, 1, 7), 'T', None), ('TypeVarTuple', (1, 9, 1, 12), 'Ts'), ('ParamSpec', (1, 14, 1, 17), 'P')], ('arguments', [], [], None, [], [], None, []), [('Pass', (1, 22, 1, 26))], [], None, None)], []), -('Module', [('FunctionDef', (1, 0, 1, 31), 'f', [('TypeVar', (1, 6, 1, 12), 'T', ('Name', (1, 9, 1, 12), 'int', ('Load',))), ('TypeVarTuple', (1, 14, 1, 17), 'Ts'), ('ParamSpec', (1, 19, 1, 22), 'P')], ('arguments', [], [], None, [], [], None, []), [('Pass', (1, 27, 1, 31))], [], None, None)], []), -('Module', [('FunctionDef', (1, 0, 1, 38), 'f', [('TypeVar', (1, 6, 1, 19), 'T', ('Tuple', (1, 9, 1, 19), [('Name', (1, 10, 1, 13), 'int', ('Load',)), ('Name', (1, 15, 1, 18), 'str', ('Load',))], ('Load',))), ('TypeVarTuple', (1, 21, 1, 24), 'Ts'), ('ParamSpec', (1, 26, 1, 29), 'P')], ('arguments', [], [], None, [], [], None, []), [('Pass', (1, 34, 1, 38))], [], None, None)], []), +('Module', [('ClassDef', (1, 0, 1, 16), 'X', [], [], [('Pass', (1, 12, 1, 16))], [], [('TypeVar', (1, 8, 1, 9), 'T', None)])], []), +('Module', [('ClassDef', (1, 0, 1, 26), 'X', [], [], [('Pass', (1, 22, 1, 26))], [], [('TypeVar', (1, 8, 1, 9), 'T', None), ('TypeVarTuple', (1, 11, 1, 14), 'Ts'), ('ParamSpec', (1, 16, 1, 19), 'P')])], []), +('Module', [('ClassDef', (1, 0, 1, 31), 'X', [], [], [('Pass', (1, 27, 1, 31))], [], [('TypeVar', (1, 8, 1, 14), 'T', ('Name', (1, 11, 1, 14), 'int', ('Load',))), ('TypeVarTuple', (1, 16, 1, 19), 'Ts'), ('ParamSpec', (1, 21, 1, 24), 'P')])], []), +('Module', [('ClassDef', (1, 0, 1, 38), 'X', [], [], [('Pass', (1, 34, 1, 38))], [], [('TypeVar', (1, 8, 1, 21), 'T', ('Tuple', (1, 11, 1, 21), [('Name', (1, 12, 1, 15), 'int', ('Load',)), ('Name', (1, 17, 1, 20), 'str', ('Load',))], ('Load',))), ('TypeVarTuple', (1, 23, 1, 26), 'Ts'), ('ParamSpec', (1, 28, 1, 31), 'P')])], []), +('Module', [('FunctionDef', (1, 0, 1, 16), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (1, 12, 1, 16))], [], None, None, [('TypeVar', (1, 6, 1, 7), 'T', None)])], []), +('Module', [('FunctionDef', (1, 0, 1, 26), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (1, 22, 1, 26))], [], None, None, [('TypeVar', (1, 6, 1, 7), 'T', None), ('TypeVarTuple', (1, 9, 1, 12), 'Ts'), ('ParamSpec', (1, 14, 1, 17), 'P')])], []), +('Module', [('FunctionDef', (1, 0, 1, 31), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (1, 27, 1, 31))], [], None, None, [('TypeVar', (1, 6, 1, 12), 'T', ('Name', (1, 9, 1, 12), 'int', ('Load',))), ('TypeVarTuple', (1, 14, 1, 17), 'Ts'), ('ParamSpec', (1, 19, 1, 22), 'P')])], []), +('Module', [('FunctionDef', (1, 0, 1, 38), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (1, 34, 1, 38))], [], None, None, [('TypeVar', (1, 6, 1, 19), 'T', ('Tuple', (1, 9, 1, 19), [('Name', (1, 10, 1, 13), 'int', ('Load',)), ('Name', (1, 15, 1, 18), 'str', ('Load',))], ('Load',))), ('TypeVarTuple', (1, 21, 1, 24), 'Ts'), ('ParamSpec', (1, 26, 1, 29), 'P')])], []), ] single_results = [ ('Interactive', [('Expr', (1, 0, 1, 3), ('BinOp', (1, 0, 1, 3), ('Constant', (1, 0, 1, 1), 1, None), ('Add',), ('Constant', (1, 2, 1, 3), 2, None)))]), diff --git a/Lib/test/test_asyncgen.py b/Lib/test/test_asyncgen.py index 09e4010b0e53d6..4f00558770dafd 100644 --- a/Lib/test/test_asyncgen.py +++ b/Lib/test/test_asyncgen.py @@ -415,8 +415,9 @@ async def gen(): self.assertIsInstance(g.ag_frame, types.FrameType) self.assertFalse(g.ag_running) self.assertIsInstance(g.ag_code, types.CodeType) - - self.assertTrue(inspect.isawaitable(g.aclose())) + aclose = g.aclose() + self.assertTrue(inspect.isawaitable(aclose)) + aclose.close() class AsyncGenAsyncioTest(unittest.TestCase): @@ -1693,5 +1694,38 @@ async def run(): self.loop.run_until_complete(run()) +class TestUnawaitedWarnings(unittest.TestCase): + def test_asend(self): + async def gen(): + yield 1 + + msg = f"coroutine method 'asend' of '{gen.__qualname__}' was never awaited" + with self.assertWarnsRegex(RuntimeWarning, msg): + g = gen() + g.asend(None) + gc_collect() + + def test_athrow(self): + async def gen(): + yield 1 + + msg = f"coroutine method 'athrow' of '{gen.__qualname__}' was never awaited" + with self.assertWarnsRegex(RuntimeWarning, msg): + g = gen() + g.athrow(RuntimeError) + gc_collect() + + def test_aclose(self): + async def gen(): + yield 1 + + msg = f"coroutine method 'aclose' of '{gen.__qualname__}' was never awaited" + with self.assertWarnsRegex(RuntimeWarning, msg): + g = gen() + g.aclose() + gc_collect() + + + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_configparser.py b/Lib/test/test_configparser.py index da17c00063c56d..eef439412528d3 100644 --- a/Lib/test/test_configparser.py +++ b/Lib/test/test_configparser.py @@ -907,9 +907,6 @@ def test_interpolation(self): if self.interpolation == configparser._UNSET: self.assertEqual(e.args, ("bar11", "Foo", "something %(with11)s lots of interpolation (11 steps)")) - elif isinstance(self.interpolation, configparser.LegacyInterpolation): - self.assertEqual(e.args, ("bar11", "Foo", - "something %(with11)s lots of interpolation (11 steps)")) def test_interpolation_missing_value(self): cf = self.get_interpolation_config() @@ -921,9 +918,6 @@ def test_interpolation_missing_value(self): if self.interpolation == configparser._UNSET: self.assertEqual(e.args, ('name', 'Interpolation Error', '%(reference)s', 'reference')) - elif isinstance(self.interpolation, configparser.LegacyInterpolation): - self.assertEqual(e.args, ('name', 'Interpolation Error', - '%(reference)s', 'reference')) def test_items(self): self.check_items_config([('default', ''), @@ -942,9 +936,6 @@ def test_safe_interpolation(self): self.assertEqual(cf.get("section", "ok"), "xxx/%s") if self.interpolation == configparser._UNSET: self.assertEqual(cf.get("section", "not_ok"), "xxx/xxx/%s") - elif isinstance(self.interpolation, configparser.LegacyInterpolation): - with self.assertRaises(TypeError): - cf.get("section", "not_ok") def test_set_malformatted_interpolation(self): cf = self.fromstring("[sect]\n" @@ -1025,31 +1016,6 @@ class CustomConfigParser(configparser.ConfigParser): cf.read_string(self.ini) self.assertMatchesIni(cf) - -class ConfigParserTestCaseLegacyInterpolation(ConfigParserTestCase): - config_class = configparser.ConfigParser - with warnings.catch_warnings(): - warnings.simplefilter("ignore", DeprecationWarning) - interpolation = configparser.LegacyInterpolation() - - def test_set_malformatted_interpolation(self): - cf = self.fromstring("[sect]\n" - "option1{eq}foo\n".format(eq=self.delimiters[0])) - - self.assertEqual(cf.get('sect', "option1"), "foo") - - cf.set("sect", "option1", "%foo") - self.assertEqual(cf.get('sect', "option1"), "%foo") - cf.set("sect", "option1", "foo%") - self.assertEqual(cf.get('sect', "option1"), "foo%") - cf.set("sect", "option1", "f%oo") - self.assertEqual(cf.get('sect', "option1"), "f%oo") - - # bug #5741: double percents are *not* malformed - cf.set("sect", "option2", "foo%%bar") - self.assertEqual(cf.get("sect", "option2"), "foo%%bar") - - class ConfigParserTestCaseInvalidInterpolationType(unittest.TestCase): def test_error_on_wrong_type_for_interpolation(self): for value in [configparser.ExtendedInterpolation, 42, "a string"]: @@ -1636,14 +1602,6 @@ def test_interpolation_validation(self): self.assertEqual(str(cm.exception), "bad interpolation variable " "reference '%(()'") - def test_legacyinterpolation_deprecation(self): - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always", DeprecationWarning) - configparser.LegacyInterpolation() - self.assertGreaterEqual(len(w), 1) - for warning in w: - self.assertIs(warning.category, DeprecationWarning) - def test_sectionproxy_repr(self): parser = configparser.ConfigParser() parser.read_string(""" diff --git a/Lib/test/test_cppext.py b/Lib/test/test_cppext.py index 4fb62d87e860fc..e2fedc9735079f 100644 --- a/Lib/test/test_cppext.py +++ b/Lib/test/test_cppext.py @@ -35,39 +35,20 @@ def test_build_cpp03(self): # the test uses venv+pip: skip if it's not available @support.requires_venv_with_pip() def check_build(self, std_cpp03, extension_name): - # Build in a temporary directory - with os_helper.temp_cwd(): - self._check_build(std_cpp03, extension_name) + venv_dir = 'env' + with support.setup_venv_with_pip_setuptools_wheel(venv_dir) as python_exe: + self._check_build(std_cpp03, extension_name, python_exe) - def _check_build(self, std_cpp03, extension_name): + def _check_build(self, std_cpp03, extension_name, python_exe): pkg_dir = 'pkg' os.mkdir(pkg_dir) shutil.copy(SETUP_TESTCPPEXT, os.path.join(pkg_dir, "setup.py")) - venv_dir = 'env' - verbose = support.verbose - - # Create virtual environment to get setuptools - cmd = [sys.executable, '-X', 'dev', '-m', 'venv', venv_dir] - if verbose: - print() - print('Run:', ' '.join(cmd)) - subprocess.run(cmd, check=True) - - # Get the Python executable of the venv - python_exe = 'python' - if sys.executable.endswith('.exe'): - python_exe += '.exe' - if MS_WINDOWS: - python = os.path.join(venv_dir, 'Scripts', python_exe) - else: - python = os.path.join(venv_dir, 'bin', python_exe) - def run_cmd(operation, cmd): env = os.environ.copy() env['CPYTHON_TEST_CPP_STD'] = 'c++03' if std_cpp03 else 'c++11' env['CPYTHON_TEST_EXT_NAME'] = extension_name - if verbose: + if support.verbose: print('Run:', ' '.join(cmd)) subprocess.run(cmd, check=True, env=env) else: @@ -81,14 +62,8 @@ def run_cmd(operation, cmd): self.fail( f"{operation} failed with exit code {proc.returncode}") - cmd = [python, '-X', 'dev', - '-m', 'pip', 'install', - support.findfile('setuptools-67.6.1-py3-none-any.whl'), - support.findfile('wheel-0.40.0-py3-none-any.whl')] - run_cmd('Install build dependencies', cmd) - # Build and install the C++ extension - cmd = [python, '-X', 'dev', + cmd = [python_exe, '-X', 'dev', '-m', 'pip', 'install', '--no-build-isolation', os.path.abspath(pkg_dir)] run_cmd('Install', cmd) @@ -96,14 +71,14 @@ def run_cmd(operation, cmd): # Do a reference run. Until we test that running python # doesn't leak references (gh-94755), run it so one can manually check # -X showrefcount results against this baseline. - cmd = [python, + cmd = [python_exe, '-X', 'dev', '-X', 'showrefcount', '-c', 'pass'] run_cmd('Reference run', cmd) # Import the C++ extension - cmd = [python, + cmd = [python_exe, '-X', 'dev', '-X', 'showrefcount', '-c', f"import {extension_name}"] diff --git a/Lib/test/test_imghdr.py b/Lib/test/test_imghdr.py deleted file mode 100644 index 208c8eee455e7b..00000000000000 --- a/Lib/test/test_imghdr.py +++ /dev/null @@ -1,144 +0,0 @@ -import io -import os -import pathlib -import unittest -import warnings -from test.support import findfile, warnings_helper -from test.support.os_helper import TESTFN, unlink - -imghdr = warnings_helper.import_deprecated("imghdr") - - -TEST_FILES = ( - ('python.png', 'png'), - ('python.gif', 'gif'), - ('python.bmp', 'bmp'), - ('python.ppm', 'ppm'), - ('python.pgm', 'pgm'), - ('python.pbm', 'pbm'), - ('python.jpg', 'jpeg'), - ('python-raw.jpg', 'jpeg'), # raw JPEG without JFIF/EXIF markers - ('python.ras', 'rast'), - ('python.sgi', 'rgb'), - ('python.tiff', 'tiff'), - ('python.xbm', 'xbm'), - ('python.webp', 'webp'), - ('python.exr', 'exr'), -) - -class UnseekableIO(io.FileIO): - def tell(self): - raise io.UnsupportedOperation - - def seek(self, *args, **kwargs): - raise io.UnsupportedOperation - -class TestImghdr(unittest.TestCase): - @classmethod - def setUpClass(cls): - cls.testfile = findfile('python.png', subdir='imghdrdata') - with open(cls.testfile, 'rb') as stream: - cls.testdata = stream.read() - - def tearDown(self): - unlink(TESTFN) - - def test_data(self): - for filename, expected in TEST_FILES: - filename = findfile(filename, subdir='imghdrdata') - self.assertEqual(imghdr.what(filename), expected) - with open(filename, 'rb') as stream: - self.assertEqual(imghdr.what(stream), expected) - with open(filename, 'rb') as stream: - data = stream.read() - self.assertEqual(imghdr.what(None, data), expected) - self.assertEqual(imghdr.what(None, bytearray(data)), expected) - - def test_pathlike_filename(self): - for filename, expected in TEST_FILES: - with self.subTest(filename=filename): - filename = findfile(filename, subdir='imghdrdata') - self.assertEqual(imghdr.what(pathlib.Path(filename)), expected) - - def test_register_test(self): - def test_jumbo(h, file): - if h.startswith(b'eggs'): - return 'ham' - imghdr.tests.append(test_jumbo) - self.addCleanup(imghdr.tests.pop) - self.assertEqual(imghdr.what(None, b'eggs'), 'ham') - - def test_file_pos(self): - with open(TESTFN, 'wb') as stream: - stream.write(b'ababagalamaga') - pos = stream.tell() - stream.write(self.testdata) - with open(TESTFN, 'rb') as stream: - stream.seek(pos) - self.assertEqual(imghdr.what(stream), 'png') - self.assertEqual(stream.tell(), pos) - - def test_bad_args(self): - with self.assertRaises(TypeError): - imghdr.what() - with self.assertRaises(AttributeError): - imghdr.what(None) - with self.assertRaises(TypeError): - imghdr.what(self.testfile, 1) - with self.assertRaises(AttributeError): - imghdr.what(os.fsencode(self.testfile)) - with open(self.testfile, 'rb') as f: - with self.assertRaises(AttributeError): - imghdr.what(f.fileno()) - - def test_invalid_headers(self): - for header in (b'\211PN\r\n', - b'\001\331', - b'\x59\xA6', - b'cutecat', - b'000000JFI', - b'GIF80'): - self.assertIsNone(imghdr.what(None, header)) - - def test_string_data(self): - with warnings.catch_warnings(): - warnings.simplefilter("ignore", BytesWarning) - for filename, _ in TEST_FILES: - filename = findfile(filename, subdir='imghdrdata') - with open(filename, 'rb') as stream: - data = stream.read().decode('latin1') - with self.assertRaises(TypeError): - imghdr.what(io.StringIO(data)) - with self.assertRaises(TypeError): - imghdr.what(None, data) - - def test_missing_file(self): - with self.assertRaises(FileNotFoundError): - imghdr.what('missing') - - def test_closed_file(self): - stream = open(self.testfile, 'rb') - stream.close() - with self.assertRaises(ValueError) as cm: - imghdr.what(stream) - stream = io.BytesIO(self.testdata) - stream.close() - with self.assertRaises(ValueError) as cm: - imghdr.what(stream) - - def test_unseekable(self): - with open(TESTFN, 'wb') as stream: - stream.write(self.testdata) - with UnseekableIO(TESTFN, 'rb') as stream: - with self.assertRaises(io.UnsupportedOperation): - imghdr.what(stream) - - def test_output_stream(self): - with open(TESTFN, 'wb') as stream: - stream.write(self.testdata) - stream.seek(0) - with self.assertRaises(OSError) as cm: - imghdr.what(stream) - -if __name__ == '__main__': - unittest.main() diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py index 0590e49d0e1a43..a7bd680d0f5bcc 100644 --- a/Lib/test/test_inspect.py +++ b/Lib/test/test_inspect.py @@ -776,6 +776,22 @@ def test_twoline_indented_lambda(self): # where the second line _is_ indented. self.assertSourceEqual(mod2.tlli, 33, 34) + def test_parenthesized_multiline_lambda(self): + # Test inspect.getsource with a parenthesized multi-line lambda + # function. + self.assertSourceEqual(mod2.parenthesized_lambda, 279, 279) + self.assertSourceEqual(mod2.parenthesized_lambda2, 281, 281) + self.assertSourceEqual(mod2.parenthesized_lambda3, 283, 283) + + def test_post_line_parenthesized_lambda(self): + # Test inspect.getsource with a parenthesized multi-line lambda + # function. + self.assertSourceEqual(mod2.post_line_parenthesized_lambda1, 286, 287) + + def test_nested_lambda(self): + # Test inspect.getsource with a nested lambda function. + self.assertSourceEqual(mod2.nested_lambda, 291, 292) + def test_onelinefunc(self): # Test inspect.getsource with a regular one-line function. self.assertSourceEqual(mod2.onelinefunc, 37, 37) @@ -2766,6 +2782,11 @@ class ThisWorksNow: # Regression test for issue #20586 test_callable(_testcapi.docstring_with_signature_but_no_doc) + # Regression test for gh-104955 + method = bytearray.__release_buffer__ + sig = test_unbound_method(method) + self.assertEqual(list(sig.parameters), ['self', 'buffer']) + @cpython_only @unittest.skipIf(MISSING_C_DOCSTRINGS, "Signature information for builtins requires docstrings") diff --git a/Lib/test/test_itertools.py b/Lib/test/test_itertools.py index 9fe559d4b7eed5..4d6ea780e15373 100644 --- a/Lib/test/test_itertools.py +++ b/Lib/test/test_itertools.py @@ -15,6 +15,26 @@ import struct import threading import gc +import warnings + +def pickle_deprecated(testfunc): + """ Run the test three times. + First, verify that a Deprecation Warning is raised. + Second, run normally but with DeprecationWarnings temporarily disabled. + Third, run with warnings promoted to errors. + """ + def inner(self): + with self.assertWarns(DeprecationWarning): + testfunc(self) + with warnings.catch_warnings(): + warnings.simplefilter("ignore", category=DeprecationWarning) + testfunc(self) + with warnings.catch_warnings(): + warnings.simplefilter("error", category=DeprecationWarning) + with self.assertRaises((DeprecationWarning, AssertionError, SystemError)): + testfunc(self) + + return inner maxsize = support.MAX_Py_ssize_t minsize = -maxsize-1 @@ -124,6 +144,7 @@ def expand(it, i=0): c = expand(compare[took:]) self.assertEqual(a, c); + @pickle_deprecated def test_accumulate(self): self.assertEqual(list(accumulate(range(10))), # one positional arg [0, 1, 3, 6, 10, 15, 21, 28, 36, 45]) @@ -220,6 +241,7 @@ def test_chain_from_iterable(self): self.assertRaises(TypeError, list, chain.from_iterable([2, 3])) self.assertEqual(list(islice(chain.from_iterable(repeat(range(5))), 2)), [0, 1]) + @pickle_deprecated def test_chain_reducible(self): for oper in [copy.deepcopy] + picklecopiers: it = chain('abc', 'def') @@ -233,6 +255,7 @@ def test_chain_reducible(self): for proto in range(pickle.HIGHEST_PROTOCOL + 1): self.pickletest(proto, chain('abc', 'def'), compare=list('abcdef')) + @pickle_deprecated def test_chain_setstate(self): self.assertRaises(TypeError, chain().__setstate__, ()) self.assertRaises(TypeError, chain().__setstate__, []) @@ -246,6 +269,7 @@ def test_chain_setstate(self): it.__setstate__((iter(['abc', 'def']), iter(['ghi']))) self.assertEqual(list(it), ['ghi', 'a', 'b', 'c', 'd', 'e', 'f']) + @pickle_deprecated def test_combinations(self): self.assertRaises(TypeError, combinations, 'abc') # missing r argument self.assertRaises(TypeError, combinations, 'abc', 2, 1) # too many arguments @@ -269,7 +293,6 @@ def test_combinations(self): self.assertEqual(list(op(testIntermediate)), [(0,1,3), (0,2,3), (1,2,3)]) - def combinations1(iterable, r): 'Pure python version shown in the docs' pool = tuple(iterable) @@ -337,6 +360,7 @@ def test_combinations_tuple_reuse(self): self.assertEqual(len(set(map(id, combinations('abcde', 3)))), 1) self.assertNotEqual(len(set(map(id, list(combinations('abcde', 3))))), 1) + @pickle_deprecated def test_combinations_with_replacement(self): cwr = combinations_with_replacement self.assertRaises(TypeError, cwr, 'abc') # missing r argument @@ -425,6 +449,7 @@ def test_combinations_with_replacement_tuple_reuse(self): self.assertEqual(len(set(map(id, cwr('abcde', 3)))), 1) self.assertNotEqual(len(set(map(id, list(cwr('abcde', 3))))), 1) + @pickle_deprecated def test_permutations(self): self.assertRaises(TypeError, permutations) # too few arguments self.assertRaises(TypeError, permutations, 'abc', 2, 1) # too many arguments @@ -531,6 +556,7 @@ def test_combinatorics(self): self.assertEqual(comb, list(filter(set(perm).__contains__, cwr))) # comb: cwr that is a perm self.assertEqual(comb, sorted(set(cwr) & set(perm))) # comb: both a cwr and a perm + @pickle_deprecated def test_compress(self): self.assertEqual(list(compress(data='ABCDEF', selectors=[1,0,1,0,1,1])), list('ACEF')) self.assertEqual(list(compress('ABCDEF', [1,0,1,0,1,1])), list('ACEF')) @@ -564,7 +590,7 @@ def test_compress(self): next(testIntermediate) self.assertEqual(list(op(testIntermediate)), list(result2)) - + @pickle_deprecated def test_count(self): self.assertEqual(lzip('abc',count()), [('a', 0), ('b', 1), ('c', 2)]) self.assertEqual(lzip('abc',count(3)), [('a', 3), ('b', 4), ('c', 5)]) @@ -613,6 +639,7 @@ def test_count(self): #check proper internal error handling for large "step' sizes count(1, maxsize+5); sys.exc_info() + @pickle_deprecated def test_count_with_stride(self): self.assertEqual(lzip('abc',count(2,3)), [('a', 2), ('b', 5), ('c', 8)]) self.assertEqual(lzip('abc',count(start=2,step=3)), @@ -675,6 +702,7 @@ def test_cycle(self): self.assertRaises(TypeError, cycle, 5) self.assertEqual(list(islice(cycle(gen3()),10)), [0,1,2,0,1,2,0,1,2,0]) + @pickle_deprecated def test_cycle_copy_pickle(self): # check copy, deepcopy, pickle c = cycle('abc') @@ -711,6 +739,7 @@ def test_cycle_copy_pickle(self): d = pickle.loads(p) # rebuild the cycle object self.assertEqual(take(20, d), list('cdeabcdeabcdeabcdeab')) + @pickle_deprecated def test_cycle_unpickle_compat(self): testcases = [ b'citertools\ncycle\n(c__builtin__\niter\n((lI1\naI2\naI3\natRI1\nbtR((lI1\naI0\ntb.', @@ -742,6 +771,7 @@ def test_cycle_unpickle_compat(self): it = pickle.loads(t) self.assertEqual(take(10, it), [2, 3, 1, 2, 3, 1, 2, 3, 1, 2]) + @pickle_deprecated def test_cycle_setstate(self): # Verify both modes for restoring state @@ -778,6 +808,7 @@ def test_cycle_setstate(self): self.assertRaises(TypeError, cycle('').__setstate__, ()) self.assertRaises(TypeError, cycle('').__setstate__, ([],)) + @pickle_deprecated def test_groupby(self): # Check whether it accepts arguments correctly self.assertEqual([], list(groupby([]))) @@ -935,6 +966,7 @@ def test_filter(self): c = filter(isEven, range(6)) self.pickletest(proto, c) + @pickle_deprecated def test_filterfalse(self): self.assertEqual(list(filterfalse(isEven, range(6))), [1,3,5]) self.assertEqual(list(filterfalse(None, [0,1,0,2,0])), [0,0,0]) @@ -965,6 +997,7 @@ def test_zip(self): lzip('abc', 'def')) @support.impl_detail("tuple reuse is specific to CPython") + @pickle_deprecated def test_zip_tuple_reuse(self): ids = list(map(id, zip('abc', 'def'))) self.assertEqual(min(ids), max(ids)) @@ -1040,6 +1073,7 @@ def test_zip_longest_tuple_reuse(self): ids = list(map(id, list(zip_longest('abc', 'def')))) self.assertEqual(len(dict.fromkeys(ids)), len(ids)) + @pickle_deprecated def test_zip_longest_pickling(self): for proto in range(pickle.HIGHEST_PROTOCOL + 1): self.pickletest(proto, zip_longest("abc", "def")) @@ -1186,6 +1220,7 @@ def test_product_tuple_reuse(self): self.assertEqual(len(set(map(id, product('abc', 'def')))), 1) self.assertNotEqual(len(set(map(id, list(product('abc', 'def'))))), 1) + @pickle_deprecated def test_product_pickling(self): # check copy, deepcopy, pickle for args, result in [ @@ -1201,6 +1236,7 @@ def test_product_pickling(self): for proto in range(pickle.HIGHEST_PROTOCOL + 1): self.pickletest(proto, product(*args)) + @pickle_deprecated def test_product_issue_25021(self): # test that indices are properly clamped to the length of the tuples p = product((1, 2),(3,)) @@ -1211,6 +1247,7 @@ def test_product_issue_25021(self): p.__setstate__((0, 0, 0x1000)) # will access tuple element 1 if not clamped self.assertRaises(StopIteration, next, p) + @pickle_deprecated def test_repeat(self): self.assertEqual(list(repeat(object='a', times=3)), ['a', 'a', 'a']) self.assertEqual(lzip(range(3),repeat('a')), @@ -1243,6 +1280,7 @@ def test_repeat_with_negative_times(self): self.assertEqual(repr(repeat('a', times=-1)), "repeat('a', 0)") self.assertEqual(repr(repeat('a', times=-2)), "repeat('a', 0)") + @pickle_deprecated def test_map(self): self.assertEqual(list(map(operator.pow, range(3), range(1,7))), [0**1, 1**2, 2**3]) @@ -1273,6 +1311,7 @@ def test_map(self): c = map(tupleize, 'abc', count()) self.pickletest(proto, c) + @pickle_deprecated def test_starmap(self): self.assertEqual(list(starmap(operator.pow, zip(range(3), range(1,7)))), [0**1, 1**2, 2**3]) @@ -1300,6 +1339,7 @@ def test_starmap(self): c = starmap(operator.pow, zip(range(3), range(1,7))) self.pickletest(proto, c) + @pickle_deprecated def test_islice(self): for args in [ # islice(args) should agree with range(args) (10, 20, 3), @@ -1394,6 +1434,7 @@ def __index__(self): self.assertEqual(list(islice(range(100), IntLike(10), IntLike(50), IntLike(5))), list(range(10,50,5))) + @pickle_deprecated def test_takewhile(self): data = [1, 3, 5, 20, 2, 4, 6, 8] self.assertEqual(list(takewhile(underten, data)), [1, 3, 5]) @@ -1414,6 +1455,7 @@ def test_takewhile(self): for proto in range(pickle.HIGHEST_PROTOCOL + 1): self.pickletest(proto, takewhile(underten, data)) + @pickle_deprecated def test_dropwhile(self): data = [1, 3, 5, 20, 2, 4, 6, 8] self.assertEqual(list(dropwhile(underten, data)), [20, 2, 4, 6, 8]) @@ -1431,6 +1473,7 @@ def test_dropwhile(self): for proto in range(pickle.HIGHEST_PROTOCOL + 1): self.pickletest(proto, dropwhile(underten, data)) + @pickle_deprecated def test_tee(self): n = 200 @@ -1732,6 +1775,7 @@ class TestExamples(unittest.TestCase): def test_accumulate(self): self.assertEqual(list(accumulate([1,2,3,4,5])), [1, 3, 6, 10, 15]) + @pickle_deprecated def test_accumulate_reducible(self): # check copy, deepcopy, pickle data = [1, 2, 3, 4, 5] @@ -1747,6 +1791,7 @@ def test_accumulate_reducible(self): self.assertEqual(list(copy.deepcopy(it)), accumulated[1:]) self.assertEqual(list(copy.copy(it)), accumulated[1:]) + @pickle_deprecated def test_accumulate_reducible_none(self): # Issue #25718: total is None it = accumulate([None, None, None], operator.is_) diff --git a/Lib/test/test_ntpath.py b/Lib/test/test_ntpath.py index 0e57c165ca98ea..538d758624c9d6 100644 --- a/Lib/test/test_ntpath.py +++ b/Lib/test/test_ntpath.py @@ -992,6 +992,26 @@ def test_fast_paths_in_use(self): self.assertTrue(os.path.exists is nt._path_exists) self.assertFalse(inspect.isfunction(os.path.exists)) + @unittest.skipIf(os.name != 'nt', "Dev Drives only exist on Win32") + def test_isdevdrive(self): + # Result may be True or False, but shouldn't raise + self.assertIn(ntpath.isdevdrive(os_helper.TESTFN), (True, False)) + # ntpath.isdevdrive can handle relative paths + self.assertIn(ntpath.isdevdrive("."), (True, False)) + self.assertIn(ntpath.isdevdrive(b"."), (True, False)) + # Volume syntax is supported + self.assertIn(ntpath.isdevdrive(os.listvolumes()[0]), (True, False)) + # Invalid volume returns False from os.path method + self.assertFalse(ntpath.isdevdrive(r"\\?\Volume{00000000-0000-0000-0000-000000000000}\\")) + # Invalid volume raises from underlying helper + with self.assertRaises(OSError): + nt._path_isdevdrive(r"\\?\Volume{00000000-0000-0000-0000-000000000000}\\") + + @unittest.skipIf(os.name == 'nt', "isdevdrive fallback only used off Win32") + def test_isdevdrive_fallback(self): + # Fallback always returns False + self.assertFalse(ntpath.isdevdrive(os_helper.TESTFN)) + class NtCommonTest(test_genericpath.CommonTest, unittest.TestCase): pathmodule = ntpath diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py index 0a38c2469e450d..d2f59416e582b3 100644 --- a/Lib/test/test_pathlib.py +++ b/Lib/test/test_pathlib.py @@ -795,6 +795,12 @@ def test_div(self): pp = P('//a') / '/c' self.assertEqual(pp, P('/c')) + def test_parse_windows_path(self): + P = self.cls + p = P('c:', 'a', 'b') + pp = P(pathlib.PureWindowsPath('c:\\a\\b')) + self.assertEqual(p, pp) + class PureWindowsPathTest(_BasePurePathTest, unittest.TestCase): cls = pathlib.PureWindowsPath @@ -910,6 +916,7 @@ def test_eq(self): self.assertEqual(P('a/B'), P('A/b')) self.assertEqual(P('C:a/B'), P('c:A/b')) self.assertEqual(P('//Some/SHARE/a/B'), P('//somE/share/A/b')) + self.assertEqual(P('\u0130'), P('i\u0307')) def test_as_uri(self): P = self.cls @@ -1862,6 +1869,35 @@ def _check(path, pattern, case_sensitive, expected): _check(path, "dirb/file*", True, []) _check(path, "dirb/file*", False, ["dirB/fileB"]) + @os_helper.skip_unless_symlink + def test_glob_follow_symlinks_common(self): + def _check(path, glob, expected): + actual = {path for path in path.glob(glob, follow_symlinks=True) + if "linkD" not in path.parent.parts} # exclude symlink loop. + self.assertEqual(actual, { P(BASE, q) for q in expected }) + P = self.cls + p = P(BASE) + _check(p, "fileB", []) + _check(p, "dir*/file*", ["dirB/fileB", "dirC/fileC"]) + _check(p, "*A", ["dirA", "fileA", "linkA"]) + _check(p, "*B/*", ["dirB/fileB", "dirB/linkD", "linkB/fileB", "linkB/linkD"]) + _check(p, "*/fileB", ["dirB/fileB", "linkB/fileB"]) + _check(p, "*/", ["dirA", "dirB", "dirC", "dirE", "linkB"]) + + @os_helper.skip_unless_symlink + def test_glob_no_follow_symlinks_common(self): + def _check(path, glob, expected): + actual = {path for path in path.glob(glob, follow_symlinks=False)} + self.assertEqual(actual, { P(BASE, q) for q in expected }) + P = self.cls + p = P(BASE) + _check(p, "fileB", []) + _check(p, "dir*/file*", ["dirB/fileB", "dirC/fileC"]) + _check(p, "*A", ["dirA", "fileA", "linkA"]) + _check(p, "*B/*", ["dirB/fileB", "dirB/linkD"]) + _check(p, "*/fileB", ["dirB/fileB"]) + _check(p, "*/", ["dirA", "dirB", "dirC", "dirE"]) + def test_rglob_common(self): def _check(glob, expected): self.assertEqual(sorted(glob), sorted(P(BASE, q) for q in expected)) @@ -1905,6 +1941,60 @@ def _check(glob, expected): _check(p.rglob("*.txt"), ["dirC/novel.txt"]) _check(p.rglob("*.*"), ["dirC/novel.txt"]) + @os_helper.skip_unless_symlink + def test_rglob_follow_symlinks_common(self): + def _check(path, glob, expected): + actual = {path for path in path.rglob(glob, follow_symlinks=True) + if 'linkD' not in path.parent.parts} # exclude symlink loop. + self.assertEqual(actual, { P(BASE, q) for q in expected }) + P = self.cls + p = P(BASE) + _check(p, "fileB", ["dirB/fileB", "dirA/linkC/fileB", "linkB/fileB"]) + _check(p, "*/fileA", []) + _check(p, "*/fileB", ["dirB/fileB", "dirA/linkC/fileB", "linkB/fileB"]) + _check(p, "file*", ["fileA", "dirA/linkC/fileB", "dirB/fileB", + "dirC/fileC", "dirC/dirD/fileD", "linkB/fileB"]) + _check(p, "*/", ["dirA", "dirA/linkC", "dirA/linkC/linkD", "dirB", "dirB/linkD", + "dirC", "dirC/dirD", "dirE", "linkB", "linkB/linkD"]) + _check(p, "", ["", "dirA", "dirA/linkC", "dirA/linkC/linkD", "dirB", "dirB/linkD", + "dirC", "dirE", "dirC/dirD", "linkB", "linkB/linkD"]) + + p = P(BASE, "dirC") + _check(p, "*", ["dirC/fileC", "dirC/novel.txt", + "dirC/dirD", "dirC/dirD/fileD"]) + _check(p, "file*", ["dirC/fileC", "dirC/dirD/fileD"]) + _check(p, "*/*", ["dirC/dirD/fileD"]) + _check(p, "*/", ["dirC/dirD"]) + _check(p, "", ["dirC", "dirC/dirD"]) + # gh-91616, a re module regression + _check(p, "*.txt", ["dirC/novel.txt"]) + _check(p, "*.*", ["dirC/novel.txt"]) + + @os_helper.skip_unless_symlink + def test_rglob_no_follow_symlinks_common(self): + def _check(path, glob, expected): + actual = {path for path in path.rglob(glob, follow_symlinks=False)} + self.assertEqual(actual, { P(BASE, q) for q in expected }) + P = self.cls + p = P(BASE) + _check(p, "fileB", ["dirB/fileB"]) + _check(p, "*/fileA", []) + _check(p, "*/fileB", ["dirB/fileB"]) + _check(p, "file*", ["fileA", "dirB/fileB", "dirC/fileC", "dirC/dirD/fileD", ]) + _check(p, "*/", ["dirA", "dirB", "dirC", "dirC/dirD", "dirE"]) + _check(p, "", ["", "dirA", "dirB", "dirC", "dirE", "dirC/dirD"]) + + p = P(BASE, "dirC") + _check(p, "*", ["dirC/fileC", "dirC/novel.txt", + "dirC/dirD", "dirC/dirD/fileD"]) + _check(p, "file*", ["dirC/fileC", "dirC/dirD/fileD"]) + _check(p, "*/*", ["dirC/dirD/fileD"]) + _check(p, "*/", ["dirC/dirD"]) + _check(p, "", ["dirC", "dirC/dirD"]) + # gh-91616, a re module regression + _check(p, "*.txt", ["dirC/novel.txt"]) + _check(p, "*.*", ["dirC/novel.txt"]) + @os_helper.skip_unless_symlink def test_rglob_symlink_loop(self): # Don't get fooled by symlink loops (Issue #26012). diff --git a/Lib/test/test_peg_generator/__init__.py b/Lib/test/test_peg_generator/__init__.py index 7c402c3d7c5acf..77f72fcc7c6e3b 100644 --- a/Lib/test/test_peg_generator/__init__.py +++ b/Lib/test/test_peg_generator/__init__.py @@ -3,9 +3,6 @@ from test import support from test.support import load_package_tests -# TODO: gh-92584: peg_generator uses distutils which was removed in Python 3.12 -raise unittest.SkipTest("distutils has been removed in Python 3.12") - if support.check_sanitizer(address=True, memory=True): # bpo-46633: Skip the test because it is too slow when Python is built diff --git a/Lib/test/test_peg_generator/test_c_parser.py b/Lib/test/test_peg_generator/test_c_parser.py index d34ffef0dbc5ec..af39faeba94357 100644 --- a/Lib/test/test_peg_generator/test_c_parser.py +++ b/Lib/test/test_peg_generator/test_c_parser.py @@ -1,3 +1,5 @@ +import contextlib +import subprocess import sysconfig import textwrap import unittest @@ -8,7 +10,7 @@ from test import test_tools from test import support -from test.support import os_helper +from test.support import os_helper, import_helper from test.support.script_helper import assert_python_ok _py_cflags_nodist = sysconfig.get_config_var("PY_CFLAGS_NODIST") @@ -88,6 +90,16 @@ def setUpClass(cls): cls.library_dir = tempfile.mkdtemp(dir=cls.tmp_base) cls.addClassCleanup(shutil.rmtree, cls.library_dir) + with contextlib.ExitStack() as stack: + python_exe = stack.enter_context(support.setup_venv_with_pip_setuptools_wheel("venv")) + sitepackages = subprocess.check_output( + [python_exe, "-c", "import sysconfig; print(sysconfig.get_path('platlib'))"], + text=True, + ).strip() + stack.enter_context(import_helper.DirsOnSysPath(sitepackages)) + cls.addClassCleanup(stack.pop_all().close) + + @support.requires_venv_with_pip() def setUp(self): self._backup_config_vars = dict(sysconfig._CONFIG_VARS) cmd = support.missing_compiler_executable() diff --git a/Lib/test/test_peg_generator/test_pegen.py b/Lib/test/test_peg_generator/test_pegen.py index 30e992ed213c67..876bf789f48282 100644 --- a/Lib/test/test_peg_generator/test_pegen.py +++ b/Lib/test/test_peg_generator/test_pegen.py @@ -552,14 +552,14 @@ def test_mutually_left_recursive(self) -> None: string="D", start=(1, 0), end=(1, 1), - line="D A C A E", + line="D A C A E\n", ), TokenInfo( type=NAME, string="A", start=(1, 2), end=(1, 3), - line="D A C A E", + line="D A C A E\n", ), ], TokenInfo( @@ -567,7 +567,7 @@ def test_mutually_left_recursive(self) -> None: string="C", start=(1, 4), end=(1, 5), - line="D A C A E", + line="D A C A E\n", ), ], TokenInfo( @@ -575,11 +575,11 @@ def test_mutually_left_recursive(self) -> None: string="A", start=(1, 6), end=(1, 7), - line="D A C A E", + line="D A C A E\n", ), ], TokenInfo( - type=NAME, string="E", start=(1, 8), end=(1, 9), line="D A C A E" + type=NAME, string="E", start=(1, 8), end=(1, 9), line="D A C A E\n" ), ], ) @@ -594,22 +594,22 @@ def test_mutually_left_recursive(self) -> None: string="B", start=(1, 0), end=(1, 1), - line="B C A E", + line="B C A E\n", ), TokenInfo( type=NAME, string="C", start=(1, 2), end=(1, 3), - line="B C A E", + line="B C A E\n", ), ], TokenInfo( - type=NAME, string="A", start=(1, 4), end=(1, 5), line="B C A E" + type=NAME, string="A", start=(1, 4), end=(1, 5), line="B C A E\n" ), ], TokenInfo( - type=NAME, string="E", start=(1, 6), end=(1, 7), line="B C A E" + type=NAME, string="E", start=(1, 6), end=(1, 7), line="B C A E\n" ), ], ) @@ -650,14 +650,15 @@ def test_lookahead(self) -> None: """ parser_class = make_parser(grammar) node = parse_string("foo = 12 + 12 .", parser_class) + self.maxDiff = None self.assertEqual( node, [ TokenInfo( - NAME, string="foo", start=(1, 0), end=(1, 3), line="foo = 12 + 12 ." + NAME, string="foo", start=(1, 0), end=(1, 3), line="foo = 12 + 12 .\n" ), TokenInfo( - OP, string="=", start=(1, 4), end=(1, 5), line="foo = 12 + 12 ." + OP, string="=", start=(1, 4), end=(1, 5), line="foo = 12 + 12 .\n" ), [ TokenInfo( @@ -665,7 +666,7 @@ def test_lookahead(self) -> None: string="12", start=(1, 6), end=(1, 8), - line="foo = 12 + 12 .", + line="foo = 12 + 12 .\n", ), [ [ @@ -674,14 +675,14 @@ def test_lookahead(self) -> None: string="+", start=(1, 9), end=(1, 10), - line="foo = 12 + 12 .", + line="foo = 12 + 12 .\n", ), TokenInfo( NUMBER, string="12", start=(1, 11), end=(1, 13), - line="foo = 12 + 12 .", + line="foo = 12 + 12 .\n", ), ] ], @@ -733,9 +734,9 @@ def test_cut(self) -> None: self.assertEqual( node, [ - TokenInfo(OP, string="(", start=(1, 0), end=(1, 1), line="(1)"), - TokenInfo(NUMBER, string="1", start=(1, 1), end=(1, 2), line="(1)"), - TokenInfo(OP, string=")", start=(1, 2), end=(1, 3), line="(1)"), + TokenInfo(OP, string="(", start=(1, 0), end=(1, 1), line="(1)\n"), + TokenInfo(NUMBER, string="1", start=(1, 1), end=(1, 2), line="(1)\n"), + TokenInfo(OP, string=")", start=(1, 2), end=(1, 3), line="(1)\n"), ], ) @@ -794,7 +795,7 @@ def test_soft_keyword(self) -> None: start: | "number" n=NUMBER { eval(n.string) } | "string" n=STRING { n.string } - | SOFT_KEYWORD l=NAME n=(NUMBER | NAME | STRING) { f"{l.string} = {n.string}"} + | SOFT_KEYWORD l=NAME n=(NUMBER | NAME | STRING) { l.string + " = " + n.string } """ parser_class = make_parser(grammar) self.assertEqual(parse_string("number 1", parser_class), 1) diff --git a/Lib/test/test_tix.py b/Lib/test/test_tix.py deleted file mode 100644 index d0d2a164ad2c67..00000000000000 --- a/Lib/test/test_tix.py +++ /dev/null @@ -1,40 +0,0 @@ -import sys -import unittest -from test import support -from test.support import import_helper -from test.support import check_sanitizer - -if check_sanitizer(address=True, memory=True): - raise unittest.SkipTest("Tests involving libX11 can SEGFAULT on ASAN/MSAN builds") - - -# Skip this test if the _tkinter module wasn't built. -_tkinter = import_helper.import_module('_tkinter') - -# Skip test if tk cannot be initialized. -support.requires('gui') - -# Suppress the deprecation warning -tix = import_helper.import_module('tkinter.tix', deprecated=True) -from tkinter import TclError - - -class TestTix(unittest.TestCase): - - def setUp(self): - try: - self.root = tix.Tk() - except TclError: - if sys.platform.startswith('win'): - self.fail('Tix should always be available on Windows') - self.skipTest('Tix not available') - else: - self.addCleanup(self.root.destroy) - - def test_tix_available(self): - # this test is just here to make setUp run - pass - - -if __name__ == '__main__': - unittest.main() diff --git a/Lib/test/test_tkinter/test_images.py b/Lib/test/test_tkinter/test_images.py index b6f8b79ae689fa..c07de867ce04b7 100644 --- a/Lib/test/test_tkinter/test_images.py +++ b/Lib/test/test_tkinter/test_images.py @@ -66,7 +66,7 @@ class BitmapImageTest(AbstractTkTest, unittest.TestCase): @classmethod def setUpClass(cls): AbstractTkTest.setUpClass.__func__(cls) - cls.testfile = support.findfile('python.xbm', subdir='imghdrdata') + cls.testfile = support.findfile('python.xbm', subdir='tkinterdata') def test_create_from_file(self): image = tkinter.BitmapImage('::img::test', master=self.root, @@ -150,7 +150,7 @@ class PhotoImageTest(AbstractTkTest, unittest.TestCase): @classmethod def setUpClass(cls): AbstractTkTest.setUpClass.__func__(cls) - cls.testfile = support.findfile('python.gif', subdir='imghdrdata') + cls.testfile = support.findfile('python.gif', subdir='tkinterdata') def create(self): return tkinter.PhotoImage('::img::test', master=self.root, @@ -163,7 +163,7 @@ def colorlist(self, *args): return tkinter._join(args) def check_create_from_file(self, ext): - testfile = support.findfile('python.' + ext, subdir='imghdrdata') + testfile = support.findfile('python.' + ext, subdir='tkinterdata') image = tkinter.PhotoImage('::img::test', master=self.root, file=testfile) self.assertEqual(str(image), '::img::test') @@ -178,7 +178,7 @@ def check_create_from_file(self, ext): self.assertNotIn('::img::test', self.root.image_names()) def check_create_from_data(self, ext): - testfile = support.findfile('python.' + ext, subdir='imghdrdata') + testfile = support.findfile('python.' + ext, subdir='tkinterdata') with open(testfile, 'rb') as f: data = f.read() image = tkinter.PhotoImage('::img::test', master=self.root, diff --git a/Lib/test/test_tkinter/test_widgets.py b/Lib/test/test_tkinter/test_widgets.py index 76cc16e5b977de..34e67c0cbc44a3 100644 --- a/Lib/test/test_tkinter/test_widgets.py +++ b/Lib/test/test_tkinter/test_widgets.py @@ -1408,10 +1408,13 @@ def test_configure_title(self): def test_configure_type(self): widget = self.create() + opts = ('normal, tearoff, or menubar' + if widget.info_patchlevel() < (8, 7) else + 'menubar, normal, or tearoff') self.checkEnumParam( widget, 'type', 'normal', 'tearoff', 'menubar', - errmsg='bad type "{}": must be normal, tearoff, or menubar', + errmsg='bad type "{}": must be ' + opts, ) def test_entryconfigure(self): diff --git a/Lib/test/test_tkinter/widget_tests.py b/Lib/test/test_tkinter/widget_tests.py index 85b0511aba3c7a..f60087a6e9f385 100644 --- a/Lib/test/test_tkinter/widget_tests.py +++ b/Lib/test/test_tkinter/widget_tests.py @@ -250,7 +250,7 @@ def test_configure_bitmap(self): widget = self.create() self.checkParam(widget, 'bitmap', 'questhead') self.checkParam(widget, 'bitmap', 'gray50') - filename = test.support.findfile('python.xbm', subdir='imghdrdata') + filename = test.support.findfile('python.xbm', subdir='tkinterdata') self.checkParam(widget, 'bitmap', '@' + filename) # Cocoa Tk widgets don't detect invalid -bitmap values # See https://core.tcl.tk/tk/info/31cd33dbf0 diff --git a/Lib/test/test_tokenize.py b/Lib/test/test_tokenize.py index 251ce2b864a9d8..cd11dddd0fe51a 100644 --- a/Lib/test/test_tokenize.py +++ b/Lib/test/test_tokenize.py @@ -82,8 +82,34 @@ def test_basic(self): NAME 'False' (4, 11) (4, 16) COMMENT '# NEWLINE' (4, 17) (4, 26) NEWLINE '\\n' (4, 26) (4, 27) - DEDENT '' (4, 27) (4, 27) + DEDENT '' (5, 0) (5, 0) """) + + self.check_tokenize("if True:\r\n # NL\r\n foo='bar'\r\n\r\n", """\ + NAME 'if' (1, 0) (1, 2) + NAME 'True' (1, 3) (1, 7) + OP ':' (1, 7) (1, 8) + NEWLINE '\\r\\n' (1, 8) (1, 10) + COMMENT '# NL' (2, 4) (2, 8) + NL '\\r\\n' (2, 8) (2, 10) + INDENT ' ' (3, 0) (3, 4) + NAME 'foo' (3, 4) (3, 7) + OP '=' (3, 7) (3, 8) + STRING "\'bar\'" (3, 8) (3, 13) + NEWLINE '\\r\\n' (3, 13) (3, 15) + NL '\\r\\n' (4, 0) (4, 2) + DEDENT '' (5, 0) (5, 0) + """) + + self.check_tokenize("x = 1 + \\\r\n1\r\n", """\ + NAME 'x' (1, 0) (1, 1) + OP '=' (1, 2) (1, 3) + NUMBER '1' (1, 4) (1, 5) + OP '+' (1, 6) (1, 7) + NUMBER '1' (2, 0) (2, 1) + NEWLINE '\\r\\n' (2, 1) (2, 3) + """) + indent_error_file = b"""\ def k(x): x += 2 @@ -755,8 +781,8 @@ def test_tabs(self): NEWLINE '\\n' (2, 5) (2, 6) INDENT ' \\t' (3, 0) (3, 9) NAME 'pass' (3, 9) (3, 13) - DEDENT '' (3, 14) (3, 14) - DEDENT '' (3, 14) (3, 14) + DEDENT '' (4, 0) (4, 0) + DEDENT '' (4, 0) (4, 0) """) def test_non_ascii_identifiers(self): @@ -968,7 +994,7 @@ async def foo(): NUMBER '1' (2, 17) (2, 18) OP ':' (2, 18) (2, 19) NAME 'pass' (2, 20) (2, 24) - DEDENT '' (2, 25) (2, 25) + DEDENT '' (3, 0) (3, 0) """) self.check_tokenize('''async def foo(async): await''', """\ @@ -1016,7 +1042,7 @@ async def bar(): pass NAME 'await' (6, 2) (6, 7) OP '=' (6, 8) (6, 9) NUMBER '2' (6, 10) (6, 11) - DEDENT '' (6, 12) (6, 12) + DEDENT '' (7, 0) (7, 0) """) self.check_tokenize('''\ @@ -1054,7 +1080,7 @@ async def bar(): pass NAME 'await' (6, 2) (6, 7) OP '=' (6, 8) (6, 9) NUMBER '2' (6, 10) (6, 11) - DEDENT '' (6, 12) (6, 12) + DEDENT '' (7, 0) (7, 0) """) def test_newline_after_parenthesized_block_with_comment(self): @@ -1174,7 +1200,7 @@ def readline(): # skip the initial encoding token and the end tokens tokens = list(_tokenize(readline(), encoding='utf-8'))[:-2] - expected_tokens = [TokenInfo(3, '"ЉЊЈЁЂ"', (1, 0), (1, 7), '"ЉЊЈЁЂ"')] + expected_tokens = [TokenInfo(3, '"ЉЊЈЁЂ"', (1, 0), (1, 7), '"ЉЊЈЁЂ"\n')] self.assertEqual(tokens, expected_tokens, "bytes not decoded with encoding") @@ -1657,7 +1683,6 @@ def check_roundtrip(self, f): code = f.encode('utf-8') else: code = f.read() - f.close() readline = iter(code.splitlines(keepends=True)).__next__ tokens5 = list(tokenize(readline)) tokens2 = [tok[:2] for tok in tokens5] @@ -1672,6 +1697,17 @@ def check_roundtrip(self, f): tokens2_from5 = [tok[:2] for tok in tokenize(readline5)] self.assertEqual(tokens2_from5, tokens2) + def check_line_extraction(self, f): + if isinstance(f, str): + code = f.encode('utf-8') + else: + code = f.read() + readline = iter(code.splitlines(keepends=True)).__next__ + for tok in tokenize(readline): + if tok.type in {ENCODING, ENDMARKER}: + continue + self.assertEqual(tok.string, tok.line[tok.start[1]: tok.end[1]]) + def test_roundtrip(self): # There are some standard formatting practices that are easy to get right. @@ -1766,8 +1802,9 @@ def test_random_files(self): if support.verbose >= 2: print('tokenize', testfile) with open(testfile, 'rb') as f: - # with self.subTest(file=testfile): - self.check_roundtrip(f) + with self.subTest(file=testfile): + self.check_roundtrip(f) + self.check_line_extraction(f) def roundtrip(self, code): @@ -2065,6 +2102,10 @@ def test_string(self): b\ c"""', """\ STRING 'rb"\""a\\\\\\nb\\\\\\nc"\""' (1, 0) (3, 4) + """) + + self.check_tokenize(r'"hola\\\r\ndfgf"', """\ + STRING \'"hola\\\\\\\\\\\\r\\\\ndfgf"\' (1, 0) (1, 16) """) self.check_tokenize('f"abc"', """\ @@ -2101,6 +2142,12 @@ def test_string(self): FSTRING_START 'Rf"' (1, 0) (1, 3) FSTRING_MIDDLE 'abc\\\\\\ndef' (1, 3) (2, 3) FSTRING_END '"' (2, 3) (2, 4) + """) + + self.check_tokenize(r'f"hola\\\r\ndfgf"', """\ + FSTRING_START \'f"\' (1, 0) (1, 2) + FSTRING_MIDDLE 'hola\\\\\\\\\\\\r\\\\ndfgf' (1, 2) (1, 16) + FSTRING_END \'"\' (1, 16) (1, 17) """) def test_function(self): @@ -2669,7 +2716,8 @@ def generate_source(indents): valid = generate_source(MAXINDENT - 1) tokens = list(_generate_tokens_from_c_tokenizer(valid)) - self.assertEqual(tokens[-1].type, DEDENT) + self.assertEqual(tokens[-2].type, DEDENT) + self.assertEqual(tokens[-1].type, ENDMARKER) compile(valid, "", "exec") invalid = generate_source(MAXINDENT) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 57a29269bb9566..27bce76395b99e 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -6474,7 +6474,7 @@ def __len__(self): return 0 self.assertEqual(len(MMC()), 0) - assert callable(MMC.update) + self.assertTrue(callable(MMC.update)) self.assertIsInstance(MMC(), typing.Mapping) class MMB(typing.MutableMapping[KT, VT]): @@ -6669,8 +6669,8 @@ def foo(a: A) -> Optional[BaseException]: else: return a() - assert isinstance(foo(KeyboardInterrupt), KeyboardInterrupt) - assert foo(None) is None + self.assertIsInstance(foo(KeyboardInterrupt), KeyboardInterrupt) + self.assertIsNone(foo(None)) class TestModules(TestCase): @@ -7017,6 +7017,10 @@ def test_basics_functional_syntax(self): self.assertEqual(Emp.__bases__, (dict,)) self.assertEqual(Emp.__annotations__, {'name': str, 'id': int}) self.assertEqual(Emp.__total__, True) + self.assertEqual(Emp.__required_keys__, {'name', 'id'}) + self.assertIsInstance(Emp.__required_keys__, frozenset) + self.assertEqual(Emp.__optional_keys__, set()) + self.assertIsInstance(Emp.__optional_keys__, frozenset) def test_typeddict_create_errors(self): with self.assertRaises(TypeError): @@ -7092,7 +7096,9 @@ def test_total(self): self.assertEqual(D(x=1), {'x': 1}) self.assertEqual(D.__total__, False) self.assertEqual(D.__required_keys__, frozenset()) + self.assertIsInstance(D.__required_keys__, frozenset) self.assertEqual(D.__optional_keys__, {'x'}) + self.assertIsInstance(D.__optional_keys__, frozenset) self.assertEqual(Options(), {}) self.assertEqual(Options(log_level=2), {'log_level': 2}) @@ -7104,8 +7110,10 @@ def test_optional_keys(self): class Point2Dor3D(Point2D, total=False): z: int - assert Point2Dor3D.__required_keys__ == frozenset(['x', 'y']) - assert Point2Dor3D.__optional_keys__ == frozenset(['z']) + self.assertEqual(Point2Dor3D.__required_keys__, frozenset(['x', 'y'])) + self.assertIsInstance(Point2Dor3D.__required_keys__, frozenset) + self.assertEqual(Point2Dor3D.__optional_keys__, frozenset(['z'])) + self.assertIsInstance(Point2Dor3D.__optional_keys__, frozenset) def test_keys_inheritance(self): class BaseAnimal(TypedDict): @@ -7118,26 +7126,26 @@ class Animal(BaseAnimal, total=False): class Cat(Animal): fur_color: str - assert BaseAnimal.__required_keys__ == frozenset(['name']) - assert BaseAnimal.__optional_keys__ == frozenset([]) - assert BaseAnimal.__annotations__ == {'name': str} + self.assertEqual(BaseAnimal.__required_keys__, frozenset(['name'])) + self.assertEqual(BaseAnimal.__optional_keys__, frozenset([])) + self.assertEqual(BaseAnimal.__annotations__, {'name': str}) - assert Animal.__required_keys__ == frozenset(['name']) - assert Animal.__optional_keys__ == frozenset(['tail', 'voice']) - assert Animal.__annotations__ == { + self.assertEqual(Animal.__required_keys__, frozenset(['name'])) + self.assertEqual(Animal.__optional_keys__, frozenset(['tail', 'voice'])) + self.assertEqual(Animal.__annotations__, { 'name': str, 'tail': bool, 'voice': str, - } + }) - assert Cat.__required_keys__ == frozenset(['name', 'fur_color']) - assert Cat.__optional_keys__ == frozenset(['tail', 'voice']) - assert Cat.__annotations__ == { + self.assertEqual(Cat.__required_keys__, frozenset(['name', 'fur_color'])) + self.assertEqual(Cat.__optional_keys__, frozenset(['tail', 'voice'])) + self.assertEqual(Cat.__annotations__, { 'fur_color': str, 'name': str, 'tail': bool, 'voice': str, - } + }) def test_required_notrequired_keys(self): self.assertEqual(NontotalMovie.__required_keys__, @@ -7367,11 +7375,11 @@ class C(B[int]): self.assertEqual(C.__total__, True) self.assertEqual(C.__optional_keys__, frozenset(['b'])) self.assertEqual(C.__required_keys__, frozenset(['a', 'c'])) - assert C.__annotations__ == { + self.assertEqual(C.__annotations__, { 'a': T, 'b': KT, 'c': int, - } + }) with self.assertRaises(TypeError): C[str] @@ -7386,11 +7394,11 @@ class Point3D(Point2DGeneric[T], Generic[T, KT]): self.assertEqual(Point3D.__total__, True) self.assertEqual(Point3D.__optional_keys__, frozenset()) self.assertEqual(Point3D.__required_keys__, frozenset(['a', 'b', 'c'])) - assert Point3D.__annotations__ == { + self.assertEqual(Point3D.__annotations__, { 'a': T, 'b': T, 'c': KT, - } + }) self.assertEqual(Point3D[int, str].__origin__, Point3D) with self.assertRaises(TypeError): @@ -7417,11 +7425,11 @@ class WithImplicitAny(B): self.assertEqual(WithImplicitAny.__total__, True) self.assertEqual(WithImplicitAny.__optional_keys__, frozenset(['b'])) self.assertEqual(WithImplicitAny.__required_keys__, frozenset(['a', 'c'])) - assert WithImplicitAny.__annotations__ == { + self.assertEqual(WithImplicitAny.__annotations__, { 'a': T, 'b': KT, 'c': int, - } + }) with self.assertRaises(TypeError): WithImplicitAny[str] diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py index 95944c7c711620..5205604c2c7185 100644 --- a/Lib/test/test_venv.py +++ b/Lib/test/test_venv.py @@ -600,15 +600,14 @@ def test_zippath_from_non_installed_posix(self): ld_library_path_env = "DYLD_LIBRARY_PATH" else: ld_library_path_env = "LD_LIBRARY_PATH" - # Note that in address sanitizer mode, the current runtime - # implementation leaks memory due to not being able to correctly - # clean all unicode objects during runtime shutdown. Therefore, - # this uses subprocess.run instead of subprocess.check_call to - # maintain the core of the test while not failing due to the refleaks. - # This should be able to use check_call once all refleaks are fixed. - subprocess.run(cmd, - env={"PYTHONPATH": pythonpath, - ld_library_path_env: ld_library_path}) + child_env = { + "PYTHONPATH": pythonpath, + ld_library_path_env: ld_library_path, + } + if asan_options := os.environ.get("ASAN_OPTIONS"): + # prevent https://github.com/python/cpython/issues/104839 + child_env["ASAN_OPTIONS"] = asan_options + subprocess.check_call(cmd, env=child_env) envpy = os.path.join(self.env_dir, self.bindir, self.exe) # Now check the venv created from the non-installed python has # correct zip path in pythonpath. diff --git a/Lib/test/imghdrdata/python.gif b/Lib/test/tkinterdata/python.gif similarity index 100% rename from Lib/test/imghdrdata/python.gif rename to Lib/test/tkinterdata/python.gif diff --git a/Lib/test/imghdrdata/python.pgm b/Lib/test/tkinterdata/python.pgm similarity index 100% rename from Lib/test/imghdrdata/python.pgm rename to Lib/test/tkinterdata/python.pgm diff --git a/Lib/test/imghdrdata/python.png b/Lib/test/tkinterdata/python.png similarity index 100% rename from Lib/test/imghdrdata/python.png rename to Lib/test/tkinterdata/python.png diff --git a/Lib/test/imghdrdata/python.ppm b/Lib/test/tkinterdata/python.ppm similarity index 100% rename from Lib/test/imghdrdata/python.ppm rename to Lib/test/tkinterdata/python.ppm diff --git a/Lib/test/imghdrdata/python.xbm b/Lib/test/tkinterdata/python.xbm similarity index 100% rename from Lib/test/imghdrdata/python.xbm rename to Lib/test/tkinterdata/python.xbm diff --git a/Lib/tkinter/tix.py b/Lib/tkinter/tix.py deleted file mode 100644 index ce218265d4a824..00000000000000 --- a/Lib/tkinter/tix.py +++ /dev/null @@ -1,1948 +0,0 @@ -# Tix.py -- Tix widget wrappers. -# -# For Tix, see http://tix.sourceforge.net -# -# - Sudhir Shenoy (sshenoy@gol.com), Dec. 1995. -# based on an idea of Jean-Marc Lugrin (lugrin@ms.com) -# -# NOTE: In order to minimize changes to Tkinter.py, some of the code here -# (TixWidget.__init__) has been taken from Tkinter (Widget.__init__) -# and will break if there are major changes in Tkinter. -# -# The Tix widgets are represented by a class hierarchy in python with proper -# inheritance of base classes. -# -# As a result after creating a 'w = StdButtonBox', I can write -# w.ok['text'] = 'Who Cares' -# or w.ok['bg'] = w['bg'] -# or even w.ok.invoke() -# etc. -# -# Compare the demo tixwidgets.py to the original Tcl program and you will -# appreciate the advantages. -# -# NOTE: This module is deprecated since Python 3.6. - -import os -import warnings -import tkinter -from tkinter import * -from tkinter import _cnfmerge - -warnings.warn( - 'The Tix Tk extension is unmaintained, and the tkinter.tix wrapper module' - ' is deprecated in favor of tkinter.ttk', - DeprecationWarning, - stacklevel=2, - ) - -# Some more constants (for consistency with Tkinter) -WINDOW = 'window' -TEXT = 'text' -STATUS = 'status' -IMMEDIATE = 'immediate' -IMAGE = 'image' -IMAGETEXT = 'imagetext' -BALLOON = 'balloon' -AUTO = 'auto' -ACROSSTOP = 'acrosstop' - -# A few useful constants for the Grid widget -ASCII = 'ascii' -CELL = 'cell' -COLUMN = 'column' -DECREASING = 'decreasing' -INCREASING = 'increasing' -INTEGER = 'integer' -MAIN = 'main' -MAX = 'max' -REAL = 'real' -ROW = 'row' -S_REGION = 's-region' -X_REGION = 'x-region' -Y_REGION = 'y-region' - -# Some constants used by Tkinter dooneevent() -TCL_DONT_WAIT = 1 << 1 -TCL_WINDOW_EVENTS = 1 << 2 -TCL_FILE_EVENTS = 1 << 3 -TCL_TIMER_EVENTS = 1 << 4 -TCL_IDLE_EVENTS = 1 << 5 -TCL_ALL_EVENTS = 0 - -# BEWARE - this is implemented by copying some code from the Widget class -# in Tkinter (to override Widget initialization) and is therefore -# liable to break. - -# Could probably add this to Tkinter.Misc -class tixCommand: - """The tix commands provide access to miscellaneous elements - of Tix's internal state and the Tix application context. - Most of the information manipulated by these commands pertains - to the application as a whole, or to a screen or - display, rather than to a particular window. - - This is a mixin class, assumed to be mixed to Tkinter.Tk - that supports the self.tk.call method. - """ - - def tix_addbitmapdir(self, directory): - """Tix maintains a list of directories under which - the tix_getimage and tix_getbitmap commands will - search for image files. The standard bitmap directory - is $TIX_LIBRARY/bitmaps. The addbitmapdir command - adds directory into this list. By using this - command, the image files of an applications can - also be located using the tix_getimage or tix_getbitmap - command. - """ - return self.tk.call('tix', 'addbitmapdir', directory) - - def tix_cget(self, option): - """Returns the current value of the configuration - option given by option. Option may be any of the - options described in the CONFIGURATION OPTIONS section. - """ - return self.tk.call('tix', 'cget', option) - - def tix_configure(self, cnf=None, **kw): - """Query or modify the configuration options of the Tix application - context. If no option is specified, returns a dictionary all of the - available options. If option is specified with no value, then the - command returns a list describing the one named option (this list - will be identical to the corresponding sublist of the value - returned if no option is specified). If one or more option-value - pairs are specified, then the command modifies the given option(s) - to have the given value(s); in this case the command returns an - empty string. Option may be any of the configuration options. - """ - # Copied from Tkinter.py - if kw: - cnf = _cnfmerge((cnf, kw)) - elif cnf: - cnf = _cnfmerge(cnf) - if cnf is None: - return self._getconfigure('tix', 'configure') - if isinstance(cnf, str): - return self._getconfigure1('tix', 'configure', '-'+cnf) - return self.tk.call(('tix', 'configure') + self._options(cnf)) - - def tix_filedialog(self, dlgclass=None): - """Returns the file selection dialog that may be shared among - different calls from this application. This command will create a - file selection dialog widget when it is called the first time. This - dialog will be returned by all subsequent calls to tix_filedialog. - An optional dlgclass parameter can be passed to specified what type - of file selection dialog widget is desired. Possible options are - tix FileSelectDialog or tixExFileSelectDialog. - """ - if dlgclass is not None: - return self.tk.call('tix', 'filedialog', dlgclass) - else: - return self.tk.call('tix', 'filedialog') - - def tix_getbitmap(self, name): - """Locates a bitmap file of the name name.xpm or name in one of the - bitmap directories (see the tix_addbitmapdir command above). By - using tix_getbitmap, you can avoid hard coding the pathnames of the - bitmap files in your application. When successful, it returns the - complete pathname of the bitmap file, prefixed with the character - '@'. The returned value can be used to configure the -bitmap - option of the TK and Tix widgets. - """ - return self.tk.call('tix', 'getbitmap', name) - - def tix_getimage(self, name): - """Locates an image file of the name name.xpm, name.xbm or name.ppm - in one of the bitmap directories (see the addbitmapdir command - above). If more than one file with the same name (but different - extensions) exist, then the image type is chosen according to the - depth of the X display: xbm images are chosen on monochrome - displays and color images are chosen on color displays. By using - tix_ getimage, you can avoid hard coding the pathnames of the - image files in your application. When successful, this command - returns the name of the newly created image, which can be used to - configure the -image option of the Tk and Tix widgets. - """ - return self.tk.call('tix', 'getimage', name) - - def tix_option_get(self, name): - """Gets the options maintained by the Tix - scheme mechanism. Available options include: - - active_bg active_fg bg - bold_font dark1_bg dark1_fg - dark2_bg dark2_fg disabled_fg - fg fixed_font font - inactive_bg inactive_fg input1_bg - input2_bg italic_font light1_bg - light1_fg light2_bg light2_fg - menu_font output1_bg output2_bg - select_bg select_fg selector - """ - # could use self.tk.globalgetvar('tixOption', name) - return self.tk.call('tix', 'option', 'get', name) - - def tix_resetoptions(self, newScheme, newFontSet, newScmPrio=None): - """Resets the scheme and fontset of the Tix application to - newScheme and newFontSet, respectively. This affects only those - widgets created after this call. Therefore, it is best to call the - resetoptions command before the creation of any widgets in a Tix - application. - - The optional parameter newScmPrio can be given to reset the - priority level of the Tk options set by the Tix schemes. - - Because of the way Tk handles the X option database, after Tix has - been has imported and inited, it is not possible to reset the color - schemes and font sets using the tix config command. Instead, the - tix_resetoptions command must be used. - """ - if newScmPrio is not None: - return self.tk.call('tix', 'resetoptions', newScheme, newFontSet, newScmPrio) - else: - return self.tk.call('tix', 'resetoptions', newScheme, newFontSet) - -class Tk(tkinter.Tk, tixCommand): - """Toplevel widget of Tix which represents mostly the main window - of an application. It has an associated Tcl interpreter.""" - def __init__(self, screenName=None, baseName=None, className='Tix'): - tkinter.Tk.__init__(self, screenName, baseName, className) - tixlib = os.environ.get('TIX_LIBRARY') - self.tk.eval('global auto_path; lappend auto_path [file dir [info nameof]]') - if tixlib is not None: - self.tk.eval('global auto_path; lappend auto_path {%s}' % tixlib) - self.tk.eval('global tcl_pkgPath; lappend tcl_pkgPath {%s}' % tixlib) - # Load Tix - this should work dynamically or statically - # If it's static, tcl/tix8.1/pkgIndex.tcl should have - # 'load {} Tix' - # If it's dynamic under Unix, tcl/tix8.1/pkgIndex.tcl should have - # 'load libtix8.1.8.3.so Tix' - self.tk.eval('package require Tix') - - def destroy(self): - # For safety, remove the delete_window binding before destroy - self.protocol("WM_DELETE_WINDOW", "") - tkinter.Tk.destroy(self) - -# The Tix 'tixForm' geometry manager -class Form: - """The Tix Form geometry manager - - Widgets can be arranged by specifying attachments to other widgets. - See Tix documentation for complete details""" - - def config(self, cnf={}, **kw): - self.tk.call('tixForm', self._w, *self._options(cnf, kw)) - - form = config - - def __setitem__(self, key, value): - Form.form(self, {key: value}) - - def check(self): - return self.tk.call('tixForm', 'check', self._w) - - def forget(self): - self.tk.call('tixForm', 'forget', self._w) - - def grid(self, xsize=0, ysize=0): - if (not xsize) and (not ysize): - x = self.tk.call('tixForm', 'grid', self._w) - y = self.tk.splitlist(x) - z = () - for x in y: - z = z + (self.tk.getint(x),) - return z - return self.tk.call('tixForm', 'grid', self._w, xsize, ysize) - - def info(self, option=None): - if not option: - return self.tk.call('tixForm', 'info', self._w) - if option[0] != '-': - option = '-' + option - return self.tk.call('tixForm', 'info', self._w, option) - - def slaves(self): - return [self._nametowidget(x) for x in - self.tk.splitlist( - self.tk.call( - 'tixForm', 'slaves', self._w))] - - - -tkinter.Widget.__bases__ = tkinter.Widget.__bases__ + (Form,) - -class TixWidget(tkinter.Widget): - """A TixWidget class is used to package all (or most) Tix widgets. - - Widget initialization is extended in two ways: - 1) It is possible to give a list of options which must be part of - the creation command (so called Tix 'static' options). These cannot be - given as a 'config' command later. - 2) It is possible to give the name of an existing TK widget. These are - child widgets created automatically by a Tix mega-widget. The Tk call - to create these widgets is therefore bypassed in TixWidget.__init__ - - Both options are for use by subclasses only. - """ - def __init__ (self, master=None, widgetName=None, - static_options=None, cnf={}, kw={}): - # Merge keywords and dictionary arguments - if kw: - cnf = _cnfmerge((cnf, kw)) - else: - cnf = _cnfmerge(cnf) - - # Move static options into extra. static_options must be - # a list of keywords (or None). - extra=() - - # 'options' is always a static option - if static_options: - static_options.append('options') - else: - static_options = ['options'] - - for k,v in list(cnf.items()): - if k in static_options: - extra = extra + ('-' + k, v) - del cnf[k] - - self.widgetName = widgetName - self._setup(master, cnf) - - # If widgetName is None, this is a dummy creation call where the - # corresponding Tk widget has already been created by Tix - if widgetName: - self.tk.call(widgetName, self._w, *extra) - - # Non-static options - to be done via a 'config' command - if cnf: - Widget.config(self, cnf) - - # Dictionary to hold subwidget names for easier access. We can't - # use the children list because the public Tix names may not be the - # same as the pathname component - self.subwidget_list = {} - - # We set up an attribute access function so that it is possible to - # do w.ok['text'] = 'Hello' rather than w.subwidget('ok')['text'] = 'Hello' - # when w is a StdButtonBox. - # We can even do w.ok.invoke() because w.ok is subclassed from the - # Button class if you go through the proper constructors - def __getattr__(self, name): - if name in self.subwidget_list: - return self.subwidget_list[name] - raise AttributeError(name) - - def set_silent(self, value): - """Set a variable without calling its action routine""" - self.tk.call('tixSetSilent', self._w, value) - - def subwidget(self, name): - """Return the named subwidget (which must have been created by - the sub-class).""" - n = self._subwidget_name(name) - if not n: - raise TclError("Subwidget " + name + " not child of " + self._name) - # Remove header of name and leading dot - n = n[len(self._w)+1:] - return self._nametowidget(n) - - def subwidgets_all(self): - """Return all subwidgets.""" - names = self._subwidget_names() - if not names: - return [] - retlist = [] - for name in names: - name = name[len(self._w)+1:] - try: - retlist.append(self._nametowidget(name)) - except: - # some of the widgets are unknown e.g. border in LabelFrame - pass - return retlist - - def _subwidget_name(self,name): - """Get a subwidget name (returns a String, not a Widget !)""" - try: - return self.tk.call(self._w, 'subwidget', name) - except TclError: - return None - - def _subwidget_names(self): - """Return the name of all subwidgets.""" - try: - x = self.tk.call(self._w, 'subwidgets', '-all') - return self.tk.splitlist(x) - except TclError: - return None - - def config_all(self, option, value): - """Set configuration options for all subwidgets (and self).""" - if option == '': - return - elif not isinstance(option, str): - option = repr(option) - if not isinstance(value, str): - value = repr(value) - names = self._subwidget_names() - for name in names: - self.tk.call(name, 'configure', '-' + option, value) - # These are missing from Tkinter - def image_create(self, imgtype, cnf={}, master=None, **kw): - if master is None: - master = self - if kw and cnf: cnf = _cnfmerge((cnf, kw)) - elif kw: cnf = kw - options = () - for k, v in cnf.items(): - if callable(v): - v = self._register(v) - options = options + ('-'+k, v) - return master.tk.call(('image', 'create', imgtype,) + options) - def image_delete(self, imgname): - try: - self.tk.call('image', 'delete', imgname) - except TclError: - # May happen if the root was destroyed - pass - -# Subwidgets are child widgets created automatically by mega-widgets. -# In python, we have to create these subwidgets manually to mirror their -# existence in Tk/Tix. -class TixSubWidget(TixWidget): - """Subwidget class. - - This is used to mirror child widgets automatically created - by Tix/Tk as part of a mega-widget in Python (which is not informed - of this)""" - - def __init__(self, master, name, - destroy_physically=1, check_intermediate=1): - if check_intermediate: - path = master._subwidget_name(name) - try: - path = path[len(master._w)+1:] - plist = path.split('.') - except: - plist = [] - - if not check_intermediate: - # immediate descendant - TixWidget.__init__(self, master, None, None, {'name' : name}) - else: - # Ensure that the intermediate widgets exist - parent = master - for i in range(len(plist) - 1): - n = '.'.join(plist[:i+1]) - try: - w = master._nametowidget(n) - parent = w - except KeyError: - # Create the intermediate widget - parent = TixSubWidget(parent, plist[i], - destroy_physically=0, - check_intermediate=0) - # The Tk widget name is in plist, not in name - if plist: - name = plist[-1] - TixWidget.__init__(self, parent, None, None, {'name' : name}) - self.destroy_physically = destroy_physically - - def destroy(self): - # For some widgets e.g., a NoteBook, when we call destructors, - # we must be careful not to destroy the frame widget since this - # also destroys the parent NoteBook thus leading to an exception - # in Tkinter when it finally calls Tcl to destroy the NoteBook - for c in list(self.children.values()): c.destroy() - if self._name in self.master.children: - del self.master.children[self._name] - if self._name in self.master.subwidget_list: - del self.master.subwidget_list[self._name] - if self.destroy_physically: - # This is bypassed only for a few widgets - self.tk.call('destroy', self._w) - - -# Useful class to create a display style - later shared by many items. -# Contributed by Steffen Kremser -class DisplayStyle: - """DisplayStyle - handle configuration options shared by - (multiple) Display Items""" - - def __init__(self, itemtype, cnf={}, *, master=None, **kw): - if master is None: - if 'refwindow' in kw: - master = kw['refwindow'] - elif 'refwindow' in cnf: - master = cnf['refwindow'] - else: - master = tkinter._get_default_root('create display style') - self.tk = master.tk - self.stylename = self.tk.call('tixDisplayStyle', itemtype, - *self._options(cnf,kw) ) - - def __str__(self): - return self.stylename - - def _options(self, cnf, kw): - if kw and cnf: - cnf = _cnfmerge((cnf, kw)) - elif kw: - cnf = kw - opts = () - for k, v in cnf.items(): - opts = opts + ('-'+k, v) - return opts - - def delete(self): - self.tk.call(self.stylename, 'delete') - - def __setitem__(self,key,value): - self.tk.call(self.stylename, 'configure', '-%s'%key, value) - - def config(self, cnf={}, **kw): - return self._getconfigure( - self.stylename, 'configure', *self._options(cnf,kw)) - - def __getitem__(self,key): - return self.tk.call(self.stylename, 'cget', '-%s'%key) - - -###################################################### -### The Tix Widget classes - in alphabetical order ### -###################################################### - -class Balloon(TixWidget): - """Balloon help widget. - - Subwidget Class - --------- ----- - label Label - message Message""" - - # FIXME: It should inherit -superclass tixShell - def __init__(self, master=None, cnf={}, **kw): - # static seem to be -installcolormap -initwait -statusbar -cursor - static = ['options', 'installcolormap', 'initwait', 'statusbar', - 'cursor'] - TixWidget.__init__(self, master, 'tixBalloon', static, cnf, kw) - self.subwidget_list['label'] = _dummyLabel(self, 'label', - destroy_physically=0) - self.subwidget_list['message'] = _dummyLabel(self, 'message', - destroy_physically=0) - - def bind_widget(self, widget, cnf={}, **kw): - """Bind balloon widget to another. - One balloon widget may be bound to several widgets at the same time""" - self.tk.call(self._w, 'bind', widget._w, *self._options(cnf, kw)) - - def unbind_widget(self, widget): - self.tk.call(self._w, 'unbind', widget._w) - -class ButtonBox(TixWidget): - """ButtonBox - A container for pushbuttons. - Subwidgets are the buttons added with the add method. - """ - def __init__(self, master=None, cnf={}, **kw): - TixWidget.__init__(self, master, 'tixButtonBox', - ['orientation', 'options'], cnf, kw) - - def add(self, name, cnf={}, **kw): - """Add a button with given name to box.""" - - btn = self.tk.call(self._w, 'add', name, *self._options(cnf, kw)) - self.subwidget_list[name] = _dummyButton(self, name) - return btn - - def invoke(self, name): - if name in self.subwidget_list: - self.tk.call(self._w, 'invoke', name) - -class ComboBox(TixWidget): - """ComboBox - an Entry field with a dropdown menu. The user can select a - choice by either typing in the entry subwidget or selecting from the - listbox subwidget. - - Subwidget Class - --------- ----- - entry Entry - arrow Button - slistbox ScrolledListBox - tick Button - cross Button : present if created with the fancy option""" - - # FIXME: It should inherit -superclass tixLabelWidget - def __init__ (self, master=None, cnf={}, **kw): - TixWidget.__init__(self, master, 'tixComboBox', - ['editable', 'dropdown', 'fancy', 'options'], - cnf, kw) - self.subwidget_list['label'] = _dummyLabel(self, 'label') - self.subwidget_list['entry'] = _dummyEntry(self, 'entry') - self.subwidget_list['arrow'] = _dummyButton(self, 'arrow') - self.subwidget_list['slistbox'] = _dummyScrolledListBox(self, - 'slistbox') - try: - self.subwidget_list['tick'] = _dummyButton(self, 'tick') - self.subwidget_list['cross'] = _dummyButton(self, 'cross') - except TypeError: - # unavailable when -fancy not specified - pass - - # align - - def add_history(self, str): - self.tk.call(self._w, 'addhistory', str) - - def append_history(self, str): - self.tk.call(self._w, 'appendhistory', str) - - def insert(self, index, str): - self.tk.call(self._w, 'insert', index, str) - - def pick(self, index): - self.tk.call(self._w, 'pick', index) - -class Control(TixWidget): - """Control - An entry field with value change arrows. The user can - adjust the value by pressing the two arrow buttons or by entering - the value directly into the entry. The new value will be checked - against the user-defined upper and lower limits. - - Subwidget Class - --------- ----- - incr Button - decr Button - entry Entry - label Label""" - - # FIXME: It should inherit -superclass tixLabelWidget - def __init__ (self, master=None, cnf={}, **kw): - TixWidget.__init__(self, master, 'tixControl', ['options'], cnf, kw) - self.subwidget_list['incr'] = _dummyButton(self, 'incr') - self.subwidget_list['decr'] = _dummyButton(self, 'decr') - self.subwidget_list['label'] = _dummyLabel(self, 'label') - self.subwidget_list['entry'] = _dummyEntry(self, 'entry') - - def decrement(self): - self.tk.call(self._w, 'decr') - - def increment(self): - self.tk.call(self._w, 'incr') - - def invoke(self): - self.tk.call(self._w, 'invoke') - - def update(self): - self.tk.call(self._w, 'update') - -class DirList(TixWidget): - """DirList - displays a list view of a directory, its previous - directories and its sub-directories. The user can choose one of - the directories displayed in the list or change to another directory. - - Subwidget Class - --------- ----- - hlist HList - hsb Scrollbar - vsb Scrollbar""" - - # FIXME: It should inherit -superclass tixScrolledHList - def __init__(self, master, cnf={}, **kw): - TixWidget.__init__(self, master, 'tixDirList', ['options'], cnf, kw) - self.subwidget_list['hlist'] = _dummyHList(self, 'hlist') - self.subwidget_list['vsb'] = _dummyScrollbar(self, 'vsb') - self.subwidget_list['hsb'] = _dummyScrollbar(self, 'hsb') - - def chdir(self, dir): - self.tk.call(self._w, 'chdir', dir) - -class DirTree(TixWidget): - """DirTree - Directory Listing in a hierarchical view. - Displays a tree view of a directory, its previous directories and its - sub-directories. The user can choose one of the directories displayed - in the list or change to another directory. - - Subwidget Class - --------- ----- - hlist HList - hsb Scrollbar - vsb Scrollbar""" - - # FIXME: It should inherit -superclass tixScrolledHList - def __init__(self, master, cnf={}, **kw): - TixWidget.__init__(self, master, 'tixDirTree', ['options'], cnf, kw) - self.subwidget_list['hlist'] = _dummyHList(self, 'hlist') - self.subwidget_list['vsb'] = _dummyScrollbar(self, 'vsb') - self.subwidget_list['hsb'] = _dummyScrollbar(self, 'hsb') - - def chdir(self, dir): - self.tk.call(self._w, 'chdir', dir) - -class DirSelectBox(TixWidget): - """DirSelectBox - Motif style file select box. - It is generally used for - the user to choose a file. FileSelectBox stores the files mostly - recently selected into a ComboBox widget so that they can be quickly - selected again. - - Subwidget Class - --------- ----- - selection ComboBox - filter ComboBox - dirlist ScrolledListBox - filelist ScrolledListBox""" - - def __init__(self, master, cnf={}, **kw): - TixWidget.__init__(self, master, 'tixDirSelectBox', ['options'], cnf, kw) - self.subwidget_list['dirlist'] = _dummyDirList(self, 'dirlist') - self.subwidget_list['dircbx'] = _dummyFileComboBox(self, 'dircbx') - -class ExFileSelectBox(TixWidget): - """ExFileSelectBox - MS Windows style file select box. - It provides a convenient method for the user to select files. - - Subwidget Class - --------- ----- - cancel Button - ok Button - hidden Checkbutton - types ComboBox - dir ComboBox - file ComboBox - dirlist ScrolledListBox - filelist ScrolledListBox""" - - def __init__(self, master, cnf={}, **kw): - TixWidget.__init__(self, master, 'tixExFileSelectBox', ['options'], cnf, kw) - self.subwidget_list['cancel'] = _dummyButton(self, 'cancel') - self.subwidget_list['ok'] = _dummyButton(self, 'ok') - self.subwidget_list['hidden'] = _dummyCheckbutton(self, 'hidden') - self.subwidget_list['types'] = _dummyComboBox(self, 'types') - self.subwidget_list['dir'] = _dummyComboBox(self, 'dir') - self.subwidget_list['dirlist'] = _dummyDirList(self, 'dirlist') - self.subwidget_list['file'] = _dummyComboBox(self, 'file') - self.subwidget_list['filelist'] = _dummyScrolledListBox(self, 'filelist') - - def filter(self): - self.tk.call(self._w, 'filter') - - def invoke(self): - self.tk.call(self._w, 'invoke') - - -# Should inherit from a Dialog class -class DirSelectDialog(TixWidget): - """The DirSelectDialog widget presents the directories in the file - system in a dialog window. The user can use this dialog window to - navigate through the file system to select the desired directory. - - Subwidgets Class - ---------- ----- - dirbox DirSelectDialog""" - - # FIXME: It should inherit -superclass tixDialogShell - def __init__(self, master, cnf={}, **kw): - TixWidget.__init__(self, master, 'tixDirSelectDialog', - ['options'], cnf, kw) - self.subwidget_list['dirbox'] = _dummyDirSelectBox(self, 'dirbox') - # cancel and ok buttons are missing - - def popup(self): - self.tk.call(self._w, 'popup') - - def popdown(self): - self.tk.call(self._w, 'popdown') - - -# Should inherit from a Dialog class -class ExFileSelectDialog(TixWidget): - """ExFileSelectDialog - MS Windows style file select dialog. - It provides a convenient method for the user to select files. - - Subwidgets Class - ---------- ----- - fsbox ExFileSelectBox""" - - # FIXME: It should inherit -superclass tixDialogShell - def __init__(self, master, cnf={}, **kw): - TixWidget.__init__(self, master, 'tixExFileSelectDialog', - ['options'], cnf, kw) - self.subwidget_list['fsbox'] = _dummyExFileSelectBox(self, 'fsbox') - - def popup(self): - self.tk.call(self._w, 'popup') - - def popdown(self): - self.tk.call(self._w, 'popdown') - -class FileSelectBox(TixWidget): - """ExFileSelectBox - Motif style file select box. - It is generally used for - the user to choose a file. FileSelectBox stores the files mostly - recently selected into a ComboBox widget so that they can be quickly - selected again. - - Subwidget Class - --------- ----- - selection ComboBox - filter ComboBox - dirlist ScrolledListBox - filelist ScrolledListBox""" - - def __init__(self, master, cnf={}, **kw): - TixWidget.__init__(self, master, 'tixFileSelectBox', ['options'], cnf, kw) - self.subwidget_list['dirlist'] = _dummyScrolledListBox(self, 'dirlist') - self.subwidget_list['filelist'] = _dummyScrolledListBox(self, 'filelist') - self.subwidget_list['filter'] = _dummyComboBox(self, 'filter') - self.subwidget_list['selection'] = _dummyComboBox(self, 'selection') - - def apply_filter(self): # name of subwidget is same as command - self.tk.call(self._w, 'filter') - - def invoke(self): - self.tk.call(self._w, 'invoke') - -# Should inherit from a Dialog class -class FileSelectDialog(TixWidget): - """FileSelectDialog - Motif style file select dialog. - - Subwidgets Class - ---------- ----- - btns StdButtonBox - fsbox FileSelectBox""" - - # FIXME: It should inherit -superclass tixStdDialogShell - def __init__(self, master, cnf={}, **kw): - TixWidget.__init__(self, master, 'tixFileSelectDialog', - ['options'], cnf, kw) - self.subwidget_list['btns'] = _dummyStdButtonBox(self, 'btns') - self.subwidget_list['fsbox'] = _dummyFileSelectBox(self, 'fsbox') - - def popup(self): - self.tk.call(self._w, 'popup') - - def popdown(self): - self.tk.call(self._w, 'popdown') - -class FileEntry(TixWidget): - """FileEntry - Entry field with button that invokes a FileSelectDialog. - The user can type in the filename manually. Alternatively, the user can - press the button widget that sits next to the entry, which will bring - up a file selection dialog. - - Subwidgets Class - ---------- ----- - button Button - entry Entry""" - - # FIXME: It should inherit -superclass tixLabelWidget - def __init__(self, master, cnf={}, **kw): - TixWidget.__init__(self, master, 'tixFileEntry', - ['dialogtype', 'options'], cnf, kw) - self.subwidget_list['button'] = _dummyButton(self, 'button') - self.subwidget_list['entry'] = _dummyEntry(self, 'entry') - - def invoke(self): - self.tk.call(self._w, 'invoke') - - def file_dialog(self): - # FIXME: return python object - pass - -class HList(TixWidget, XView, YView): - """HList - Hierarchy display widget can be used to display any data - that have a hierarchical structure, for example, file system directory - trees. The list entries are indented and connected by branch lines - according to their places in the hierarchy. - - Subwidgets - None""" - - def __init__ (self,master=None,cnf={}, **kw): - TixWidget.__init__(self, master, 'tixHList', - ['columns', 'options'], cnf, kw) - - def add(self, entry, cnf={}, **kw): - return self.tk.call(self._w, 'add', entry, *self._options(cnf, kw)) - - def add_child(self, parent=None, cnf={}, **kw): - if parent is None: - parent = '' - return self.tk.call( - self._w, 'addchild', parent, *self._options(cnf, kw)) - - def anchor_set(self, entry): - self.tk.call(self._w, 'anchor', 'set', entry) - - def anchor_clear(self): - self.tk.call(self._w, 'anchor', 'clear') - - def column_width(self, col=0, width=None, chars=None): - if not chars: - return self.tk.call(self._w, 'column', 'width', col, width) - else: - return self.tk.call(self._w, 'column', 'width', col, - '-char', chars) - - def delete_all(self): - self.tk.call(self._w, 'delete', 'all') - - def delete_entry(self, entry): - self.tk.call(self._w, 'delete', 'entry', entry) - - def delete_offsprings(self, entry): - self.tk.call(self._w, 'delete', 'offsprings', entry) - - def delete_siblings(self, entry): - self.tk.call(self._w, 'delete', 'siblings', entry) - - def dragsite_set(self, index): - self.tk.call(self._w, 'dragsite', 'set', index) - - def dragsite_clear(self): - self.tk.call(self._w, 'dragsite', 'clear') - - def dropsite_set(self, index): - self.tk.call(self._w, 'dropsite', 'set', index) - - def dropsite_clear(self): - self.tk.call(self._w, 'dropsite', 'clear') - - def header_create(self, col, cnf={}, **kw): - self.tk.call(self._w, 'header', 'create', col, *self._options(cnf, kw)) - - def header_configure(self, col, cnf={}, **kw): - if cnf is None: - return self._getconfigure(self._w, 'header', 'configure', col) - self.tk.call(self._w, 'header', 'configure', col, - *self._options(cnf, kw)) - - def header_cget(self, col, opt): - return self.tk.call(self._w, 'header', 'cget', col, opt) - - def header_exists(self, col): - # A workaround to Tix library bug (issue #25464). - # The documented command is "exists", but only erroneous "exist" is - # accepted. - return self.tk.getboolean(self.tk.call(self._w, 'header', 'exist', col)) - header_exist = header_exists - - def header_delete(self, col): - self.tk.call(self._w, 'header', 'delete', col) - - def header_size(self, col): - return self.tk.call(self._w, 'header', 'size', col) - - def hide_entry(self, entry): - self.tk.call(self._w, 'hide', 'entry', entry) - - def indicator_create(self, entry, cnf={}, **kw): - self.tk.call( - self._w, 'indicator', 'create', entry, *self._options(cnf, kw)) - - def indicator_configure(self, entry, cnf={}, **kw): - if cnf is None: - return self._getconfigure( - self._w, 'indicator', 'configure', entry) - self.tk.call( - self._w, 'indicator', 'configure', entry, *self._options(cnf, kw)) - - def indicator_cget(self, entry, opt): - return self.tk.call(self._w, 'indicator', 'cget', entry, opt) - - def indicator_exists(self, entry): - return self.tk.call (self._w, 'indicator', 'exists', entry) - - def indicator_delete(self, entry): - self.tk.call(self._w, 'indicator', 'delete', entry) - - def indicator_size(self, entry): - return self.tk.call(self._w, 'indicator', 'size', entry) - - def info_anchor(self): - return self.tk.call(self._w, 'info', 'anchor') - - def info_bbox(self, entry): - return self._getints( - self.tk.call(self._w, 'info', 'bbox', entry)) or None - - def info_children(self, entry=None): - c = self.tk.call(self._w, 'info', 'children', entry) - return self.tk.splitlist(c) - - def info_data(self, entry): - return self.tk.call(self._w, 'info', 'data', entry) - - def info_dragsite(self): - return self.tk.call(self._w, 'info', 'dragsite') - - def info_dropsite(self): - return self.tk.call(self._w, 'info', 'dropsite') - - def info_exists(self, entry): - return self.tk.call(self._w, 'info', 'exists', entry) - - def info_hidden(self, entry): - return self.tk.call(self._w, 'info', 'hidden', entry) - - def info_next(self, entry): - return self.tk.call(self._w, 'info', 'next', entry) - - def info_parent(self, entry): - return self.tk.call(self._w, 'info', 'parent', entry) - - def info_prev(self, entry): - return self.tk.call(self._w, 'info', 'prev', entry) - - def info_selection(self): - c = self.tk.call(self._w, 'info', 'selection') - return self.tk.splitlist(c) - - def item_cget(self, entry, col, opt): - return self.tk.call(self._w, 'item', 'cget', entry, col, opt) - - def item_configure(self, entry, col, cnf={}, **kw): - if cnf is None: - return self._getconfigure(self._w, 'item', 'configure', entry, col) - self.tk.call(self._w, 'item', 'configure', entry, col, - *self._options(cnf, kw)) - - def item_create(self, entry, col, cnf={}, **kw): - self.tk.call( - self._w, 'item', 'create', entry, col, *self._options(cnf, kw)) - - def item_exists(self, entry, col): - return self.tk.call(self._w, 'item', 'exists', entry, col) - - def item_delete(self, entry, col): - self.tk.call(self._w, 'item', 'delete', entry, col) - - def entrycget(self, entry, opt): - return self.tk.call(self._w, 'entrycget', entry, opt) - - def entryconfigure(self, entry, cnf={}, **kw): - if cnf is None: - return self._getconfigure(self._w, 'entryconfigure', entry) - self.tk.call(self._w, 'entryconfigure', entry, - *self._options(cnf, kw)) - - def nearest(self, y): - return self.tk.call(self._w, 'nearest', y) - - def see(self, entry): - self.tk.call(self._w, 'see', entry) - - def selection_clear(self, cnf={}, **kw): - self.tk.call(self._w, 'selection', 'clear', *self._options(cnf, kw)) - - def selection_includes(self, entry): - return self.tk.call(self._w, 'selection', 'includes', entry) - - def selection_set(self, first, last=None): - self.tk.call(self._w, 'selection', 'set', first, last) - - def show_entry(self, entry): - return self.tk.call(self._w, 'show', 'entry', entry) - -class InputOnly(TixWidget): - """InputOnly - Invisible widget. Unix only. - - Subwidgets - None""" - - def __init__ (self,master=None,cnf={}, **kw): - TixWidget.__init__(self, master, 'tixInputOnly', None, cnf, kw) - -class LabelEntry(TixWidget): - """LabelEntry - Entry field with label. Packages an entry widget - and a label into one mega widget. It can be used to simplify the creation - of ``entry-form'' type of interface. - - Subwidgets Class - ---------- ----- - label Label - entry Entry""" - - def __init__ (self,master=None,cnf={}, **kw): - TixWidget.__init__(self, master, 'tixLabelEntry', - ['labelside','options'], cnf, kw) - self.subwidget_list['label'] = _dummyLabel(self, 'label') - self.subwidget_list['entry'] = _dummyEntry(self, 'entry') - -class LabelFrame(TixWidget): - """LabelFrame - Labelled Frame container. Packages a frame widget - and a label into one mega widget. To create widgets inside a - LabelFrame widget, one creates the new widgets relative to the - frame subwidget and manage them inside the frame subwidget. - - Subwidgets Class - ---------- ----- - label Label - frame Frame""" - - def __init__ (self,master=None,cnf={}, **kw): - TixWidget.__init__(self, master, 'tixLabelFrame', - ['labelside','options'], cnf, kw) - self.subwidget_list['label'] = _dummyLabel(self, 'label') - self.subwidget_list['frame'] = _dummyFrame(self, 'frame') - - -class ListNoteBook(TixWidget): - """A ListNoteBook widget is very similar to the TixNoteBook widget: - it can be used to display many windows in a limited space using a - notebook metaphor. The notebook is divided into a stack of pages - (windows). At one time only one of these pages can be shown. - The user can navigate through these pages by - choosing the name of the desired page in the hlist subwidget.""" - - def __init__(self, master, cnf={}, **kw): - TixWidget.__init__(self, master, 'tixListNoteBook', ['options'], cnf, kw) - # Is this necessary? It's not an exposed subwidget in Tix. - self.subwidget_list['pane'] = _dummyPanedWindow(self, 'pane', - destroy_physically=0) - self.subwidget_list['hlist'] = _dummyHList(self, 'hlist') - self.subwidget_list['shlist'] = _dummyScrolledHList(self, 'shlist') - - def add(self, name, cnf={}, **kw): - self.tk.call(self._w, 'add', name, *self._options(cnf, kw)) - self.subwidget_list[name] = TixSubWidget(self, name) - return self.subwidget_list[name] - - def page(self, name): - return self.subwidget(name) - - def pages(self): - # Can't call subwidgets_all directly because we don't want .nbframe - names = self.tk.splitlist(self.tk.call(self._w, 'pages')) - ret = [] - for x in names: - ret.append(self.subwidget(x)) - return ret - - def raise_page(self, name): # raise is a python keyword - self.tk.call(self._w, 'raise', name) - -class Meter(TixWidget): - """The Meter widget can be used to show the progress of a background - job which may take a long time to execute. - """ - - def __init__(self, master=None, cnf={}, **kw): - TixWidget.__init__(self, master, 'tixMeter', - ['options'], cnf, kw) - -class NoteBook(TixWidget): - """NoteBook - Multi-page container widget (tabbed notebook metaphor). - - Subwidgets Class - ---------- ----- - nbframe NoteBookFrame - page widgets added dynamically with the add method""" - - def __init__ (self,master=None,cnf={}, **kw): - TixWidget.__init__(self,master,'tixNoteBook', ['options'], cnf, kw) - self.subwidget_list['nbframe'] = TixSubWidget(self, 'nbframe', - destroy_physically=0) - - def add(self, name, cnf={}, **kw): - self.tk.call(self._w, 'add', name, *self._options(cnf, kw)) - self.subwidget_list[name] = TixSubWidget(self, name) - return self.subwidget_list[name] - - def delete(self, name): - self.tk.call(self._w, 'delete', name) - self.subwidget_list[name].destroy() - del self.subwidget_list[name] - - def page(self, name): - return self.subwidget(name) - - def pages(self): - # Can't call subwidgets_all directly because we don't want .nbframe - names = self.tk.splitlist(self.tk.call(self._w, 'pages')) - ret = [] - for x in names: - ret.append(self.subwidget(x)) - return ret - - def raise_page(self, name): # raise is a python keyword - self.tk.call(self._w, 'raise', name) - - def raised(self): - return self.tk.call(self._w, 'raised') - -class NoteBookFrame(TixWidget): - # FIXME: This is dangerous to expose to be called on its own. - pass - -class OptionMenu(TixWidget): - """OptionMenu - creates a menu button of options. - - Subwidget Class - --------- ----- - menubutton Menubutton - menu Menu""" - - def __init__(self, master, cnf={}, **kw): - TixWidget.__init__(self, master, 'tixOptionMenu', ['options'], cnf, kw) - self.subwidget_list['menubutton'] = _dummyMenubutton(self, 'menubutton') - self.subwidget_list['menu'] = _dummyMenu(self, 'menu') - - def add_command(self, name, cnf={}, **kw): - self.tk.call(self._w, 'add', 'command', name, *self._options(cnf, kw)) - - def add_separator(self, name, cnf={}, **kw): - self.tk.call(self._w, 'add', 'separator', name, *self._options(cnf, kw)) - - def delete(self, name): - self.tk.call(self._w, 'delete', name) - - def disable(self, name): - self.tk.call(self._w, 'disable', name) - - def enable(self, name): - self.tk.call(self._w, 'enable', name) - -class PanedWindow(TixWidget): - """PanedWindow - Multi-pane container widget - allows the user to interactively manipulate the sizes of several - panes. The panes can be arranged either vertically or horizontally.The - user changes the sizes of the panes by dragging the resize handle - between two panes. - - Subwidgets Class - ---------- ----- - g/p widgets added dynamically with the add method.""" - - def __init__(self, master, cnf={}, **kw): - TixWidget.__init__(self, master, 'tixPanedWindow', ['orientation', 'options'], cnf, kw) - - # add delete forget panecget paneconfigure panes setsize - def add(self, name, cnf={}, **kw): - self.tk.call(self._w, 'add', name, *self._options(cnf, kw)) - self.subwidget_list[name] = TixSubWidget(self, name, - check_intermediate=0) - return self.subwidget_list[name] - - def delete(self, name): - self.tk.call(self._w, 'delete', name) - self.subwidget_list[name].destroy() - del self.subwidget_list[name] - - def forget(self, name): - self.tk.call(self._w, 'forget', name) - - def panecget(self, entry, opt): - return self.tk.call(self._w, 'panecget', entry, opt) - - def paneconfigure(self, entry, cnf={}, **kw): - if cnf is None: - return self._getconfigure(self._w, 'paneconfigure', entry) - self.tk.call(self._w, 'paneconfigure', entry, *self._options(cnf, kw)) - - def panes(self): - names = self.tk.splitlist(self.tk.call(self._w, 'panes')) - return [self.subwidget(x) for x in names] - -class PopupMenu(TixWidget): - """PopupMenu widget can be used as a replacement of the tk_popup command. - The advantage of the Tix PopupMenu widget is it requires less application - code to manipulate. - - - Subwidgets Class - ---------- ----- - menubutton Menubutton - menu Menu""" - - # FIXME: It should inherit -superclass tixShell - def __init__(self, master, cnf={}, **kw): - TixWidget.__init__(self, master, 'tixPopupMenu', ['options'], cnf, kw) - self.subwidget_list['menubutton'] = _dummyMenubutton(self, 'menubutton') - self.subwidget_list['menu'] = _dummyMenu(self, 'menu') - - def bind_widget(self, widget): - self.tk.call(self._w, 'bind', widget._w) - - def unbind_widget(self, widget): - self.tk.call(self._w, 'unbind', widget._w) - - def post_widget(self, widget, x, y): - self.tk.call(self._w, 'post', widget._w, x, y) - -class ResizeHandle(TixWidget): - """Internal widget to draw resize handles on Scrolled widgets.""" - def __init__(self, master, cnf={}, **kw): - # There seems to be a Tix bug rejecting the configure method - # Let's try making the flags -static - flags = ['options', 'command', 'cursorfg', 'cursorbg', - 'handlesize', 'hintcolor', 'hintwidth', - 'x', 'y'] - # In fact, x y height width are configurable - TixWidget.__init__(self, master, 'tixResizeHandle', - flags, cnf, kw) - - def attach_widget(self, widget): - self.tk.call(self._w, 'attachwidget', widget._w) - - def detach_widget(self, widget): - self.tk.call(self._w, 'detachwidget', widget._w) - - def hide(self, widget): - self.tk.call(self._w, 'hide', widget._w) - - def show(self, widget): - self.tk.call(self._w, 'show', widget._w) - -class ScrolledHList(TixWidget): - """ScrolledHList - HList with automatic scrollbars.""" - - # FIXME: It should inherit -superclass tixScrolledWidget - def __init__(self, master, cnf={}, **kw): - TixWidget.__init__(self, master, 'tixScrolledHList', ['options'], - cnf, kw) - self.subwidget_list['hlist'] = _dummyHList(self, 'hlist') - self.subwidget_list['vsb'] = _dummyScrollbar(self, 'vsb') - self.subwidget_list['hsb'] = _dummyScrollbar(self, 'hsb') - -class ScrolledListBox(TixWidget): - """ScrolledListBox - Listbox with automatic scrollbars.""" - - # FIXME: It should inherit -superclass tixScrolledWidget - def __init__(self, master, cnf={}, **kw): - TixWidget.__init__(self, master, 'tixScrolledListBox', ['options'], cnf, kw) - self.subwidget_list['listbox'] = _dummyListbox(self, 'listbox') - self.subwidget_list['vsb'] = _dummyScrollbar(self, 'vsb') - self.subwidget_list['hsb'] = _dummyScrollbar(self, 'hsb') - -class ScrolledText(TixWidget): - """ScrolledText - Text with automatic scrollbars.""" - - # FIXME: It should inherit -superclass tixScrolledWidget - def __init__(self, master, cnf={}, **kw): - TixWidget.__init__(self, master, 'tixScrolledText', ['options'], cnf, kw) - self.subwidget_list['text'] = _dummyText(self, 'text') - self.subwidget_list['vsb'] = _dummyScrollbar(self, 'vsb') - self.subwidget_list['hsb'] = _dummyScrollbar(self, 'hsb') - -class ScrolledTList(TixWidget): - """ScrolledTList - TList with automatic scrollbars.""" - - # FIXME: It should inherit -superclass tixScrolledWidget - def __init__(self, master, cnf={}, **kw): - TixWidget.__init__(self, master, 'tixScrolledTList', ['options'], - cnf, kw) - self.subwidget_list['tlist'] = _dummyTList(self, 'tlist') - self.subwidget_list['vsb'] = _dummyScrollbar(self, 'vsb') - self.subwidget_list['hsb'] = _dummyScrollbar(self, 'hsb') - -class ScrolledWindow(TixWidget): - """ScrolledWindow - Window with automatic scrollbars.""" - - # FIXME: It should inherit -superclass tixScrolledWidget - def __init__(self, master, cnf={}, **kw): - TixWidget.__init__(self, master, 'tixScrolledWindow', ['options'], cnf, kw) - self.subwidget_list['window'] = _dummyFrame(self, 'window') - self.subwidget_list['vsb'] = _dummyScrollbar(self, 'vsb') - self.subwidget_list['hsb'] = _dummyScrollbar(self, 'hsb') - -class Select(TixWidget): - """Select - Container of button subwidgets. It can be used to provide - radio-box or check-box style of selection options for the user. - - Subwidgets are buttons added dynamically using the add method.""" - - # FIXME: It should inherit -superclass tixLabelWidget - def __init__(self, master, cnf={}, **kw): - TixWidget.__init__(self, master, 'tixSelect', - ['allowzero', 'radio', 'orientation', 'labelside', - 'options'], - cnf, kw) - self.subwidget_list['label'] = _dummyLabel(self, 'label') - - def add(self, name, cnf={}, **kw): - self.tk.call(self._w, 'add', name, *self._options(cnf, kw)) - self.subwidget_list[name] = _dummyButton(self, name) - return self.subwidget_list[name] - - def invoke(self, name): - self.tk.call(self._w, 'invoke', name) - -class Shell(TixWidget): - """Toplevel window. - - Subwidgets - None""" - - def __init__ (self,master=None,cnf={}, **kw): - TixWidget.__init__(self, master, 'tixShell', ['options', 'title'], cnf, kw) - -class DialogShell(TixWidget): - """Toplevel window, with popup popdown and center methods. - It tells the window manager that it is a dialog window and should be - treated specially. The exact treatment depends on the treatment of - the window manager. - - Subwidgets - None""" - - # FIXME: It should inherit from Shell - def __init__ (self,master=None,cnf={}, **kw): - TixWidget.__init__(self, master, - 'tixDialogShell', - ['options', 'title', 'mapped', - 'minheight', 'minwidth', - 'parent', 'transient'], cnf, kw) - - def popdown(self): - self.tk.call(self._w, 'popdown') - - def popup(self): - self.tk.call(self._w, 'popup') - - def center(self): - self.tk.call(self._w, 'center') - -class StdButtonBox(TixWidget): - """StdButtonBox - Standard Button Box (OK, Apply, Cancel and Help) """ - - def __init__(self, master=None, cnf={}, **kw): - TixWidget.__init__(self, master, 'tixStdButtonBox', - ['orientation', 'options'], cnf, kw) - self.subwidget_list['ok'] = _dummyButton(self, 'ok') - self.subwidget_list['apply'] = _dummyButton(self, 'apply') - self.subwidget_list['cancel'] = _dummyButton(self, 'cancel') - self.subwidget_list['help'] = _dummyButton(self, 'help') - - def invoke(self, name): - if name in self.subwidget_list: - self.tk.call(self._w, 'invoke', name) - -class TList(TixWidget, XView, YView): - """TList - Hierarchy display widget which can be - used to display data in a tabular format. The list entries of a TList - widget are similar to the entries in the Tk listbox widget. The main - differences are (1) the TList widget can display the list entries in a - two dimensional format and (2) you can use graphical images as well as - multiple colors and fonts for the list entries. - - Subwidgets - None""" - - def __init__ (self,master=None,cnf={}, **kw): - TixWidget.__init__(self, master, 'tixTList', ['options'], cnf, kw) - - def active_set(self, index): - self.tk.call(self._w, 'active', 'set', index) - - def active_clear(self): - self.tk.call(self._w, 'active', 'clear') - - def anchor_set(self, index): - self.tk.call(self._w, 'anchor', 'set', index) - - def anchor_clear(self): - self.tk.call(self._w, 'anchor', 'clear') - - def delete(self, from_, to=None): - self.tk.call(self._w, 'delete', from_, to) - - def dragsite_set(self, index): - self.tk.call(self._w, 'dragsite', 'set', index) - - def dragsite_clear(self): - self.tk.call(self._w, 'dragsite', 'clear') - - def dropsite_set(self, index): - self.tk.call(self._w, 'dropsite', 'set', index) - - def dropsite_clear(self): - self.tk.call(self._w, 'dropsite', 'clear') - - def insert(self, index, cnf={}, **kw): - self.tk.call(self._w, 'insert', index, *self._options(cnf, kw)) - - def info_active(self): - return self.tk.call(self._w, 'info', 'active') - - def info_anchor(self): - return self.tk.call(self._w, 'info', 'anchor') - - def info_down(self, index): - return self.tk.call(self._w, 'info', 'down', index) - - def info_left(self, index): - return self.tk.call(self._w, 'info', 'left', index) - - def info_right(self, index): - return self.tk.call(self._w, 'info', 'right', index) - - def info_selection(self): - c = self.tk.call(self._w, 'info', 'selection') - return self.tk.splitlist(c) - - def info_size(self): - return self.tk.call(self._w, 'info', 'size') - - def info_up(self, index): - return self.tk.call(self._w, 'info', 'up', index) - - def nearest(self, x, y): - return self.tk.call(self._w, 'nearest', x, y) - - def see(self, index): - self.tk.call(self._w, 'see', index) - - def selection_clear(self, cnf={}, **kw): - self.tk.call(self._w, 'selection', 'clear', *self._options(cnf, kw)) - - def selection_includes(self, index): - return self.tk.call(self._w, 'selection', 'includes', index) - - def selection_set(self, first, last=None): - self.tk.call(self._w, 'selection', 'set', first, last) - -class Tree(TixWidget): - """Tree - The tixTree widget can be used to display hierarchical - data in a tree form. The user can adjust - the view of the tree by opening or closing parts of the tree.""" - - # FIXME: It should inherit -superclass tixScrolledWidget - def __init__(self, master=None, cnf={}, **kw): - TixWidget.__init__(self, master, 'tixTree', - ['options'], cnf, kw) - self.subwidget_list['hlist'] = _dummyHList(self, 'hlist') - self.subwidget_list['vsb'] = _dummyScrollbar(self, 'vsb') - self.subwidget_list['hsb'] = _dummyScrollbar(self, 'hsb') - - def autosetmode(self): - '''This command calls the setmode method for all the entries in this - Tree widget: if an entry has no child entries, its mode is set to - none. Otherwise, if the entry has any hidden child entries, its mode is - set to open; otherwise its mode is set to close.''' - self.tk.call(self._w, 'autosetmode') - - def close(self, entrypath): - '''Close the entry given by entryPath if its mode is close.''' - self.tk.call(self._w, 'close', entrypath) - - def getmode(self, entrypath): - '''Returns the current mode of the entry given by entryPath.''' - return self.tk.call(self._w, 'getmode', entrypath) - - def open(self, entrypath): - '''Open the entry given by entryPath if its mode is open.''' - self.tk.call(self._w, 'open', entrypath) - - def setmode(self, entrypath, mode='none'): - '''This command is used to indicate whether the entry given by - entryPath has children entries and whether the children are visible. mode - must be one of open, close or none. If mode is set to open, a (+) - indicator is drawn next the entry. If mode is set to close, a (-) - indicator is drawn next the entry. If mode is set to none, no - indicators will be drawn for this entry. The default mode is none. The - open mode indicates the entry has hidden children and this entry can be - opened by the user. The close mode indicates that all the children of the - entry are now visible and the entry can be closed by the user.''' - self.tk.call(self._w, 'setmode', entrypath, mode) - - -# Could try subclassing Tree for CheckList - would need another arg to init -class CheckList(TixWidget): - """The CheckList widget - displays a list of items to be selected by the user. CheckList acts - similarly to the Tk checkbutton or radiobutton widgets, except it is - capable of handling many more items than checkbuttons or radiobuttons. - """ - # FIXME: It should inherit -superclass tixTree - def __init__(self, master=None, cnf={}, **kw): - TixWidget.__init__(self, master, 'tixCheckList', - ['options', 'radio'], cnf, kw) - self.subwidget_list['hlist'] = _dummyHList(self, 'hlist') - self.subwidget_list['vsb'] = _dummyScrollbar(self, 'vsb') - self.subwidget_list['hsb'] = _dummyScrollbar(self, 'hsb') - - def autosetmode(self): - '''This command calls the setmode method for all the entries in this - Tree widget: if an entry has no child entries, its mode is set to - none. Otherwise, if the entry has any hidden child entries, its mode is - set to open; otherwise its mode is set to close.''' - self.tk.call(self._w, 'autosetmode') - - def close(self, entrypath): - '''Close the entry given by entryPath if its mode is close.''' - self.tk.call(self._w, 'close', entrypath) - - def getmode(self, entrypath): - '''Returns the current mode of the entry given by entryPath.''' - return self.tk.call(self._w, 'getmode', entrypath) - - def open(self, entrypath): - '''Open the entry given by entryPath if its mode is open.''' - self.tk.call(self._w, 'open', entrypath) - - def getselection(self, mode='on'): - '''Returns a list of items whose status matches status. If status is - not specified, the list of items in the "on" status will be returned. - Mode can be on, off, default''' - return self.tk.splitlist(self.tk.call(self._w, 'getselection', mode)) - - def getstatus(self, entrypath): - '''Returns the current status of entryPath.''' - return self.tk.call(self._w, 'getstatus', entrypath) - - def setstatus(self, entrypath, mode='on'): - '''Sets the status of entryPath to be status. A bitmap will be - displayed next to the entry its status is on, off or default.''' - self.tk.call(self._w, 'setstatus', entrypath, mode) - - -########################################################################### -### The subclassing below is used to instantiate the subwidgets in each ### -### mega widget. This allows us to access their methods directly. ### -########################################################################### - -class _dummyButton(Button, TixSubWidget): - def __init__(self, master, name, destroy_physically=1): - TixSubWidget.__init__(self, master, name, destroy_physically) - -class _dummyCheckbutton(Checkbutton, TixSubWidget): - def __init__(self, master, name, destroy_physically=1): - TixSubWidget.__init__(self, master, name, destroy_physically) - -class _dummyEntry(Entry, TixSubWidget): - def __init__(self, master, name, destroy_physically=1): - TixSubWidget.__init__(self, master, name, destroy_physically) - -class _dummyFrame(Frame, TixSubWidget): - def __init__(self, master, name, destroy_physically=1): - TixSubWidget.__init__(self, master, name, destroy_physically) - -class _dummyLabel(Label, TixSubWidget): - def __init__(self, master, name, destroy_physically=1): - TixSubWidget.__init__(self, master, name, destroy_physically) - -class _dummyListbox(Listbox, TixSubWidget): - def __init__(self, master, name, destroy_physically=1): - TixSubWidget.__init__(self, master, name, destroy_physically) - -class _dummyMenu(Menu, TixSubWidget): - def __init__(self, master, name, destroy_physically=1): - TixSubWidget.__init__(self, master, name, destroy_physically) - -class _dummyMenubutton(Menubutton, TixSubWidget): - def __init__(self, master, name, destroy_physically=1): - TixSubWidget.__init__(self, master, name, destroy_physically) - -class _dummyScrollbar(Scrollbar, TixSubWidget): - def __init__(self, master, name, destroy_physically=1): - TixSubWidget.__init__(self, master, name, destroy_physically) - -class _dummyText(Text, TixSubWidget): - def __init__(self, master, name, destroy_physically=1): - TixSubWidget.__init__(self, master, name, destroy_physically) - -class _dummyScrolledListBox(ScrolledListBox, TixSubWidget): - def __init__(self, master, name, destroy_physically=1): - TixSubWidget.__init__(self, master, name, destroy_physically) - self.subwidget_list['listbox'] = _dummyListbox(self, 'listbox') - self.subwidget_list['vsb'] = _dummyScrollbar(self, 'vsb') - self.subwidget_list['hsb'] = _dummyScrollbar(self, 'hsb') - -class _dummyHList(HList, TixSubWidget): - def __init__(self, master, name, destroy_physically=1): - TixSubWidget.__init__(self, master, name, destroy_physically) - -class _dummyScrolledHList(ScrolledHList, TixSubWidget): - def __init__(self, master, name, destroy_physically=1): - TixSubWidget.__init__(self, master, name, destroy_physically) - self.subwidget_list['hlist'] = _dummyHList(self, 'hlist') - self.subwidget_list['vsb'] = _dummyScrollbar(self, 'vsb') - self.subwidget_list['hsb'] = _dummyScrollbar(self, 'hsb') - -class _dummyTList(TList, TixSubWidget): - def __init__(self, master, name, destroy_physically=1): - TixSubWidget.__init__(self, master, name, destroy_physically) - -class _dummyComboBox(ComboBox, TixSubWidget): - def __init__(self, master, name, destroy_physically=1): - TixSubWidget.__init__(self, master, name, ['fancy',destroy_physically]) - self.subwidget_list['label'] = _dummyLabel(self, 'label') - self.subwidget_list['entry'] = _dummyEntry(self, 'entry') - self.subwidget_list['arrow'] = _dummyButton(self, 'arrow') - - self.subwidget_list['slistbox'] = _dummyScrolledListBox(self, - 'slistbox') - try: - self.subwidget_list['tick'] = _dummyButton(self, 'tick') - #cross Button : present if created with the fancy option - self.subwidget_list['cross'] = _dummyButton(self, 'cross') - except TypeError: - # unavailable when -fancy not specified - pass - -class _dummyDirList(DirList, TixSubWidget): - def __init__(self, master, name, destroy_physically=1): - TixSubWidget.__init__(self, master, name, destroy_physically) - self.subwidget_list['hlist'] = _dummyHList(self, 'hlist') - self.subwidget_list['vsb'] = _dummyScrollbar(self, 'vsb') - self.subwidget_list['hsb'] = _dummyScrollbar(self, 'hsb') - -class _dummyDirSelectBox(DirSelectBox, TixSubWidget): - def __init__(self, master, name, destroy_physically=1): - TixSubWidget.__init__(self, master, name, destroy_physically) - self.subwidget_list['dirlist'] = _dummyDirList(self, 'dirlist') - self.subwidget_list['dircbx'] = _dummyFileComboBox(self, 'dircbx') - -class _dummyExFileSelectBox(ExFileSelectBox, TixSubWidget): - def __init__(self, master, name, destroy_physically=1): - TixSubWidget.__init__(self, master, name, destroy_physically) - self.subwidget_list['cancel'] = _dummyButton(self, 'cancel') - self.subwidget_list['ok'] = _dummyButton(self, 'ok') - self.subwidget_list['hidden'] = _dummyCheckbutton(self, 'hidden') - self.subwidget_list['types'] = _dummyComboBox(self, 'types') - self.subwidget_list['dir'] = _dummyComboBox(self, 'dir') - self.subwidget_list['dirlist'] = _dummyScrolledListBox(self, 'dirlist') - self.subwidget_list['file'] = _dummyComboBox(self, 'file') - self.subwidget_list['filelist'] = _dummyScrolledListBox(self, 'filelist') - -class _dummyFileSelectBox(FileSelectBox, TixSubWidget): - def __init__(self, master, name, destroy_physically=1): - TixSubWidget.__init__(self, master, name, destroy_physically) - self.subwidget_list['dirlist'] = _dummyScrolledListBox(self, 'dirlist') - self.subwidget_list['filelist'] = _dummyScrolledListBox(self, 'filelist') - self.subwidget_list['filter'] = _dummyComboBox(self, 'filter') - self.subwidget_list['selection'] = _dummyComboBox(self, 'selection') - -class _dummyFileComboBox(ComboBox, TixSubWidget): - def __init__(self, master, name, destroy_physically=1): - TixSubWidget.__init__(self, master, name, destroy_physically) - self.subwidget_list['dircbx'] = _dummyComboBox(self, 'dircbx') - -class _dummyStdButtonBox(StdButtonBox, TixSubWidget): - def __init__(self, master, name, destroy_physically=1): - TixSubWidget.__init__(self, master, name, destroy_physically) - self.subwidget_list['ok'] = _dummyButton(self, 'ok') - self.subwidget_list['apply'] = _dummyButton(self, 'apply') - self.subwidget_list['cancel'] = _dummyButton(self, 'cancel') - self.subwidget_list['help'] = _dummyButton(self, 'help') - -class _dummyNoteBookFrame(NoteBookFrame, TixSubWidget): - def __init__(self, master, name, destroy_physically=0): - TixSubWidget.__init__(self, master, name, destroy_physically) - -class _dummyPanedWindow(PanedWindow, TixSubWidget): - def __init__(self, master, name, destroy_physically=1): - TixSubWidget.__init__(self, master, name, destroy_physically) - -######################## -### Utility Routines ### -######################## - -#mike Should tixDestroy be exposed as a wrapper? - but not for widgets. - -def OptionName(widget): - '''Returns the qualified path name for the widget. Normally used to set - default options for subwidgets. See tixwidgets.py''' - return widget.tk.call('tixOptionName', widget._w) - -# Called with a dictionary argument of the form -# {'*.c':'C source files', '*.txt':'Text Files', '*':'All files'} -# returns a string which can be used to configure the fsbox file types -# in an ExFileSelectBox. i.e., -# '{{*} {* - All files}} {{*.c} {*.c - C source files}} {{*.txt} {*.txt - Text Files}}' -def FileTypeList(dict): - s = '' - for type in dict.keys(): - s = s + '{{' + type + '} {' + type + ' - ' + dict[type] + '}} ' - return s - -# Still to be done: -# tixIconView -class CObjView(TixWidget): - """This file implements the Canvas Object View widget. This is a base - class of IconView. It implements automatic placement/adjustment of the - scrollbars according to the canvas objects inside the canvas subwidget. - The scrollbars are adjusted so that the canvas is just large enough - to see all the objects. - """ - # FIXME: It should inherit -superclass tixScrolledWidget - pass - - -class Grid(TixWidget, XView, YView): - '''The Tix Grid command creates a new window and makes it into a - tixGrid widget. Additional options, may be specified on the command - line or in the option database to configure aspects such as its cursor - and relief. - - A Grid widget displays its contents in a two dimensional grid of cells. - Each cell may contain one Tix display item, which may be in text, - graphics or other formats. See the DisplayStyle class for more information - about Tix display items. Individual cells, or groups of cells, can be - formatted with a wide range of attributes, such as its color, relief and - border. - - Subwidgets - None''' - # valid specific resources as of Tk 8.4 - # editdonecmd, editnotifycmd, floatingcols, floatingrows, formatcmd, - # highlightbackground, highlightcolor, leftmargin, itemtype, selectmode, - # selectunit, topmargin, - def __init__(self, master=None, cnf={}, **kw): - static= [] - self.cnf= cnf - TixWidget.__init__(self, master, 'tixGrid', static, cnf, kw) - - # valid options as of Tk 8.4 - # anchor, bdtype, cget, configure, delete, dragsite, dropsite, entrycget, - # edit, entryconfigure, format, geometryinfo, info, index, move, nearest, - # selection, set, size, unset, xview, yview - def anchor_clear(self): - """Removes the selection anchor.""" - self.tk.call(self, 'anchor', 'clear') - - def anchor_get(self): - "Get the (x,y) coordinate of the current anchor cell" - return self._getints(self.tk.call(self, 'anchor', 'get')) - - def anchor_set(self, x, y): - """Set the selection anchor to the cell at (x, y).""" - self.tk.call(self, 'anchor', 'set', x, y) - - def delete_row(self, from_, to=None): - """Delete rows between from_ and to inclusive. - If to is not provided, delete only row at from_""" - if to is None: - self.tk.call(self, 'delete', 'row', from_) - else: - self.tk.call(self, 'delete', 'row', from_, to) - - def delete_column(self, from_, to=None): - """Delete columns between from_ and to inclusive. - If to is not provided, delete only column at from_""" - if to is None: - self.tk.call(self, 'delete', 'column', from_) - else: - self.tk.call(self, 'delete', 'column', from_, to) - - def edit_apply(self): - """If any cell is being edited, de-highlight the cell and applies - the changes.""" - self.tk.call(self, 'edit', 'apply') - - def edit_set(self, x, y): - """Highlights the cell at (x, y) for editing, if the -editnotify - command returns True for this cell.""" - self.tk.call(self, 'edit', 'set', x, y) - - def entrycget(self, x, y, option): - "Get the option value for cell at (x,y)" - if option and option[0] != '-': - option = '-' + option - return self.tk.call(self, 'entrycget', x, y, option) - - def entryconfigure(self, x, y, cnf=None, **kw): - return self._configure(('entryconfigure', x, y), cnf, kw) - - # def format - # def index - - def info_exists(self, x, y): - "Return True if display item exists at (x,y)" - return self._getboolean(self.tk.call(self, 'info', 'exists', x, y)) - - def info_bbox(self, x, y): - # This seems to always return '', at least for 'text' displayitems - return self.tk.call(self, 'info', 'bbox', x, y) - - def move_column(self, from_, to, offset): - """Moves the range of columns from position FROM through TO by - the distance indicated by OFFSET. For example, move_column(2, 4, 1) - moves the columns 2,3,4 to columns 3,4,5.""" - self.tk.call(self, 'move', 'column', from_, to, offset) - - def move_row(self, from_, to, offset): - """Moves the range of rows from position FROM through TO by - the distance indicated by OFFSET. - For example, move_row(2, 4, 1) moves the rows 2,3,4 to rows 3,4,5.""" - self.tk.call(self, 'move', 'row', from_, to, offset) - - def nearest(self, x, y): - "Return coordinate of cell nearest pixel coordinate (x,y)" - return self._getints(self.tk.call(self, 'nearest', x, y)) - - # def selection adjust - # def selection clear - # def selection includes - # def selection set - # def selection toggle - - def set(self, x, y, itemtype=None, **kw): - args= self._options(self.cnf, kw) - if itemtype is not None: - args= ('-itemtype', itemtype) + args - self.tk.call(self, 'set', x, y, *args) - - def size_column(self, index, **kw): - """Queries or sets the size of the column given by - INDEX. INDEX may be any non-negative - integer that gives the position of a given column. - INDEX can also be the string "default"; in this case, this command - queries or sets the default size of all columns. - When no option-value pair is given, this command returns a tuple - containing the current size setting of the given column. When - option-value pairs are given, the corresponding options of the - size setting of the given column are changed. Options may be one - of the following: - pad0 pixels - Specifies the paddings to the left of a column. - pad1 pixels - Specifies the paddings to the right of a column. - size val - Specifies the width of a column. Val may be: - "auto" -- the width of the column is set to the - width of the widest cell in the column; - a valid Tk screen distance unit; - or a real number following by the word chars - (e.g. 3.4chars) that sets the width of the column to the - given number of characters.""" - return self.tk.splitlist(self.tk.call(self._w, 'size', 'column', index, - *self._options({}, kw))) - - def size_row(self, index, **kw): - """Queries or sets the size of the row given by - INDEX. INDEX may be any non-negative - integer that gives the position of a given row . - INDEX can also be the string "default"; in this case, this command - queries or sets the default size of all rows. - When no option-value pair is given, this command returns a list con- - taining the current size setting of the given row . When option-value - pairs are given, the corresponding options of the size setting of the - given row are changed. Options may be one of the following: - pad0 pixels - Specifies the paddings to the top of a row. - pad1 pixels - Specifies the paddings to the bottom of a row. - size val - Specifies the height of a row. Val may be: - "auto" -- the height of the row is set to the - height of the highest cell in the row; - a valid Tk screen distance unit; - or a real number following by the word chars - (e.g. 3.4chars) that sets the height of the row to the - given number of characters.""" - return self.tk.splitlist(self.tk.call( - self, 'size', 'row', index, *self._options({}, kw))) - - def unset(self, x, y): - """Clears the cell at (x, y) by removing its display item.""" - self.tk.call(self._w, 'unset', x, y) - - -class ScrolledGrid(Grid): - '''Scrolled Grid widgets''' - - # FIXME: It should inherit -superclass tixScrolledWidget - def __init__(self, master=None, cnf={}, **kw): - static= [] - self.cnf= cnf - TixWidget.__init__(self, master, 'tixScrolledGrid', static, cnf, kw) diff --git a/Lib/tokenize.py b/Lib/tokenize.py index 911f0f12f9bb7e..4895e94d1dfda7 100644 --- a/Lib/tokenize.py +++ b/Lib/tokenize.py @@ -447,13 +447,8 @@ def tokenize(readline): def _tokenize(rl_gen, encoding): source = b"".join(rl_gen).decode(encoding) - token = None for token in _generate_tokens_from_c_tokenizer(source, extra_tokens=True): yield token - if token is not None: - last_line, _ = token.start - yield TokenInfo(ENDMARKER, '', (last_line + 1, 0), (last_line + 1, 0), '') - def generate_tokens(readline): """Tokenize a source reading Python code as unicode strings. diff --git a/Lib/traceback.py b/Lib/traceback.py index 419f6e81b5e1be..0ea77bfb94612e 100644 --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -658,6 +658,8 @@ class TracebackException: - :attr:`__cause__` A TracebackException of the original *__cause__*. - :attr:`__context__` A TracebackException of the original *__context__*. + - :attr:`exceptions` For exception groups - a list of TracebackException + instances for the nested *exceptions*. ``None`` for other exceptions. - :attr:`__suppress_context__` The *__suppress_context__* value from the original exception. - :attr:`stack` A `StackSummary` representing the traceback. diff --git a/Lib/turtle.py b/Lib/turtle.py index cf111158b7c149..54f0efa4c06224 100644 --- a/Lib/turtle.py +++ b/Lib/turtle.py @@ -127,7 +127,7 @@ 'isvisible', 'left', 'lt', 'onclick', 'ondrag', 'onrelease', 'pd', 'pen', 'pencolor', 'pendown', 'pensize', 'penup', 'pos', 'position', 'pu', 'radians', 'right', 'reset', 'resizemode', 'rt', - 'seth', 'setheading', 'setpos', 'setposition', 'settiltangle', + 'seth', 'setheading', 'setpos', 'setposition', 'setundobuffer', 'setx', 'sety', 'shape', 'shapesize', 'shapetransform', 'shearfactor', 'showturtle', 'speed', 'st', 'stamp', 'teleport', 'tilt', 'tiltangle', 'towards', 'turtlesize', 'undo', 'undobufferentries', 'up', 'width', @@ -2896,33 +2896,6 @@ def shearfactor(self, shear=None): return self._shearfactor self.pen(resizemode="user", shearfactor=shear) - def settiltangle(self, angle): - """Rotate the turtleshape to point in the specified direction - - Argument: angle -- number - - Rotate the turtleshape to point in the direction specified by angle, - regardless of its current tilt-angle. DO NOT change the turtle's - heading (direction of movement). - - Deprecated since Python 3.1 - - Examples (for a Turtle instance named turtle): - >>> turtle.shape("circle") - >>> turtle.shapesize(5,2) - >>> turtle.settiltangle(45) - >>> turtle.stamp() - >>> turtle.fd(50) - >>> turtle.settiltangle(-45) - >>> turtle.stamp() - >>> turtle.fd(50) - """ - warnings._deprecated("turtle.RawTurtle.settiltangle()", - "{name!r} is deprecated since Python 3.1 and scheduled " - "for removal in Python {remove}. Use tiltangle() instead.", - remove=(3, 13)) - self.tiltangle(angle) - def tiltangle(self, angle=None): """Set or return the current tilt-angle. @@ -2935,9 +2908,6 @@ def tiltangle(self, angle=None): between the orientation of the turtleshape and the heading of the turtle (its direction of movement). - (Incorrectly marked as deprecated since Python 3.1, it is really - settiltangle that is deprecated.) - Examples (for a Turtle instance named turtle): >>> turtle.shape("circle") >>> turtle.shapesize(5, 2) diff --git a/Lib/typing.py b/Lib/typing.py index 13f0883e3bfcc6..8c874797d290c0 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -2709,7 +2709,7 @@ def __new__(cls, typename, bases, ns): def NamedTuple(typename, fields=None, /, **kwargs): """Typed version of namedtuple. - Usage in Python versions >= 3.6:: + Usage:: class Employee(NamedTuple): name: str @@ -2726,9 +2726,6 @@ class Employee(NamedTuple): Employee = NamedTuple('Employee', name=str, id=int) - In Python versions <= 3.5 use:: - - Employee = NamedTuple('Employee', [('name', str), ('id', int)]) """ if fields is None: fields = kwargs.items() diff --git a/Lib/unittest/main.py b/Lib/unittest/main.py index 51b81a6c3728bb..03963e0b1b2a04 100644 --- a/Lib/unittest/main.py +++ b/Lib/unittest/main.py @@ -104,16 +104,6 @@ def __init__(self, module='__main__', defaultTest=None, argv=None, self.parseArgs(argv) self.runTests() - def usageExit(self, msg=None): - warnings.warn("TestProgram.usageExit() is deprecated and will be" - " removed in Python 3.13", DeprecationWarning) - if msg: - print(msg) - if self._discovery_parser is None: - self._initArgParsers() - self._print_help() - sys.exit(2) - def _print_help(self, *args, **kwargs): if self.module is None: print(self._main_parser.format_help()) diff --git a/Lib/webbrowser.py b/Lib/webbrowser.py index b86d131f030d80..c1bd667a56e579 100755 --- a/Lib/webbrowser.py +++ b/Lib/webbrowser.py @@ -570,60 +570,10 @@ def open(self, url, new=0, autoraise=True): return True # -# Platform support for MacOS +# Platform support for macOS # if sys.platform == 'darwin': - # Adapted from patch submitted to SourceForge by Steven J. Burr - class MacOSX(BaseBrowser): - """Launcher class for Aqua browsers on Mac OS X - - Optionally specify a browser name on instantiation. Note that this - will not work for Aqua browsers if the user has moved the application - package after installation. - - If no browser is specified, the default browser, as specified in the - Internet System Preferences panel, will be used. - """ - def __init__(self, name): - warnings.warn(f'{self.__class__.__name__} is deprecated in 3.11' - ' use MacOSXOSAScript instead.', DeprecationWarning, stacklevel=2) - self.name = name - - def open(self, url, new=0, autoraise=True): - sys.audit("webbrowser.open", url) - assert "'" not in url - # hack for local urls - if not ':' in url: - url = 'file:'+url - - # new must be 0 or 1 - new = int(bool(new)) - if self.name == "default": - # User called open, open_new or get without a browser parameter - script = 'open location "%s"' % url.replace('"', '%22') # opens in default browser - else: - # User called get and chose a browser - if self.name == "OmniWeb": - toWindow = "" - else: - # Include toWindow parameter of OpenURL command for browsers - # that support it. 0 == new window; -1 == existing - toWindow = "toWindow %d" % (new - 1) - cmd = 'OpenURL "%s"' % url.replace('"', '%22') - script = '''tell application "%s" - activate - %s %s - end tell''' % (self.name, cmd, toWindow) - # Open pipe to AppleScript through osascript command - osapipe = os.popen("osascript", "w") - if osapipe is None: - return False - # Write script to osascript's stdin - osapipe.write(script) - rc = osapipe.close() - return not rc - class MacOSXOSAScript(BaseBrowser): def __init__(self, name='default'): super().__init__(name) diff --git a/Makefile.pre.in b/Makefile.pre.in index b277092f20079d..7ae94ff02cd285 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -2116,7 +2116,6 @@ TESTSUBDIRS= idlelib/idle_test \ test/decimaltestdata \ test/dtracedata \ test/encoded_modules \ - test/imghdrdata \ test/leakers \ test/libregrtest \ test/subprocessdata \ @@ -2210,6 +2209,7 @@ TESTSUBDIRS= idlelib/idle_test \ test/test_zipfile \ test/test_zoneinfo \ test/test_zoneinfo/data \ + test/tkinterdata \ test/tracedmodules \ test/typinganndata \ test/xmltestdata \ diff --git a/Misc/NEWS.d/3.11.0a1.rst b/Misc/NEWS.d/3.11.0a1.rst index 284717a8764bec..ea96206b710a85 100644 --- a/Misc/NEWS.d/3.11.0a1.rst +++ b/Misc/NEWS.d/3.11.0a1.rst @@ -1966,8 +1966,8 @@ A new function ``operator.call`` has been added, such that .. nonce: ofe3ms .. section: Library -:class:`webbrowser.MacOSX` is deprecated and will be removed in Python 3.13. -It is untested and undocumented and also not used by webbrowser itself. +:class:`!webbrowser.MacOSX` is deprecated and will be removed in Python 3.13. +It is untested and undocumented and also not used by :mod:`webbrowser` itself. Patch by Dong-hee Na. .. diff --git a/Misc/NEWS.d/3.11.0a3.rst b/Misc/NEWS.d/3.11.0a3.rst index 426531904993a9..7fdc191c244849 100644 --- a/Misc/NEWS.d/3.11.0a3.rst +++ b/Misc/NEWS.d/3.11.0a3.rst @@ -440,7 +440,7 @@ Added missing kw_only parameter to dataclasses.make_dataclass(). .. nonce: aGyr1I .. section: Library -The :meth:`turtle.RawTurtle.settiltangle` is deprecated since Python 3.1, it +The :meth:`!turtle.RawTurtle.settiltangle` is deprecated since Python 3.1, it now emits a deprecation warning and will be removed in Python 3.13. Use :meth:`turtle.RawTurtle.tiltangle` instead. diff --git a/Misc/NEWS.d/3.11.0a7.rst b/Misc/NEWS.d/3.11.0a7.rst index 8e7ccd4d6771ee..d3e59a2195669f 100644 --- a/Misc/NEWS.d/3.11.0a7.rst +++ b/Misc/NEWS.d/3.11.0a7.rst @@ -989,7 +989,7 @@ references in :ref:`PEP 585 generic aliases `. .. nonce: xnhT4a .. section: Library -Add :exc:`DeprecationWarning` to :class:`LegacyInterpolation`, deprecated in +Add :exc:`DeprecationWarning` to :class:`!LegacyInterpolation`, deprecated in the docstring since Python 3.2. Will be removed in Python 3.13. Use :class:`BasicInterpolation` or :class:`ExtendedInterpolation` instead. @@ -1038,7 +1038,7 @@ Add optional parameter *dir_fd* in :func:`shutil.rmtree`. .. nonce: AixHW7 .. section: Library -:meth:`~unittest.TestProgram.usageExit` is marked deprecated, to be removed +:meth:`~!unittest.TestProgram.usageExit` is marked deprecated, to be removed in 3.13. .. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-05-18-12-48-39.gh-issue-89091.FDzRcW.rst b/Misc/NEWS.d/next/Core and Builtins/2023-05-18-12-48-39.gh-issue-89091.FDzRcW.rst new file mode 100644 index 00000000000000..084ea708997ef3 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-05-18-12-48-39.gh-issue-89091.FDzRcW.rst @@ -0,0 +1 @@ +Raise :exc:`RuntimeWarning` for unawaited async generator methods like :meth:`~agen.asend`, :meth:`~agen.athrow` and :meth:`~agen.aclose`. Patch by Kumar Aditya. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-05-25-21-40-39.gh-issue-104955.LZx7jf.rst b/Misc/NEWS.d/next/Core and Builtins/2023-05-25-21-40-39.gh-issue-104955.LZx7jf.rst new file mode 100644 index 00000000000000..9fccf2a41ffb6f --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-05-25-21-40-39.gh-issue-104955.LZx7jf.rst @@ -0,0 +1,2 @@ +Fix signature for the new :meth:`~object.__release_buffer__` slot. Patch by Jelle +Zijlstra. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-05-26-14-09-47.gh-issue-104972.El2UjE.rst b/Misc/NEWS.d/next/Core and Builtins/2023-05-26-14-09-47.gh-issue-104972.El2UjE.rst new file mode 100644 index 00000000000000..05d50c108c7b77 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-05-26-14-09-47.gh-issue-104972.El2UjE.rst @@ -0,0 +1,2 @@ +Ensure that the ``line`` attribute in :class:`tokenize.TokenInfo` objects in +the :mod:`tokenize` module are always correct. Patch by Pablo Galindo diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-05-26-15-16-11.gh-issue-104976.6dLitD.rst b/Misc/NEWS.d/next/Core and Builtins/2023-05-26-15-16-11.gh-issue-104976.6dLitD.rst new file mode 100644 index 00000000000000..377e8e76362687 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-05-26-15-16-11.gh-issue-104976.6dLitD.rst @@ -0,0 +1,3 @@ +Ensure that trailing ``DEDENT`` :class:`tokenize.TokenInfo` objects emitted +by the :mod:`tokenize` module are reported as in Python 3.11. Patch by Pablo +Galindo diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-05-27-16-23-16.gh-issue-105017.KQrsC0.rst b/Misc/NEWS.d/next/Core and Builtins/2023-05-27-16-23-16.gh-issue-105017.KQrsC0.rst new file mode 100644 index 00000000000000..d41a2169ccb3de --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-05-27-16-23-16.gh-issue-105017.KQrsC0.rst @@ -0,0 +1 @@ +Do not include an additional final ``NL`` token when parsing files having CRLF lines. Patch by Marta Gómez. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-05-27-16-57-11.gh-issue-105013.IsDgDY.rst b/Misc/NEWS.d/next/Core and Builtins/2023-05-27-16-57-11.gh-issue-105013.IsDgDY.rst new file mode 100644 index 00000000000000..a9917c2849982a --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-05-27-16-57-11.gh-issue-105013.IsDgDY.rst @@ -0,0 +1,2 @@ +Fix handling of multiline parenthesized lambdas in +:func:`inspect.getsource`. Patch by Pablo Galindo diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-05-27-21-50-48.gh-issue-105017.4sDyDV.rst b/Misc/NEWS.d/next/Core and Builtins/2023-05-27-21-50-48.gh-issue-105017.4sDyDV.rst new file mode 100644 index 00000000000000..02d653c2d658eb --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-05-27-21-50-48.gh-issue-105017.4sDyDV.rst @@ -0,0 +1 @@ +Show CRLF lines in the tokenize string attribute in both NL and NEWLINE tokens. Patch by Marta Gómez. diff --git a/Misc/NEWS.d/next/Documentation/2023-05-25-22-34-31.gh-issue-104943.J2v1Pc.rst b/Misc/NEWS.d/next/Documentation/2023-05-25-22-34-31.gh-issue-104943.J2v1Pc.rst new file mode 100644 index 00000000000000..bc4d03b8e95f86 --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2023-05-25-22-34-31.gh-issue-104943.J2v1Pc.rst @@ -0,0 +1 @@ +Remove mentions of old Python versions in :class:`typing.NamedTuple`. diff --git a/Misc/NEWS.d/next/Documentation/2023-05-28-21-01-00.gh-issue-89455.qAKRrA.rst b/Misc/NEWS.d/next/Documentation/2023-05-28-21-01-00.gh-issue-89455.qAKRrA.rst new file mode 100644 index 00000000000000..fdfa4357f001b5 --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2023-05-28-21-01-00.gh-issue-89455.qAKRrA.rst @@ -0,0 +1,3 @@ +Add missing documentation for the ``max_group_depth`` and ``max_group_width`` +parameters and the ``exceptions`` attribute of the +:class:`traceback.TracebackException` class. diff --git a/Misc/NEWS.d/next/Library/2023-02-18-22-55-48.gh-issue-102024.RUmg_D.rst b/Misc/NEWS.d/next/Library/2023-02-18-22-55-48.gh-issue-102024.RUmg_D.rst new file mode 100644 index 00000000000000..bb9e28e06c5554 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-02-18-22-55-48.gh-issue-102024.RUmg_D.rst @@ -0,0 +1 @@ +Reduce calls of ``_idle_semaphore.release()`` in :func:`concurrent.futures.thread._worker`. diff --git a/Misc/NEWS.d/next/Library/2023-03-12-03-37-03.gh-issue-77609.aOQttm.rst b/Misc/NEWS.d/next/Library/2023-03-12-03-37-03.gh-issue-77609.aOQttm.rst new file mode 100644 index 00000000000000..35e61088de58a6 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-03-12-03-37-03.gh-issue-77609.aOQttm.rst @@ -0,0 +1,2 @@ +Add *follow_symlinks* argument to :meth:`pathlib.Path.glob` and +:meth:`~pathlib.Path.rglob`, defaulting to false. diff --git a/Misc/NEWS.d/next/Library/2023-05-23-02-13-11.gh-issue-104773.JNiEjv.rst b/Misc/NEWS.d/next/Library/2023-05-23-02-13-11.gh-issue-104773.JNiEjv.rst new file mode 100644 index 00000000000000..213b6f5b764258 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-05-23-02-13-11.gh-issue-104773.JNiEjv.rst @@ -0,0 +1,2 @@ +:pep:`594`: Remove the :mod:`!imghdr` module, deprecated in Python 3.11. +Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/Library/2023-05-23-18-31-49.gh-issue-104799.MJYOw6.rst b/Misc/NEWS.d/next/Library/2023-05-23-18-31-49.gh-issue-104799.MJYOw6.rst new file mode 100644 index 00000000000000..614918d7572969 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-05-23-18-31-49.gh-issue-104799.MJYOw6.rst @@ -0,0 +1,4 @@ +Adjust the location of the (see :pep:`695`) ``type_params`` field on +:class:`ast.ClassDef`, :class:`ast.AsyncFunctionDef`, and +:class:`ast.FunctionDef` to better preserve backward compatibility. Patch by +Jelle Zijlstra diff --git a/Misc/NEWS.d/next/Library/2023-05-23-21-25-54.gh-issue-104804.78fiE6.rst b/Misc/NEWS.d/next/Library/2023-05-23-21-25-54.gh-issue-104804.78fiE6.rst new file mode 100644 index 00000000000000..78409cf7cbc9dd --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-05-23-21-25-54.gh-issue-104804.78fiE6.rst @@ -0,0 +1,2 @@ +Remove the untested and undocumented :mod:`webbrowser` :class:`!MacOSX` class, deprecated in Python 3.11. +Patch by Hugo van Kemenade. diff --git a/Misc/NEWS.d/next/Library/2023-05-24-17-22-56.gh-issue-75552._QlrpQ.rst b/Misc/NEWS.d/next/Library/2023-05-24-17-22-56.gh-issue-75552._QlrpQ.rst new file mode 100644 index 00000000000000..e70712af97944f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-05-24-17-22-56.gh-issue-75552._QlrpQ.rst @@ -0,0 +1 @@ +Removed the ``tkinter.tix`` module, deprecated since Python 3.6. diff --git a/Misc/NEWS.d/next/Library/2023-05-24-19-48-16.gh-issue-104876.Z00Qnk.rst b/Misc/NEWS.d/next/Library/2023-05-24-19-48-16.gh-issue-104876.Z00Qnk.rst new file mode 100644 index 00000000000000..ce1f5606415085 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-05-24-19-48-16.gh-issue-104876.Z00Qnk.rst @@ -0,0 +1,3 @@ +Remove the :meth:`!turtle.RawTurtle.settiltangle` method, deprecated in docs +since Python 3.1 and with a deprecation warning since Python 3.11. Patch by +Hugo van Kemenade. diff --git a/Misc/NEWS.d/next/Library/2023-05-24-21-30-40.gh-issue-104886.8TuV-_.rst b/Misc/NEWS.d/next/Library/2023-05-24-21-30-40.gh-issue-104886.8TuV-_.rst new file mode 100644 index 00000000000000..2f6796bed7b17c --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-05-24-21-30-40.gh-issue-104886.8TuV-_.rst @@ -0,0 +1,3 @@ +Remove the undocumented :class:`!configparser.LegacyInterpolation` class, +deprecated in the docstring since Python 3.2, and with a deprecation warning +since Python 3.11. Patch by Hugo van Kemenade. diff --git a/Misc/NEWS.d/next/Library/2023-05-25-22-54-20.gh-issue-104947.hi6TUr.rst b/Misc/NEWS.d/next/Library/2023-05-25-22-54-20.gh-issue-104947.hi6TUr.rst new file mode 100644 index 00000000000000..4af73d73d2a717 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-05-25-22-54-20.gh-issue-104947.hi6TUr.rst @@ -0,0 +1,2 @@ +Make comparisons between :class:`pathlib.PureWindowsPath` objects consistent +across Windows and Posix to match 3.11 behavior. diff --git a/Misc/NEWS.d/next/Library/2023-05-25-23-34-54.gh-issue-103631.x5Urye.rst b/Misc/NEWS.d/next/Library/2023-05-25-23-34-54.gh-issue-103631.x5Urye.rst new file mode 100644 index 00000000000000..d1eb2d3ed6191f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-05-25-23-34-54.gh-issue-103631.x5Urye.rst @@ -0,0 +1,2 @@ +Fix ``pathlib.PurePosixPath(pathlib.PureWindowsPath(...))`` not converting +path separators to restore 3.11 compatible behavior. diff --git a/Misc/NEWS.d/next/Library/2023-05-26-01-31-30.gh-issue-101588.RaqxFy.rst b/Misc/NEWS.d/next/Library/2023-05-26-01-31-30.gh-issue-101588.RaqxFy.rst new file mode 100644 index 00000000000000..07e3dc468f7d9a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-05-26-01-31-30.gh-issue-101588.RaqxFy.rst @@ -0,0 +1 @@ +Deprecate undocumented copy/deepcopy/pickle support for itertools. diff --git a/Misc/NEWS.d/next/Library/2023-05-26-21-33-24.gh-issue-104992.dbq9WK.rst b/Misc/NEWS.d/next/Library/2023-05-26-21-33-24.gh-issue-104992.dbq9WK.rst new file mode 100644 index 00000000000000..d72646a6541f19 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-05-26-21-33-24.gh-issue-104992.dbq9WK.rst @@ -0,0 +1,2 @@ +Remove the untested and undocumented :meth:`!unittest.TestProgram.usageExit` +method, deprecated in Python 3.11. Patch by Hugo van Kemenade. diff --git a/Misc/NEWS.d/next/Windows/2023-05-23-19-26-28.gh-issue-104803.gqxYml.rst b/Misc/NEWS.d/next/Windows/2023-05-23-19-26-28.gh-issue-104803.gqxYml.rst new file mode 100644 index 00000000000000..d2242c76189970 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2023-05-23-19-26-28.gh-issue-104803.gqxYml.rst @@ -0,0 +1,3 @@ +Add :func:`os.path.isdevdrive` to detect whether a path is on a Windows Dev +Drive. Returns ``False`` on platforms that do not support Dev Drive, and is +absent on non-Windows platforms. diff --git a/Misc/NEWS.d/next/Windows/2023-05-29-17-09-31.gh-issue-103646.U8oGQx.rst b/Misc/NEWS.d/next/Windows/2023-05-29-17-09-31.gh-issue-103646.U8oGQx.rst new file mode 100644 index 00000000000000..71c1e7c6594cbf --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2023-05-29-17-09-31.gh-issue-103646.U8oGQx.rst @@ -0,0 +1,5 @@ +When installed from the Microsoft Store, ``pip`` no longer defaults to +per-user installs. However, as the install directory is unwritable, it +should automatically decide to do a per-user install anyway. This should +resolve issues when ``pip`` is passed an option that conflicts with +``--user``. diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 7e33558dba3e32..08ce172c6a8fcb 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -122,7 +122,6 @@ typedef enum { fut_state prefix##_state; \ int prefix##_log_tb; \ int prefix##_blocking; \ - PyObject *dict; \ PyObject *prefix##_weakreflist; \ PyObject *prefix##_cancelled_exc; @@ -489,7 +488,6 @@ future_init(FutureObj *fut, PyObject *loop) PyObject *res; int is_true; - // Same to FutureObj_clear() but not clearing fut->dict Py_CLEAR(fut->fut_loop); Py_CLEAR(fut->fut_callback0); Py_CLEAR(fut->fut_context0); @@ -814,7 +812,7 @@ FutureObj_clear(FutureObj *fut) Py_CLEAR(fut->fut_source_tb); Py_CLEAR(fut->fut_cancel_msg); Py_CLEAR(fut->fut_cancelled_exc); - Py_CLEAR(fut->dict); + _PyObject_ClearManagedDict((PyObject *)fut); return 0; } @@ -832,7 +830,7 @@ FutureObj_traverse(FutureObj *fut, visitproc visit, void *arg) Py_VISIT(fut->fut_source_tb); Py_VISIT(fut->fut_cancel_msg); Py_VISIT(fut->fut_cancelled_exc); - Py_VISIT(fut->dict); + _PyObject_VisitManagedDict((PyObject *)fut, visit, arg); return 0; } @@ -1502,7 +1500,6 @@ static PyMethodDef FutureType_methods[] = { static PyMemberDef FutureType_members[] = { {"__weaklistoffset__", T_PYSSIZET, offsetof(FutureObj, fut_weakreflist), READONLY}, - {"__dictoffset__", T_PYSSIZET, offsetof(FutureObj, dict), READONLY}, {NULL}, }; @@ -1551,7 +1548,7 @@ static PyType_Spec Future_spec = { .name = "_asyncio.Future", .basicsize = sizeof(FutureObj), .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | - Py_TPFLAGS_IMMUTABLETYPE), + Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_MANAGED_DICT), .slots = Future_slots, }; @@ -2183,7 +2180,7 @@ TaskObj_traverse(TaskObj *task, visitproc visit, void *arg) Py_VISIT(fut->fut_source_tb); Py_VISIT(fut->fut_cancel_msg); Py_VISIT(fut->fut_cancelled_exc); - Py_VISIT(fut->dict); + _PyObject_VisitManagedDict((PyObject *)fut, visit, arg); return 0; } @@ -2640,7 +2637,6 @@ static PyMethodDef TaskType_methods[] = { static PyMemberDef TaskType_members[] = { {"__weaklistoffset__", T_PYSSIZET, offsetof(TaskObj, task_weakreflist), READONLY}, - {"__dictoffset__", T_PYSSIZET, offsetof(TaskObj, dict), READONLY}, {NULL}, }; @@ -2677,7 +2673,7 @@ static PyType_Spec Task_spec = { .name = "_asyncio.Task", .basicsize = sizeof(TaskObj), .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | - Py_TPFLAGS_IMMUTABLETYPE), + Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_MANAGED_DICT), .slots = Task_slots, }; diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h index 8b0550d832fc0a..3312bd667694dd 100644 --- a/Modules/clinic/posixmodule.c.h +++ b/Modules/clinic/posixmodule.c.h @@ -1715,6 +1715,70 @@ os_listmounts(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec #if defined(MS_WINDOWS) +PyDoc_STRVAR(os__path_isdevdrive__doc__, +"_path_isdevdrive($module, /, path)\n" +"--\n" +"\n" +"Determines whether the specified path is on a Windows Dev Drive."); + +#define OS__PATH_ISDEVDRIVE_METHODDEF \ + {"_path_isdevdrive", _PyCFunction_CAST(os__path_isdevdrive), METH_FASTCALL|METH_KEYWORDS, os__path_isdevdrive__doc__}, + +static PyObject * +os__path_isdevdrive_impl(PyObject *module, path_t *path); + +static PyObject * +os__path_isdevdrive(PyObject *module, 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 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(path), }, + }; + #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[] = {"path", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "_path_isdevdrive", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + path_t path = PATH_T_INITIALIZE("_path_isdevdrive", "path", 0, 0); + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + if (!args) { + goto exit; + } + if (!path_converter(args[0], &path)) { + goto exit; + } + return_value = os__path_isdevdrive_impl(module, &path); + +exit: + /* Cleanup for path */ + path_cleanup(&path); + + return return_value; +} + +#endif /* defined(MS_WINDOWS) */ + +#if defined(MS_WINDOWS) + PyDoc_STRVAR(os__getfullpathname__doc__, "_getfullpathname($module, path, /)\n" "--\n" @@ -11379,6 +11443,10 @@ os_waitstatus_to_exitcode(PyObject *module, PyObject *const *args, Py_ssize_t na #define OS_LISTMOUNTS_METHODDEF #endif /* !defined(OS_LISTMOUNTS_METHODDEF) */ +#ifndef OS__PATH_ISDEVDRIVE_METHODDEF + #define OS__PATH_ISDEVDRIVE_METHODDEF +#endif /* !defined(OS__PATH_ISDEVDRIVE_METHODDEF) */ + #ifndef OS__GETFULLPATHNAME_METHODDEF #define OS__GETFULLPATHNAME_METHODDEF #endif /* !defined(OS__GETFULLPATHNAME_METHODDEF) */ @@ -11922,4 +11990,4 @@ os_waitstatus_to_exitcode(PyObject *module, PyObject *const *args, Py_ssize_t na #ifndef OS_WAITSTATUS_TO_EXITCODE_METHODDEF #define OS_WAITSTATUS_TO_EXITCODE_METHODDEF #endif /* !defined(OS_WAITSTATUS_TO_EXITCODE_METHODDEF) */ -/*[clinic end generated code: output=47750e0e29c8d707 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=9d8b0d6717c9af54 input=a9049054013a1b77]*/ diff --git a/Modules/itertoolsmodule.c b/Modules/itertoolsmodule.c index 555eab09935e9e..4a6d1314b3864e 100644 --- a/Modules/itertoolsmodule.c +++ b/Modules/itertoolsmodule.c @@ -93,6 +93,16 @@ class itertools.pairwise "pairwiseobject *" "clinic_state()->pairwise_type" #undef clinic_state_by_cls #undef clinic_state +/* Deprecation of pickle support: GH-101588 *********************************/ + +#define ITERTOOL_PICKLE_DEPRECATION \ + if (PyErr_WarnEx( \ + PyExc_DeprecationWarning, \ + "Itertool pickle/copy/deepcopy support " \ + "will be removed in a Python 3.14.", 1) < 0) { \ + return NULL; \ + } + /* batched object ************************************************************/ /* Note: The built-in zip() function includes a "strict" argument @@ -506,6 +516,7 @@ groupby_reduce(groupbyobject *lz, PyObject *Py_UNUSED(ignored)) /* reduce as a 'new' call with an optional 'setstate' if groupby * has started */ + ITERTOOL_PICKLE_DEPRECATION; PyObject *value; if (lz->tgtkey && lz->currkey && lz->currvalue) value = Py_BuildValue("O(OO)(OOO)", Py_TYPE(lz), @@ -522,6 +533,7 @@ PyDoc_STRVAR(reduce_doc, "Return state information for pickling."); static PyObject * groupby_setstate(groupbyobject *lz, PyObject *state) { + ITERTOOL_PICKLE_DEPRECATION; PyObject *currkey, *currvalue, *tgtkey; if (!PyTuple_Check(state)) { PyErr_SetString(PyExc_TypeError, "state is not a tuple"); @@ -660,6 +672,7 @@ _grouper_next(_grouperobject *igo) static PyObject * _grouper_reduce(_grouperobject *lz, PyObject *Py_UNUSED(ignored)) { + ITERTOOL_PICKLE_DEPRECATION; if (((groupbyobject *)lz->parent)->currgrouper != lz) { return Py_BuildValue("N(())", _PyEval_GetBuiltin(&_Py_ID(iter))); } @@ -828,6 +841,7 @@ teedataobject_dealloc(teedataobject *tdo) static PyObject * teedataobject_reduce(teedataobject *tdo, PyObject *Py_UNUSED(ignored)) { + ITERTOOL_PICKLE_DEPRECATION; int i; /* create a temporary list of already iterated values */ PyObject *values = PyList_New(tdo->numread); @@ -1041,12 +1055,14 @@ tee_dealloc(teeobject *to) static PyObject * tee_reduce(teeobject *to, PyObject *Py_UNUSED(ignored)) { + ITERTOOL_PICKLE_DEPRECATION; return Py_BuildValue("O(())(Oi)", Py_TYPE(to), to->dataobj, to->index); } static PyObject * tee_setstate(teeobject *to, PyObject *state) { + ITERTOOL_PICKLE_DEPRECATION; teedataobject *tdo; int index; if (!PyTuple_Check(state)) { @@ -1275,6 +1291,7 @@ cycle_next(cycleobject *lz) static PyObject * cycle_reduce(cycleobject *lz, PyObject *Py_UNUSED(ignored)) { + ITERTOOL_PICKLE_DEPRECATION; /* Create a new cycle with the iterator tuple, then set the saved state */ if (lz->it == NULL) { PyObject *it = PyObject_GetIter(lz->saved); @@ -1298,6 +1315,7 @@ cycle_reduce(cycleobject *lz, PyObject *Py_UNUSED(ignored)) static PyObject * cycle_setstate(cycleobject *lz, PyObject *state) { + ITERTOOL_PICKLE_DEPRECATION; PyObject *saved=NULL; int firstpass; if (!PyTuple_Check(state)) { @@ -1446,12 +1464,14 @@ dropwhile_next(dropwhileobject *lz) static PyObject * dropwhile_reduce(dropwhileobject *lz, PyObject *Py_UNUSED(ignored)) { + ITERTOOL_PICKLE_DEPRECATION; return Py_BuildValue("O(OO)l", Py_TYPE(lz), lz->func, lz->it, lz->start); } static PyObject * dropwhile_setstate(dropwhileobject *lz, PyObject *state) { + ITERTOOL_PICKLE_DEPRECATION; int start = PyObject_IsTrue(state); if (start < 0) return NULL; @@ -1584,12 +1604,14 @@ takewhile_next(takewhileobject *lz) static PyObject * takewhile_reduce(takewhileobject *lz, PyObject *Py_UNUSED(ignored)) { + ITERTOOL_PICKLE_DEPRECATION; return Py_BuildValue("O(OO)l", Py_TYPE(lz), lz->func, lz->it, lz->stop); } static PyObject * takewhile_reduce_setstate(takewhileobject *lz, PyObject *state) { + ITERTOOL_PICKLE_DEPRECATION; int stop = PyObject_IsTrue(state); if (stop < 0) @@ -1786,6 +1808,7 @@ islice_next(isliceobject *lz) static PyObject * islice_reduce(isliceobject *lz, PyObject *Py_UNUSED(ignored)) { + ITERTOOL_PICKLE_DEPRECATION; /* When unpickled, generate a new object with the same bounds, * then 'setstate' with the next and count */ @@ -1818,6 +1841,7 @@ islice_reduce(isliceobject *lz, PyObject *Py_UNUSED(ignored)) static PyObject * islice_setstate(isliceobject *lz, PyObject *state) { + ITERTOOL_PICKLE_DEPRECATION; Py_ssize_t cnt = PyLong_AsSsize_t(state); if (cnt == -1 && PyErr_Occurred()) @@ -1953,6 +1977,7 @@ starmap_next(starmapobject *lz) static PyObject * starmap_reduce(starmapobject *lz, PyObject *Py_UNUSED(ignored)) { + ITERTOOL_PICKLE_DEPRECATION; /* Just pickle the iterator */ return Py_BuildValue("O(OO)", Py_TYPE(lz), lz->func, lz->it); } @@ -2109,6 +2134,7 @@ chain_next(chainobject *lz) static PyObject * chain_reduce(chainobject *lz, PyObject *Py_UNUSED(ignored)) { + ITERTOOL_PICKLE_DEPRECATION; if (lz->source) { /* we can't pickle function objects (itertools.from_iterable) so * we must use setstate to replace the iterable. One day we @@ -2128,6 +2154,7 @@ chain_reduce(chainobject *lz, PyObject *Py_UNUSED(ignored)) static PyObject * chain_setstate(chainobject *lz, PyObject *state) { + ITERTOOL_PICKLE_DEPRECATION; PyObject *source, *active=NULL; if (!PyTuple_Check(state)) { @@ -2403,6 +2430,7 @@ product_next(productobject *lz) static PyObject * product_reduce(productobject *lz, PyObject *Py_UNUSED(ignored)) { + ITERTOOL_PICKLE_DEPRECATION; if (lz->stopped) { return Py_BuildValue("O(())", Py_TYPE(lz)); } else if (lz->result == NULL) { @@ -2433,6 +2461,7 @@ product_reduce(productobject *lz, PyObject *Py_UNUSED(ignored)) static PyObject * product_setstate(productobject *lz, PyObject *state) { + ITERTOOL_PICKLE_DEPRECATION; PyObject *result; Py_ssize_t n, i; @@ -2711,6 +2740,7 @@ combinations_next(combinationsobject *co) static PyObject * combinations_reduce(combinationsobject *lz, PyObject *Py_UNUSED(ignored)) { + ITERTOOL_PICKLE_DEPRECATION; if (lz->result == NULL) { return Py_BuildValue("O(On)", Py_TYPE(lz), lz->pool, lz->r); } else if (lz->stopped) { @@ -2740,6 +2770,7 @@ combinations_reduce(combinationsobject *lz, PyObject *Py_UNUSED(ignored)) static PyObject * combinations_setstate(combinationsobject *lz, PyObject *state) { + ITERTOOL_PICKLE_DEPRECATION; PyObject *result; Py_ssize_t i; Py_ssize_t n = PyTuple_GET_SIZE(lz->pool); @@ -3019,6 +3050,7 @@ cwr_next(cwrobject *co) static PyObject * cwr_reduce(cwrobject *lz, PyObject *Py_UNUSED(ignored)) { + ITERTOOL_PICKLE_DEPRECATION; if (lz->result == NULL) { return Py_BuildValue("O(On)", Py_TYPE(lz), lz->pool, lz->r); } else if (lz->stopped) { @@ -3047,6 +3079,7 @@ cwr_reduce(cwrobject *lz, PyObject *Py_UNUSED(ignored)) static PyObject * cwr_setstate(cwrobject *lz, PyObject *state) { + ITERTOOL_PICKLE_DEPRECATION; PyObject *result; Py_ssize_t n, i; @@ -3354,6 +3387,7 @@ permutations_next(permutationsobject *po) static PyObject * permutations_reduce(permutationsobject *po, PyObject *Py_UNUSED(ignored)) { + ITERTOOL_PICKLE_DEPRECATION; if (po->result == NULL) { return Py_BuildValue("O(On)", Py_TYPE(po), po->pool, po->r); } else if (po->stopped) { @@ -3396,6 +3430,7 @@ permutations_reduce(permutationsobject *po, PyObject *Py_UNUSED(ignored)) static PyObject * permutations_setstate(permutationsobject *po, PyObject *state) { + ITERTOOL_PICKLE_DEPRECATION; PyObject *indices, *cycles, *result; Py_ssize_t n, i; @@ -3593,6 +3628,7 @@ accumulate_next(accumulateobject *lz) static PyObject * accumulate_reduce(accumulateobject *lz, PyObject *Py_UNUSED(ignored)) { + ITERTOOL_PICKLE_DEPRECATION; itertools_state *state = lz->state; if (lz->initial != Py_None) { @@ -3628,6 +3664,7 @@ accumulate_reduce(accumulateobject *lz, PyObject *Py_UNUSED(ignored)) static PyObject * accumulate_setstate(accumulateobject *lz, PyObject *state) { + ITERTOOL_PICKLE_DEPRECATION; Py_INCREF(state); Py_XSETREF(lz->total, state); Py_RETURN_NONE; @@ -3776,6 +3813,7 @@ compress_next(compressobject *lz) static PyObject * compress_reduce(compressobject *lz, PyObject *Py_UNUSED(ignored)) { + ITERTOOL_PICKLE_DEPRECATION; return Py_BuildValue("O(OO)", Py_TYPE(lz), lz->data, lz->selectors); } @@ -3908,6 +3946,7 @@ filterfalse_next(filterfalseobject *lz) static PyObject * filterfalse_reduce(filterfalseobject *lz, PyObject *Py_UNUSED(ignored)) { + ITERTOOL_PICKLE_DEPRECATION; return Py_BuildValue("O(OO)", Py_TYPE(lz), lz->func, lz->it); } @@ -4135,6 +4174,7 @@ count_repr(countobject *lz) static PyObject * count_reduce(countobject *lz, PyObject *Py_UNUSED(ignored)) { + ITERTOOL_PICKLE_DEPRECATION; if (lz->cnt == PY_SSIZE_T_MAX) return Py_BuildValue("O(OO)", Py_TYPE(lz), lz->long_cnt, lz->long_step); return Py_BuildValue("O(n)", Py_TYPE(lz), lz->cnt); @@ -4258,6 +4298,7 @@ PyDoc_STRVAR(length_hint_doc, "Private method returning an estimate of len(list( static PyObject * repeat_reduce(repeatobject *ro, PyObject *Py_UNUSED(ignored)) { + ITERTOOL_PICKLE_DEPRECATION; /* unpickle this so that a new repeat iterator is constructed with an * object, then call __setstate__ on it to set cnt */ @@ -4478,7 +4519,7 @@ zip_longest_next(ziplongestobject *lz) static PyObject * zip_longest_reduce(ziplongestobject *lz, PyObject *Py_UNUSED(ignored)) { - + ITERTOOL_PICKLE_DEPRECATION; /* Create a new tuple with empty sequences where appropriate to pickle. * Then use setstate to set the fillvalue */ @@ -4505,6 +4546,7 @@ zip_longest_reduce(ziplongestobject *lz, PyObject *Py_UNUSED(ignored)) static PyObject * zip_longest_setstate(ziplongestobject *lz, PyObject *state) { + ITERTOOL_PICKLE_DEPRECATION; Py_INCREF(state); Py_XSETREF(lz->fillvalue, state); Py_RETURN_NONE; diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 294e9a2ca6e8fc..8a0c1608ab099c 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -4534,6 +4534,95 @@ os_listmounts_impl(PyObject *module, path_t *volume) } +/*[clinic input] +os._path_isdevdrive + + path: path_t + +Determines whether the specified path is on a Windows Dev Drive. + +[clinic start generated code]*/ + +static PyObject * +os__path_isdevdrive_impl(PyObject *module, path_t *path) +/*[clinic end generated code: output=1f437ea6677433a2 input=ee83e4996a48e23d]*/ +{ +#ifndef PERSISTENT_VOLUME_STATE_DEV_VOLUME + /* This flag will be documented at + https://learn.microsoft.com/windows-hardware/drivers/ddi/ntifs/ns-ntifs-_file_fs_persistent_volume_information + after release, and will be available in the latest WinSDK. + We include the flag to avoid a specific version dependency + on the latest WinSDK. */ + const int PERSISTENT_VOLUME_STATE_DEV_VOLUME = 0x00002000; +#endif + int err = 0; + PyObject *r = NULL; + wchar_t volume[MAX_PATH]; + + Py_BEGIN_ALLOW_THREADS + if (!GetVolumePathNameW(path->wide, volume, MAX_PATH)) { + /* invalid path of some kind */ + /* Note that this also includes the case where a volume is mounted + in a path longer than 260 characters. This is likely to be rare + and problematic for other reasons, so a (soft) failure in this + check seems okay. */ + err = GetLastError(); + } else if (GetDriveTypeW(volume) != DRIVE_FIXED) { + /* only care about local dev drives */ + r = Py_False; + } else { + HANDLE hVolume = CreateFileW( + volume, + FILE_READ_ATTRIBUTES, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, + NULL + ); + if (hVolume == INVALID_HANDLE_VALUE) { + err = GetLastError(); + } else { + FILE_FS_PERSISTENT_VOLUME_INFORMATION volumeState = {0}; + volumeState.Version = 1; + volumeState.FlagMask = PERSISTENT_VOLUME_STATE_DEV_VOLUME; + if (!DeviceIoControl( + hVolume, + FSCTL_QUERY_PERSISTENT_VOLUME_STATE, + &volumeState, + sizeof(volumeState), + &volumeState, + sizeof(volumeState), + NULL, + NULL + )) { + err = GetLastError(); + } + CloseHandle(hVolume); + if (err == ERROR_INVALID_PARAMETER) { + /* not supported on this platform */ + r = Py_False; + } else if (!err) { + r = (volumeState.VolumeFlags & PERSISTENT_VOLUME_STATE_DEV_VOLUME) + ? Py_True : Py_False; + } + } + } + Py_END_ALLOW_THREADS + + if (err) { + PyErr_SetFromWindowsErr(err); + return NULL; + } + + if (r) { + return Py_NewRef(r); + } + + return NULL; +} + + int _PyOS_getfullpathname(const wchar_t *path, wchar_t **abspath_p) { @@ -15805,6 +15894,7 @@ static PyMethodDef posix_methods[] = { OS_SETNS_METHODDEF OS_UNSHARE_METHODDEF + OS__PATH_ISDEVDRIVE_METHODDEF OS__PATH_ISDIR_METHODDEF OS__PATH_ISFILE_METHODDEF OS__PATH_ISLINK_METHODDEF diff --git a/Objects/genobject.c b/Objects/genobject.c index 1abfc83ab678ef..b40cf41a60027d 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -1735,6 +1735,10 @@ async_gen_unwrap_value(PyAsyncGenObject *gen, PyObject *result) static void async_gen_asend_dealloc(PyAsyncGenASend *o) { + if (PyObject_CallFinalizerFromDealloc((PyObject *)o)) { + return; + } + _PyObject_GC_UNTRACK((PyObject *)o); Py_CLEAR(o->ags_gen); Py_CLEAR(o->ags_sendval); @@ -1839,6 +1843,13 @@ async_gen_asend_close(PyAsyncGenASend *o, PyObject *args) Py_RETURN_NONE; } +static void +async_gen_asend_finalize(PyAsyncGenASend *o) +{ + if (o->ags_state == AWAITABLE_STATE_INIT) { + _PyErr_WarnUnawaitedAgenMethod(o->ags_gen, &_Py_ID(asend)); + } +} static PyMethodDef async_gen_asend_methods[] = { {"send", (PyCFunction)async_gen_asend_send, METH_O, send_doc}, @@ -1896,6 +1907,7 @@ PyTypeObject _PyAsyncGenASend_Type = { 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ + .tp_finalize = (destructor)async_gen_asend_finalize, }; @@ -2053,6 +2065,10 @@ _PyAsyncGenValueWrapperNew(PyThreadState *tstate, PyObject *val) static void async_gen_athrow_dealloc(PyAsyncGenAThrow *o) { + if (PyObject_CallFinalizerFromDealloc((PyObject *)o)) { + return; + } + _PyObject_GC_UNTRACK((PyObject *)o); Py_CLEAR(o->agt_gen); Py_CLEAR(o->agt_args); @@ -2256,6 +2272,15 @@ async_gen_athrow_close(PyAsyncGenAThrow *o, PyObject *args) } +static void +async_gen_athrow_finalize(PyAsyncGenAThrow *o) +{ + if (o->agt_state == AWAITABLE_STATE_INIT) { + PyObject *method = o->agt_args ? &_Py_ID(athrow) : &_Py_ID(aclose); + _PyErr_WarnUnawaitedAgenMethod(o->agt_gen, method); + } +} + static PyMethodDef async_gen_athrow_methods[] = { {"send", (PyCFunction)async_gen_athrow_send, METH_O, send_doc}, {"throw", _PyCFunction_CAST(async_gen_athrow_throw), @@ -2313,6 +2338,7 @@ PyTypeObject _PyAsyncGenAThrow_Type = { 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ + .tp_finalize = (destructor)async_gen_athrow_finalize, }; diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 2fbcafe91aadc6..0a2e1b1d3c1f78 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -9428,7 +9428,7 @@ static pytype_slotdef slotdefs[] = { "__buffer__($self, flags, /)\n--\n\n" "Return a buffer object that exposes the underlying memory of the object."), BUFSLOT(__release_buffer__, bf_releasebuffer, slot_bf_releasebuffer, wrap_releasebuffer, - "__release_buffer__($self, /)\n--\n\n" + "__release_buffer__($self, buffer, /)\n--\n\n" "Release the buffer object that exposes the underlying memory of the object."), AMSLOT(__await__, am_await, slot_am_await, wrap_unaryfunc, diff --git a/Objects/typevarobject.c b/Objects/typevarobject.c index 6aa0d8a3bc53be..0b7d84c706d94e 100644 --- a/Objects/typevarobject.c +++ b/Objects/typevarobject.c @@ -443,45 +443,38 @@ static PyMethodDef typevar_methods[] = { PyDoc_STRVAR(typevar_doc, "Type variable.\n\ \n\ -Usage::\n\ +The preferred way to construct a type variable is via the dedicated syntax\n\ +for generic functions, classes, and type aliases:\n\ \n\ - T = TypeVar('T') # Can be anything\n\ - A = TypeVar('A', str, bytes) # Must be str or bytes\n\ + class Sequence[T]: # T is a TypeVar\n\ + ...\n\ \n\ -Type variables exist primarily for the benefit of static type\n\ -checkers. They serve as the parameters for generic types as well\n\ -as for generic function definitions. See class Generic for more\n\ -information on generic types. Generic functions work as follows:\n\ -\n\ - def repeat(x: T, n: int) -> List[T]:\n\ - '''Return a list containing n references to x.'''\n\ - return [x]*n\n\ +This syntax can also be used to create bound and constrained type\n\ +variables:\n\ \n\ - def longest(x: A, y: A) -> A:\n\ - '''Return the longest of two strings.'''\n\ - return x if len(x) >= len(y) else y\n\ + class StrSequence[S: str]: # S is a TypeVar bound to str\n\ + ...\n\ \n\ -The latter example's signature is essentially the overloading\n\ -of (str, str) -> str and (bytes, bytes) -> bytes. Also note\n\ -that if the arguments are instances of some subclass of str,\n\ -the return type is still plain str.\n\ + class StrOrBytesSequence[A: (str, bytes)]: # A is a TypeVar constrained to str or bytes\n\ + ...\n\ \n\ -At runtime, isinstance(x, T) and issubclass(C, T) will raise TypeError.\n\ +However, if desired, reusable type variables can also be constructed\n\ +manually, like so:\n\ \n\ -Type variables defined with covariant=True or contravariant=True\n\ -can be used to declare covariant or contravariant generic types.\n\ -See PEP 484 for more details. By default generic types are invariant\n\ -in all type variables.\n\ + T = TypeVar('T') # Can be anything\n\ + S = TypeVar('S', bound=str) # Can be any subtype of str\n\ + A = TypeVar('A', str, bytes) # Must be exactly str or bytes\n\ \n\ -Type variables can be introspected. e.g.:\n\ -\n\ - T.__name__ == 'T'\n\ - T.__constraints__ == ()\n\ - T.__covariant__ == False\n\ - T.__contravariant__ = False\n\ - A.__constraints__ == (str, bytes)\n\ +Type variables exist primarily for the benefit of static type\n\ +checkers. They serve as the parameters for generic types as well\n\ +as for generic function and type alias definitions.\n\ \n\ -Note that only type variables defined in global scope can be pickled.\n\ +The variance of type variables is inferred by type checkers when they are created\n\ +through the type parameter syntax and when ``infer_variance=True`` is passed.\n\ +Manually created type variables may be explicitly marked covariant or\n\ +contravariant by passing ``covariant=True`` or ``contravariant=True``.\n\ +By default, manually created type variables are invariant. See PEP 484\n\ +and PEP 695 for more details.\n\ "); static PyType_Slot typevar_slots[] = { @@ -942,7 +935,14 @@ static PyMethodDef paramspec_methods[] = { PyDoc_STRVAR(paramspec_doc, "Parameter specification variable.\n\ \n\ -Usage::\n\ +The preferred way to construct a parameter specification is via the dedicated syntax\n\ +for generic functions, classes, and type aliases, where\n\ +the use of '**' creates a parameter specification:\n\ +\n\ + type IntFunc[**P] = Callable[P, int]\n\ +\n\ +For compatibility with Python 3.11 and earlier, ParamSpec objects\n\ +can also be created as follows:\n\ \n\ P = ParamSpec('P')\n\ \n\ @@ -952,12 +952,9 @@ callable to another callable, a pattern commonly found in higher order\n\ functions and decorators. They are only valid when used in ``Concatenate``,\n\ or as the first argument to ``Callable``, or as parameters for user-defined\n\ Generics. See class Generic for more information on generic types. An\n\ -example for annotating a decorator::\n\ -\n\ - T = TypeVar('T')\n\ - P = ParamSpec('P')\n\ +example for annotating a decorator:\n\ \n\ - def add_logging(f: Callable[P, T]) -> Callable[P, T]:\n\ + def add_logging[**P, T](f: Callable[P, T]) -> Callable[P, T]:\n\ '''A type-safe decorator to add logging to a function.'''\n\ def inner(*args: P.args, **kwargs: P.kwargs) -> T:\n\ logging.info(f'{f.__name__} was called')\n\ @@ -969,17 +966,9 @@ example for annotating a decorator::\n\ '''Add two numbers together.'''\n\ return x + y\n\ \n\ -Parameter specification variables defined with covariant=True or\n\ -contravariant=True can be used to declare covariant or contravariant\n\ -generic types. These keyword arguments are valid, but their actual semantics\n\ -are yet to be decided. See PEP 612 for details.\n\ -\n\ Parameter specification variables can be introspected. e.g.:\n\ \n\ P.__name__ == 'P'\n\ - P.__bound__ == None\n\ - P.__covariant__ == False\n\ - P.__contravariant__ == False\n\ \n\ Note that only parameter specification variables defined in global scope can\n\ be pickled.\n\ @@ -1175,9 +1164,18 @@ static PyMethodDef typevartuple_methods[] = { }; PyDoc_STRVAR(typevartuple_doc, -"Type variable tuple.\n\ +"Type variable tuple. A specialized form of type variable that enables\n\ +variadic generics.\n\ +\n\ +The preferred way to construct a type variable tuple is via the dedicated syntax\n\ +for generic functions, classes, and type aliases, where a single\n\ +'*' indicates a type variable tuple:\n\ +\n\ + def move_first_element_to_last[T, *Ts](tup: tuple[T, *Ts]) -> tuple[*Ts, T]:\n\ + return (*tup[1:], tup[0])\n\ \n\ -Usage:\n\ +For compatibility with Python 3.11 and earlier, TypeVarTuple objects\n\ +can also be created as follows:\n\ \n\ Ts = TypeVarTuple('Ts') # Can be given any name\n\ \n\ @@ -1185,7 +1183,7 @@ Just as a TypeVar (type variable) is a placeholder for a single type,\n\ a TypeVarTuple is a placeholder for an *arbitrary* number of types. For\n\ example, if we define a generic class using a TypeVarTuple:\n\ \n\ - class C(Generic[*Ts]): ...\n\ + class C[*Ts]: ...\n\ \n\ Then we can parameterize that class with an arbitrary number of type\n\ arguments:\n\ @@ -1441,6 +1439,23 @@ PyDoc_STRVAR(typealias_doc, Type aliases are created through the type statement:\n\ \n\ type Alias = int\n\ +\n\ +In this example, Alias and int will be treated equivalently by static\n\ +type checkers.\n\ +\n\ +At runtime, Alias is an instance of TypeAliasType. The __name__ attribute\n\ +holds the name of the type alias. The value of the type\n\ +alias is stored in the __value__ attribute. It is evaluated lazily, so\n\ +the value is computed only if the attribute is accessed.\n\ +\n\ +Type aliases can also be generic:\n\ +\n\ + type ListOrSet[T] = list[T] | set[T]\n\ +\n\ +In this case, the type parameters of the alias are stored in the\n\ +__type_params__ attribute.\n\ +\n\ +See PEP 695 for more information.\n\ "); static PyNumberMethods typealias_as_number = { @@ -1489,14 +1504,14 @@ PyDoc_STRVAR(generic_doc, \n\ A generic type is typically declared by inheriting from\n\ this class parameterized with one or more type variables.\n\ -For example, a generic mapping type might be defined as::\n\ +For example, a generic mapping type might be defined as:\n\ \n\ class Mapping(Generic[KT, VT]):\n\ def __getitem__(self, key: KT) -> VT:\n\ ...\n\ # Etc.\n\ \n\ -This class can then be used as follows::\n\ +This class can then be used as follows:\n\ \n\ def lookup_name(mapping: Mapping[KT, VT], key: KT, default: VT) -> VT:\n\ try:\n\ diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index ec5684b1d09502..6f25f91c0ff28d 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -236,7 +236,7 @@ static inline PyObject *get_interned_dict(PyInterpreterState *interp) } Py_ssize_t -_PyUnicode_InternedSize() +_PyUnicode_InternedSize(void) { return PyObject_Length(get_interned_dict(_PyInterpreterState_GET())); } diff --git a/PC/layout/support/options.py b/PC/layout/support/options.py index 26d13f5377ad59..60256fb32fe329 100644 --- a/PC/layout/support/options.py +++ b/PC/layout/support/options.py @@ -41,7 +41,6 @@ def public(f): "options": [ "stable", "pip", - "pip-user", "tcltk", "idle", "venv", diff --git a/PCbuild/get_externals.bat b/PCbuild/get_externals.bat index 39003d954d705a..d345317e65de53 100644 --- a/PCbuild/get_externals.bat +++ b/PCbuild/get_externals.bat @@ -57,7 +57,6 @@ if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-1.1.1t set libraries=%libraries% sqlite-3.42.0.0 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tcl-core-8.6.13.0 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tk-8.6.13.0 -if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tix-8.4.3.6 set libraries=%libraries% xz-5.2.5 set libraries=%libraries% zlib-1.2.13 diff --git a/PCbuild/prepare_tcltk.bat b/PCbuild/prepare_tcltk.bat index 4a43ed1582ce33..ecef247282094d 100644 --- a/PCbuild/prepare_tcltk.bat +++ b/PCbuild/prepare_tcltk.bat @@ -48,12 +48,9 @@ call "%PCBUILD%\get_externals.bat" --tkinter-src %ORG_SETTING% %MSBUILD% "%PCBUILD%\tcl.vcxproj" /p:Configuration=Release /p:Platform=Win32 %MSBUILD% "%PCBUILD%\tk.vcxproj" /p:Configuration=Release /p:Platform=Win32 -%MSBUILD% "%PCBUILD%\tix.vcxproj" /p:Configuration=Release /p:Platform=Win32 %MSBUILD% "%PCBUILD%\tcl.vcxproj" /p:Configuration=Release /p:Platform=x64 %MSBUILD% "%PCBUILD%\tk.vcxproj" /p:Configuration=Release /p:Platform=x64 -%MSBUILD% "%PCBUILD%\tix.vcxproj" /p:Configuration=Release /p:Platform=x64 %MSBUILD% "%PCBUILD%\tcl.vcxproj" /p:Configuration=Release /p:Platform=ARM64 %MSBUILD% "%PCBUILD%\tk.vcxproj" /p:Configuration=Release /p:Platform=ARM64 -%MSBUILD% "%PCBUILD%\tix.vcxproj" /p:Configuration=Release /p:Platform=ARM64 diff --git a/PCbuild/readme.txt b/PCbuild/readme.txt index 903424982749f2..05b7909c45a356 100644 --- a/PCbuild/readme.txt +++ b/PCbuild/readme.txt @@ -282,7 +282,7 @@ The property files used are: * python (versions, directories and build names) * pyproject (base settings for all projects) * openssl (used by projects dependent upon OpenSSL) - * tcltk (used by _tkinter, tcl, tk and tix projects) + * tcltk (used by _tkinter, tcl, and tk projects) The pyproject property file defines all of the build settings for each project, with some projects overriding certain specific values. The GUI diff --git a/PCbuild/regen.targets b/PCbuild/regen.targets index 107066817ba6b0..15f3d1375a10a2 100644 --- a/PCbuild/regen.targets +++ b/PCbuild/regen.targets @@ -107,8 +107,7 @@ $(opensslOutDir)LICENSE; $(libffiDir)LICENSE;" /> <_LicenseSources Include="$(tcltkDir)tcllicense.terms; - $(tcltkDir)tklicense.terms; - $(tcltkDir)tixlicense.terms" Condition="$(IncludeTkinter)" /> + $(tcltkDir)tklicense.terms" Condition="$(IncludeTkinter)" /> 8.6.13.0 $(TclVersion) - 8.4.3.6 $([System.Version]::Parse($(TclVersion)).Major) $([System.Version]::Parse($(TclVersion)).Minor) $([System.Version]::Parse($(TclVersion)).Build) @@ -13,13 +12,8 @@ $([System.Version]::Parse($(TkVersion)).Minor) $([System.Version]::Parse($(TkVersion)).Build) $([System.Version]::Parse($(TkVersion)).Revision) - $([System.Version]::Parse($(TixVersion)).Major) - $([System.Version]::Parse($(TixVersion)).Minor) - $([System.Version]::Parse($(TixVersion)).Build) - $([System.Version]::Parse($(TixVersion)).Revision) $(ExternalsDir)tcl-core-$(TclVersion)\ $(ExternalsDir)tk-$(TkVersion)\ - $(ExternalsDir)tix-$(TixVersion)\ $(ExternalsDir)tcltk-$(TclVersion)\$(ArchName)\ $(tcltkDir)\bin\tclsh$(TclMajorVersion)$(TclMinorVersion)t.exe $(tcltkDir)\..\win32\bin\tclsh$(TclMajorVersion)$(TclMinorVersion)t.exe @@ -31,8 +25,6 @@ tk$(TkMajorVersion)$(TkMinorVersion)t$(TclDebugExt).dll tk$(TkMajorVersion)$(TkMinorVersion)t$(TclDebugExt).lib zlib1.dll - tix$(TixMajorVersion)$(TixMinorVersion)$(TclDebugExt).dll - $(tcltkDir)lib\tix$(TixMajorVersion).$(TixMinorVersion).$(TixPatchLevel)\$(tixDLLName) $(tcltkDir)lib\tcl$(TclMajorVersion)$(TclMinorVersion)t$(TclDebugExt).lib;$(tcltkDir)lib\tk$(TkMajorVersion)$(TkMinorVersion)t$(TclDebugExt).lib IX86 AMD64 diff --git a/PCbuild/tix.vcxproj b/PCbuild/tix.vcxproj deleted file mode 100644 index 48abcd27c87759..00000000000000 --- a/PCbuild/tix.vcxproj +++ /dev/null @@ -1,101 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - PGInstrument - Win32 - - - PGInstrument - x64 - - - PGUpdate - Win32 - - - PGUpdate - x64 - - - Debug - x64 - - - Release - x64 - - - Release - ARM64 - - - - {C5A3E7FB-9695-4B2E-960B-1D9F43F1E555} - tix - true - - - - - - - - Makefile - $(tcltkDir) - $(tixDLLPath) - - - - - - BUILDDIRTOP="$(BuildDirTop)" TCL_DIR="$(tclDir.TrimEnd(`\`))" TK_DIR="$(tkDir.TrimEnd(`\`))" INSTALL_DIR="$(OutDir.TrimEnd(`\`))" TCLSH_EXE="$(tclWin32Exe)" - DEBUG=1 NODEBUG=0 TCL_DBGX=g TK_DBGX=g - DEBUG=0 NODEBUG=1 - -c -W3 -nologo -MD -wd4028 -wd4090 -wd4244 -wd4267 -wd4312 - setlocal -set VCINSTALLDIR=$(VCInstallDir) -cd /D "$(tixDir)win" -nmake /nologo -f makefile.vc MACHINE=$(TclMachine) cflags="$(CFlags)" $(DebugFlags) $(TclShortVersions) $(TixDirs) all install -copy /Y ..\license.terms "$(OutDir)\tixlicense.terms" - - rmdir /q/s "$(OutDir.TrimEnd(`\`))" - - - - - - - - - - - - - {b5fd6f1d-129e-4bff-9340-03606fac7283} - false - - - {7e85eccf-a72c-4da4-9e52-884508e80ba1} - false - - - - - - - - - - - - - \ No newline at end of file diff --git a/Parser/Python.asdl b/Parser/Python.asdl index dc2eb802b0436c..93632a09f0959b 100644 --- a/Parser/Python.asdl +++ b/Parser/Python.asdl @@ -8,19 +8,19 @@ module Python | Expression(expr body) | FunctionType(expr* argtypes, expr returns) - stmt = FunctionDef(identifier name, type_param* type_params, arguments args, + stmt = FunctionDef(identifier name, arguments args, stmt* body, expr* decorator_list, expr? returns, - string? type_comment) - | AsyncFunctionDef(identifier name, type_param* type_params, arguments args, + string? type_comment, type_param* type_params) + | AsyncFunctionDef(identifier name, arguments args, stmt* body, expr* decorator_list, expr? returns, - string? type_comment) + string? type_comment, type_param* type_params) | ClassDef(identifier name, - type_param* type_params, expr* bases, keyword* keywords, stmt* body, - expr* decorator_list) + expr* decorator_list, + type_param* type_params) | Return(expr? value) | Delete(expr* targets) diff --git a/Parser/action_helpers.c b/Parser/action_helpers.c index 06d77b64cacbcc..c4d8f75e542805 100644 --- a/Parser/action_helpers.c +++ b/Parser/action_helpers.c @@ -752,22 +752,25 @@ _PyPegen_function_def_decorators(Parser *p, asdl_expr_seq *decorators, stmt_ty f assert(function_def != NULL); if (function_def->kind == AsyncFunctionDef_kind) { return _PyAST_AsyncFunctionDef( - function_def->v.FunctionDef.name, function_def->v.FunctionDef.type_params, - function_def->v.FunctionDef.args, - function_def->v.FunctionDef.body, decorators, function_def->v.FunctionDef.returns, - function_def->v.FunctionDef.type_comment, function_def->lineno, - function_def->col_offset, function_def->end_lineno, function_def->end_col_offset, - p->arena); + function_def->v.AsyncFunctionDef.name, + function_def->v.AsyncFunctionDef.args, + function_def->v.AsyncFunctionDef.body, decorators, + function_def->v.AsyncFunctionDef.returns, + function_def->v.AsyncFunctionDef.type_comment, + function_def->v.AsyncFunctionDef.type_params, + function_def->lineno, function_def->col_offset, + function_def->end_lineno, function_def->end_col_offset, p->arena); } return _PyAST_FunctionDef( - function_def->v.FunctionDef.name, function_def->v.FunctionDef.type_params, + function_def->v.FunctionDef.name, function_def->v.FunctionDef.args, function_def->v.FunctionDef.body, decorators, function_def->v.FunctionDef.returns, - function_def->v.FunctionDef.type_comment, function_def->lineno, - function_def->col_offset, function_def->end_lineno, - function_def->end_col_offset, p->arena); + function_def->v.FunctionDef.type_comment, + function_def->v.FunctionDef.type_params, + function_def->lineno, function_def->col_offset, + function_def->end_lineno, function_def->end_col_offset, p->arena); } /* Construct a ClassDef equivalent to class_def, but with decorators */ @@ -776,9 +779,10 @@ _PyPegen_class_def_decorators(Parser *p, asdl_expr_seq *decorators, stmt_ty clas { assert(class_def != NULL); return _PyAST_ClassDef( - class_def->v.ClassDef.name, class_def->v.ClassDef.type_params, + class_def->v.ClassDef.name, class_def->v.ClassDef.bases, class_def->v.ClassDef.keywords, class_def->v.ClassDef.body, decorators, + class_def->v.ClassDef.type_params, class_def->lineno, class_def->col_offset, class_def->end_lineno, class_def->end_col_offset, p->arena); } diff --git a/Parser/parser.c b/Parser/parser.c index fc5466fea2b3fc..1705ebd456b5ff 100644 --- a/Parser/parser.c +++ b/Parser/parser.c @@ -4425,7 +4425,7 @@ class_def_raw_rule(Parser *p) UNUSED(_end_lineno); // Only used by EXTRA macro int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro - _res = _PyAST_ClassDef ( a -> v . Name . id , t , ( b ) ? ( ( expr_ty ) b ) -> v . Call . args : NULL , ( b ) ? ( ( expr_ty ) b ) -> v . Call . keywords : NULL , c , NULL , EXTRA ); + _res = _PyAST_ClassDef ( a -> v . Name . id , ( b ) ? ( ( expr_ty ) b ) -> v . Call . args : NULL , ( b ) ? ( ( expr_ty ) b ) -> v . Call . keywords : NULL , c , NULL , t , EXTRA ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; p->level--; @@ -4602,7 +4602,7 @@ function_def_raw_rule(Parser *p) UNUSED(_end_lineno); // Only used by EXTRA macro int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro - _res = _PyAST_FunctionDef ( n -> v . Name . id , t , ( params ) ? params : CHECK ( arguments_ty , _PyPegen_empty_arguments ( p ) ) , b , NULL , a , NEW_TYPE_COMMENT ( p , tc ) , EXTRA ); + _res = _PyAST_FunctionDef ( n -> v . Name . id , ( params ) ? params : CHECK ( arguments_ty , _PyPegen_empty_arguments ( p ) ) , b , NULL , a , NEW_TYPE_COMMENT ( p , tc ) , t , EXTRA ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; p->level--; @@ -4665,7 +4665,7 @@ function_def_raw_rule(Parser *p) UNUSED(_end_lineno); // Only used by EXTRA macro int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro - _res = CHECK_VERSION ( stmt_ty , 5 , "Async functions are" , _PyAST_AsyncFunctionDef ( n -> v . Name . id , t , ( params ) ? params : CHECK ( arguments_ty , _PyPegen_empty_arguments ( p ) ) , b , NULL , a , NEW_TYPE_COMMENT ( p , tc ) , EXTRA ) ); + _res = CHECK_VERSION ( stmt_ty , 5 , "Async functions are" , _PyAST_AsyncFunctionDef ( n -> v . Name . id , ( params ) ? params : CHECK ( arguments_ty , _PyPegen_empty_arguments ( p ) ) , b , NULL , a , NEW_TYPE_COMMENT ( p , tc ) , t , EXTRA ) ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; p->level--; diff --git a/Parser/pegen.c b/Parser/pegen.c index b031a6f5d440e8..b9894dd0acc546 100644 --- a/Parser/pegen.c +++ b/Parser/pegen.c @@ -924,9 +924,9 @@ _PyPegen_run_parser_from_string(const char *str, int start_rule, PyObject *filen struct tok_state *tok; if (flags != NULL && flags->cf_flags & PyCF_IGNORE_COOKIE) { - tok = _PyTokenizer_FromUTF8(str, exec_input); + tok = _PyTokenizer_FromUTF8(str, exec_input, 0); } else { - tok = _PyTokenizer_FromString(str, exec_input); + tok = _PyTokenizer_FromString(str, exec_input, 0); } if (tok == NULL) { if (PyErr_Occurred()) { diff --git a/Parser/tokenizer.c b/Parser/tokenizer.c index 1e8f785a331ac5..59c817293fbfcd 100644 --- a/Parser/tokenizer.c +++ b/Parser/tokenizer.c @@ -772,7 +772,8 @@ translate_into_utf8(const char* str, const char* enc) { static char * -translate_newlines(const char *s, int exec_input, struct tok_state *tok) { +translate_newlines(const char *s, int exec_input, int preserve_crlf, + struct tok_state *tok) { int skip_next_lf = 0; size_t needed_length = strlen(s) + 2, final_length; char *buf, *current; @@ -792,7 +793,7 @@ translate_newlines(const char *s, int exec_input, struct tok_state *tok) { break; } } - if (c == '\r') { + if (!preserve_crlf && c == '\r') { skip_next_lf = 1; c = '\n'; } @@ -800,7 +801,7 @@ translate_newlines(const char *s, int exec_input, struct tok_state *tok) { } /* If this is exec input, add a newline to the end of the string if there isn't one already. */ - if (exec_input && c != '\n') { + if (exec_input && c != '\n' && c != '\0') { *current = '\n'; current++; } @@ -822,14 +823,14 @@ translate_newlines(const char *s, int exec_input, struct tok_state *tok) { inside TOK. */ static char * -decode_str(const char *input, int single, struct tok_state *tok) +decode_str(const char *input, int single, struct tok_state *tok, int preserve_crlf) { PyObject* utf8 = NULL; char *str; const char *s; const char *newl[2] = {NULL, NULL}; int lineno = 0; - tok->input = str = translate_newlines(input, single, tok); + tok->input = str = translate_newlines(input, single, preserve_crlf, tok); if (str == NULL) return NULL; tok->enc = NULL; @@ -881,14 +882,14 @@ decode_str(const char *input, int single, struct tok_state *tok) /* Set up tokenizer for string */ struct tok_state * -_PyTokenizer_FromString(const char *str, int exec_input) +_PyTokenizer_FromString(const char *str, int exec_input, int preserve_crlf) { struct tok_state *tok = tok_new(); char *decoded; if (tok == NULL) return NULL; - decoded = decode_str(str, exec_input, tok); + decoded = decode_str(str, exec_input, tok, preserve_crlf); if (decoded == NULL) { _PyTokenizer_Free(tok); return NULL; @@ -902,13 +903,13 @@ _PyTokenizer_FromString(const char *str, int exec_input) /* Set up tokenizer for UTF-8 string */ struct tok_state * -_PyTokenizer_FromUTF8(const char *str, int exec_input) +_PyTokenizer_FromUTF8(const char *str, int exec_input, int preserve_crlf) { struct tok_state *tok = tok_new(); char *translated; if (tok == NULL) return NULL; - tok->input = translated = translate_newlines(str, exec_input, tok); + tok->input = translated = translate_newlines(str, exec_input, preserve_crlf, tok); if (translated == NULL) { _PyTokenizer_Free(tok); return NULL; @@ -1050,7 +1051,7 @@ tok_underflow_interactive(struct tok_state *tok) { } char *newtok = PyOS_Readline(tok->fp ? tok->fp : stdin, stdout, tok->prompt); if (newtok != NULL) { - char *translated = translate_newlines(newtok, 0, tok); + char *translated = translate_newlines(newtok, 0, 0, tok); PyMem_Free(newtok); if (translated == NULL) { return 0; @@ -1594,6 +1595,9 @@ tok_decimal_tail(struct tok_state *tok) static inline int tok_continuation_line(struct tok_state *tok) { int c = tok_nextc(tok); + if (c == '\r') { + c = tok_nextc(tok); + } if (c != '\n') { tok->done = E_LINECONT; return -1; @@ -1693,7 +1697,7 @@ tok_get_normal_mode(struct tok_state *tok, tokenizer_mode* current_tok, struct t } } tok_backup(tok, c); - if (c == '#' || c == '\n') { + if (c == '#' || c == '\n' || c == '\r') { /* Lines with only whitespace and/or comments shouldn't affect the indentation and are not passed to the parser as NEWLINE tokens, @@ -1760,7 +1764,7 @@ tok_get_normal_mode(struct tok_state *tok, tokenizer_mode* current_tok, struct t tok->starting_col_offset = tok->col_offset; /* Return pending indents/dedents */ - if (tok->pendin != 0) { + if (tok->pendin != 0) { if (tok->pendin < 0) { if (tok->tok_extra_tokens) { p_start = tok->cur; @@ -1822,7 +1826,7 @@ tok_get_normal_mode(struct tok_state *tok, tokenizer_mode* current_tok, struct t const char *prefix, *type_start; int current_starting_col_offset; - while (c != EOF && c != '\n') { + while (c != EOF && c != '\n' && c != '\r') { c = tok_nextc(tok); } @@ -2002,6 +2006,10 @@ tok_get_normal_mode(struct tok_state *tok, tokenizer_mode* current_tok, struct t return MAKE_TOKEN(NAME); } + if (c == '\r') { + c = tok_nextc(tok); + } + /* Newline */ if (c == '\n') { tok->atbol = 1; @@ -2405,7 +2413,10 @@ tok_get_normal_mode(struct tok_state *tok, tokenizer_mode* current_tok, struct t else { end_quote_size = 0; if (c == '\\') { - tok_nextc(tok); /* skip escaped char */ + c = tok_nextc(tok); /* skip escaped char */ + if (c == '\r') { + c = tok_nextc(tok); + } } } } @@ -2696,6 +2707,9 @@ tok_get_fstring_mode(struct tok_state *tok, tokenizer_mode* current_tok, struct return MAKE_TOKEN(FSTRING_MIDDLE); } else if (c == '\\') { int peek = tok_nextc(tok); + if (peek == '\r') { + peek = tok_nextc(tok); + } // Special case when the backslash is right before a curly // brace. We have to restore and return the control back // to the loop for the next iteration. diff --git a/Parser/tokenizer.h b/Parser/tokenizer.h index 3f34763239acda..02749e355da812 100644 --- a/Parser/tokenizer.h +++ b/Parser/tokenizer.h @@ -21,7 +21,7 @@ enum decoding_state { }; enum interactive_underflow_t { - /* Normal mode of operation: return a new token when asked in interactie mode */ + /* Normal mode of operation: return a new token when asked in interactive mode */ IUNDERFLOW_NORMAL, /* Forcefully return ENDMARKER when asked for a new token in interactive mode. This * can be used to prevent the tokenizer to prompt the user for new tokens */ @@ -135,8 +135,8 @@ struct tok_state { #endif }; -extern struct tok_state *_PyTokenizer_FromString(const char *, int); -extern struct tok_state *_PyTokenizer_FromUTF8(const char *, int); +extern struct tok_state *_PyTokenizer_FromString(const char *, int, int); +extern struct tok_state *_PyTokenizer_FromUTF8(const char *, int, int); extern struct tok_state *_PyTokenizer_FromFile(FILE *, const char*, const char *, const char *); extern void _PyTokenizer_Free(struct tok_state *); diff --git a/Python/Python-ast.c b/Python/Python-ast.c index 87906d975d7414..030c082a4a6b14 100644 --- a/Python/Python-ast.c +++ b/Python/Python-ast.c @@ -409,29 +409,29 @@ static const char * const stmt_attributes[] = { static PyObject* ast2obj_stmt(struct ast_state *state, void*); static const char * const FunctionDef_fields[]={ "name", - "type_params", "args", "body", "decorator_list", "returns", "type_comment", + "type_params", }; static const char * const AsyncFunctionDef_fields[]={ "name", - "type_params", "args", "body", "decorator_list", "returns", "type_comment", + "type_params", }; static const char * const ClassDef_fields[]={ "name", - "type_params", "bases", "keywords", "body", "decorator_list", + "type_params", }; static const char * const Return_fields[]={ "value", @@ -1169,9 +1169,9 @@ init_types(struct ast_state *state) "FunctionType(expr* argtypes, expr returns)"); if (!state->FunctionType_type) return 0; state->stmt_type = make_type(state, "stmt", state->AST_type, NULL, 0, - "stmt = FunctionDef(identifier name, type_param* type_params, arguments args, stmt* body, expr* decorator_list, expr? returns, string? type_comment)\n" - " | AsyncFunctionDef(identifier name, type_param* type_params, arguments args, stmt* body, expr* decorator_list, expr? returns, string? type_comment)\n" - " | ClassDef(identifier name, type_param* type_params, expr* bases, keyword* keywords, stmt* body, expr* decorator_list)\n" + "stmt = FunctionDef(identifier name, arguments args, stmt* body, expr* decorator_list, expr? returns, string? type_comment, type_param* type_params)\n" + " | AsyncFunctionDef(identifier name, arguments args, stmt* body, expr* decorator_list, expr? returns, string? type_comment, type_param* type_params)\n" + " | ClassDef(identifier name, expr* bases, keyword* keywords, stmt* body, expr* decorator_list, type_param* type_params)\n" " | Return(expr? value)\n" " | Delete(expr* targets)\n" " | Assign(expr* targets, expr value, string? type_comment)\n" @@ -1206,7 +1206,7 @@ init_types(struct ast_state *state) return 0; state->FunctionDef_type = make_type(state, "FunctionDef", state->stmt_type, FunctionDef_fields, 7, - "FunctionDef(identifier name, type_param* type_params, arguments args, stmt* body, expr* decorator_list, expr? returns, string? type_comment)"); + "FunctionDef(identifier name, arguments args, stmt* body, expr* decorator_list, expr? returns, string? type_comment, type_param* type_params)"); if (!state->FunctionDef_type) return 0; if (PyObject_SetAttr(state->FunctionDef_type, state->returns, Py_None) == -1) @@ -1217,7 +1217,7 @@ init_types(struct ast_state *state) state->AsyncFunctionDef_type = make_type(state, "AsyncFunctionDef", state->stmt_type, AsyncFunctionDef_fields, 7, - "AsyncFunctionDef(identifier name, type_param* type_params, arguments args, stmt* body, expr* decorator_list, expr? returns, string? type_comment)"); + "AsyncFunctionDef(identifier name, arguments args, stmt* body, expr* decorator_list, expr? returns, string? type_comment, type_param* type_params)"); if (!state->AsyncFunctionDef_type) return 0; if (PyObject_SetAttr(state->AsyncFunctionDef_type, state->returns, Py_None) == -1) @@ -1227,7 +1227,7 @@ init_types(struct ast_state *state) return 0; state->ClassDef_type = make_type(state, "ClassDef", state->stmt_type, ClassDef_fields, 6, - "ClassDef(identifier name, type_param* type_params, expr* bases, keyword* keywords, stmt* body, expr* decorator_list)"); + "ClassDef(identifier name, expr* bases, keyword* keywords, stmt* body, expr* decorator_list, type_param* type_params)"); if (!state->ClassDef_type) return 0; state->Return_type = make_type(state, "Return", state->stmt_type, Return_fields, 1, @@ -2032,11 +2032,11 @@ _PyAST_FunctionType(asdl_expr_seq * argtypes, expr_ty returns, PyArena *arena) } stmt_ty -_PyAST_FunctionDef(identifier name, asdl_type_param_seq * type_params, - arguments_ty args, asdl_stmt_seq * body, asdl_expr_seq * - decorator_list, expr_ty returns, string type_comment, int - lineno, int col_offset, int end_lineno, int end_col_offset, - PyArena *arena) +_PyAST_FunctionDef(identifier name, arguments_ty args, asdl_stmt_seq * body, + asdl_expr_seq * decorator_list, expr_ty returns, string + type_comment, asdl_type_param_seq * type_params, int lineno, + int col_offset, int end_lineno, int end_col_offset, PyArena + *arena) { stmt_ty p; if (!name) { @@ -2054,12 +2054,12 @@ _PyAST_FunctionDef(identifier name, asdl_type_param_seq * type_params, return NULL; p->kind = FunctionDef_kind; p->v.FunctionDef.name = name; - p->v.FunctionDef.type_params = type_params; p->v.FunctionDef.args = args; p->v.FunctionDef.body = body; p->v.FunctionDef.decorator_list = decorator_list; p->v.FunctionDef.returns = returns; p->v.FunctionDef.type_comment = type_comment; + p->v.FunctionDef.type_params = type_params; p->lineno = lineno; p->col_offset = col_offset; p->end_lineno = end_lineno; @@ -2068,9 +2068,9 @@ _PyAST_FunctionDef(identifier name, asdl_type_param_seq * type_params, } stmt_ty -_PyAST_AsyncFunctionDef(identifier name, asdl_type_param_seq * type_params, - arguments_ty args, asdl_stmt_seq * body, asdl_expr_seq - * decorator_list, expr_ty returns, string type_comment, +_PyAST_AsyncFunctionDef(identifier name, arguments_ty args, asdl_stmt_seq * + body, asdl_expr_seq * decorator_list, expr_ty returns, + string type_comment, asdl_type_param_seq * type_params, int lineno, int col_offset, int end_lineno, int end_col_offset, PyArena *arena) { @@ -2090,12 +2090,12 @@ _PyAST_AsyncFunctionDef(identifier name, asdl_type_param_seq * type_params, return NULL; p->kind = AsyncFunctionDef_kind; p->v.AsyncFunctionDef.name = name; - p->v.AsyncFunctionDef.type_params = type_params; p->v.AsyncFunctionDef.args = args; p->v.AsyncFunctionDef.body = body; p->v.AsyncFunctionDef.decorator_list = decorator_list; p->v.AsyncFunctionDef.returns = returns; p->v.AsyncFunctionDef.type_comment = type_comment; + p->v.AsyncFunctionDef.type_params = type_params; p->lineno = lineno; p->col_offset = col_offset; p->end_lineno = end_lineno; @@ -2104,11 +2104,10 @@ _PyAST_AsyncFunctionDef(identifier name, asdl_type_param_seq * type_params, } stmt_ty -_PyAST_ClassDef(identifier name, asdl_type_param_seq * type_params, - asdl_expr_seq * bases, asdl_keyword_seq * keywords, - asdl_stmt_seq * body, asdl_expr_seq * decorator_list, int - lineno, int col_offset, int end_lineno, int end_col_offset, - PyArena *arena) +_PyAST_ClassDef(identifier name, asdl_expr_seq * bases, asdl_keyword_seq * + keywords, asdl_stmt_seq * body, asdl_expr_seq * decorator_list, + asdl_type_param_seq * type_params, int lineno, int col_offset, + int end_lineno, int end_col_offset, PyArena *arena) { stmt_ty p; if (!name) { @@ -2121,11 +2120,11 @@ _PyAST_ClassDef(identifier name, asdl_type_param_seq * type_params, return NULL; p->kind = ClassDef_kind; p->v.ClassDef.name = name; - p->v.ClassDef.type_params = type_params; p->v.ClassDef.bases = bases; p->v.ClassDef.keywords = keywords; p->v.ClassDef.body = body; p->v.ClassDef.decorator_list = decorator_list; + p->v.ClassDef.type_params = type_params; p->lineno = lineno; p->col_offset = col_offset; p->end_lineno = end_lineno; @@ -3883,12 +3882,6 @@ ast2obj_stmt(struct ast_state *state, void* _o) if (PyObject_SetAttr(result, state->name, value) == -1) goto failed; Py_DECREF(value); - value = ast2obj_list(state, (asdl_seq*)o->v.FunctionDef.type_params, - ast2obj_type_param); - if (!value) goto failed; - if (PyObject_SetAttr(result, state->type_params, value) == -1) - goto failed; - Py_DECREF(value); value = ast2obj_arguments(state, o->v.FunctionDef.args); if (!value) goto failed; if (PyObject_SetAttr(result, state->args, value) == -1) @@ -3916,6 +3909,12 @@ ast2obj_stmt(struct ast_state *state, void* _o) if (PyObject_SetAttr(result, state->type_comment, value) == -1) goto failed; Py_DECREF(value); + value = ast2obj_list(state, (asdl_seq*)o->v.FunctionDef.type_params, + ast2obj_type_param); + if (!value) goto failed; + if (PyObject_SetAttr(result, state->type_params, value) == -1) + goto failed; + Py_DECREF(value); break; case AsyncFunctionDef_kind: tp = (PyTypeObject *)state->AsyncFunctionDef_type; @@ -3926,13 +3925,6 @@ ast2obj_stmt(struct ast_state *state, void* _o) if (PyObject_SetAttr(result, state->name, value) == -1) goto failed; Py_DECREF(value); - value = ast2obj_list(state, - (asdl_seq*)o->v.AsyncFunctionDef.type_params, - ast2obj_type_param); - if (!value) goto failed; - if (PyObject_SetAttr(result, state->type_params, value) == -1) - goto failed; - Py_DECREF(value); value = ast2obj_arguments(state, o->v.AsyncFunctionDef.args); if (!value) goto failed; if (PyObject_SetAttr(result, state->args, value) == -1) @@ -3961,6 +3953,13 @@ ast2obj_stmt(struct ast_state *state, void* _o) if (PyObject_SetAttr(result, state->type_comment, value) == -1) goto failed; Py_DECREF(value); + value = ast2obj_list(state, + (asdl_seq*)o->v.AsyncFunctionDef.type_params, + ast2obj_type_param); + if (!value) goto failed; + if (PyObject_SetAttr(result, state->type_params, value) == -1) + goto failed; + Py_DECREF(value); break; case ClassDef_kind: tp = (PyTypeObject *)state->ClassDef_type; @@ -3971,12 +3970,6 @@ ast2obj_stmt(struct ast_state *state, void* _o) if (PyObject_SetAttr(result, state->name, value) == -1) goto failed; Py_DECREF(value); - value = ast2obj_list(state, (asdl_seq*)o->v.ClassDef.type_params, - ast2obj_type_param); - if (!value) goto failed; - if (PyObject_SetAttr(result, state->type_params, value) == -1) - goto failed; - Py_DECREF(value); value = ast2obj_list(state, (asdl_seq*)o->v.ClassDef.bases, ast2obj_expr); if (!value) goto failed; @@ -4001,6 +3994,12 @@ ast2obj_stmt(struct ast_state *state, void* _o) if (PyObject_SetAttr(result, state->decorator_list, value) == -1) goto failed; Py_DECREF(value); + value = ast2obj_list(state, (asdl_seq*)o->v.ClassDef.type_params, + ast2obj_type_param); + if (!value) goto failed; + if (PyObject_SetAttr(result, state->type_params, value) == -1) + goto failed; + Py_DECREF(value); break; case Return_kind: tp = (PyTypeObject *)state->Return_type; @@ -6075,12 +6074,12 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* } if (isinstance) { identifier name; - asdl_type_param_seq* type_params; arguments_ty args; asdl_stmt_seq* body; asdl_expr_seq* decorator_list; expr_ty returns; string type_comment; + asdl_type_param_seq* type_params; if (_PyObject_LookupAttr(obj, state->name, &tmp) < 0) { return 1; @@ -6099,42 +6098,6 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (res != 0) goto failed; Py_CLEAR(tmp); } - if (_PyObject_LookupAttr(obj, state->type_params, &tmp) < 0) { - return 1; - } - if (tmp == NULL) { - PyErr_SetString(PyExc_TypeError, "required field \"type_params\" missing from FunctionDef"); - return 1; - } - else { - int res; - Py_ssize_t len; - Py_ssize_t i; - if (!PyList_Check(tmp)) { - PyErr_Format(PyExc_TypeError, "FunctionDef field \"type_params\" must be a list, not a %.200s", _PyType_Name(Py_TYPE(tmp))); - goto failed; - } - len = PyList_GET_SIZE(tmp); - type_params = _Py_asdl_type_param_seq_new(len, arena); - if (type_params == NULL) goto failed; - for (i = 0; i < len; i++) { - type_param_ty val; - PyObject *tmp2 = Py_NewRef(PyList_GET_ITEM(tmp, i)); - if (_Py_EnterRecursiveCall(" while traversing 'FunctionDef' node")) { - goto failed; - } - res = obj2ast_type_param(state, tmp2, &val, arena); - _Py_LeaveRecursiveCall(); - Py_DECREF(tmp2); - if (res != 0) goto failed; - if (len != PyList_GET_SIZE(tmp)) { - PyErr_SetString(PyExc_RuntimeError, "FunctionDef field \"type_params\" changed size during iteration"); - goto failed; - } - asdl_seq_SET(type_params, i, val); - } - Py_CLEAR(tmp); - } if (_PyObject_LookupAttr(obj, state->args, &tmp) < 0) { return 1; } @@ -6258,10 +6221,46 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (res != 0) goto failed; Py_CLEAR(tmp); } - *out = _PyAST_FunctionDef(name, type_params, args, body, - decorator_list, returns, type_comment, - lineno, col_offset, end_lineno, - end_col_offset, arena); + if (_PyObject_LookupAttr(obj, state->type_params, &tmp) < 0) { + return 1; + } + if (tmp == NULL) { + PyErr_SetString(PyExc_TypeError, "required field \"type_params\" missing from FunctionDef"); + return 1; + } + else { + int res; + Py_ssize_t len; + Py_ssize_t i; + if (!PyList_Check(tmp)) { + PyErr_Format(PyExc_TypeError, "FunctionDef field \"type_params\" must be a list, not a %.200s", _PyType_Name(Py_TYPE(tmp))); + goto failed; + } + len = PyList_GET_SIZE(tmp); + type_params = _Py_asdl_type_param_seq_new(len, arena); + if (type_params == NULL) goto failed; + for (i = 0; i < len; i++) { + type_param_ty val; + PyObject *tmp2 = Py_NewRef(PyList_GET_ITEM(tmp, i)); + if (_Py_EnterRecursiveCall(" while traversing 'FunctionDef' node")) { + goto failed; + } + res = obj2ast_type_param(state, tmp2, &val, arena); + _Py_LeaveRecursiveCall(); + Py_DECREF(tmp2); + if (res != 0) goto failed; + if (len != PyList_GET_SIZE(tmp)) { + PyErr_SetString(PyExc_RuntimeError, "FunctionDef field \"type_params\" changed size during iteration"); + goto failed; + } + asdl_seq_SET(type_params, i, val); + } + Py_CLEAR(tmp); + } + *out = _PyAST_FunctionDef(name, args, body, decorator_list, returns, + type_comment, type_params, lineno, + col_offset, end_lineno, end_col_offset, + arena); if (*out == NULL) goto failed; return 0; } @@ -6272,12 +6271,12 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* } if (isinstance) { identifier name; - asdl_type_param_seq* type_params; arguments_ty args; asdl_stmt_seq* body; asdl_expr_seq* decorator_list; expr_ty returns; string type_comment; + asdl_type_param_seq* type_params; if (_PyObject_LookupAttr(obj, state->name, &tmp) < 0) { return 1; @@ -6296,42 +6295,6 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (res != 0) goto failed; Py_CLEAR(tmp); } - if (_PyObject_LookupAttr(obj, state->type_params, &tmp) < 0) { - return 1; - } - if (tmp == NULL) { - PyErr_SetString(PyExc_TypeError, "required field \"type_params\" missing from AsyncFunctionDef"); - return 1; - } - else { - int res; - Py_ssize_t len; - Py_ssize_t i; - if (!PyList_Check(tmp)) { - PyErr_Format(PyExc_TypeError, "AsyncFunctionDef field \"type_params\" must be a list, not a %.200s", _PyType_Name(Py_TYPE(tmp))); - goto failed; - } - len = PyList_GET_SIZE(tmp); - type_params = _Py_asdl_type_param_seq_new(len, arena); - if (type_params == NULL) goto failed; - for (i = 0; i < len; i++) { - type_param_ty val; - PyObject *tmp2 = Py_NewRef(PyList_GET_ITEM(tmp, i)); - if (_Py_EnterRecursiveCall(" while traversing 'AsyncFunctionDef' node")) { - goto failed; - } - res = obj2ast_type_param(state, tmp2, &val, arena); - _Py_LeaveRecursiveCall(); - Py_DECREF(tmp2); - if (res != 0) goto failed; - if (len != PyList_GET_SIZE(tmp)) { - PyErr_SetString(PyExc_RuntimeError, "AsyncFunctionDef field \"type_params\" changed size during iteration"); - goto failed; - } - asdl_seq_SET(type_params, i, val); - } - Py_CLEAR(tmp); - } if (_PyObject_LookupAttr(obj, state->args, &tmp) < 0) { return 1; } @@ -6455,8 +6418,44 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (res != 0) goto failed; Py_CLEAR(tmp); } - *out = _PyAST_AsyncFunctionDef(name, type_params, args, body, - decorator_list, returns, type_comment, + if (_PyObject_LookupAttr(obj, state->type_params, &tmp) < 0) { + return 1; + } + if (tmp == NULL) { + PyErr_SetString(PyExc_TypeError, "required field \"type_params\" missing from AsyncFunctionDef"); + return 1; + } + else { + int res; + Py_ssize_t len; + Py_ssize_t i; + if (!PyList_Check(tmp)) { + PyErr_Format(PyExc_TypeError, "AsyncFunctionDef field \"type_params\" must be a list, not a %.200s", _PyType_Name(Py_TYPE(tmp))); + goto failed; + } + len = PyList_GET_SIZE(tmp); + type_params = _Py_asdl_type_param_seq_new(len, arena); + if (type_params == NULL) goto failed; + for (i = 0; i < len; i++) { + type_param_ty val; + PyObject *tmp2 = Py_NewRef(PyList_GET_ITEM(tmp, i)); + if (_Py_EnterRecursiveCall(" while traversing 'AsyncFunctionDef' node")) { + goto failed; + } + res = obj2ast_type_param(state, tmp2, &val, arena); + _Py_LeaveRecursiveCall(); + Py_DECREF(tmp2); + if (res != 0) goto failed; + if (len != PyList_GET_SIZE(tmp)) { + PyErr_SetString(PyExc_RuntimeError, "AsyncFunctionDef field \"type_params\" changed size during iteration"); + goto failed; + } + asdl_seq_SET(type_params, i, val); + } + Py_CLEAR(tmp); + } + *out = _PyAST_AsyncFunctionDef(name, args, body, decorator_list, + returns, type_comment, type_params, lineno, col_offset, end_lineno, end_col_offset, arena); if (*out == NULL) goto failed; @@ -6469,11 +6468,11 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* } if (isinstance) { identifier name; - asdl_type_param_seq* type_params; asdl_expr_seq* bases; asdl_keyword_seq* keywords; asdl_stmt_seq* body; asdl_expr_seq* decorator_list; + asdl_type_param_seq* type_params; if (_PyObject_LookupAttr(obj, state->name, &tmp) < 0) { return 1; @@ -6492,42 +6491,6 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (res != 0) goto failed; Py_CLEAR(tmp); } - if (_PyObject_LookupAttr(obj, state->type_params, &tmp) < 0) { - return 1; - } - if (tmp == NULL) { - PyErr_SetString(PyExc_TypeError, "required field \"type_params\" missing from ClassDef"); - return 1; - } - else { - int res; - Py_ssize_t len; - Py_ssize_t i; - if (!PyList_Check(tmp)) { - PyErr_Format(PyExc_TypeError, "ClassDef field \"type_params\" must be a list, not a %.200s", _PyType_Name(Py_TYPE(tmp))); - goto failed; - } - len = PyList_GET_SIZE(tmp); - type_params = _Py_asdl_type_param_seq_new(len, arena); - if (type_params == NULL) goto failed; - for (i = 0; i < len; i++) { - type_param_ty val; - PyObject *tmp2 = Py_NewRef(PyList_GET_ITEM(tmp, i)); - if (_Py_EnterRecursiveCall(" while traversing 'ClassDef' node")) { - goto failed; - } - res = obj2ast_type_param(state, tmp2, &val, arena); - _Py_LeaveRecursiveCall(); - Py_DECREF(tmp2); - if (res != 0) goto failed; - if (len != PyList_GET_SIZE(tmp)) { - PyErr_SetString(PyExc_RuntimeError, "ClassDef field \"type_params\" changed size during iteration"); - goto failed; - } - asdl_seq_SET(type_params, i, val); - } - Py_CLEAR(tmp); - } if (_PyObject_LookupAttr(obj, state->bases, &tmp) < 0) { return 1; } @@ -6672,8 +6635,44 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* } Py_CLEAR(tmp); } - *out = _PyAST_ClassDef(name, type_params, bases, keywords, body, - decorator_list, lineno, col_offset, end_lineno, + if (_PyObject_LookupAttr(obj, state->type_params, &tmp) < 0) { + return 1; + } + if (tmp == NULL) { + PyErr_SetString(PyExc_TypeError, "required field \"type_params\" missing from ClassDef"); + return 1; + } + else { + int res; + Py_ssize_t len; + Py_ssize_t i; + if (!PyList_Check(tmp)) { + PyErr_Format(PyExc_TypeError, "ClassDef field \"type_params\" must be a list, not a %.200s", _PyType_Name(Py_TYPE(tmp))); + goto failed; + } + len = PyList_GET_SIZE(tmp); + type_params = _Py_asdl_type_param_seq_new(len, arena); + if (type_params == NULL) goto failed; + for (i = 0; i < len; i++) { + type_param_ty val; + PyObject *tmp2 = Py_NewRef(PyList_GET_ITEM(tmp, i)); + if (_Py_EnterRecursiveCall(" while traversing 'ClassDef' node")) { + goto failed; + } + res = obj2ast_type_param(state, tmp2, &val, arena); + _Py_LeaveRecursiveCall(); + Py_DECREF(tmp2); + if (res != 0) goto failed; + if (len != PyList_GET_SIZE(tmp)) { + PyErr_SetString(PyExc_RuntimeError, "ClassDef field \"type_params\" changed size during iteration"); + goto failed; + } + asdl_seq_SET(type_params, i, val); + } + Py_CLEAR(tmp); + } + *out = _PyAST_ClassDef(name, bases, keywords, body, decorator_list, + type_params, lineno, col_offset, end_lineno, end_col_offset, arena); if (*out == NULL) goto failed; return 0; diff --git a/Python/Python-tokenize.c b/Python/Python-tokenize.c index 0023e303b96e83..4eced66b617708 100644 --- a/Python/Python-tokenize.c +++ b/Python/Python-tokenize.c @@ -30,6 +30,7 @@ class _tokenizer.tokenizeriter "tokenizeriterobject *" "_tokenize_get_state_by_t typedef struct { PyObject_HEAD struct tok_state *tok; + int done; } tokenizeriterobject; /*[clinic input] @@ -54,7 +55,7 @@ tokenizeriter_new_impl(PyTypeObject *type, const char *source, if (filename == NULL) { return NULL; } - self->tok = _PyTokenizer_FromUTF8(source, 1); + self->tok = _PyTokenizer_FromUTF8(source, 1, 1); if (self->tok == NULL) { Py_DECREF(filename); return NULL; @@ -63,6 +64,7 @@ tokenizeriter_new_impl(PyTypeObject *type, const char *source, if (extra_tokens) { self->tok->tok_extra_tokens = 1; } + self->done = 0; return (PyObject *)self; } @@ -179,8 +181,9 @@ tokenizeriter_next(tokenizeriterobject *it) } goto exit; } - if (type == ERRORTOKEN || type == ENDMARKER) { + if (it->done || type == ERRORTOKEN) { PyErr_SetString(PyExc_StopIteration, "EOF"); + it->done = 1; goto exit; } PyObject *str = NULL; @@ -194,15 +197,24 @@ tokenizeriter_next(tokenizeriterobject *it) goto exit; } - Py_ssize_t size = it->tok->inp - it->tok->buf; - assert(it->tok->buf[size-1] == '\n'); - size -= 1; // Remove the newline character from the end of the line - PyObject *line = PyUnicode_DecodeUTF8(it->tok->buf, size, "replace"); + int is_trailing_token = 0; + if (type == ENDMARKER || (type == DEDENT && it->tok->done == E_EOF)) { + is_trailing_token = 1; + } + + const char *line_start = ISSTRINGLIT(type) ? it->tok->multi_line_start : it->tok->line_start; + PyObject* line = NULL; + if (it->tok->tok_extra_tokens && is_trailing_token) { + line = PyUnicode_FromString(""); + } else { + Py_ssize_t size = it->tok->inp - line_start; + line = PyUnicode_DecodeUTF8(line_start, size, "replace"); + } if (line == NULL) { Py_DECREF(str); goto exit; } - const char *line_start = ISSTRINGLIT(type) ? it->tok->multi_line_start : it->tok->line_start; + Py_ssize_t lineno = ISSTRINGLIT(type) ? it->tok->first_lineno : it->tok->lineno; Py_ssize_t end_lineno = it->tok->lineno; Py_ssize_t col_offset = -1; @@ -215,6 +227,10 @@ tokenizeriter_next(tokenizeriterobject *it) } if (it->tok->tok_extra_tokens) { + if (is_trailing_token) { + lineno = end_lineno = lineno + 1; + col_offset = end_col_offset = 0; + } // Necessary adjustments to match the original Python tokenize // implementation if (type > DEDENT && type < OP) { @@ -224,7 +240,12 @@ tokenizeriter_next(tokenizeriterobject *it) type = NAME; } else if (type == NEWLINE) { - str = PyUnicode_FromString("\n"); + Py_DECREF(str); + if (it->tok->start[0] == '\r') { + str = PyUnicode_FromString("\r\n"); + } else { + str = PyUnicode_FromString("\n"); + } end_col_offset++; } } @@ -232,6 +253,9 @@ tokenizeriter_next(tokenizeriterobject *it) result = Py_BuildValue("(iN(nn)(nn)N)", type, str, lineno, col_offset, end_lineno, end_col_offset, line); exit: _PyToken_Free(&token); + if (type == ENDMARKER) { + it->done = 1; + } return result; } diff --git a/Python/_warnings.c b/Python/_warnings.c index dec658680241ed..54fa5c56572f4d 100644 --- a/Python/_warnings.c +++ b/Python/_warnings.c @@ -1365,6 +1365,20 @@ PyErr_WarnExplicitFormat(PyObject *category, return ret; } +void +_PyErr_WarnUnawaitedAgenMethod(PyAsyncGenObject *agen, PyObject *method) +{ + PyObject *exc = PyErr_GetRaisedException(); + if (_PyErr_WarnFormat((PyObject *)agen, PyExc_RuntimeWarning, 1, + "coroutine method %R of %R was never awaited", + method, agen->ag_qualname) < 0) + { + PyErr_WriteUnraisable((PyObject *)agen); + } + PyErr_SetRaisedException(exc); +} + + void _PyErr_WarnUnawaitedCoroutine(PyObject *coro) { diff --git a/Python/stdlib_module_names.h b/Python/stdlib_module_names.h index 5af120d2542822..925b8b3230fce3 100644 --- a/Python/stdlib_module_names.h +++ b/Python/stdlib_module_names.h @@ -156,7 +156,6 @@ static const char* _Py_stdlib_module_names[] = { "http", "idlelib", "imaplib", -"imghdr", "importlib", "inspect", "io", diff --git a/Tools/freeze/README b/Tools/freeze/README index 098107c5d1a9d2..9b3ea1f2c723b1 100644 --- a/Tools/freeze/README +++ b/Tools/freeze/README @@ -98,13 +98,13 @@ use Tkinter without a Tcl/Tk installation. The best way to ship a frozen Tkinter program is to decide in advance where you are going to place the Tcl and Tk library files in the distributed setup, and then declare these directories in your frozen Python program using -the TCL_LIBRARY, TK_LIBRARY and TIX_LIBRARY environment variables. +the TCL_LIBRARY and TK_LIBRARY environment variables. For example, assume you will ship your frozen program in the directory /bin/windows-x86 and will place your Tcl library files in /lib/tcl8.2 and your Tk library files in /lib/tk8.2. Then placing the following lines in your frozen Python script before importing -Tkinter or Tix would set the environment correctly for Tcl/Tk/Tix: +tkinter would set the environment correctly for Tcl/Tk: import os import os.path @@ -115,17 +115,14 @@ if sys.platform == "win32": sys.path = ['', '..\\..\\lib\\python-2.0'] os.environ['TCL_LIBRARY'] = RootDir + '\\lib\\tcl8.2' os.environ['TK_LIBRARY'] = RootDir + '\\lib\\tk8.2' - os.environ['TIX_LIBRARY'] = RootDir + '\\lib\\tix8.1' elif sys.platform == "linux2": sys.path = ['', '../../lib/python-2.0'] os.environ['TCL_LIBRARY'] = RootDir + '/lib/tcl8.2' os.environ['TK_LIBRARY'] = RootDir + '/lib/tk8.2' - os.environ['TIX_LIBRARY'] = RootDir + '/lib/tix8.1' elif sys.platform == "solaris": sys.path = ['', '../../lib/python-2.0'] os.environ['TCL_LIBRARY'] = RootDir + '/lib/tcl8.2' os.environ['TK_LIBRARY'] = RootDir + '/lib/tk8.2' - os.environ['TIX_LIBRARY'] = RootDir + '/lib/tix8.1' This also adds /lib/python-2.0 to your Python path for any Python files such as _tkinter.pyd you may need. @@ -148,10 +145,9 @@ executable would be possible, in which the Tcl/Tk library files are incorporated in a frozen Python module as string literals and written to a temporary location when the program runs; this is currently left as an exercise for the reader. An easier approach is to freeze the -Tcl/Tk/Tix code into the dynamic libraries using the Tcl ET code, -or the Tix Stand-Alone-Module code. Of course, you can also simply -require that Tcl/Tk is required on the target installation, but be -careful that the version corresponds. +Tcl/Tk code into the dynamic libraries using the Tcl ET code. +Of course, you can also simply require that Tcl/Tk is required on the +target installation, but be careful that the version corresponds. There are some caveats using frozen Tkinter applications: Under Windows if you use the -s windows option, writing diff --git a/Tools/msi/purge.py b/Tools/msi/purge.py index de9fdc95202af7..e25219a6caf9d4 100644 --- a/Tools/msi/purge.py +++ b/Tools/msi/purge.py @@ -10,7 +10,7 @@ import re import sys -from urllib.request import * +from urllib.request import Request, urlopen VERSION_RE = re.compile(r'(\d+\.\d+\.\d+)([A-Za-z_]+\d+)?$') diff --git a/Tools/peg_generator/pegen/build.py b/Tools/peg_generator/pegen/build.py index 5805ff63717440..aace684045b9f8 100644 --- a/Tools/peg_generator/pegen/build.py +++ b/Tools/peg_generator/pegen/build.py @@ -1,4 +1,5 @@ import itertools +import os import pathlib import sys import sysconfig @@ -27,6 +28,46 @@ def get_extra_flags(compiler_flags: str, compiler_py_flags_nodist: str) -> List[ return f"{flags} {py_flags_nodist}".split() +def fixup_build_ext(cmd): + """Function needed to make build_ext tests pass. + + When Python was built with --enable-shared on Unix, -L. is not enough to + find libpython.so, because regrtest runs in a tempdir, not in the + source directory where the .so lives. + + When Python was built with in debug mode on Windows, build_ext commands + need their debug attribute set, and it is not done automatically for + some reason. + + This function handles both of these things. Example use: + + cmd = build_ext(dist) + support.fixup_build_ext(cmd) + cmd.ensure_finalized() + + Unlike most other Unix platforms, Mac OS X embeds absolute paths + to shared libraries into executables, so the fixup is not needed there. + + Taken from distutils (was part of the CPython stdlib until Python 3.11) + """ + if os.name == 'nt': + cmd.debug = sys.executable.endswith('_d.exe') + elif sysconfig.get_config_var('Py_ENABLE_SHARED'): + # To further add to the shared builds fun on Unix, we can't just add + # library_dirs to the Extension() instance because that doesn't get + # plumbed through to the final compiler command. + runshared = sysconfig.get_config_var('RUNSHARED') + if runshared is None: + cmd.library_dirs = ['.'] + else: + if sys.platform == 'darwin': + cmd.library_dirs = [] + else: + name, equals, value = runshared.partition('=') + cmd.library_dirs = [d for d in value.split(os.pathsep) if d] + + + def compile_c_extension( generated_source_path: str, build_dir: Optional[str] = None, @@ -49,16 +90,15 @@ def compile_c_extension( static library of the common parser sources (this is useful in case you are creating multiple extensions). """ - import distutils.log - from distutils.core import Distribution, Extension - from distutils.tests.support import fixup_build_ext # type: ignore + import setuptools.logging - from distutils.ccompiler import new_compiler - from distutils.dep_util import newer_group - from distutils.sysconfig import customize_compiler + from setuptools import Extension, Distribution + from setuptools._distutils.dep_util import newer_group + from setuptools._distutils.ccompiler import new_compiler + from setuptools._distutils.sysconfig import customize_compiler if verbose: - distutils.log.set_threshold(distutils.log.DEBUG) + setuptools.logging.set_threshold(setuptools.logging.logging.DEBUG) source_file_path = pathlib.Path(generated_source_path) extension_name = source_file_path.stem