diff --git a/Doc/c-api/exceptions.rst b/Doc/c-api/exceptions.rst
index 525a773b7c768f..5d90248f85a5d4 100644
--- a/Doc/c-api/exceptions.rst
+++ b/Doc/c-api/exceptions.rst
@@ -100,7 +100,7 @@ For convenience, some of these functions will always return a
This is the most common way to set the error indicator. The first argument
specifies the exception type; it is normally one of the standard exceptions,
e.g. :c:data:`PyExc_RuntimeError`. You need not increment its reference count.
- The second argument is an error message; it is decoded from ``'utf-8``'.
+ The second argument is an error message; it is decoded from ``'utf-8'``.
.. c:function:: void PyErr_SetObject(PyObject *type, PyObject *value)
diff --git a/Doc/faq/programming.rst b/Doc/faq/programming.rst
index af4b489fc8a483..ef80808a1a4d5e 100644
--- a/Doc/faq/programming.rst
+++ b/Doc/faq/programming.rst
@@ -1826,6 +1826,55 @@ For example, here is the implementation of
return True
return False
+
+How can a subclass control what data is stored in an immutable instance?
+------------------------------------------------------------------------
+
+When subclassing an immutable type, override the :meth:`__new__` method
+instead of the :meth:`__init__` method. The latter only runs *after* an
+instance is created, which is too late to alter data in an immutable
+instance.
+
+All of these immutable classes have a different signature than their
+parent class:
+
+.. testcode::
+
+ from datetime import date
+
+ class FirstOfMonthDate(date):
+ "Always choose the first day of the month"
+ def __new__(cls, year, month, day):
+ return super().__new__(cls, year, month, 1)
+
+ class NamedInt(int):
+ "Allow text names for some numbers"
+ xlat = {'zero': 0, 'one': 1, 'ten': 10}
+ def __new__(cls, value):
+ value = cls.xlat.get(value, value)
+ return super().__new__(cls, value)
+
+ class TitleStr(str):
+ "Convert str to name suitable for a URL path"
+ def __new__(cls, s):
+ s = s.lower().replace(' ', '-')
+ s = ''.join([c for c in s if c.isalnum() or c == '-'])
+ return super().__new__(cls, s)
+
+The classes can be used like this:
+
+.. doctest::
+
+ >>> FirstOfMonthDate(2012, 2, 14)
+ FirstOfMonthDate(2012, 2, 1)
+ >>> NamedInt('ten')
+ 10
+ >>> NamedInt(20)
+ 20
+ >>> TitleStr('Blog: Why Python Rocks')
+ 'blog-why-python-rocks'
+
+
How do I cache method calls?
----------------------------
diff --git a/Doc/library/__main__.rst b/Doc/library/__main__.rst
index a64faf1bbe3c84..116a9a9d1d729f 100644
--- a/Doc/library/__main__.rst
+++ b/Doc/library/__main__.rst
@@ -1,25 +1,368 @@
-
-:mod:`__main__` --- Top-level script environment
-================================================
+:mod:`__main__` --- Top-level code environment
+==============================================
.. module:: __main__
- :synopsis: The environment where the top-level script is run.
+ :synopsis: The environment where top-level code is run. Covers command-line
+ interfaces, import-time behavior, and ``__name__ == '__main__'``.
--------------
-``'__main__'`` is the name of the scope in which top-level code executes.
-A module's __name__ is set equal to ``'__main__'`` when read from
-standard input, a script, or from an interactive prompt.
+In Python, the special name ``__main__`` is used for two important constructs:
+
+1. the name of the top-level environment of the program, which can be
+ checked using the ``__name__ == '__main__'`` expression; and
+2. the ``__main__.py`` file in Python packages.
+
+Both of these mechanisms are related to Python modules; how users interact with
+them and how they interact with each other. They are explained in detail
+below. If you're new to Python modules, see the tutorial section
+:ref:`tut-modules` for an introduction.
+
+
+.. _name_equals_main:
+
+``__name__ == '__main__'``
+---------------------------
+
+When a Python module or package is imported, ``__name__`` is set to the
+module's name. Usually, this is the name of the Python file itself without the
+``.py`` extension::
+
+ >>> import configparser
+ >>> configparser.__name__
+ 'configparser'
+
+If the file is part of a package, ``__name__`` will also include the parent
+package's path::
+
+ >>> from concurrent.futures import process
+ >>> process.__name__
+ 'concurrent.futures.process'
+
+However, if the module is executed in the top-level code environment,
+its ``__name__`` is set to the string ``'__main__'``.
+
+What is the "top-level code environment"?
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+``__main__`` is the name of the environment where top-level code is run.
+"Top-level code" is the first user-specified Python module that starts running.
+It's "top-level" because it imports all other modules that the program needs.
+Sometimes "top-level code" is called an *entry point* to the application.
+
+The top-level code environment can be:
+
+* the scope of an interactive prompt::
+
+ >>> __name__
+ '__main__'
+
+* the Python module passed to the Python interpreter as a file argument:
+
+ .. code-block:: shell-session
+
+ $ python3 helloworld.py
+ Hello, world!
+
+* the Python module or package passed to the Python interpreter with the
+ :option:`-m` argument:
+
+ .. code-block:: shell-session
+
+ $ python3 -m tarfile
+ usage: tarfile.py [-h] [-v] (...)
+
+* Python code read by the Python interpreter from standard input:
+
+ .. code-block:: shell-session
+
+ $ echo "import this" | python3
+ The Zen of Python, by Tim Peters
+
+ Beautiful is better than ugly.
+ Explicit is better than implicit.
+ ...
+
+* Python code passed to the Python interpreter with the :option:`-c` argument:
+
+ .. code-block:: shell-session
+
+ $ python3 -c "import this"
+ The Zen of Python, by Tim Peters
+
+ Beautiful is better than ugly.
+ Explicit is better than implicit.
+ ...
+
+In each of these situations, the top-level module's ``__name__`` is set to
+``'__main__'``.
+
+As a result, a module can discover whether or not it is running in the
+top-level environment by checking its own ``__name__``, which allows a common
+idiom for conditionally executing code when the module is not initialized from
+an import statement::
+
+ if __name__ == '__main__':
+ # Execute when the module is not initialized from an import statement.
+ ...
+
+.. seealso::
+
+ For a more detailed look at how ``__name__`` is set in all situations, see
+ the tutorial section :ref:`tut-modules`.
+
+
+Idiomatic Usage
+^^^^^^^^^^^^^^^
+
+Some modules contain code that is intended for script use only, like parsing
+command-line arguments or fetching data from standard input. When a module
+like this were to be imported from a different module, for example to unit test
+it, the script code would unintentionally execute as well.
+
+This is where using the ``if __name__ == '__main__'`` code block comes in
+handy. Code within this block won't run unless the module is executed in the
+top-level environment.
+
+Putting as few statements as possible in the block below ``if __name___ ==
+'__main__'`` can improve code clarity and correctness. Most often, a function
+named ``main`` encapsulates the program's primary behavior::
+
+ # echo.py
+
+ import shlex
+ import sys
+
+ def echo(phrase: str) -> None:
+ """A dummy wrapper around print."""
+ # for demonstration purposes, you can imagine that there is some
+ # valuable and reusable logic inside this function
+ print(phrase)
+
+ def main() -> int:
+ """Echo the input arguments to standard output"""
+ phrase = shlex.join(sys.argv)
+ echo(phrase)
+ return 0
+
+ if __name__ == '__main__':
+ sys.exit(main()) # next section explains the use of sys.exit
+
+Note that if the module didn't encapsulate code inside the ``main`` function
+but instead put it directly within the ``if __name__ == '__main__'`` block,
+the ``phrase`` variable would be global to the entire module. This is
+error-prone as other functions within the module could be unintentionally using
+the global variable instead of a local name. A ``main`` function solves this
+problem.
+
+Using a ``main`` function has the added benefit of the ``echo`` function itself
+being isolated and importable elsewhere. When ``echo.py`` is imported, the
+``echo`` and ``main`` functions will be defined, but neither of them will be
+called, because ``__name__ != '__main__'``.
+
+
+Packaging Considerations
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+``main`` functions are often used to create command-line tools by specifying
+them as entry points for console scripts. When this is done,
+`pip `_ inserts the function call into a template script,
+where the return value of ``main`` is passed into :func:`sys.exit`.
+For example::
+
+ sys.exit(main())
+
+Since the call to ``main`` is wrapped in :func:`sys.exit`, the expectation is
+that your function will return some value acceptable as an input to
+:func:`sys.exit`; typically, an integer or ``None`` (which is implicitly
+returned if your function does not have a return statement).
+
+By proactively following this convention ourselves, our module will have the
+same behavior when run directly (i.e. ``python3 echo.py``) as it will have if
+we later package it as a console script entry-point in a pip-installable
+package.
+
+In particular, be careful about returning strings from your ``main`` function.
+:func:`sys.exit` will interpret a string argument as a failure message, so
+your program will have an exit code of ``1``, indicating failure, and the
+string will be written to :data:`sys.stderr`. The ``echo.py`` example from
+earlier exemplifies using the ``sys.exit(main())`` convention.
+
+.. seealso::
+
+ `Python Packaging User Guide `_
+ contains a collection of tutorials and references on how to distribute and
+ install Python packages with modern tools.
+
+
+``__main__.py`` in Python Packages
+----------------------------------
+
+If you are not familiar with Python packages, see section :ref:`tut-packages`
+of the tutorial. Most commonly, the ``__main__.py`` file is used to provide
+a command-line interface for a package. Consider the following hypothetical
+package, "bandclass":
+
+.. code-block:: text
+
+ bandclass
+ ├── __init__.py
+ ├── __main__.py
+ └── student.py
+
+``__main__.py`` will be executed when the package itself is invoked
+directly from the command line using the :option:`-m` flag. For example:
+
+.. code-block:: shell-session
+
+ $ python3 -m bandclass
+
+This command will cause ``__main__.py`` to run. How you utilize this mechanism
+will depend on the nature of the package you are writing, but in this
+hypothetical case, it might make sense to allow the teacher to search for
+students::
+
+ # bandclass/__main__.py
+
+ import sys
+ from .student import search_students
+
+ student_name = sys.argv[2] if len(sys.argv) >= 2 else ''
+ print(f'Found student: {search_students(student_name)}')
+
+Note that ``from .student import search_students`` is an example of a relative
+import. This import style must be used when referencing modules within a
+package. For more details, see :ref:`intra-package-references` in the
+:ref:`tut-modules` section of the tutorial.
+
+Idiomatic Usage
+^^^^^^^^^^^^^^^
+
+The contents of ``__main__.py`` typically isn't fenced with
+``if __name__ == '__main__'`` blocks. Instead, those files are kept short,
+functions to execute from other modules. Those other modules can then be
+easily unit-tested and are properly reusable.
+
+If used, an ``if __name__ == '__main__'`` block will still work as expected
+for a ``__main__.py`` file within a package, because its ``__name__``
+attribute will include the package's path if imported::
+
+ >>> import asyncio.__main__
+ >>> asyncio.__main__.__name__
+ 'asyncio.__main__'
+
+This won't work for ``__main__.py`` files in the root directory of a .zip file
+though. Hence, for consistency, minimal ``__main__.py`` like the :mod:`venv`
+one mentioned above are preferred.
+
+.. seealso::
+
+ See :mod:`venv` for an example of a package with a minimal ``__main__.py``
+ in the standard library. It doesn't contain a ``if __name__ == '__main__'``
+ block. You can invoke it with ``python3 -m venv [directory]``.
+
+ See :mod:`runpy` for more details on the :option:`-m` flag to the
+ interpreter executable.
+
+ See :mod:`zipapp` for how to run applications packaged as *.zip* files. In
+ this case Python looks for a ``__main__.py`` file in the root directory of
+ the archive.
+
+
+
+``import __main__``
+-------------------
+
+Regardless of which module a Python program was started with, other modules
+running within that same program can import the top-level environment's scope
+(:term:`namespace`) by importing the ``__main__`` module. This doesn't import
+a ``__main__.py`` file but rather whichever module that received the special
+name ``'__main__'``.
+
+Here is an example module that consumes the ``__main__`` namespace::
+
+ # namely.py
+
+ import __main__
+
+ def did_user_define_their_name():
+ return 'my_name' in dir(__main__)
+
+ def print_user_name():
+ if not did_user_define_their_name():
+ raise ValueError('Define the variable `my_name`!')
+
+ if '__file__' in dir(__main__):
+ print(__main__.my_name, "found in file", __main__.__file__)
+ else:
+ print(__main__.my_name)
+
+Example usage of this module could be as follows::
+
+ # start.py
+
+ import sys
+
+ from namely import print_user_name
+
+ # my_name = "Dinsdale"
+
+ def main():
+ try:
+ print_user_name()
+ except ValueError as ve:
+ return str(ve)
+
+ if __name__ == "__main__":
+ sys.exit(main())
+
+Now, if we started our program, the result would look like this:
+
+.. code-block:: shell-session
+
+ $ python3 start.py
+ Define the variable `my_name`!
+
+The exit code of the program would be 1, indicating an error. Uncommenting the
+line with ``my_name = "Dinsdale"`` fixes the program and now it exits with
+status code 0, indicating success:
+
+.. code-block:: shell-session
+
+ $ python3 start.py
+ Dinsdale found in file /path/to/start.py
+
+Note that importing ``__main__`` doesn't cause any issues with unintentionally
+running top-level code meant for script use which is put in the
+``if __name__ == "__main__"`` block of the ``start`` module. Why does this work?
+
+Python inserts an empty ``__main__`` module in :attr:`sys.modules` at
+interpreter startup, and populates it by running top-level code. In our example
+this is the ``start`` module which runs line by line and imports ``namely``.
+In turn, ``namely`` imports ``__main__`` (which is really ``start``). That's an
+import cycle! Fortunately, since the partially populated ``__main__``
+module is present in :attr:`sys.modules`, Python passes that to ``namely``.
+See :ref:`Special considerations for __main__ ` in the
+import system's reference for details on how this works.
+
+The Python REPL is another example of a "top-level environment", so anything
+defined in the REPL becomes part of the ``__main__`` scope::
-A module can discover whether or not it is running in the main scope by
-checking its own ``__name__``, which allows a common idiom for conditionally
-executing code in a module when it is run as a script or with ``python
--m`` but not when it is imported::
+ >>> import namely
+ >>> namely.did_user_define_their_name()
+ False
+ >>> namely.print_user_name()
+ Traceback (most recent call last):
+ ...
+ ValueError: Define the variable `my_name`!
+ >>> my_name = 'Jabberwocky'
+ >>> namely.did_user_define_their_name()
+ True
+ >>> namely.print_user_name()
+ Jabberwocky
- if __name__ == "__main__":
- # execute only if run as a script
- main()
+Note that in this case the ``__main__`` scope doesn't contain a ``__file__``
+attribute as it's interactive.
-For a package, the same effect can be achieved by including a
-``__main__.py`` module, the contents of which will be executed when the
-module is run with ``-m``.
+The ``__main__`` scope is used in the implementation of :mod:`pdb` and
+:mod:`rlcompleter`.
diff --git a/Doc/library/base64.rst b/Doc/library/base64.rst
index f91547bd58403e..29e52ad48e1159 100644
--- a/Doc/library/base64.rst
+++ b/Doc/library/base64.rst
@@ -78,6 +78,8 @@ The modern interface provides:
these non-alphabet characters in the input result in a
:exc:`binascii.Error`.
+ For more information about the strict base64 check, see :func:`binascii.a2b_base64`
+
.. function:: standard_b64encode(s)
diff --git a/Doc/library/idle.rst b/Doc/library/idle.rst
index 91c540513cc8d7..d740973af9124e 100644
--- a/Doc/library/idle.rst
+++ b/Doc/library/idle.rst
@@ -518,7 +518,7 @@ and not restarting the Shell thereafter. This is especially useful
after adding imports at the top of a file. This also increases
possible attribute completions.
-Completion boxes intially exclude names beginning with '_' or, for
+Completion boxes initially exclude names beginning with '_' or, for
modules, not included in '__all__'. The hidden names can be accessed
by typing '_' after '.', either before or after the box is opened.
diff --git a/Doc/library/tkinter.rst b/Doc/library/tkinter.rst
index 39766486f64f91..a48bc13c8b436b 100644
--- a/Doc/library/tkinter.rst
+++ b/Doc/library/tkinter.rst
@@ -11,9 +11,8 @@
--------------
The :mod:`tkinter` package ("Tk interface") is the standard Python interface to
-the Tk GUI toolkit. Both Tk and :mod:`tkinter` are available on most Unix
-platforms, as well as on Windows systems. (Tk itself is not part of Python; it
-is maintained at ActiveState.)
+the Tcl/Tk GUI toolkit. Both Tk and :mod:`tkinter` are available on most Unix
+platforms, including macOS, as well as on Windows systems.
Running ``python -m tkinter`` from the command line should open a window
demonstrating a simple Tk interface, letting you know that :mod:`tkinter` is
@@ -30,53 +29,50 @@ make the experience more pythonic. This documentation will concentrate on these
additions and changes, and refer to the official Tcl/Tk documentation for
details that are unchanged.
-.. seealso::
-
- Tkinter documentation:
+.. note::
- `Python Tkinter Resources `_
- The Python Tkinter Topic Guide provides a great deal of information on using Tk
- from Python and links to other sources of information on Tk.
+ Tcl/Tk 8.5 (2007) introduced a modern set of themed user interface components
+ along with a new API to use them. Both old and new APIs are still available.
+ Most documentation you will find online still uses the old API and
+ can be woefully outdated.
- `TKDocs `_
- Extensive tutorial plus friendlier widget pages for some of the widgets.
+.. seealso::
- `Tkinter 8.5 reference: a GUI for Python `_
- On-line reference material.
+ * `TkDocs `_
+ Extensive tutorial on creating user interfaces with Tkinter. Explains key concepts,
+ and illustrates recommended approaches using the modern API.
- `Programming Python `_
- Book by Mark Lutz, has excellent coverage of Tkinter.
+ * `Tkinter 8.5 reference: a GUI for Python `_
+ Reference documentation for Tkinter 8.5 detailing available classes, methods, and options.
- `Modern Tkinter for Busy Python Developers `_
- Book by Mark Roseman about building attractive and modern graphical user interfaces with Python and Tkinter.
+ Tcl/Tk Resources:
- `Python and Tkinter Programming `_
- Book by John Grayson (ISBN 1-884777-81-3).
+ * `Tk commands `_
+ Comprehensive reference to each of the underlying Tcl/Tk commands used by Tkinter.
- Tcl/Tk documentation:
+ * `Tcl/Tk Home Page `_
+ Additional documentation, and links to Tcl/Tk core development.
- `Tk commands `_
- Most commands are available as :mod:`tkinter` or :mod:`tkinter.ttk` classes.
- Change '8.6' to match the version of your Tcl/Tk installation.
+ Books:
- `Tcl/Tk recent man pages `_
- Recent Tcl/Tk manuals on www.tcl.tk.
+ * `Modern Tkinter for Busy Python Developers `_
+ By Mark Roseman. (ISBN 978-1999149567)
- `ActiveState Tcl Home Page `_
- The Tk/Tcl development is largely taking place at ActiveState.
+ * `Python and Tkinter Programming `_
+ By Alan Moore. (ISBN 978-1788835886)
- `Tcl and the Tk Toolkit `_
- Book by John Ousterhout, the inventor of Tcl.
+ * `Programming Python `_
+ By Mark Lutz; has excellent coverage of Tkinter. (ISBN 978-0596158101)
- `Practical Programming in Tcl and Tk `_
- Brent Welch's encyclopedic book.
+ * `Tcl and the Tk Toolkit (2nd edition) `_
+ By John Ousterhout, inventor of Tcl/Tk, and Ken Jones; does not cover Tkinter. (ISBN 978-0321336330)
Architecture
------------
Tcl/Tk is not a single library but rather consists of a few distinct
-modules, each with a separate functionality and its own official
+modules, each with separate functionality and its own official
documentation. Python's binary releases also ship an add-on module
together with it.
@@ -106,33 +102,26 @@ Ttk
Ttk is distributed as part of Tk, starting with Tk version 8.5. Python
bindings are provided in a separate module, :mod:`tkinter.ttk`.
-Tix
- `Tix `_ is an older
- third-party Tcl package, an add-on for Tk that adds several new widgets.
- Python bindings are found in the :mod:`tkinter.tix` module.
- It's deprecated in favor of Ttk.
+Internally, Tk and Ttk use facilities of the underlying operating system,
+i.e., Xlib on Unix/X11, Cocoa on macOS, GDI on Windows.
+
+When your Python application uses a class in Tkinter, e.g., to create a widget,
+the :mod:`tkinter` module first assembles a Tcl/Tk command string. It passes that
+Tcl command string to an internal :mod:`_tkinter` binary module, which then
+calls the Tcl interpreter to evaluate it. The Tcl interpreter will then call into the
+Tk and/or Ttk packages, which will in turn make calls to Xlib, Cocoa, or GDI.
Tkinter Modules
---------------
-Most of the time, :mod:`tkinter` is all you really need, but a number of
-additional modules are available as well. The Tk interface is located in a
-binary module named :mod:`_tkinter`. This module contains the low-level
-interface to Tk, and should never be used directly by application programmers.
-It is usually a shared library (or DLL), but might in some cases be statically
-linked with the Python interpreter.
+Support for Tkinter is spread across several modules. Most applications will need the
+main :mod:`tkinter` module, as well as the :mod:`tkinter.ttk` module, which provides
+the modern themed widget set and API::
-In addition to the Tk interface module, :mod:`tkinter` includes a number of
-Python modules, :mod:`tkinter.constants` being one of the most important.
-Importing :mod:`tkinter` will automatically import :mod:`tkinter.constants`,
-so, usually, to use Tkinter all you need is a simple import statement::
-
- import tkinter
-
-Or, more often::
from tkinter import *
+ from tkinter import ttk
.. class:: Tk(screenName=None, baseName=None, className='Tk', useTk=1)
@@ -155,7 +144,10 @@ Or, more often::
subsystem initialized) by calling its :meth:`loadtk` method.
-Other modules that provide Tk support include:
+The modules that provide Tk support include:
+
+:mod:`tkinter`
+ Main Tkinter module.
:mod:`tkinter.colorchooser`
Dialog to let the user choose a color.
@@ -178,254 +170,278 @@ Other modules that provide Tk support include:
:mod:`tkinter.simpledialog`
Basic dialogs and convenience functions.
-:mod:`tkinter.dnd`
- Drag-and-drop support for :mod:`tkinter`. This is experimental and should
- become deprecated when it is replaced with the Tk DND.
-
-:mod:`turtle`
- Turtle graphics in a Tk window.
-
-
-Tkinter Life Preserver
-----------------------
-
-.. sectionauthor:: Matt Conway
+:mod:`tkinter.ttk`
+ Themed widget set introduced in Tk 8.5, providing modern alternatives
+ for many of the classic widgets in the main :mod:`tkinter` module.
+Additional modules:
-This section is not designed to be an exhaustive tutorial on either Tk or
-Tkinter. Rather, it is intended as a stop gap, providing some introductory
-orientation on the system.
+:mod:`_tkinter`
+ A binary module that contains the low-level interface to Tcl/Tk.
+ It is automatically imported by the main :mod:`tkinter` module,
+ and should never be used directly by application programmers.
+ It is usually a shared library (or DLL), but might in some cases be
+ statically linked with the Python interpreter.
-Credits:
+:mod:`idlelib`
+ Python's Integrated Development and Learning Environment (IDLE). Based
+ on :mod:`tkinter`.
-* Tk was written by John Ousterhout while at Berkeley.
+:mod:`tkinter.constants`
+ Symbolic constants that can be used in place of strings when passing
+ various parameters to Tkinter calls. Automatically imported by the
+ main :mod:`tkinter` module.
-* Tkinter was written by Steen Lumholt and Guido van Rossum.
+:mod:`tkinter.dnd`
+ (experimental) Drag-and-drop support for :mod:`tkinter`. This will
+ become deprecated when it is replaced with the Tk DND.
-* This Life Preserver was written by Matt Conway at the University of Virginia.
+:mod:`tkinter.tix`
+ (deprecated) An older third-party Tcl/Tk package that adds several new
+ widgets. Better alternatives for most can be found in :mod:`tkinter.ttk`.
-* The HTML rendering, and some liberal editing, was produced from a FrameMaker
- version by Ken Manheimer.
+:mod:`turtle`
+ Turtle graphics in a Tk window.
-* Fredrik Lundh elaborated and revised the class interface descriptions, to get
- them current with Tk 4.2.
-* Mike Clarkson converted the documentation to LaTeX, and compiled the User
- Interface chapter of the reference manual.
+Tkinter Life Preserver
+----------------------
+This section is not designed to be an exhaustive tutorial on either Tk or
+Tkinter. For that, refer to one of the external resources noted earlier.
+Instead, this section provides a very quick orientation to what a Tkinter
+application looks like, identifies foundational Tk concepts, and
+explains how the Tkinter wrapper is structured.
-How To Use This Section
-^^^^^^^^^^^^^^^^^^^^^^^
+The remainder of this section will help you to identify the classes,
+methods, and options you'll need in your Tkinter application, and where to
+find more detailed documentation on them, including in the official Tcl/Tk
+reference manual.
-This section is designed in two parts: the first half (roughly) covers
-background material, while the second half can be taken to the keyboard as a
-handy reference.
-When trying to answer questions of the form "how do I do blah", it is often best
-to find out how to do "blah" in straight Tk, and then convert this back into the
-corresponding :mod:`tkinter` call. Python programmers can often guess at the
-correct Python command by looking at the Tk documentation. This means that in
-order to use Tkinter, you will have to know a little bit about Tk. This document
-can't fulfill that role, so the best we can do is point you to the best
-documentation that exists. Here are some hints:
+A Hello World Program
+^^^^^^^^^^^^^^^^^^^^^
-* The authors strongly suggest getting a copy of the Tk man pages.
- Specifically, the man pages in the ``manN`` directory are most useful.
- The ``man3`` man pages describe the C interface to the Tk library and thus
- are not especially helpful for script writers.
+We'll start by walking through a "Hello World" application in Tkinter. This
+isn't the smallest one we could write, but has enough to illustrate some
+key concepts you'll need to know.
-* Addison-Wesley publishes a book called Tcl and the Tk Toolkit by John
- Ousterhout (ISBN 0-201-63337-X) which is a good introduction to Tcl and Tk for
- the novice. The book is not exhaustive, and for many details it defers to the
- man pages.
+::
-* :file:`tkinter/__init__.py` is a last resort for most, but can be a good
- place to go when nothing else makes sense.
+ from tkinter import *
+ from tkinter import ttk
+ root = Tk()
+ frm = ttk.Frame(root, padding=10)
+ frm.grid()
+ ttk.Label(frm, text="Hello World!").grid(column=0, row=0)
+ ttk.Button(frm, text="Quit", command=root.destroy).grid(column=1, row=0)
+ root.mainloop()
-A Simple Hello World Program
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+After the imports, the next line creates an instance of the :class:`Tk` class,
+which initializes Tk and creates its associated Tcl interpreter. It also
+creates a toplevel window, known as the root window, which serves as the main
+window of the application.
-::
+The following line creates a frame widget, which in this case will contain
+a label and a button we'll create next. The frame is fit inside the root
+window.
- import tkinter as tk
+The next line creates a label widget holding a static text string. The
+:meth:`grid` method is used to specify the relative layout (position) of the
+label within its containing frame widget, similar to how tables in HTML work.
- class Application(tk.Frame):
- def __init__(self, master=None):
- super().__init__(master)
- self.master = master
- self.pack()
- self.create_widgets()
+A button widget is then created, and placed to the right of the label. When
+pressed, it will call the :meth:`destroy` method of the root window.
- def create_widgets(self):
- self.hi_there = tk.Button(self)
- self.hi_there["text"] = "Hello World\n(click me)"
- self.hi_there["command"] = self.say_hi
- self.hi_there.pack(side="top")
+Finally, the :meth:`mainloop` method puts everything on the display, and
+responds to user input until the program terminates.
- self.quit = tk.Button(self, text="QUIT", fg="red",
- command=self.master.destroy)
- self.quit.pack(side="bottom")
- def say_hi(self):
- print("hi there, everyone!")
- root = tk.Tk()
- app = Application(master=root)
- app.mainloop()
+Important Tk Concepts
+^^^^^^^^^^^^^^^^^^^^^
+Even this simple program illustrates the following key Tk concepts:
-A (Very) Quick Look at Tcl/Tk
------------------------------
+widgets
+ A Tkinter user interface is made up of individual *widgets*. Each widget is
+ represented as a Python object, instantiated from classes like
+ :class:`ttk.Frame`, :class:`ttk.Label`, and :class:`ttk.Button`.
-The class hierarchy looks complicated, but in actual practice, application
-programmers almost always refer to the classes at the very bottom of the
-hierarchy.
+widget hierarchy
+ Widgets are arranged in a *hierarchy*. The label and button were contained
+ within a frame, which in turn was contained within the root window. When
+ creating each *child* widget, its *parent* widget is passed as the first
+ argument to the widget constructor.
-Notes:
+configuration options
+ Widgets have *configuration options*, which modify their appearance and
+ behavior, such as the text to display in a label or button. Different
+ classes of widgets will have different sets of options.
-* These classes are provided for the purposes of organizing certain functions
- under one namespace. They aren't meant to be instantiated independently.
+geometry management
+ Widgets aren't automatically added to the user interface when they are
+ created. A *geometry manager* like ``grid`` controls where in the
+ user interface they are placed.
-* The :class:`Tk` class is meant to be instantiated only once in an application.
- Application programmers need not instantiate one explicitly, the system creates
- one whenever any of the other classes are instantiated.
+event loop
+ Tkinter reacts to user input, changes from your program, and even refreshes
+ the display only when actively running an *event loop*. If your program
+ isn't running the event loop, your user interface won't update.
-* The :class:`Widget` class is not meant to be instantiated, it is meant only
- for subclassing to make "real" widgets (in C++, this is called an 'abstract
- class').
-To make use of this reference material, there will be times when you will need
-to know how to read short passages of Tk and how to identify the various parts
-of a Tk command. (See section :ref:`tkinter-basic-mapping` for the
-:mod:`tkinter` equivalents of what's below.)
+Understanding How Tkinter Wraps Tcl/Tk
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-Tk scripts are Tcl programs. Like all Tcl programs, Tk scripts are just lists
-of tokens separated by spaces. A Tk widget is just its *class*, the *options*
-that help configure it, and the *actions* that make it do useful things.
+When your application uses Tkinter's classes and methods, internally Tkinter
+is assembling strings representing Tcl/Tk commands, and executing those
+commands in the Tcl interpreter attached to your applicaton's :class:`Tk`
+instance.
-To make a widget in Tk, the command is always of the form::
+Whether it's trying to navigate reference documentation, trying to find
+the right method or option, adapting some existing code, or debugging your
+Tkinter application, there are times that it will be useful to understand
+what those underlying Tcl/Tk commands look like.
- classCommand newPathname options
+To illustrate, here is the Tcl/Tk equivalent of the main part of the Tkinter
+script above.
-*classCommand*
- denotes which kind of widget to make (a button, a label, a menu...)
-
-.. index:: single: . (dot); in Tkinter
+::
-*newPathname*
- is the new name for this widget. All names in Tk must be unique. To help
- enforce this, widgets in Tk are named with *pathnames*, just like files in a
- file system. The top level widget, the *root*, is called ``.`` (period) and
- children are delimited by more periods. For example,
- ``.myApp.controlPanel.okButton`` might be the name of a widget.
+ ttk::frame .frm -padding 10
+ grid .frm
+ grid [ttk::label .frm.lbl -text "Hello World!"] -column 0 -row 0
+ grid [ttk::button .frm.btn -text "Quit" -command "destroy ."] -column 1 -row 0
-*options*
- configure the widget's appearance and in some cases, its behavior. The options
- come in the form of a list of flags and values. Flags are preceded by a '-',
- like Unix shell command flags, and values are put in quotes if they are more
- than one word.
-For example::
+Tcl's syntax is similar to many shell languages, where the first word is the
+command to be executed, with arguments to that command following it, separated
+by spaces. Without getting into too many details, notice the following:
- button .fred -fg red -text "hi there"
- ^ ^ \______________________/
- | | |
- class new options
- command widget (-opt val -opt val ...)
+* The commands used to create widgets (like ``ttk::frame``) correspond to
+ widget classes in Tkinter.
-Once created, the pathname to the widget becomes a new command. This new
-*widget command* is the programmer's handle for getting the new widget to
-perform some *action*. In C, you'd express this as someAction(fred,
-someOptions), in C++, you would express this as fred.someAction(someOptions),
-and in Tk, you say::
+* Tcl widget options (like ``-text``) correspond to keyword arguments in
+ Tkinter.
- .fred someAction someOptions
+* Widgets are referred to by a *pathname* in Tcl (like ``.frm.btn``),
+ whereas Tkinter doesn't use names but object references.
-Note that the object name, ``.fred``, starts with a dot.
+* A widget's place in the widget hierarchy is encoded in its (hierarchical)
+ pathname, which uses a ``.`` (dot) as a path separator. The pathname for
+ the root window is just ``.`` (dot). In Tkinter, the hierarchy is defined
+ not by pathname but by specifying the parent widget when creating each
+ child widget.
-As you'd expect, the legal values for *someAction* will depend on the widget's
-class: ``.fred disable`` works if fred is a button (fred gets greyed out), but
-does not work if fred is a label (disabling of labels is not supported in Tk).
+* Operations which are implemented as separate *commands* in Tcl (like
+ ``grid`` or ``destroy``) are represented as *methods* on Tkinter widget
+ objects. As you'll see shortly, at other times Tcl uses what appear to be
+ method calls on widget objects, which more closely mirror what would is
+ used in Tkinter.
-The legal values of *someOptions* is action dependent. Some actions, like
-``disable``, require no arguments, others, like a text-entry box's ``delete``
-command, would need arguments to specify what range of text to delete.
+How do I...? What option does...?
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-.. _tkinter-basic-mapping:
+If you're not sure how to do something in Tkinter, and you can't immediately
+find it in the tutorial or reference documentation you're using, there are a
+few strategies that can be helpful.
-Mapping Basic Tk into Tkinter
------------------------------
+First, remember that the details of how individual widgets work may vary
+across different versions of both Tkinter and Tcl/Tk. If you're searching
+documentation, make sure it corresponds to the Python and Tcl/Tk versions
+installed on your system.
-Class commands in Tk correspond to class constructors in Tkinter. ::
+When searching for how to use an API, it helps to know the exact name of the
+class, option, or method that you're using. Introspection, either in an
+interactive Python shell or with :func:`print`, can help you identify what
+you need.
- button .fred =====> fred = Button()
+To find out what configuration options are available on any widget, call its
+:meth:`configure` method, which returns a dictionary containing a variety of
+information about each object, including its default and current values. Use
+:meth:`keys` to get just the names of each option.
-The master of an object is implicit in the new name given to it at creation
-time. In Tkinter, masters are specified explicitly. ::
+::
- button .panel.fred =====> fred = Button(panel)
+ btn = ttk.Button(frm, ...)
+ print(btn.configure().keys())
-The configuration options in Tk are given in lists of hyphened tags followed by
-values. In Tkinter, options are specified as keyword-arguments in the instance
-constructor, and keyword-args for configure calls or as instance indices, in
-dictionary style, for established instances. See section
-:ref:`tkinter-setting-options` on setting options. ::
+As most widgets have many configuration options in common, it can be useful
+to find out which are specific to a particular widget class. Comparing the
+list of options to that of a simpler widget, like a frame, is one way to
+do that.
- button .fred -fg red =====> fred = Button(panel, fg="red")
- .fred configure -fg red =====> fred["fg"] = red
- OR ==> fred.config(fg="red")
+::
-In Tk, to perform an action on a widget, use the widget name as a command, and
-follow it with an action name, possibly with arguments (options). In Tkinter,
-you call methods on the class instance to invoke actions on the widget. The
-actions (methods) that a given widget can perform are listed in
-:file:`tkinter/__init__.py`. ::
+ print(set(btn.configure().keys()) - set(frm.configure().keys()))
- .fred invoke =====> fred.invoke()
+Similarly, you can find the available methods for a widget object using the
+standard :func:`dir` function. If you try it, you'll see there are over 200
+common widget methods, so again identifying those specific to a widget class
+is helpful.
-To give a widget to the packer (geometry manager), you call pack with optional
-arguments. In Tkinter, the Pack class holds all this functionality, and the
-various forms of the pack command are implemented as methods. All widgets in
-:mod:`tkinter` are subclassed from the Packer, and so inherit all the packing
-methods. See the :mod:`tkinter.tix` module documentation for additional
-information on the Form geometry manager. ::
+::
- pack .fred -side left =====> fred.pack(side="left")
+ print(dir(btn))
+ print(set(dir(btn)) - set(dir(frm)))
-How Tk and Tkinter are Related
-------------------------------
+Navigating the Tcl/Tk Reference Manual
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-From the top down:
+As noted, the official `Tk commands `_
+reference manual (man pages) is often the most accurate description of what
+specific operations on widgets do. Even when you know the name of the option
+or method that you need, you may still have a few places to look.
-Your App Here (Python)
- A Python application makes a :mod:`tkinter` call.
+While all operations in Tkinter are implemented as method calls on widget
+objects, you've seen that many Tcl/Tk operations appear as commands that
+take a widget pathname as its first parameter, followed by optional
+parameters, e.g.
-tkinter (Python Package)
- This call (say, for example, creating a button widget), is implemented in
- the :mod:`tkinter` package, which is written in Python. This Python
- function will parse the commands and the arguments and convert them into a
- form that makes them look as if they had come from a Tk script instead of
- a Python script.
+::
-_tkinter (C)
- These commands and their arguments will be passed to a C function in the
- :mod:`_tkinter` - note the underscore - extension module.
+ destroy .
+ grid .frm.btn -column 0 -row 0
-Tk Widgets (C and Tcl)
- This C function is able to make calls into other C modules, including the C
- functions that make up the Tk library. Tk is implemented in C and some Tcl.
- The Tcl part of the Tk widgets is used to bind certain default behaviors to
- widgets, and is executed once at the point where the Python :mod:`tkinter`
- package is imported. (The user never sees this stage).
+Others, however, look more like methods called on a widget object (in fact,
+when you create a widget in Tcl/Tk, it creates a Tcl command with the name
+of the widget pathname, with the first parameter to that command being the
+name of a method to call).
-Tk (C)
- The Tk part of the Tk Widgets implement the final mapping to ...
+::
-Xlib (C)
- the Xlib library to draw graphics on the screen.
+ .frm.btn invoke
+ .frm.lbl configure -text "Goodbye"
+
+
+In the official Tcl/Tk reference documentation, you'll find most operations
+that look like method calls on the man page for a specific widget (e.g.,
+you'll find the :meth:`invoke` method on the
+`ttk::button `_
+man page), while functions that take a widget as a parameter often have
+their own man page (e.g.,
+`grid `_).
+
+You'll find many common options and methods in the
+`options `_ or
+`ttk::widget `_ man
+pages, while others are found in the man page for a specific widget class.
+
+You'll also find that many Tkinter methods have compound names, e.g.,
+:func:`winfo_x`, :func:`winfo_height`, :func:`winfo_viewable`. You'd find
+documentation for all of these in the
+`winfo `_ man page.
+
+.. note::
+ Somewhat confusingly, there are also methods on all Tkinter widgets
+ that don't actually operate on the widget, but operate at a global
+ scope, independent of any widget. Examples are methods for accessing
+ the clipboard or the system bell. (They happen to be implemented as
+ methods in the base :class:`Widget` class that all Tkinter widgets
+ inherit from).
Threading model
diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst
index 47d6c3a2e38985..53cf5429b54c97 100644
--- a/Doc/library/typing.rst
+++ b/Doc/library/typing.rst
@@ -321,11 +321,11 @@ not generic but implicitly inherits from ``Iterable[Any]``::
User defined generic type aliases are also supported. Examples::
from collections.abc import Iterable
- from typing import TypeVar, Union
+ from typing import TypeVar
S = TypeVar('S')
- Response = Union[Iterable[S], int]
+ Response = Iterable[S] | int
- # Return type here is same as Union[Iterable[str], int]
+ # Return type here is same as Iterable[str] | int
def response(query: str) -> Response[str]:
...
@@ -588,9 +588,9 @@ These can be used as types in annotations using ``[]``, each having a unique syn
.. data:: Union
- Union type; ``Union[X, Y]`` means either X or Y.
+ Union type; ``Union[X, Y]`` is equivalent to ``X | Y`` and means either X or Y.
- To define a union, use e.g. ``Union[int, str]``. Details:
+ To define a union, use e.g. ``Union[int, str]`` or the shorthand ``int | str``. Details:
* The arguments must be types and there must be at least one.
@@ -604,18 +604,16 @@ These can be used as types in annotations using ``[]``, each having a unique syn
* Redundant arguments are skipped, e.g.::
- Union[int, str, int] == Union[int, str]
+ Union[int, str, int] == Union[int, str] == int | str
* When comparing unions, the argument order is ignored, e.g.::
Union[int, str] == Union[str, int]
- * You cannot subclass or instantiate a union.
+ * You cannot subclass or instantiate a ``Union``.
* You cannot write ``Union[X][Y]``.
- * You can use ``Optional[X]`` as a shorthand for ``Union[X, None]``.
-
.. versionchanged:: 3.7
Don't remove explicit subclasses from unions at runtime.
@@ -627,7 +625,7 @@ These can be used as types in annotations using ``[]``, each having a unique syn
Optional type.
- ``Optional[X]`` is equivalent to ``Union[X, None]``.
+ ``Optional[X]`` is equivalent to ``X | None`` (or ``Union[X, None]``).
Note that this is not the same concept as an optional argument,
which is one that has a default. An optional argument with a
@@ -644,6 +642,10 @@ These can be used as types in annotations using ``[]``, each having a unique syn
def foo(arg: Optional[int] = None) -> None:
...
+ .. versionchanged:: 3.10
+ Optional can now be written as ``X | None``. See
+ :ref:`union type expressions`.
+
.. data:: Callable
Callable type; ``Callable[[int], str]`` is a function of (int) -> str.
@@ -770,7 +772,7 @@ These can be used as types in annotations using ``[]``, each having a unique syn
:ref:`type variables `, and unions of any of these types.
For example::
- def new_non_team_user(user_class: Type[Union[BasicUser, ProUser]]): ...
+ def new_non_team_user(user_class: Type[BasicUser | ProUser]): ...
``Type[Any]`` is equivalent to ``Type`` which in turn is equivalent
to ``type``, which is the root of Python's metaclass hierarchy.
@@ -951,7 +953,7 @@ These can be used as types in annotations using ``[]``, each having a unique syn
conditional code flow and applying the narrowing to a block of code. The
conditional expression here is sometimes referred to as a "type guard"::
- def is_str(val: Union[str, float]):
+ def is_str(val: str | float):
# "isinstance" type guard
if isinstance(val, str):
# Type of ``val`` is narrowed to ``str``
@@ -2031,7 +2033,7 @@ Introspection helpers
For a typing object of the form ``X[Y, Z, ...]`` these functions return
``X`` and ``(Y, Z, ...)``. If ``X`` is a generic alias for a builtin or
:mod:`collections` class, it gets normalized to the original class.
- If ``X`` is a :class:`Union` or :class:`Literal` contained in another
+ If ``X`` is a union or :class:`Literal` contained in another
generic type, the order of ``(Y, Z, ...)`` may be different from the order
of the original arguments ``[Y, Z, ...]`` due to type caching.
For unsupported objects return ``None`` and ``()`` correspondingly.
@@ -2056,7 +2058,7 @@ Introspection helpers
year: int
is_typeddict(Film) # => True
- is_typeddict(Union[list, str]) # => False
+ is_typeddict(list | str) # => False
.. versionadded:: 3.10
diff --git a/Doc/library/unittest.rst b/Doc/library/unittest.rst
index 99c2f6e029448e..f0fba94677a917 100644
--- a/Doc/library/unittest.rst
+++ b/Doc/library/unittest.rst
@@ -151,6 +151,10 @@ The above examples show the most commonly used :mod:`unittest` features which
are sufficient to meet many everyday testing needs. The remainder of the
documentation explores the full feature set from first principles.
+.. versionchanged:: 3.11
+ The behavior of returning a value from a test method (other than the default
+ ``None`` value), is now deprecated.
+
.. _unittest-command-line-interface:
diff --git a/Doc/reference/import.rst b/Doc/reference/import.rst
index 81a124f745ab6a..39fcba015b6947 100644
--- a/Doc/reference/import.rst
+++ b/Doc/reference/import.rst
@@ -975,6 +975,8 @@ should expose ``XXX.YYY.ZZZ`` as a usable expression, but .moduleY is
not a valid expression.
+.. _import-dunder-main:
+
Special considerations for __main__
===================================
diff --git a/Doc/tools/susp-ignored.csv b/Doc/tools/susp-ignored.csv
index 1fde253feac2fa..35bcbd0404976f 100644
--- a/Doc/tools/susp-ignored.csv
+++ b/Doc/tools/susp-ignored.csv
@@ -110,6 +110,7 @@ howto/pyporting,,::,Programming Language :: Python :: 3
howto/regex,,::,
howto/regex,,:foo,(?:foo)
howto/urllib2,,:password,"""joe:password@example.com"""
+library/__main__,,`,
library/ast,,:upper,lower:upper
library/ast,,:step,lower:upper:step
library/audioop,,:ipos,"# factor = audioop.findfactor(in_test[ipos*2:ipos*2+len(out_test)],"
@@ -233,6 +234,12 @@ library/tarfile,,:xz,'r:xz'
library/tarfile,,:xz,'w:xz'
library/time,,:mm,
library/time,,:ss,
+library/tkinter,294,::,ttk::frame .frm -padding 10
+library/tkinter,294,::,"grid [ttk::label .frm.lbl -text ""Hello World!""] -column 0 -row 0"
+library/tkinter,294,::,"grid [ttk::button .frm.btn -text ""Quit"" -command ""destroy .""] -column 1 -row 0"
+library/tkinter,304,::,ttk::frame
+library/tkinter,402,::,ttk::button
+library/tkinter,410,::,ttk::widget
library/tracemalloc,,:limit,"for index, stat in enumerate(top_stats[:limit], 1):"
library/turtle,,::,Example::
library/unittest,,:foo,"self.assertEqual(cm.output, ['INFO:foo:first message',"
diff --git a/Doc/tutorial/modules.rst b/Doc/tutorial/modules.rst
index af595e5ca04d7e..a495c50cbde880 100644
--- a/Doc/tutorial/modules.rst
+++ b/Doc/tutorial/modules.rst
@@ -533,6 +533,8 @@ importing module needs to use submodules with the same name from different
packages.
+.. _intra-package-references:
+
Intra-package References
------------------------
diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst
index ae6ad9c52ccade..6040daaf8f846d 100644
--- a/Doc/whatsnew/3.10.rst
+++ b/Doc/whatsnew/3.10.rst
@@ -1401,6 +1401,13 @@ subclasses with the :func:`runtime_checkable` decorator
if they want runtime protocols.
(Contributed by Yurii Karabas in :issue:`38908`)
+Importing from the ``typing.io`` and ``typing.re`` submodules will now emit
+:exc:`DeprecationWarning`. These submodules have been deprecated since
+Python 3.8 and will be removed in a future version of Python. Anything
+belonging to those submodules should be imported directly from
+:mod:`typing` instead.
+(Contributed by Sebastian Rittau in :issue:`38291`)
+
unittest
--------
@@ -1696,6 +1703,12 @@ Deprecated
requires a :ref:`debug build of Python `.
(Contributed by Victor Stinner in :issue:`44584`.)
+* Importing from the ``typing.io`` and ``typing.re`` submodules will now emit
+ :exc:`DeprecationWarning`. These submodules will be removed in a future version
+ of Python. Anything belonging to these submodules should be imported directly
+ from :mod:`typing` instead.
+ (Contributed by Sebastian Rittau in :issue:`38291`)
+
.. _whatsnew310-removed:
Removed
diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst
index 49b4364be9bd7f..306385c2a90aaf 100644
--- a/Doc/whatsnew/3.11.rst
+++ b/Doc/whatsnew/3.11.rst
@@ -169,6 +169,14 @@ Other Language Changes
(Contributed by Serhiy Storchaka in :issue:`12022`.)
+Other CPython Implementation Changes
+====================================
+
+* Special methods :meth:`complex.__complex__` and :meth:`bytes.__bytes__` are implemented to
+ support :class:`typing.SupportsComplex` and :class:`typing.SupportsBytes` protocols.
+ (Contributed by Mark Dickinson and Dong-hee Na in :issue:`24234`.)
+
+
New Modules
===========
@@ -395,3 +403,7 @@ Removed
:func:`~gettext.install` are also removed, since they are only used for
the ``l*gettext()`` functions.
(Contributed by Dong-hee Na and Serhiy Storchaka in :issue:`44235`.)
+
+* The behavior of returning a value from a :class:`~unittest.TestCase` and
+ :class:`~unittest.IsolatedAsyncioTestCase` test methods (other than the default ``None``
+ value), is now deprecated.
diff --git a/Lib/base64.py b/Lib/base64.py
index e1256ad9358e7a..ffe2ce7332baa9 100755
--- a/Lib/base64.py
+++ b/Lib/base64.py
@@ -76,15 +76,16 @@ def b64decode(s, altchars=None, validate=False):
normal base-64 alphabet nor the alternative alphabet are discarded prior
to the padding check. If validate is True, these non-alphabet characters
in the input result in a binascii.Error.
+ For more information about the strict base64 check, see:
+
+ https://docs.python.org/3.11/library/binascii.html#binascii.a2b_base64
"""
s = _bytes_from_decode_data(s)
if altchars is not None:
altchars = _bytes_from_decode_data(altchars)
assert len(altchars) == 2, repr(altchars)
s = s.translate(bytes.maketrans(altchars, b'+/'))
- if validate and not re.fullmatch(b'[A-Za-z0-9+/]*={0,2}', s):
- raise binascii.Error('Non-base64 digit found')
- return binascii.a2b_base64(s)
+ return binascii.a2b_base64(s, strict_mode=validate)
def standard_b64encode(s):
diff --git a/Lib/compileall.py b/Lib/compileall.py
index 454465bd8fc4ef..3e00477616df8f 100644
--- a/Lib/compileall.py
+++ b/Lib/compileall.py
@@ -221,8 +221,8 @@ def compile_file(fullname, ddir=None, force=False, rx=None, quiet=0,
if not force:
try:
mtime = int(os.stat(fullname).st_mtime)
- expect = struct.pack('<4sll', importlib.util.MAGIC_NUMBER,
- 0, mtime)
+ expect = struct.pack('<4sLL', importlib.util.MAGIC_NUMBER,
+ 0, mtime & 0xFFFF_FFFF)
for cfile in opt_cfiles.values():
with open(cfile, 'rb') as chandle:
actual = chandle.read(12)
diff --git a/Lib/test/test_bytes.py b/Lib/test/test_bytes.py
index 13ad238242d0c5..c2fe2cc25420f8 100644
--- a/Lib/test/test_bytes.py
+++ b/Lib/test/test_bytes.py
@@ -981,6 +981,18 @@ def test_sq_item(self):
class BytesTest(BaseBytesTest, unittest.TestCase):
type2test = bytes
+ def test__bytes__(self):
+ foo = b'foo\x00bar'
+ self.assertEqual(foo.__bytes__(), foo)
+ self.assertEqual(type(foo.__bytes__()), self.type2test)
+
+ class bytes_subclass(bytes):
+ pass
+
+ bar = bytes_subclass(b'bar\x00foo')
+ self.assertEqual(bar.__bytes__(), bar)
+ self.assertEqual(type(bar.__bytes__()), self.type2test)
+
def test_getitem_error(self):
b = b'python'
msg = "byte indices must be integers or slices"
diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py
index 9165f45db64f34..db029ef731c382 100644
--- a/Lib/test/test_capi.py
+++ b/Lib/test/test_capi.py
@@ -623,6 +623,18 @@ def test_fatal_error(self):
''')
self.check_fatal_error(code, expected)
+ def test_pyobject_repr_from_null(self):
+ s = _testcapi.pyobject_repr_from_null()
+ self.assertEqual(s, '')
+
+ def test_pyobject_str_from_null(self):
+ s = _testcapi.pyobject_str_from_null()
+ self.assertEqual(s, '')
+
+ def test_pyobject_bytes_from_null(self):
+ s = _testcapi.pyobject_bytes_from_null()
+ self.assertEqual(s, b'')
+
class TestPendingCalls(unittest.TestCase):
diff --git a/Lib/test/test_code.py b/Lib/test/test_code.py
index 9d6b465115ca26..66c84a953cb3d2 100644
--- a/Lib/test/test_code.py
+++ b/Lib/test/test_code.py
@@ -212,7 +212,7 @@ def func(): pass
CodeType = type(co)
# test code constructor
- return CodeType(co.co_argcount,
+ CodeType(co.co_argcount,
co.co_posonlyargcount,
co.co_kwonlyargcount,
co.co_nlocals,
diff --git a/Lib/test/test_compileall.py b/Lib/test/test_compileall.py
index 4612953cf8df39..cc51b8c06a0637 100644
--- a/Lib/test/test_compileall.py
+++ b/Lib/test/test_compileall.py
@@ -80,9 +80,28 @@ def timestamp_metadata(self):
with open(self.bc_path, 'rb') as file:
data = file.read(12)
mtime = int(os.stat(self.source_path).st_mtime)
- compare = struct.pack('<4sll', importlib.util.MAGIC_NUMBER, 0, mtime)
+ compare = struct.pack('<4sLL', importlib.util.MAGIC_NUMBER, 0,
+ mtime & 0xFFFF_FFFF)
return data, compare
+ def test_year_2038_mtime_compilation(self):
+ # Test to make sure we can handle mtimes larger than what a 32-bit
+ # signed number can hold as part of bpo-34990
+ try:
+ os.utime(self.source_path, (2**32 - 1, 2**32 - 1))
+ except (OverflowError, OSError):
+ self.skipTest("filesystem doesn't support timestamps near 2**32")
+ self.assertTrue(compileall.compile_file(self.source_path))
+
+ def test_larger_than_32_bit_times(self):
+ # This is similar to the test above but we skip it if the OS doesn't
+ # support modification times larger than 32-bits.
+ try:
+ os.utime(self.source_path, (2**35, 2**35))
+ except (OverflowError, OSError):
+ self.skipTest("filesystem doesn't support large timestamps")
+ self.assertTrue(compileall.compile_file(self.source_path))
+
def recreation_check(self, metadata):
"""Check that compileall recreates bytecode when the new metadata is
used."""
@@ -101,7 +120,7 @@ def recreation_check(self, metadata):
def test_mtime(self):
# Test a change in mtime leads to a new .pyc.
- self.recreation_check(struct.pack('<4sll', importlib.util.MAGIC_NUMBER,
+ self.recreation_check(struct.pack('<4sLL', importlib.util.MAGIC_NUMBER,
0, 1))
def test_magic_number(self):
diff --git a/Lib/test/test_complex.py b/Lib/test/test_complex.py
index abd7e39cc5ae73..1cd025ed53a5d0 100644
--- a/Lib/test/test_complex.py
+++ b/Lib/test/test_complex.py
@@ -499,6 +499,18 @@ def __complex__(self):
self.assertEqual(complex(complex1(1j)), 2j)
self.assertRaises(TypeError, complex, complex2(1j))
+ def test___complex__(self):
+ z = 3 + 4j
+ self.assertEqual(z.__complex__(), z)
+ self.assertEqual(type(z.__complex__()), complex)
+
+ class complex_subclass(complex):
+ pass
+
+ z = complex_subclass(3 + 4j)
+ self.assertEqual(z.__complex__(), 3 + 4j)
+ self.assertEqual(type(z.__complex__()), complex)
+
@support.requires_IEEE_754
def test_constructor_special_numbers(self):
class complex2(complex):
diff --git a/Lib/test/test_doctest.py b/Lib/test/test_doctest.py
index 8f761d7a52c23e..571dc78bf5076e 100644
--- a/Lib/test/test_doctest.py
+++ b/Lib/test/test_doctest.py
@@ -668,7 +668,7 @@ def non_Python_modules(): r"""
>>> import builtins
>>> tests = doctest.DocTestFinder().find(builtins)
- >>> 816 < len(tests) < 836 # approximate number of objects with docstrings
+ >>> 820 < len(tests) < 840 # approximate number of objects with docstrings
True
>>> real_tests = [t for t in tests if len(t.examples) > 0]
>>> len(real_tests) # objects that actually have doctests
diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py
index f0f0e6f6069da7..94a95c7a00bf20 100644
--- a/Lib/test/test_subprocess.py
+++ b/Lib/test/test_subprocess.py
@@ -719,6 +719,8 @@ def test_pipesizes(self):
# However, this function is not yet in _winapi.
p.stdin.write(b"pear")
p.stdin.close()
+ p.stdout.close()
+ p.stderr.close()
finally:
p.kill()
p.wait()
@@ -746,6 +748,8 @@ def test_pipesize_default(self):
# On other platforms we cannot test the pipe size (yet). But above
# code using pipesize=-1 should not crash.
p.stdin.close()
+ p.stdout.close()
+ p.stderr.close()
finally:
p.kill()
p.wait()
@@ -3243,6 +3247,7 @@ def test_send_signal_race2(self):
with mock.patch.object(p, 'poll', new=lambda: None):
p.returncode = None
p.send_signal(signal.SIGTERM)
+ p.kill()
def test_communicate_repeated_call_after_stdout_close(self):
proc = subprocess.Popen([sys.executable, '-c',
diff --git a/Lib/test/test_tokenize.py b/Lib/test/test_tokenize.py
index 4bce1ca9c76f7c..f8b16e52976451 100644
--- a/Lib/test/test_tokenize.py
+++ b/Lib/test/test_tokenize.py
@@ -3,7 +3,7 @@
from tokenize import (tokenize, _tokenize, untokenize, NUMBER, NAME, OP,
STRING, ENDMARKER, ENCODING, tok_name, detect_encoding,
open as tokenize_open, Untokenizer, generate_tokens,
- NEWLINE)
+ NEWLINE, _generate_tokens_from_c_tokenizer)
from io import BytesIO, StringIO
import unittest
from unittest import TestCase, mock
@@ -12,7 +12,6 @@
import os
import token
-
# Converts a source string into a list of textual representation
# of the tokens such as:
# ` NAME 'if' (1, 0) (1, 2)`
@@ -1654,5 +1653,865 @@ def test_indentation_semantics_retained(self):
self.check_roundtrip(code)
+class CTokenizeTest(TestCase):
+ def check_tokenize(self, s, expected):
+ # Format the tokens in s in a table format.
+ # The ENDMARKER and final NEWLINE are omitted.
+ with self.subTest(source=s):
+ result = stringify_tokens_from_source(
+ _generate_tokens_from_c_tokenizer(s), s
+ )
+ self.assertEqual(result, expected.rstrip().splitlines())
+
+ def test_int(self):
+
+ self.check_tokenize('0xff <= 255', """\
+ NUMBER '0xff' (1, 0) (1, 4)
+ LESSEQUAL '<=' (1, 5) (1, 7)
+ NUMBER '255' (1, 8) (1, 11)
+ """)
+
+ self.check_tokenize('0b10 <= 255', """\
+ NUMBER '0b10' (1, 0) (1, 4)
+ LESSEQUAL '<=' (1, 5) (1, 7)
+ NUMBER '255' (1, 8) (1, 11)
+ """)
+
+ self.check_tokenize('0o123 <= 0O123', """\
+ NUMBER '0o123' (1, 0) (1, 5)
+ LESSEQUAL '<=' (1, 6) (1, 8)
+ NUMBER '0O123' (1, 9) (1, 14)
+ """)
+
+ self.check_tokenize('1234567 > ~0x15', """\
+ NUMBER '1234567' (1, 0) (1, 7)
+ GREATER '>' (1, 8) (1, 9)
+ TILDE '~' (1, 10) (1, 11)
+ NUMBER '0x15' (1, 11) (1, 15)
+ """)
+
+ self.check_tokenize('2134568 != 1231515', """\
+ NUMBER '2134568' (1, 0) (1, 7)
+ NOTEQUAL '!=' (1, 8) (1, 10)
+ NUMBER '1231515' (1, 11) (1, 18)
+ """)
+
+ self.check_tokenize('(-124561-1) & 200000000', """\
+ LPAR '(' (1, 0) (1, 1)
+ MINUS '-' (1, 1) (1, 2)
+ NUMBER '124561' (1, 2) (1, 8)
+ MINUS '-' (1, 8) (1, 9)
+ NUMBER '1' (1, 9) (1, 10)
+ RPAR ')' (1, 10) (1, 11)
+ AMPER '&' (1, 12) (1, 13)
+ NUMBER '200000000' (1, 14) (1, 23)
+ """)
+
+ self.check_tokenize('0xdeadbeef != -1', """\
+ NUMBER '0xdeadbeef' (1, 0) (1, 10)
+ NOTEQUAL '!=' (1, 11) (1, 13)
+ MINUS '-' (1, 14) (1, 15)
+ NUMBER '1' (1, 15) (1, 16)
+ """)
+
+ self.check_tokenize('0xdeadc0de & 12345', """\
+ NUMBER '0xdeadc0de' (1, 0) (1, 10)
+ AMPER '&' (1, 11) (1, 12)
+ NUMBER '12345' (1, 13) (1, 18)
+ """)
+
+ self.check_tokenize('0xFF & 0x15 | 1234', """\
+ NUMBER '0xFF' (1, 0) (1, 4)
+ AMPER '&' (1, 5) (1, 6)
+ NUMBER '0x15' (1, 7) (1, 11)
+ VBAR '|' (1, 12) (1, 13)
+ NUMBER '1234' (1, 14) (1, 18)
+ """)
+
+ def test_float(self):
+
+ self.check_tokenize('x = 3.14159', """\
+ NAME 'x' (1, 0) (1, 1)
+ EQUAL '=' (1, 2) (1, 3)
+ NUMBER '3.14159' (1, 4) (1, 11)
+ """)
+
+ self.check_tokenize('x = 314159.', """\
+ NAME 'x' (1, 0) (1, 1)
+ EQUAL '=' (1, 2) (1, 3)
+ NUMBER '314159.' (1, 4) (1, 11)
+ """)
+
+ self.check_tokenize('x = .314159', """\
+ NAME 'x' (1, 0) (1, 1)
+ EQUAL '=' (1, 2) (1, 3)
+ NUMBER '.314159' (1, 4) (1, 11)
+ """)
+
+ self.check_tokenize('x = 3e14159', """\
+ NAME 'x' (1, 0) (1, 1)
+ EQUAL '=' (1, 2) (1, 3)
+ NUMBER '3e14159' (1, 4) (1, 11)
+ """)
+
+ self.check_tokenize('x = 3E123', """\
+ NAME 'x' (1, 0) (1, 1)
+ EQUAL '=' (1, 2) (1, 3)
+ NUMBER '3E123' (1, 4) (1, 9)
+ """)
+
+ self.check_tokenize('x+y = 3e-1230', """\
+ NAME 'x' (1, 0) (1, 1)
+ PLUS '+' (1, 1) (1, 2)
+ NAME 'y' (1, 2) (1, 3)
+ EQUAL '=' (1, 4) (1, 5)
+ NUMBER '3e-1230' (1, 6) (1, 13)
+ """)
+
+ self.check_tokenize('x = 3.14e159', """\
+ NAME 'x' (1, 0) (1, 1)
+ EQUAL '=' (1, 2) (1, 3)
+ NUMBER '3.14e159' (1, 4) (1, 12)
+ """)
+
+ def test_string(self):
+
+ self.check_tokenize('x = \'\'; y = ""', """\
+ NAME 'x' (1, 0) (1, 1)
+ EQUAL '=' (1, 2) (1, 3)
+ STRING "''" (1, 4) (1, 6)
+ SEMI ';' (1, 6) (1, 7)
+ NAME 'y' (1, 8) (1, 9)
+ EQUAL '=' (1, 10) (1, 11)
+ STRING '""' (1, 12) (1, 14)
+ """)
+
+ self.check_tokenize('x = \'"\'; y = "\'"', """\
+ NAME 'x' (1, 0) (1, 1)
+ EQUAL '=' (1, 2) (1, 3)
+ STRING '\\'"\\'' (1, 4) (1, 7)
+ SEMI ';' (1, 7) (1, 8)
+ NAME 'y' (1, 9) (1, 10)
+ EQUAL '=' (1, 11) (1, 12)
+ STRING '"\\'"' (1, 13) (1, 16)
+ """)
+
+ self.check_tokenize('x = "doesn\'t "shrink", does it"', """\
+ NAME 'x' (1, 0) (1, 1)
+ EQUAL '=' (1, 2) (1, 3)
+ STRING '"doesn\\'t "' (1, 4) (1, 14)
+ NAME 'shrink' (1, 14) (1, 20)
+ STRING '", does it"' (1, 20) (1, 31)
+ """)
+
+ self.check_tokenize("x = 'abc' + 'ABC'", """\
+ NAME 'x' (1, 0) (1, 1)
+ EQUAL '=' (1, 2) (1, 3)
+ STRING "'abc'" (1, 4) (1, 9)
+ PLUS '+' (1, 10) (1, 11)
+ STRING "'ABC'" (1, 12) (1, 17)
+ """)
+
+ self.check_tokenize('y = "ABC" + "ABC"', """\
+ NAME 'y' (1, 0) (1, 1)
+ EQUAL '=' (1, 2) (1, 3)
+ STRING '"ABC"' (1, 4) (1, 9)
+ PLUS '+' (1, 10) (1, 11)
+ STRING '"ABC"' (1, 12) (1, 17)
+ """)
+
+ self.check_tokenize("x = r'abc' + r'ABC' + R'ABC' + R'ABC'", """\
+ NAME 'x' (1, 0) (1, 1)
+ EQUAL '=' (1, 2) (1, 3)
+ STRING "r'abc'" (1, 4) (1, 10)
+ PLUS '+' (1, 11) (1, 12)
+ STRING "r'ABC'" (1, 13) (1, 19)
+ PLUS '+' (1, 20) (1, 21)
+ STRING "R'ABC'" (1, 22) (1, 28)
+ PLUS '+' (1, 29) (1, 30)
+ STRING "R'ABC'" (1, 31) (1, 37)
+ """)
+
+ self.check_tokenize('y = r"abc" + r"ABC" + R"ABC" + R"ABC"', """\
+ NAME 'y' (1, 0) (1, 1)
+ EQUAL '=' (1, 2) (1, 3)
+ STRING 'r"abc"' (1, 4) (1, 10)
+ PLUS '+' (1, 11) (1, 12)
+ STRING 'r"ABC"' (1, 13) (1, 19)
+ PLUS '+' (1, 20) (1, 21)
+ STRING 'R"ABC"' (1, 22) (1, 28)
+ PLUS '+' (1, 29) (1, 30)
+ STRING 'R"ABC"' (1, 31) (1, 37)
+ """)
+
+ self.check_tokenize("u'abc' + U'abc'", """\
+ STRING "u'abc'" (1, 0) (1, 6)
+ PLUS '+' (1, 7) (1, 8)
+ STRING "U'abc'" (1, 9) (1, 15)
+ """)
+
+ self.check_tokenize('u"abc" + U"abc"', """\
+ STRING 'u"abc"' (1, 0) (1, 6)
+ PLUS '+' (1, 7) (1, 8)
+ STRING 'U"abc"' (1, 9) (1, 15)
+ """)
+
+ self.check_tokenize("b'abc' + B'abc'", """\
+ STRING "b'abc'" (1, 0) (1, 6)
+ PLUS '+' (1, 7) (1, 8)
+ STRING "B'abc'" (1, 9) (1, 15)
+ """)
+
+ self.check_tokenize('b"abc" + B"abc"', """\
+ STRING 'b"abc"' (1, 0) (1, 6)
+ PLUS '+' (1, 7) (1, 8)
+ STRING 'B"abc"' (1, 9) (1, 15)
+ """)
+
+ self.check_tokenize("br'abc' + bR'abc' + Br'abc' + BR'abc'", """\
+ STRING "br'abc'" (1, 0) (1, 7)
+ PLUS '+' (1, 8) (1, 9)
+ STRING "bR'abc'" (1, 10) (1, 17)
+ PLUS '+' (1, 18) (1, 19)
+ STRING "Br'abc'" (1, 20) (1, 27)
+ PLUS '+' (1, 28) (1, 29)
+ STRING "BR'abc'" (1, 30) (1, 37)
+ """)
+
+ self.check_tokenize('br"abc" + bR"abc" + Br"abc" + BR"abc"', """\
+ STRING 'br"abc"' (1, 0) (1, 7)
+ PLUS '+' (1, 8) (1, 9)
+ STRING 'bR"abc"' (1, 10) (1, 17)
+ PLUS '+' (1, 18) (1, 19)
+ STRING 'Br"abc"' (1, 20) (1, 27)
+ PLUS '+' (1, 28) (1, 29)
+ STRING 'BR"abc"' (1, 30) (1, 37)
+ """)
+
+ self.check_tokenize("rb'abc' + rB'abc' + Rb'abc' + RB'abc'", """\
+ STRING "rb'abc'" (1, 0) (1, 7)
+ PLUS '+' (1, 8) (1, 9)
+ STRING "rB'abc'" (1, 10) (1, 17)
+ PLUS '+' (1, 18) (1, 19)
+ STRING "Rb'abc'" (1, 20) (1, 27)
+ PLUS '+' (1, 28) (1, 29)
+ STRING "RB'abc'" (1, 30) (1, 37)
+ """)
+
+ self.check_tokenize('rb"abc" + rB"abc" + Rb"abc" + RB"abc"', """\
+ STRING 'rb"abc"' (1, 0) (1, 7)
+ PLUS '+' (1, 8) (1, 9)
+ STRING 'rB"abc"' (1, 10) (1, 17)
+ PLUS '+' (1, 18) (1, 19)
+ STRING 'Rb"abc"' (1, 20) (1, 27)
+ PLUS '+' (1, 28) (1, 29)
+ STRING 'RB"abc"' (1, 30) (1, 37)
+ """)
+
+ self.check_tokenize('"a\\\nde\\\nfg"', """\
+ STRING '"a\\\\\\nde\\\\\\nfg"\' (1, 0) (3, 3)
+ """)
+
+ self.check_tokenize('u"a\\\nde"', """\
+ STRING 'u"a\\\\\\nde"\' (1, 0) (2, 3)
+ """)
+
+ self.check_tokenize('rb"a\\\nd"', """\
+ STRING 'rb"a\\\\\\nd"\' (1, 0) (2, 2)
+ """)
+
+ self.check_tokenize(r'"""a\
+b"""', """\
+ STRING '\"\""a\\\\\\nb\"\""' (1, 0) (2, 4)
+ """)
+ self.check_tokenize(r'u"""a\
+b"""', """\
+ STRING 'u\"\""a\\\\\\nb\"\""' (1, 0) (2, 4)
+ """)
+ self.check_tokenize(r'rb"""a\
+b\
+c"""', """\
+ STRING 'rb"\""a\\\\\\nb\\\\\\nc"\""' (1, 0) (3, 4)
+ """)
+
+ self.check_tokenize('f"abc"', """\
+ STRING 'f"abc"' (1, 0) (1, 6)
+ """)
+
+ self.check_tokenize('fR"a{b}c"', """\
+ STRING 'fR"a{b}c"' (1, 0) (1, 9)
+ """)
+
+ self.check_tokenize('f"""abc"""', """\
+ STRING 'f\"\"\"abc\"\"\"' (1, 0) (1, 10)
+ """)
+
+ self.check_tokenize(r'f"abc\
+def"', """\
+ STRING 'f"abc\\\\\\ndef"' (1, 0) (2, 4)
+ """)
+
+ self.check_tokenize(r'Rf"abc\
+def"', """\
+ STRING 'Rf"abc\\\\\\ndef"' (1, 0) (2, 4)
+ """)
+
+ def test_function(self):
+
+ self.check_tokenize('def d22(a, b, c=2, d=2, *k): pass', """\
+ NAME 'def' (1, 0) (1, 3)
+ NAME 'd22' (1, 4) (1, 7)
+ LPAR '(' (1, 7) (1, 8)
+ NAME 'a' (1, 8) (1, 9)
+ COMMA ',' (1, 9) (1, 10)
+ NAME 'b' (1, 11) (1, 12)
+ COMMA ',' (1, 12) (1, 13)
+ NAME 'c' (1, 14) (1, 15)
+ EQUAL '=' (1, 15) (1, 16)
+ NUMBER '2' (1, 16) (1, 17)
+ COMMA ',' (1, 17) (1, 18)
+ NAME 'd' (1, 19) (1, 20)
+ EQUAL '=' (1, 20) (1, 21)
+ NUMBER '2' (1, 21) (1, 22)
+ COMMA ',' (1, 22) (1, 23)
+ STAR '*' (1, 24) (1, 25)
+ NAME 'k' (1, 25) (1, 26)
+ RPAR ')' (1, 26) (1, 27)
+ COLON ':' (1, 27) (1, 28)
+ NAME 'pass' (1, 29) (1, 33)
+ """)
+
+ self.check_tokenize('def d01v_(a=1, *k, **w): pass', """\
+ NAME 'def' (1, 0) (1, 3)
+ NAME 'd01v_' (1, 4) (1, 9)
+ LPAR '(' (1, 9) (1, 10)
+ NAME 'a' (1, 10) (1, 11)
+ EQUAL '=' (1, 11) (1, 12)
+ NUMBER '1' (1, 12) (1, 13)
+ COMMA ',' (1, 13) (1, 14)
+ STAR '*' (1, 15) (1, 16)
+ NAME 'k' (1, 16) (1, 17)
+ COMMA ',' (1, 17) (1, 18)
+ DOUBLESTAR '**' (1, 19) (1, 21)
+ NAME 'w' (1, 21) (1, 22)
+ RPAR ')' (1, 22) (1, 23)
+ COLON ':' (1, 23) (1, 24)
+ NAME 'pass' (1, 25) (1, 29)
+ """)
+
+ self.check_tokenize('def d23(a: str, b: int=3) -> int: pass', """\
+ NAME 'def' (1, 0) (1, 3)
+ NAME 'd23' (1, 4) (1, 7)
+ LPAR '(' (1, 7) (1, 8)
+ NAME 'a' (1, 8) (1, 9)
+ COLON ':' (1, 9) (1, 10)
+ NAME 'str' (1, 11) (1, 14)
+ COMMA ',' (1, 14) (1, 15)
+ NAME 'b' (1, 16) (1, 17)
+ COLON ':' (1, 17) (1, 18)
+ NAME 'int' (1, 19) (1, 22)
+ EQUAL '=' (1, 22) (1, 23)
+ NUMBER '3' (1, 23) (1, 24)
+ RPAR ')' (1, 24) (1, 25)
+ RARROW '->' (1, 26) (1, 28)
+ NAME 'int' (1, 29) (1, 32)
+ COLON ':' (1, 32) (1, 33)
+ NAME 'pass' (1, 34) (1, 38)
+ """)
+
+ def test_comparison(self):
+
+ self.check_tokenize("if 1 < 1 > 1 == 1 >= 5 <= 0x15 <= 0x12 != "
+ "1 and 5 in 1 not in 1 is 1 or 5 is not 1: pass", """\
+ NAME 'if' (1, 0) (1, 2)
+ NUMBER '1' (1, 3) (1, 4)
+ LESS '<' (1, 5) (1, 6)
+ NUMBER '1' (1, 7) (1, 8)
+ GREATER '>' (1, 9) (1, 10)
+ NUMBER '1' (1, 11) (1, 12)
+ EQEQUAL '==' (1, 13) (1, 15)
+ NUMBER '1' (1, 16) (1, 17)
+ GREATEREQUAL '>=' (1, 18) (1, 20)
+ NUMBER '5' (1, 21) (1, 22)
+ LESSEQUAL '<=' (1, 23) (1, 25)
+ NUMBER '0x15' (1, 26) (1, 30)
+ LESSEQUAL '<=' (1, 31) (1, 33)
+ NUMBER '0x12' (1, 34) (1, 38)
+ NOTEQUAL '!=' (1, 39) (1, 41)
+ NUMBER '1' (1, 42) (1, 43)
+ NAME 'and' (1, 44) (1, 47)
+ NUMBER '5' (1, 48) (1, 49)
+ NAME 'in' (1, 50) (1, 52)
+ NUMBER '1' (1, 53) (1, 54)
+ NAME 'not' (1, 55) (1, 58)
+ NAME 'in' (1, 59) (1, 61)
+ NUMBER '1' (1, 62) (1, 63)
+ NAME 'is' (1, 64) (1, 66)
+ NUMBER '1' (1, 67) (1, 68)
+ NAME 'or' (1, 69) (1, 71)
+ NUMBER '5' (1, 72) (1, 73)
+ NAME 'is' (1, 74) (1, 76)
+ NAME 'not' (1, 77) (1, 80)
+ NUMBER '1' (1, 81) (1, 82)
+ COLON ':' (1, 82) (1, 83)
+ NAME 'pass' (1, 84) (1, 88)
+ """)
+
+ def test_additive(self):
+
+ self.check_tokenize('x = 1 - y + 15 - 1 + 0x124 + z + a[5]', """\
+ NAME 'x' (1, 0) (1, 1)
+ EQUAL '=' (1, 2) (1, 3)
+ NUMBER '1' (1, 4) (1, 5)
+ MINUS '-' (1, 6) (1, 7)
+ NAME 'y' (1, 8) (1, 9)
+ PLUS '+' (1, 10) (1, 11)
+ NUMBER '15' (1, 12) (1, 14)
+ MINUS '-' (1, 15) (1, 16)
+ NUMBER '1' (1, 17) (1, 18)
+ PLUS '+' (1, 19) (1, 20)
+ NUMBER '0x124' (1, 21) (1, 26)
+ PLUS '+' (1, 27) (1, 28)
+ NAME 'z' (1, 29) (1, 30)
+ PLUS '+' (1, 31) (1, 32)
+ NAME 'a' (1, 33) (1, 34)
+ LSQB '[' (1, 34) (1, 35)
+ NUMBER '5' (1, 35) (1, 36)
+ RSQB ']' (1, 36) (1, 37)
+ """)
+
+ def test_multiplicative(self):
+
+ self.check_tokenize('x = 1//1*1/5*12%0x12@42', """\
+ NAME 'x' (1, 0) (1, 1)
+ EQUAL '=' (1, 2) (1, 3)
+ NUMBER '1' (1, 4) (1, 5)
+ DOUBLESLASH '//' (1, 5) (1, 7)
+ NUMBER '1' (1, 7) (1, 8)
+ STAR '*' (1, 8) (1, 9)
+ NUMBER '1' (1, 9) (1, 10)
+ SLASH '/' (1, 10) (1, 11)
+ NUMBER '5' (1, 11) (1, 12)
+ STAR '*' (1, 12) (1, 13)
+ NUMBER '12' (1, 13) (1, 15)
+ PERCENT '%' (1, 15) (1, 16)
+ NUMBER '0x12' (1, 16) (1, 20)
+ AT '@' (1, 20) (1, 21)
+ NUMBER '42' (1, 21) (1, 23)
+ """)
+
+ def test_unary(self):
+
+ self.check_tokenize('~1 ^ 1 & 1 |1 ^ -1', """\
+ TILDE '~' (1, 0) (1, 1)
+ NUMBER '1' (1, 1) (1, 2)
+ CIRCUMFLEX '^' (1, 3) (1, 4)
+ NUMBER '1' (1, 5) (1, 6)
+ AMPER '&' (1, 7) (1, 8)
+ NUMBER '1' (1, 9) (1, 10)
+ VBAR '|' (1, 11) (1, 12)
+ NUMBER '1' (1, 12) (1, 13)
+ CIRCUMFLEX '^' (1, 14) (1, 15)
+ MINUS '-' (1, 16) (1, 17)
+ NUMBER '1' (1, 17) (1, 18)
+ """)
+
+ self.check_tokenize('-1*1/1+1*1//1 - ---1**1', """\
+ MINUS '-' (1, 0) (1, 1)
+ NUMBER '1' (1, 1) (1, 2)
+ STAR '*' (1, 2) (1, 3)
+ NUMBER '1' (1, 3) (1, 4)
+ SLASH '/' (1, 4) (1, 5)
+ NUMBER '1' (1, 5) (1, 6)
+ PLUS '+' (1, 6) (1, 7)
+ NUMBER '1' (1, 7) (1, 8)
+ STAR '*' (1, 8) (1, 9)
+ NUMBER '1' (1, 9) (1, 10)
+ DOUBLESLASH '//' (1, 10) (1, 12)
+ NUMBER '1' (1, 12) (1, 13)
+ MINUS '-' (1, 14) (1, 15)
+ MINUS '-' (1, 16) (1, 17)
+ MINUS '-' (1, 17) (1, 18)
+ MINUS '-' (1, 18) (1, 19)
+ NUMBER '1' (1, 19) (1, 20)
+ DOUBLESTAR '**' (1, 20) (1, 22)
+ NUMBER '1' (1, 22) (1, 23)
+ """)
+
+ def test_selector(self):
+
+ self.check_tokenize("import sys, time\nx = sys.modules['time'].time()", """\
+ NAME 'import' (1, 0) (1, 6)
+ NAME 'sys' (1, 7) (1, 10)
+ COMMA ',' (1, 10) (1, 11)
+ NAME 'time' (1, 12) (1, 16)
+ NEWLINE '' (1, 16) (1, 16)
+ NAME 'x' (2, 0) (2, 1)
+ EQUAL '=' (2, 2) (2, 3)
+ NAME 'sys' (2, 4) (2, 7)
+ DOT '.' (2, 7) (2, 8)
+ NAME 'modules' (2, 8) (2, 15)
+ LSQB '[' (2, 15) (2, 16)
+ STRING "'time'" (2, 16) (2, 22)
+ RSQB ']' (2, 22) (2, 23)
+ DOT '.' (2, 23) (2, 24)
+ NAME 'time' (2, 24) (2, 28)
+ LPAR '(' (2, 28) (2, 29)
+ RPAR ')' (2, 29) (2, 30)
+ """)
+
+ def test_method(self):
+
+ self.check_tokenize('@staticmethod\ndef foo(x,y): pass', """\
+ AT '@' (1, 0) (1, 1)
+ NAME 'staticmethod' (1, 1) (1, 13)
+ NEWLINE '' (1, 13) (1, 13)
+ NAME 'def' (2, 0) (2, 3)
+ NAME 'foo' (2, 4) (2, 7)
+ LPAR '(' (2, 7) (2, 8)
+ NAME 'x' (2, 8) (2, 9)
+ COMMA ',' (2, 9) (2, 10)
+ NAME 'y' (2, 10) (2, 11)
+ RPAR ')' (2, 11) (2, 12)
+ COLON ':' (2, 12) (2, 13)
+ NAME 'pass' (2, 14) (2, 18)
+ """)
+
+ def test_tabs(self):
+
+ self.check_tokenize('@staticmethod\ndef foo(x,y): pass', """\
+ AT '@' (1, 0) (1, 1)
+ NAME 'staticmethod' (1, 1) (1, 13)
+ NEWLINE '' (1, 13) (1, 13)
+ NAME 'def' (2, 0) (2, 3)
+ NAME 'foo' (2, 4) (2, 7)
+ LPAR '(' (2, 7) (2, 8)
+ NAME 'x' (2, 8) (2, 9)
+ COMMA ',' (2, 9) (2, 10)
+ NAME 'y' (2, 10) (2, 11)
+ RPAR ')' (2, 11) (2, 12)
+ COLON ':' (2, 12) (2, 13)
+ NAME 'pass' (2, 14) (2, 18)
+ """)
+
+ def test_async(self):
+
+ self.check_tokenize('async = 1', """\
+ ASYNC 'async' (1, 0) (1, 5)
+ EQUAL '=' (1, 6) (1, 7)
+ NUMBER '1' (1, 8) (1, 9)
+ """)
+
+ self.check_tokenize('a = (async = 1)', """\
+ NAME 'a' (1, 0) (1, 1)
+ EQUAL '=' (1, 2) (1, 3)
+ LPAR '(' (1, 4) (1, 5)
+ ASYNC 'async' (1, 5) (1, 10)
+ EQUAL '=' (1, 11) (1, 12)
+ NUMBER '1' (1, 13) (1, 14)
+ RPAR ')' (1, 14) (1, 15)
+ """)
+
+ self.check_tokenize('async()', """\
+ ASYNC 'async' (1, 0) (1, 5)
+ LPAR '(' (1, 5) (1, 6)
+ RPAR ')' (1, 6) (1, 7)
+ """)
+
+ self.check_tokenize('class async(Bar):pass', """\
+ NAME 'class' (1, 0) (1, 5)
+ ASYNC 'async' (1, 6) (1, 11)
+ LPAR '(' (1, 11) (1, 12)
+ NAME 'Bar' (1, 12) (1, 15)
+ RPAR ')' (1, 15) (1, 16)
+ COLON ':' (1, 16) (1, 17)
+ NAME 'pass' (1, 17) (1, 21)
+ """)
+
+ self.check_tokenize('class async:pass', """\
+ NAME 'class' (1, 0) (1, 5)
+ ASYNC 'async' (1, 6) (1, 11)
+ COLON ':' (1, 11) (1, 12)
+ NAME 'pass' (1, 12) (1, 16)
+ """)
+
+ self.check_tokenize('await = 1', """\
+ AWAIT 'await' (1, 0) (1, 5)
+ EQUAL '=' (1, 6) (1, 7)
+ NUMBER '1' (1, 8) (1, 9)
+ """)
+
+ self.check_tokenize('foo.async', """\
+ NAME 'foo' (1, 0) (1, 3)
+ DOT '.' (1, 3) (1, 4)
+ ASYNC 'async' (1, 4) (1, 9)
+ """)
+
+ self.check_tokenize('async for a in b: pass', """\
+ ASYNC 'async' (1, 0) (1, 5)
+ NAME 'for' (1, 6) (1, 9)
+ NAME 'a' (1, 10) (1, 11)
+ NAME 'in' (1, 12) (1, 14)
+ NAME 'b' (1, 15) (1, 16)
+ COLON ':' (1, 16) (1, 17)
+ NAME 'pass' (1, 18) (1, 22)
+ """)
+
+ self.check_tokenize('async with a as b: pass', """\
+ ASYNC 'async' (1, 0) (1, 5)
+ NAME 'with' (1, 6) (1, 10)
+ NAME 'a' (1, 11) (1, 12)
+ NAME 'as' (1, 13) (1, 15)
+ NAME 'b' (1, 16) (1, 17)
+ COLON ':' (1, 17) (1, 18)
+ NAME 'pass' (1, 19) (1, 23)
+ """)
+
+ self.check_tokenize('async.foo', """\
+ ASYNC 'async' (1, 0) (1, 5)
+ DOT '.' (1, 5) (1, 6)
+ NAME 'foo' (1, 6) (1, 9)
+ """)
+
+ self.check_tokenize('async', """\
+ ASYNC 'async' (1, 0) (1, 5)
+ """)
+
+ self.check_tokenize('async\n#comment\nawait', """\
+ ASYNC 'async' (1, 0) (1, 5)
+ NEWLINE '' (1, 5) (1, 5)
+ AWAIT 'await' (3, 0) (3, 5)
+ """)
+
+ self.check_tokenize('async\n...\nawait', """\
+ ASYNC 'async' (1, 0) (1, 5)
+ NEWLINE '' (1, 5) (1, 5)
+ ELLIPSIS '...' (2, 0) (2, 3)
+ NEWLINE '' (2, 3) (2, 3)
+ AWAIT 'await' (3, 0) (3, 5)
+ """)
+
+ self.check_tokenize('async\nawait', """\
+ ASYNC 'async' (1, 0) (1, 5)
+ NEWLINE '' (1, 5) (1, 5)
+ AWAIT 'await' (2, 0) (2, 5)
+ """)
+
+ self.check_tokenize('foo.async + 1', """\
+ NAME 'foo' (1, 0) (1, 3)
+ DOT '.' (1, 3) (1, 4)
+ ASYNC 'async' (1, 4) (1, 9)
+ PLUS '+' (1, 10) (1, 11)
+ NUMBER '1' (1, 12) (1, 13)
+ """)
+
+ self.check_tokenize('async def foo(): pass', """\
+ ASYNC 'async' (1, 0) (1, 5)
+ NAME 'def' (1, 6) (1, 9)
+ NAME 'foo' (1, 10) (1, 13)
+ LPAR '(' (1, 13) (1, 14)
+ RPAR ')' (1, 14) (1, 15)
+ COLON ':' (1, 15) (1, 16)
+ NAME 'pass' (1, 17) (1, 21)
+ """)
+
+ self.check_tokenize('''\
+async def foo():
+ def foo(await):
+ await = 1
+ if 1:
+ await
+async += 1
+''', """\
+ ASYNC 'async' (1, 0) (1, 5)
+ NAME 'def' (1, 6) (1, 9)
+ NAME 'foo' (1, 10) (1, 13)
+ LPAR '(' (1, 13) (1, 14)
+ RPAR ')' (1, 14) (1, 15)
+ COLON ':' (1, 15) (1, 16)
+ NEWLINE '' (1, 16) (1, 16)
+ INDENT '' (2, -1) (2, -1)
+ NAME 'def' (2, 2) (2, 5)
+ NAME 'foo' (2, 6) (2, 9)
+ LPAR '(' (2, 9) (2, 10)
+ AWAIT 'await' (2, 10) (2, 15)
+ RPAR ')' (2, 15) (2, 16)
+ COLON ':' (2, 16) (2, 17)
+ NEWLINE '' (2, 17) (2, 17)
+ INDENT '' (3, -1) (3, -1)
+ AWAIT 'await' (3, 4) (3, 9)
+ EQUAL '=' (3, 10) (3, 11)
+ NUMBER '1' (3, 12) (3, 13)
+ NEWLINE '' (3, 13) (3, 13)
+ DEDENT '' (4, -1) (4, -1)
+ NAME 'if' (4, 2) (4, 4)
+ NUMBER '1' (4, 5) (4, 6)
+ COLON ':' (4, 6) (4, 7)
+ NEWLINE '' (4, 7) (4, 7)
+ INDENT '' (5, -1) (5, -1)
+ AWAIT 'await' (5, 4) (5, 9)
+ NEWLINE '' (5, 9) (5, 9)
+ DEDENT '' (6, -1) (6, -1)
+ DEDENT '' (6, -1) (6, -1)
+ ASYNC 'async' (6, 0) (6, 5)
+ PLUSEQUAL '+=' (6, 6) (6, 8)
+ NUMBER '1' (6, 9) (6, 10)
+ NEWLINE '' (6, 10) (6, 10)
+ """)
+
+ self.check_tokenize('async def foo():\n async for i in 1: pass', """\
+ ASYNC 'async' (1, 0) (1, 5)
+ NAME 'def' (1, 6) (1, 9)
+ NAME 'foo' (1, 10) (1, 13)
+ LPAR '(' (1, 13) (1, 14)
+ RPAR ')' (1, 14) (1, 15)
+ COLON ':' (1, 15) (1, 16)
+ NEWLINE '' (1, 16) (1, 16)
+ INDENT '' (2, -1) (2, -1)
+ ASYNC 'async' (2, 2) (2, 7)
+ NAME 'for' (2, 8) (2, 11)
+ NAME 'i' (2, 12) (2, 13)
+ NAME 'in' (2, 14) (2, 16)
+ NUMBER '1' (2, 17) (2, 18)
+ COLON ':' (2, 18) (2, 19)
+ NAME 'pass' (2, 20) (2, 24)
+ DEDENT '' (2, -1) (2, -1)
+ """)
+
+ self.check_tokenize('async def foo(async): await', """\
+ ASYNC 'async' (1, 0) (1, 5)
+ NAME 'def' (1, 6) (1, 9)
+ NAME 'foo' (1, 10) (1, 13)
+ LPAR '(' (1, 13) (1, 14)
+ ASYNC 'async' (1, 14) (1, 19)
+ RPAR ')' (1, 19) (1, 20)
+ COLON ':' (1, 20) (1, 21)
+ AWAIT 'await' (1, 22) (1, 27)
+ """)
+
+ self.check_tokenize('''\
+def f():
+
+ def baz(): pass
+ async def bar(): pass
+
+ await = 2''', """\
+ NAME 'def' (1, 0) (1, 3)
+ NAME 'f' (1, 4) (1, 5)
+ LPAR '(' (1, 5) (1, 6)
+ RPAR ')' (1, 6) (1, 7)
+ COLON ':' (1, 7) (1, 8)
+ NEWLINE '' (1, 8) (1, 8)
+ INDENT '' (3, -1) (3, -1)
+ NAME 'def' (3, 2) (3, 5)
+ NAME 'baz' (3, 6) (3, 9)
+ LPAR '(' (3, 9) (3, 10)
+ RPAR ')' (3, 10) (3, 11)
+ COLON ':' (3, 11) (3, 12)
+ NAME 'pass' (3, 13) (3, 17)
+ NEWLINE '' (3, 17) (3, 17)
+ ASYNC 'async' (4, 2) (4, 7)
+ NAME 'def' (4, 8) (4, 11)
+ NAME 'bar' (4, 12) (4, 15)
+ LPAR '(' (4, 15) (4, 16)
+ RPAR ')' (4, 16) (4, 17)
+ COLON ':' (4, 17) (4, 18)
+ NAME 'pass' (4, 19) (4, 23)
+ NEWLINE '' (4, 23) (4, 23)
+ AWAIT 'await' (6, 2) (6, 7)
+ EQUAL '=' (6, 8) (6, 9)
+ NUMBER '2' (6, 10) (6, 11)
+ DEDENT '' (6, -1) (6, -1)
+ """)
+
+ self.check_tokenize('''\
+async def f():
+
+ def baz(): pass
+ async def bar(): pass
+
+ await = 2''', """\
+ ASYNC 'async' (1, 0) (1, 5)
+ NAME 'def' (1, 6) (1, 9)
+ NAME 'f' (1, 10) (1, 11)
+ LPAR '(' (1, 11) (1, 12)
+ RPAR ')' (1, 12) (1, 13)
+ COLON ':' (1, 13) (1, 14)
+ NEWLINE '' (1, 14) (1, 14)
+ INDENT '' (3, -1) (3, -1)
+ NAME 'def' (3, 2) (3, 5)
+ NAME 'baz' (3, 6) (3, 9)
+ LPAR '(' (3, 9) (3, 10)
+ RPAR ')' (3, 10) (3, 11)
+ COLON ':' (3, 11) (3, 12)
+ NAME 'pass' (3, 13) (3, 17)
+ NEWLINE '' (3, 17) (3, 17)
+ ASYNC 'async' (4, 2) (4, 7)
+ NAME 'def' (4, 8) (4, 11)
+ NAME 'bar' (4, 12) (4, 15)
+ LPAR '(' (4, 15) (4, 16)
+ RPAR ')' (4, 16) (4, 17)
+ COLON ':' (4, 17) (4, 18)
+ NAME 'pass' (4, 19) (4, 23)
+ NEWLINE '' (4, 23) (4, 23)
+ AWAIT 'await' (6, 2) (6, 7)
+ EQUAL '=' (6, 8) (6, 9)
+ NUMBER '2' (6, 10) (6, 11)
+ DEDENT '' (6, -1) (6, -1)
+ """)
+
+ def test_unicode(self):
+
+ self.check_tokenize("Örter = u'places'\ngrün = U'green'", """\
+ NAME 'Örter' (1, 0) (1, 6)
+ EQUAL '=' (1, 7) (1, 8)
+ STRING "u'places'" (1, 9) (1, 18)
+ NEWLINE '' (1, 18) (1, 18)
+ NAME 'grün' (2, 0) (2, 5)
+ EQUAL '=' (2, 6) (2, 7)
+ STRING "U'green'" (2, 8) (2, 16)
+ """)
+
+ def test_invalid_syntax(self):
+ def get_tokens(string):
+ return list(_generate_tokens_from_c_tokenizer(string))
+
+ self.assertRaises(SyntaxError, get_tokens, "(1+2]")
+ self.assertRaises(SyntaxError, get_tokens, "(1+2}")
+ self.assertRaises(SyntaxError, get_tokens, "{1+2]")
+
+ self.assertRaises(SyntaxError, get_tokens, "1_")
+ self.assertRaises(SyntaxError, get_tokens, "1.2_")
+ self.assertRaises(SyntaxError, get_tokens, "1e2_")
+ self.assertRaises(SyntaxError, get_tokens, "1e+")
+
+ self.assertRaises(SyntaxError, get_tokens, "\xa0")
+ self.assertRaises(SyntaxError, get_tokens, "€")
+
+ self.assertRaises(SyntaxError, get_tokens, "0b12")
+ self.assertRaises(SyntaxError, get_tokens, "0b1_2")
+ self.assertRaises(SyntaxError, get_tokens, "0b2")
+ self.assertRaises(SyntaxError, get_tokens, "0b1_")
+ self.assertRaises(SyntaxError, get_tokens, "0b")
+ self.assertRaises(SyntaxError, get_tokens, "0o18")
+ self.assertRaises(SyntaxError, get_tokens, "0o1_8")
+ self.assertRaises(SyntaxError, get_tokens, "0o8")
+ self.assertRaises(SyntaxError, get_tokens, "0o1_")
+ self.assertRaises(SyntaxError, get_tokens, "0o")
+ self.assertRaises(SyntaxError, get_tokens, "0x1_")
+ self.assertRaises(SyntaxError, get_tokens, "0x")
+ self.assertRaises(SyntaxError, get_tokens, "1_")
+ self.assertRaises(SyntaxError, get_tokens, "012")
+ self.assertRaises(SyntaxError, get_tokens, "1.2_")
+ self.assertRaises(SyntaxError, get_tokens, "1e2_")
+ self.assertRaises(SyntaxError, get_tokens, "1e+")
+
+ self.assertRaises(SyntaxError, get_tokens, "'sdfsdf")
+ self.assertRaises(SyntaxError, get_tokens, "'''sdfsdf''")
+
+ self.assertRaises(SyntaxError, get_tokens, "("*1000+"a"+")"*1000)
+ self.assertRaises(SyntaxError, get_tokens, "]")
+
+
if __name__ == "__main__":
unittest.main()
diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py
index 3bd5894f425741..de4db51bdc7075 100644
--- a/Lib/test/test_typing.py
+++ b/Lib/test/test_typing.py
@@ -1533,21 +1533,21 @@ def test_supports_float(self):
def test_supports_complex(self):
- # Note: complex itself doesn't have __complex__.
class C:
def __complex__(self):
return 0j
+ self.assertIsSubclass(complex, typing.SupportsComplex)
self.assertIsSubclass(C, typing.SupportsComplex)
self.assertNotIsSubclass(str, typing.SupportsComplex)
def test_supports_bytes(self):
- # Note: bytes itself doesn't have __bytes__.
class B:
def __bytes__(self):
return b''
+ self.assertIsSubclass(bytes, typing.SupportsBytes)
self.assertIsSubclass(B, typing.SupportsBytes)
self.assertNotIsSubclass(str, typing.SupportsBytes)
diff --git a/Lib/test/test_zipimport.py b/Lib/test/test_zipimport.py
index 4e88902e3e31d8..c57f8615eb4c93 100644
--- a/Lib/test/test_zipimport.py
+++ b/Lib/test/test_zipimport.py
@@ -37,14 +37,9 @@ def get_file():
def make_pyc(co, mtime, size):
data = marshal.dumps(co)
- if type(mtime) is type(0.0):
- # Mac mtimes need a bit of special casing
- if mtime < 0x7fffffff:
- mtime = int(mtime)
- else:
- mtime = int(-0x100000000 + int(mtime))
pyc = (importlib.util.MAGIC_NUMBER +
- struct.pack("enable_callback_tracebacks) {
+ callback_context *ctx = (callback_context *)sqlite3_user_data(context);
+ assert(ctx != NULL);
+ assert(ctx->state != NULL);
+ if (ctx->state->enable_callback_tracebacks) {
PyErr_Print();
}
else {
@@ -625,7 +627,6 @@ static void
_pysqlite_func_callback(sqlite3_context *context, int argc, sqlite3_value **argv)
{
PyObject* args;
- PyObject* py_func;
PyObject* py_retval = NULL;
int ok;
@@ -633,11 +634,11 @@ _pysqlite_func_callback(sqlite3_context *context, int argc, sqlite3_value **argv
threadstate = PyGILState_Ensure();
- py_func = (PyObject*)sqlite3_user_data(context);
-
args = _pysqlite_build_py_params(context, argc, argv);
if (args) {
- py_retval = PyObject_CallObject(py_func, args);
+ callback_context *ctx = (callback_context *)sqlite3_user_data(context);
+ assert(ctx != NULL);
+ py_retval = PyObject_CallObject(ctx->callable, args);
Py_DECREF(args);
}
@@ -657,7 +658,6 @@ static void _pysqlite_step_callback(sqlite3_context *context, int argc, sqlite3_
{
PyObject* args;
PyObject* function_result = NULL;
- PyObject* aggregate_class;
PyObject** aggregate_instance;
PyObject* stepmethod = NULL;
@@ -665,12 +665,12 @@ static void _pysqlite_step_callback(sqlite3_context *context, int argc, sqlite3_
threadstate = PyGILState_Ensure();
- aggregate_class = (PyObject*)sqlite3_user_data(context);
-
aggregate_instance = (PyObject**)sqlite3_aggregate_context(context, sizeof(PyObject*));
if (*aggregate_instance == NULL) {
- *aggregate_instance = _PyObject_CallNoArg(aggregate_class);
+ callback_context *ctx = (callback_context *)sqlite3_user_data(context);
+ assert(ctx != NULL);
+ *aggregate_instance = _PyObject_CallNoArg(ctx->callable);
if (!*aggregate_instance) {
set_sqlite_error(context,
"user-defined aggregate's '__init__' method raised error");
@@ -784,14 +784,35 @@ static void _pysqlite_drop_unused_cursor_references(pysqlite_Connection* self)
Py_SETREF(self->cursors, new_list);
}
-static void _destructor(void* args)
+static callback_context *
+create_callback_context(pysqlite_state *state, PyObject *callable)
{
- // This function may be called without the GIL held, so we need to ensure
- // that we destroy 'args' with the GIL
- PyGILState_STATE gstate;
- gstate = PyGILState_Ensure();
- Py_DECREF((PyObject*)args);
+ PyGILState_STATE gstate = PyGILState_Ensure();
+ callback_context *ctx = PyMem_Malloc(sizeof(callback_context));
+ if (ctx != NULL) {
+ ctx->callable = Py_NewRef(callable);
+ ctx->state = state;
+ }
PyGILState_Release(gstate);
+ return ctx;
+}
+
+static void
+free_callback_context(callback_context *ctx)
+{
+ if (ctx != NULL) {
+ // This function may be called without the GIL held, so we need to
+ // ensure that we destroy 'ctx' with the GIL held.
+ PyGILState_STATE gstate = PyGILState_Ensure();
+ Py_DECREF(ctx->callable);
+ PyMem_Free(ctx);
+ PyGILState_Release(gstate);
+ }
+}
+
+static void _destructor(void* args)
+{
+ free_callback_context((callback_context *)args);
}
/*[clinic input]
@@ -833,11 +854,11 @@ pysqlite_connection_create_function_impl(pysqlite_Connection *self,
flags |= SQLITE_DETERMINISTIC;
#endif
}
- rc = sqlite3_create_function_v2(self->db,
- name,
- narg,
- flags,
- (void*)Py_NewRef(func),
+ callback_context *ctx = create_callback_context(self->state, func);
+ if (ctx == NULL) {
+ return NULL;
+ }
+ rc = sqlite3_create_function_v2(self->db, name, narg, flags, ctx,
_pysqlite_func_callback,
NULL,
NULL,
@@ -873,11 +894,12 @@ pysqlite_connection_create_aggregate_impl(pysqlite_Connection *self,
return NULL;
}
- rc = sqlite3_create_function_v2(self->db,
- name,
- n_arg,
- SQLITE_UTF8,
- (void*)Py_NewRef(aggregate_class),
+ callback_context *ctx = create_callback_context(self->state,
+ aggregate_class);
+ if (ctx == NULL) {
+ return NULL;
+ }
+ rc = sqlite3_create_function_v2(self->db, name, n_arg, SQLITE_UTF8, ctx,
0,
&_pysqlite_step_callback,
&_pysqlite_final_callback,
@@ -1439,7 +1461,6 @@ pysqlite_collation_callback(
int text1_length, const void* text1_data,
int text2_length, const void* text2_data)
{
- PyObject* callback = (PyObject*)context;
PyObject* string1 = 0;
PyObject* string2 = 0;
PyGILState_STATE gilstate;
@@ -1459,8 +1480,10 @@ pysqlite_collation_callback(
goto finally; /* failed to allocate strings */
}
+ callback_context *ctx = (callback_context *)context;
+ assert(ctx != NULL);
PyObject *args[] = { string1, string2 }; // Borrowed refs.
- retval = PyObject_Vectorcall(callback, args, 2, NULL);
+ retval = PyObject_Vectorcall(ctx->callable, args, 2, NULL);
if (retval == NULL) {
/* execution failed */
goto finally;
@@ -1690,6 +1713,7 @@ pysqlite_connection_create_collation_impl(pysqlite_Connection *self,
return NULL;
}
+ callback_context *ctx = NULL;
int rc;
int flags = SQLITE_UTF8;
if (callable == Py_None) {
@@ -1701,8 +1725,11 @@ pysqlite_connection_create_collation_impl(pysqlite_Connection *self,
PyErr_SetString(PyExc_TypeError, "parameter must be callable");
return NULL;
}
- rc = sqlite3_create_collation_v2(self->db, name, flags,
- Py_NewRef(callable),
+ ctx = create_callback_context(self->state, callable);
+ if (ctx == NULL) {
+ return NULL;
+ }
+ rc = sqlite3_create_collation_v2(self->db, name, flags, ctx,
&pysqlite_collation_callback,
&_destructor);
}
@@ -1713,7 +1740,7 @@ pysqlite_connection_create_collation_impl(pysqlite_Connection *self,
* the context before returning.
*/
if (callable != Py_None) {
- Py_DECREF(callable);
+ free_callback_context(ctx);
}
_pysqlite_seterror(self->state, self->db);
return NULL;
diff --git a/Modules/_sqlite/connection.h b/Modules/_sqlite/connection.h
index 4f08a6d5f7b21d..11b3a8005e1f95 100644
--- a/Modules/_sqlite/connection.h
+++ b/Modules/_sqlite/connection.h
@@ -32,6 +32,12 @@
#include "sqlite3.h"
+typedef struct _callback_context
+{
+ PyObject *callable;
+ pysqlite_state *state;
+} callback_context;
+
typedef struct
{
PyObject_HEAD
diff --git a/Modules/_sqlite/cursor.c b/Modules/_sqlite/cursor.c
index b747a945817b44..44d5921afd6d08 100644
--- a/Modules/_sqlite/cursor.c
+++ b/Modules/_sqlite/cursor.c
@@ -53,7 +53,6 @@ pysqlite_cursor_init_impl(pysqlite_Cursor *self,
Py_INCREF(connection);
Py_XSETREF(self->connection, connection);
Py_CLEAR(self->statement);
- Py_CLEAR(self->next_row);
Py_CLEAR(self->row_cast_map);
Py_INCREF(Py_None);
@@ -94,7 +93,6 @@ cursor_traverse(pysqlite_Cursor *self, visitproc visit, void *arg)
Py_VISIT(self->lastrowid);
Py_VISIT(self->row_factory);
Py_VISIT(self->statement);
- Py_VISIT(self->next_row);
return 0;
}
@@ -107,8 +105,6 @@ cursor_clear(pysqlite_Cursor *self)
Py_CLEAR(self->lastrowid);
Py_CLEAR(self->row_factory);
Py_CLEAR(self->statement);
- Py_CLEAR(self->next_row);
-
return 0;
}
@@ -488,8 +484,6 @@ _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* operation
self->locked = 1;
self->reset = 0;
- Py_CLEAR(self->next_row);
-
if (multiple) {
if (PyIter_Check(second_argument)) {
/* iterator */
@@ -646,11 +640,7 @@ _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* operation
}
}
- if (rc == SQLITE_ROW) {
- self->next_row = _pysqlite_fetch_one_row(self);
- if (self->next_row == NULL)
- goto error;
- } else if (rc == SQLITE_DONE && !multiple) {
+ if (rc == SQLITE_DONE && !multiple) {
Py_CLEAR(self->statement);
}
@@ -805,10 +795,6 @@ pysqlite_cursor_executescript_impl(pysqlite_Cursor *self,
static PyObject *
pysqlite_cursor_iternext(pysqlite_Cursor *self)
{
- PyObject* next_row_tuple;
- PyObject* next_row;
- int rc;
-
if (!check_cursor(self)) {
return NULL;
}
@@ -819,47 +805,39 @@ pysqlite_cursor_iternext(pysqlite_Cursor *self)
return NULL;
}
- if (!self->next_row) {
- Py_CLEAR(self->statement);
+ if (self->statement == NULL) {
return NULL;
}
- next_row_tuple = self->next_row;
- assert(next_row_tuple != NULL);
- self->next_row = NULL;
-
- if (self->row_factory != Py_None) {
- next_row = PyObject_CallFunction(self->row_factory, "OO", self, next_row_tuple);
- if (next_row == NULL) {
- self->next_row = next_row_tuple;
- return NULL;
- }
- Py_DECREF(next_row_tuple);
- } else {
- next_row = next_row_tuple;
+ sqlite3_stmt *stmt = self->statement->st;
+ assert(stmt != NULL);
+ if (sqlite3_data_count(stmt) == 0) {
+ Py_CLEAR(self->statement);
+ return NULL;
}
- if (self->statement) {
- rc = pysqlite_step(self->statement->st);
- if (PyErr_Occurred()) {
- Py_DECREF(next_row);
- return NULL;
- }
- if (rc != SQLITE_DONE && rc != SQLITE_ROW) {
- Py_DECREF(next_row);
- _pysqlite_seterror(self->connection->state, self->connection->db);
- return NULL;
- }
-
- if (rc == SQLITE_ROW) {
- self->next_row = _pysqlite_fetch_one_row(self);
- if (self->next_row == NULL) {
- return NULL;
- }
- }
+ PyObject *row = _pysqlite_fetch_one_row(self);
+ if (row == NULL) {
+ return NULL;
}
-
- return next_row;
+ int rc = pysqlite_step(stmt);
+ if (rc == SQLITE_DONE) {
+ Py_CLEAR(self->statement);
+ }
+ else if (rc != SQLITE_ROW) {
+ (void)_pysqlite_seterror(self->connection->state,
+ self->connection->db);
+ Py_DECREF(row);
+ return NULL;
+ }
+ if (!Py_IsNone(self->row_factory)) {
+ PyObject *factory = self->row_factory;
+ PyObject *args[] = { (PyObject *)self, row, };
+ PyObject *new_row = PyObject_Vectorcall(factory, args, 2, NULL);
+ Py_DECREF(row);
+ row = new_row;
+ }
+ return row;
}
/*[clinic input]
diff --git a/Modules/_sqlite/cursor.h b/Modules/_sqlite/cursor.h
index 9a5e9eede23025..d26d20a9fc5eaa 100644
--- a/Modules/_sqlite/cursor.h
+++ b/Modules/_sqlite/cursor.h
@@ -46,13 +46,9 @@ typedef struct
int locked;
int initialized;
- /* the next row to be returned, NULL if no next row available */
- PyObject* next_row;
-
PyObject* in_weakreflist; /* List of weak references */
} pysqlite_Cursor;
int pysqlite_cursor_setup_types(PyObject *module);
-#define UNKNOWN (-1)
#endif
diff --git a/Modules/_sqlite/prepare_protocol.h b/Modules/_sqlite/prepare_protocol.h
index f24cef5497139f..afc55a8c1c4eb1 100644
--- a/Modules/_sqlite/prepare_protocol.h
+++ b/Modules/_sqlite/prepare_protocol.h
@@ -32,5 +32,4 @@ typedef struct
int pysqlite_prepare_protocol_setup_types(PyObject *module);
-#define UNKNOWN (-1)
#endif
diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c
index e5f1ad312b41b6..68dd285c380e0f 100644
--- a/Modules/_testcapimodule.c
+++ b/Modules/_testcapimodule.c
@@ -2376,16 +2376,22 @@ test_long_numbits(PyObject *self, PyObject *Py_UNUSED(ignored))
Py_RETURN_NONE;
}
-/* Example passing NULLs to PyObject_Str(NULL). */
+static PyObject *
+pyobject_repr_from_null(PyObject *self, PyObject *Py_UNUSED(ignored))
+{
+ return PyObject_Repr(NULL);
+}
static PyObject *
-test_null_strings(PyObject *self, PyObject *Py_UNUSED(ignored))
+pyobject_str_from_null(PyObject *self, PyObject *Py_UNUSED(ignored))
{
- PyObject *o1 = PyObject_Str(NULL), *o2 = PyObject_Str(NULL);
- PyObject *tuple = PyTuple_Pack(2, o1, o2);
- Py_XDECREF(o1);
- Py_XDECREF(o2);
- return tuple;
+ return PyObject_Str(NULL);
+}
+
+static PyObject *
+pyobject_bytes_from_null(PyObject *self, PyObject *Py_UNUSED(ignored))
+{
+ return PyObject_Bytes(NULL);
}
static PyObject *
@@ -5702,7 +5708,9 @@ static PyMethodDef TestMethods[] = {
{"test_k_code", test_k_code, METH_NOARGS},
{"test_empty_argparse", test_empty_argparse, METH_NOARGS},
{"parse_tuple_and_keywords", parse_tuple_and_keywords, METH_VARARGS},
- {"test_null_strings", test_null_strings, METH_NOARGS},
+ {"pyobject_repr_from_null", pyobject_repr_from_null, METH_NOARGS},
+ {"pyobject_str_from_null", pyobject_str_from_null, METH_NOARGS},
+ {"pyobject_bytes_from_null", pyobject_bytes_from_null, METH_NOARGS},
{"test_string_from_format", (PyCFunction)test_string_from_format, METH_NOARGS},
{"test_with_docstring", test_with_docstring, METH_NOARGS,
PyDoc_STR("This is a pretty normal docstring.")},
diff --git a/Modules/config.c.in b/Modules/config.c.in
index d69e8e88b0ca45..6081f95759538f 100644
--- a/Modules/config.c.in
+++ b/Modules/config.c.in
@@ -28,6 +28,7 @@ extern PyObject* PyMarshal_Init(void);
extern PyObject* PyInit__imp(void);
extern PyObject* PyInit_gc(void);
extern PyObject* PyInit__ast(void);
+extern PyObject* PyInit__tokenize(void);
extern PyObject* _PyWarnings_Init(void);
extern PyObject* PyInit__string(void);
@@ -44,6 +45,9 @@ struct _inittab _PyImport_Inittab[] = {
/* This lives in Python/Python-ast.c */
{"_ast", PyInit__ast},
+ /* This lives in Python/Python-tokenizer.c */
+ {"_tokenize", PyInit__tokenize},
+
/* These entries are here for sys.builtin_module_names */
{"builtins", NULL},
{"sys", NULL},
diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c
index eaedb0b5689b2a..27f766bef6ed84 100644
--- a/Objects/bytesobject.c
+++ b/Objects/bytesobject.c
@@ -1687,6 +1687,25 @@ static PyBufferProcs bytes_as_buffer = {
};
+/*[clinic input]
+bytes.__bytes__
+Convert this value to exact type bytes.
+[clinic start generated code]*/
+
+static PyObject *
+bytes___bytes___impl(PyBytesObject *self)
+/*[clinic end generated code: output=63a306a9bc0caac5 input=34ec5ddba98bd6bb]*/
+{
+ if (PyBytes_CheckExact(self)) {
+ Py_INCREF(self);
+ return (PyObject *)self;
+ }
+ else {
+ return PyBytes_FromStringAndSize(self->ob_sval, Py_SIZE(self));
+ }
+}
+
+
#define LEFTSTRIP 0
#define RIGHTSTRIP 1
#define BOTHSTRIP 2
@@ -2474,6 +2493,7 @@ bytes_getnewargs(PyBytesObject *v, PyObject *Py_UNUSED(ignored))
static PyMethodDef
bytes_methods[] = {
{"__getnewargs__", (PyCFunction)bytes_getnewargs, METH_NOARGS},
+ BYTES___BYTES___METHODDEF
{"capitalize", stringlib_capitalize, METH_NOARGS,
_Py_capitalize__doc__},
STRINGLIB_CENTER_METHODDEF
diff --git a/Objects/clinic/bytesobject.c.h b/Objects/clinic/bytesobject.c.h
index 9e365ce1a088ba..103a3642813218 100644
--- a/Objects/clinic/bytesobject.c.h
+++ b/Objects/clinic/bytesobject.c.h
@@ -2,6 +2,24 @@
preserve
[clinic start generated code]*/
+PyDoc_STRVAR(bytes___bytes____doc__,
+"__bytes__($self, /)\n"
+"--\n"
+"\n"
+"Convert this value to exact type bytes.");
+
+#define BYTES___BYTES___METHODDEF \
+ {"__bytes__", (PyCFunction)bytes___bytes__, METH_NOARGS, bytes___bytes____doc__},
+
+static PyObject *
+bytes___bytes___impl(PyBytesObject *self);
+
+static PyObject *
+bytes___bytes__(PyBytesObject *self, PyObject *Py_UNUSED(ignored))
+{
+ return bytes___bytes___impl(self);
+}
+
PyDoc_STRVAR(bytes_split__doc__,
"split($self, /, sep=None, maxsplit=-1)\n"
"--\n"
@@ -878,4 +896,4 @@ bytes_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
exit:
return return_value;
}
-/*[clinic end generated code: output=b3f0ec2753246b9c input=a9049054013a1b77]*/
+/*[clinic end generated code: output=d706344859f40122 input=a9049054013a1b77]*/
diff --git a/Objects/clinic/complexobject.c.h b/Objects/clinic/complexobject.c.h
index 557fbf9752faf7..e7d8065e874ef3 100644
--- a/Objects/clinic/complexobject.c.h
+++ b/Objects/clinic/complexobject.c.h
@@ -69,6 +69,24 @@ complex___format__(PyComplexObject *self, PyObject *arg)
return return_value;
}
+PyDoc_STRVAR(complex___complex____doc__,
+"__complex__($self, /)\n"
+"--\n"
+"\n"
+"Convert this value to exact type complex.");
+
+#define COMPLEX___COMPLEX___METHODDEF \
+ {"__complex__", (PyCFunction)complex___complex__, METH_NOARGS, complex___complex____doc__},
+
+static PyObject *
+complex___complex___impl(PyComplexObject *self);
+
+static PyObject *
+complex___complex__(PyComplexObject *self, PyObject *Py_UNUSED(ignored))
+{
+ return complex___complex___impl(self);
+}
+
PyDoc_STRVAR(complex_new__doc__,
"complex(real=0, imag=0)\n"
"--\n"
@@ -113,4 +131,4 @@ complex_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
exit:
return return_value;
}
-/*[clinic end generated code: output=056cac3226d94967 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=6d85094ace15677e input=a9049054013a1b77]*/
diff --git a/Objects/complexobject.c b/Objects/complexobject.c
index 3e479497cfcc31..cfe6c737578a03 100644
--- a/Objects/complexobject.c
+++ b/Objects/complexobject.c
@@ -693,8 +693,29 @@ complex___format___impl(PyComplexObject *self, PyObject *format_spec)
return _PyUnicodeWriter_Finish(&writer);
}
+/*[clinic input]
+complex.__complex__
+
+Convert this value to exact type complex.
+[clinic start generated code]*/
+
+static PyObject *
+complex___complex___impl(PyComplexObject *self)
+/*[clinic end generated code: output=e6b35ba3d275dc9c input=3589ada9d27db854]*/
+{
+ if (PyComplex_CheckExact(self)) {
+ Py_INCREF(self);
+ return (PyObject *)self;
+ }
+ else {
+ return PyComplex_FromCComplex(self->cval);
+ }
+}
+
+
static PyMethodDef complex_methods[] = {
COMPLEX_CONJUGATE_METHODDEF
+ COMPLEX___COMPLEX___METHODDEF
COMPLEX___GETNEWARGS___METHODDEF
COMPLEX___FORMAT___METHODDEF
{NULL, NULL} /* sentinel */
diff --git a/PC/config.c b/PC/config.c
index 11743ea45a969d..9d900c78e40d00 100644
--- a/PC/config.c
+++ b/PC/config.c
@@ -72,9 +72,8 @@ extern PyObject* _PyWarnings_Init(void);
extern PyObject* PyInit__string(void);
extern PyObject* PyInit__stat(void);
extern PyObject* PyInit__opcode(void);
-
extern PyObject* PyInit__contextvars(void);
-
+extern PyObject* PyInit__tokenize(void);
/* tools/freeze/makeconfig.py marker for additional "extern" */
/* -- ADDMODULE MARKER 1 -- */
@@ -83,7 +82,6 @@ extern PyObject* PyMarshal_Init(void);
extern PyObject* PyInit__imp(void);
struct _inittab _PyImport_Inittab[] = {
-
{"_abc", PyInit__abc},
{"array", PyInit_array},
{"_ast", PyInit__ast},
@@ -105,6 +103,7 @@ struct _inittab _PyImport_Inittab[] = {
{"_blake2", PyInit__blake2},
{"time", PyInit_time},
{"_thread", PyInit__thread},
+ {"_tokenize", PyInit__tokenize},
{"_typing", PyInit__typing},
{"_statistics", PyInit__statistics},
#ifdef WIN32
diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj
index ebc0f2879f9595..b8cadf469355f4 100644
--- a/PCbuild/pythoncore.vcxproj
+++ b/PCbuild/pythoncore.vcxproj
@@ -488,6 +488,7 @@
+
diff --git a/Python/Python-tokenize.c b/Python/Python-tokenize.c
new file mode 100644
index 00000000000000..b9fb1693ce117e
--- /dev/null
+++ b/Python/Python-tokenize.c
@@ -0,0 +1,195 @@
+#include "Python.h"
+#include "../Parser/tokenizer.h"
+
+static struct PyModuleDef _tokenizemodule;
+
+typedef struct {
+ PyTypeObject* TokenizerIter;
+} tokenize_state;
+
+static tokenize_state*
+get_tokenize_state(PyObject* module)
+{
+ return (tokenize_state*)PyModule_GetState(module);
+}
+
+#define _tokenize_get_state_by_type(type) \
+ get_tokenize_state(_PyType_GetModuleByDef(type, &_tokenizemodule))
+
+#include "clinic/Python-tokenize.c.h"
+
+/*[clinic input]
+module _tokenizer
+class _tokenizer.tokenizeriter "tokenizeriterobject *" "_tokenize_get_state_by_type(type)->TokenizerIter"
+[clinic start generated code]*/
+/*[clinic end generated code: output=da39a3ee5e6b4b0d input=96d98ee2fef7a8bc]*/
+
+typedef struct {
+ PyObject_HEAD
+ struct tok_state* tok;
+} tokenizeriterobject;
+
+/*[clinic input]
+@classmethod
+_tokenizer.tokenizeriter.__new__ as tokenizeriter_new
+
+ source: str
+[clinic start generated code]*/
+
+static PyObject *
+tokenizeriter_new_impl(PyTypeObject *type, const char *source)
+/*[clinic end generated code: output=7fd9f46cf9263cbb input=4384b368407375c6]*/
+{
+ tokenizeriterobject* self = (tokenizeriterobject*)type->tp_alloc(type, 0);
+ if (self == NULL) {
+ return NULL;
+ }
+ PyObject* filename = PyUnicode_FromString("");
+ if (filename == NULL) {
+ return NULL;
+ }
+ self->tok = PyTokenizer_FromUTF8(source, 1);
+ if (self->tok == NULL) {
+ return NULL;
+ }
+ self->tok->filename = filename;
+ return (PyObject*)self;
+}
+
+static PyObject*
+tokenizeriter_next(tokenizeriterobject* it)
+{
+ const char* start;
+ const char* end;
+ int type = PyTokenizer_Get(it->tok, &start, &end);
+ if (type == ERRORTOKEN && PyErr_Occurred()) {
+ return NULL;
+ }
+ if (type == ERRORTOKEN || type == ENDMARKER) {
+ PyErr_SetString(PyExc_StopIteration, "EOF");
+ return NULL;
+ }
+ PyObject* str = NULL;
+ if (start == NULL || end == NULL) {
+ str = PyUnicode_FromString("");
+ } else {
+ str = PyUnicode_FromStringAndSize(start, end - start);
+ }
+ if (str == NULL) {
+ return NULL;
+ }
+
+ Py_ssize_t size = it->tok->inp - it->tok->buf;
+ PyObject* line = PyUnicode_DecodeUTF8(it->tok->buf, size, "replace");
+ if (line == NULL) {
+ Py_DECREF(str);
+ return NULL;
+ }
+ const char* line_start = type == STRING ? it->tok->multi_line_start : it->tok->line_start;
+ int lineno = type == STRING ? it->tok->first_lineno : it->tok->lineno;
+ int end_lineno = it->tok->lineno;
+ int col_offset = -1;
+ int end_col_offset = -1;
+ if (start != NULL && start >= line_start) {
+ col_offset = (int)(start - line_start);
+ }
+ if (end != NULL && end >= it->tok->line_start) {
+ end_col_offset = (int)(end - it->tok->line_start);
+ }
+
+ return Py_BuildValue("(NiiiiiN)", str, type, lineno, end_lineno, col_offset, end_col_offset, line);
+}
+
+static void
+tokenizeriter_dealloc(tokenizeriterobject* it)
+{
+ PyTypeObject* tp = Py_TYPE(it);
+ PyTokenizer_Free(it->tok);
+ tp->tp_free(it);
+ Py_DECREF(tp);
+}
+
+static PyType_Slot tokenizeriter_slots[] = {
+ {Py_tp_new, tokenizeriter_new},
+ {Py_tp_dealloc, tokenizeriter_dealloc},
+ {Py_tp_getattro, PyObject_GenericGetAttr},
+ {Py_tp_iter, PyObject_SelfIter},
+ {Py_tp_iternext, tokenizeriter_next},
+ {0, NULL},
+};
+
+static PyType_Spec tokenizeriter_spec = {
+ .name = "_tokenize.TokenizerIter",
+ .basicsize = sizeof(tokenizeriterobject),
+ .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE),
+ .slots = tokenizeriter_slots,
+};
+
+
+static int
+tokenizemodule_exec(PyObject* m)
+{
+ tokenize_state* state = get_tokenize_state(m);
+ if (state == NULL) {
+ return -1;
+ }
+
+ state->TokenizerIter = (PyTypeObject *)PyType_FromModuleAndSpec(
+ m, &tokenizeriter_spec, NULL);
+ if (state->TokenizerIter == NULL) {
+ return -1;
+ }
+ if (PyModule_AddType(m, state->TokenizerIter) < 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static PyMethodDef tokenize_methods[] = {
+ {NULL, NULL, 0, NULL} /* Sentinel */
+};
+
+static PyModuleDef_Slot tokenizemodule_slots[] = {
+ {Py_mod_exec, tokenizemodule_exec},
+ {0, NULL}
+};
+
+static int
+tokenizemodule_traverse(PyObject *m, visitproc visit, void *arg)
+{
+ tokenize_state *state = get_tokenize_state(m);
+ Py_VISIT(state->TokenizerIter);
+ return 0;
+}
+
+static int
+tokenizemodule_clear(PyObject *m)
+{
+ tokenize_state *state = get_tokenize_state(m);
+ Py_CLEAR(state->TokenizerIter);
+ return 0;
+}
+
+static void
+tokenizemodule_free(void *m)
+{
+ tokenizemodule_clear((PyObject *)m);
+}
+
+static struct PyModuleDef _tokenizemodule = {
+ PyModuleDef_HEAD_INIT,
+ .m_name = "_tokenize",
+ .m_size = sizeof(tokenize_state),
+ .m_slots = tokenizemodule_slots,
+ .m_methods = tokenize_methods,
+ .m_traverse = tokenizemodule_traverse,
+ .m_clear = tokenizemodule_clear,
+ .m_free = tokenizemodule_free,
+};
+
+PyMODINIT_FUNC
+PyInit__tokenize(void)
+{
+ return PyModuleDef_Init(&_tokenizemodule);
+}
diff --git a/Python/clinic/Python-tokenize.c.h b/Python/clinic/Python-tokenize.c.h
new file mode 100644
index 00000000000000..050b4d49448c36
--- /dev/null
+++ b/Python/clinic/Python-tokenize.c.h
@@ -0,0 +1,41 @@
+/*[clinic input]
+preserve
+[clinic start generated code]*/
+
+static PyObject *
+tokenizeriter_new_impl(PyTypeObject *type, const char *source);
+
+static PyObject *
+tokenizeriter_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
+{
+ PyObject *return_value = NULL;
+ static const char * const _keywords[] = {"source", NULL};
+ static _PyArg_Parser _parser = {NULL, _keywords, "tokenizeriter", 0};
+ PyObject *argsbuf[1];
+ PyObject * const *fastargs;
+ Py_ssize_t nargs = PyTuple_GET_SIZE(args);
+ const char *source;
+
+ fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 1, 0, argsbuf);
+ if (!fastargs) {
+ goto exit;
+ }
+ if (!PyUnicode_Check(fastargs[0])) {
+ _PyArg_BadArgument("tokenizeriter", "argument 'source'", "str", fastargs[0]);
+ goto exit;
+ }
+ Py_ssize_t source_length;
+ source = PyUnicode_AsUTF8AndSize(fastargs[0], &source_length);
+ if (source == NULL) {
+ goto exit;
+ }
+ if (strlen(source) != (size_t)source_length) {
+ PyErr_SetString(PyExc_ValueError, "embedded null character");
+ goto exit;
+ }
+ return_value = tokenizeriter_new_impl(type, source);
+
+exit:
+ return return_value;
+}
+/*[clinic end generated code: output=dfcd64774e01bfe6 input=a9049054013a1b77]*/
diff --git a/Python/stdlib_module_names.h b/Python/stdlib_module_names.h
index 3c5f1768305cb0..2f75c2e54cd5e9 100644
--- a/Python/stdlib_module_names.h
+++ b/Python/stdlib_module_names.h
@@ -80,6 +80,7 @@ static const char* _Py_stdlib_module_names[] = {
"_thread",
"_threading_local",
"_tkinter",
+"_tokenize",
"_tracemalloc",
"_typing",
"_uuid",
diff --git a/README.rst b/README.rst
index 898680af095dee..067d7d8ca2b818 100644
--- a/README.rst
+++ b/README.rst
@@ -229,12 +229,13 @@ Proposals for enhancement
-------------------------
If you have a proposal to change Python, you may want to send an email to the
-comp.lang.python or `python-ideas`_ mailing lists for initial feedback. A
+`comp.lang.python`_ or `python-ideas`_ mailing lists for initial feedback. A
Python Enhancement Proposal (PEP) may be submitted if your idea gains ground.
All current PEPs, as well as guidelines for submitting a new PEP, are listed at
`python.org/dev/peps/ `_.
.. _python-ideas: https://mail.python.org/mailman/listinfo/python-ideas/
+.. _comp.lang.python: https://mail.python.org/mailman/listinfo/python-list
Release Schedule
diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py
index b52ac3b00de9ee..b5a884536a9ebe 100755
--- a/Tools/clinic/clinic.py
+++ b/Tools/clinic/clinic.py
@@ -2179,9 +2179,7 @@ def __repr__(self):
__abs__
__add__
__and__
-__bytes__
__call__
-__complex__
__delitem__
__divmod__
__eq__