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__