diff -Nru python3.11-3.11.0~rc1/configure python3.11-3.11.0~rc2/configure --- python3.11-3.11.0~rc1/configure 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/configure 2022-09-11 19:23:30.000000000 +0000 @@ -2,7 +2,7 @@ # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.69 for python 3.11. # -# Report bugs to . +# Report bugs to . # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. @@ -267,10 +267,10 @@ $as_echo "$0: be upgraded to zsh 4.3.4 or later." else $as_echo "$0: Please tell bug-autoconf@gnu.org and -$0: https://bugs.python.org/ about your system, including -$0: any error possibly output before this message. Then -$0: install a modern shell, or manually run the script -$0: under such a shell if you do have one." +$0: https://github.com/python/cpython/issues/ about your +$0: system, including any error possibly output before this +$0: message. Then install a modern shell, or manually run +$0: the script under such a shell if you do have one." fi exit 1 fi @@ -582,7 +582,7 @@ PACKAGE_TARNAME='python' PACKAGE_VERSION='3.11' PACKAGE_STRING='python 3.11' -PACKAGE_BUGREPORT='https://bugs.python.org/' +PACKAGE_BUGREPORT='https://github.com/python/cpython/issues/' PACKAGE_URL='' ac_unique_file="Include/object.h" @@ -1918,7 +1918,7 @@ Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. -Report bugs to . +Report bugs to . _ACEOF ac_status=$? fi @@ -2140,9 +2140,9 @@ $as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} -( $as_echo "## --------------------------------------- ## -## Report this to https://bugs.python.org/ ## -## --------------------------------------- ##" +( $as_echo "## -------------------------------------------------------- ## +## Report this to https://github.com/python/cpython/issues/ ## +## -------------------------------------------------------- ##" ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac @@ -19835,7 +19835,7 @@ # rounding issues. The result of this test has little meaning on non # IEEE 754 platforms. On IEEE 754, test should return 1 if rounding # mode is round-to-nearest and double rounding issues are present, and -# 0 otherwise. See http://bugs.python.org/issue2937 for more info. +# 0 otherwise. See https://github.com/python/cpython/issues/47186 for more info. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for x87-style double rounding" >&5 $as_echo_n "checking for x87-style double rounding... " >&6; } if ${ac_cv_x87_double_rounding+:} false; then : @@ -26451,7 +26451,7 @@ Configuration headers: $config_headers -Report bugs to ." +Report bugs to ." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 diff -Nru python3.11-3.11.0~rc1/configure.ac python3.11-3.11.0~rc2/configure.ac --- python3.11-3.11.0~rc1/configure.ac 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/configure.ac 2022-09-11 19:23:30.000000000 +0000 @@ -2,7 +2,7 @@ dnl * Please run autoreconf -if to test your changes! * dnl *************************************************** dnl -dnl Python's configure script requires autoconf 2.69 and autoconf-archive. +dnl Python's configure.ac file requires autoconf 2.69 and autoconf-archive. dnl # Set VERSION so we only need to edit in one place (i.e., here) @@ -10,7 +10,7 @@ AC_PREREQ([2.69]) -AC_INIT([python],[PYTHON_VERSION],[https://bugs.python.org/]) +AC_INIT([python],[PYTHON_VERSION],[https://github.com/python/cpython/issues/]) m4_ifdef( [AX_C_FLOAT_WORDS_BIGENDIAN], @@ -2066,7 +2066,7 @@ dnl build with WASM debug info if either Py_DEBUG is set or the target is dnl node-debug or browser-debug. AS_VAR_IF([Py_DEBUG], [yes], [wasm_debug=yes], [wasm_debug=no]) - + dnl Start with 20 MB and allow to grow AS_VAR_APPEND([LDFLAGS_NODIST], [" -sALLOW_MEMORY_GROWTH -sTOTAL_MEMORY=20971520"]) @@ -5415,7 +5415,7 @@ # rounding issues. The result of this test has little meaning on non # IEEE 754 platforms. On IEEE 754, test should return 1 if rounding # mode is round-to-nearest and double rounding issues are present, and -# 0 otherwise. See http://bugs.python.org/issue2937 for more info. +# 0 otherwise. See https://github.com/python/cpython/issues/47186 for more info. AC_CACHE_CHECK([for x87-style double rounding], [ac_cv_x87_double_rounding], [ # $BASECFLAGS may affect the result ac_save_cc="$CC" diff -Nru python3.11-3.11.0~rc1/debian/changelog python3.11-3.11.0~rc2/debian/changelog --- python3.11-3.11.0~rc1/debian/changelog 2022-08-08 18:31:54.000000000 +0000 +++ python3.11-3.11.0~rc2/debian/changelog 2022-09-13 15:07:34.000000000 +0000 @@ -1,3 +1,9 @@ +python3.11 (3.11.0~rc2-1+focal1) focal; urgency=medium + + * Python 3.11.0 release candidate 2. + + -- Anthony Sottile (deadsnakes) Tue, 13 Sep 2022 15:07:34 +0000 + python3.11 (3.11.0~rc1-1+focal1) focal; urgency=medium * Python 3.11.0 release candidate 1. diff -Nru python3.11-3.11.0~rc1/debian/patches/0028-sysconfigdata-name.patch python3.11-3.11.0~rc2/debian/patches/0028-sysconfigdata-name.patch --- python3.11-3.11.0~rc1/debian/patches/0028-sysconfigdata-name.patch 2022-08-08 18:31:54.000000000 +0000 +++ python3.11-3.11.0~rc2/debian/patches/0028-sysconfigdata-name.patch 2022-09-13 15:07:34.000000000 +0000 @@ -53,7 +53,7 @@ -rm -r $(DESTDIR)$(DESTSHARED)/__pycache__ diff --git a/configure.ac b/configure.ac -index ef3da9e..6419ec9 100644 +index 2b0760b..6d51226 100644 --- a/configure.ac +++ b/configure.ac @@ -162,7 +162,7 @@ AC_ARG_WITH( diff -Nru python3.11-3.11.0~rc1/debian/patches/disable-sem-check.diff python3.11-3.11.0~rc2/debian/patches/disable-sem-check.diff --- python3.11-3.11.0~rc1/debian/patches/disable-sem-check.diff 2022-08-08 18:31:54.000000000 +0000 +++ python3.11-3.11.0~rc2/debian/patches/disable-sem-check.diff 2022-09-13 15:07:34.000000000 +0000 @@ -9,7 +9,7 @@ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/configure.ac b/configure.ac -index 5e274c2..ef3da9e 100644 +index 7b4dfd0..2b0760b 100644 --- a/configure.ac +++ b/configure.ac @@ -5495,12 +5495,17 @@ AC_CACHE_CHECK([whether POSIX semaphores are enabled], [ac_cv_posix_semaphores_e diff -Nru python3.11-3.11.0~rc1/debian/patches/link-opt.diff python3.11-3.11.0~rc2/debian/patches/link-opt.diff --- python3.11-3.11.0~rc1/debian/patches/link-opt.diff 2022-08-08 18:31:54.000000000 +0000 +++ python3.11-3.11.0~rc2/debian/patches/link-opt.diff 2022-09-13 15:07:34.000000000 +0000 @@ -9,7 +9,7 @@ 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac -index 77fb609..5e274c2 100644 +index ab5e1de..7b4dfd0 100644 --- a/configure.ac +++ b/configure.ac @@ -3133,8 +3133,8 @@ then diff -Nru python3.11-3.11.0~rc1/debian/rules python3.11-3.11.0~rc2/debian/rules --- python3.11-3.11.0~rc1/debian/rules 2022-08-08 18:31:54.000000000 +0000 +++ python3.11-3.11.0~rc2/debian/rules 2022-09-13 15:07:34.000000000 +0000 @@ -83,7 +83,7 @@ else echo Unknown; fi) VER=3.11 -SVER=3.11.0~rc1 +SVER=3.11.0~rc2 NVER=3.12 PVER=python$(VER) EXT_VER=$(subst .,,$(VER)) diff -Nru python3.11-3.11.0~rc1/Doc/c-api/object.rst python3.11-3.11.0~rc2/Doc/c-api/object.rst --- python3.11-3.11.0~rc1/Doc/c-api/object.rst 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/c-api/object.rst 2022-09-11 19:23:30.000000000 +0000 @@ -126,6 +126,14 @@ A generic implementation for the getter of a ``__dict__`` descriptor. It creates the dictionary if necessary. + This function may also be called to get the :py:attr:`~object.__dict__` + of the object *o*. Pass ``NULL`` for *context* when calling it. + Since this function may need to allocate memory for the + dictionary, it may be more efficient to call :c:func:`PyObject_GetAttr` + when accessing an attribute on the object. + + On failure, returns ``NULL`` with an exception set. + .. versionadded:: 3.3 @@ -137,6 +145,16 @@ .. versionadded:: 3.3 +.. c:function:: PyObject** _PyObject_GetDictPtr(PyObject *obj) + + Return a pointer to :py:attr:`~object.__dict__` of the object *obj*. + If there is no ``__dict__``, return ``NULL`` without setting an exception. + + This function may need to allocate memory for the + dictionary, so it may be more efficient to call :c:func:`PyObject_GetAttr` + when accessing an attribute on the object. + + .. c:function:: PyObject* PyObject_RichCompare(PyObject *o1, PyObject *o2, int opid) Compare the values of *o1* and *o2* using the operation specified by *opid*, diff -Nru python3.11-3.11.0~rc1/Doc/c-api/typeobj.rst python3.11-3.11.0~rc2/Doc/c-api/typeobj.rst --- python3.11-3.11.0~rc1/Doc/c-api/typeobj.rst 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/c-api/typeobj.rst 2022-09-11 19:23:30.000000000 +0000 @@ -1709,18 +1709,11 @@ :c:member:`~PyTypeObject.tp_dictoffset` should be set to ``-4`` to indicate that the dictionary is at the very end of the structure. - The real dictionary offset in an instance can be computed from a negative - :c:member:`~PyTypeObject.tp_dictoffset` as follows:: - - dictoffset = tp_basicsize + abs(ob_size)*tp_itemsize + tp_dictoffset - if dictoffset is not aligned on sizeof(void*): - round up to sizeof(void*) - - where :c:member:`~PyTypeObject.tp_basicsize`, :c:member:`~PyTypeObject.tp_itemsize` and :c:member:`~PyTypeObject.tp_dictoffset` are - taken from the type object, and :attr:`ob_size` is taken from the instance. The - absolute value is taken because ints use the sign of :attr:`ob_size` to - store the sign of the number. (There's never a need to do this calculation - yourself; it is done for you by :c:func:`_PyObject_GetDictPtr`.) + The :c:member:`~PyTypeObject.tp_dictoffset` should be regarded as write-only. + To get the pointer to the dictionary call :c:func:`PyObject_GenericGetDict`. + Calling :c:func:`PyObject_GenericGetDict` may need to allocate memory for the + dictionary, so it is may be more efficient to call :c:func:`PyObject_GetAttr` + when accessing an attribute on the object. **Inheritance:** @@ -1991,9 +1984,6 @@ PyErr_Restore(error_type, error_value, error_traceback); } - For this field to be taken into account (even through inheritance), - you must also set the :const:`Py_TPFLAGS_HAVE_FINALIZE` flags bit. - Also, note that, in a garbage collected Python, :c:member:`~PyTypeObject.tp_dealloc` may be called from any Python thread, not just the thread which created the object (if the object @@ -2011,6 +2001,12 @@ .. versionadded:: 3.4 + .. versionchanged:: 3.8 + + Before version 3.8 it was necessary to set the + :const:`Py_TPFLAGS_HAVE_FINALIZE` flags bit in order for this field to be + used. This is no longer required. + .. seealso:: "Safe object finalization" (:pep:`442`) @@ -2048,9 +2044,9 @@ :ref:`sub-interpreters `, so they should not include any subinterpreter-specific state. -Also, since :c:type:`PyTypeObject` is not part of the :ref:`stable ABI `, -any extension modules using static types must be compiled for a specific -Python minor version. +Also, since :c:type:`PyTypeObject` is only part of the :ref:`Limited API +` as an opaque struct, any extension modules using static types must be +compiled for a specific Python minor version. .. _heap-types: diff -Nru python3.11-3.11.0~rc1/Doc/data/python3.11.abi python3.11-3.11.0~rc2/Doc/data/python3.11.abi --- python3.11-3.11.0~rc1/Doc/data/python3.11.abi 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/data/python3.11.abi 2022-09-11 19:23:30.000000000 +0000 @@ -1639,7 +1639,7 @@ - + @@ -1825,7 +1825,7 @@ - + @@ -1971,6 +1971,9 @@ + + + @@ -1995,7 +1998,7 @@ - + diff -Nru python3.11-3.11.0~rc1/Doc/faq/general.rst python3.11-3.11.0~rc2/Doc/faq/general.rst --- python3.11-3.11.0~rc1/Doc/faq/general.rst 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/faq/general.rst 2022-09-11 19:23:30.000000000 +0000 @@ -113,8 +113,8 @@ The language comes with a large standard library that covers areas such as string processing (regular expressions, Unicode, calculating differences between -files), internet protocols (HTTP, FTP, SMTP, XML-RPC, POP, IMAP, CGI -programming), software engineering (unit testing, logging, profiling, parsing +files), internet protocols (HTTP, FTP, SMTP, XML-RPC, POP, IMAP), +software engineering (unit testing, logging, profiling, parsing Python code), and operating system interfaces (system calls, filesystems, TCP/IP sockets). Look at the table of contents for :ref:`library-index` to get an idea of what's available. A wide variety of third-party extensions are also diff -Nru python3.11-3.11.0~rc1/Doc/glossary.rst python3.11-3.11.0~rc2/Doc/glossary.rst --- python3.11-3.11.0~rc1/Doc/glossary.rst 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/glossary.rst 2022-09-11 19:23:30.000000000 +0000 @@ -566,9 +566,9 @@ from their :func:`id`. IDLE - An Integrated Development Environment for Python. IDLE is a basic editor - and interpreter environment which ships with the standard distribution of - Python. + An Integrated Development and Learning Environment for Python. + :ref:`idle` is a basic editor and interpreter environment + which ships with the standard distribution of Python. immutable An object with a fixed value. Immutable objects include numbers, strings and diff -Nru python3.11-3.11.0~rc1/Doc/howto/descriptor.rst python3.11-3.11.0~rc2/Doc/howto/descriptor.rst --- python3.11-3.11.0~rc1/Doc/howto/descriptor.rst 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/howto/descriptor.rst 2022-09-11 19:23:30.000000000 +0000 @@ -582,11 +582,18 @@ .. testcode:: + def find_name_in_mro(cls, name, default): + "Emulate _PyType_Lookup() in Objects/typeobject.c" + for base in cls.__mro__: + if name in vars(base): + return vars(base)[name] + return default + def object_getattribute(obj, name): "Emulate PyObject_GenericGetAttr() in Objects/object.c" null = object() objtype = type(obj) - cls_var = getattr(objtype, name, null) + cls_var = find_name_in_mro(objtype, name, null) descr_get = getattr(type(cls_var), '__get__', null) if descr_get is not null: if (hasattr(type(cls_var), '__set__') @@ -663,6 +670,15 @@ def __getattr__(self, name): return ('getattr_hook', self, name) + class D1: + def __get__(self, obj, objtype=None): + return type(self), obj, objtype + + class U1: + x = D1() + + class U2(U1): + pass .. doctest:: :hide: @@ -696,6 +712,10 @@ >>> b.g == b['g'] == ('getattr_hook', b, 'g') True + >>> u2 = U2() + >>> object_getattribute(u2, 'x') == u2.x == (D1, u2, U2) + True + Note, there is no :meth:`__getattr__` hook in the :meth:`__getattribute__` code. That is why calling :meth:`__getattribute__` directly or with ``super().__getattribute__`` will bypass :meth:`__getattr__` entirely. diff -Nru python3.11-3.11.0~rc1/Doc/howto/logging-cookbook.rst python3.11-3.11.0~rc2/Doc/howto/logging-cookbook.rst --- python3.11-3.11.0~rc1/Doc/howto/logging-cookbook.rst 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/howto/logging-cookbook.rst 2022-09-11 19:23:30.000000000 +0000 @@ -2678,6 +2678,88 @@ ... +.. _buffered-smtp: + +Sending logging messages to email, with buffering +------------------------------------------------- + +To illustrate how you can send log messages via email, so that a set number of +messages are sent per email, you can subclass +:class:`~logging.handlers.BufferingHandler`. In the following example, which you can +adapt to suit your specific needs, a simple test harness is provided which allows you +to run the script with command line arguments specifying what you typically need to +send things via SMTP. (Run the downloaded script with the ``-h`` argument to see the +required and optional arguments.) + +.. code-block:: python + + import logging + import logging.handlers + import smtplib + + class BufferingSMTPHandler(logging.handlers.BufferingHandler): + def __init__(self, mailhost, port, username, password, fromaddr, toaddrs, + subject, capacity): + logging.handlers.BufferingHandler.__init__(self, capacity) + self.mailhost = mailhost + self.mailport = port + self.username = username + self.password = password + self.fromaddr = fromaddr + if isinstance(toaddrs, str): + toaddrs = [toaddrs] + self.toaddrs = toaddrs + self.subject = subject + self.setFormatter(logging.Formatter("%(asctime)s %(levelname)-5s %(message)s")) + + def flush(self): + if len(self.buffer) > 0: + try: + smtp = smtplib.SMTP(self.mailhost, self.mailport) + smtp.starttls() + smtp.login(self.username, self.password) + msg = "From: %s\r\nTo: %s\r\nSubject: %s\r\n\r\n" % (self.fromaddr, ','.join(self.toaddrs), self.subject) + for record in self.buffer: + s = self.format(record) + msg = msg + s + "\r\n" + smtp.sendmail(self.fromaddr, self.toaddrs, msg) + smtp.quit() + except Exception: + if logging.raiseExceptions: + raise + self.buffer = [] + + if __name__ == '__main__': + import argparse + + ap = argparse.ArgumentParser() + aa = ap.add_argument + aa('host', metavar='HOST', help='SMTP server') + aa('--port', '-p', type=int, default=587, help='SMTP port') + aa('user', metavar='USER', help='SMTP username') + aa('password', metavar='PASSWORD', help='SMTP password') + aa('to', metavar='TO', help='Addressee for emails') + aa('sender', metavar='SENDER', help='Sender email address') + aa('--subject', '-s', + default='Test Logging email from Python logging module (buffering)', + help='Subject of email') + options = ap.parse_args() + logger = logging.getLogger() + logger.setLevel(logging.DEBUG) + h = BufferingSMTPHandler(options.host, options.port, options.user, + options.password, options.sender, + options.to, options.subject, 10) + logger.addHandler(h) + for i in range(102): + logger.info("Info index = %d", i) + h.flush() + h.close() + +If you run this script and your SMTP server is correctly set up, you should find that +it sends eleven emails to the addressee you specify. The first ten emails will each +have ten log messages, and the eleventh will have two messages. That makes up 102 +messages as specified in the script. + .. _utc-formatting: Formatting times using UTC (GMT) via configuration diff -Nru python3.11-3.11.0~rc1/Doc/includes/sqlite3/adapter_point_1.py python3.11-3.11.0~rc2/Doc/includes/sqlite3/adapter_point_1.py --- python3.11-3.11.0~rc1/Doc/includes/sqlite3/adapter_point_1.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/includes/sqlite3/adapter_point_1.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,18 +0,0 @@ -import sqlite3 - -class Point: - def __init__(self, x, y): - self.x, self.y = x, y - - def __conform__(self, protocol): - if protocol is sqlite3.PrepareProtocol: - return "%f;%f" % (self.x, self.y) - -con = sqlite3.connect(":memory:") -cur = con.cursor() - -p = Point(4.0, -3.2) -cur.execute("select ?", (p,)) -print(cur.fetchone()[0]) - -con.close() diff -Nru python3.11-3.11.0~rc1/Doc/includes/sqlite3/adapter_point_2.py python3.11-3.11.0~rc2/Doc/includes/sqlite3/adapter_point_2.py --- python3.11-3.11.0~rc1/Doc/includes/sqlite3/adapter_point_2.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/includes/sqlite3/adapter_point_2.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ -import sqlite3 - -class Point: - def __init__(self, x, y): - self.x, self.y = x, y - -def adapt_point(point): - return "%f;%f" % (point.x, point.y) - -sqlite3.register_adapter(Point, adapt_point) - -con = sqlite3.connect(":memory:") -cur = con.cursor() - -p = Point(4.0, -3.2) -cur.execute("select ?", (p,)) -print(cur.fetchone()[0]) - -con.close() diff -Nru python3.11-3.11.0~rc1/Doc/includes/sqlite3/blob.py python3.11-3.11.0~rc2/Doc/includes/sqlite3/blob.py --- python3.11-3.11.0~rc1/Doc/includes/sqlite3/blob.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/includes/sqlite3/blob.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ -import sqlite3 - -con = sqlite3.connect(":memory:") -con.execute("create table test(blob_col blob)") -con.execute("insert into test(blob_col) values (zeroblob(13))") - -# Write to our blob, using two write operations: -with con.blobopen("test", "blob_col", 1) as blob: - blob.write(b"hello, ") - blob.write(b"world.") - # Modify the first and last bytes of our blob - blob[0] = ord("H") - blob[-1] = ord("!") - -# Read the contents of our blob -with con.blobopen("test", "blob_col", 1) as blob: - greeting = blob.read() - -print(greeting) # outputs "b'Hello, world!'" diff -Nru python3.11-3.11.0~rc1/Doc/includes/sqlite3/collation_reverse.py python3.11-3.11.0~rc2/Doc/includes/sqlite3/collation_reverse.py --- python3.11-3.11.0~rc1/Doc/includes/sqlite3/collation_reverse.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/includes/sqlite3/collation_reverse.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,20 +0,0 @@ -import sqlite3 - -def collate_reverse(string1, string2): - if string1 == string2: - return 0 - elif string1 < string2: - return 1 - else: - return -1 - -con = sqlite3.connect(":memory:") -con.create_collation("reverse", collate_reverse) - -cur = con.cursor() -cur.execute("create table test(x)") -cur.executemany("insert into test(x) values (?)", [("a",), ("b",)]) -cur.execute("select x from test order by x collate reverse") -for row in cur: - print(row) -con.close() diff -Nru python3.11-3.11.0~rc1/Doc/includes/sqlite3/complete_statement.py python3.11-3.11.0~rc2/Doc/includes/sqlite3/complete_statement.py --- python3.11-3.11.0~rc1/Doc/includes/sqlite3/complete_statement.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/includes/sqlite3/complete_statement.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,33 +0,0 @@ -# A minimal SQLite shell for experiments - -import sqlite3 - -con = sqlite3.connect(":memory:") -con.isolation_level = None -cur = con.cursor() - -buffer = "" - -print("Enter your SQL commands to execute in sqlite3.") -print("Enter a blank line to exit.") - -while True: - line = input() - if line == "": - break - buffer += line - if sqlite3.complete_statement(buffer): - try: - buffer = buffer.strip() - cur.execute(buffer) - - if buffer.lstrip().upper().startswith("SELECT"): - print(cur.fetchall()) - except sqlite3.Error as e: - err_msg = str(e) - err_code = e.sqlite_errorcode - err_name = e.sqlite_errorname - print(f"{err_name} ({err_code}): {err_msg}") - buffer = "" - -con.close() diff -Nru python3.11-3.11.0~rc1/Doc/includes/sqlite3/converter_point.py python3.11-3.11.0~rc2/Doc/includes/sqlite3/converter_point.py --- python3.11-3.11.0~rc1/Doc/includes/sqlite3/converter_point.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/includes/sqlite3/converter_point.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,40 +0,0 @@ -import sqlite3 - -class Point: - def __init__(self, x, y): - self.x, self.y = x, y - - def __repr__(self): - return f"Point({self.x}, {self.y})" - -def adapt_point(point): - return f"{point.x};{point.y}".encode("utf-8") - -def convert_point(s): - x, y = list(map(float, s.split(b";"))) - return Point(x, y) - -# Register the adapter and converter -sqlite3.register_adapter(Point, adapt_point) -sqlite3.register_converter("point", convert_point) - -# 1) Parse using declared types -p = Point(4.0, -3.2) -con = sqlite3.connect(":memory:", detect_types=sqlite3.PARSE_DECLTYPES) -cur = con.execute("create table test(p point)") - -cur.execute("insert into test(p) values (?)", (p,)) -cur.execute("select p from test") -print("with declared types:", cur.fetchone()[0]) -cur.close() -con.close() - -# 2) Parse using column names -con = sqlite3.connect(":memory:", detect_types=sqlite3.PARSE_COLNAMES) -cur = con.execute("create table test(p)") - -cur.execute("insert into test(p) values (?)", (p,)) -cur.execute('select p as "p [point]" from test') -print("with column names:", cur.fetchone()[0]) -cur.close() -con.close() diff -Nru python3.11-3.11.0~rc1/Doc/includes/sqlite3/ctx_manager.py python3.11-3.11.0~rc2/Doc/includes/sqlite3/ctx_manager.py --- python3.11-3.11.0~rc1/Doc/includes/sqlite3/ctx_manager.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/includes/sqlite3/ctx_manager.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,20 +0,0 @@ -import sqlite3 - -con = sqlite3.connect(":memory:") -con.execute("create table lang (id integer primary key, name varchar unique)") - -# Successful, con.commit() is called automatically afterwards -with con: - con.execute("insert into lang(name) values (?)", ("Python",)) - -# con.rollback() is called after the with block finishes with an exception, the -# exception is still raised and must be caught -try: - with con: - con.execute("insert into lang(name) values (?)", ("Python",)) -except sqlite3.IntegrityError: - print("couldn't add Python twice") - -# Connection object used as context manager only commits or rollbacks transactions, -# so the connection object should be closed manually -con.close() diff -Nru python3.11-3.11.0~rc1/Doc/includes/sqlite3/execute_1.py python3.11-3.11.0~rc2/Doc/includes/sqlite3/execute_1.py --- python3.11-3.11.0~rc1/Doc/includes/sqlite3/execute_1.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/includes/sqlite3/execute_1.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,22 +0,0 @@ -import sqlite3 - -con = sqlite3.connect(":memory:") -cur = con.cursor() -cur.execute("create table lang (name, first_appeared)") - -# This is the qmark style: -cur.execute("insert into lang values (?, ?)", ("C", 1972)) - -# The qmark style used with executemany(): -lang_list = [ - ("Fortran", 1957), - ("Python", 1991), - ("Go", 2009), -] -cur.executemany("insert into lang values (?, ?)", lang_list) - -# And this is the named style: -cur.execute("select * from lang where first_appeared=:year", {"year": 1972}) -print(cur.fetchall()) - -con.close() diff -Nru python3.11-3.11.0~rc1/Doc/includes/sqlite3/load_extension.py python3.11-3.11.0~rc2/Doc/includes/sqlite3/load_extension.py --- python3.11-3.11.0~rc1/Doc/includes/sqlite3/load_extension.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/includes/sqlite3/load_extension.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,28 +0,0 @@ -import sqlite3 - -con = sqlite3.connect(":memory:") - -# enable extension loading -con.enable_load_extension(True) - -# Load the fulltext search extension -con.execute("select load_extension('./fts3.so')") - -# alternatively you can load the extension using an API call: -# con.load_extension("./fts3.so") - -# disable extension loading again -con.enable_load_extension(False) - -# example from SQLite wiki -con.execute("create virtual table recipe using fts3(name, ingredients)") -con.executescript(""" - insert into recipe (name, ingredients) values ('broccoli stew', 'broccoli peppers cheese tomatoes'); - insert into recipe (name, ingredients) values ('pumpkin stew', 'pumpkin onions garlic celery'); - insert into recipe (name, ingredients) values ('broccoli pie', 'broccoli cheese onions flour'); - insert into recipe (name, ingredients) values ('pumpkin pie', 'pumpkin sugar flour butter'); - """) -for row in con.execute("select rowid, name, ingredients from recipe where name match 'pie'"): - print(row) - -con.close() diff -Nru python3.11-3.11.0~rc1/Doc/includes/sqlite3/md5func.py python3.11-3.11.0~rc2/Doc/includes/sqlite3/md5func.py --- python3.11-3.11.0~rc1/Doc/includes/sqlite3/md5func.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/includes/sqlite3/md5func.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -import sqlite3 -import hashlib - -def md5sum(t): - return hashlib.md5(t).hexdigest() - -con = sqlite3.connect(":memory:") -con.create_function("md5", 1, md5sum) -cur = con.cursor() -cur.execute("select md5(?)", (b"foo",)) -print(cur.fetchone()[0]) - -con.close() diff -Nru python3.11-3.11.0~rc1/Doc/includes/sqlite3/mysumaggr.py python3.11-3.11.0~rc2/Doc/includes/sqlite3/mysumaggr.py --- python3.11-3.11.0~rc1/Doc/includes/sqlite3/mysumaggr.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/includes/sqlite3/mysumaggr.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,22 +0,0 @@ -import sqlite3 - -class MySum: - def __init__(self): - self.count = 0 - - def step(self, value): - self.count += value - - def finalize(self): - return self.count - -con = sqlite3.connect(":memory:") -con.create_aggregate("mysum", 1, MySum) -cur = con.cursor() -cur.execute("create table test(i)") -cur.execute("insert into test(i) values (1)") -cur.execute("insert into test(i) values (2)") -cur.execute("select mysum(i) from test") -print(cur.fetchone()[0]) - -con.close() diff -Nru python3.11-3.11.0~rc1/Doc/includes/sqlite3/rowclass.py python3.11-3.11.0~rc2/Doc/includes/sqlite3/rowclass.py --- python3.11-3.11.0~rc1/Doc/includes/sqlite3/rowclass.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/includes/sqlite3/rowclass.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,14 +0,0 @@ -import sqlite3 - -con = sqlite3.connect(":memory:") -con.row_factory = sqlite3.Row - -cur = con.cursor() -cur.execute("select 'John' as name, 42 as age") -for row in cur: - assert row[0] == row["name"] - assert row["name"] == row["nAmE"] - assert row[1] == row["age"] - assert row[1] == row["AgE"] - -con.close() diff -Nru python3.11-3.11.0~rc1/Doc/includes/sqlite3/row_factory.py python3.11-3.11.0~rc2/Doc/includes/sqlite3/row_factory.py --- python3.11-3.11.0~rc1/Doc/includes/sqlite3/row_factory.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/includes/sqlite3/row_factory.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,15 +0,0 @@ -import sqlite3 - -def dict_factory(cursor, row): - d = {} - for idx, col in enumerate(cursor.description): - d[col[0]] = row[idx] - return d - -con = sqlite3.connect(":memory:") -con.row_factory = dict_factory -cur = con.cursor() -cur.execute("select 1 as a") -print(cur.fetchone()["a"]) - -con.close() diff -Nru python3.11-3.11.0~rc1/Doc/includes/sqlite3/shortcut_methods.py python3.11-3.11.0~rc2/Doc/includes/sqlite3/shortcut_methods.py --- python3.11-3.11.0~rc1/Doc/includes/sqlite3/shortcut_methods.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/includes/sqlite3/shortcut_methods.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,24 +0,0 @@ -import sqlite3 - -langs = [ - ("C++", 1985), - ("Objective-C", 1984), -] - -con = sqlite3.connect(":memory:") - -# Create the table -con.execute("create table lang(name, first_appeared)") - -# Fill the table -con.executemany("insert into lang(name, first_appeared) values (?, ?)", langs) - -# Print the table contents -for row in con.execute("select name, first_appeared from lang"): - print(row) - -print("I just deleted", con.execute("delete from lang").rowcount, "rows") - -# close is not a shortcut method and it's not called automatically, -# so the connection object should be closed manually -con.close() diff -Nru python3.11-3.11.0~rc1/Doc/includes/sqlite3/sumintwindow.py python3.11-3.11.0~rc2/Doc/includes/sqlite3/sumintwindow.py --- python3.11-3.11.0~rc1/Doc/includes/sqlite3/sumintwindow.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/includes/sqlite3/sumintwindow.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,46 +0,0 @@ -# Example taken from https://www.sqlite.org/windowfunctions.html#udfwinfunc -import sqlite3 - - -class WindowSumInt: - def __init__(self): - self.count = 0 - - def step(self, value): - """Adds a row to the current window.""" - self.count += value - - def value(self): - """Returns the current value of the aggregate.""" - return self.count - - def inverse(self, value): - """Removes a row from the current window.""" - self.count -= value - - def finalize(self): - """Returns the final value of the aggregate. - - Any clean-up actions should be placed here. - """ - return self.count - - -con = sqlite3.connect(":memory:") -cur = con.execute("create table test(x, y)") -values = [ - ("a", 4), - ("b", 5), - ("c", 3), - ("d", 8), - ("e", 1), -] -cur.executemany("insert into test values(?, ?)", values) -con.create_window_function("sumint", 1, WindowSumInt) -cur.execute(""" - select x, sumint(y) over ( - order by x rows between 1 preceding and 1 following - ) as sum_y - from test order by x -""") -print(cur.fetchall()) diff -Nru python3.11-3.11.0~rc1/Doc/includes/sqlite3/text_factory.py python3.11-3.11.0~rc2/Doc/includes/sqlite3/text_factory.py --- python3.11-3.11.0~rc1/Doc/includes/sqlite3/text_factory.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/includes/sqlite3/text_factory.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,29 +0,0 @@ -import sqlite3 - -con = sqlite3.connect(":memory:") -cur = con.cursor() - -AUSTRIA = "Österreich" - -# by default, rows are returned as str -cur.execute("select ?", (AUSTRIA,)) -row = cur.fetchone() -assert row[0] == AUSTRIA - -# but we can make sqlite3 always return bytestrings ... -con.text_factory = bytes -cur.execute("select ?", (AUSTRIA,)) -row = cur.fetchone() -assert type(row[0]) is bytes -# the bytestrings will be encoded in UTF-8, unless you stored garbage in the -# database ... -assert row[0] == AUSTRIA.encode("utf-8") - -# we can also implement a custom text_factory ... -# here we implement one that appends "foo" to all strings -con.text_factory = lambda x: x.decode("utf-8") + "foo" -cur.execute("select ?", ("bar",)) -row = cur.fetchone() -assert row[0] == "barfoo" - -con.close() diff -Nru python3.11-3.11.0~rc1/Doc/library/asyncio-api-index.rst python3.11-3.11.0~rc2/Doc/library/asyncio-api-index.rst --- python3.11-3.11.0~rc1/Doc/library/asyncio-api-index.rst 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/library/asyncio-api-index.rst 2022-09-11 19:23:30.000000000 +0000 @@ -21,8 +21,25 @@ * - :func:`run` - Create event loop, run a coroutine, close the loop. + * - :class:`Runner` + - A context manager that simplifies multiple async function calls. + + * - :class:`Task` + - Task object. + + * - :class:`TaskGroup` + - A context manager that holds a group of tasks. Provides + a convenient and reliable way to wait for all tasks in the group to + finish. + * - :func:`create_task` - - Start an asyncio Task. + - Start an asyncio Task, then returns it. + + * - :func:`current_task` + - Return the current Task. + + * - :func:`all_tasks` + - Return all tasks that are not yet finished for an event loop. * - ``await`` :func:`sleep` - Sleep for a number of seconds. @@ -39,14 +56,8 @@ * - ``await`` :func:`wait` - Monitor for completion. - * - :func:`current_task` - - Return the current Task. - - * - :func:`all_tasks` - - Return all tasks for an event loop. - - * - :class:`Task` - - Task object. + * - :func:`timeout` + - Run with a timeout. Useful in cases when `wait_for` is not suitable. * - :func:`to_thread` - Asynchronously run a function in a separate OS thread. diff -Nru python3.11-3.11.0~rc1/Doc/library/asyncio-eventloop.rst python3.11-3.11.0~rc2/Doc/library/asyncio-eventloop.rst --- python3.11-3.11.0~rc1/Doc/library/asyncio-eventloop.rst 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/library/asyncio-eventloop.rst 2022-09-11 19:23:30.000000000 +0000 @@ -332,7 +332,7 @@ .. method:: loop.create_task(coro, *, name=None, context=None) - Schedule the execution of a :ref:`coroutine`. + Schedule the execution of :ref:`coroutine ` *coro*. Return a :class:`Task` object. Third-party event loops can use their own subclass of :class:`Task` diff -Nru python3.11-3.11.0~rc1/Doc/library/asyncio-future.rst python3.11-3.11.0~rc2/Doc/library/asyncio-future.rst --- python3.11-3.11.0~rc1/Doc/library/asyncio-future.rst 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/library/asyncio-future.rst 2022-09-11 19:23:30.000000000 +0000 @@ -55,7 +55,7 @@ preferred way for creating new Tasks. Save a reference to the result of this function, to avoid - a task disappearing mid execution. + a task disappearing mid-execution. .. versionchanged:: 3.5.1 The function accepts any :term:`awaitable` object. diff -Nru python3.11-3.11.0~rc1/Doc/library/asyncio-runner.rst python3.11-3.11.0~rc2/Doc/library/asyncio-runner.rst --- python3.11-3.11.0~rc1/Doc/library/asyncio-runner.rst 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/library/asyncio-runner.rst 2022-09-11 19:23:30.000000000 +0000 @@ -75,7 +75,9 @@ :ref:`asyncio-debug-mode` settings. *loop_factory* could be used for overriding the loop creation. - :func:`asyncio.new_event_loop` is used if ``None``. + It is the responsibility of the *loop_factory* to set the created loop as the + current one. By default :func:`asyncio.new_event_loop` is used and set as + current event loop with :func:`asyncio.set_event_loop` if *loop_factory* is ``None``. Basically, :func:`asyncio.run()` example can be rewritten with the runner usage:: diff -Nru python3.11-3.11.0~rc1/Doc/library/asyncio-task.rst python3.11-3.11.0~rc2/Doc/library/asyncio-task.rst --- python3.11-3.11.0~rc1/Doc/library/asyncio-task.rst 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/library/asyncio-task.rst 2022-09-11 19:23:30.000000000 +0000 @@ -40,7 +40,7 @@ >>> main() -To actually run a coroutine, asyncio provides three main mechanisms: +To actually run a coroutine, asyncio provides the following mechanisms: * The :func:`asyncio.run` function to run the top-level entry point "main()" function (see the above example.) @@ -254,9 +254,9 @@ .. important:: Save a reference to the result of this function, to avoid - a task disappearing mid execution. The event loop only keeps + a task disappearing mid-execution. The event loop only keeps weak references to tasks. A task that isn't referenced elsewhere - may get garbage-collected at any time, even before it's done. + may get garbage collected at any time, even before it's done. For reliable "fire-and-forget" background tasks, gather them in a collection:: @@ -520,7 +520,8 @@ The statement:: - res = await shield(something()) + task = asyncio.create_task(something()) + res = await shield(task) is equivalent to:: @@ -539,11 +540,19 @@ the ``shield()`` function should be combined with a try/except clause, as follows:: + task = asyncio.create_task(something()) try: - res = await shield(something()) + res = await shield(task) except CancelledError: res = None + .. important:: + + Save a reference to tasks passed to this function, to avoid + a task disappearing mid-execution. The event loop only keeps + weak references to tasks. A task that isn't referenced elsewhere + may get garbage collected at any time, even before it's done. + .. versionchanged:: 3.10 Removed the *loop* parameter. diff -Nru python3.11-3.11.0~rc1/Doc/library/bdb.rst python3.11-3.11.0~rc2/Doc/library/bdb.rst --- python3.11-3.11.0~rc1/Doc/library/bdb.rst 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/library/bdb.rst 2022-09-11 19:23:30.000000000 +0000 @@ -20,20 +20,21 @@ The :mod:`bdb` module also defines two classes: -.. class:: Breakpoint(self, file, line, temporary=0, cond=None, funcname=None) +.. class:: Breakpoint(self, file, line, temporary=False, cond=None, funcname=None) This class implements temporary breakpoints, ignore counts, disabling and (re-)enabling, and conditionals. Breakpoints are indexed by number through a list called :attr:`bpbynumber` - and by ``(file, line)`` pairs through :attr:`bplist`. The former points to a - single instance of class :class:`Breakpoint`. The latter points to a list of - such instances since there may be more than one breakpoint per line. - - When creating a breakpoint, its associated filename should be in canonical - form. If a *funcname* is defined, a breakpoint hit will be counted when the - first line of that function is executed. A conditional breakpoint always - counts a hit. + and by ``(file, line)`` pairs through :attr:`bplist`. The former points to + a single instance of class :class:`Breakpoint`. The latter points to a list + of such instances since there may be more than one breakpoint per line. + + When creating a breakpoint, its associated :attr:`file name ` should + be in canonical form. If a :attr:`funcname` is defined, a breakpoint + :attr:`hit ` will be counted when the first line of that function is + executed. A :attr:`conditional ` breakpoint always counts a + :attr:`hit `. :class:`Breakpoint` instances have the following methods: @@ -59,12 +60,12 @@ Return a string with all the information about the breakpoint, nicely formatted: - * The breakpoint number. - * If it is temporary or not. - * Its file,line position. - * The condition that causes a break. - * If it must be ignored the next N times. - * The breakpoint hit count. + * Breakpoint number. + * Temporary status (del or keep). + * File/line position. + * Break condition. + * Number of times to ignore. + * Number of times hit. .. versionadded:: 3.2 @@ -73,6 +74,49 @@ Print the output of :meth:`bpformat` to the file *out*, or if it is ``None``, to standard output. + :class:`Breakpoint` instances have the following attributes: + + .. attribute:: file + + File name of the :class:`Breakpoint`. + + .. attribute:: line + + Line number of the :class:`Breakpoint` within :attr:`file`. + + .. attribute:: temporary + + True if a :class:`Breakpoint` at (file, line) is temporary. + + .. attribute:: cond + + Condition for evaluating a :class:`Breakpoint` at (file, line). + + .. attribute:: funcname + + Function name that defines whether a :class:`Breakpoint` is hit upon + entering the function. + + .. attribute:: enabled + + True if :class:`Breakpoint` is enabled. + + .. attribute:: bpbynumber + + Numeric index for a single instance of a :class:`Breakpoint`. + + .. attribute:: bplist + + Dictionary of :class:`Breakpoint` instances indexed by + (:attr:`file`, :attr:`line`) tuples. + + .. attribute:: ignore + + Number of times to ignore a :class:`Breakpoint`. + + .. attribute:: hits + + Count of the number of times a :class:`Breakpoint` has been hit. .. class:: Bdb(skip=None) @@ -95,9 +139,12 @@ .. method:: canonic(filename) - Auxiliary method for getting a filename in a canonical form, that is, as a - case-normalized (on case-insensitive filesystems) absolute path, stripped - of surrounding angle brackets. + Return canonical form of *filename*. + + For real file names, the canonical form is an operating-system-dependent, + :func:`case-normalized ` :func:`absolute path + `. A *filename* with angle brackets, such as `""` + generated in interactive mode, is returned unchanged. .. method:: reset() @@ -166,45 +213,46 @@ Normally derived classes don't override the following methods, but they may if they want to redefine the definition of stopping and breakpoints. + .. method:: is_skipped_line(module_name) + + Return True if *module_name* matches any skip pattern. + .. method:: stop_here(frame) - This method checks if the *frame* is somewhere below :attr:`botframe` in - the call stack. :attr:`botframe` is the frame in which debugging started. + Return True if *frame* is below the starting frame in the stack. .. method:: break_here(frame) - This method checks if there is a breakpoint in the filename and line - belonging to *frame* or, at least, in the current function. If the - breakpoint is a temporary one, this method deletes it. + Return True if there is an effective breakpoint for this line. + + Check whether a line or function breakpoint exists and is in effect. Delete temporary + breakpoints based on information from :func:`effective`. .. method:: break_anywhere(frame) - This method checks if there is a breakpoint in the filename of the current - frame. + Return True if any breakpoint exists for *frame*'s filename. Derived classes should override these methods to gain control over debugger operation. .. method:: user_call(frame, argument_list) - This method is called from :meth:`dispatch_call` when there is the - possibility that a break might be necessary anywhere inside the called - function. + Called from :meth:`dispatch_call` if a break might stop inside the + called function. .. method:: user_line(frame) - This method is called from :meth:`dispatch_line` when either - :meth:`stop_here` or :meth:`break_here` yields ``True``. + Called from :meth:`dispatch_line` when either :meth:`stop_here` or + :meth:`break_here` returns ``True``. .. method:: user_return(frame, return_value) - This method is called from :meth:`dispatch_return` when :meth:`stop_here` - yields ``True``. + Called from :meth:`dispatch_return` when :meth:`stop_here` returns ``True``. .. method:: user_exception(frame, exc_info) - This method is called from :meth:`dispatch_exception` when - :meth:`stop_here` yields ``True``. + Called from :meth:`dispatch_exception` when :meth:`stop_here` + returns ``True``. .. method:: do_clear(arg) @@ -228,9 +276,9 @@ Stop when returning from the given frame. - .. method:: set_until(frame) + .. method:: set_until(frame, lineno=None) - Stop when the line with the line no greater than the current one is + Stop when the line with the *lineno* greater than the current one is reached or when returning from current frame. .. method:: set_trace([frame]) @@ -253,7 +301,7 @@ breakpoints. These methods return a string containing an error message if something went wrong, or ``None`` if all is well. - .. method:: set_break(filename, lineno, temporary=0, cond, funcname) + .. method:: set_break(filename, lineno, temporary=False, cond=None, funcname=None) Set a new breakpoint. If the *lineno* line doesn't exist for the *filename* passed as argument, return an error message. The *filename* @@ -261,8 +309,8 @@ .. method:: clear_break(filename, lineno) - Delete the breakpoints in *filename* and *lineno*. If none were set, an - error message is returned. + Delete the breakpoints in *filename* and *lineno*. If none were set, + return an error message. .. method:: clear_bpbynumber(arg) @@ -272,12 +320,13 @@ .. method:: clear_all_file_breaks(filename) - Delete all breakpoints in *filename*. If none were set, an error message - is returned. + Delete all breakpoints in *filename*. If none were set, return an error + message. .. method:: clear_all_breaks() - Delete all existing breakpoints. + Delete all existing breakpoints. If none were set, return an error + message. .. method:: get_bpbynumber(arg) @@ -290,7 +339,7 @@ .. method:: get_break(filename, lineno) - Check if there is a breakpoint for *lineno* of *filename*. + Return True if there is a breakpoint for *lineno* in *filename*. .. method:: get_breaks(filename, lineno) @@ -311,16 +360,18 @@ .. method:: get_stack(f, t) - Get a list of records for a frame and all higher (calling) and lower - frames, and the size of the higher part. + Return a list of (frame, lineno) tuples in a stack trace, and a size. + + The most recently called frame is last in the list. The size is the number + of frames below the frame where the debugger was invoked. .. method:: format_stack_entry(frame_lineno, lprefix=': ') - Return a string with information about a stack entry, identified by a - ``(frame, lineno)`` tuple: + Return a string with information about a stack entry, which is a + ``(frame, lineno)`` tuple. The return string contains: - * The canonical form of the filename which contains the frame. - * The function name, or ``""``. + * The canonical filename which contains the frame. + * The function name or ``""``. * The input arguments. * The return value. * The line of code (if it exists). @@ -352,20 +403,34 @@ .. function:: checkfuncname(b, frame) - Check whether we should break here, depending on the way the breakpoint *b* - was set. + Return True if we should break here, depending on the way the + :class:`Breakpoint` *b* was set. - If it was set via line number, it checks if ``b.line`` is the same as the one - in the frame also passed as argument. If the breakpoint was set via function - name, we have to check we are in the right frame (the right function) and if - we are in its first executable line. + If it was set via line number, it checks if + :attr:`b.line ` is the same as the one in *frame*. + If the breakpoint was set via + :attr:`function name `, we have to check we are in + the right *frame* (the right function) and if we are on its first executable + line. .. function:: effective(file, line, frame) - Determine if there is an effective (active) breakpoint at this line of code. - Return a tuple of the breakpoint and a boolean that indicates if it is ok - to delete a temporary breakpoint. Return ``(None, None)`` if there is no - matching breakpoint. + Return ``(active breakpoint, delete temporary flag)`` or ``(None, None)`` as the + breakpoint to act upon. + + The *active breakpoint* is the first entry in + :attr:`bplist ` for the + (:attr:`file `, :attr:`line `) + (which must exist) that is :attr:`enabled `, for + which :func:`checkfuncname` is True, and that has neither a False + :attr:`condition ` nor positive + :attr:`ignore ` count. The *flag*, meaning that a + temporary breakpoint should be deleted, is False only when the + :attr:`cond ` cannot be evaluated (in which case, + :attr:`ignore ` count is ignored). + + If no such entry exists, then (None, None) is returned. + .. function:: set_trace() diff -Nru python3.11-3.11.0~rc1/Doc/library/bisect.rst python3.11-3.11.0~rc2/Doc/library/bisect.rst --- python3.11-3.11.0~rc1/Doc/library/bisect.rst 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/library/bisect.rst 2022-09-11 19:23:30.000000000 +0000 @@ -216,7 +216,7 @@ ... Movie('Aliens', 1986, 'Scott') ... ] - >>> # Find the first movie released on or after 1960 + >>> # Find the first movie released after 1960 >>> by_year = attrgetter('released') >>> movies.sort(key=by_year) >>> movies[bisect(movies, 1960, key=by_year)] diff -Nru python3.11-3.11.0~rc1/Doc/library/functions.rst python3.11-3.11.0~rc2/Doc/library/functions.rst --- python3.11-3.11.0~rc1/Doc/library/functions.rst 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/library/functions.rst 2022-09-11 19:23:30.000000000 +0000 @@ -164,6 +164,8 @@ :func:`sys.breakpointhook` can be set to some other function and :func:`breakpoint` will automatically call that, allowing you to drop into the debugger of choice. + If :func:`sys.breakpointhook` is not available to be called, this function will + raise :exc:`RuntimeError`. .. audit-event:: builtins.breakpoint breakpointhook breakpoint @@ -908,6 +910,13 @@ .. versionchanged:: 3.11 The delegation to :meth:`__trunc__` is deprecated. + .. versionchanged:: 3.11 + :class:`int` string inputs and string representations can be limited to + help avoid denial of service attacks. A :exc:`ValueError` is raised when + the limit is exceeded while converting a string *x* to an :class:`int` or + when converting an :class:`int` into a string would exceed the limit. + See the :ref:`integer string conversion length limitation + ` documentation. .. function:: isinstance(object, classinfo) @@ -1513,6 +1522,8 @@ of the type of the object together with additional information often including the name and address of the object. A class can control what this function returns for its instances by defining a :meth:`__repr__` method. + If :func:`sys.displayhook` is not accessible, this function will raise + :exc:`RuntimeError`. .. function:: reversed(seq) diff -Nru python3.11-3.11.0~rc1/Doc/library/functools.rst python3.11-3.11.0~rc2/Doc/library/functools.rst --- python3.11-3.11.0~rc1/Doc/library/functools.rst 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/library/functools.rst 2022-09-11 19:23:30.000000000 +0000 @@ -119,7 +119,7 @@ tool for programs being converted from Python 2 which supported the use of comparison functions. - A comparison function is any callable that accept two arguments, compares them, + A comparison function is any callable that accepts two arguments, compares them, and returns a negative number for less-than, zero for equality, or a positive number for greater-than. A key function is a callable that accepts one argument and returns another value to be used as the sort key. diff -Nru python3.11-3.11.0~rc1/Doc/library/grp.rst python3.11-3.11.0~rc2/Doc/library/grp.rst --- python3.11-3.11.0~rc1/Doc/library/grp.rst 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/library/grp.rst 2022-09-11 19:23:30.000000000 +0000 @@ -45,9 +45,8 @@ Return the group database entry for the given numeric group ID. :exc:`KeyError` is raised if the entry asked for cannot be found. - .. deprecated:: 3.6 - Since Python 3.6 the support of non-integer arguments like floats or - strings in :func:`getgrgid` is deprecated. + .. versionchanged:: 3.10 + :exc:`TypeError` is raised for non-integer arguments like floats or strings. .. function:: getgrnam(name) diff -Nru python3.11-3.11.0~rc1/Doc/library/idle.rst python3.11-3.11.0~rc2/Doc/library/idle.rst --- python3.11-3.11.0~rc1/Doc/library/idle.rst 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/library/idle.rst 2022-09-11 19:23:30.000000000 +0000 @@ -61,17 +61,17 @@ Open... Open an existing file with an Open dialog. -Recent Files - Open a list of recent files. Click one to open it. - Open Module... Open an existing module (searches sys.path). +Recent Files + Open a list of recent files. Click one to open it. + .. index:: - single: Class browser + single: Module browser single: Path browser -Class Browser +Module Browser Show functions, classes, and methods in the current Editor file in a tree structure. In the shell, open a module first. @@ -89,7 +89,7 @@ Save the current window with a Save As dialog. The file saved becomes the new associated file for the window. (If your file namager is set to hide extensions, the current extension will be omitted in the file name box. - If the new filename has no '.', '.py' and .'txt' will be added for Python + If the new filename has no '.', '.py' and '.txt' will be added for Python and text files, except that on macOS Aqua,'.py' is added for all files.) Save Copy As... @@ -117,6 +117,9 @@ Redo Redo the last undone change to the current window. +Select All + Select the entire contents of the current window. + Cut Copy selection into the system-wide clipboard; then delete the selection. @@ -128,9 +131,6 @@ The clipboard functions are also available in context menus. -Select All - Select the entire contents of the current window. - Find... Open a search dialog with many options @@ -159,12 +159,12 @@ Expand a prefix you have typed to match a full word in the same window; repeat to get a different expansion. -Show call tip +Show Call Tip After an unclosed parenthesis for a function, open a small window with function parameter hints. See :ref:`Calltips ` in the Editing and navigation section below. -Show surrounding parens +Show Surrounding Parens Highlight the surrounding parenthesis. .. _format-menu: @@ -172,6 +172,11 @@ Format menu (Editor window only) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Format Paragraph + Reformat the current blank-line-delimited paragraph in comment block or + multiline string or selected line in a string. All lines in the + paragraph will be formatted to less than N columns, where N defaults to 72. + Indent Region Shift selected lines right by the indent width (default 4 spaces). @@ -198,12 +203,7 @@ Open a dialog to change indent width. The accepted default by the Python community is 4 spaces. -Format Paragraph - Reformat the current blank-line-delimited paragraph in comment block or - multiline string or selected line in a string. All lines in the - paragraph will be formatted to less than N columns, where N defaults to 72. - -Strip trailing whitespace +Strip Trailing Chitespace Remove trailing space and other whitespace characters after the last non-whitespace character of a line by applying str.rstrip to each line, including lines within multiline strings. Except for Shell windows, @@ -474,6 +474,14 @@ See also the indent/dedent region commands on the :ref:`Format menu `. +Search and Replace +^^^^^^^^^^^^^^^^^^ + +Any selection becomes a search target. However, only selections within +a line work because searches are only performed within lines with the +terminal newline removed. If ``[x] Regular expresion`` is checked, the +target is interpreted according to the Python re module. + .. _completions: Completions @@ -975,3 +983,23 @@ beginning of config-extensions.def in the idlelib directory for further information. The only current default extension is zzdummy, an example also used for testing. + + +idlelib +------- + +.. module:: idlelib + :synopsis: Implementation package for the IDLE shell/editor. + +**Source code:** :source:`Lib/idlelib` + +-------------- + +The Lib/idlelib package implements the IDLE application. See the rest +of this page for how to use IDLE. + +The files in idlelib are described in idlelib/README.txt. Access it +either in idlelib or click Help => About IDLE on the IDLE menu. This +file also maps IDLE menu items to the code that implements the item. +Except for files listed under 'Startup', the idlelib code is 'private' in +sense that feature changes can be backported (see :pep:`434`). diff -Nru python3.11-3.11.0~rc1/Doc/library/io.rst python3.11-3.11.0~rc2/Doc/library/io.rst --- python3.11-3.11.0~rc1/Doc/library/io.rst 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/library/io.rst 2022-09-11 19:23:30.000000000 +0000 @@ -1052,8 +1052,12 @@ The initial value of the buffer can be set by providing *initial_value*. If newline translation is enabled, newlines will be encoded as if by - :meth:`~TextIOBase.write`. The stream is positioned at the start of - the buffer. + :meth:`~TextIOBase.write`. The stream is positioned at the start of the + buffer which emulates opening an existing file in a `w+` mode, making it + ready for an immediate write from the beginning or for a write that + would overwrite the initial value. To emulate opening a file in an `a+` + mode ready for appending, use `f.seek(0, io.SEEK_END)` to reposition the + stream at the end of the buffer. The *newline* argument works like that of :class:`TextIOWrapper`, except that when writing output to the stream, if *newline* is ``None``, diff -Nru python3.11-3.11.0~rc1/Doc/library/itertools.rst python3.11-3.11.0~rc2/Doc/library/itertools.rst --- python3.11-3.11.0~rc1/Doc/library/itertools.rst 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/library/itertools.rst 2022-09-11 19:23:30.000000000 +0000 @@ -800,6 +800,18 @@ window.append(x) yield sum(map(operator.mul, kernel, window)) + def polynomial_from_roots(roots): + """Compute a polynomial's coefficients from its roots. + + (x - 5) (x + 4) (x - 3) expands to: x³ -4x² -17x + 60 + """ + # polynomial_from_roots([5, -4, 3]) --> [1, -4, -17, 60] + roots = list(map(operator.neg, roots)) + return [ + sum(map(math.prod, combinations(roots, k))) + for k in range(len(roots) + 1) + ] + def flatten(list_of_lists): "Flatten one level of nesting" return chain.from_iterable(list_of_lists) @@ -1137,6 +1149,13 @@ >>> list(convolve(data, [1, -2, 1])) [20, 0, -36, 24, -20, 20, -20, -4, 16] + >>> polynomial_from_roots([5, -4, 3]) + [1, -4, -17, 60] + >>> factored = lambda x: (x - 5) * (x + 4) * (x - 3) + >>> expanded = lambda x: x**3 -4*x**2 -17*x + 60 + >>> all(factored(x) == expanded(x) for x in range(-10, 11)) + True + >>> list(flatten([('a', 'b'), (), ('c', 'd', 'e'), ('f',), ('g', 'h', 'i')])) ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'] diff -Nru python3.11-3.11.0~rc1/Doc/library/json.rst python3.11-3.11.0~rc2/Doc/library/json.rst --- python3.11-3.11.0~rc1/Doc/library/json.rst 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/library/json.rst 2022-09-11 19:23:30.000000000 +0000 @@ -18,6 +18,11 @@ `JavaScript `_ object literal syntax (although it is not a strict subset of JavaScript [#rfc-errata]_ ). +.. warning:: + Be cautious when parsing JSON data from untrusted sources. A malicious + JSON string may cause the decoder to consume considerable CPU and memory + resources. Limiting the size of data to be parsed is recommended. + :mod:`json` exposes an API familiar to users of the standard library :mod:`marshal` and :mod:`pickle` modules. @@ -248,6 +253,12 @@ be used to use another datatype or parser for JSON integers (e.g. :class:`float`). + .. versionchanged:: 3.11 + The default *parse_int* of :func:`int` now limits the maximum length of + the integer string via the interpreter's :ref:`integer string + conversion length limitation ` to help avoid denial + of service attacks. + *parse_constant*, if specified, will be called with one of the following strings: ``'-Infinity'``, ``'Infinity'``, ``'NaN'``. This can be used to raise an exception if invalid JSON numbers diff -Nru python3.11-3.11.0~rc1/Doc/library/logging.config.rst python3.11-3.11.0~rc2/Doc/library/logging.config.rst --- python3.11-3.11.0~rc1/Doc/library/logging.config.rst 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/library/logging.config.rst 2022-09-11 19:23:30.000000000 +0000 @@ -534,6 +534,25 @@ The ``filters`` member of ``handlers`` and ``loggers`` can take filter instances in addition to ids. +You can also specify a special key ``'.'`` whose value is a dictionary is a +mapping of attribute names to values. If found, the specified attributes will +be set on the user-defined object before it is returned. Thus, with the +following configuration:: + + { + '()' : 'my.package.customFormatterFactory', + 'bar' : 'baz', + 'spam' : 99.9, + 'answer' : 42, + '.' { + 'foo': 'bar', + 'baz': 'bozz' + } + } + +the returned formatter will have attribute ``foo`` set to ``'bar'`` and +attribute ``baz`` set to ``'bozz'``. + .. _logging-config-dict-externalobj: diff -Nru python3.11-3.11.0~rc1/Doc/library/logging.handlers.rst python3.11-3.11.0~rc2/Doc/library/logging.handlers.rst --- python3.11-3.11.0~rc1/Doc/library/logging.handlers.rst 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/library/logging.handlers.rst 2022-09-11 19:23:30.000000000 +0000 @@ -572,6 +572,13 @@ Returns a new instance of the :class:`DatagramHandler` class intended to communicate with a remote machine whose address is given by *host* and *port*. + .. note:: As UDP is not a streaming protocol, there is no persistent connection + between an instance of this handler and *host*. For this reason, when using a + network socket, a DNS lookup might have to be made each time an event is + logged, which can introduce some latency into the system. If this affects you, + you can do a lookup yourself and initialize this handler using the looked-up IP + address rather than the hostname. + .. versionchanged:: 3.4 If ``port`` is specified as ``None``, a Unix domain socket is created using the value in ``host`` - otherwise, a UDP socket is created. @@ -629,6 +636,12 @@ application needs to run on several platforms). On Windows, you pretty much have to use the UDP option. + .. note:: On macOS 12.x (Monterey), Apple has changed the behaviour of their + syslog daemon - it no longer listens on a domain socket. Therefore, you cannot + expect :class:`SysLogHandler` to work on this system. + + See :gh:`91070` for more information. + .. versionchanged:: 3.2 *socktype* was added. @@ -1047,6 +1060,20 @@ the record to a dict or JSON string, or send a modified copy of the record while leaving the original intact. + .. note:: The base implementation formats the message with arguments, sets + the ``message`` and ``msg`` attributes to the formatted message and + sets the ``args`` and ``exc_text`` attributes to ``None`` to allow + pickling and to prevent further attempts at formatting. This means + that a handler on the :class:`QueueListener` side won't have the + information to do custom formatting, e.g. of exceptions. You may wish + to subclass ``QueueHandler`` and override this method to e.g. avoid + setting ``exc_text`` to ``None``. Note that the ``message`` / ``msg`` + / ``args`` changes are related to ensuring the record is pickleable, + and you might or might not be able to avoid doing that depending on + whether your ``args`` are pickleable. (Note that you may have to + consider not only your own code but also code in any libraries that + you use.) + .. method:: enqueue(record) Enqueues the record on the queue using ``put_nowait()``; you may diff -Nru python3.11-3.11.0~rc1/Doc/library/logging.rst python3.11-3.11.0~rc2/Doc/library/logging.rst --- python3.11-3.11.0~rc1/Doc/library/logging.rst 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/library/logging.rst 2022-09-11 19:23:30.000000000 +0000 @@ -652,6 +652,35 @@ :func:`traceback.print_stack`, but with the last newline removed) as a string. This default implementation just returns the input value. +.. class:: BufferingFormatter(linefmt=None) + + A base formatter class suitable for subclassing when you want to format a + number of records. You can pass a :class:`Formatter` instance which you want + to use to format each line (that corresponds to a single record). If not + specified, the default formatter (which just outputs the event message) is + used as the line formatter. + + .. method:: formatHeader(records) + + Return a header for a list of *records*. The base implementation just + returns the empty string. You will need to override this method if you + want specific behaviour, e.g. to show the count of records, a title or a + separator line. + + .. method:: formatFooter(records) + + Return a footer for a list of *records*. The base implementation just + returns the empty string. You will need to override this method if you + want specific behaviour, e.g. to show the count of records or a separator + line. + + .. method:: format(records) + + Return formatted text for a list of *records*. The base implementation + just returns the empty string if there are no records; otherwise, it + returns the concatenation of the header, each record formatted with the + line formatter, and the footer. + .. _filter: Filter Objects diff -Nru python3.11-3.11.0~rc1/Doc/library/multiprocessing.rst python3.11-3.11.0~rc2/Doc/library/multiprocessing.rst --- python3.11-3.11.0~rc1/Doc/library/multiprocessing.rst 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/library/multiprocessing.rst 2022-09-11 19:23:30.000000000 +0000 @@ -45,6 +45,16 @@ [1, 4, 9] +.. seealso:: + + :class:`concurrent.futures.ProcessPoolExecutor` offers a higher level interface + to push tasks to a background process without blocking execution of the + calling process. Compared to using the :class:`~multiprocessing.pool.Pool` + interface directly, the :mod:`concurrent.futures` API more readily allows + the submission of work to the underlying process pool to be separated from + waiting for the results. + + The :class:`Process` class ~~~~~~~~~~~~~~~~~~~~~~~~~~ diff -Nru python3.11-3.11.0~rc1/Doc/library/numbers.rst python3.11-3.11.0~rc2/Doc/library/numbers.rst --- python3.11-3.11.0~rc1/Doc/library/numbers.rst 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/library/numbers.rst 2022-09-11 19:23:30.000000000 +0000 @@ -58,11 +58,14 @@ .. class:: Rational - Subtypes :class:`Real` and adds - :attr:`~Rational.numerator` and :attr:`~Rational.denominator` properties, which - should be in lowest terms. With these, it provides a default for + Subtypes :class:`Real` and adds :attr:`~Rational.numerator` and + :attr:`~Rational.denominator` properties. It also provides a default for :func:`float`. + The :attr:`~Rational.numerator` and :attr:`~Rational.denominator` values + should be instances of :class:`Integral` and should be in lowest terms with + :attr:`~Rational.denominator` positive. + .. attribute:: numerator Abstract. diff -Nru python3.11-3.11.0~rc1/Doc/library/sqlite3.rst python3.11-3.11.0~rc2/Doc/library/sqlite3.rst --- python3.11-3.11.0~rc1/Doc/library/sqlite3.rst 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/library/sqlite3.rst 2022-09-11 19:23:30.000000000 +0000 @@ -8,11 +8,17 @@ **Source code:** :source:`Lib/sqlite3/` +.. Make sure we always doctest the tutorial with an empty database. -.. _sqlite3-intro: +.. testsetup:: + + import sqlite3 + src = sqlite3.connect(":memory:", isolation_level=None) + dst = sqlite3.connect("tutorial.db", isolation_level=None) + src.backup(dst) + del src, dst -Introduction ------------- +.. _sqlite3-intro: SQLite is a C library that provides a lightweight disk-based database that doesn't require a separate server process and allows accessing the database @@ -21,99 +27,19 @@ application using SQLite and then port the code to a larger database such as PostgreSQL or Oracle. -The sqlite3 module was written by Gerhard Häring. It provides an SQL interface +The :mod:`!sqlite3` module was written by Gerhard Häring. It provides an SQL interface compliant with the DB-API 2.0 specification described by :pep:`249`, and requires SQLite 3.7.15 or newer. This document includes four main sections: -* :ref:`sqlite3-tutorial` teaches how to use the sqlite3 module. +* :ref:`sqlite3-tutorial` teaches how to use the :mod:`!sqlite3` module. * :ref:`sqlite3-reference` describes the classes and functions this module defines. * :ref:`sqlite3-howtos` details how to handle specific tasks. * :ref:`sqlite3-explanation` provides in-depth background on transaction control. - -.. _sqlite3-tutorial: - -Tutorial --------- - -To use the module, start by creating a :class:`Connection` object that -represents the database. Here the data will be stored in the -:file:`example.db` file:: - - import sqlite3 - con = sqlite3.connect('example.db') - -The special path name ``:memory:`` can be provided to create a temporary -database in RAM. - -Once a :class:`Connection` has been established, create a :class:`Cursor` object -and call its :meth:`~Cursor.execute` method to perform SQL commands:: - - cur = con.cursor() - - # Create table - cur.execute('''CREATE TABLE stocks - (date text, trans text, symbol text, qty real, price real)''') - - # Insert a row of data - cur.execute("INSERT INTO stocks VALUES ('2006-01-05','BUY','RHAT',100,35.14)") - - # Save (commit) the changes - con.commit() - - # We can also close the connection if we are done with it. - # Just be sure any changes have been committed or they will be lost. - con.close() - -The saved data is persistent: it can be reloaded in a subsequent session even -after restarting the Python interpreter:: - - import sqlite3 - con = sqlite3.connect('example.db') - cur = con.cursor() - -At this point, our database only contains one row:: - - >>> res = cur.execute('SELECT count(rowid) FROM stocks') - >>> print(res.fetchone()) - (1,) - -The result is a one-item :class:`tuple`: -one row, with one column. -Now, let us insert three more rows of data, -using :meth:`~Cursor.executemany`:: - - >>> data = [ - ... ('2006-03-28', 'BUY', 'IBM', 1000, 45.0), - ... ('2006-04-05', 'BUY', 'MSFT', 1000, 72.0), - ... ('2006-04-06', 'SELL', 'IBM', 500, 53.0), - ... ] - >>> cur.executemany('INSERT INTO stocks VALUES(?, ?, ?, ?, ?)', data) - -Notice that we used ``?`` placeholders to bind *data* to the query. -Always use placeholders instead of :ref:`string formatting ` -to bind Python values to SQL statements, -to avoid `SQL injection attacks`_. -See the :ref:`placeholders how-to ` for more details. - -Then, retrieve the data by iterating over the result of a ``SELECT`` statement:: - - >>> for row in cur.execute('SELECT * FROM stocks ORDER BY price'): - ... print(row) - - ('2006-01-05', 'BUY', 'RHAT', 100, 35.14) - ('2006-03-28', 'BUY', 'IBM', 1000, 45.0) - ('2006-04-06', 'SELL', 'IBM', 500, 53.0) - ('2006-04-05', 'BUY', 'MSFT', 1000, 72.0) - -You've now created an SQLite database using the :mod:`!sqlite3` module. - -.. _SQL injection attacks: https://en.wikipedia.org/wiki/SQL_injection - .. seealso:: https://www.sqlite.org @@ -127,137 +53,212 @@ PEP written by Marc-André Lemburg. -.. _sqlite3-reference: - -Reference ---------- - -.. _sqlite3-module-contents: - -Module functions and constants -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - -.. data:: apilevel - - String constant stating the supported DB-API level. Required by the DB-API. - Hard-coded to ``"2.0"``. - -.. data:: paramstyle - - String constant stating the type of parameter marker formatting expected by - the :mod:`!sqlite3` module. Required by the DB-API. Hard-coded to - ``"qmark"``. - - .. note:: - - The :mod:`!sqlite3` module supports both ``qmark`` and ``numeric`` DB-API - parameter styles, because that is what the underlying SQLite library - supports. However, the DB-API does not allow multiple values for - the ``paramstyle`` attribute. - -.. data:: version - - Version number of this module as a :class:`string `. - This is not the version of the SQLite library. - - -.. data:: version_info - - Version number of this module as a :class:`tuple` of :class:`integers `. - This is not the version of the SQLite library. +.. We use the following practises for SQL code: + - UPPERCASE for keywords + - snake_case for schema + - single quotes for string literals + - singular for table names + - if needed, use double quotes for table and column names +.. _sqlite3-tutorial: -.. data:: sqlite_version +Tutorial +-------- - Version number of the runtime SQLite library as a :class:`string `. +In this tutorial, you will create a database of Monty Python movies +using basic :mod:`!sqlite3` functionality. +It assumes a fundamental understanding of database concepts, +including `cursors`_ and `transactions`_. + +First, we need to create a new database and open +a database connection to allow :mod:`!sqlite3` to work with it. +Call :func:`sqlite3.connect` to to create a connection to +the database :file:`tutorial.db` in the current working directory, +implicitly creating it if it does not exist: +.. testcode:: -.. data:: sqlite_version_info + import sqlite3 + con = sqlite3.connect("tutorial.db") - Version number of the runtime SQLite library as a :class:`tuple` of - :class:`integers `. +The returned :class:`Connection` object ``con`` +represents the connection to the on-disk database. +In order to execute SQL statements and fetch results from SQL queries, +we will need to use a database cursor. +Call :meth:`con.cursor() ` to create the :class:`Cursor`: -.. data:: threadsafety +.. testcode:: - Integer constant required by the DB-API 2.0, stating the level of thread - safety the :mod:`!sqlite3` module supports. This attribute is set based on - the default `threading mode `_ the - underlying SQLite library is compiled with. The SQLite threading modes are: + cur = con.cursor() - 1. **Single-thread**: In this mode, all mutexes are disabled and SQLite is - unsafe to use in more than a single thread at once. - 2. **Multi-thread**: In this mode, SQLite can be safely used by multiple - threads provided that no single database connection is used - simultaneously in two or more threads. - 3. **Serialized**: In serialized mode, SQLite can be safely used by - multiple threads with no restriction. +Now that we've got a database connection and a cursor, +we can create a database table ``movie`` with columns for title, +release year, and review score. +For simplicity, we can just use column names in the table declaration -- +thanks to the `flexible typing`_ feature of SQLite, +specifying the data types is optional. +Execute the ``CREATE TABLE`` statement +by calling :meth:`cur.execute(...) `: + +.. testcode:: + + cur.execute("CREATE TABLE movie(title, year, score)") + +.. Ideally, we'd use sqlite_schema instead of sqlite_master below, + but SQLite versions older than 3.33.0 do not recognise that variant. + +We can verify that the new table has been created by querying +the ``sqlite_master`` table built-in to SQLite, +which should now contain an entry for the ``movie`` table definition +(see `The Schema Table`_ for details). +Execute that query by calling :meth:`cur.execute(...) `, +assign the result to ``res``, +and call :meth:`res.fetchone() ` to fetch the resulting row: + +.. doctest:: + + >>> res = cur.execute("SELECT name FROM sqlite_master") + >>> res.fetchone() + ('movie',) + +We can see that the table has been created, +as the query returns a :class:`tuple` containing the table's name. +If we query ``sqlite_master`` for a non-existent table ``spam``, +:meth:`!res.fetchone()` will return ``None``: + +.. doctest:: + + >>> res = cur.execute("SELECT name FROM sqlite_master WHERE name='spam'") + >>> res.fetchone() is None + True + +Now, add two rows of data supplied as SQL literals +by executing an ``INSERT`` statement, +once again by calling :meth:`cur.execute(...) `: + +.. testcode:: + + cur.execute(""" + INSERT INTO movie VALUES + ('Monty Python and the Holy Grail', 1975, 8.2), + ('And Now for Something Completely Different', 1971, 7.5) + """) + +The ``INSERT`` statement implicitly opens a transaction, +which needs to be committed before changes are saved in the database +(see :ref:`sqlite3-controlling-transactions` for details). +Call :meth:`con.commit() ` on the connection object +to commit the transaction: - The mappings from SQLite threading modes to DB-API 2.0 threadsafety levels - are as follows: +.. testcode:: - +------------------+-----------------+----------------------+-------------------------------+ - | SQLite threading | `threadsafety`_ | `SQLITE_THREADSAFE`_ | DB-API 2.0 meaning | - | mode | | | | - +==================+=================+======================+===============================+ - | single-thread | 0 | 0 | Threads may not share the | - | | | | module | - +------------------+-----------------+----------------------+-------------------------------+ - | multi-thread | 1 | 2 | Threads may share the module, | - | | | | but not connections | - +------------------+-----------------+----------------------+-------------------------------+ - | serialized | 3 | 1 | Threads may share the module, | - | | | | connections and cursors | - +------------------+-----------------+----------------------+-------------------------------+ + con.commit() - .. _threadsafety: https://peps.python.org/pep-0249/#threadsafety - .. _SQLITE_THREADSAFE: https://sqlite.org/compile.html#threadsafe +We can verify that the data was inserted correctly +by executing a ``SELECT`` query. +Use the now-familiar :meth:`cur.execute(...) ` to +assign the result to ``res``, +and call :meth:`res.fetchall() ` to return all resulting rows: + +.. doctest:: + + >>> res = cur.execute("SELECT score FROM movie") + >>> res.fetchall() + [(8.2,), (7.5,)] + +The result is a :class:`list` of two :class:`!tuple`\s, one per row, +each containing that row's ``score`` value. + +Now, insert three more rows by calling +:meth:`cur.executemany(...) `: + +.. testcode:: + + data = [ + ("Monty Python Live at the Hollywood Bowl", 1982, 7.9), + ("Monty Python's The Meaning of Life", 1983, 7.5), + ("Monty Python's Life of Brian", 1979, 8.0), + ] + cur.executemany("INSERT INTO movie VALUES(?, ?, ?)", data) + con.commit() # Remember to commit the transaction after executing INSERT. - .. versionchanged:: 3.11 - Set *threadsafety* dynamically instead of hard-coding it to ``1``. +Notice that ``?`` placeholders are used to bind ``data`` to the query. +Always use placeholders instead of :ref:`string formatting ` +to bind Python values to SQL statements, +to avoid `SQL injection attacks`_ +(see :ref:`sqlite3-placeholders` for more details). -.. data:: PARSE_DECLTYPES +We can verify that the new rows were inserted +by executing a ``SELECT`` query, +this time iterating over the results of the query: - Pass this flag value to the *detect_types* parameter of - :func:`connect` to look up a converter function using - the declared types for each column. - The types are declared when the database table is created. - :mod:`!sqlite3` will look up a converter function using the first word of the - declared type as the converter dictionary key. - For example: +.. doctest:: + >>> for row in cur.execute("SELECT year, title FROM movie ORDER BY year"): + ... print(row) + (1971, 'And Now for Something Completely Different') + (1975, 'Monty Python and the Holy Grail') + (1979, "Monty Python's Life of Brian") + (1982, 'Monty Python Live at the Hollywood Bowl') + (1983, "Monty Python's The Meaning of Life") + +Each row is a two-item :class:`tuple` of ``(year, title)``, +matching the columns selected in the query. + +Finally, verify that the database has been written to disk +by calling :meth:`con.close() ` +to close the existing connection, opening a new one, +creating a new cursor, then querying the database: + +.. doctest:: + + >>> con.close() + >>> new_con = sqlite3.connect("tutorial.db") + >>> new_cur = new_con.cursor() + >>> res = new_cur.execute("SELECT title, year FROM movie ORDER BY score DESC") + >>> title, year = res.fetchone() + >>> print(f'The highest scoring Monty Python movie is {title!r}, released in {year}') + The highest scoring Monty Python movie is 'Monty Python and the Holy Grail', released in 1975 - .. code-block:: sql +You've now created an SQLite database using the :mod:`!sqlite3` module, +inserted data and retrieved values from it in multiple ways. - CREATE TABLE test( - i integer primary key, ! will look up a converter named "integer" - p point, ! will look up a converter named "point" - n number(10) ! will look up a converter named "number" - ) +.. _SQL injection attacks: https://en.wikipedia.org/wiki/SQL_injection +.. _The Schema Table: https://www.sqlite.org/schematab.html +.. _cursors: https://en.wikipedia.org/wiki/Cursor_(databases) +.. _flexible typing: https://www.sqlite.org/flextypegood.html +.. _sqlite_master: https://www.sqlite.org/schematab.html +.. _transactions: https://en.wikipedia.org/wiki/Database_transaction - This flag may be combined with :const:`PARSE_COLNAMES` using the ``|`` - (bitwise or) operator. +.. seealso:: + * :ref:`sqlite3-howtos` for further reading: -.. data:: PARSE_COLNAMES + * :ref:`sqlite3-placeholders` + * :ref:`sqlite3-adapters` + * :ref:`sqlite3-converters` + * :ref:`sqlite3-connection-context-manager` - Pass this flag value to the *detect_types* parameter of - :func:`connect` to look up a converter function by - using the type name, parsed from the query column name, - as the converter dictionary key. - The type name must be wrapped in square brackets (``[]``). + * :ref:`sqlite3-explanation` for in-depth background on transaction control. - .. code-block:: sql +.. _sqlite3-reference: - SELECT p as "p [point]" FROM test; ! will look up converter "point" +Reference +--------- - This flag may be combined with :const:`PARSE_DECLTYPES` using the ``|`` - (bitwise or) operator. +.. We keep the old sqlite3-module-contents ref to prevent breaking links. +.. _sqlite3-module-contents: +.. _sqlite3-module-functions: +Module functions +^^^^^^^^^^^^^^^^ -.. function:: connect(database, timeout=5.0, detect_types=0, isolation_level="DEFERRED", check_same_thread=True, factory=sqlite3.Connection, cached_statements=128, uri=False) +.. function:: connect(database, timeout=5.0, detect_types=0, \ + isolation_level="DEFERRED", check_same_thread=True, \ + factory=sqlite3.Connection, cached_statements=128, \ + uri=False) Open a connection to an SQLite database. @@ -334,6 +335,66 @@ .. versionadded:: 3.10 The ``sqlite3.connect/handle`` auditing event. +.. function:: complete_statement(statement) + + Return ``True`` if the string *statement* appears to contain + one or more complete SQL statements. + No syntactic verification or parsing of any kind is performed, + other than checking that there are no unclosed string literals + and the statement is terminated by a semicolon. + + For example: + + .. doctest:: + + >>> sqlite3.complete_statement("SELECT foo FROM bar;") + True + >>> sqlite3.complete_statement("SELECT foo") + False + + This function may be useful during command-line input + to determine if the entered text seems to form a complete SQL statement, + or if additional input is needed before calling :meth:`~Cursor.execute`. + +.. function:: enable_callback_tracebacks(flag, /) + + Enable or disable callback tracebacks. + By default you will not get any tracebacks in user-defined functions, + aggregates, converters, authorizer callbacks etc. If you want to debug them, + you can call this function with *flag* set to ``True``. Afterwards, you + will get tracebacks from callbacks on :data:`sys.stderr`. Use ``False`` + to disable the feature again. + + Register an :func:`unraisable hook handler ` for an + improved debug experience: + + .. testsetup:: sqlite3.trace + + import sqlite3 + + .. doctest:: sqlite3.trace + + >>> sqlite3.enable_callback_tracebacks(True) + >>> con = sqlite3.connect(":memory:") + >>> def evil_trace(stmt): + ... 5/0 + >>> con.set_trace_callback(evil_trace) + >>> def debug(unraisable): + ... print(f"{unraisable.exc_value!r} in callback {unraisable.object.__name__}") + ... print(f"Error message: {unraisable.err_msg}") + >>> import sys + >>> sys.unraisablehook = debug + >>> cur = con.execute("SELECT 1") + ZeroDivisionError('division by zero') in callback evil_trace + Error message: None + +.. function:: register_adapter(type, adapter, /) + + Register an *adapter* callable to adapt the Python type *type* into an + SQLite type. + The adapter is called with a Python object of type *type* as its sole + argument, and must return a value of a + :ref:`type that SQLite natively understands `. .. function:: register_converter(typename, converter, /) @@ -349,54 +410,132 @@ case-insensitively. -.. function:: register_adapter(type, adapter, /) +.. _sqlite3-module-constants: - Register an *adapter* callable to adapt the Python type *type* into an - SQLite type. - The adapter is called with a Python object of type *type* as its sole - argument, and must return a value of a - :ref:`type that SQLite natively understands `. +Module constants +^^^^^^^^^^^^^^^^ +.. data:: PARSE_COLNAMES -.. function:: complete_statement(statement) + Pass this flag value to the *detect_types* parameter of + :func:`connect` to look up a converter function by + using the type name, parsed from the query column name, + as the converter dictionary key. + The type name must be wrapped in square brackets (``[]``). - Returns ``True`` if the string *statement* contains one or more complete SQL - statements terminated by semicolons. It does not verify that the SQL is - syntactically correct, only that there are no unclosed string literals and the - statement is terminated by a semicolon. + .. code-block:: sql - This can be used to build a shell for SQLite, as in the following example: + SELECT p as "p [point]" FROM test; ! will look up converter "point" + This flag may be combined with :const:`PARSE_DECLTYPES` using the ``|`` + (bitwise or) operator. - .. literalinclude:: ../includes/sqlite3/complete_statement.py +.. data:: PARSE_DECLTYPES + Pass this flag value to the *detect_types* parameter of + :func:`connect` to look up a converter function using + the declared types for each column. + The types are declared when the database table is created. + :mod:`!sqlite3` will look up a converter function using the first word of the + declared type as the converter dictionary key. + For example: -.. function:: enable_callback_tracebacks(flag, /) + .. code-block:: sql - Enable or disable callback tracebacks. - By default you will not get any tracebacks in user-defined functions, - aggregates, converters, authorizer callbacks etc. If you want to debug them, - you can call this function with *flag* set to ``True``. Afterwards, you - will get tracebacks from callbacks on :data:`sys.stderr`. Use ``False`` - to disable the feature again. + CREATE TABLE test( + i integer primary key, ! will look up a converter named "integer" + p point, ! will look up a converter named "point" + n number(10) ! will look up a converter named "number" + ) - Register an :func:`unraisable hook handler ` for an - improved debug experience:: + This flag may be combined with :const:`PARSE_COLNAMES` using the ``|`` + (bitwise or) operator. - >>> import sqlite3 - >>> sqlite3.enable_callback_tracebacks(True) - >>> cx = sqlite3.connect(":memory:") - >>> cx.set_trace_callback(lambda stmt: 5/0) - >>> cx.execute("select 1") - Exception ignored in: at 0x10b4e3ee0> - Traceback (most recent call last): - File "", line 1, in - ZeroDivisionError: division by zero - >>> import sys - >>> sys.unraisablehook = lambda unraisable: print(unraisable) - >>> cx.execute("select 1") - UnraisableHookArgs(exc_type=, exc_value=ZeroDivisionError('division by zero'), exc_traceback=, err_msg=None, object= at 0x10b4e3ee0>) - +.. data:: SQLITE_OK + SQLITE_DENY + SQLITE_IGNORE + + Flags that should be returned by the *authorizer_callback* callable + passed to :meth:`Connection.set_authorizer`, to indicate whether: + + * Access is allowed (:const:`!SQLITE_OK`), + * The SQL statement should be aborted with an error (:const:`!SQLITE_DENY`) + * The column should be treated as a ``NULL`` value (:const:`!SQLITE_IGNORE`) + +.. data:: apilevel + + String constant stating the supported DB-API level. Required by the DB-API. + Hard-coded to ``"2.0"``. + +.. data:: paramstyle + + String constant stating the type of parameter marker formatting expected by + the :mod:`!sqlite3` module. Required by the DB-API. Hard-coded to + ``"qmark"``. + + .. note:: + + The :mod:`!sqlite3` module supports both ``qmark`` and ``numeric`` DB-API + parameter styles, because that is what the underlying SQLite library + supports. However, the DB-API does not allow multiple values for + the ``paramstyle`` attribute. + +.. data:: sqlite_version + + Version number of the runtime SQLite library as a :class:`string `. + +.. data:: sqlite_version_info + + Version number of the runtime SQLite library as a :class:`tuple` of + :class:`integers `. + +.. data:: threadsafety + + Integer constant required by the DB-API 2.0, stating the level of thread + safety the :mod:`!sqlite3` module supports. This attribute is set based on + the default `threading mode `_ the + underlying SQLite library is compiled with. The SQLite threading modes are: + + 1. **Single-thread**: In this mode, all mutexes are disabled and SQLite is + unsafe to use in more than a single thread at once. + 2. **Multi-thread**: In this mode, SQLite can be safely used by multiple + threads provided that no single database connection is used + simultaneously in two or more threads. + 3. **Serialized**: In serialized mode, SQLite can be safely used by + multiple threads with no restriction. + + The mappings from SQLite threading modes to DB-API 2.0 threadsafety levels + are as follows: + + +------------------+-----------------+----------------------+-------------------------------+ + | SQLite threading | `threadsafety`_ | `SQLITE_THREADSAFE`_ | DB-API 2.0 meaning | + | mode | | | | + +==================+=================+======================+===============================+ + | single-thread | 0 | 0 | Threads may not share the | + | | | | module | + +------------------+-----------------+----------------------+-------------------------------+ + | multi-thread | 1 | 2 | Threads may share the module, | + | | | | but not connections | + +------------------+-----------------+----------------------+-------------------------------+ + | serialized | 3 | 1 | Threads may share the module, | + | | | | connections and cursors | + +------------------+-----------------+----------------------+-------------------------------+ + + .. _threadsafety: https://peps.python.org/pep-0249/#threadsafety + .. _SQLITE_THREADSAFE: https://sqlite.org/compile.html#threadsafe + + .. versionchanged:: 3.11 + Set *threadsafety* dynamically instead of hard-coding it to ``1``. + +.. data:: version + + Version number of this module as a :class:`string `. + This is not the version of the SQLite library. + +.. data:: version_info + + Version number of this module as a :class:`tuple` of :class:`integers `. + This is not the version of the SQLite library. .. _sqlite3-connection-objects: @@ -439,7 +578,81 @@ ``True`` if a transaction is active (there are uncommitted changes), ``False`` otherwise. - .. versionadded:: 3.2 + .. versionadded:: 3.2 + + .. attribute:: row_factory + + A callable that accepts two arguments, + a :class:`Cursor` object and the raw row results as a :class:`tuple`, + and returns a custom object representing an SQLite row. + + Example: + + .. doctest:: + + >>> def dict_factory(cursor, row): + ... col_names = [col[0] for col in cursor.description] + ... return {key: value for key, value in zip(col_names, row)} + >>> con = sqlite3.connect(":memory:") + >>> con.row_factory = dict_factory + >>> for row in con.execute("SELECT 1 AS a, 2 AS b"): + ... print(row) + {'a': 1, 'b': 2} + + If returning a tuple doesn't suffice and you want name-based access to + columns, you should consider setting :attr:`row_factory` to the + highly optimized :class:`sqlite3.Row` type. :class:`Row` provides both + index-based and case-insensitive name-based access to columns with almost no + memory overhead. It will probably be better than your own custom + dictionary-based approach or even a db_row based solution. + + .. XXX what's a db_row-based solution? + + .. attribute:: text_factory + + A callable that accepts a :class:`bytes` parameter and returns a text + representation of it. + The callable is invoked for SQLite values with the ``TEXT`` data type. + By default, this attribute is set to :class:`str`. + If you want to return ``bytes`` instead, set *text_factory* to ``bytes``. + + Example: + + .. testcode:: + + con = sqlite3.connect(":memory:") + cur = con.cursor() + + AUSTRIA = "Österreich" + + # by default, rows are returned as str + cur.execute("SELECT ?", (AUSTRIA,)) + row = cur.fetchone() + assert row[0] == AUSTRIA + + # but we can make sqlite3 always return bytestrings ... + con.text_factory = bytes + cur.execute("SELECT ?", (AUSTRIA,)) + row = cur.fetchone() + assert type(row[0]) is bytes + # the bytestrings will be encoded in UTF-8, unless you stored garbage in the + # database ... + assert row[0] == AUSTRIA.encode("utf-8") + + # we can also implement a custom text_factory ... + # here we implement one that appends "foo" to all strings + con.text_factory = lambda x: x.decode("utf-8") + "foo" + cur.execute("SELECT ?", ("bar",)) + row = cur.fetchone() + assert row[0] == "barfoo" + + con.close() + + .. attribute:: total_changes + + Return the total number of database rows that have been modified, inserted, or + deleted since the database connection was opened. + .. method:: cursor(factory=Cursor) @@ -448,7 +661,7 @@ supplied, this must be a callable returning an instance of :class:`Cursor` or its subclasses. - .. method:: blobopen(table, column, row, /, *, readonly=False, name="main") + .. method:: blobopen(table, column, row, /, \*, readonly=False, name="main") Open a :class:`Blob` handle to an existing :abbr:`BLOB (Binary Large OBject)`. @@ -518,7 +731,7 @@ :meth:`~Cursor.executescript` on it with the given *sql_script*. Return the new cursor object. - .. method:: create_function(name, narg, func, *, deterministic=False) + .. method:: create_function(name, narg, func, \*, deterministic=False) Create or remove a user-defined SQL function. @@ -549,7 +762,16 @@ Example: - .. literalinclude:: ../includes/sqlite3/md5func.py + .. doctest:: + + >>> import hashlib + >>> def md5sum(t): + ... return hashlib.md5(t).hexdigest() + >>> con = sqlite3.connect(":memory:") + >>> con.create_function("md5", 1, md5sum) + >>> for row in con.execute("SELECT md5(?)", (b"foo",)): + ... print(row) + ('acbd18db4cc2f85cedef654fccc4a4d8',) .. method:: create_aggregate(name, /, n_arg, aggregate_class) @@ -578,7 +800,32 @@ Example: - .. literalinclude:: ../includes/sqlite3/mysumaggr.py + .. testcode:: + + class MySum: + def __init__(self): + self.count = 0 + + def step(self, value): + self.count += value + + def finalize(self): + return self.count + + con = sqlite3.connect(":memory:") + con.create_aggregate("mysum", 1, MySum) + cur = con.execute("CREATE TABLE test(i)") + cur.execute("INSERT INTO test(i) VALUES(1)") + cur.execute("INSERT INTO test(i) VALUES(2)") + cur.execute("SELECT mysum(i) FROM test") + print(cur.fetchone()[0]) + + con.close() + + .. testoutput:: + :hide: + + 3 .. method:: create_window_function(name, num_params, aggregate_class, /) @@ -616,8 +863,56 @@ Example: - .. literalinclude:: ../includes/sqlite3/sumintwindow.py + .. testcode:: + + # Example taken from https://www.sqlite.org/windowfunctions.html#udfwinfunc + class WindowSumInt: + def __init__(self): + self.count = 0 + + def step(self, value): + """Add a row to the current window.""" + self.count += value + + def value(self): + """Return the current value of the aggregate.""" + return self.count + + def inverse(self, value): + """Remove a row from the current window.""" + self.count -= value + + def finalize(self): + """Return the final value of the aggregate. + + Any clean-up actions should be placed here. + """ + return self.count + + + con = sqlite3.connect(":memory:") + cur = con.execute("CREATE TABLE test(x, y)") + values = [ + ("a", 4), + ("b", 5), + ("c", 3), + ("d", 8), + ("e", 1), + ] + cur.executemany("INSERT INTO test VALUES(?, ?)", values) + con.create_window_function("sumint", 1, WindowSumInt) + cur.execute(""" + SELECT x, sumint(y) OVER ( + ORDER BY x ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING + ) AS sum_y + FROM test ORDER BY x + """) + print(cur.fetchall()) + + .. testoutput:: + :hide: + [('a', 9), ('b', 12), ('c', 16), ('d', 12), ('e', 9)] .. method:: create_collation(name, callable) @@ -631,7 +926,31 @@ The following example shows a reverse sorting collation: - .. literalinclude:: ../includes/sqlite3/collation_reverse.py + .. testcode:: + + def collate_reverse(string1, string2): + if string1 == string2: + return 0 + elif string1 < string2: + return 1 + else: + return -1 + + con = sqlite3.connect(":memory:") + con.create_collation("reverse", collate_reverse) + + cur = con.execute("CREATE TABLE test(x)") + cur.executemany("INSERT INTO test(x) VALUES(?)", [("a",), ("b",)]) + cur.execute("SELECT x FROM test ORDER BY x COLLATE reverse") + for row in cur: + print(row) + con.close() + + .. testoutput:: + :hide: + + ('b',) + ('a',) Remove a collation function by setting *callable* to ``None``. @@ -651,10 +970,9 @@ Register callable *authorizer_callback* to be invoked for each attempt to access a column of a table in the database. The callback should return - :const:`SQLITE_OK` if access is allowed, :const:`SQLITE_DENY` if the entire SQL - statement should be aborted with an error and :const:`SQLITE_IGNORE` if the - column should be treated as a NULL value. These constants are available in the - :mod:`!sqlite3` module. + one of :const:`SQLITE_OK`, :const:`SQLITE_DENY`, or :const:`SQLITE_IGNORE` + to signal how access to the column should be handled + by the underlying SQLite library. The first argument to the callback signifies what kind of operation is to be authorized. The second and third argument will be arguments or ``None`` @@ -698,7 +1016,7 @@ ignored. Note that the backend does not only run statements passed to the :meth:`Cursor.execute` methods. Other sources include the :ref:`transaction management ` of the - sqlite3 module and the execution of triggers defined in the current + :mod:`!sqlite3` module and the execution of triggers defined in the current database. Passing ``None`` as *trace_callback* will disable the trace callback. @@ -737,59 +1055,56 @@ .. versionchanged:: 3.10 Added the ``sqlite3.enable_load_extension`` auditing event. - .. literalinclude:: ../includes/sqlite3/load_extension.py - - .. method:: load_extension(path, /) - - Load an SQLite extension from a shared library located at *path*. - Enable extension loading with :meth:`enable_load_extension` before - calling this method. - - .. audit-event:: sqlite3.load_extension connection,path sqlite3.Connection.load_extension - - .. versionadded:: 3.2 - - .. versionchanged:: 3.10 - Added the ``sqlite3.load_extension`` auditing event. - - .. attribute:: row_factory + .. testsetup:: sqlite3.loadext - A callable that accepts two arguments, - a :class:`Cursor` object and the raw row results as a :class:`tuple`, - and returns a custom object representing an SQLite row. - - Example: + import sqlite3 + con = sqlite3.connect(":memory:") - .. literalinclude:: ../includes/sqlite3/row_factory.py + .. testcode:: sqlite3.loadext + :skipif: True # not testable at the moment - If returning a tuple doesn't suffice and you want name-based access to - columns, you should consider setting :attr:`row_factory` to the - highly optimized :class:`sqlite3.Row` type. :class:`Row` provides both - index-based and case-insensitive name-based access to columns with almost no - memory overhead. It will probably be better than your own custom - dictionary-based approach or even a db_row based solution. + con.enable_load_extension(True) - .. XXX what's a db_row-based solution? + # Load the fulltext search extension + con.execute("select load_extension('./fts3.so')") + # alternatively you can load the extension using an API call: + # con.load_extension("./fts3.so") + + # disable extension loading again + con.enable_load_extension(False) + + # example from SQLite wiki + con.execute("CREATE VIRTUAL TABLE recipe USING fts3(name, ingredients)") + con.executescript(""" + INSERT INTO recipe (name, ingredients) VALUES('broccoli stew', 'broccoli peppers cheese tomatoes'); + INSERT INTO recipe (name, ingredients) VALUES('pumpkin stew', 'pumpkin onions garlic celery'); + INSERT INTO recipe (name, ingredients) VALUES('broccoli pie', 'broccoli cheese onions flour'); + INSERT INTO recipe (name, ingredients) VALUES('pumpkin pie', 'pumpkin sugar flour butter'); + """) + for row in con.execute("SELECT rowid, name, ingredients FROM recipe WHERE name MATCH 'pie'"): + print(row) - .. attribute:: text_factory + con.close() - A callable that accepts a :class:`bytes` parameter and returns a text - representation of it. - The callable is invoked for SQLite values with the ``TEXT`` data type. - By default, this attribute is set to :class:`str`. - If you want to return ``bytes`` instead, set *text_factory* to ``bytes``. + .. testoutput:: sqlite3.loadext + :hide: - Example: + (2, 'broccoli pie', 'broccoli cheese onions flour') + (3, 'pumpkin pie', 'pumpkin sugar flour butter') - .. literalinclude:: ../includes/sqlite3/text_factory.py + .. method:: load_extension(path, /) + Load an SQLite extension from a shared library located at *path*. + Enable extension loading with :meth:`enable_load_extension` before + calling this method. - .. attribute:: total_changes + .. audit-event:: sqlite3.load_extension connection,path sqlite3.Connection.load_extension - Return the total number of database rows that have been modified, inserted, or - deleted since the database connection was opened. + .. versionadded:: 3.2 + .. versionchanged:: 3.10 + Added the ``sqlite3.load_extension`` auditing event. .. method:: iterdump @@ -797,19 +1112,19 @@ Useful when saving an in-memory database for later restoration. Similar to the ``.dump`` command in the :program:`sqlite3` shell. - Example:: + Example: - # Convert file existing_db.db to SQL dump file dump.sql - import sqlite3 + .. testcode:: - con = sqlite3.connect('existing_db.db') + # Convert file example.db to SQL dump file dump.sql + con = sqlite3.connect('example.db') with open('dump.sql', 'w') as f: for line in con.iterdump(): f.write('%s\n' % line) con.close() - .. method:: backup(target, *, pages=-1, progress=None, name="main", sleep=0.250) + .. method:: backup(target, \*, pages=-1, progress=None, name="main", sleep=0.250) Create a backup of an SQLite database. @@ -845,27 +1160,32 @@ The number of seconds to sleep between successive attempts to back up remaining pages. - Example 1, copy an existing database into another:: + Example 1, copy an existing database into another: - import sqlite3 + .. testcode:: def progress(status, remaining, total): print(f'Copied {total-remaining} of {total} pages...') - con = sqlite3.connect('existing_db.db') - bck = sqlite3.connect('backup.db') - with bck: - con.backup(bck, pages=1, progress=progress) - bck.close() - con.close() + src = sqlite3.connect('example.db') + dst = sqlite3.connect('backup.db') + with dst: + src.backup(dst, pages=1, progress=progress) + dst.close() + src.close() - Example 2, copy an existing database into a transient copy:: + .. testoutput:: + :hide: - import sqlite3 + Copied 0 of 0 pages... + + Example 2, copy an existing database into a transient copy: + + .. testcode:: - source = sqlite3.connect('existing_db.db') - dest = sqlite3.connect(':memory:') - source.backup(dest) + src = sqlite3.connect('example.db') + dst = sqlite3.connect(':memory:') + src.backup(dst) .. versionadded:: 3.7 @@ -881,12 +1201,20 @@ :raises ProgrammingError: If *category* is not recognised by the underlying SQLite library. - Example, query the maximum length of an SQL statement:: + Example, query the maximum length of an SQL statement + for :class:`Connection` ``con`` (the default is 1000000000): + + .. testsetup:: sqlite3.limits import sqlite3 con = sqlite3.connect(":memory:") - lim = con.getlimit(sqlite3.SQLITE_LIMIT_SQL_LENGTH) - print(f"SQLITE_LIMIT_SQL_LENGTH={lim}") + con.setlimit(sqlite3.SQLITE_LIMIT_SQL_LENGTH, 1_000_000_000) + con.setlimit(sqlite3.SQLITE_LIMIT_ATTACHED, 10) + + .. doctest:: sqlite3.limits + + >>> con.getlimit(sqlite3.SQLITE_LIMIT_SQL_LENGTH) + 1000000000 .. versionadded:: 3.11 @@ -910,18 +1238,22 @@ :raises ProgrammingError: If *category* is not recognised by the underlying SQLite library. - Example, limit the number of attached databases to 1:: + Example, limit the number of attached databases to 1 + for :class:`Connection` ``con`` (the default limit is 10): - import sqlite3 - con = sqlite3.connect(":memory:") - con.setlimit(sqlite3.SQLITE_LIMIT_ATTACHED, 1) + .. doctest:: sqlite3.limits + + >>> con.setlimit(sqlite3.SQLITE_LIMIT_ATTACHED, 1) + 10 + >>> con.getlimit(sqlite3.SQLITE_LIMIT_ATTACHED) + 1 .. versionadded:: 3.11 .. _SQLite limit category: https://www.sqlite.org/c3ref/c_limit_attached.html - .. method:: serialize(*, name="main") + .. method:: serialize(\*, name="main") Serialize a database into a :class:`bytes` object. For an ordinary on-disk database file, the serialization is just a copy of the @@ -943,7 +1275,7 @@ .. versionadded:: 3.11 - .. method:: deserialize(data, /, *, name="main") + .. method:: deserialize(data, /, \*, name="main") Deserialize a :meth:`serialized ` database into a :class:`Connection`. @@ -990,11 +1322,25 @@ Cursor objects are :term:`iterators `, meaning that if you :meth:`~Cursor.execute` a ``SELECT`` query, - you can simply iterate over the cursor to fetch the resulting rows:: + you can simply iterate over the cursor to fetch the resulting rows: + + .. testsetup:: sqlite3.cursor + + import sqlite3 + con = sqlite3.connect(":memory:", isolation_level=None) + cur = con.execute("CREATE TABLE data(t)") + cur.execute("INSERT INTO data VALUES(1)") - for row in cur.execute("select * from data"): + .. testcode:: sqlite3.cursor + + for row in cur.execute("SELECT t FROM data"): print(row) + .. testoutput:: sqlite3.cursor + :hide: + + (1,) + .. _database cursor: https://en.wikipedia.org/wiki/Cursor_(databases) .. class:: Cursor @@ -1030,14 +1376,16 @@ :term:`iterator` yielding parameters instead of a sequence. Uses the same implicit transaction handling as :meth:`~Cursor.execute`. - Example:: + Example: + + .. testcode:: sqlite3.cursor - data = [ - ("row1",), - ("row2",), - ] - # cur is an sqlite3.Cursor object - cur.executemany("insert into t values(?)", data) + rows = [ + ("row1",), + ("row2",), + ] + # cur is an sqlite3.Cursor object + cur.executemany("INSERT INTO data VALUES(?)", rows) .. method:: executescript(sql_script, /) @@ -1049,15 +1397,17 @@ *sql_script* must be a :class:`string `. - Example:: + Example: + + .. testcode:: sqlite3.cursor # cur is an sqlite3.Cursor object cur.executescript(""" - begin; - create table person(firstname, lastname, age); - create table book(title, author, published); - create table publisher(name, address); - commit; + BEGIN; + CREATE TABLE person(firstname, lastname, age); + CREATE TABLE book(title, author, published); + CREATE TABLE publisher(name, address); + COMMIT; """) @@ -1105,13 +1455,32 @@ Required by the DB-API. Does nothing in :mod:`!sqlite3`. - .. attribute:: rowcount + .. attribute:: arraysize - Read-only attribute that provides the number of modified rows for - ``INSERT``, ``UPDATE``, ``DELETE``, and ``REPLACE`` statements; - is ``-1`` for other statements, - including :abbr:`CTE (Common Table Expression)` queries. - It is only updated by the :meth:`execute` and :meth:`executemany` methods. + Read/write attribute that controls the number of rows returned by :meth:`fetchmany`. + The default value is 1 which means a single row would be fetched per call. + + .. attribute:: connection + + Read-only attribute that provides the SQLite database :class:`Connection` + belonging to the cursor. A :class:`Cursor` object created by + calling :meth:`con.cursor() ` will have a + :attr:`connection` attribute that refers to *con*: + + .. doctest:: + + >>> con = sqlite3.connect(":memory:") + >>> cur = con.cursor() + >>> cur.connection == con + True + + .. attribute:: description + + Read-only attribute that provides the column names of the last query. To + remain compatible with the Python DB API, it returns a 7-tuple for each + column where the last six items of each tuple are ``None``. + + It is set for ``SELECT`` statements without any matching rows as well. .. attribute:: lastrowid @@ -1128,31 +1497,20 @@ .. versionchanged:: 3.6 Added support for the ``REPLACE`` statement. - .. attribute:: arraysize - - Read/write attribute that controls the number of rows returned by :meth:`fetchmany`. - The default value is 1 which means a single row would be fetched per call. - - .. attribute:: description - - Read-only attribute that provides the column names of the last query. To - remain compatible with the Python DB API, it returns a 7-tuple for each - column where the last six items of each tuple are ``None``. - - It is set for ``SELECT`` statements without any matching rows as well. + .. attribute:: rowcount - .. attribute:: connection + Read-only attribute that provides the number of modified rows for + ``INSERT``, ``UPDATE``, ``DELETE``, and ``REPLACE`` statements; + is ``-1`` for other statements, + including :abbr:`CTE (Common Table Expression)` queries. + It is only updated by the :meth:`execute` and :meth:`executemany` methods. - Read-only attribute that provides the SQLite database :class:`Connection` - belonging to the cursor. A :class:`Cursor` object created by - calling :meth:`con.cursor() ` will have a - :attr:`connection` attribute that refers to *con*:: - >>> con = sqlite3.connect(":memory:") - >>> cur = con.cursor() - >>> cur.connection == con - True +.. The sqlite3.Row example used to be a how-to. It has now been incorporated + into the Row reference. We keep the anchor here in order not to break + existing links. +.. _sqlite3-columns-by-name: .. _sqlite3-row-objects: Row objects @@ -1160,10 +1518,9 @@ .. class:: Row - A :class:`Row` instance serves as a highly optimized + A :class:`!Row` instance serves as a highly optimized :attr:`~Connection.row_factory` for :class:`Connection` objects. - It tries to mimic a :class:`tuple` in most of its features, - and supports iteration, :func:`repr`, equality testing, :func:`len`, + It supports iteration, equality testing, :func:`len`, and :term:`mapping` access by column name and index. Two row objects compare equal if have equal columns and equal members. @@ -1177,45 +1534,20 @@ .. versionchanged:: 3.5 Added support of slicing. -Let's assume we initialize a table as in the example given above:: + Example: - con = sqlite3.connect(":memory:") - cur = con.cursor() - cur.execute('''create table stocks - (date text, trans text, symbol text, - qty real, price real)''') - cur.execute("""insert into stocks - values ('2006-01-05','BUY','RHAT',100,35.14)""") - con.commit() - cur.close() + .. doctest:: -Now we plug :class:`Row` in:: - - >>> con.row_factory = sqlite3.Row - >>> cur = con.cursor() - >>> cur.execute('select * from stocks') - - >>> r = cur.fetchone() - >>> type(r) - - >>> tuple(r) - ('2006-01-05', 'BUY', 'RHAT', 100.0, 35.14) - >>> len(r) - 5 - >>> r[2] - 'RHAT' - >>> r.keys() - ['date', 'trans', 'symbol', 'qty', 'price'] - >>> r['qty'] - 100.0 - >>> for member in r: - ... print(member) - ... - 2006-01-05 - BUY - RHAT - 100.0 - 35.14 + >>> con = sqlite3.connect(":memory:") + >>> con.row_factory = sqlite3.Row + >>> res = con.execute("SELECT 'Earth' AS name, 6378 AS radius") + >>> row = res.fetchone() + >>> row.keys() + ['name', 'radius'] + >>> row[0], row["name"] # Access by index and name. + ('Earth', 'Earth') + >>> row["RADIUS"] # Column names are case-insensitive. + 6378 .. _sqlite3-blob-objects: @@ -1235,7 +1567,30 @@ Use the :class:`Blob` as a :term:`context manager` to ensure that the blob handle is closed after use. - .. literalinclude:: ../includes/sqlite3/blob.py + .. testcode:: + + con = sqlite3.connect(":memory:") + con.execute("CREATE TABLE test(blob_col blob)") + con.execute("INSERT INTO test(blob_col) VALUES(zeroblob(13))") + + # Write to our blob, using two write operations: + with con.blobopen("test", "blob_col", 1) as blob: + blob.write(b"hello, ") + blob.write(b"world.") + # Modify the first and last bytes of our blob + blob[0] = ord("H") + blob[-1] = ord("!") + + # Read the contents of our blob + with con.blobopen("test", "blob_col", 1) as blob: + greeting = blob.read() + + print(greeting) # outputs "b'Hello, world!'" + + .. testoutput:: + :hide: + + b'Hello, world!' .. method:: close() @@ -1424,6 +1779,38 @@ Python types via :ref:`converters `. +.. _sqlite3-default-converters: + +Default adapters and converters +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +There are default adapters for the date and datetime types in the datetime +module. They will be sent as ISO dates/ISO timestamps to SQLite. + +The default converters are registered under the name "date" for +:class:`datetime.date` and under the name "timestamp" for +:class:`datetime.datetime`. + +This way, you can use date/timestamps from Python without any additional +fiddling in most cases. The format of the adapters is also compatible with the +experimental SQLite date/time functions. + +The following example demonstrates this. + +.. literalinclude:: ../includes/sqlite3/pysqlite_datetime.py + +If a timestamp stored in SQLite has a fractional part longer than 6 +numbers, its value will be truncated to microsecond precision by the +timestamp converter. + +.. note:: + + The default "timestamp" converter ignores UTC offsets in the database and + always returns a naive :class:`datetime.datetime` object. To preserve UTC + offsets in timestamps, either leave converters disabled, or register an + offset-aware converter with :func:`register_converter`. + + .. _sqlite3-howtos: How-to guides @@ -1431,8 +1818,8 @@ .. _sqlite3-placeholders: -Using placeholders to bind values in SQL queries -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +How to use placeholders to bind values in SQL queries +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SQL operations usually need to use values from Python variables. However, beware of using Python's string operations to assemble queries, as they @@ -1456,13 +1843,36 @@ keys for all named parameters. Any extra items are ignored. Here's an example of both styles: -.. literalinclude:: ../includes/sqlite3/execute_1.py +.. testcode:: + + con = sqlite3.connect(":memory:") + cur = con.execute("CREATE TABLE lang(name, first_appeared)") + + # This is the qmark style: + cur.execute("INSERT INTO lang VALUES(?, ?)", ("C", 1972)) + + # The qmark style used with executemany(): + lang_list = [ + ("Fortran", 1957), + ("Python", 1991), + ("Go", 2009), + ] + cur.executemany("INSERT INTO lang VALUES(?, ?)", lang_list) + + # And this is the named style: + cur.execute("SELECT * FROM lang WHERE first_appeared = :year", {"year": 1972}) + print(cur.fetchall()) + +.. testoutput:: + :hide: + + [('C', 1972)] .. _sqlite3-adapters: -Using adapters to store custom Python types in SQLite databases -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +How to adapt custom Python types to SQLite values +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SQLite supports only a limited set of data types natively. To store custom Python types in SQLite databases, *adapt* them to one of the @@ -1479,10 +1889,10 @@ .. _sqlite3-conform: -Letting your object adapt itself -"""""""""""""""""""""""""""""""" +How to write adaptable objects +"""""""""""""""""""""""""""""" -Suppose we have a ``Point`` class that represents a pair of coordinates, +Suppose we have a :class:`!Point` class that represents a pair of coordinates, ``x`` and ``y``, in a Cartesian coordinate system. The coordinate pair will be stored as a text string in the database, using a semicolon to separate the coordinates. @@ -1490,41 +1900,80 @@ method which returns the adapted value. The object passed to *protocol* will be of type :class:`PrepareProtocol`. -.. literalinclude:: ../includes/sqlite3/adapter_point_1.py +.. testcode:: + + class Point: + def __init__(self, x, y): + self.x, self.y = x, y + + def __conform__(self, protocol): + if protocol is sqlite3.PrepareProtocol: + return f"{self.x};{self.y}" + + con = sqlite3.connect(":memory:") + cur = con.cursor() + + cur.execute("SELECT ?", (Point(4.0, -3.2),)) + print(cur.fetchone()[0]) + +.. testoutput:: + :hide: + 4.0;-3.2 -Registering an adapter callable -""""""""""""""""""""""""""""""" + +How to register adapter callables +""""""""""""""""""""""""""""""""" The other possibility is to create a function that converts the Python object to an SQLite-compatible type. This function can then be registered using :func:`register_adapter`. -.. literalinclude:: ../includes/sqlite3/adapter_point_2.py +.. testcode:: + + class Point: + def __init__(self, x, y): + self.x, self.y = x, y + + def adapt_point(point): + return f"{point.x};{point.y}" + + sqlite3.register_adapter(Point, adapt_point) + + con = sqlite3.connect(":memory:") + cur = con.cursor() + + cur.execute("SELECT ?", (Point(1.0, 2.5),)) + print(cur.fetchone()[0]) + +.. testoutput:: + :hide: + + 1.0;2.5 .. _sqlite3-converters: -Converting SQLite values to custom Python types -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +How to convert SQLite values to custom Python types +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Writing an adapter lets you convert *from* custom Python types *to* SQLite values. To be able to convert *from* SQLite values *to* custom Python types, we use *converters*. -Let's go back to the :class:`Point` class. We stored the x and y coordinates +Let's go back to the :class:`!Point` class. We stored the x and y coordinates separated via semicolons as strings in SQLite. First, we'll define a converter function that accepts the string as a parameter -and constructs a :class:`Point` object from it. +and constructs a :class:`!Point` object from it. .. note:: Converter functions are **always** passed a :class:`bytes` object, no matter the underlying SQLite data type. -:: +.. testcode:: def convert_point(s): x, y = map(float, s.split(b";")) @@ -1542,37 +1991,50 @@ The following example illustrates the implicit and explicit approaches: -.. literalinclude:: ../includes/sqlite3/converter_point.py - - -Default adapters and converters -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. testcode:: -There are default adapters for the date and datetime types in the datetime -module. They will be sent as ISO dates/ISO timestamps to SQLite. + class Point: + def __init__(self, x, y): + self.x, self.y = x, y -The default converters are registered under the name "date" for -:class:`datetime.date` and under the name "timestamp" for -:class:`datetime.datetime`. + def __repr__(self): + return f"Point({self.x}, {self.y})" -This way, you can use date/timestamps from Python without any additional -fiddling in most cases. The format of the adapters is also compatible with the -experimental SQLite date/time functions. + def adapt_point(point): + return f"{point.x};{point.y}".encode("utf-8") -The following example demonstrates this. + def convert_point(s): + x, y = list(map(float, s.split(b";"))) + return Point(x, y) -.. literalinclude:: ../includes/sqlite3/pysqlite_datetime.py + # Register the adapter and converter + sqlite3.register_adapter(Point, adapt_point) + sqlite3.register_converter("point", convert_point) + + # 1) Parse using declared types + p = Point(4.0, -3.2) + con = sqlite3.connect(":memory:", detect_types=sqlite3.PARSE_DECLTYPES) + cur = con.execute("CREATE TABLE test(p point)") + + cur.execute("INSERT INTO test(p) VALUES(?)", (p,)) + cur.execute("SELECT p FROM test") + print("with declared types:", cur.fetchone()[0]) + cur.close() + con.close() -If a timestamp stored in SQLite has a fractional part longer than 6 -numbers, its value will be truncated to microsecond precision by the -timestamp converter. + # 2) Parse using column names + con = sqlite3.connect(":memory:", detect_types=sqlite3.PARSE_COLNAMES) + cur = con.execute("CREATE TABLE test(p)") + + cur.execute("INSERT INTO test(p) VALUES(?)", (p,)) + cur.execute('SELECT p AS "p [point]" FROM test') + print("with column names:", cur.fetchone()[0]) -.. note:: +.. testoutput:: + :hide: - The default "timestamp" converter ignores UTC offsets in the database and - always returns a naive :class:`datetime.datetime` object. To preserve UTC - offsets in timestamps, either leave converters disabled, or register an - offset-aware converter with :func:`register_converter`. + with declared types: Point(4.0, -3.2) + with column names: Point(4.0, -3.2) .. _sqlite3-adapter-converter-recipes: @@ -1582,7 +2044,7 @@ This section shows recipes for common adapters and converters. -.. code-block:: +.. testcode:: import datetime import sqlite3 @@ -1595,7 +2057,7 @@ """Adapt datetime.datetime to timezone-naive ISO 8601 date.""" return val.isoformat() - def adapt_datetime_epoch(val) + def adapt_datetime_epoch(val): """Adapt datetime.datetime to Unix timestamp.""" return int(val.timestamp()) @@ -1622,8 +2084,8 @@ .. _sqlite3-connection-shortcuts: -Using connection shortcut methods -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +How to use connection shortcut methods +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Using the :meth:`~Connection.execute`, :meth:`~Connection.executemany`, and :meth:`~Connection.executescript` @@ -1634,26 +2096,38 @@ objects. This way, you can execute a ``SELECT`` statement and iterate over it directly using only a single call on the :class:`Connection` object. -.. literalinclude:: ../includes/sqlite3/shortcut_methods.py +.. testcode:: + # Create and fill the table. + con = sqlite3.connect(":memory:") + con.execute("CREATE TABLE lang(name, first_appeared)") + data = [ + ("C++", 1985), + ("Objective-C", 1984), + ] + con.executemany("INSERT INTO lang(name, first_appeared) VALUES(?, ?)", data) + + # Print the table contents + for row in con.execute("SELECT name, first_appeared FROM lang"): + print(row) -.. _sqlite3-columns-by-name: - -Accessing columns by name instead of by index -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + print("I just deleted", con.execute("DELETE FROM lang").rowcount, "rows") -One useful feature of the :mod:`!sqlite3` module is the built-in -:class:`sqlite3.Row` class designed to be used as a row factory. + # close() is not a shortcut method and it's not called automatically; + # the connection object should be closed manually + con.close() -Rows wrapped with this class can be accessed both by index (like tuples) and -case-insensitively by name: +.. testoutput:: + :hide: -.. literalinclude:: ../includes/sqlite3/rowclass.py + ('C++', 1985) + ('Objective-C', 1984) + I just deleted 2 rows .. _sqlite3-connection-context-manager: -Using the connection as a context manager +How to use the connection context manager ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ A :class:`Connection` object can be used as a context manager that @@ -1673,33 +2147,72 @@ The context manager neither implicitly opens a new transaction nor closes the connection. -.. literalinclude:: ../includes/sqlite3/ctx_manager.py +.. testcode:: + + con = sqlite3.connect(":memory:") + con.execute("CREATE TABLE lang(id INTEGER PRIMARY KEY, name VARCHAR UNIQUE)") + + # Successful, con.commit() is called automatically afterwards + with con: + con.execute("INSERT INTO lang(name) VALUES(?)", ("Python",)) + + # con.rollback() is called after the with block finishes with an exception, + # the exception is still raised and must be caught + try: + with con: + con.execute("INSERT INTO lang(name) VALUES(?)", ("Python",)) + except sqlite3.IntegrityError: + print("couldn't add Python twice") + + # Connection object used as context manager only commits or rollbacks transactions, + # so the connection object should be closed manually + con.close() + +.. testoutput:: + :hide: + + couldn't add Python twice .. _sqlite3-uri-tricks: -Working with SQLite URIs -^^^^^^^^^^^^^^^^^^^^^^^^ +How to work with SQLite URIs +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Some useful URI tricks include: -* Open a database in read-only mode:: +* Open a database in read-only mode: + +.. doctest:: - con = sqlite3.connect("file:template.db?mode=ro", uri=True) + >>> con = sqlite3.connect("file:tutorial.db?mode=ro", uri=True) + >>> con.execute("CREATE TABLE readonly(data)") + Traceback (most recent call last): + OperationalError: attempt to write a readonly database * Do not implicitly create a new database file if it does not already exist; - will raise :exc:`~sqlite3.OperationalError` if unable to create a new file:: + will raise :exc:`~sqlite3.OperationalError` if unable to create a new file: + +.. doctest:: + + >>> con = sqlite3.connect("file:nosuchdb.db?mode=rw", uri=True) + Traceback (most recent call last): + OperationalError: unable to open database file + + +* Create a shared named in-memory database: - con = sqlite3.connect("file:nosuchdb.db?mode=rw", uri=True) +.. testcode:: -* Create a shared named in-memory database:: + db = "file:mem1?mode=memory&cache=shared" + con1 = sqlite3.connect(db, uri=True) + con2 = sqlite3.connect(db, uri=True) + with con1: + con1.execute("CREATE TABLE shared(data)") + con1.execute("INSERT INTO shared VALUES(28)") + res = con2.execute("SELECT data FROM shared") + assert res.fetchone() == (28,) - con1 = sqlite3.connect("file:mem1?mode=memory&cache=shared", uri=True) - con2 = sqlite3.connect("file:mem1?mode=memory&cache=shared", uri=True) - con1.execute("create table t(t)") - con1.execute("insert into t values(28)") - con1.commit() - rows = con2.execute("select * from t").fetchall() More information about this feature, including a list of parameters, can be found in the `SQLite URI documentation`_. diff -Nru python3.11-3.11.0~rc1/Doc/library/stdtypes.rst python3.11-3.11.0~rc2/Doc/library/stdtypes.rst --- python3.11-3.11.0~rc1/Doc/library/stdtypes.rst 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/library/stdtypes.rst 2022-09-11 19:23:30.000000000 +0000 @@ -2548,9 +2548,10 @@ If you want to make the hex string easier to read, you can specify a single character separator *sep* parameter to include in the output. - By default between each byte. A second optional *bytes_per_sep* - parameter controls the spacing. Positive values calculate the - separator position from the right, negative values from the left. + By default, this separator will be included between each byte. + A second optional *bytes_per_sep* parameter controls the spacing. + Positive values calculate the separator position from the right, + negative values from the left. >>> value = b'\xf0\xf1\xf2' >>> value.hex('-') @@ -5455,6 +5456,165 @@ [] +.. _int_max_str_digits: + +Integer string conversion length limitation +=========================================== + +CPython has a global limit for converting between :class:`int` and :class:`str` +to mitigate denial of service attacks. This limit *only* applies to decimal or +other non-power-of-two number bases. Hexadecimal, octal, and binary conversions +are unlimited. The limit can be configured. + +The :class:`int` type in CPython is an abitrary length number stored in binary +form (commonly known as a "bignum"). There exists no algorithm that can convert +a string to a binary integer or a binary integer to a string in linear time, +*unless* the base is a power of 2. Even the best known algorithms for base 10 +have sub-quadratic complexity. Converting a large value such as ``int('1' * +500_000)`` can take over a second on a fast CPU. + +Limiting conversion size offers a practical way to avoid `CVE-2020-10735 +`_. + +The limit is applied to the number of digit characters in the input or output +string when a non-linear conversion algorithm would be involved. Underscores +and the sign are not counted towards the limit. + +When an operation would exceed the limit, a :exc:`ValueError` is raised: + +.. doctest:: + + >>> import sys + >>> sys.set_int_max_str_digits(4300) # Illustrative, this is the default. + >>> _ = int('2' * 5432) + Traceback (most recent call last): + ... + ValueError: Exceeds the limit (4300) for integer string conversion: value has 5432 digits. + >>> i = int('2' * 4300) + >>> len(str(i)) + 4300 + >>> i_squared = i*i + >>> len(str(i_squared)) + Traceback (most recent call last): + ... + ValueError: Exceeds the limit (4300) for integer string conversion: value has 8599 digits. + >>> len(hex(i_squared)) + 7144 + >>> assert int(hex(i_squared), base=16) == i*i # Hexadecimal is unlimited. + +The default limit is 4300 digits as provided in +:data:`sys.int_info.default_max_str_digits `. +The lowest limit that can be configured is 640 digits as provided in +:data:`sys.int_info.str_digits_check_threshold `. + +Verification: + +.. doctest:: + + >>> import sys + >>> assert sys.int_info.default_max_str_digits == 4300, sys.int_info + >>> assert sys.int_info.str_digits_check_threshold == 640, sys.int_info + >>> msg = int('578966293710682886880994035146873798396722250538762761564' + ... '9252925514383915483333812743580549779436104706260696366600' + ... '571186405732').to_bytes(53, 'big') + ... + +.. versionadded:: 3.11 + +Affected APIs +------------- + +The limitation only applies to potentially slow conversions between :class:`int` +and :class:`str` or :class:`bytes`: + +* ``int(string)`` with default base 10. +* ``int(string, base)`` for all bases that are not a power of 2. +* ``str(integer)``. +* ``repr(integer)`` +* any other string conversion to base 10, for example ``f"{integer}"``, + ``"{}".format(integer)``, or ``b"%d" % integer``. + +The limitations do not apply to functions with a linear algorithm: + +* ``int(string, base)`` with base 2, 4, 8, 16, or 32. +* :func:`int.from_bytes` and :func:`int.to_bytes`. +* :func:`hex`, :func:`oct`, :func:`bin`. +* :ref:`formatspec` for hex, octal, and binary numbers. +* :class:`str` to :class:`float`. +* :class:`str` to :class:`decimal.Decimal`. + +Configuring the limit +--------------------- + +Before Python starts up you can use an environment variable or an interpreter +command line flag to configure the limit: + +* :envvar:`PYTHONINTMAXSTRDIGITS`, e.g. + ``PYTHONINTMAXSTRDIGITS=640 python3`` to set the limit to 640 or + ``PYTHONINTMAXSTRDIGITS=0 python3`` to disable the limitation. +* :option:`-X int_max_str_digits <-X>`, e.g. + ``python3 -X int_max_str_digits=640`` +* :data:`sys.flags.int_max_str_digits` contains the value of + :envvar:`PYTHONINTMAXSTRDIGITS` or :option:`-X int_max_str_digits <-X>`. + If both the env var and the ``-X`` option are set, the ``-X`` option takes + precedence. A value of *-1* indicates that both were unset, thus a value of + :data:`sys.int_info.default_max_str_digits` was used during initilization. + +From code, you can inspect the current limit and set a new one using these +:mod:`sys` APIs: + +* :func:`sys.get_int_max_str_digits` and :func:`sys.set_int_max_str_digits` are + a getter and setter for the interpreter-wide limit. Subinterpreters have + their own limit. + +Information about the default and minimum can be found in :attr:`sys.int_info`: + +* :data:`sys.int_info.default_max_str_digits ` is the compiled-in + default limit. +* :data:`sys.int_info.str_digits_check_threshold ` is the lowest + accepted value for the limit (other than 0 which disables it). + +.. versionadded:: 3.11 + +.. caution:: + + Setting a low limit *can* lead to problems. While rare, code exists that + contains integer constants in decimal in their source that exceed the + minimum threshold. A consequence of setting the limit is that Python source + code containing decimal integer literals longer than the limit will + encounter an error during parsing, usually at startup time or import time or + even at installation time - anytime an up to date ``.pyc`` does not already + exist for the code. A workaround for source that contains such large + constants is to convert them to ``0x`` hexadecimal form as it has no limit. + + Test your application thoroughly if you use a low limit. Ensure your tests + run with the limit set early via the environment or flag so that it applies + during startup and even during any installation step that may invoke Python + to precompile ``.py`` sources to ``.pyc`` files. + +Recommended configuration +------------------------- + +The default :data:`sys.int_info.default_max_str_digits` is expected to be +reasonable for most applications. If your application requires a different +limit, set it from your main entry point using Python version agnostic code as +these APIs were added in security patch releases in versions before 3.11. + +Example:: + + >>> import sys + >>> if hasattr(sys, "set_int_max_str_digits"): + ... upper_bound = 68000 + ... lower_bound = 4004 + ... current_limit = sys.get_int_max_str_digits() + ... if current_limit == 0 or current_limit > upper_bound: + ... sys.set_int_max_str_digits(upper_bound) + ... elif current_limit < lower_bound: + ... sys.set_int_max_str_digits(lower_bound) + +If you need to disable it entirely, set it to ``0``. + + .. rubric:: Footnotes .. [1] Additional information on these special methods may be found in the Python diff -Nru python3.11-3.11.0~rc1/Doc/library/sys.rst python3.11-3.11.0~rc2/Doc/library/sys.rst --- python3.11-3.11.0~rc1/Doc/library/sys.rst 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/library/sys.rst 2022-09-11 19:23:30.000000000 +0000 @@ -502,9 +502,9 @@ The :term:`named tuple` *flags* exposes the status of command line flags. The attributes are read only. - ============================= ================================================================ + ============================= ============================================================================================================== attribute flag - ============================= ================================================================ + ============================= ============================================================================================================== :const:`debug` :option:`-d` :const:`inspect` :option:`-i` :const:`interactive` :option:`-i` @@ -521,7 +521,8 @@ :const:`dev_mode` :option:`-X dev <-X>` (:ref:`Python Development Mode `) :const:`utf8_mode` :option:`-X utf8 <-X>` :const:`safe_path` :option:`-P` - ============================= ================================================================ + :const:`int_max_str_digits` :option:`-X int_max_str_digits <-X>` (:ref:`integer string conversion length limitation `) + ============================= ============================================================================================================== .. versionchanged:: 3.2 Added ``quiet`` attribute for the new :option:`-q` flag. @@ -543,6 +544,9 @@ .. versionchanged:: 3.11 Added the ``safe_path`` attribute for :option:`-P` option. + .. versionchanged:: 3.11 + Added the ``int_max_str_digits`` attribute. + .. data:: float_info @@ -723,6 +727,13 @@ .. versionadded:: 3.6 +.. function:: get_int_max_str_digits() + + Returns the current value for the :ref:`integer string conversion length + limitation `. See also :func:`set_int_max_str_digits`. + + .. versionadded:: 3.11 + .. function:: getrefcount(object) Return the reference count of the *object*. The count returned is generally one @@ -996,19 +1007,31 @@ .. tabularcolumns:: |l|L| - +-------------------------+----------------------------------------------+ - | Attribute | Explanation | - +=========================+==============================================+ - | :const:`bits_per_digit` | number of bits held in each digit. Python | - | | integers are stored internally in base | - | | ``2**int_info.bits_per_digit`` | - +-------------------------+----------------------------------------------+ - | :const:`sizeof_digit` | size in bytes of the C type used to | - | | represent a digit | - +-------------------------+----------------------------------------------+ + +----------------------------------------+-----------------------------------------------+ + | Attribute | Explanation | + +========================================+===============================================+ + | :const:`bits_per_digit` | number of bits held in each digit. Python | + | | integers are stored internally in base | + | | ``2**int_info.bits_per_digit`` | + +----------------------------------------+-----------------------------------------------+ + | :const:`sizeof_digit` | size in bytes of the C type used to | + | | represent a digit | + +----------------------------------------+-----------------------------------------------+ + | :const:`default_max_str_digits` | default value for | + | | :func:`sys.get_int_max_str_digits` when it | + | | is not otherwise explicitly configured. | + +----------------------------------------+-----------------------------------------------+ + | :const:`str_digits_check_threshold` | minimum non-zero value for | + | | :func:`sys.set_int_max_str_digits`, | + | | :envvar:`PYTHONINTMAXSTRDIGITS`, or | + | | :option:`-X int_max_str_digits <-X>`. | + +----------------------------------------+-----------------------------------------------+ .. versionadded:: 3.1 + .. versionchanged:: 3.11 + Added ``default_max_str_digits`` and ``str_digits_check_threshold``. + .. data:: __interactivehook__ @@ -1308,6 +1331,14 @@ .. availability:: Unix. +.. function:: set_int_max_str_digits(n) + + Set the :ref:`integer string conversion length limitation + ` used by this interpreter. See also + :func:`get_int_max_str_digits`. + + .. versionadded:: 3.11 + .. function:: setprofile(profilefunc) .. index:: diff -Nru python3.11-3.11.0~rc1/Doc/library/test.rst python3.11-3.11.0~rc2/Doc/library/test.rst --- python3.11-3.11.0~rc1/Doc/library/test.rst 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/library/test.rst 2022-09-11 19:23:30.000000000 +0000 @@ -951,6 +951,16 @@ .. versionadded:: 3.10 +.. function:: adjust_int_max_str_digits(max_digits) + + This function returns a context manager that will change the global + :func:`sys.set_int_max_str_digits` setting for the duration of the + context to allow execution of test code that needs a different limit + on the number of digits when converting between an integer and string. + + .. versionadded:: 3.11 + + The :mod:`test.support` module defines the following classes: diff -Nru python3.11-3.11.0~rc1/Doc/library/threading.rst python3.11-3.11.0~rc2/Doc/library/threading.rst --- python3.11-3.11.0~rc1/Doc/library/threading.rst 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/library/threading.rst 2022-09-11 19:23:30.000000000 +0000 @@ -9,11 +9,23 @@ -------------- This module constructs higher-level threading interfaces on top of the lower -level :mod:`_thread` module. See also the :mod:`queue` module. +level :mod:`_thread` module. .. versionchanged:: 3.7 This module used to be optional, it is now always available. +.. seealso:: + + :class:`concurrent.futures.ThreadPoolExecutor` offers a higher level interface + to push tasks to a background thread without blocking execution of the + calling thread, while still being able to retrieve their results when needed. + + :mod:`queue` provides a thread-safe interface for exchanging data between + running threads. + + :mod:`asyncio` offers an alternative approach to achieving task level + concurrency without requiring the use of multiple operating system threads. + .. note:: In the Python 2.x series, this module contained ``camelCase`` names diff -Nru python3.11-3.11.0~rc1/Doc/library/traceback.rst python3.11-3.11.0~rc2/Doc/library/traceback.rst --- python3.11-3.11.0~rc1/Doc/library/traceback.rst 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/library/traceback.rst 2022-09-11 19:23:30.000000000 +0000 @@ -425,9 +425,9 @@ import sys, traceback def lumberjack(): - bright_side_of_death() + bright_side_of_life() - def bright_side_of_death(): + def bright_side_of_life(): return tuple()[0] try: @@ -437,9 +437,7 @@ print("*** print_tb:") traceback.print_tb(exc_traceback, limit=1, file=sys.stdout) print("*** print_exception:") - # exc_type below is ignored on 3.5 and later - traceback.print_exception(exc_type, exc_value, exc_traceback, - limit=2, file=sys.stdout) + traceback.print_exception(exc_value, limit=2, file=sys.stdout) print("*** print_exc:") traceback.print_exc(limit=2, file=sys.stdout) print("*** format_exc, first and last line:") @@ -447,9 +445,7 @@ print(formatted_lines[0]) print(formatted_lines[-1]) print("*** format_exception:") - # exc_type below is ignored on 3.5 and later - print(repr(traceback.format_exception(exc_type, exc_value, - exc_traceback))) + print(repr(traceback.format_exception(exc_value))) print("*** extract_tb:") print(repr(traceback.extract_tb(exc_traceback))) print("*** format_tb:") @@ -469,14 +465,14 @@ File "", line 10, in lumberjack() File "", line 4, in lumberjack - bright_side_of_death() + bright_side_of_life() IndexError: tuple index out of range *** print_exc: Traceback (most recent call last): File "", line 10, in lumberjack() File "", line 4, in lumberjack - bright_side_of_death() + bright_side_of_life() IndexError: tuple index out of range *** format_exc, first and last line: Traceback (most recent call last): @@ -484,17 +480,17 @@ *** format_exception: ['Traceback (most recent call last):\n', ' File "", line 10, in \n lumberjack()\n', - ' File "", line 4, in lumberjack\n bright_side_of_death()\n', - ' File "", line 7, in bright_side_of_death\n return tuple()[0]\n ~~~~~~~^^^\n', + ' File "", line 4, in lumberjack\n bright_side_of_life()\n', + ' File "", line 7, in bright_side_of_life\n return tuple()[0]\n ~~~~~~~^^^\n', 'IndexError: tuple index out of range\n'] *** extract_tb: [, line 10 in >, , line 4 in lumberjack>, - , line 7 in bright_side_of_death>] + , line 7 in bright_side_of_life>] *** format_tb: [' File "", line 10, in \n lumberjack()\n', - ' File "", line 4, in lumberjack\n bright_side_of_death()\n', - ' File "", line 7, in bright_side_of_death\n return tuple()[0]\n ~~~~~~~^^^\n'] + ' File "", line 4, in lumberjack\n bright_side_of_life()\n', + ' File "", line 7, in bright_side_of_life\n return tuple()[0]\n ~~~~~~~^^^\n'] *** tb_lineno: 10 diff -Nru python3.11-3.11.0~rc1/Doc/library/typing.rst python3.11-3.11.0~rc2/Doc/library/typing.rst --- python3.11-3.11.0~rc1/Doc/library/typing.rst 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/library/typing.rst 2022-09-11 19:23:30.000000000 +0000 @@ -1305,20 +1305,25 @@ T = TypeVar('T') Ts = TypeVarTuple('Ts') - def remove_first_element(tup: tuple[T, *Ts]) -> tuple[*Ts]: - return tup[1:] + def move_first_element_to_last(tup: tuple[T, *Ts]) -> tuple[*Ts, T]: + return (*tup[1:], tup[0]) # T is bound to int, Ts is bound to () - # Return value is (), which has type tuple[()] - remove_first_element(tup=(1,)) + # Return value is (1,), which has type tuple[int] + move_first_element_to_last(tup=(1,)) # T is bound to int, Ts is bound to (str,) - # Return value is ('spam',), which has type tuple[str] - remove_first_element(tup=(1, 'spam')) + # Return value is ('spam', 1), which has type tuple[str, int] + move_first_element_to_last(tup=(1, 'spam')) # T is bound to int, Ts is bound to (str, float) - # Return value is ('spam', 3.0), which has type tuple[str, float] - remove_first_element(tup=(1, 'spam', 3.0)) + # Return value is ('spam', 3.0, 1), which has type tuple[str, float, int] + move_first_element_to_last(tup=(1, 'spam', 3.0)) + + # This fails to type check (and fails at runtime) + # because tuple[()] is not compatible with tuple[T, *Ts] + # (at least one element is required) + move_first_element_to_last(tup=()) Note the use of the unpacking operator ``*`` in ``tuple[T, *Ts]``. Conceptually, you can think of ``Ts`` as a tuple of type variables @@ -1825,6 +1830,9 @@ True .. attribute:: __required_keys__ + + .. versionadded:: 3.9 + .. attribute:: __optional_keys__ ``Point2D.__required_keys__`` and ``Point2D.__optional_keys__`` return @@ -1852,6 +1860,8 @@ >>> Point3D.__optional_keys__ == frozenset({'x', 'y'}) True + .. versionadded:: 3.9 + See :pep:`589` for more examples and detailed rules of using ``TypedDict``. .. versionadded:: 3.8 diff -Nru python3.11-3.11.0~rc1/Doc/reference/compound_stmts.rst python3.11-3.11.0~rc2/Doc/reference/compound_stmts.rst --- python3.11-3.11.0~rc1/Doc/reference/compound_stmts.rst 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/reference/compound_stmts.rst 2022-09-11 19:23:30.000000000 +0000 @@ -1122,7 +1122,7 @@ These classes accept a single positional argument, and the pattern there is matched against the whole object rather than an attribute. For example ``int(0|1)`` matches - the value ``0``, but not the values ``0.0`` or ``False``. + the value ``0``, but not the value ``0.0``. In simple terms ``CLS(P1, attr=P2)`` matches only if the following happens: diff -Nru python3.11-3.11.0~rc1/Doc/reference/lexical_analysis.rst python3.11-3.11.0~rc2/Doc/reference/lexical_analysis.rst --- python3.11-3.11.0~rc1/Doc/reference/lexical_analysis.rst 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/reference/lexical_analysis.rst 2022-09-11 19:23:30.000000000 +0000 @@ -548,7 +548,7 @@ +-----------------+---------------------------------+-------+ | Escape Sequence | Meaning | Notes | +=================+=================================+=======+ -| ``\newline`` | Backslash and newline ignored | | +| ``\``\ | Backslash and newline ignored | \(1) | +-----------------+---------------------------------+-------+ | ``\\`` | Backslash (``\``) | | +-----------------+---------------------------------+-------+ @@ -570,10 +570,10 @@ +-----------------+---------------------------------+-------+ | ``\v`` | ASCII Vertical Tab (VT) | | +-----------------+---------------------------------+-------+ -| ``\ooo`` | Character with octal value | (1,3) | +| ``\ooo`` | Character with octal value | (2,4) | | | *ooo* | | +-----------------+---------------------------------+-------+ -| ``\xhh`` | Character with hex value *hh* | (2,3) | +| ``\xhh`` | Character with hex value *hh* | (3,4) | +-----------------+---------------------------------+-------+ Escape sequences only recognized in string literals are: @@ -581,19 +581,30 @@ +-----------------+---------------------------------+-------+ | Escape Sequence | Meaning | Notes | +=================+=================================+=======+ -| ``\N{name}`` | Character named *name* in the | \(4) | +| ``\N{name}`` | Character named *name* in the | \(5) | | | Unicode database | | +-----------------+---------------------------------+-------+ -| ``\uxxxx`` | Character with 16-bit hex value | \(5) | +| ``\uxxxx`` | Character with 16-bit hex value | \(6) | | | *xxxx* | | +-----------------+---------------------------------+-------+ -| ``\Uxxxxxxxx`` | Character with 32-bit hex value | \(6) | +| ``\Uxxxxxxxx`` | Character with 32-bit hex value | \(7) | | | *xxxxxxxx* | | +-----------------+---------------------------------+-------+ Notes: (1) + A backslash can be added at the end of a line to ignore the newline:: + + >>> 'This string will not include \ + ... backslashes or newline characters.' + 'This string will not include backslashes or newline characters.' + + The same result can be achieved using :ref:`triple-quoted strings `, + or parentheses and :ref:`string literal concatenation `. + + +(2) As in Standard C, up to three octal digits are accepted. .. versionchanged:: 3.11 @@ -601,22 +612,22 @@ In a future Python version they will be a :exc:`SyntaxWarning` and eventually a :exc:`SyntaxError`. -(2) +(3) Unlike in Standard C, exactly two hex digits are required. -(3) +(4) In a bytes literal, hexadecimal and octal escapes denote the byte with the given value. In a string literal, these escapes denote a Unicode character with the given value. -(4) +(5) .. versionchanged:: 3.3 Support for name aliases [#]_ has been added. -(5) +(6) Exactly four hex digits are required. -(6) +(7) Any Unicode character can be encoded this way. Exactly eight hex digits are required. diff -Nru python3.11-3.11.0~rc1/Doc/tutorial/datastructures.rst python3.11-3.11.0~rc2/Doc/tutorial/datastructures.rst --- python3.11-3.11.0~rc1/Doc/tutorial/datastructures.rst 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/tutorial/datastructures.rst 2022-09-11 19:23:30.000000000 +0000 @@ -106,7 +106,7 @@ 0 >>> fruits.index('banana') 3 - >>> fruits.index('banana', 4) # Find next banana starting a position 4 + >>> fruits.index('banana', 4) # Find next banana starting at position 4 6 >>> fruits.reverse() >>> fruits diff -Nru python3.11-3.11.0~rc1/Doc/tutorial/whatnow.rst python3.11-3.11.0~rc2/Doc/tutorial/whatnow.rst --- python3.11-3.11.0~rc1/Doc/tutorial/whatnow.rst 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/tutorial/whatnow.rst 2022-09-11 19:23:30.000000000 +0000 @@ -17,7 +17,7 @@ reference material about types, functions, and the modules in the standard library. The standard Python distribution includes a *lot* of additional code. There are modules to read Unix mailboxes, retrieve documents via HTTP, generate - random numbers, parse command-line options, write CGI programs, compress data, + random numbers, parse command-line options, compress data, and many other tasks. Skimming through the Library Reference will give you an idea of what's available. diff -Nru python3.11-3.11.0~rc1/Doc/using/cmdline.rst python3.11-3.11.0~rc2/Doc/using/cmdline.rst --- python3.11-3.11.0~rc1/Doc/using/cmdline.rst 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/using/cmdline.rst 2022-09-11 19:23:30.000000000 +0000 @@ -502,6 +502,9 @@ stored in a traceback of a trace. Use ``-X tracemalloc=NFRAME`` to start tracing with a traceback limit of *NFRAME* frames. See the :func:`tracemalloc.start` for more information. + * ``-X int_max_str_digits`` configures the :ref:`integer string conversion + length limitation `. See also + :envvar:`PYTHONINTMAXSTRDIGITS`. * ``-X importtime`` to show how long each import takes. It shows module name, cumulative time (including nested imports) and self time (excluding nested imports). Note that its output may be broken in multi-threaded @@ -573,6 +576,8 @@ .. versionadded:: 3.11 The ``-X frozen_modules`` option. + .. versionadded:: 3.11 + The ``-X int_max_str_digits`` option. Options you shouldn't use @@ -749,6 +754,13 @@ .. versionadded:: 3.2.3 +.. envvar:: PYTHONINTMAXSTRDIGITS + + If this variable is set to an integer, it is used to configure the + interpreter's global :ref:`integer string conversion length limitation + `. + + .. versionadded:: 3.11 .. envvar:: PYTHONIOENCODING diff -Nru python3.11-3.11.0~rc1/Doc/using/mac.rst python3.11-3.11.0~rc2/Doc/using/mac.rst --- python3.11-3.11.0~rc1/Doc/using/mac.rst 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/using/mac.rst 2022-09-11 19:23:30.000000000 +0000 @@ -17,15 +17,16 @@ Getting and Installing MacPython ================================ -macOS since version 10.8 comes with Python 2.7 pre-installed by Apple. If you wish, you -are invited to install the most recent version of Python 3 from the Python +macOS used to come with Python 2.7 pre-installed between versions +10.8 and `12.3 `_. +You are invited to install the most recent version of Python 3 from the Python website (https://www.python.org). A current "universal binary" build of Python, which runs natively on the Mac's new Intel and legacy PPC CPU's, is available there. What you get after installing is a number of things: -* A :file:`Python 3.9` folder in your :file:`Applications` folder. In here +* A :file:`Python 3.12` folder in your :file:`Applications` folder. In here you find IDLE, the development environment that is a standard part of official Python distributions; and PythonLauncher, which handles double-clicking Python scripts from the Finder. diff -Nru python3.11-3.11.0~rc1/Doc/whatsnew/2.5.rst python3.11-3.11.0~rc2/Doc/whatsnew/2.5.rst --- python3.11-3.11.0~rc1/Doc/whatsnew/2.5.rst 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/whatsnew/2.5.rst 2022-09-11 19:23:30.000000000 +0000 @@ -2019,7 +2019,7 @@ .. seealso:: - http://www.pysqlite.org + https://www.pysqlite.org The pysqlite web page. https://www.sqlite.org diff -Nru python3.11-3.11.0~rc1/Doc/whatsnew/3.11.rst python3.11-3.11.0~rc2/Doc/whatsnew/3.11.rst --- python3.11-3.11.0~rc1/Doc/whatsnew/3.11.rst 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Doc/whatsnew/3.11.rst 2022-09-11 19:23:30.000000000 +0000 @@ -49,13 +49,14 @@ For full details, see the :ref:`changelog `. + Summary -- Release highlights ============================= .. This section singles out the most important changes in Python 3.11. Brevity is key. -- Python 3.11 is up to 10-60% faster than Python 3.10. On average, we measured a +- Python 3.11 is between 10-60% faster than Python 3.10. On average, we measured a 1.25x speedup on the standard benchmark suite. See `Faster CPython`_ for details. .. PEP-sized items next. @@ -63,7 +64,22 @@ New syntax features: * :pep:`654`: Exception Groups and ``except*``. - (Contributed by Irit Katriel in :issue:`45292`.) + +New built-in features: + +* :pep:`678`: Enriching Exceptions with Notes. + +New standard library modules: + +* :pep:`680`: ``tomllib`` — Support for Parsing TOML in the Standard Library. + +Interpreter improvements: + +* :pep:`657`: Include Fine Grained Error Locations in Tracebacks. +* New :option:`-P` command line option and :envvar:`PYTHONSAFEPATH` environment + variable to disable automatically prepending a potentially unsafe path + (the working dir or script directory, depending on invocation) + to :data:`sys.path`. New typing features: @@ -71,12 +87,13 @@ * :pep:`655`: Marking individual TypedDict items as required or potentially missing. * :pep:`673`: ``Self`` type. * :pep:`675`: Arbitrary literal string type. +* :pep:`681`: Data Class Transforms. -Security improvements: +Important deprecations, removals or restrictions: -* New :option:`-P` command line option and :envvar:`PYTHONSAFEPATH` environment - variable to not prepend a potentially unsafe path to :data:`sys.path` such as - the current directory, the script's directory or an empty string. +* :pep:`594`: Removing dead batteries from the standard library. +* :pep:`624`: Remove ``Py_UNICODE`` encoder APIs. +* :pep:`670`: Convert macros to functions in the Python C API. New Features @@ -158,8 +175,25 @@ See :pep:`657` for more details. (Contributed by Pablo Galindo, Batuhan Taskaya and Ammar Askar in :issue:`43950`.) -Exceptions can be enriched with notes (PEP 678) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +PEP 654: Exception Groups and ``except*`` +----------------------------------------- + +:pep:`654` introduces language features that enable a program +to raise and handle multiple unrelated exceptions simultaneously. +The builtin types :exc:`ExceptionGroup` and :exc:`BaseExceptionGroup` +make it possible to group exceptions and raise them together, +and the new :keyword:`except* ` syntax generalizes +:keyword:`except` to match subgroups of exception groups. + +See :pep:`654` for more details. + +(Contributed by Irit Katriel in :issue:`45292`. PEP written by +Irit Katriel, Yury Selivanov and Guido van Rossum.) + + +PEP 678: Exceptions can be enriched with notes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The :meth:`add_note` method was added to :exc:`BaseException`. It can be used to enrich exceptions with context information which is not available @@ -335,6 +369,28 @@ that was planned for this release has been indefinitely postponed. See `this message `_ for more information. +Windows py.exe launcher improvements +------------------------------------ + +The copy of :ref:`launcher` included with Python 3.11 has been significantly +updated. It now supports company/tag syntax as defined in :pep:`514` using the +``-V:/`` argument instead of the limited ``-x.y`` argument. This +allows launching distributions other than ``PythonCore``, which is the one +obtained from `python.org `_. + +When using ``-V:`` selectors, either company or tag can be omitted, but all +installs will be searched. For example, ``-V:OtherPython/`` will select the +"best" tag registered for ``OtherPython``, while ``-V:3.11`` or ``-V:/3.11`` +will select the "best" distribution with tag ``3.11``. + +When using legacy ``-x``, ``-x.y``, ``-x-ZZ`` or ``-x.y-ZZ`` arguments, all +existing behaviour should be preserved from past versions. Only releases from +``PythonCore`` will be selected. However, the ``-64`` suffix now implies "not +32-bit", as there are multiple supported 64-bit platforms. 32-bit runtimes are +detected by checking its tag for a ``-32`` suffix. All releases of Python +since 3.5 have included this in their 32-bit builds. + + Other Language Changes ====================== @@ -417,6 +473,23 @@ :data:`sys.path`. Otherwise, initialization will recalculate the path and replace any values added to ``module_search_paths``. +* The output of the :option:`--help` option is changed to fit inside 50 lines and 80 + columns. Information about :ref:`Python environment variables ` + and :option:`-X options <-X>` is available with the new :option:`--help-env` or + :option:`--help-xoptions` flags, and with :option:`--help-all`. + (Contributed by Éric Araujo in :issue:`46142`.) + +* Converting between :class:`int` and :class:`str` in bases other than 2 + (binary), 4, 8 (octal), 16 (hexadecimal), or 32 such as base 10 (decimal) + now raises a :exc:`ValueError` if the number of digits in string form is + above a limit to avoid potential denial of service attacks due to the + algorithmic complexity. This is a mitigation for `CVE-2020-10735 + `_. + This limit can be configured or disabled by environment variable, command + line flag, or :mod:`sys` APIs. See the :ref:`integer string conversion + length limitation ` documentation. The default limit + is 4300 digits in string form. + New Modules =========== @@ -454,6 +527,13 @@ holding a group of tasks that will wait for all of them upon exit. (Contributed by Yury Seliganov and others.) +contextlib +---------- + +Added non parallel-safe :func:`~contextlib.chdir` context manager to change +the current working directory and then restore it on exit. Simple wrapper +around :func:`~os.chdir`. (Contributed by Filipe Laíns in :issue:`25625`) + datetime -------- @@ -561,6 +641,10 @@ OpenSSL support. (Contributed by Christian Heimes in :issue:`47098`.) +* Add :func:`hashlib.file_digest`, a helper function for efficient hashing + of files or file-like objects. + (Contributed by Christian Heimes in :gh:`89313`.) + IDLE and idlelib ---------------- @@ -774,6 +858,20 @@ it had a resolution of 1 millisecond (10\ :sup:`-3` seconds). (Contributed by Benjamin Szőke, Dong-hee Na, Eryk Sun and Victor Stinner in :issue:`21302` and :issue:`45429`.) + +traceback +--------- + +* Add :func:`traceback.StackSummary.format_frame_summary` to allow users + to override which frames appear in the traceback, and how they are + formatted. + (Contributed by Ammar Askar in :issue:`44569`.) + +* Add :func:`traceback.TracebackException.print`, which prints the + formatted :exc:`~traceback.TracebackException` instance to a file. + (Contributed by Irit Katriel in :issue:`33809`.) + + typing ------ @@ -1208,6 +1306,8 @@ * :opcode:`JUMP_IF_TRUE_OR_POP` and :opcode:`JUMP_IF_FALSE_OR_POP` are now relative rather than absolute. +* :opcode:`RESUME` has been added. It is a no-op. Performs internal tracing, + debugging and optimization checks. Deprecated ========== @@ -1549,11 +1649,16 @@ :func:`compile` and other related functions. If invalid positions are detected, a :exc:`ValueError` will be raised. (Contributed by Pablo Galindo in :gh:`93351`) +* :c:member:`~PyTypeObject.tp_dictoffset` should be treated as write-only. + It can be set to describe C extension clases to the VM, but should be regarded + as meaningless when read. To get the pointer to the object's dictionary call + :c:func:`PyObject_GenericGetDict` instead. Build Changes ============= -* Building Python now requires a C11 compiler without optional C11 features. +* Building Python now requires a C11 compiler. Optional C11 features are not + required. (Contributed by Victor Stinner in :issue:`46656`.) * Building Python now requires support of IEEE 754 floating point numbers. @@ -1591,16 +1696,23 @@ * Build dependencies, compiler flags, and linker flags for most stdlib extension modules are now detected by :program:`configure`. libffi, libnsl, - libsqlite3, zlib, bzip2, liblzma, libcrypt, Tcl/Tk libs, and uuid flags - are detected by ``pkg-config`` (when available). + libsqlite3, zlib, bzip2, liblzma, libcrypt, Tcl/Tk, and uuid flags + are detected by ``pkg-config`` (when available). :mod:`tkinter` now + requires ``pkg-config`` command to detect development settings for Tcl/Tk + headers and libraries. (Contributed by Christian Heimes and Erlend Egeberg Aasland in :issue:`45847`, :issue:`45747`, and :issue:`45763`.) .. note:: - Use the environment variables ``TCLTK_CFLAGS`` and ``TCLTK_LIBS`` to - manually specify the location of Tcl/Tk headers and libraries. - The :program:`configure` options ``--with-tcltk-includes`` and - ``--with-tcltk-libs`` have been removed. + Use the environment variables :envvar:`TCLTK_CFLAGS` and + :envvar:`TCLTK_LIBS` to manually specify the location of Tcl/Tk headers + and libraries. The :program:`configure` options ``--with-tcltk-includes`` + and ``--with-tcltk-libs`` have been removed. + + On RHEL 7 and CentOS 7 the development packages do not provide ``tcl.pc`` + and ``tk.pc``, use :envvar:`TCLTK_LIBS="-ltk8.5 -ltkstub8.5 -ltcl8.5"`. + The directory ``Misc/rhel7`` contains ``.pc`` files and instructions + how to build Python with RHEL 7's and CentOS 7's Tcl/Tk and OpenSSL. * CPython now has :pep:`11` tier 3 support for cross compiling to WebAssembly platform ``wasm32-unknown-emscripten`` (Python in the browser). The effort @@ -2063,5 +2175,30 @@ API). (Contributed by Victor Stinner in :issue:`45412`.) +* Remove the :c:type:`Py_UNICODE` encoder APIs, + as they have been deprecated since Python 3.3, + are little used + and are inefficient relative to the recommended alternatives. + + The removed functions are: + + * :func:`!PyUnicode_Encode` + * :func:`!PyUnicode_EncodeASCII` + * :func:`!PyUnicode_EncodeLatin1` + * :func:`!PyUnicode_EncodeUTF7` + * :func:`!PyUnicode_EncodeUTF8` + * :func:`!PyUnicode_EncodeUTF16` + * :func:`!PyUnicode_EncodeUTF32` + * :func:`!PyUnicode_EncodeUnicodeEscape` + * :func:`!PyUnicode_EncodeRawUnicodeEscape` + * :func:`!PyUnicode_EncodeCharmap` + * :func:`!PyUnicode_TranslateCharmap` + * :func:`!PyUnicode_EncodeDecimal` + * :func:`!PyUnicode_TransformDecimalToASCII` + + See :pep:`624` for details and + :pep:`migration guidance <624#alternative-apis>`. + (Contributed by Inada Naoki in :issue:`44029`.) + .. _libb2: https://www.blake2.net/ diff -Nru python3.11-3.11.0~rc1/Grammar/python.gram python3.11-3.11.0~rc2/Grammar/python.gram --- python3.11-3.11.0~rc1/Grammar/python.gram 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Grammar/python.gram 2022-09-11 19:23:30.000000000 +0000 @@ -287,9 +287,9 @@ parameters[arguments_ty]: | a=slash_no_default b[asdl_arg_seq*]=param_no_default* c=param_with_default* d=[star_etc] { - _PyPegen_make_arguments(p, a, NULL, b, c, d) } + CHECK_VERSION(arguments_ty, 8, "Positional-only parameters are", _PyPegen_make_arguments(p, a, NULL, b, c, d)) } | a=slash_with_default b=param_with_default* c=[star_etc] { - _PyPegen_make_arguments(p, NULL, a, NULL, b, c) } + CHECK_VERSION(arguments_ty, 8, "Positional-only parameters are", _PyPegen_make_arguments(p, NULL, a, NULL, b, c)) } | a[asdl_arg_seq*]=param_no_default+ b=param_with_default* c=[star_etc] { _PyPegen_make_arguments(p, NULL, NULL, a, b, c) } | a=param_with_default+ b=[star_etc] { _PyPegen_make_arguments(p, NULL, NULL, NULL, a, b)} @@ -830,9 +830,9 @@ # lambda_parameters[arguments_ty]: | a=lambda_slash_no_default b[asdl_arg_seq*]=lambda_param_no_default* c=lambda_param_with_default* d=[lambda_star_etc] { - _PyPegen_make_arguments(p, a, NULL, b, c, d) } + CHECK_VERSION(arguments_ty, 8, "Positional-only parameters are", _PyPegen_make_arguments(p, a, NULL, b, c, d)) } | a=lambda_slash_with_default b=lambda_param_with_default* c=[lambda_star_etc] { - _PyPegen_make_arguments(p, NULL, a, NULL, b, c) } + CHECK_VERSION(arguments_ty, 8, "Positional-only parameters are", _PyPegen_make_arguments(p, NULL, a, NULL, b, c)) } | a[asdl_arg_seq*]=lambda_param_no_default+ b=lambda_param_with_default* c=[lambda_star_etc] { _PyPegen_make_arguments(p, NULL, NULL, a, b, c) } | a=lambda_param_with_default+ b=[lambda_star_etc] { _PyPegen_make_arguments(p, NULL, NULL, NULL, a, b)} diff -Nru python3.11-3.11.0~rc1/Include/internal/pycore_atomic.h python3.11-3.11.0~rc2/Include/internal/pycore_atomic.h --- python3.11-3.11.0~rc1/Include/internal/pycore_atomic.h 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Include/internal/pycore_atomic.h 2022-09-11 19:23:30.000000000 +0000 @@ -236,7 +236,7 @@ in hardware they will fall back to a full memory barrier as well. This might affect performance but likely only in some very specific and - hard to meassure scenario. + hard to measure scenario. */ #if defined(_M_IX86) || defined(_M_X64) typedef enum _Py_memory_order { diff -Nru python3.11-3.11.0~rc1/Include/internal/pycore_dict.h python3.11-3.11.0~rc2/Include/internal/pycore_dict.h --- python3.11-3.11.0~rc1/Include/internal/pycore_dict.h 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Include/internal/pycore_dict.h 2022-09-11 19:23:30.000000000 +0000 @@ -141,17 +141,8 @@ #define DK_LOG_SIZE(dk) ((dk)->dk_log2_size) #if SIZEOF_VOID_P > 4 #define DK_SIZE(dk) (((int64_t)1)<dk_kind == DICT_KEYS_GENERAL), (PyDictKeyEntry*)(&((int8_t*)((dk)->dk_indices))[(size_t)1 << (dk)->dk_log2_index_bytes])) diff -Nru python3.11-3.11.0~rc1/Include/internal/pycore_frame.h python3.11-3.11.0~rc2/Include/internal/pycore_frame.h --- python3.11-3.11.0~rc1/Include/internal/pycore_frame.h 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Include/internal/pycore_frame.h 2022-09-11 19:23:30.000000000 +0000 @@ -192,17 +192,25 @@ extern _PyInterpreterFrame * _PyThreadState_BumpFramePointerSlow(PyThreadState *tstate, size_t size); +static inline bool +_PyThreadState_HasStackSpace(PyThreadState *tstate, size_t size) +{ + assert( + (tstate->datastack_top == NULL && tstate->datastack_limit == NULL) + || + (tstate->datastack_top != NULL && tstate->datastack_limit != NULL) + ); + return tstate->datastack_top != NULL && + size < (size_t)(tstate->datastack_limit - tstate->datastack_top); +} + static inline _PyInterpreterFrame * _PyThreadState_BumpFramePointer(PyThreadState *tstate, size_t size) { - PyObject **base = tstate->datastack_top; - if (base) { - PyObject **top = base + size; - assert(tstate->datastack_limit); - if (top < tstate->datastack_limit) { - tstate->datastack_top = top; - return (_PyInterpreterFrame *)base; - } + if (_PyThreadState_HasStackSpace(tstate, size)) { + _PyInterpreterFrame *res = (_PyInterpreterFrame *)tstate->datastack_top; + tstate->datastack_top += size; + return res; } return _PyThreadState_BumpFramePointerSlow(tstate, size); } diff -Nru python3.11-3.11.0~rc1/Include/internal/pycore_initconfig.h python3.11-3.11.0~rc2/Include/internal/pycore_initconfig.h --- python3.11-3.11.0~rc1/Include/internal/pycore_initconfig.h 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Include/internal/pycore_initconfig.h 2022-09-11 19:23:30.000000000 +0000 @@ -170,6 +170,8 @@ PyAPI_FUNC(PyObject*) _Py_Get_Getpath_CodeObject(void); +extern int _Py_global_config_int_max_str_digits; + /* --- Function used for testing ---------------------------------- */ diff -Nru python3.11-3.11.0~rc1/Include/internal/pycore_interp.h python3.11-3.11.0~rc2/Include/internal/pycore_interp.h --- python3.11-3.11.0~rc1/Include/internal/pycore_interp.h 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Include/internal/pycore_interp.h 2022-09-11 19:23:30.000000000 +0000 @@ -176,6 +176,8 @@ struct type_cache type_cache; struct callable_cache callable_cache; + int int_max_str_digits; + /* The following fields are here to avoid allocation during init. The data is exposed through PyInterpreterState pointer fields. These fields should not be accessed directly outside of init. diff -Nru python3.11-3.11.0~rc1/Include/internal/pycore_long.h python3.11-3.11.0~rc2/Include/internal/pycore_long.h --- python3.11-3.11.0~rc1/Include/internal/pycore_long.h 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Include/internal/pycore_long.h 2022-09-11 19:23:30.000000000 +0000 @@ -11,6 +11,41 @@ #include "pycore_global_objects.h" // _PY_NSMALLNEGINTS #include "pycore_runtime.h" // _PyRuntime +/* + * Default int base conversion size limitation: Denial of Service prevention. + * + * Chosen such that this isn't wildly slow on modern hardware and so that + * everyone's existing deployed numpy test suite passes before + * https://github.com/numpy/numpy/issues/22098 is widely available. + * + * $ python -m timeit -s 's = "1"*4300' 'int(s)' + * 2000 loops, best of 5: 125 usec per loop + * $ python -m timeit -s 's = "1"*4300; v = int(s)' 'str(v)' + * 1000 loops, best of 5: 311 usec per loop + * (zen2 cloud VM) + * + * 4300 decimal digits fits a ~14284 bit number. + */ +#define _PY_LONG_DEFAULT_MAX_STR_DIGITS 4300 +/* + * Threshold for max digits check. For performance reasons int() and + * int.__str__() don't checks values that are smaller than this + * threshold. Acts as a guaranteed minimum size limit for bignums that + * applications can expect from CPython. + * + * % python -m timeit -s 's = "1"*640; v = int(s)' 'str(int(s))' + * 20000 loops, best of 5: 12 usec per loop + * + * "640 digits should be enough for anyone." - gps + * fits a ~2126 bit decimal number. + */ +#define _PY_LONG_MAX_STR_DIGITS_THRESHOLD 640 + +#if ((_PY_LONG_DEFAULT_MAX_STR_DIGITS != 0) && \ + (_PY_LONG_DEFAULT_MAX_STR_DIGITS < _PY_LONG_MAX_STR_DIGITS_THRESHOLD)) +# error "_PY_LONG_DEFAULT_MAX_STR_DIGITS smaller than threshold." +#endif + /* runtime lifecycle */ diff -Nru python3.11-3.11.0~rc1/Include/patchlevel.h python3.11-3.11.0~rc2/Include/patchlevel.h --- python3.11-3.11.0~rc1/Include/patchlevel.h 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Include/patchlevel.h 2022-09-11 19:23:30.000000000 +0000 @@ -20,10 +20,10 @@ #define PY_MINOR_VERSION 11 #define PY_MICRO_VERSION 0 #define PY_RELEASE_LEVEL PY_RELEASE_LEVEL_GAMMA -#define PY_RELEASE_SERIAL 1 +#define PY_RELEASE_SERIAL 2 /* Version as a string */ -#define PY_VERSION "3.11.0rc1" +#define PY_VERSION "3.11.0rc2" /*--end constants--*/ /* Version as a single 4-byte hex number, e.g. 0x010502B2 == 1.5.2b2. diff -Nru python3.11-3.11.0~rc1/Lib/ast.py python3.11-3.11.0~rc2/Lib/ast.py --- python3.11-3.11.0~rc1/Lib/ast.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/ast.py 2022-09-11 19:23:30.000000000 +0000 @@ -852,7 +852,7 @@ def visit_ImportFrom(self, node): self.fill("from ") - self.write("." * node.level) + self.write("." * (node.level or 0)) if node.module: self.write(node.module) self.write(" import ") diff -Nru python3.11-3.11.0~rc1/Lib/asyncio/runners.py python3.11-3.11.0~rc2/Lib/asyncio/runners.py --- python3.11-3.11.0~rc1/Lib/asyncio/runners.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/asyncio/runners.py 2022-09-11 19:23:30.000000000 +0000 @@ -115,8 +115,6 @@ self._interrupt_count = 0 try: - if self._set_event_loop: - events.set_event_loop(self._loop) return self._loop.run_until_complete(task) except exceptions.CancelledError: if self._interrupt_count > 0: @@ -137,7 +135,11 @@ return if self._loop_factory is None: self._loop = events.new_event_loop() - self._set_event_loop = True + if not self._set_event_loop: + # Call set_event_loop only once to avoid calling + # attach_loop multiple times on child watchers + events.set_event_loop(self._loop) + self._set_event_loop = True else: self._loop = self._loop_factory() if self._debug is not None: diff -Nru python3.11-3.11.0~rc1/Lib/asyncio/streams.py python3.11-3.11.0~rc2/Lib/asyncio/streams.py --- python3.11-3.11.0~rc1/Lib/asyncio/streams.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/asyncio/streams.py 2022-09-11 19:23:30.000000000 +0000 @@ -2,6 +2,7 @@ 'StreamReader', 'StreamWriter', 'StreamReaderProtocol', 'open_connection', 'start_server') +import collections import socket import sys import warnings @@ -129,7 +130,7 @@ else: self._loop = loop self._paused = False - self._drain_waiter = None + self._drain_waiters = collections.deque() self._connection_lost = False def pause_writing(self): @@ -144,38 +145,34 @@ if self._loop.get_debug(): logger.debug("%r resumes writing", self) - waiter = self._drain_waiter - if waiter is not None: - self._drain_waiter = None + for waiter in self._drain_waiters: if not waiter.done(): waiter.set_result(None) def connection_lost(self, exc): self._connection_lost = True - # Wake up the writer if currently paused. + # Wake up the writer(s) if currently paused. if not self._paused: return - waiter = self._drain_waiter - if waiter is None: - return - self._drain_waiter = None - if waiter.done(): - return - if exc is None: - waiter.set_result(None) - else: - waiter.set_exception(exc) + + for waiter in self._drain_waiters: + if not waiter.done(): + if exc is None: + waiter.set_result(None) + else: + waiter.set_exception(exc) async def _drain_helper(self): if self._connection_lost: raise ConnectionResetError('Connection lost') if not self._paused: return - waiter = self._drain_waiter - assert waiter is None or waiter.cancelled() waiter = self._loop.create_future() - self._drain_waiter = waiter - await waiter + self._drain_waiters.append(waiter) + try: + await waiter + finally: + self._drain_waiters.remove(waiter) def _get_close_waiter(self, stream): raise NotImplementedError @@ -206,6 +203,7 @@ self._strong_reader = stream_reader self._reject_connection = False self._stream_writer = None + self._task = None self._transport = None self._client_connected_cb = client_connected_cb self._over_ssl = False @@ -248,7 +246,7 @@ res = self._client_connected_cb(reader, self._stream_writer) if coroutines.iscoroutine(res): - self._loop.create_task(res) + self._task = self._loop.create_task(res) self._strong_reader = None def connection_lost(self, exc): @@ -266,6 +264,7 @@ super().connection_lost(exc) self._stream_reader_wr = None self._stream_writer = None + self._task = None self._transport = None def data_received(self, data): diff -Nru python3.11-3.11.0~rc1/Lib/asyncio/taskgroups.py python3.11-3.11.0~rc2/Lib/asyncio/taskgroups.py --- python3.11-3.11.0~rc1/Lib/asyncio/taskgroups.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/asyncio/taskgroups.py 2022-09-11 19:23:30.000000000 +0000 @@ -1,4 +1,5 @@ -# Adapted with permission from the EdgeDB project. +# Adapted with permission from the EdgeDB project; +# license: PSFL. __all__ = ["TaskGroup"] @@ -115,10 +116,9 @@ if self._base_error is not None: raise self._base_error - if propagate_cancellation_error is not None: - # The wrapping task was cancelled; since we're done with - # closing all child tasks, just propagate the cancellation - # request now. + # Propagate CancelledError if there is one, except if there + # are other errors -- those have priority. + if propagate_cancellation_error and not self._errors: raise propagate_cancellation_error if et is not None and et is not exceptions.CancelledError: diff -Nru python3.11-3.11.0~rc1/Lib/asyncio/tasks.py python3.11-3.11.0~rc2/Lib/asyncio/tasks.py --- python3.11-3.11.0~rc1/Lib/asyncio/tasks.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/asyncio/tasks.py 2022-09-11 19:23:30.000000000 +0000 @@ -848,7 +848,8 @@ The statement - res = await shield(something()) + task = asyncio.create_task(something()) + res = await shield(task) is exactly equivalent to the statement @@ -864,10 +865,16 @@ If you want to completely ignore cancellation (not recommended) you can combine shield() with a try/except clause, as follows: + task = asyncio.create_task(something()) try: - res = await shield(something()) + res = await shield(task) except CancelledError: res = None + + Save a reference to tasks passed to this function, to avoid + a task disappearing mid-execution. The event loop only keeps + weak references to tasks. A task that isn't referenced elsewhere + may get garbage collected at any time, even before it's done. """ inner = _ensure_future(arg) if inner.done(): diff -Nru python3.11-3.11.0~rc1/Lib/bdb.py python3.11-3.11.0~rc2/Lib/bdb.py --- python3.11-3.11.0~rc1/Lib/bdb.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/bdb.py 2022-09-11 19:23:30.000000000 +0000 @@ -805,15 +805,18 @@ return True -# Determines if there is an effective (active) breakpoint at this -# line of code. Returns breakpoint number or 0 if none def effective(file, line, frame): - """Determine which breakpoint for this file:line is to be acted upon. + """Return (active breakpoint, delete temporary flag) or (None, None) as + breakpoint to act upon. - Called only if we know there is a breakpoint at this location. Return - the breakpoint that was triggered and a boolean that indicates if it is - ok to delete a temporary breakpoint. Return (None, None) if there is no - matching breakpoint. + The "active breakpoint" is the first entry in bplist[line, file] (which + must exist) that is enabled, for which checkfuncname is True, and that + has neither a False condition nor a positive ignore count. The flag, + meaning that a temporary breakpoint should be deleted, is False only + when the condiion cannot be evaluated (in which case, ignore count is + ignored). + + If no such entry exists, then (None, None) is returned. """ possibles = Breakpoint.bplist[file, line] for b in possibles: diff -Nru python3.11-3.11.0~rc1/Lib/crypt.py python3.11-3.11.0~rc2/Lib/crypt.py --- python3.11-3.11.0~rc1/Lib/crypt.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/crypt.py 2022-09-11 19:23:30.000000000 +0000 @@ -98,7 +98,7 @@ result = crypt('', salt) except OSError as e: # Not all libc libraries support all encryption methods. - if e.errno == errno.EINVAL: + if e.errno in {errno.EINVAL, errno.EPERM, errno.ENOSYS}: return False raise if result and len(result) == method.total_size: diff -Nru python3.11-3.11.0~rc1/Lib/dis.py python3.11-3.11.0~rc2/Lib/dis.py --- python3.11-3.11.0~rc1/Lib/dis.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/dis.py 2022-09-11 19:23:30.000000000 +0000 @@ -392,7 +392,7 @@ else: return UNKNOWN, '' -def parse_varint(iterator): +def _parse_varint(iterator): b = next(iterator) val = b & 63 while b&64: @@ -401,16 +401,16 @@ val |= b&63 return val -def parse_exception_table(code): +def _parse_exception_table(code): iterator = iter(code.co_exceptiontable) entries = [] try: while True: - start = parse_varint(iterator)*2 - length = parse_varint(iterator)*2 + start = _parse_varint(iterator)*2 + length = _parse_varint(iterator)*2 end = start + length - target = parse_varint(iterator)*2 - dl = parse_varint(iterator) + target = _parse_varint(iterator)*2 + dl = _parse_varint(iterator) depth = dl >> 1 lasti = bool(dl&1) entries.append(_ExceptionTableEntry(start, end, target, depth, lasti)) @@ -519,7 +519,7 @@ def disassemble(co, lasti=-1, *, file=None, show_caches=False, adaptive=False): """Disassemble a code object.""" linestarts = dict(findlinestarts(co)) - exception_entries = parse_exception_table(co) + exception_entries = _parse_exception_table(co) _disassemble_bytes(_get_code_array(co, adaptive), lasti, co._varname_from_oparg, co.co_names, co.co_consts, linestarts, file=file, @@ -706,7 +706,7 @@ self._linestarts = dict(findlinestarts(co)) self._original_object = x self.current_offset = current_offset - self.exception_entries = parse_exception_table(co) + self.exception_entries = _parse_exception_table(co) self.show_caches = show_caches self.adaptive = adaptive diff -Nru python3.11-3.11.0~rc1/Lib/enum.py python3.11-3.11.0~rc2/Lib/enum.py --- python3.11-3.11.0~rc1/Lib/enum.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/enum.py 2022-09-11 19:23:30.000000000 +0000 @@ -1904,7 +1904,7 @@ else: checked_value = checked_dict[key] simple_value = simple_dict[key] - if callable(checked_value): + if callable(checked_value) or isinstance(checked_value, bltns.property): continue if key == '__doc__': # remove all spaces/tabs diff -Nru python3.11-3.11.0~rc1/Lib/idlelib/browser.py python3.11-3.11.0~rc2/Lib/idlelib/browser.py --- python3.11-3.11.0~rc1/Lib/idlelib/browser.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/idlelib/browser.py 2022-09-11 19:23:30.000000000 +0000 @@ -52,7 +52,7 @@ # If obj.name != key, it has already been suffixed. supers = [] for sup in obj.super: - if type(sup) is type(''): + if isinstance(sup, str): sname = sup else: sname = sup.name diff -Nru python3.11-3.11.0~rc1/Lib/idlelib/configdialog.py python3.11-3.11.0~rc2/Lib/idlelib/configdialog.py --- python3.11-3.11.0~rc1/Lib/idlelib/configdialog.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/idlelib/configdialog.py 2022-09-11 19:23:30.000000000 +0000 @@ -121,7 +121,7 @@ self.winpage = WinPage(note) self.shedpage = ShedPage(note) - note.add(self.fontpage, text='Fonts/Tabs') + note.add(self.fontpage, text=' Fonts ') note.add(self.highpage, text='Highlights') note.add(self.keyspage, text=' Keys ') note.add(self.winpage, text=' Windows ') diff -Nru python3.11-3.11.0~rc1/Lib/idlelib/editor.py python3.11-3.11.0~rc2/Lib/idlelib/editor.py --- python3.11-3.11.0~rc1/Lib/idlelib/editor.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/idlelib/editor.py 2022-09-11 19:23:30.000000000 +0000 @@ -86,10 +86,20 @@ dochome = os.path.join(basepath, pyver, 'Doc', 'index.html') elif sys.platform[:3] == 'win': - chmfile = os.path.join(sys.base_prefix, 'Doc', - 'Python%s.chm' % _sphinx_version()) - if os.path.isfile(chmfile): - dochome = chmfile + import winreg # Windows only, block only executed once. + docfile = '' + KEY = (rf"Software\Python\PythonCore\{sys.winver}" + r"\Help\Main Python Documentation") + try: + docfile = winreg.QueryValue(winreg.HKEY_CURRENT_USER, KEY) + except FileNotFoundError: + try: + docfile = winreg.QueryValue(winreg.HKEY_LOCAL_MACHINE, + KEY) + except FileNotFoundError: + pass + if os.path.isfile(docfile): + dochome = docfile elif sys.platform == 'darwin': # documentation may be stored inside a python framework dochome = os.path.join(sys.base_prefix, diff -Nru python3.11-3.11.0~rc1/Lib/idlelib/help.html python3.11-3.11.0~rc2/Lib/idlelib/help.html --- python3.11-3.11.0~rc1/Lib/idlelib/help.html 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/idlelib/help.html 2022-09-11 19:23:30.000000000 +0000 @@ -91,6 +91,7 @@
  • Editor windows
  • Key bindings
  • Automatic indentation
  • +
  • Search and Replace
  • Completions
  • Calltips
  • Code Context
  • @@ -114,6 +115,7 @@
  • Extensions
  • +
  • idlelib
  • @@ -237,13 +239,13 @@
    Open…

    Open an existing file with an Open dialog.

    -
    Recent Files

    Open a list of recent files. Click one to open it.

    -
    Open Module…

    Open an existing module (searches sys.path).

    +
    Recent Files

    Open a list of recent files. Click one to open it.

    +
    -
    Class Browser

    Show functions, classes, and methods in the current Editor file in a +

    Module Browser

    Show functions, classes, and methods in the current Editor file in a tree structure. In the shell, open a module first.

    Path Browser

    Show sys.path directories, modules, functions, classes and methods in a @@ -255,10 +257,13 @@ do Save As instead.

    Save As…

    Save the current window with a Save As dialog. The file saved becomes the -new associated file for the window.

    +new associated file for the window. (If your file namager is set to hide +extensions, the current extension will be omitted in the file name box. +If the new filename has no ‘.’, ‘.py’ and ‘.txt’ will be added for Python +and text files, except that on macOS Aqua,’.py’ is added for all files.)

    Save Copy As…

    Save the current window to different file without changing the associated -file.

    +file. (See Save As note above about filename extensions.)

    Print Window

    Print the current window to the default printer.

    @@ -278,6 +283,8 @@
    Redo

    Redo the last undone change to the current window.

    +
    Select All

    Select the entire contents of the current window.

    +
    Cut

    Copy selection into the system-wide clipboard; then delete the selection.

    Copy

    Copy selection into the system-wide clipboard.

    @@ -287,8 +294,6 @@

    The clipboard functions are also available in context menus.

    -
    Select All

    Select the entire contents of the current window.

    -
    Find…

    Open a search dialog with many options

    Find Again

    Repeat the last search, if there is one.

    @@ -309,17 +314,21 @@
    Expand Word

    Expand a prefix you have typed to match a full word in the same window; repeat to get a different expansion.

    -
    Show call tip

    After an unclosed parenthesis for a function, open a small window with +

    Show Call Tip

    After an unclosed parenthesis for a function, open a small window with function parameter hints. See Calltips in the Editing and navigation section below.

    -
    Show surrounding parens

    Highlight the surrounding parenthesis.

    +
    Show Surrounding Parens

    Highlight the surrounding parenthesis.

    Format menu (Editor window only)

    +
    Format Paragraph

    Reformat the current blank-line-delimited paragraph in comment block or +multiline string or selected line in a string. All lines in the +paragraph will be formatted to less than N columns, where N defaults to 72.

    +
    Indent Region

    Shift selected lines right by the indent width (default 4 spaces).

    Dedent Region

    Shift selected lines left by the indent width (default 4 spaces).

    @@ -338,11 +347,7 @@
    New Indent Width

    Open a dialog to change indent width. The accepted default by the Python community is 4 spaces.

    -
    Format Paragraph

    Reformat the current blank-line-delimited paragraph in comment block or -multiline string or selected line in a string. All lines in the -paragraph will be formatted to less than N columns, where N defaults to 72.

    -
    -
    Strip trailing whitespace

    Remove trailing space and other whitespace characters after the last +

    Strip Trailing Chitespace

    Remove trailing space and other whitespace characters after the last non-whitespace character of a line by applying str.rstrip to each line, including lines within multiline strings. Except for Shell windows, remove extra newlines at the end of the file.

    @@ -565,6 +570,13 @@

    See also the indent/dedent region commands on the Format menu.

    +
    +

    Search and Replace

    +

    Any selection becomes a search target. However, only selections within +a line work because searches are only performed within lines with the +terminal newline removed. If [x] Regular expresion is checked, the +target is interpreted according to the Python re module.

    +

    Completions

    Completions are supplied, when requested and available, for module @@ -991,6 +1003,18 @@ also used for testing.

    +
    +

    idlelib

    +

    Source code: Lib/idlelib

    +
    +

    The Lib/idlelib package implements the IDLE application. See the rest +of this page for how to use IDLE.

    +

    The files in idlelib are described in idlelib/README.txt. Access it +either in idlelib or click Help => About IDLE on the IDLE menu. This +file also maps IDLE menu items to the code that implements the item. +Except for files listed under ‘Startup’, the idlelib code is ‘private’ in +sense that feature changes can be backported (see PEP 434).

    +
    @@ -1021,6 +1045,7 @@
  • Editor windows
  • Key bindings
  • Automatic indentation
  • +
  • Search and Replace
  • Completions
  • Calltips
  • Code Context
  • @@ -1044,6 +1069,7 @@
  • Extensions
  • +
  • idlelib
  • @@ -1141,7 +1167,7 @@

    - Last updated on Jul 03, 2022. + Last updated on Sep 03, 2022. Found a bug?
    diff -Nru python3.11-3.11.0~rc1/Lib/idlelib/idle_test/test_sidebar.py python3.11-3.11.0~rc2/Lib/idlelib/idle_test/test_sidebar.py --- python3.11-3.11.0~rc1/Lib/idlelib/idle_test/test_sidebar.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/idlelib/idle_test/test_sidebar.py 2022-09-11 19:23:30.000000000 +0000 @@ -6,6 +6,7 @@ import unittest import unittest.mock from test.support import requires, swap_attr +from test import support import tkinter as tk from idlelib.idle_test.tkinter_testing_utils import run_in_tk_mainloop @@ -612,7 +613,8 @@ @run_in_tk_mainloop() def test_very_long_wrapped_line(self): - with swap_attr(self.shell, 'squeezer', None): + with support.adjust_int_max_str_digits(11_111), \ + swap_attr(self.shell, 'squeezer', None): self.do_input('x = ' + '1'*10_000 + '\n') yield self.assertEqual(self.get_sidebar_lines(), ['>>>']) diff -Nru python3.11-3.11.0~rc1/Lib/idlelib/mainmenu.py python3.11-3.11.0~rc2/Lib/idlelib/mainmenu.py --- python3.11-3.11.0~rc1/Lib/idlelib/mainmenu.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/idlelib/mainmenu.py 2022-09-11 19:23:30.000000000 +0000 @@ -111,7 +111,7 @@ ('help', [ ('_About IDLE', '<>'), None, - ('_IDLE Help', '<>'), + ('_IDLE Doc', '<>'), ('Python _Docs', '<>'), ]), ] diff -Nru python3.11-3.11.0~rc1/Lib/idlelib/README.txt python3.11-3.11.0~rc2/Lib/idlelib/README.txt --- python3.11-3.11.0~rc1/Lib/idlelib/README.txt 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/idlelib/README.txt 2022-09-11 19:23:30.000000000 +0000 @@ -243,8 +243,8 @@ Generally use PEP 8. -import ------- +import statements +----------------- Put imports at the top, unless there is a good reason otherwise. PEP 8 says to group stdlib, 3rd-party dependencies, and package imports. For idlelib, the groups are general stdlib, tkinter, and idlelib. @@ -259,3 +259,32 @@ Within module imports like "from idlelib.mod import class" may cause circular imports to deadlock. Even without this, circular imports may require at least one of the imports to be delayed until a function call. + +What's New entries +------------------ + +Repository directory Doc/whatsnew/ has a file 3.n.rst for each 3.n +Python version. For the first entry in each file, add subsection +'IDLE and idlelib', in alphabetical position, to the 'Improved Modules' +section. For the rest of cpython, entries to 3.(n+1).rst begin with +the release of 3.n.0b1. For IDLE, entries for features backported from +'main' to '3.n' during its beta period do not got in 3.(n+1).rst. The +latter usually gets its first entry during the 3.n.0 candidate period +or after the 3.n.0 release. + +When, as per PEP 434, feature changes are backported, entries are placed +in the 3.n.rst file *in the main branch* for each Python version n that +gets the backport. (Note: the format of entries have varied between +versions.) Add a line "New in 3.n maintenance releases." before the +first back-ported feature after 3.n.0 is released. Since each older +version file gets a different number of backports, it is easiest to +make a separate PR for each file and label it with the backports +needed. + +Github repository and issues +---------------------------- + +The CPython repository is https://github.com/python/cpython. The +IDLE Issues listing is https://github.com/orgs/python/projects/31. +The main classification is by Topic, based on the IDLE menu. View the +topics list by clicking the [<]] button in the upper right. diff -Nru python3.11-3.11.0~rc1/Lib/io.py python3.11-3.11.0~rc2/Lib/io.py --- python3.11-3.11.0~rc1/Lib/io.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/io.py 2022-09-11 19:23:30.000000000 +0000 @@ -70,7 +70,7 @@ global OpenWrapper OpenWrapper = open return OpenWrapper - raise AttributeError("module {__name__!r} has no attribute {name!r}") + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") # Pretend this exception was created here. diff -Nru python3.11-3.11.0~rc1/Lib/logging/config.py python3.11-3.11.0~rc2/Lib/logging/config.py --- python3.11-3.11.0~rc1/Lib/logging/config.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/logging/config.py 2022-09-11 19:23:30.000000000 +0000 @@ -795,6 +795,7 @@ """Configure a non-root logger from a dictionary.""" logger = logging.getLogger(name) self.common_logger_config(logger, config, incremental) + logger.disabled = False propagate = config.get('propagate', None) if propagate is not None: logger.propagate = propagate diff -Nru python3.11-3.11.0~rc1/Lib/logging/handlers.py python3.11-3.11.0~rc2/Lib/logging/handlers.py --- python3.11-3.11.0~rc1/Lib/logging/handlers.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/logging/handlers.py 2022-09-11 19:23:30.000000000 +0000 @@ -348,11 +348,15 @@ record is not used, as we are just comparing times, but it is needed so the method signatures are the same """ - # See bpo-45401: Never rollover anything other than regular files - if os.path.exists(self.baseFilename) and not os.path.isfile(self.baseFilename): - return False t = int(time.time()) if t >= self.rolloverAt: + # See #89564: Never rollover anything other than regular files + if os.path.exists(self.baseFilename) and not os.path.isfile(self.baseFilename): + # The file is not a regular file, so do not rollover, but do + # set the next rollover time to avoid repeated checks. + self.rolloverAt = self.computeRollover(t) + return False + return True return False @@ -1107,7 +1111,16 @@ dllname = os.path.join(dllname[0], r'win32service.pyd') self.dllname = dllname self.logtype = logtype - self._welu.AddSourceToRegistry(appname, dllname, logtype) + # Administrative privileges are required to add a source to the registry. + # This may not be available for a user that just wants to add to an + # existing source - handle this specific case. + try: + self._welu.AddSourceToRegistry(appname, dllname, logtype) + except Exception as e: + # This will probably be a pywintypes.error. Only raise if it's not + # an "access denied" error, else let it pass + if getattr(e, 'winerror', None) != 5: # not access denied + raise self.deftype = win32evtlog.EVENTLOG_ERROR_TYPE self.typemap = { logging.DEBUG : win32evtlog.EVENTLOG_INFORMATION_TYPE, diff -Nru python3.11-3.11.0~rc1/Lib/logging/__init__.py python3.11-3.11.0~rc2/Lib/logging/__init__.py --- python3.11-3.11.0~rc1/Lib/logging/__init__.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/logging/__init__.py 2022-09-11 19:23:30.000000000 +0000 @@ -334,7 +334,7 @@ self.lineno = lineno self.funcName = func self.created = ct - self.msecs = (ct - int(ct)) * 1000 + self.msecs = int((ct - int(ct)) * 1000) + 0.0 # see gh-89047 self.relativeCreated = (self.created - _startTime) * 1000 if logThreads: self.thread = threading.get_ident() diff -Nru python3.11-3.11.0~rc1/Lib/numbers.py python3.11-3.11.0~rc2/Lib/numbers.py --- python3.11-3.11.0~rc1/Lib/numbers.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/numbers.py 2022-09-11 19:23:30.000000000 +0000 @@ -288,7 +288,7 @@ so that ratios of huge integers convert without overflowing. """ - return self.numerator / self.denominator + return int(self.numerator) / int(self.denominator) class Integral(Rational): diff -Nru python3.11-3.11.0~rc1/Lib/pdb.py python3.11-3.11.0~rc2/Lib/pdb.py --- python3.11-3.11.0~rc1/Lib/pdb.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/pdb.py 2022-09-11 19:23:30.000000000 +0000 @@ -131,7 +131,7 @@ return self -class ScriptTarget(str): +class _ScriptTarget(str): def __new__(cls, val): # Mutate self to be the "real path". res = super().__new__(cls, os.path.realpath(val)) @@ -167,7 +167,7 @@ return f"exec(compile({fp.read()!r}, {self!r}, 'exec'))" -class ModuleTarget(str): +class _ModuleTarget(str): def check(self): try: self._details @@ -1625,7 +1625,7 @@ return fullname return None - def _run(self, target: Union[ModuleTarget, ScriptTarget]): + def _run(self, target: Union[_ModuleTarget, _ScriptTarget]): # When bdb sets tracing, a number of call and line events happen # BEFORE debugger even reaches user's code (and the exact sequence of # events depends on python version). Take special measures to @@ -1750,7 +1750,7 @@ commands = [optarg for opt, optarg in opts if opt in ['-c', '--command']] module_indicated = any(opt in ['-m'] for opt, optarg in opts) - cls = ModuleTarget if module_indicated else ScriptTarget + cls = _ModuleTarget if module_indicated else _ScriptTarget target = cls(args[0]) target.check() diff -Nru python3.11-3.11.0~rc1/Lib/posixpath.py python3.11-3.11.0~rc2/Lib/posixpath.py --- python3.11-3.11.0~rc1/Lib/posixpath.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/posixpath.py 2022-09-11 19:23:30.000000000 +0000 @@ -364,7 +364,7 @@ initial_slashes = path.startswith(sep) # POSIX allows one or two initial slashes, but treats three or more # as single slash. - # (see http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13) + # (see https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13) if (initial_slashes and path.startswith(sep*2) and not path.startswith(sep*3)): initial_slashes = 2 diff -Nru python3.11-3.11.0~rc1/Lib/pydoc_data/topics.py python3.11-3.11.0~rc2/Lib/pydoc_data/topics.py --- python3.11-3.11.0~rc1/Lib/pydoc_data/topics.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/pydoc_data/topics.py 2022-09-11 19:23:30.000000000 +0000 @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Autogenerated by Sphinx on Fri Aug 5 15:44:44 2022 +# Autogenerated by Sphinx on Sun Sep 11 20:22:13 2022 topics = {'assert': 'The "assert" statement\n' '**********************\n' '\n' @@ -3482,8 +3482,8 @@ ' there is matched against the whole object rather than an ' 'attribute.\n' ' For example "int(0|1)" matches the value "0", but not the ' - 'values\n' - ' "0.0" or "False".\n' + 'value\n' + ' "0.0".\n' '\n' 'In simple terms "CLS(P1, attr=P2)" matches only if the ' 'following\n' @@ -8983,31 +8983,7 @@ ' still alive. The list is in definition order. Example:\n' '\n' ' >>> int.__subclasses__()\n' - " []\n" - '\n' - '-[ Footnotes ]-\n' - '\n' - '[1] Additional information on these special methods may be ' - 'found in\n' - ' the Python Reference Manual (Basic customization).\n' - '\n' - '[2] As a consequence, the list "[1, 2]" is considered equal ' - 'to "[1.0,\n' - ' 2.0]", and similarly for tuples.\n' - '\n' - '[3] They must have since the parser can’t tell the type of ' - 'the\n' - ' operands.\n' - '\n' - '[4] Cased characters are those with general category ' - 'property being\n' - ' one of “Lu” (Letter, uppercase), “Ll” (Letter, ' - 'lowercase), or “Lt”\n' - ' (Letter, titlecase).\n' - '\n' - '[5] To format only a tuple you should therefore provide a ' - 'singleton\n' - ' tuple whose only element is the tuple to be formatted.\n', + " []\n", 'specialnames': 'Special method names\n' '********************\n' '\n' @@ -12256,8 +12232,8 @@ '| Escape Sequence | Meaning | Notes ' '|\n' '|===================|===================================|=========|\n' - '| "\\newline" | Backslash and newline ignored ' - '| |\n' + '| "\\" | Backslash and newline ignored | ' + '(1) |\n' '+-------------------+-----------------------------------+---------+\n' '| "\\\\" | Backslash ("\\") ' '| |\n' @@ -12290,10 +12266,10 @@ '| |\n' '+-------------------+-----------------------------------+---------+\n' '| "\\ooo" | Character with octal value *ooo* | ' - '(1,3) |\n' + '(2,4) |\n' '+-------------------+-----------------------------------+---------+\n' '| "\\xhh" | Character with hex value *hh* | ' - '(2,3) |\n' + '(3,4) |\n' '+-------------------+-----------------------------------+---------+\n' '\n' 'Escape sequences only recognized in string literals are:\n' @@ -12303,24 +12279,36 @@ '|\n' '|===================|===================================|=========|\n' '| "\\N{name}" | Character named *name* in the | ' - '(4) |\n' + '(5) |\n' '| | Unicode database | ' '|\n' '+-------------------+-----------------------------------+---------+\n' '| "\\uxxxx" | Character with 16-bit hex value | ' - '(5) |\n' + '(6) |\n' '| | *xxxx* | ' '|\n' '+-------------------+-----------------------------------+---------+\n' '| "\\Uxxxxxxxx" | Character with 32-bit hex value | ' - '(6) |\n' + '(7) |\n' '| | *xxxxxxxx* | ' '|\n' '+-------------------+-----------------------------------+---------+\n' '\n' 'Notes:\n' '\n' - '1. As in Standard C, up to three octal digits are accepted.\n' + '1. A backslash can be added at the end of a line to ignore the\n' + ' newline:\n' + '\n' + " >>> 'This string will not include \\\n" + " ... backslashes or newline characters.'\n" + " 'This string will not include backslashes or newline " + "characters.'\n" + '\n' + ' The same result can be achieved using triple-quoted strings, ' + 'or\n' + ' parentheses and string literal concatenation.\n' + '\n' + '2. As in Standard C, up to three octal digits are accepted.\n' '\n' ' Changed in version 3.11: Octal escapes with value larger than\n' ' "0o377" produce a "DeprecationWarning". In a future Python ' @@ -12328,20 +12316,20 @@ ' they will be a "SyntaxWarning" and eventually a ' '"SyntaxError".\n' '\n' - '2. Unlike in Standard C, exactly two hex digits are required.\n' + '3. Unlike in Standard C, exactly two hex digits are required.\n' '\n' - '3. In a bytes literal, hexadecimal and octal escapes denote the ' + '4. In a bytes literal, hexadecimal and octal escapes denote the ' 'byte\n' ' with the given value. In a string literal, these escapes ' 'denote a\n' ' Unicode character with the given value.\n' '\n' - '4. Changed in version 3.3: Support for name aliases [1] has been\n' + '5. Changed in version 3.3: Support for name aliases [1] has been\n' ' added.\n' '\n' - '5. Exactly four hex digits are required.\n' + '6. Exactly four hex digits are required.\n' '\n' - '6. Any Unicode character can be encoded this way. Exactly eight ' + '7. Any Unicode character can be encoded this way. Exactly eight ' 'hex\n' ' digits are required.\n' '\n' diff -Nru python3.11-3.11.0~rc1/Lib/test/support/__init__.py python3.11-3.11.0~rc2/Lib/test/support/__init__.py --- python3.11-3.11.0~rc1/Lib/test/support/__init__.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/test/support/__init__.py 2022-09-11 19:23:30.000000000 +0000 @@ -2223,3 +2223,14 @@ except ImportError: ctypes = None return unittest.skipUnless(ctypes, 'venv: pip requires ctypes') + + +@contextlib.contextmanager +def adjust_int_max_str_digits(max_digits): + """Temporarily change the integer string conversion length limit.""" + current = sys.get_int_max_str_digits() + try: + sys.set_int_max_str_digits(max_digits) + yield + finally: + sys.set_int_max_str_digits(current) diff -Nru python3.11-3.11.0~rc1/Lib/test/test_ast.py python3.11-3.11.0~rc2/Lib/test/test_ast.py --- python3.11-3.11.0~rc1/Lib/test/test_ast.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/test/test_ast.py 2022-09-11 19:23:30.000000000 +0000 @@ -738,6 +738,21 @@ expressions[0] = f"expr = {ast.expr.__subclasses__()[0].__doc__}" self.assertCountEqual(ast.expr.__doc__.split("\n"), expressions) + def test_positional_only_feature_version(self): + ast.parse('def foo(x, /): ...', feature_version=(3, 8)) + ast.parse('def bar(x=1, /): ...', feature_version=(3, 8)) + with self.assertRaises(SyntaxError): + ast.parse('def foo(x, /): ...', feature_version=(3, 7)) + with self.assertRaises(SyntaxError): + ast.parse('def bar(x=1, /): ...', feature_version=(3, 7)) + + ast.parse('lambda x, /: ...', feature_version=(3, 8)) + ast.parse('lambda x=1, /: ...', feature_version=(3, 8)) + with self.assertRaises(SyntaxError): + ast.parse('lambda x, /: ...', feature_version=(3, 7)) + with self.assertRaises(SyntaxError): + ast.parse('lambda x=1, /: ...', feature_version=(3, 7)) + def test_parenthesized_with_feature_version(self): ast.parse('with (CtxManager() as example): ...', feature_version=(3, 10)) # While advertised as a feature in Python 3.10, this was allowed starting 3.9 @@ -746,7 +761,7 @@ ast.parse('with (CtxManager() as example): ...', feature_version=(3, 8)) ast.parse('with CtxManager() as example: ...', feature_version=(3, 8)) - def test_issue40614_feature_version(self): + def test_debug_f_string_feature_version(self): ast.parse('f"{x=}"', feature_version=(3, 8)) with self.assertRaises(SyntaxError): ast.parse('f"{x=}"', feature_version=(3, 7)) @@ -1115,6 +1130,14 @@ self.assertRaises(ValueError, ast.literal_eval, '+True') self.assertRaises(ValueError, ast.literal_eval, '2+3') + def test_literal_eval_str_int_limit(self): + with support.adjust_int_max_str_digits(4000): + ast.literal_eval('3'*4000) # no error + with self.assertRaises(SyntaxError) as err_ctx: + ast.literal_eval('3'*4001) + self.assertIn('Exceeds the limit ', str(err_ctx.exception)) + self.assertIn(' Consider hexadecimal ', str(err_ctx.exception)) + def test_literal_eval_complex(self): # Issue #4907 self.assertEqual(ast.literal_eval('6j'), 6j) diff -Nru python3.11-3.11.0~rc1/Lib/test/test_asyncio/test_runners.py python3.11-3.11.0~rc2/Lib/test/test_asyncio/test_runners.py --- python3.11-3.11.0~rc1/Lib/test/test_asyncio/test_runners.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/test/test_asyncio/test_runners.py 2022-09-11 19:23:30.000000000 +0000 @@ -456,6 +456,20 @@ ): runner.run(coro()) + def test_set_event_loop_called_once(self): + # See https://github.com/python/cpython/issues/95736 + async def coro(): + pass + + policy = asyncio.get_event_loop_policy() + policy.set_event_loop = mock.Mock() + runner = asyncio.Runner() + runner.run(coro()) + runner.run(coro()) + + self.assertEqual(1, policy.set_event_loop.call_count) + runner.close() + if __name__ == '__main__': unittest.main() diff -Nru python3.11-3.11.0~rc1/Lib/test/test_asyncio/test_streams.py python3.11-3.11.0~rc2/Lib/test/test_asyncio/test_streams.py --- python3.11-3.11.0~rc1/Lib/test/test_asyncio/test_streams.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/test/test_asyncio/test_streams.py 2022-09-11 19:23:30.000000000 +0000 @@ -864,6 +864,25 @@ self.assertEqual(cm.filename, __file__) self.assertIs(protocol._loop, self.loop) + def test_multiple_drain(self): + # See https://github.com/python/cpython/issues/74116 + drained = 0 + + async def drainer(stream): + nonlocal drained + await stream._drain_helper() + drained += 1 + + async def main(): + loop = asyncio.get_running_loop() + stream = asyncio.streams.FlowControlMixin(loop) + stream.pause_writing() + loop.call_later(0.1, stream.resume_writing) + await asyncio.gather(*[drainer(stream) for _ in range(10)]) + self.assertEqual(drained, 10) + + self.loop.run_until_complete(main()) + def test_drain_raises(self): # See http://bugs.python.org/issue25441 diff -Nru python3.11-3.11.0~rc1/Lib/test/test_asyncio/test_taskgroups.py python3.11-3.11.0~rc2/Lib/test/test_asyncio/test_taskgroups.py --- python3.11-3.11.0~rc1/Lib/test/test_asyncio/test_taskgroups.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/test/test_asyncio/test_taskgroups.py 2022-09-11 19:23:30.000000000 +0000 @@ -1,4 +1,5 @@ -# Adapted with permission from the EdgeDB project. +# Adapted with permission from the EdgeDB project; +# license: PSFL. import asyncio @@ -229,29 +230,29 @@ self.assertEqual(NUM, 15) - async def test_cancellation_in_body(self): + async def test_taskgroup_08(self): async def foo(): - await asyncio.sleep(0.1) - 1 / 0 + try: + await asyncio.sleep(10) + finally: + 1 / 0 async def runner(): async with taskgroups.TaskGroup() as g: for _ in range(5): g.create_task(foo()) - try: - await asyncio.sleep(10) - except asyncio.CancelledError: - raise + await asyncio.sleep(10) r = asyncio.create_task(runner()) await asyncio.sleep(0.1) self.assertFalse(r.done()) r.cancel() - with self.assertRaises(asyncio.CancelledError) as cm: + with self.assertRaises(ExceptionGroup) as cm: await r + self.assertEqual(get_error_types(cm.exception), {ZeroDivisionError}) async def test_taskgroup_09(self): @@ -315,8 +316,10 @@ async def test_taskgroup_11(self): async def foo(): - await asyncio.sleep(0.1) - 1 / 0 + try: + await asyncio.sleep(10) + finally: + 1 / 0 async def runner(): async with taskgroups.TaskGroup(): @@ -324,24 +327,26 @@ for _ in range(5): g2.create_task(foo()) - try: - await asyncio.sleep(10) - except asyncio.CancelledError: - raise + await asyncio.sleep(10) r = asyncio.create_task(runner()) await asyncio.sleep(0.1) self.assertFalse(r.done()) r.cancel() - with self.assertRaises(asyncio.CancelledError): + with self.assertRaises(ExceptionGroup) as cm: await r + self.assertEqual(get_error_types(cm.exception), {ExceptionGroup}) + self.assertEqual(get_error_types(cm.exception.exceptions[0]), {ZeroDivisionError}) + async def test_taskgroup_12(self): async def foo(): - await asyncio.sleep(0.1) - 1 / 0 + try: + await asyncio.sleep(10) + finally: + 1 / 0 async def runner(): async with taskgroups.TaskGroup() as g1: @@ -351,19 +356,19 @@ for _ in range(5): g2.create_task(foo()) - try: - await asyncio.sleep(10) - except asyncio.CancelledError: - raise + await asyncio.sleep(10) r = asyncio.create_task(runner()) await asyncio.sleep(0.1) self.assertFalse(r.done()) r.cancel() - with self.assertRaises(asyncio.CancelledError): + with self.assertRaises(ExceptionGroup) as cm: await r + self.assertEqual(get_error_types(cm.exception), {ExceptionGroup}) + self.assertEqual(get_error_types(cm.exception.exceptions[0]), {ZeroDivisionError}) + async def test_taskgroup_13(self): async def crash_after(t): @@ -423,8 +428,9 @@ self.assertFalse(r.done()) r.cancel() - with self.assertRaises(asyncio.CancelledError): + with self.assertRaises(ExceptionGroup) as cm: await r + self.assertEqual(get_error_types(cm.exception), {ZeroDivisionError}) async def test_taskgroup_16(self): @@ -450,8 +456,9 @@ self.assertFalse(r.done()) r.cancel() - with self.assertRaises(asyncio.CancelledError): + with self.assertRaises(ExceptionGroup) as cm: await r + self.assertEqual(get_error_types(cm.exception), {ZeroDivisionError}) async def test_taskgroup_17(self): NUM = 0 diff -Nru python3.11-3.11.0~rc1/Lib/test/test_builtin.py python3.11-3.11.0~rc2/Lib/test/test_builtin.py --- python3.11-3.11.0~rc1/Lib/test/test_builtin.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/test/test_builtin.py 2022-09-11 19:23:30.000000000 +0000 @@ -737,11 +737,6 @@ self.assertRaises(TypeError, exec, code, {'__builtins__': 123}) - # no __build_class__ function - code = compile("class A: pass", "", "exec") - self.assertRaisesRegex(NameError, "__build_class__ not found", - exec, code, {'__builtins__': {}}) - class frozendict_error(Exception): pass @@ -758,6 +753,15 @@ self.assertRaises(frozendict_error, exec, code, {'__builtins__': frozen_builtins}) + # no __build_class__ function + code = compile("class A: pass", "", "exec") + self.assertRaisesRegex(NameError, "__build_class__ not found", + exec, code, {'__builtins__': {}}) + # __build_class__ in a custom __builtins__ + exec(code, {'__builtins__': frozen_builtins}) + self.assertRaisesRegex(NameError, "__build_class__ not found", + exec, code, {'__builtins__': frozendict()}) + # read-only globals namespace = frozendict({}) code = compile("x=1", "test", "exec") diff -Nru python3.11-3.11.0~rc1/Lib/test/test_bytes.py python3.11-3.11.0~rc2/Lib/test/test_bytes.py --- python3.11-3.11.0~rc1/Lib/test/test_bytes.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/test/test_bytes.py 2022-09-11 19:23:30.000000000 +0000 @@ -715,6 +715,24 @@ self.assertEqual(b, b'hello,\x00world!') self.assertIs(type(b), self.type2test) + def check(fmt, vals, result): + b = self.type2test(fmt) + b = b % vals + self.assertEqual(b, result) + self.assertIs(type(b), self.type2test) + + # A set of tests adapted from test_unicode:UnicodeTest.test_formatting + check(b'...%(foo)b...', {b'foo':b"abc"}, b'...abc...') + check(b'...%(f(o)o)b...', {b'f(o)o':b"abc", b'foo':b'bar'}, b'...abc...') + check(b'...%(foo)b...', {b'foo':b"abc",b'def':123}, b'...abc...') + check(b'%*b', (5, b'abc',), b' abc') + check(b'%*b', (-5, b'abc',), b'abc ') + check(b'%*.*b', (5, 2, b'abc',), b' ab') + check(b'%*.*b', (5, 3, b'abc',), b' abc') + check(b'%i %*.*b', (10, 5, 3, b'abc',), b'10 abc') + check(b'%i%b %*.*b', (10, b'3', 5, 3, b'abc',), b'103 abc') + check(b'%c', b'a', b'a') + def test_imod(self): b = self.type2test(b'hello, %b!') orig = b diff -Nru python3.11-3.11.0~rc1/Lib/test/test_capi.py python3.11-3.11.0~rc2/Lib/test/test_capi.py --- python3.11-3.11.0~rc1/Lib/test/test_capi.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/test/test_capi.py 2022-09-11 19:23:30.000000000 +0000 @@ -915,6 +915,21 @@ t.start() t.join() + @threading_helper.reap_threads + @threading_helper.requires_working_threading() + def test_gilstate_ensure_no_deadlock(self): + # See https://github.com/python/cpython/issues/96071 + code = textwrap.dedent(f""" + import _testcapi + + def callback(): + print('callback called') + + _testcapi._test_thread_state(callback) + """) + ret = assert_python_ok('-X', 'tracemalloc', '-c', code) + self.assertIn(b'callback called', ret.out) + class Test_testcapi(unittest.TestCase): locals().update((name, getattr(_testcapi, name)) diff -Nru python3.11-3.11.0~rc1/Lib/test/test_check_c_globals.py python3.11-3.11.0~rc2/Lib/test/test_check_c_globals.py --- python3.11-3.11.0~rc1/Lib/test/test_check_c_globals.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/test/test_check_c_globals.py 2022-09-11 19:23:30.000000000 +0000 @@ -1,9 +1,14 @@ import unittest import test.test_tools +from test.support.warnings_helper import save_restore_warnings_filters test.test_tools.skip_if_missing('c-analyzer') with test.test_tools.imports_under_tool('c-analyzer'): - from cpython.__main__ import main + # gh-95349: Save/restore warnings filters to leave them unchanged. + # Importing the c-analyzer imports docutils which imports pkg_resources + # which adds a warnings filter. + with save_restore_warnings_filters(): + from cpython.__main__ import main class ActualChecks(unittest.TestCase): diff -Nru python3.11-3.11.0~rc1/Lib/test/test_cmd_line.py python3.11-3.11.0~rc2/Lib/test/test_cmd_line.py --- python3.11-3.11.0~rc1/Lib/test/test_cmd_line.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/test/test_cmd_line.py 2022-09-11 19:23:30.000000000 +0000 @@ -868,6 +868,39 @@ self.assertTrue(proc.stderr.startswith(err_msg), proc.stderr) self.assertNotEqual(proc.returncode, 0) + def test_int_max_str_digits(self): + code = "import sys; print(sys.flags.int_max_str_digits, sys.get_int_max_str_digits())" + + assert_python_failure('-X', 'int_max_str_digits', '-c', code) + assert_python_failure('-X', 'int_max_str_digits=foo', '-c', code) + assert_python_failure('-X', 'int_max_str_digits=100', '-c', code) + + assert_python_failure('-c', code, PYTHONINTMAXSTRDIGITS='foo') + assert_python_failure('-c', code, PYTHONINTMAXSTRDIGITS='100') + + def res2int(res): + out = res.out.strip().decode("utf-8") + return tuple(int(i) for i in out.split()) + + res = assert_python_ok('-c', code) + self.assertEqual(res2int(res), (-1, sys.get_int_max_str_digits())) + res = assert_python_ok('-X', 'int_max_str_digits=0', '-c', code) + self.assertEqual(res2int(res), (0, 0)) + res = assert_python_ok('-X', 'int_max_str_digits=4000', '-c', code) + self.assertEqual(res2int(res), (4000, 4000)) + res = assert_python_ok('-X', 'int_max_str_digits=100000', '-c', code) + self.assertEqual(res2int(res), (100000, 100000)) + + res = assert_python_ok('-c', code, PYTHONINTMAXSTRDIGITS='0') + self.assertEqual(res2int(res), (0, 0)) + res = assert_python_ok('-c', code, PYTHONINTMAXSTRDIGITS='4000') + self.assertEqual(res2int(res), (4000, 4000)) + res = assert_python_ok( + '-X', 'int_max_str_digits=6000', '-c', code, + PYTHONINTMAXSTRDIGITS='4000' + ) + self.assertEqual(res2int(res), (6000, 6000)) + @unittest.skipIf(interpreter_requires_environment(), 'Cannot run -I tests when PYTHON env vars are required.') diff -Nru python3.11-3.11.0~rc1/Lib/test/test_compile.py python3.11-3.11.0~rc2/Lib/test/test_compile.py --- python3.11-3.11.0~rc1/Lib/test/test_compile.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/test/test_compile.py 2022-09-11 19:23:30.000000000 +0000 @@ -197,6 +197,19 @@ self.assertEqual(eval("0o777"), 511) self.assertEqual(eval("-0o0000010"), -8) + def test_int_literals_too_long(self): + n = 3000 + source = f"a = 1\nb = 2\nc = {'3'*n}\nd = 4" + with support.adjust_int_max_str_digits(n): + compile(source, "", "exec") # no errors. + with support.adjust_int_max_str_digits(n-1): + with self.assertRaises(SyntaxError) as err_ctx: + compile(source, "", "exec") + exc = err_ctx.exception + self.assertEqual(exc.lineno, 3) + self.assertIn('Exceeds the limit ', str(exc)) + self.assertIn(' Consider hexadecimal ', str(exc)) + def test_unary_minus(self): # Verify treatment of unary minus on negative numbers SF bug #660455 if sys.maxsize == 2147483647: diff -Nru python3.11-3.11.0~rc1/Lib/test/test_coroutines.py python3.11-3.11.0~rc2/Lib/test/test_coroutines.py --- python3.11-3.11.0~rc1/Lib/test/test_coroutines.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/test/test_coroutines.py 2022-09-11 19:23:30.000000000 +0000 @@ -4,6 +4,7 @@ import pickle import sys import types +import traceback import unittest import warnings from test import support @@ -2207,6 +2208,29 @@ with self.assertWarns(RuntimeWarning): gen.cr_frame.clear() + def test_stack_in_coroutine_throw(self): + # Regression test for https://github.com/python/cpython/issues/93592 + async def a(): + return await b() + + async def b(): + return await c() + + @types.coroutine + def c(): + try: + # traceback.print_stack() + yield len(traceback.extract_stack()) + except ZeroDivisionError: + # traceback.print_stack() + yield len(traceback.extract_stack()) + + coro = a() + len_send = coro.send(None) + len_throw = coro.throw(ZeroDivisionError) + # before fixing, visible stack from throw would be shorter than from send. + self.assertEqual(len_send, len_throw) + @unittest.skipIf( support.is_emscripten or support.is_wasi, diff -Nru python3.11-3.11.0~rc1/Lib/test/test_dataclasses.py python3.11-3.11.0~rc2/Lib/test/test_dataclasses.py --- python3.11-3.11.0~rc1/Lib/test/test_dataclasses.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/test/test_dataclasses.py 2022-09-11 19:23:30.000000000 +0000 @@ -2215,12 +2215,12 @@ self.assertEqual(c.z, 100) def test_no_init(self): - dataclass(init=False) + @dataclass(init=False) class C: i: int = 0 self.assertEqual(C().i, 0) - dataclass(init=False) + @dataclass(init=False) class C: i: int = 2 def __init__(self): diff -Nru python3.11-3.11.0~rc1/Lib/test/test_decimal.py python3.11-3.11.0~rc2/Lib/test/test_decimal.py --- python3.11-3.11.0~rc1/Lib/test/test_decimal.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/test/test_decimal.py 2022-09-11 19:23:30.000000000 +0000 @@ -2526,6 +2526,15 @@ class PyUsabilityTest(UsabilityTest): decimal = P + def setUp(self): + super().setUp() + self._previous_int_limit = sys.get_int_max_str_digits() + sys.set_int_max_str_digits(7000) + + def tearDown(self): + sys.set_int_max_str_digits(self._previous_int_limit) + super().tearDown() + class PythonAPItests(unittest.TestCase): def test_abc(self): @@ -4626,6 +4635,15 @@ class PyCoverage(Coverage): decimal = P + def setUp(self): + super().setUp() + self._previous_int_limit = sys.get_int_max_str_digits() + sys.set_int_max_str_digits(7000) + + def tearDown(self): + sys.set_int_max_str_digits(self._previous_int_limit) + super().tearDown() + class PyFunctionality(unittest.TestCase): """Extra functionality in decimal.py""" diff -Nru python3.11-3.11.0~rc1/Lib/test/test_descrtut.py python3.11-3.11.0~rc2/Lib/test/test_descrtut.py --- python3.11-3.11.0~rc1/Lib/test/test_descrtut.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/test/test_descrtut.py 2022-09-11 19:23:30.000000000 +0000 @@ -1,7 +1,7 @@ # This contains most of the executable examples from Guido's descr # tutorial, once at # -# http://www.python.org/2.2/descrintro.html +# https://www.python.org/download/releases/2.2.3/descrintro/ # # A few examples left implicit in the writeup were fleshed out, a few were # skipped due to lack of interest (e.g., faking super() by hand isn't diff -Nru python3.11-3.11.0~rc1/Lib/test/test_dynamic.py python3.11-3.11.0~rc2/Lib/test/test_dynamic.py --- python3.11-3.11.0~rc1/Lib/test/test_dynamic.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/test/test_dynamic.py 2022-09-11 19:23:30.000000000 +0000 @@ -1,6 +1,7 @@ # Test the most dynamic corner cases of Python's runtime semantics. import builtins +import sys import unittest from test.support import swap_item, swap_attr @@ -146,5 +147,48 @@ for _ in range(30): self.assertEqual(sum_1000(), expected) + +class TestTracing(unittest.TestCase): + + def setUp(self): + self.addCleanup(sys.settrace, sys.gettrace()) + sys.settrace(None) + + def test_after_specialization(self): + + def trace(frame, event, arg): + return trace + + turn_on_trace = False + + class C: + def __init__(self, x): + self.x = x + def __del__(self): + if turn_on_trace: + sys.settrace(trace) + + def f(): + # LOAD_GLOBAL[_BUILTIN] immediately follows the call to C.__del__ + C(0).x, len + + def g(): + # BINARY_SUSCR[_LIST_INT] immediately follows the call to C.__del__ + [0][C(0).x] + + def h(): + # BINARY_OP[_ADD_INT] immediately follows the call to C.__del__ + 0 + C(0).x + + for func in (f, g, h): + with self.subTest(func.__name__): + for _ in range(58): + func() + turn_on_trace = True + func() + sys.settrace(None) + turn_on_trace = False + + if __name__ == "__main__": unittest.main() diff -Nru python3.11-3.11.0~rc1/Lib/test/test_enum.py python3.11-3.11.0~rc2/Lib/test/test_enum.py --- python3.11-3.11.0~rc1/Lib/test/test_enum.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/test/test_enum.py 2022-09-11 19:23:30.000000000 +0000 @@ -4337,10 +4337,16 @@ CYAN = 1 MAGENTA = 2 YELLOW = 3 + @bltns.property + def zeroth(self): + return 'zeroed %s' % self.name class CheckedColor(Enum): CYAN = 1 MAGENTA = 2 YELLOW = 3 + @bltns.property + def zeroth(self): + return 'zeroed %s' % self.name self.assertTrue(_test_simple_enum(CheckedColor, SimpleColor) is None) SimpleColor.MAGENTA._value_ = 9 self.assertRaisesRegex( diff -Nru python3.11-3.11.0~rc1/Lib/test/test_exceptions.py python3.11-3.11.0~rc2/Lib/test/test_exceptions.py --- python3.11-3.11.0~rc1/Lib/test/test_exceptions.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/test/test_exceptions.py 2022-09-11 19:23:30.000000000 +0000 @@ -2058,6 +2058,11 @@ except AttributeError as exc: self.assertEqual("bluch", exc.name) self.assertEqual(obj, exc.obj) + try: + object.__getattribute__(obj, "bluch") + except AttributeError as exc: + self.assertEqual("bluch", exc.name) + self.assertEqual(obj, exc.obj) def test_getattr_has_name_and_obj_for_method(self): class A: diff -Nru python3.11-3.11.0~rc1/Lib/test/test_float.py python3.11-3.11.0~rc2/Lib/test/test_float.py --- python3.11-3.11.0~rc1/Lib/test/test_float.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/test/test_float.py 2022-09-11 19:23:30.000000000 +0000 @@ -138,6 +138,10 @@ check('123\xbd') check(' 123 456 ') check(b' 123 456 ') + # all whitespace (cf. https://github.com/python/cpython/issues/95605) + check('') + check(' ') + check('\t \n') # non-ascii digits (error came from non-digit '!') check('\u0663\u0661\u0664!') diff -Nru python3.11-3.11.0~rc1/Lib/test/test_frame.py python3.11-3.11.0~rc2/Lib/test/test_frame.py --- python3.11-3.11.0~rc1/Lib/test/test_frame.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/test/test_frame.py 2022-09-11 19:23:30.000000000 +0000 @@ -235,6 +235,28 @@ r"^$" % (file_repr, offset + 5)) +class TestIncompleteFrameAreInvisible(unittest.TestCase): + + def test_issue95818(self): + #See GH-95818 for details + import gc + self.addCleanup(gc.set_threshold, *gc.get_threshold()) + + gc.set_threshold(1,1,1) + class GCHello: + def __del__(self): + print("Destroyed from gc") + + def gen(): + yield + + fd = open(__file__) + l = [fd, GCHello()] + l.append(l) + del fd + del l + gen() + if __name__ == "__main__": unittest.main() diff -Nru python3.11-3.11.0~rc1/Lib/test/test_grp.py python3.11-3.11.0~rc2/Lib/test/test_grp.py --- python3.11-3.11.0~rc1/Lib/test/test_grp.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/test/test_grp.py 2022-09-11 19:23:30.000000000 +0000 @@ -49,10 +49,12 @@ def test_errors(self): self.assertRaises(TypeError, grp.getgrgid) + self.assertRaises(TypeError, grp.getgrgid, 3.14) self.assertRaises(TypeError, grp.getgrnam) + self.assertRaises(TypeError, grp.getgrnam, 42) self.assertRaises(TypeError, grp.getgrall, 42) # embedded null character - self.assertRaises(ValueError, grp.getgrnam, 'a\x00b') + self.assertRaisesRegex(ValueError, 'null', grp.getgrnam, 'a\x00b') # try to get some errors bynames = {} diff -Nru python3.11-3.11.0~rc1/Lib/test/test_int.py python3.11-3.11.0~rc2/Lib/test/test_int.py --- python3.11-3.11.0~rc1/Lib/test/test_int.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/test/test_int.py 2022-09-11 19:23:30.000000000 +0000 @@ -1,4 +1,5 @@ import sys +import time import unittest from test import support @@ -577,5 +578,202 @@ self.assertEqual(int('1_2_3_4_5_6_7', 32), 1144132807) +class IntStrDigitLimitsTests(unittest.TestCase): + + int_class = int # Override this in subclasses to reuse the suite. + + def setUp(self): + super().setUp() + self._previous_limit = sys.get_int_max_str_digits() + sys.set_int_max_str_digits(2048) + + def tearDown(self): + sys.set_int_max_str_digits(self._previous_limit) + super().tearDown() + + def test_disabled_limit(self): + self.assertGreater(sys.get_int_max_str_digits(), 0) + self.assertLess(sys.get_int_max_str_digits(), 20_000) + with support.adjust_int_max_str_digits(0): + self.assertEqual(sys.get_int_max_str_digits(), 0) + i = self.int_class('1' * 20_000) + str(i) + self.assertGreater(sys.get_int_max_str_digits(), 0) + + def test_max_str_digits_edge_cases(self): + """Ignore the +/- sign and space padding.""" + int_class = self.int_class + maxdigits = sys.get_int_max_str_digits() + + int_class('1' * maxdigits) + int_class(' ' + '1' * maxdigits) + int_class('1' * maxdigits + ' ') + int_class('+' + '1' * maxdigits) + int_class('-' + '1' * maxdigits) + self.assertEqual(len(str(10 ** (maxdigits - 1))), maxdigits) + + def check(self, i, base=None): + with self.assertRaises(ValueError): + if base is None: + self.int_class(i) + else: + self.int_class(i, base) + + def test_max_str_digits(self): + maxdigits = sys.get_int_max_str_digits() + + self.check('1' * (maxdigits + 1)) + self.check(' ' + '1' * (maxdigits + 1)) + self.check('1' * (maxdigits + 1) + ' ') + self.check('+' + '1' * (maxdigits + 1)) + self.check('-' + '1' * (maxdigits + 1)) + self.check('1' * (maxdigits + 1)) + + i = 10 ** maxdigits + with self.assertRaises(ValueError): + str(i) + + def test_denial_of_service_prevented_int_to_str(self): + """Regression test: ensure we fail before performing O(N**2) work.""" + maxdigits = sys.get_int_max_str_digits() + assert maxdigits < 50_000, maxdigits # A test prerequisite. + get_time = time.process_time + if get_time() <= 0: # some platforms like WASM lack process_time() + get_time = time.monotonic + + huge_int = int(f'0x{"c"*65_000}', base=16) # 78268 decimal digits. + digits = 78_268 + with support.adjust_int_max_str_digits(digits): + start = get_time() + huge_decimal = str(huge_int) + seconds_to_convert = get_time() - start + self.assertEqual(len(huge_decimal), digits) + # Ensuring that we chose a slow enough conversion to measure. + # It takes 0.1 seconds on a Zen based cloud VM in an opt build. + # Some OSes have a low res 1/64s timer, skip if hard to measure. + if seconds_to_convert < 1/64: + raise unittest.SkipTest('"slow" conversion took only ' + f'{seconds_to_convert} seconds.') + + # We test with the limit almost at the size needed to check performance. + # The performant limit check is slightly fuzzy, give it a some room. + with support.adjust_int_max_str_digits(int(.995 * digits)): + with self.assertRaises(ValueError) as err: + start = get_time() + str(huge_int) + seconds_to_fail_huge = get_time() - start + self.assertIn('conversion', str(err.exception)) + self.assertLessEqual(seconds_to_fail_huge, seconds_to_convert/2) + + # Now we test that a conversion that would take 30x as long also fails + # in a similarly fast fashion. + extra_huge_int = int(f'0x{"c"*500_000}', base=16) # 602060 digits. + with self.assertRaises(ValueError) as err: + start = get_time() + # If not limited, 8 seconds said Zen based cloud VM. + str(extra_huge_int) + seconds_to_fail_extra_huge = get_time() - start + self.assertIn('conversion', str(err.exception)) + self.assertLess(seconds_to_fail_extra_huge, seconds_to_convert/2) + + def test_denial_of_service_prevented_str_to_int(self): + """Regression test: ensure we fail before performing O(N**2) work.""" + maxdigits = sys.get_int_max_str_digits() + assert maxdigits < 100_000, maxdigits # A test prerequisite. + get_time = time.process_time + if get_time() <= 0: # some platforms like WASM lack process_time() + get_time = time.monotonic + + digits = 133700 + huge = '8'*digits + with support.adjust_int_max_str_digits(digits): + start = get_time() + int(huge) + seconds_to_convert = get_time() - start + # Ensuring that we chose a slow enough conversion to measure. + # It takes 0.1 seconds on a Zen based cloud VM in an opt build. + # Some OSes have a low res 1/64s timer, skip if hard to measure. + if seconds_to_convert < 1/64: + raise unittest.SkipTest('"slow" conversion took only ' + f'{seconds_to_convert} seconds.') + + with support.adjust_int_max_str_digits(digits - 1): + with self.assertRaises(ValueError) as err: + start = get_time() + int(huge) + seconds_to_fail_huge = get_time() - start + self.assertIn('conversion', str(err.exception)) + self.assertLessEqual(seconds_to_fail_huge, seconds_to_convert/2) + + # Now we test that a conversion that would take 30x as long also fails + # in a similarly fast fashion. + extra_huge = '7'*1_200_000 + with self.assertRaises(ValueError) as err: + start = get_time() + # If not limited, 8 seconds in the Zen based cloud VM. + int(extra_huge) + seconds_to_fail_extra_huge = get_time() - start + self.assertIn('conversion', str(err.exception)) + self.assertLessEqual(seconds_to_fail_extra_huge, seconds_to_convert/2) + + def test_power_of_two_bases_unlimited(self): + """The limit does not apply to power of 2 bases.""" + maxdigits = sys.get_int_max_str_digits() + + for base in (2, 4, 8, 16, 32): + with self.subTest(base=base): + self.int_class('1' * (maxdigits + 1), base) + assert maxdigits < 100_000 + self.int_class('1' * 100_000, base) + + def test_underscores_ignored(self): + maxdigits = sys.get_int_max_str_digits() + + triples = maxdigits // 3 + s = '111' * triples + s_ = '1_11' * triples + self.int_class(s) # succeeds + self.int_class(s_) # succeeds + self.check(f'{s}111') + self.check(f'{s_}_111') + + def test_sign_not_counted(self): + int_class = self.int_class + max_digits = sys.get_int_max_str_digits() + s = '5' * max_digits + i = int_class(s) + pos_i = int_class(f'+{s}') + assert i == pos_i + neg_i = int_class(f'-{s}') + assert -pos_i == neg_i + str(pos_i) + str(neg_i) + + def _other_base_helper(self, base): + int_class = self.int_class + max_digits = sys.get_int_max_str_digits() + s = '2' * max_digits + i = int_class(s, base) + if base > 10: + with self.assertRaises(ValueError): + str(i) + elif base < 10: + str(i) + with self.assertRaises(ValueError) as err: + int_class(f'{s}1', base) + + def test_int_from_other_bases(self): + base = 3 + with self.subTest(base=base): + self._other_base_helper(base) + base = 36 + with self.subTest(base=base): + self._other_base_helper(base) + + +class IntSubclassStrDigitLimitsTests(IntStrDigitLimitsTests): + int_class = IntSubclass + + if __name__ == "__main__": unittest.main() diff -Nru python3.11-3.11.0~rc1/Lib/test/test_json/test_decode.py python3.11-3.11.0~rc2/Lib/test/test_json/test_decode.py --- python3.11-3.11.0~rc1/Lib/test/test_json/test_decode.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/test/test_json/test_decode.py 2022-09-11 19:23:30.000000000 +0000 @@ -2,6 +2,7 @@ from io import StringIO from collections import OrderedDict from test.test_json import PyTest, CTest +from test import support class TestDecode: @@ -95,5 +96,13 @@ d = self.json.JSONDecoder() self.assertRaises(ValueError, d.raw_decode, 'a'*42, -50000) + def test_limit_int(self): + maxdigits = 5000 + with support.adjust_int_max_str_digits(maxdigits): + self.loads('1' * maxdigits) + with self.assertRaises(ValueError): + self.loads('1' * (maxdigits + 1)) + + class TestPyDecode(TestDecode, PyTest): pass class TestCDecode(TestDecode, CTest): pass diff -Nru python3.11-3.11.0~rc1/Lib/test/test_launcher.py python3.11-3.11.0~rc2/Lib/test/test_launcher.py --- python3.11-3.11.0~rc1/Lib/test/test_launcher.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/test/test_launcher.py 2022-09-11 19:23:30.000000000 +0000 @@ -239,9 +239,11 @@ return data def py_ini(self, content): - if not self.py_exe: - self.py_exe = self.find_py() - return PreservePyIni(self.py_exe.with_name("py.ini"), content) + local_appdata = os.environ.get("LOCALAPPDATA") + if not local_appdata: + raise unittest.SkipTest("LOCALAPPDATA environment variable is " + "missing or empty") + return PreservePyIni(Path(local_appdata) / "py.ini", content) @contextlib.contextmanager def script(self, content, encoding="utf-8"): @@ -557,6 +559,13 @@ self.assertEqual("3.100", data["SearchInfo.tag"]) self.assertEqual(f'X.Y.exe -prearg "{script}" -postarg', data["stdout"].strip()) + def test_py_handle_64_in_ini(self): + with self.py_ini("\n".join(["[defaults]", "python=3.999-64"])): + # Expect this to fail, but should get oldStyleTag flipped on + data = self.run_py([], allow_fail=True, expect_returncode=103) + self.assertEqual("3.999-64", data["SearchInfo.tag"]) + self.assertEqual("True", data["SearchInfo.oldStyleTag"]) + def test_search_path(self): stem = Path(sys.executable).stem with self.py_ini(TEST_PY_COMMANDS): diff -Nru python3.11-3.11.0~rc1/Lib/test/test_logging.py python3.11-3.11.0~rc2/Lib/test/test_logging.py --- python3.11-3.11.0~rc1/Lib/test/test_logging.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/test/test_logging.py 2022-09-11 19:23:30.000000000 +0000 @@ -1,4 +1,4 @@ -# Copyright 2001-2021 by Vinay Sajip. All Rights Reserved. +# Copyright 2001-2022 by Vinay Sajip. All Rights Reserved. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose and without fee is hereby granted, @@ -16,7 +16,7 @@ """Test harness for the logging module. Run all tests. -Copyright (C) 2001-2021 Vinay Sajip. All Rights Reserved. +Copyright (C) 2001-2022 Vinay Sajip. All Rights Reserved. """ import logging @@ -3504,6 +3504,35 @@ {"version": 1, "root": {"level": "DEBUG", "filters": [filter_]}} ) + def test_90195(self): + # See gh-90195 + config = { + 'version': 1, + 'disable_existing_loggers': False, + 'handlers': { + 'console': { + 'level': 'DEBUG', + 'class': 'logging.StreamHandler', + }, + }, + 'loggers': { + 'a': { + 'level': 'DEBUG', + 'handlers': ['console'] + } + } + } + logger = logging.getLogger('a') + self.assertFalse(logger.disabled) + self.apply_config(config) + self.assertFalse(logger.disabled) + # Should disable all loggers ... + self.apply_config({'version': 1}) + self.assertTrue(logger.disabled) + del config['disable_existing_loggers'] + self.apply_config(config) + # Logger should be enabled, since explicitly mentioned + self.assertFalse(logger.disabled) class ManagerTest(BaseTest): def test_manager_loggerclass(self): @@ -4088,6 +4117,14 @@ f.converter = time.gmtime self.assertEqual(f.formatTime(r), '21/04/1993 08:03:00') + def test_issue_89047(self): + f = logging.Formatter(fmt='{asctime}.{msecs:03.0f} {message}', style='{', datefmt="%Y-%m-%d %H:%M:%S") + for i in range(2500): + time.sleep(0.0004) + r = logging.makeLogRecord({'msg': 'Message %d' % (i + 1)}) + s = f.format(r) + self.assertNotIn('.1000', s) + class TestBufferingFormatter(logging.BufferingFormatter): def formatHeader(self, records): diff -Nru python3.11-3.11.0~rc1/Lib/test/test_minidom.py python3.11-3.11.0~rc2/Lib/test/test_minidom.py --- python3.11-3.11.0~rc1/Lib/test/test_minidom.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/test/test_minidom.py 2022-09-11 19:23:30.000000000 +0000 @@ -9,7 +9,7 @@ import pyexpat import xml.dom.minidom -from xml.dom.minidom import parse, Node, Document, parseString +from xml.dom.minidom import parse, Attr, Node, Document, parseString from xml.dom.minidom import getDOMImplementation from xml.parsers.expat import ExpatError @@ -77,6 +77,20 @@ dom.unlink() self.confirm(isinstance(dom, Document)) + def testAttrModeSetsParamsAsAttrs(self): + attr = Attr("qName", "namespaceURI", "localName", "prefix") + self.assertEqual(attr.name, "qName") + self.assertEqual(attr.namespaceURI, "namespaceURI") + self.assertEqual(attr.prefix, "prefix") + self.assertEqual(attr.localName, "localName") + + def testAttrModeSetsNonOptionalAttrs(self): + attr = Attr("qName", "namespaceURI", None, "prefix") + self.assertEqual(attr.name, "qName") + self.assertEqual(attr.namespaceURI, "namespaceURI") + self.assertEqual(attr.prefix, "prefix") + self.assertEqual(attr.localName, attr.name) + def testGetElementsByTagName(self): dom = parse(tstfile) self.confirm(dom.getElementsByTagName("LI") == \ diff -Nru python3.11-3.11.0~rc1/Lib/test/test_numeric_tower.py python3.11-3.11.0~rc2/Lib/test/test_numeric_tower.py --- python3.11-3.11.0~rc1/Lib/test/test_numeric_tower.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/test/test_numeric_tower.py 2022-09-11 19:23:30.000000000 +0000 @@ -14,6 +14,27 @@ _PyHASH_MODULUS = sys.hash_info.modulus _PyHASH_INF = sys.hash_info.inf + +class DummyIntegral(int): + """Dummy Integral class to test conversion of the Rational to float.""" + + def __mul__(self, other): + return DummyIntegral(super().__mul__(other)) + __rmul__ = __mul__ + + def __truediv__(self, other): + return NotImplemented + __rtruediv__ = __truediv__ + + @property + def numerator(self): + return DummyIntegral(self) + + @property + def denominator(self): + return DummyIntegral(1) + + class HashTest(unittest.TestCase): def check_equal_hash(self, x, y): # check both that x and y are equal and that their hashes are equal @@ -121,6 +142,13 @@ self.assertEqual(hash(F(7*_PyHASH_MODULUS, 1)), 0) self.assertEqual(hash(F(-_PyHASH_MODULUS, 1)), 0) + # The numbers ABC doesn't enforce that the "true" division + # of integers produces a float. This tests that the + # Rational.__float__() method has required type conversions. + x = F(DummyIntegral(1), DummyIntegral(2), _normalize=False) + self.assertRaises(TypeError, lambda: x.numerator/x.denominator) + self.assertEqual(float(x), 0.5) + def test_hash_normalization(self): # Test for a bug encountered while changing long_hash. # diff -Nru python3.11-3.11.0~rc1/Lib/test/test_pwd.py python3.11-3.11.0~rc2/Lib/test/test_pwd.py --- python3.11-3.11.0~rc1/Lib/test/test_pwd.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/test/test_pwd.py 2022-09-11 19:23:30.000000000 +0000 @@ -59,6 +59,8 @@ self.assertRaises(TypeError, pwd.getpwnam) self.assertRaises(TypeError, pwd.getpwnam, 42) self.assertRaises(TypeError, pwd.getpwall, 42) + # embedded null character + self.assertRaisesRegex(ValueError, 'null', pwd.getpwnam, 'a\x00b') # try to get some errors bynames = {} diff -Nru python3.11-3.11.0~rc1/Lib/test/test_pyclbr.py python3.11-3.11.0~rc2/Lib/test/test_pyclbr.py --- python3.11-3.11.0~rc1/Lib/test/test_pyclbr.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/test/test_pyclbr.py 2022-09-11 19:23:30.000000000 +0000 @@ -229,7 +229,7 @@ cm( 'pdb', # pyclbr does not handle elegantly `typing` or properties - ignore=('Union', 'ModuleTarget', 'ScriptTarget'), + ignore=('Union', '_ModuleTarget', '_ScriptTarget'), ) cm('pydoc', ignore=('input', 'output',)) # properties diff -Nru python3.11-3.11.0~rc1/Lib/test/test_socket.py python3.11-3.11.0~rc2/Lib/test/test_socket.py --- python3.11-3.11.0~rc1/Lib/test/test_socket.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/test/test_socket.py 2022-09-11 19:23:30.000000000 +0000 @@ -1389,10 +1389,21 @@ def testSockName(self): # Testing getsockname() - port = socket_helper.find_unused_port() sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.addCleanup(sock.close) - sock.bind(("0.0.0.0", port)) + + # Since find_unused_port() is inherently subject to race conditions, we + # call it a couple times if necessary. + for i in itertools.count(): + port = socket_helper.find_unused_port() + try: + sock.bind(("0.0.0.0", port)) + except OSError as e: + if e.errno != errno.EADDRINUSE or i == 5: + raise + else: + break + name = sock.getsockname() # XXX(nnorwitz): http://tinyurl.com/os5jz seems to indicate # it reasonable to get the host's addr in addition to 0.0.0.0. diff -Nru python3.11-3.11.0~rc1/Lib/test/test_source_encoding.py python3.11-3.11.0~rc2/Lib/test/test_source_encoding.py --- python3.11-3.11.0~rc1/Lib/test/test_source_encoding.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/test/test_source_encoding.py 2022-09-11 19:23:30.000000000 +0000 @@ -148,6 +148,18 @@ self.assertTrue(c.exception.args[0].startswith(expected), msg=c.exception.args[0]) + def test_file_parse_error_multiline(self): + # gh96611: + with open(TESTFN, "wb") as fd: + fd.write(b'print("""\n\xb1""")\n') + + try: + retcode, stdout, stderr = script_helper.assert_python_failure(TESTFN) + + self.assertGreater(retcode, 0) + self.assertIn(b"Non-UTF-8 code starting with '\\xb1'", stderr) + finally: + os.unlink(TESTFN) class AbstractSourceEncodingTest: @@ -224,6 +236,74 @@ out = self.check_script_output(src, br"'\n\n\n'") +class UTF8ValidatorTest(unittest.TestCase): + @unittest.skipIf(not sys.platform.startswith("linux"), + "Too slow to run on non-Linux platforms") + def test_invalid_utf8(self): + # This is a port of test_utf8_decode_invalid_sequences in + # test_unicode.py to exercise the separate utf8 validator in + # Parser/tokenizer.c used when reading source files. + + # That file is written using low-level C file I/O, so the only way to + # test it is to write actual files to disk. + + # Each example is put inside a string at the top of the file so + # it's an otherwise valid Python source file. Put some newlines + # beforehand so we can assert that the error is reported on the + # correct line. + template = b'\n\n\n"%s"\n' + + fn = TESTFN + self.addCleanup(unlink, fn) + + def check(content): + with open(fn, 'wb') as fp: + fp.write(template % content) + rc, stdout, stderr = script_helper.assert_python_failure(fn) + # We want to assert that the python subprocess failed gracefully, + # not via a signal. + self.assertGreaterEqual(rc, 1) + self.assertIn(b"Non-UTF-8 code starting with", stderr) + self.assertIn(b"on line 4", stderr) + + # continuation bytes in a sequence of 2, 3, or 4 bytes + continuation_bytes = [bytes([x]) for x in range(0x80, 0xC0)] + # start bytes of a 2-byte sequence equivalent to code points < 0x7F + invalid_2B_seq_start_bytes = [bytes([x]) for x in range(0xC0, 0xC2)] + # start bytes of a 4-byte sequence equivalent to code points > 0x10FFFF + invalid_4B_seq_start_bytes = [bytes([x]) for x in range(0xF5, 0xF8)] + invalid_start_bytes = ( + continuation_bytes + invalid_2B_seq_start_bytes + + invalid_4B_seq_start_bytes + [bytes([x]) for x in range(0xF7, 0x100)] + ) + + for byte in invalid_start_bytes: + check(byte) + + for sb in invalid_2B_seq_start_bytes: + for cb in continuation_bytes: + check(sb + cb) + + for sb in invalid_4B_seq_start_bytes: + for cb1 in continuation_bytes[:3]: + for cb3 in continuation_bytes[:3]: + check(sb+cb1+b'\x80'+cb3) + + for cb in [bytes([x]) for x in range(0x80, 0xA0)]: + check(b'\xE0'+cb+b'\x80') + check(b'\xE0'+cb+b'\xBF') + # surrogates + for cb in [bytes([x]) for x in range(0xA0, 0xC0)]: + check(b'\xED'+cb+b'\x80') + check(b'\xED'+cb+b'\xBF') + for cb in [bytes([x]) for x in range(0x80, 0x90)]: + check(b'\xF0'+cb+b'\x80\x80') + check(b'\xF0'+cb+b'\xBF\xBF') + for cb in [bytes([x]) for x in range(0x90, 0xC0)]: + check(b'\xF4'+cb+b'\x80\x80') + check(b'\xF4'+cb+b'\xBF\xBF') + + class BytesSourceEncodingTest(AbstractSourceEncodingTest, unittest.TestCase): def check_script_output(self, src, expected): diff -Nru python3.11-3.11.0~rc1/Lib/test/test_sys.py python3.11-3.11.0~rc2/Lib/test/test_sys.py --- python3.11-3.11.0~rc1/Lib/test/test_sys.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/test/test_sys.py 2022-09-11 19:23:30.000000000 +0000 @@ -550,11 +550,17 @@ self.assertIsInstance(sys.executable, str) self.assertEqual(len(sys.float_info), 11) self.assertEqual(sys.float_info.radix, 2) - self.assertEqual(len(sys.int_info), 2) + self.assertEqual(len(sys.int_info), 4) self.assertTrue(sys.int_info.bits_per_digit % 5 == 0) self.assertTrue(sys.int_info.sizeof_digit >= 1) + self.assertGreaterEqual(sys.int_info.default_max_str_digits, 500) + self.assertGreaterEqual(sys.int_info.str_digits_check_threshold, 100) + self.assertGreater(sys.int_info.default_max_str_digits, + sys.int_info.str_digits_check_threshold) self.assertEqual(type(sys.int_info.bits_per_digit), int) self.assertEqual(type(sys.int_info.sizeof_digit), int) + self.assertIsInstance(sys.int_info.default_max_str_digits, int) + self.assertIsInstance(sys.int_info.str_digits_check_threshold, int) self.assertIsInstance(sys.hexversion, int) self.assertEqual(len(sys.hash_info), 9) @@ -628,6 +634,14 @@ self.assertEqual(len(info), 3) self.assertIn(info.name, ('nt', 'pthread', 'pthread-stubs', 'solaris', None)) self.assertIn(info.lock, ('semaphore', 'mutex+cond', None)) + if sys.platform.startswith(("linux", "freebsd")): + self.assertEqual(info.name, "pthread") + elif sys.platform == "win32": + self.assertEqual(info.name, "nt") + elif sys.platform == "emscripten": + self.assertIn(info.name, {"pthread", "pthread-stubs"}) + elif sys.platform == "wasi": + self.assertEqual(info.name, "pthread-stubs") @unittest.skipUnless(support.is_emscripten, "only available on Emscripten") def test_emscripten_info(self): @@ -669,7 +683,7 @@ "dont_write_bytecode", "no_user_site", "no_site", "ignore_environment", "verbose", "bytes_warning", "quiet", "hash_randomization", "isolated", "dev_mode", "utf8_mode", - "warn_default_encoding", "safe_path") + "warn_default_encoding", "safe_path", "int_max_str_digits") for attr in attrs: self.assertTrue(hasattr(sys.flags, attr), attr) attr_type = bool if attr in ("dev_mode", "safe_path") else int diff -Nru python3.11-3.11.0~rc1/Lib/test/test_tracemalloc.py python3.11-3.11.0~rc2/Lib/test/test_tracemalloc.py --- python3.11-3.11.0~rc1/Lib/test/test_tracemalloc.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/test/test_tracemalloc.py 2022-09-11 19:23:30.000000000 +0000 @@ -360,6 +360,20 @@ else: support.wait_process(pid, exitcode=0) + def test_no_incomplete_frames(self): + tracemalloc.stop() + tracemalloc.start(8) + + def f(x): + def g(): + return x + return g + + obj = f(0).__closure__[0] + traceback = tracemalloc.get_object_traceback(obj) + self.assertIn("test_tracemalloc", traceback[-1].filename) + self.assertNotIn("test_tracemalloc", traceback[-2].filename) + class TestSnapshot(unittest.TestCase): maxDiff = 4000 diff -Nru python3.11-3.11.0~rc1/Lib/test/test_type_comments.py python3.11-3.11.0~rc2/Lib/test/test_type_comments.py --- python3.11-3.11.0~rc1/Lib/test/test_type_comments.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/test/test_type_comments.py 2022-09-11 19:23:30.000000000 +0000 @@ -323,7 +323,7 @@ self.assertEqual(tree.type_ignores, []) def test_longargs(self): - for tree in self.parse_all(longargs): + for tree in self.parse_all(longargs, minver=8): for t in tree.body: # The expected args are encoded in the function name todo = set(t.name[1:]) diff -Nru python3.11-3.11.0~rc1/Lib/test/test_typing.py python3.11-3.11.0~rc2/Lib/test/test_typing.py --- python3.11-3.11.0~rc1/Lib/test/test_typing.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/test/test_typing.py 2022-09-11 19:23:30.000000000 +0000 @@ -591,6 +591,7 @@ def test_one_parameter(self): T = TypeVar('T') Ts = TypeVarTuple('Ts') + Ts2 = TypeVarTuple('Ts2') class C(Generic[T]): pass @@ -616,6 +617,8 @@ # Should definitely raise TypeError: list only takes one argument. ('list[T, *tuple_type[int, ...]]', '[int]', 'list[int, *tuple_type[int, ...]]'), ('List[T, *tuple_type[int, ...]]', '[int]', 'TypeError'), + # Should raise, because more than one `TypeVarTuple` is not supported. + ('generic[*Ts, *Ts2]', '[int]', 'TypeError'), ] for alias_template, args_template, expected_template in tests: diff -Nru python3.11-3.11.0~rc1/Lib/test/test_unparse.py python3.11-3.11.0~rc2/Lib/test/test_unparse.py --- python3.11-3.11.0~rc1/Lib/test/test_unparse.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/test/test_unparse.py 2022-09-11 19:23:30.000000000 +0000 @@ -422,6 +422,12 @@ def test_invalid_yield_from(self): self.check_invalid(ast.YieldFrom(value=None)) + def test_import_from_level_none(self): + tree = ast.ImportFrom(module='mod', names=[ast.alias(name='x')]) + self.assertEqual(ast.unparse(tree), "from mod import x") + tree = ast.ImportFrom(module='mod', names=[ast.alias(name='x')], level=None) + self.assertEqual(ast.unparse(tree), "from mod import x") + def test_docstrings(self): docstrings = ( 'this ends with double quote"', diff -Nru python3.11-3.11.0~rc1/Lib/test/test_xmlrpc.py python3.11-3.11.0~rc2/Lib/test/test_xmlrpc.py --- python3.11-3.11.0~rc1/Lib/test/test_xmlrpc.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/test/test_xmlrpc.py 2022-09-11 19:23:30.000000000 +0000 @@ -289,6 +289,16 @@ check('9876543210.0123456789', decimal.Decimal('9876543210.0123456789')) + def test_limit_int(self): + check = self.check_loads + maxdigits = 5000 + with support.adjust_int_max_str_digits(maxdigits): + s = '1' * (maxdigits + 1) + with self.assertRaises(ValueError): + check(f'{s}', None) + with self.assertRaises(ValueError): + check(f'{s}', None) + def test_get_host_info(self): # see bug #3613, this raised a TypeError transp = xmlrpc.client.Transport() diff -Nru python3.11-3.11.0~rc1/Lib/threading.py python3.11-3.11.0~rc2/Lib/threading.py --- python3.11-3.11.0~rc1/Lib/threading.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/threading.py 2022-09-11 19:23:30.000000000 +0000 @@ -572,7 +572,7 @@ def isSet(self): """Return true if and only if the internal flag is true. - This method is deprecated, use notify_all() instead. + This method is deprecated, use is_set() instead. """ import warnings diff -Nru python3.11-3.11.0~rc1/Lib/typing.py python3.11-3.11.0~rc2/Lib/typing.py --- python3.11-3.11.0~rc1/Lib/typing.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/typing.py 2022-09-11 19:23:30.000000000 +0000 @@ -1071,7 +1071,7 @@ def __typing_prepare_subst__(self, alias, args): params = alias.__parameters__ typevartuple_index = params.index(self) - for param in enumerate(params[typevartuple_index + 1:]): + for param in params[typevartuple_index + 1:]: if isinstance(param, TypeVarTuple): raise TypeError(f"More than one TypeVarTuple parameter in {alias}") diff -Nru python3.11-3.11.0~rc1/Lib/unittest/async_case.py python3.11-3.11.0~rc2/Lib/unittest/async_case.py --- python3.11-3.11.0~rc1/Lib/unittest/async_case.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/unittest/async_case.py 2022-09-11 19:23:30.000000000 +0000 @@ -79,6 +79,10 @@ return result def _callSetUp(self): + # Force loop to be initialized and set as the current loop + # so that setUp functions can use get_event_loop() and get the + # correct loop instance. + self._asyncioRunner.get_loop() self._asyncioTestContext.run(self.setUp) self._callAsync(self.asyncSetUp) diff -Nru python3.11-3.11.0~rc1/Lib/unittest/test/test_async_case.py python3.11-3.11.0~rc2/Lib/unittest/test/test_async_case.py --- python3.11-3.11.0~rc1/Lib/unittest/test/test_async_case.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/unittest/test/test_async_case.py 2022-09-11 19:23:30.000000000 +0000 @@ -43,10 +43,10 @@ class TestAsyncCase(unittest.TestCase): maxDiff = None - def tearDown(self): + def setUp(self): # Ensure that IsolatedAsyncioTestCase instances are destroyed before # starting a new event loop - support.gc_collect() + self.addCleanup(support.gc_collect) def test_full_cycle(self): class Test(unittest.IsolatedAsyncioTestCase): @@ -151,6 +151,7 @@ events = [] test = Test("test_func") + self.addCleanup(test._tearDownAsyncioRunner) try: test.debug() except MyException: @@ -186,6 +187,7 @@ events = [] test = Test("test_func") + self.addCleanup(test._tearDownAsyncioRunner) try: test.debug() except MyException: @@ -221,6 +223,7 @@ events = [] test = Test("test_func") + self.addCleanup(test._tearDownAsyncioRunner) try: test.debug() except MyException: @@ -262,6 +265,7 @@ events = [] test = Test("test_func") + self.addCleanup(test._tearDownAsyncioRunner) try: test.debug() except MyException: @@ -424,6 +428,7 @@ events = [] test = Test("test_func") + self.addCleanup(test._tearDownAsyncioRunner) try: test.debug() except MyException: @@ -434,6 +439,21 @@ test.doCleanups() self.assertEqual(events, ['asyncSetUp', 'test', 'cleanup']) + def test_setup_get_event_loop(self): + # See https://github.com/python/cpython/issues/95736 + # Make sure the default event loop is not used + asyncio.set_event_loop(None) + + class TestCase1(unittest.IsolatedAsyncioTestCase): + def setUp(self): + asyncio.get_event_loop_policy().get_event_loop() + + async def test_demo1(self): + pass + + test = TestCase1('test_demo1') + result = test.run() + self.assertTrue(result.wasSuccessful()) if __name__ == "__main__": unittest.main() diff -Nru python3.11-3.11.0~rc1/Lib/xml/dom/minidom.py python3.11-3.11.0~rc2/Lib/xml/dom/minidom.py --- python3.11-3.11.0~rc1/Lib/xml/dom/minidom.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/xml/dom/minidom.py 2022-09-11 19:23:30.000000000 +0000 @@ -358,6 +358,8 @@ self._name = qName self.namespaceURI = namespaceURI self._prefix = prefix + if localName is not None: + self._localName = localName self.childNodes = NodeList() # Add the single child node that represents the value of the attr diff -Nru python3.11-3.11.0~rc1/Lib/zipfile.py python3.11-3.11.0~rc2/Lib/zipfile.py --- python3.11-3.11.0~rc1/Lib/zipfile.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Lib/zipfile.py 2022-09-11 19:23:30.000000000 +0000 @@ -480,7 +480,7 @@ def _encodeFilenameFlags(self): try: - return self.filename.encode('ascii'), self.flag_bits & ~_MASK_UTF_FILENAME + return self.filename.encode('ascii'), self.flag_bits except UnicodeEncodeError: return self.filename.encode('utf-8'), self.flag_bits | _MASK_UTF_FILENAME diff -Nru python3.11-3.11.0~rc1/Mac/BuildScript/build-installer.py python3.11-3.11.0~rc2/Mac/BuildScript/build-installer.py --- python3.11-3.11.0~rc1/Mac/BuildScript/build-installer.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Mac/BuildScript/build-installer.py 2022-09-11 19:23:30.000000000 +0000 @@ -246,9 +246,10 @@ result.extend([ dict( - name="OpenSSL 1.1.1n", - url="https://www.openssl.org/source/openssl-1.1.1n.tar.gz", - checksum='2aad5635f9bb338bc2c6b7d19cbc9676', + name="OpenSSL 1.1.1q", + url="https://www.openssl.org/source/openssl-1.1.1q.tar.gz", + checksum='d7939ce614029cdff0b6c20f0e2e5703158a489a72b2507b8bd51bf8c8fd10ca', + patches=['openssl1.1.1q-pr-18719.patch'], buildrecipe=build_universal_openssl, configure=None, install=None, @@ -796,10 +797,16 @@ print("Downloading %s"%(name,)) downloadURL(url, fname) print("Archive for %s stored as %s"%(name, fname)) + if len(checksum) == 32: + algo = 'md5' + elif len(checksum) == 64: + algo = 'sha256' + else: + raise ValueError(checksum) if os.system( - 'MD5=$(openssl md5 %s) ; test "${MD5##*= }" = "%s"' - % (shellQuote(fname), checksum) ): - fatal('MD5 checksum mismatch for file %s' % fname) + 'CHECKSUM=$(openssl %s %s) ; test "${CHECKSUM##*= }" = "%s"' + % (algo, shellQuote(fname), checksum) ): + fatal('%s checksum mismatch for file %s' % (algo, fname)) def build_universal_openssl(basedir, archList): """ diff -Nru python3.11-3.11.0~rc1/Mac/BuildScript/openssl1.1.1q-pr-18719.patch python3.11-3.11.0~rc2/Mac/BuildScript/openssl1.1.1q-pr-18719.patch --- python3.11-3.11.0~rc1/Mac/BuildScript/openssl1.1.1q-pr-18719.patch 1970-01-01 00:00:00.000000000 +0000 +++ python3.11-3.11.0~rc2/Mac/BuildScript/openssl1.1.1q-pr-18719.patch 2022-09-11 19:23:30.000000000 +0000 @@ -0,0 +1,17 @@ +https://github.com/openssl/openssl/commit/60f011f584d80447e86cae1d1bd3ae24bc13235b +This fixes a regression in 1.1.1q: + +test/v3ext.c:201:24: error: implicitly declaring library function 'memcmp' with type 'int (const void *, const void *, unsigned long)' [-Werror,-Wimplicit-function-declaration] + if (!TEST_true(memcmp(ip1->data, ip2->data, ip1->length) <= 0)) + +diff -Naur openssl-1.1.1q/test/v3ext.c openssl-1.1.1q-patched/test/v3ext.c +--- openssl-1.1.1q/test/v3ext.c 2022-07-05 09:08:33.000000000 +0000 ++++ openssl-1.1.1q-patched/test/v3ext.c 2022-09-05 16:54:45.740859256 +0000 +@@ -8,6 +8,7 @@ + */ + + #include ++#include + #include + #include + #include diff -Nru python3.11-3.11.0~rc1/Misc/ACKS python3.11-3.11.0~rc2/Misc/ACKS --- python3.11-3.11.0~rc1/Misc/ACKS 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Misc/ACKS 2022-09-11 19:23:30.000000000 +0000 @@ -1415,6 +1415,7 @@ Matheus Vieira Portela Davin Potts Guillaume Pratte +Pedro Pregueiro Florian Preinstorfer Alex Prengère Amrit Prem @@ -1437,6 +1438,7 @@ Jeffrey Rackauckas Jérôme Radix Burton Radons +Kirill (python273) R. Abhilash Raj Shorya Raj Ajith Ramachandran @@ -1980,6 +1982,7 @@ Darren Worrall Thomas Wouters Daniel Wozniak +Simon Wrede Marcin Niemira Wei Wu Heiko Wundram diff -Nru python3.11-3.11.0~rc1/Misc/NEWS python3.11-3.11.0~rc2/Misc/NEWS --- python3.11-3.11.0~rc1/Misc/NEWS 2022-08-05 14:59:15.000000000 +0000 +++ python3.11-3.11.0~rc2/Misc/NEWS 2022-09-11 19:37:33.000000000 +0000 @@ -2,6 +2,178 @@ Python News +++++++++++ +What's New in Python 3.11.0 release candidate 2? +================================================ + +*Release date: 2022-09-11* + +Security +-------- + +- gh-issue-95778: Converting between :class:`int` and :class:`str` in bases + other than 2 (binary), 4, 8 (octal), 16 (hexadecimal), or 32 such as base + 10 (decimal) now raises a :exc:`ValueError` if the number of digits in + string form is above a limit to avoid potential denial of service attacks + due to the algorithmic complexity. This is a mitigation for + `CVE-2020-10735 + `_. + + This new limit can be configured or disabled by environment variable, + command line flag, or :mod:`sys` APIs. See the :ref:`integer string + conversion length limitation ` documentation. The + default limit is 4300 digits in string form. + + Patch by Gregory P. Smith [Google] and Christian Heimes [Red Hat] with + feedback from Victor Stinner, Thomas Wouters, Steve Dower, Ned Deily, and + Mark Dickinson. + +Core and Builtins +----------------- + +- gh-issue-96678: Fix case of undefined behavior in ceval.c + +- gh-issue-96641: Do not expose ``KeyWrapper`` in :mod:`_functools`. + +- gh-issue-96636: Ensure that tracing, ``sys.setrace()``, is turned on + immediately. In pre-release versions of 3.11, some tracing events might + have been lost when turning on tracing in a ``__del__`` method or + interrupt. + +- gh-issue-96572: Fix use after free in trace refs build mode. Patch by + Kumar Aditya. + +- gh-issue-96611: When loading a file with invalid UTF-8 inside a multi-line + string, a correct SyntaxError is emitted. + +- gh-issue-96612: Make sure that incomplete frames do not show up in + tracemalloc traces. + +- gh-issue-96569: Remove two cases of undefined behavior, by adding NULL + checks. + +- gh-issue-96582: Fix possible ``NULL`` pointer dereference in + ``_PyThread_CurrentFrames``. Patch by Kumar Aditya. + +- gh-issue-96352: Fix :exc:`AttributeError` missing ``name`` and ``obj`` + attributes in :meth:`object.__getattribute__`. Patch by Philip Georgi. + +- gh-issue-96268: Loading a file with invalid UTF-8 will now report the + broken character at the correct location. + +- gh-issue-96187: Fixed a bug that caused ``_PyCode_GetExtra`` to return + garbage for negative indexes. Patch by Pablo Galindo + +- gh-issue-96071: Fix a deadlock in :c:func:`PyGILState_Ensure` when + allocating new thread state. Patch by Kumar Aditya. + +- gh-issue-96046: :c:func:`PyType_Ready` now initializes ``ht_cached_keys`` + and performs additional checks to ensure that type objects are properly + configured. This avoids crashes in 3rd party packages that don't use + regular API to create new types. + +- gh-issue-95818: Skip over incomplete frames in + :c:func:`PyThreadState_GetFrame`. + +- gh-issue-95876: Fix format string in + ``_PyPegen_raise_error_known_location`` that can lead to memory corruption + on some 64bit systems. The function was building a tuple with ``i`` (int) + instead of ``n`` (Py_ssize_t) for Py_ssize_t arguments. + +- gh-issue-95605: Fix misleading contents of error message when converting + an all-whitespace string to :class:`float`. + +- gh-issue-94996: :func:`ast.parse` will no longer parse function + definitions with positional-only params when passed ``feature_version`` + less than ``(3, 8)``. Patch by Shantanu Jain. + +Library +------- + +- gh-issue-96700: Fix incorrect error message in the :mod:`io` module. + +- gh-issue-96652: Fix the faulthandler implementation of + ``faulthandler.register(signal, chain=True)`` if the ``sigaction()`` + function is not available: don't call the previous signal handler if it's + NULL. Patch by Victor Stinner. + +- gh-issue-68163: Correct conversion of :class:`numbers.Rational`'s to + :class:`float`. + +- gh-issue-96385: Fix ``TypeVarTuple.__typing_prepare_subst__``. + ``TypeError`` was not raised when using more than one ``TypeVarTuple``, + like ``[*T, *V]`` in type alias substitutions. + +- gh-issue-90467: Fix :class:`asyncio.streams.StreamReaderProtocol` to keep + a strong reference to the created task, so that it's not garbage collected + +- gh-issue-96159: Fix a performance regression in logging + TimedRotatingFileHandler. Only check for special files when the rollover + time has passed. + +- gh-issue-96175: Fix unused ``localName`` parameter in the ``Attr`` class + in :mod:`xml.dom.minidom`. + +- gh-issue-96125: Fix incorrect condition that causes + ``sys.thread_info.name`` to be wrong on pthread platforms. + +- gh-issue-95463: Remove an incompatible change from :issue:`28080` that + caused a regression that ignored the utf8 in ``ZipInfo.flag_bits``. Patch + by Pablo Galindo. + +- gh-issue-95899: Fix :class:`asyncio.Runner` to call + :func:`asyncio.set_event_loop` only once to avoid calling + :meth:`~asyncio.AbstractChildWatcher.attach_loop` multiple times on child + watchers. Patch by Kumar Aditya. + +- gh-issue-95736: Fix :class:`unittest.IsolatedAsyncioTestCase` to set event + loop before calling setup functions. Patch by Kumar Aditya. + +- gh-issue-95704: When a task catches :exc:`asyncio.CancelledError` and + raises some other error, the other error should generally not silently be + suppressed. + +- gh-issue-95231: Fail gracefully if :data:`~errno.EPERM` or + :data:`~errno.ENOSYS` is raised when loading :mod:`crypt` methods. This + may happen when trying to load ``MD5`` on a Linux kernel with :abbr:`FIPS + (Federal Information Processing Standard)` enabled. + +- gh-issue-74116: Allow :meth:`asyncio.StreamWriter.drain` to be awaited + concurrently by multiple tasks. Patch by Kumar Aditya. + +- gh-issue-92986: Fix :func:`ast.unparse` when ``ImportFrom.level`` is None + +Documentation +------------- + +- gh-issue-96098: Improve discoverability of the higher level + concurrent.futures module by providing clearer links from the lower level + threading and multiprocessing modules. + +- gh-issue-95957: What's New 3.11 now has instructions for how to provide + compiler and linker flags for Tcl/Tk and OpenSSL on RHEL 7 and CentOS 7. + +Tests +----- + +- gh-issue-95243: Mitigate the inherent race condition from using + find_unused_port() in testSockName() by trying to find an unused port a + few times before failing. Patch by Ross Burton. + +Build +----- + +- gh-issue-94682: Build and test with OpenSSL 1.1.1q + +Windows +------- + +- gh-issue-96577: Fixes a potential buffer overrun in :mod:`msilib`. + +- gh-issue-96559: Fixes the Windows launcher not using the compatible + interpretation of default tags found in configuration files when no tag + was passed to the command. + + What's New in Python 3.11.0 release candidate 1? ================================================ @@ -2144,8 +2316,8 @@ length may be wrong. - bpo-45171: Fix handling of the ``stacklevel`` argument to logging - functions in the :mod:`logging` module so that it is consistent accross - all logging functions and, as advertised, similar to the ``stacklevel`` + functions in the :mod:`logging` module so that it is consistent across all + logging functions and, as advertised, similar to the ``stacklevel`` argument used in :meth:`~warnings.warn`. - bpo-24959: Fix bug where :mod:`unittest` sometimes drops frames from @@ -2726,8 +2898,8 @@ - bpo-46860: Respect `--with-suffix` when building on case-insensitive file systems. -- bpo-46656: Building Python now requires a C11 compiler without optional - C11 features. Patch by Victor Stinner. +- bpo-46656: Building Python now requires a C11 compiler. Optional C11 + features are not required. Patch by Victor Stinner. - bpo-46656: Building Python now requires support for floating point Not-a-Number (NaN): remove the ``Py_NO_NAN`` macro. Patch by by Victor diff -Nru python3.11-3.11.0~rc1/Misc/rhel7/openssl.pc python3.11-3.11.0~rc2/Misc/rhel7/openssl.pc --- python3.11-3.11.0~rc1/Misc/rhel7/openssl.pc 1970-01-01 00:00:00.000000000 +0000 +++ python3.11-3.11.0~rc2/Misc/rhel7/openssl.pc 2022-09-11 19:23:30.000000000 +0000 @@ -0,0 +1,3 @@ +Name: OpenSSL +Version: 1.1.1k +Requires: libssl11 libcrypto11 diff -Nru python3.11-3.11.0~rc1/Misc/rhel7/README.md python3.11-3.11.0~rc2/Misc/rhel7/README.md --- python3.11-3.11.0~rc1/Misc/rhel7/README.md 1970-01-01 00:00:00.000000000 +0000 +++ python3.11-3.11.0~rc2/Misc/rhel7/README.md 2022-09-11 19:23:30.000000000 +0000 @@ -0,0 +1,19 @@ +# pkg-config overrides for RHEL 7 and CentOS 7 + +RHEL 7 and CentOS 7 do not provide pkg-config `.pc` files for Tcl/Tk. The + OpenSSL 1.1.1 pkg-config file is named `openssl11.pc` and not picked up + by Python's `configure` script. + +To build Python with system Tcl/Tk libs and OpenSSL 1.1 package, first +install the developer packages and the `pkgconfig` package with `pkg-config` +command. + +```shell +sudo yum install pkgconfig 'tcl-devel >= 8.5.12' 'tk-devel >= 8.5.12' openssl11-devel +``` + +The run `configure` with `PKG_CONFIG_PATH` environment variable. + +```shell +PKG_CONFIG_PATH=Misc/rhel7 ./configure -C +``` diff -Nru python3.11-3.11.0~rc1/Misc/rhel7/tcl.pc python3.11-3.11.0~rc2/Misc/rhel7/tcl.pc --- python3.11-3.11.0~rc1/Misc/rhel7/tcl.pc 1970-01-01 00:00:00.000000000 +0000 +++ python3.11-3.11.0~rc2/Misc/rhel7/tcl.pc 2022-09-11 19:23:30.000000000 +0000 @@ -0,0 +1,4 @@ +Name: Tool Command Language +Version: 8.5.12 +Libs: -ltcl8.5 -ltclstub8.5 +# Libs.private: -ldl -lz -lpthread -lm diff -Nru python3.11-3.11.0~rc1/Misc/rhel7/tk.pc python3.11-3.11.0~rc2/Misc/rhel7/tk.pc --- python3.11-3.11.0~rc1/Misc/rhel7/tk.pc 1970-01-01 00:00:00.000000000 +0000 +++ python3.11-3.11.0~rc2/Misc/rhel7/tk.pc 2022-09-11 19:23:30.000000000 +0000 @@ -0,0 +1,5 @@ +Name: The Tk Toolkit +Version: 8.5.12 +Requires: tcl >= 8.5.12 +Libs: -ltk8.5 -ltkstub8.5 +# Libs.private: -lXft -lfontconfig -lfreetype -lfontconfig -lX11 diff -Nru python3.11-3.11.0~rc1/Modules/_asynciomodule.c python3.11-3.11.0~rc2/Modules/_asynciomodule.c --- python3.11-3.11.0~rc1/Modules/_asynciomodule.c 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Modules/_asynciomodule.c 2022-09-11 19:23:30.000000000 +0000 @@ -631,8 +631,6 @@ } else { exc = PyObject_CallOneArg(asyncio_CancelledError, msg); } - PyException_SetContext(exc, fut->fut_cancelled_exc); - Py_CLEAR(fut->fut_cancelled_exc); return exc; } @@ -640,6 +638,9 @@ future_set_cancelled_error(FutureObj *fut) { PyObject *exc = create_cancelled_error(fut); + if (exc == NULL) { + return; + } PyErr_SetObject(asyncio_CancelledError, exc); Py_DECREF(exc); } diff -Nru python3.11-3.11.0~rc1/Modules/atexitmodule.c python3.11-3.11.0~rc2/Modules/atexitmodule.c --- python3.11-3.11.0~rc1/Modules/atexitmodule.c 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Modules/atexitmodule.c 2022-09-11 19:23:30.000000000 +0000 @@ -185,7 +185,7 @@ \n\ Run all registered exit functions.\n\ \n\ -If a callaback raises an exception, it is logged with sys.unraisablehook."); +If a callback raises an exception, it is logged with sys.unraisablehook."); static PyObject * atexit_run_exitfuncs(PyObject *module, PyObject *unused) diff -Nru python3.11-3.11.0~rc1/Modules/faulthandler.c python3.11-3.11.0~rc2/Modules/faulthandler.c --- python3.11-3.11.0~rc1/Modules/faulthandler.c 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Modules/faulthandler.c 2022-09-11 19:23:30.000000000 +0000 @@ -862,7 +862,7 @@ errno = save_errno; } #else - if (user->chain) { + if (user->chain && user->previous != NULL) { errno = save_errno; /* call the previous signal handler */ user->previous(signum); diff -Nru python3.11-3.11.0~rc1/Modules/_functoolsmodule.c python3.11-3.11.0~rc2/Modules/_functoolsmodule.c --- python3.11-3.11.0~rc1/Modules/_functoolsmodule.c 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Modules/_functoolsmodule.c 2022-09-11 19:23:30.000000000 +0000 @@ -1471,9 +1471,8 @@ if (state->keyobject_type == NULL) { return -1; } - if (PyModule_AddType(module, state->keyobject_type) < 0) { - return -1; - } + // keyobject_type is used only internally. + // So we don't expose it in module namespace. state->lru_list_elem_type = (PyTypeObject *)PyType_FromModuleAndSpec( module, &lru_list_elem_type_spec, NULL); diff -Nru python3.11-3.11.0~rc1/Modules/_testcapimodule.c python3.11-3.11.0~rc2/Modules/_testcapimodule.c --- python3.11-3.11.0~rc1/Modules/_testcapimodule.c 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Modules/_testcapimodule.c 2022-09-11 19:23:30.000000000 +0000 @@ -2959,7 +2959,7 @@ month = PyDateTime_GET_MONTH(obj); day = PyDateTime_GET_DAY(obj); - return Py_BuildValue("(lll)", year, month, day); + return Py_BuildValue("(iii)", year, month, day); } static PyObject * @@ -2973,7 +2973,7 @@ microsecond = PyDateTime_DATE_GET_MICROSECOND(obj); PyObject *tzinfo = PyDateTime_DATE_GET_TZINFO(obj); - return Py_BuildValue("(llllO)", hour, minute, second, microsecond, tzinfo); + return Py_BuildValue("(iiiiO)", hour, minute, second, microsecond, tzinfo); } static PyObject * @@ -2987,7 +2987,7 @@ microsecond = PyDateTime_TIME_GET_MICROSECOND(obj); PyObject *tzinfo = PyDateTime_TIME_GET_TZINFO(obj); - return Py_BuildValue("(llllO)", hour, minute, second, microsecond, tzinfo); + return Py_BuildValue("(iiiiO)", hour, minute, second, microsecond, tzinfo); } static PyObject * @@ -2999,7 +2999,7 @@ seconds = PyDateTime_DELTA_GET_SECONDS(obj); microseconds = PyDateTime_DELTA_GET_MICROSECONDS(obj); - return Py_BuildValue("(lll)", days, seconds, microseconds); + return Py_BuildValue("(iii)", days, seconds, microseconds); } /* test_thread_state spawns a thread of its own, and that thread releases diff -Nru python3.11-3.11.0~rc1/Modules/_tracemalloc.c python3.11-3.11.0~rc2/Modules/_tracemalloc.c --- python3.11-3.11.0~rc1/Modules/_tracemalloc.c 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Modules/_tracemalloc.c 2022-09-11 19:23:30.000000000 +0000 @@ -400,7 +400,13 @@ } _PyInterpreterFrame *pyframe = tstate->cframe->current_frame; - for (; pyframe != NULL;) { + for (;;) { + while (pyframe && _PyFrame_IsIncomplete(pyframe)) { + pyframe = pyframe->previous; + } + if (pyframe == NULL) { + break; + } if (traceback->nframe < _Py_tracemalloc_config.max_nframe) { tracemalloc_get_frame(pyframe, &traceback->frames[traceback->nframe]); assert(traceback->frames[traceback->nframe].filename != NULL); @@ -410,8 +416,7 @@ traceback->total_nframe++; } - _PyInterpreterFrame *back = pyframe->previous; - pyframe = back; + pyframe = pyframe->previous; } } diff -Nru python3.11-3.11.0~rc1/Objects/codeobject.c python3.11-3.11.0~rc2/Objects/codeobject.c --- python3.11-3.11.0~rc1/Objects/codeobject.c 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Objects/codeobject.c 2022-09-11 19:23:30.000000000 +0000 @@ -1337,7 +1337,7 @@ PyCodeObject *o = (PyCodeObject*) code; _PyCodeObjectExtra *co_extra = (_PyCodeObjectExtra*) o->co_extra; - if (co_extra == NULL || co_extra->ce_size <= index) { + if (co_extra == NULL || index < 0 || co_extra->ce_size <= index) { *extra = NULL; return 0; } diff -Nru python3.11-3.11.0~rc1/Objects/exception_handling_notes.txt python3.11-3.11.0~rc2/Objects/exception_handling_notes.txt --- python3.11-3.11.0~rc1/Objects/exception_handling_notes.txt 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Objects/exception_handling_notes.txt 2022-09-11 19:23:30.000000000 +0000 @@ -43,33 +43,36 @@ In 3.11, the SETUP_FINALLY and POP_BLOCK are eliminated, replaced with a table to determine where to jump to when an exception is raised. - 2 0 NOP + 1 0 RESUME 0 - 3 2 LOAD_GLOBAL 0 (g) - 4 LOAD_CONST 1 (0) - 6 CALL_NO_KW 1 - 8 POP_TOP - 10 LOAD_CONST 0 (None) - 12 RETURN_VALUE - >> 14 PUSH_EXC_INFO - - 4 16 POP_TOP - 18 POP_TOP - 20 POP_TOP - - 5 22 POP_EXCEPT - 24 LOAD_CONST 2 ('fail') - 26 RETURN_VALUE - >> 28 POP_EXCEPT_AND_RERAISE + 2 2 NOP + + 3 4 LOAD_GLOBAL 1 (NULL + g) + 16 LOAD_CONST 1 (0) + 18 PRECALL 1 + 22 CALL 1 + 32 POP_TOP + 34 LOAD_CONST 0 (None) + 36 RETURN_VALUE + >> 38 PUSH_EXC_INFO + + 4 40 POP_TOP + + 5 42 POP_EXCEPT + 44 LOAD_CONST 2 ('fail') + 46 RETURN_VALUE + >> 48 COPY 3 + 50 POP_EXCEPT + 52 RERAISE 1 ExceptionTable: - 2 to 8 -> 14 [0] - 14 to 20 -> 28 [3] lasti + 4 to 32 -> 38 [0] + 38 to 40 -> 48 [1] lasti -(Note this code is from an early 3.11 alpha, the NOP may well have be removed before release). +(Note this code is from 3.11, later versions may have slightly different bytecode.) If an instruction raises an exception then its offset is used to find the target to jump to. -For example, the CALL_NO_KW at offset 6, falls into the range 2 to 8. -So, if g() raises an exception, then control jumps to offset 14. +For example, the CALL at offset 22, falls into the range 4 to 32. +So, if g() raises an exception, then control jumps to offset 38. Unwinding @@ -84,9 +87,9 @@ If there is no relevant entry, the exception bubbles up to the caller. If there is an entry, then: - 1. pop values from the stack until it matches the stack depth for the handler, + 1. pop values from the stack until it matches the stack depth for the handler. 2. if 'lasti' is true, then push the offset that the exception was raised at. - 3. push the exception to the stack as three values: traceback, value, type, + 3. push the exception to the stack. 4. jump to the target offset and resume execution. diff -Nru python3.11-3.11.0~rc1/Objects/floatobject.c python3.11-3.11.0~rc2/Objects/floatobject.c --- python3.11-3.11.0~rc1/Objects/floatobject.c 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Objects/floatobject.c 2022-09-11 19:23:30.000000000 +0000 @@ -162,11 +162,18 @@ double x; const char *end; const char *last = s + len; - /* strip space */ + /* strip leading whitespace */ while (s < last && Py_ISSPACE(*s)) { s++; } + if (s == last) { + PyErr_Format(PyExc_ValueError, + "could not convert string to float: " + "%R", obj); + return NULL; + } + /* strip trailing whitespace */ while (s < last - 1 && Py_ISSPACE(last[-1])) { last--; } diff -Nru python3.11-3.11.0~rc1/Objects/longobject.c python3.11-3.11.0~rc2/Objects/longobject.c --- python3.11-3.11.0~rc1/Objects/longobject.c 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Objects/longobject.c 2022-09-11 19:23:30.000000000 +0000 @@ -36,6 +36,9 @@ #define IS_SMALL_INT(ival) (-_PY_NSMALLNEGINTS <= (ival) && (ival) < _PY_NSMALLPOSINTS) #define IS_SMALL_UINT(ival) ((ival) < _PY_NSMALLPOSINTS) +#define _MAX_STR_DIGITS_ERROR_FMT_TO_INT "Exceeds the limit (%d) for integer string conversion: value has %zd digits" +#define _MAX_STR_DIGITS_ERROR_FMT_TO_STR "Exceeds the limit (%d) for integer string conversion" + static inline void _Py_DECREF_INT(PyLongObject *op) { @@ -1724,6 +1727,23 @@ size_a = Py_ABS(Py_SIZE(a)); negative = Py_SIZE(a) < 0; + /* quick and dirty pre-check for overflowing the decimal digit limit, + based on the inequality 10/3 >= log2(10) + + explanation in https://github.com/python/cpython/pull/96537 + */ + if (size_a >= 10 * _PY_LONG_MAX_STR_DIGITS_THRESHOLD + / (3 * PyLong_SHIFT) + 2) { + PyInterpreterState *interp = _PyInterpreterState_GET(); + int max_str_digits = interp->int_max_str_digits; + if ((max_str_digits > 0) && + (max_str_digits / (3 * PyLong_SHIFT) <= (size_a - 11) / 10)) { + PyErr_Format(PyExc_ValueError, _MAX_STR_DIGITS_ERROR_FMT_TO_STR, + max_str_digits); + return -1; + } + } + /* quick and dirty upper bound for the number of digits required to express a in base _PyLong_DECIMAL_BASE: @@ -1783,6 +1803,17 @@ tenpow *= 10; strlen++; } + if (strlen > _PY_LONG_MAX_STR_DIGITS_THRESHOLD) { + PyInterpreterState *interp = _PyInterpreterState_GET(); + int max_str_digits = interp->int_max_str_digits; + Py_ssize_t strlen_nosign = strlen - negative; + if ((max_str_digits > 0) && (strlen_nosign > max_str_digits)) { + Py_DECREF(scratch); + PyErr_Format(PyExc_ValueError, _MAX_STR_DIGITS_ERROR_FMT_TO_STR, + max_str_digits); + return -1; + } + } if (writer) { if (_PyUnicodeWriter_Prepare(writer, strlen, '9') == -1) { Py_DECREF(scratch); @@ -2296,6 +2327,7 @@ start = str; if ((base & (base - 1)) == 0) { + /* binary bases are not limited by int_max_str_digits */ int res = long_from_binary_base(&str, base, &z); if (res < 0) { /* Syntax error. */ @@ -2447,6 +2479,17 @@ goto onError; } + /* Limit the size to avoid excessive computation attacks. */ + if (digits > _PY_LONG_MAX_STR_DIGITS_THRESHOLD) { + PyInterpreterState *interp = _PyInterpreterState_GET(); + int max_str_digits = interp->int_max_str_digits; + if ((max_str_digits > 0) && (digits > max_str_digits)) { + PyErr_Format(PyExc_ValueError, _MAX_STR_DIGITS_ERROR_FMT_TO_INT, + max_str_digits, digits); + return NULL; + } + } + /* Create an int object that can contain the largest possible * integer with this base and length. Note that there's no * need to initialize z->ob_digit -- no slot is read up before @@ -5323,6 +5366,7 @@ } return PyLong_FromLong(0L); } + /* default base and limit, forward to standard implementation */ if (obase == NULL) return PyNumber_Long(x); @@ -6058,6 +6102,8 @@ static PyStructSequence_Field int_info_fields[] = { {"bits_per_digit", "size of a digit in bits"}, {"sizeof_digit", "size in bytes of the C type used to represent a digit"}, + {"default_max_str_digits", "maximum string conversion digits limitation"}, + {"str_digits_check_threshold", "minimum positive value for int_max_str_digits"}, {NULL, NULL} }; @@ -6065,7 +6111,7 @@ "sys.int_info", /* name */ int_info__doc__, /* doc */ int_info_fields, /* fields */ - 2 /* number of fields */ + 4 /* number of fields */ }; PyObject * @@ -6080,6 +6126,17 @@ PyLong_FromLong(PyLong_SHIFT)); PyStructSequence_SET_ITEM(int_info, field++, PyLong_FromLong(sizeof(digit))); + /* + * The following two fields were added after investigating uses of + * sys.int_info in the wild: Exceedingly rarely used. The ONLY use found was + * numba using sys.int_info.bits_per_digit as attribute access rather than + * sequence unpacking. Cython and sympy also refer to sys.int_info but only + * as info for debugging. No concern about adding these in a backport. + */ + PyStructSequence_SET_ITEM(int_info, field++, + PyLong_FromLong(_PY_LONG_DEFAULT_MAX_STR_DIGITS)); + PyStructSequence_SET_ITEM(int_info, field++, + PyLong_FromLong(_PY_LONG_MAX_STR_DIGITS_THRESHOLD)); if (PyErr_Occurred()) { Py_CLEAR(int_info); return NULL; @@ -6107,6 +6164,10 @@ return _PyStatus_ERR("can't init int info type"); } } + interp->int_max_str_digits = _Py_global_config_int_max_str_digits; + if (interp->int_max_str_digits == -1) { + interp->int_max_str_digits = _PY_LONG_DEFAULT_MAX_STR_DIGITS; + } return _PyStatus_OK(); } diff -Nru python3.11-3.11.0~rc1/Objects/object.c python3.11-3.11.0~rc2/Objects/object.c --- python3.11-3.11.0~rc1/Objects/object.c 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Objects/object.c 2022-09-11 19:23:30.000000000 +0000 @@ -1086,7 +1086,11 @@ /* Helper to get a pointer to an object's __dict__ slot, if any. * Creates the dict from inline attributes if necessary. - * Does not set an exception. */ + * Does not set an exception. + * + * Note that the tp_dictoffset docs used to recommend this function, + * so it should be treated as part of the public API. + */ PyObject ** _PyObject_GetDictPtr(PyObject *obj) { @@ -1348,6 +1352,8 @@ PyErr_Format(PyExc_AttributeError, "'%.50s' object has no attribute '%U'", tp->tp_name, name); + + set_attribute_error_context(obj, name); } done: Py_XDECREF(descr); diff -Nru python3.11-3.11.0~rc1/Objects/typeobject.c python3.11-3.11.0~rc2/Objects/typeobject.c --- python3.11-3.11.0~rc1/Objects/typeobject.c 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Objects/typeobject.c 2022-09-11 19:23:30.000000000 +0000 @@ -3202,11 +3202,6 @@ // Put the proper slots in place fixup_slot_dispatchers(type); - if (type->tp_flags & Py_TPFLAGS_MANAGED_DICT) { - PyHeapTypeObject *et = (PyHeapTypeObject*)type; - et->ht_cached_keys = _PyDict_NewKeysForClass(); - } - if (type_new_set_names(type) < 0) { goto error; } @@ -3588,10 +3583,6 @@ if (PyType_Ready(type) < 0) goto fail; - if (type->tp_flags & Py_TPFLAGS_MANAGED_DICT) { - res->ht_cached_keys = _PyDict_NewKeysForClass(); - } - if (type->tp_doc) { PyObject *__doc__ = PyUnicode_FromString(_PyType_DocWithoutSignature(type->tp_name, type->tp_doc)); if (!__doc__) @@ -6409,6 +6400,29 @@ return 0; } +static int +type_ready_managed_dict(PyTypeObject *type) +{ + if (!(type->tp_flags & Py_TPFLAGS_MANAGED_DICT)) { + return 0; + } + if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE)) { + PyErr_Format(PyExc_SystemError, + "type %s has the Py_TPFLAGS_MANAGED_DICT flag " + "but not Py_TPFLAGS_HEAPTYPE flag", + type->tp_name); + return -1; + } + PyHeapTypeObject* et = (PyHeapTypeObject*)type; + if (et->ht_cached_keys == NULL) { + et->ht_cached_keys = _PyDict_NewKeysForClass(); + if (et->ht_cached_keys == NULL) { + PyErr_NoMemory(); + return -1; + } + } + return 0; +} static int type_ready_post_checks(PyTypeObject *type) @@ -6469,6 +6483,9 @@ if (type_ready_add_subclasses(type) < 0) { return -1; } + if (type_ready_managed_dict(type) < 0) { + return -1; + } if (type_ready_post_checks(type) < 0) { return -1; } diff -Nru python3.11-3.11.0~rc1/Parser/parser.c python3.11-3.11.0~rc2/Parser/parser.c --- python3.11-3.11.0~rc1/Parser/parser.c 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Parser/parser.c 2022-09-11 19:23:30.000000000 +0000 @@ -4637,7 +4637,7 @@ ) { D(fprintf(stderr, "%*c+ parameters[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "slash_no_default param_no_default* param_with_default* star_etc?")); - _res = _PyPegen_make_arguments ( p , a , NULL , b , c , d ); + _res = CHECK_VERSION ( arguments_ty , 8 , "Positional-only parameters are" , _PyPegen_make_arguments ( p , a , NULL , b , c , d ) ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; p->level--; @@ -4667,7 +4667,7 @@ ) { D(fprintf(stderr, "%*c+ parameters[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "slash_with_default param_with_default* star_etc?")); - _res = _PyPegen_make_arguments ( p , NULL , a , NULL , b , c ); + _res = CHECK_VERSION ( arguments_ty , 8 , "Positional-only parameters are" , _PyPegen_make_arguments ( p , NULL , a , NULL , b , c ) ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; p->level--; @@ -14659,7 +14659,7 @@ ) { D(fprintf(stderr, "%*c+ lambda_parameters[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_slash_no_default lambda_param_no_default* lambda_param_with_default* lambda_star_etc?")); - _res = _PyPegen_make_arguments ( p , a , NULL , b , c , d ); + _res = CHECK_VERSION ( arguments_ty , 8 , "Positional-only parameters are" , _PyPegen_make_arguments ( p , a , NULL , b , c , d ) ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; p->level--; @@ -14689,7 +14689,7 @@ ) { D(fprintf(stderr, "%*c+ lambda_parameters[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_slash_with_default lambda_param_with_default* lambda_star_etc?")); - _res = _PyPegen_make_arguments ( p , NULL , a , NULL , b , c ); + _res = CHECK_VERSION ( arguments_ty , 8 , "Positional-only parameters are" , _PyPegen_make_arguments ( p , NULL , a , NULL , b , c ) ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; p->level--; diff -Nru python3.11-3.11.0~rc1/Parser/pegen.c python3.11-3.11.0~rc2/Parser/pegen.c --- python3.11-3.11.0~rc1/Parser/pegen.c 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Parser/pegen.c 2022-09-11 19:23:30.000000000 +0000 @@ -1,5 +1,6 @@ #include #include "pycore_ast.h" // _PyAST_Validate(), +#include "pycore_pystate.h" // _PyThreadState_GET() #include #include "tokenizer.h" @@ -651,6 +652,28 @@ if (c == NULL) { p->error_indicator = 1; + PyThreadState *tstate = _PyThreadState_GET(); + // The only way a ValueError should happen in _this_ code is via + // PyLong_FromString hitting a length limit. + if (tstate->curexc_type == PyExc_ValueError && + tstate->curexc_value != NULL) { + PyObject *type, *value, *tb; + // This acts as PyErr_Clear() as we're replacing curexc. + PyErr_Fetch(&type, &value, &tb); + Py_XDECREF(tb); + Py_DECREF(type); + /* Intentionally omitting columns to avoid a wall of 1000s of '^'s + * on the error message. Nobody is going to overlook their huge + * numeric literal once given the line. */ + RAISE_ERROR_KNOWN_LOCATION( + p, PyExc_SyntaxError, + t->lineno, -1 /* col_offset */, + t->end_lineno, -1 /* end_col_offset */, + "%S - Consider hexadecimal for huge integer literals " + "to avoid decimal conversion limits.", + value); + Py_DECREF(value); + } return NULL; } diff -Nru python3.11-3.11.0~rc1/Parser/pegen_errors.c python3.11-3.11.0~rc2/Parser/pegen_errors.c --- python3.11-3.11.0~rc1/Parser/pegen_errors.c 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Parser/pegen_errors.c 2022-09-11 19:23:30.000000000 +0000 @@ -371,7 +371,7 @@ } } } - tmp = Py_BuildValue("(OiiNii)", p->tok->filename, lineno, col_number, error_line, end_lineno, end_col_number); + tmp = Py_BuildValue("(OnnNnn)", p->tok->filename, lineno, col_number, error_line, end_lineno, end_col_number); if (!tmp) { goto error; } diff -Nru python3.11-3.11.0~rc1/Parser/tokenizer.c python3.11-3.11.0~rc2/Parser/tokenizer.c --- python3.11-3.11.0~rc1/Parser/tokenizer.c 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Parser/tokenizer.c 2022-09-11 19:23:30.000000000 +0000 @@ -486,25 +486,59 @@ /* Check whether the characters at s start a valid UTF-8 sequence. Return the number of characters forming - the sequence if yes, 0 if not. */ -static int valid_utf8(const unsigned char* s) + the sequence if yes, 0 if not. The special cases match + those in stringlib/codecs.h:utf8_decode. +*/ +static int +valid_utf8(const unsigned char* s) { int expected = 0; int length; - if (*s < 0x80) + if (*s < 0x80) { /* single-byte code */ return 1; - if (*s < 0xc0) - /* following byte */ - return 0; - if (*s < 0xE0) + } + else if (*s < 0xE0) { + /* \xC2\x80-\xDF\xBF -- 0080-07FF */ + if (*s < 0xC2) { + /* invalid sequence + \x80-\xBF -- continuation byte + \xC0-\xC1 -- fake 0000-007F */ + return 0; + } expected = 1; - else if (*s < 0xF0) + } + else if (*s < 0xF0) { + /* \xE0\xA0\x80-\xEF\xBF\xBF -- 0800-FFFF */ + if (*s == 0xE0 && *(s + 1) < 0xA0) { + /* invalid sequence + \xE0\x80\x80-\xE0\x9F\xBF -- fake 0000-0800 */ + return 0; + } + else if (*s == 0xED && *(s + 1) >= 0xA0) { + /* Decoding UTF-8 sequences in range \xED\xA0\x80-\xED\xBF\xBF + will result in surrogates in range D800-DFFF. Surrogates are + not valid UTF-8 so they are rejected. + See https://www.unicode.org/versions/Unicode5.2.0/ch03.pdf + (table 3-7) and http://www.rfc-editor.org/rfc/rfc3629.txt */ + return 0; + } expected = 2; - else if (*s < 0xF8) + } + else if (*s < 0xF5) { + /* \xF0\x90\x80\x80-\xF4\x8F\xBF\xBF -- 10000-10FFFF */ + if (*(s + 1) < 0x90 ? *s == 0xF0 : *s == 0xF4) { + /* invalid sequence -- one of: + \xF0\x80\x80\x80-\xF0\x8F\xBF\xBF -- fake 0000-FFFF + \xF4\x90\x80\x80- -- 110000- overflow */ + return 0; + } expected = 3; - else + } + else { + /* invalid start byte */ return 0; + } length = expected + 1; for (; expected; expected--) if (s[expected] < 0x80 || s[expected] >= 0xC0) @@ -525,14 +559,12 @@ } } if (badchar) { - /* Need to add 1 to the line number, since this line - has not been counted, yet. */ PyErr_Format(PyExc_SyntaxError, "Non-UTF-8 code starting with '\\x%.2x' " "in file %U on line %i, " "but no encoding declared; " "see https://peps.python.org/pep-0263/ for details", - badchar, tok->filename, tok->lineno + 1); + badchar, tok->filename, tok->lineno); return 0; } return 1; @@ -1945,6 +1977,8 @@ /* Get rest of string */ while (end_quote_size != quote_size) { c = tok_nextc(tok); + if (tok->done == E_DECODE) + break; if (c == EOF || (quote_size == 1 && c == '\n')) { assert(tok->multi_line_start != NULL); // shift the tok_state's location into diff -Nru python3.11-3.11.0~rc1/PC/launcher2.c python3.11-3.11.0~rc2/PC/launcher2.c --- python3.11-3.11.0~rc1/PC/launcher2.c 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/PC/launcher2.c 2022-09-11 19:23:30.000000000 +0000 @@ -393,12 +393,6 @@ // only currently possible high priority environment is an active virtual // environment bool lowPriorityTag; - // if true, we had an old-style tag with '-64' suffix, and so do not - // want to match tags like '3.x-32' - bool exclude32Bit; - // if true, we had an old-style tag with '-32' suffix, and so *only* - // want to match tags like '3.x-32' - bool only32Bit; // if true, allow PEP 514 lookup to override 'executable' bool allowExecutableOverride; // if true, allow a nearby pyvenv.cfg to locate the executable @@ -483,8 +477,6 @@ DEBUG_2(tag, tagLength); DEBUG_BOOL(oldStyleTag); DEBUG_BOOL(lowPriorityTag); - DEBUG_BOOL(exclude32Bit); - DEBUG_BOOL(only32Bit); DEBUG_BOOL(allowDefaults); DEBUG_BOOL(allowExecutableOverride); DEBUG_BOOL(windowed); @@ -649,17 +641,6 @@ search->tagLength = argLen; search->oldStyleTag = true; search->restOfCmdLine = tail; - // If the tag ends with -64, we want to exclude 32-bit runtimes - // (If the tag ends with -32, it will be filtered later) - if (argLen > 3) { - if (0 == _compareArgument(&arg[argLen - 3], 3, L"-64", 3)) { - search->tagLength -= 3; - search->exclude32Bit = true; - } else if (0 == _compareArgument(&arg[argLen - 3], 3, L"-32", 3)) { - search->tagLength -= 3; - search->only32Bit = true; - } - } } else if (STARTSWITH(L"V:") || STARTSWITH(L"-version:")) { // Arguments starting with 'V:' specify company and/or tag const wchar_t *argStart = wcschr(arg, L':') + 1; @@ -1087,6 +1068,7 @@ if (!slash) { search->tag = tag; search->tagLength = n; + search->oldStyleTag = true; } else { search->company = tag; search->companyLength = (int)(slash - tag); @@ -1966,10 +1948,25 @@ } } else if (0 == _compare(env->company, -1, L"PythonCore", -1)) { // Old-style tags can only match PythonCore entries - if (_startsWith(env->tag, -1, search->tag, search->tagLength)) { - if (search->exclude32Bit && _is32Bit(env)) { + + // If the tag ends with -64, we want to exclude 32-bit runtimes + // (If the tag ends with -32, it will be filtered later) + int tagLength = search->tagLength; + bool exclude32Bit = false, only32Bit = false; + if (tagLength > 3) { + if (0 == _compareArgument(&search->tag[tagLength - 3], 3, L"-64", 3)) { + tagLength -= 3; + exclude32Bit = true; + } else if (0 == _compareArgument(&search->tag[tagLength - 3], 3, L"-32", 3)) { + tagLength -= 3; + only32Bit = true; + } + } + + if (_startsWith(env->tag, -1, search->tag, tagLength)) { + if (exclude32Bit && _is32Bit(env)) { debug(L"# Excluding %s/%s because it looks like 32bit\n", env->company, env->tag); - } else if (search->only32Bit && !_is32Bit(env)) { + } else if (only32Bit && !_is32Bit(env)) { debug(L"# Excluding %s/%s because it doesn't look 32bit\n", env->company, env->tag); } else { *best = env; diff -Nru python3.11-3.11.0~rc1/PC/_msi.c python3.11-3.11.0~rc2/PC/_msi.c --- python3.11-3.11.0~rc1/PC/_msi.c 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/PC/_msi.c 2022-09-11 19:23:30.000000000 +0000 @@ -360,7 +360,7 @@ int code; char buf[2000]; char *res = buf; - DWORD size = sizeof(buf); + DWORD size = Py_ARRAY_LENGTH(buf); MSIHANDLE err = MsiGetLastErrorRecord(); if (err == 0) { @@ -484,7 +484,7 @@ unsigned int status; WCHAR buf[2000]; WCHAR *res = buf; - DWORD size = sizeof(buf); + DWORD size = Py_ARRAY_LENGTH(buf); PyObject* string; status = MsiRecordGetStringW(self->h, field, res, &size); diff -Nru python3.11-3.11.0~rc1/PCbuild/get_externals.bat python3.11-3.11.0~rc2/PCbuild/get_externals.bat --- python3.11-3.11.0~rc1/PCbuild/get_externals.bat 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/PCbuild/get_externals.bat 2022-09-11 19:23:30.000000000 +0000 @@ -53,7 +53,7 @@ set libraries= set libraries=%libraries% bzip2-1.0.8 if NOT "%IncludeLibffiSrc%"=="false" set libraries=%libraries% libffi-3.4.2 -if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-1.1.1n +if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-1.1.1q set libraries=%libraries% sqlite-3.38.4.0 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tcl-core-8.6.12.1 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tk-8.6.12.1 @@ -77,7 +77,7 @@ set binaries= if NOT "%IncludeLibffi%"=="false" set binaries=%binaries% libffi-3.4.2 -if NOT "%IncludeSSL%"=="false" set binaries=%binaries% openssl-bin-1.1.1n +if NOT "%IncludeSSL%"=="false" set binaries=%binaries% openssl-bin-1.1.1q if NOT "%IncludeTkinter%"=="false" set binaries=%binaries% tcltk-8.6.12.1 if NOT "%IncludeSSLSrc%"=="false" set binaries=%binaries% nasm-2.11.06 diff -Nru python3.11-3.11.0~rc1/PCbuild/python.props python3.11-3.11.0~rc2/PCbuild/python.props --- python3.11-3.11.0~rc1/PCbuild/python.props 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/PCbuild/python.props 2022-09-11 19:23:30.000000000 +0000 @@ -67,8 +67,8 @@ $(ExternalsDir)libffi-3.4.2\ $(ExternalsDir)libffi-3.4.2\$(ArchName)\ $(libffiOutDir)include - $(ExternalsDir)openssl-1.1.1n\ - $(ExternalsDir)openssl-bin-1.1.1n\$(ArchName)\ + $(ExternalsDir)openssl-1.1.1q\ + $(ExternalsDir)openssl-bin-1.1.1q\$(ArchName)\ $(opensslOutDir)include $(ExternalsDir)\nasm-2.11.06\ $(ExternalsDir)\zlib-1.2.12\ diff -Nru python3.11-3.11.0~rc1/PCbuild/readme.txt python3.11-3.11.0~rc2/PCbuild/readme.txt --- python3.11-3.11.0~rc1/PCbuild/readme.txt 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/PCbuild/readme.txt 2022-09-11 19:23:30.000000000 +0000 @@ -168,7 +168,7 @@ Homepage: https://tukaani.org/xz/ _ssl - Python wrapper for version 1.1.1k of the OpenSSL secure sockets + Python wrapper for version 1.1.1q of the OpenSSL secure sockets library, which is downloaded from our binaries repository at https://github.com/python/cpython-bin-deps. diff -Nru python3.11-3.11.0~rc1/Python/ceval.c python3.11-3.11.0~rc2/Python/ceval.c --- python3.11-3.11.0~rc1/Python/ceval.c 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Python/ceval.c 2022-09-11 19:23:30.000000000 +0000 @@ -44,7 +44,7 @@ # error "ceval.c must be build with Py_BUILD_CORE define for best performance" #endif -#ifndef Py_DEBUG +#if !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) // GH-89279: The MSVC compiler does not inline these static inline functions // in PGO build in _PyEval_EvalFrameDefault(), because this function is over // the limit of PGO, and that limit cannot be configured. @@ -1327,12 +1327,6 @@ #define PRE_DISPATCH_GOTO() ((void)0) #endif -#define NOTRACE_DISPATCH() \ - { \ - NEXTOPARG(); \ - PRE_DISPATCH_GOTO(); \ - DISPATCH_GOTO(); \ - } /* Do interpreter dispatch accounting for tracing and instrumentation */ #define DISPATCH() \ @@ -1344,10 +1338,11 @@ DISPATCH_GOTO(); \ } -#define NOTRACE_DISPATCH_SAME_OPARG() \ +#define DISPATCH_SAME_OPARG() \ { \ opcode = _Py_OPCODE(*next_instr); \ PRE_DISPATCH_GOTO(); \ + opcode |= cframe.use_tracing OR_DTRACE_LINE; \ DISPATCH_GOTO(); \ } @@ -1851,7 +1846,7 @@ } Py_INCREF(value); PUSH(value); - NOTRACE_DISPATCH(); + DISPATCH(); } TARGET(LOAD_FAST__LOAD_CONST) { @@ -1866,7 +1861,7 @@ value = GETITEM(consts, oparg); Py_INCREF(value); PUSH(value); - NOTRACE_DISPATCH(); + DISPATCH(); } TARGET(STORE_FAST__LOAD_FAST) { @@ -1880,7 +1875,7 @@ } Py_INCREF(value); PUSH(value); - NOTRACE_DISPATCH(); + DISPATCH(); } TARGET(STORE_FAST__STORE_FAST) { @@ -1890,7 +1885,7 @@ next_instr++; value = POP(); SETLOCAL(oparg, value); - NOTRACE_DISPATCH(); + DISPATCH(); } TARGET(LOAD_CONST__LOAD_FAST) { @@ -1905,7 +1900,7 @@ } Py_INCREF(value); PUSH(value); - NOTRACE_DISPATCH(); + DISPATCH(); } TARGET(POP_TOP) { @@ -1984,7 +1979,7 @@ goto error; } JUMPBY(INLINE_CACHE_ENTRIES_BINARY_OP); - NOTRACE_DISPATCH(); + DISPATCH(); } TARGET(BINARY_OP_MULTIPLY_FLOAT) { @@ -2005,7 +2000,7 @@ goto error; } JUMPBY(INLINE_CACHE_ENTRIES_BINARY_OP); - NOTRACE_DISPATCH(); + DISPATCH(); } TARGET(BINARY_OP_SUBTRACT_INT) { @@ -2024,7 +2019,7 @@ goto error; } JUMPBY(INLINE_CACHE_ENTRIES_BINARY_OP); - NOTRACE_DISPATCH(); + DISPATCH(); } TARGET(BINARY_OP_SUBTRACT_FLOAT) { @@ -2044,7 +2039,7 @@ goto error; } JUMPBY(INLINE_CACHE_ENTRIES_BINARY_OP); - NOTRACE_DISPATCH(); + DISPATCH(); } TARGET(BINARY_OP_ADD_UNICODE) { @@ -2063,7 +2058,7 @@ goto error; } JUMPBY(INLINE_CACHE_ENTRIES_BINARY_OP); - NOTRACE_DISPATCH(); + DISPATCH(); } TARGET(BINARY_OP_INPLACE_ADD_UNICODE) { @@ -2099,7 +2094,7 @@ } // The STORE_FAST is already done. JUMPBY(INLINE_CACHE_ENTRIES_BINARY_OP + 1); - NOTRACE_DISPATCH(); + DISPATCH(); } TARGET(BINARY_OP_ADD_FLOAT) { @@ -2120,7 +2115,7 @@ goto error; } JUMPBY(INLINE_CACHE_ENTRIES_BINARY_OP); - NOTRACE_DISPATCH(); + DISPATCH(); } TARGET(BINARY_OP_ADD_INT) { @@ -2139,7 +2134,7 @@ goto error; } JUMPBY(INLINE_CACHE_ENTRIES_BINARY_OP); - NOTRACE_DISPATCH(); + DISPATCH(); } TARGET(BINARY_SUBSCR) { @@ -2165,7 +2160,7 @@ if (_Py_Specialize_BinarySubscr(container, sub, next_instr) < 0) { goto error; } - NOTRACE_DISPATCH_SAME_OPARG(); + DISPATCH_SAME_OPARG(); } else { STAT_INC(BINARY_SUBSCR, deferred); @@ -2196,7 +2191,7 @@ SET_TOP(res); Py_DECREF(list); JUMPBY(INLINE_CACHE_ENTRIES_BINARY_SUBSCR); - NOTRACE_DISPATCH(); + DISPATCH(); } TARGET(BINARY_SUBSCR_TUPLE_INT) { @@ -2221,7 +2216,7 @@ SET_TOP(res); Py_DECREF(tuple); JUMPBY(INLINE_CACHE_ENTRIES_BINARY_SUBSCR); - NOTRACE_DISPATCH(); + DISPATCH(); } TARGET(BINARY_SUBSCR_DICT) { @@ -2330,7 +2325,7 @@ if (_Py_Specialize_StoreSubscr(container, sub, next_instr) < 0) { goto error; } - NOTRACE_DISPATCH_SAME_OPARG(); + DISPATCH_SAME_OPARG(); } else { STAT_INC(STORE_SUBSCR, deferred); @@ -2362,7 +2357,7 @@ _Py_DECREF_SPECIALIZED(sub, (destructor)PyObject_Free); Py_DECREF(list); JUMPBY(INLINE_CACHE_ENTRIES_STORE_SUBSCR); - NOTRACE_DISPATCH(); + DISPATCH(); } TARGET(STORE_SUBSCR_DICT) { @@ -2820,7 +2815,7 @@ PyObject *seq = TOP(); next_instr--; _Py_Specialize_UnpackSequence(seq, next_instr, oparg); - NOTRACE_DISPATCH_SAME_OPARG(); + DISPATCH_SAME_OPARG(); } else { STAT_INC(UNPACK_SEQUENCE, deferred); @@ -2838,7 +2833,7 @@ PUSH(Py_NewRef(PyTuple_GET_ITEM(seq, 0))); Py_DECREF(seq); JUMPBY(INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE); - NOTRACE_DISPATCH(); + DISPATCH(); } TARGET(UNPACK_SEQUENCE_TUPLE) { @@ -2853,7 +2848,7 @@ } Py_DECREF(seq); JUMPBY(INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE); - NOTRACE_DISPATCH(); + DISPATCH(); } TARGET(UNPACK_SEQUENCE_LIST) { @@ -2868,7 +2863,7 @@ } Py_DECREF(seq); JUMPBY(INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE); - NOTRACE_DISPATCH(); + DISPATCH(); } TARGET(UNPACK_EX) { @@ -3063,7 +3058,7 @@ if (_Py_Specialize_LoadGlobal(GLOBALS(), BUILTINS(), next_instr, name) < 0) { goto error; } - NOTRACE_DISPATCH_SAME_OPARG(); + DISPATCH_SAME_OPARG(); } else { STAT_INC(LOAD_GLOBAL, deferred); @@ -3090,7 +3085,7 @@ STACK_GROW(push_null+1); Py_INCREF(res); SET_TOP(res); - NOTRACE_DISPATCH(); + DISPATCH(); } TARGET(LOAD_GLOBAL_BUILTIN) { @@ -3115,7 +3110,7 @@ STACK_GROW(push_null+1); Py_INCREF(res); SET_TOP(res); - NOTRACE_DISPATCH(); + DISPATCH(); } TARGET(DELETE_FAST) { @@ -3488,7 +3483,7 @@ if (_Py_Specialize_LoadAttr(owner, next_instr, name) < 0) { goto error; } - NOTRACE_DISPATCH_SAME_OPARG(); + DISPATCH_SAME_OPARG(); } else { STAT_INC(LOAD_ATTR, deferred); @@ -3517,7 +3512,7 @@ SET_TOP(res); Py_DECREF(owner); JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR); - NOTRACE_DISPATCH(); + DISPATCH(); } TARGET(LOAD_ATTR_MODULE) { @@ -3529,7 +3524,7 @@ SET_TOP(res); Py_DECREF(owner); JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR); - NOTRACE_DISPATCH(); + DISPATCH(); } TARGET(LOAD_ATTR_WITH_HINT) { @@ -3564,7 +3559,7 @@ SET_TOP(res); Py_DECREF(owner); JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR); - NOTRACE_DISPATCH(); + DISPATCH(); } TARGET(LOAD_ATTR_SLOT) { @@ -3584,9 +3579,10 @@ SET_TOP(res); Py_DECREF(owner); JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR); - NOTRACE_DISPATCH(); + DISPATCH(); } + DISPATCH(); TARGET(STORE_ATTR_ADAPTIVE) { assert(cframe.use_tracing == 0); _PyAttrCache *cache = (_PyAttrCache *)next_instr; @@ -3597,7 +3593,7 @@ if (_Py_Specialize_StoreAttr(owner, next_instr, name) < 0) { goto error; } - NOTRACE_DISPATCH_SAME_OPARG(); + DISPATCH_SAME_OPARG(); } else { STAT_INC(STORE_ATTR, deferred); @@ -3631,7 +3627,7 @@ } Py_DECREF(owner); JUMPBY(INLINE_CACHE_ENTRIES_STORE_ATTR); - NOTRACE_DISPATCH(); + DISPATCH(); } TARGET(STORE_ATTR_WITH_HINT) { @@ -3678,7 +3674,7 @@ dict->ma_version_tag = DICT_NEXT_VERSION(); Py_DECREF(owner); JUMPBY(INLINE_CACHE_ENTRIES_STORE_ATTR); - NOTRACE_DISPATCH(); + DISPATCH(); } TARGET(STORE_ATTR_SLOT) { @@ -3698,7 +3694,7 @@ Py_XDECREF(old_value); Py_DECREF(owner); JUMPBY(INLINE_CACHE_ENTRIES_STORE_ATTR); - NOTRACE_DISPATCH(); + DISPATCH(); } TARGET(COMPARE_OP) { @@ -3725,7 +3721,7 @@ PyObject *left = SECOND(); next_instr--; _Py_Specialize_CompareOp(left, right, next_instr, oparg); - NOTRACE_DISPATCH_SAME_OPARG(); + DISPATCH_SAME_OPARG(); } else { STAT_INC(COMPARE_OP, deferred); @@ -3773,7 +3769,7 @@ opcode == POP_JUMP_FORWARD_IF_FALSE); JUMPBY(1 + oparg); } - NOTRACE_DISPATCH(); + DISPATCH(); } TARGET(COMPARE_OP_INT_JUMP) { @@ -3816,7 +3812,7 @@ opcode == POP_JUMP_FORWARD_IF_FALSE); JUMPBY(1 + oparg); } - NOTRACE_DISPATCH(); + DISPATCH(); } TARGET(COMPARE_OP_STR_JUMP) { @@ -3860,7 +3856,7 @@ opcode == POP_JUMP_FORWARD_IF_FALSE); JUMPBY(1 + oparg); } - NOTRACE_DISPATCH(); + DISPATCH(); } TARGET(IS_OP) { @@ -4534,7 +4530,7 @@ if (_Py_Specialize_LoadMethod(owner, next_instr, name) < 0) { goto error; } - NOTRACE_DISPATCH_SAME_OPARG(); + DISPATCH_SAME_OPARG(); } else { STAT_INC(LOAD_METHOD, deferred); @@ -4566,7 +4562,7 @@ SET_TOP(res); PUSH(self); JUMPBY(INLINE_CACHE_ENTRIES_LOAD_METHOD); - NOTRACE_DISPATCH(); + DISPATCH(); } TARGET(LOAD_METHOD_WITH_DICT) { @@ -4598,7 +4594,7 @@ SET_TOP(res); PUSH(self); JUMPBY(INLINE_CACHE_ENTRIES_LOAD_METHOD); - NOTRACE_DISPATCH(); + DISPATCH(); } TARGET(LOAD_METHOD_NO_DICT) { @@ -4617,7 +4613,7 @@ SET_TOP(res); PUSH(self); JUMPBY(INLINE_CACHE_ENTRIES_LOAD_METHOD); - NOTRACE_DISPATCH(); + DISPATCH(); } TARGET(LOAD_METHOD_MODULE) { @@ -4630,7 +4626,7 @@ Py_DECREF(owner); PUSH(res); JUMPBY(INLINE_CACHE_ENTRIES_LOAD_METHOD); - NOTRACE_DISPATCH(); + DISPATCH(); } TARGET(LOAD_METHOD_CLASS) { @@ -4653,7 +4649,7 @@ Py_DECREF(cls); PUSH(res); JUMPBY(INLINE_CACHE_ENTRIES_LOAD_METHOD); - NOTRACE_DISPATCH(); + DISPATCH(); } TARGET(PRECALL) { @@ -4808,7 +4804,7 @@ if (err < 0) { goto error; } - NOTRACE_DISPATCH_SAME_OPARG(); + DISPATCH_SAME_OPARG(); } else { STAT_INC(PRECALL, deferred); @@ -4829,7 +4825,7 @@ if (err < 0) { goto error; } - NOTRACE_DISPATCH_SAME_OPARG(); + DISPATCH_SAME_OPARG(); } else { STAT_INC(CALL, deferred); @@ -4929,7 +4925,7 @@ Py_DECREF(obj); STACK_SHRINK(2); SET_TOP(res); - NOTRACE_DISPATCH(); + DISPATCH(); } TARGET(PRECALL_NO_KW_STR_1) { @@ -5199,7 +5195,7 @@ STACK_SHRINK(2); Py_DECREF(list); Py_DECREF(callable); - NOTRACE_DISPATCH(); + DISPATCH(); } TARGET(PRECALL_NO_KW_METHOD_DESCRIPTOR_O) { @@ -5569,7 +5565,7 @@ PyObject *rhs = TOP(); next_instr--; _Py_Specialize_BinaryOp(lhs, rhs, next_instr, oparg, &GETLOCAL(0)); - NOTRACE_DISPATCH_SAME_OPARG(); + DISPATCH_SAME_OPARG(); } else { STAT_INC(BINARY_OP, deferred); @@ -5604,10 +5600,12 @@ } TARGET(EXTENDED_ARG_QUICK) { + assert(cframe.use_tracing == 0); assert(oparg); - oparg <<= 8; - oparg |= _Py_OPARG(*next_instr); - NOTRACE_DISPATCH_SAME_OPARG(); + int oldoparg = oparg; + NEXTOPARG(); + oparg |= oldoparg << 8; + DISPATCH_GOTO(); } TARGET(CACHE) { @@ -6168,7 +6166,13 @@ /* Pack other positional arguments into the *args argument */ if (co->co_flags & CO_VARARGS) { PyObject *u = NULL; - u = _PyTuple_FromArraySteal(args + n, argcount - n); + if (argcount == n) { + u = Py_NewRef(&_Py_SINGLETON(tuple_empty)); + } + else { + assert(args != NULL); + u = _PyTuple_FromArraySteal(args + n, argcount - n); + } if (u == NULL) { goto fail_post_positional; } diff -Nru python3.11-3.11.0~rc1/Python/clinic/sysmodule.c.h python3.11-3.11.0~rc2/Python/clinic/sysmodule.c.h --- python3.11-3.11.0~rc1/Python/clinic/sysmodule.c.h 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Python/clinic/sysmodule.c.h 2022-09-11 19:23:30.000000000 +0000 @@ -669,6 +669,59 @@ #endif /* defined(USE_MALLOPT) */ +PyDoc_STRVAR(sys_get_int_max_str_digits__doc__, +"get_int_max_str_digits($module, /)\n" +"--\n" +"\n" +"Set the maximum string digits limit for non-binary int<->str conversions."); + +#define SYS_GET_INT_MAX_STR_DIGITS_METHODDEF \ + {"get_int_max_str_digits", (PyCFunction)sys_get_int_max_str_digits, METH_NOARGS, sys_get_int_max_str_digits__doc__}, + +static PyObject * +sys_get_int_max_str_digits_impl(PyObject *module); + +static PyObject * +sys_get_int_max_str_digits(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return sys_get_int_max_str_digits_impl(module); +} + +PyDoc_STRVAR(sys_set_int_max_str_digits__doc__, +"set_int_max_str_digits($module, /, maxdigits)\n" +"--\n" +"\n" +"Set the maximum string digits limit for non-binary int<->str conversions."); + +#define SYS_SET_INT_MAX_STR_DIGITS_METHODDEF \ + {"set_int_max_str_digits", _PyCFunction_CAST(sys_set_int_max_str_digits), METH_FASTCALL|METH_KEYWORDS, sys_set_int_max_str_digits__doc__}, + +static PyObject * +sys_set_int_max_str_digits_impl(PyObject *module, int maxdigits); + +static PyObject * +sys_set_int_max_str_digits(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + static const char * const _keywords[] = {"maxdigits", NULL}; + static _PyArg_Parser _parser = {NULL, _keywords, "set_int_max_str_digits", 0}; + PyObject *argsbuf[1]; + int maxdigits; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + if (!args) { + goto exit; + } + maxdigits = _PyLong_AsInt(args[0]); + if (maxdigits == -1 && PyErr_Occurred()) { + goto exit; + } + return_value = sys_set_int_max_str_digits_impl(module, maxdigits); + +exit: + return return_value; +} + PyDoc_STRVAR(sys_getrefcount__doc__, "getrefcount($module, object, /)\n" "--\n" @@ -1014,4 +1067,4 @@ #ifndef SYS_GETANDROIDAPILEVEL_METHODDEF #define SYS_GETANDROIDAPILEVEL_METHODDEF #endif /* !defined(SYS_GETANDROIDAPILEVEL_METHODDEF) */ -/*[clinic end generated code: output=98efd34fd9b9b6ab input=a9049054013a1b77]*/ +/*[clinic end generated code: output=21a32aa71d36a98c input=a9049054013a1b77]*/ diff -Nru python3.11-3.11.0~rc1/Python/initconfig.c python3.11-3.11.0~rc2/Python/initconfig.c --- python3.11-3.11.0~rc1/Python/initconfig.c 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Python/initconfig.c 2022-09-11 19:23:30.000000000 +0000 @@ -3,6 +3,7 @@ #include "pycore_getopt.h" // _PyOS_GetOpt() #include "pycore_initconfig.h" // _PyStatus_OK() #include "pycore_interp.h" // _PyInterpreterState.runtime +#include "pycore_long.h" // _PY_LONG_MAX_STR_DIGITS_THRESHOLD #include "pycore_pathconfig.h" // _Py_path_config #include "pycore_pyerrors.h" // _PyErr_Fetch() #include "pycore_pylifecycle.h" // _Py_PreInitializeFromConfig() @@ -119,7 +120,11 @@ when the interpreter displays tracebacks.\n\ \n\ -X frozen_modules=[on|off]: whether or not frozen modules should be used.\n\ - The default is \"on\" (or \"off\" if you are running a local build)."; + The default is \"on\" (or \"off\" if you are running a local build).\n\ +\n\ +-X int_max_str_digits=number: limit the size of int<->str conversions.\n\ + This helps avoid denial of service attacks when parsing untrusted data.\n\ + The default is sys.int_info.default_max_str_digits. 0 disables."; /* Envvars that don't have equivalent command-line options are listed first */ static const char usage_envvars[] = @@ -139,6 +144,10 @@ " to seed the hashes of str and bytes objects. It can also be set to an\n" " integer in the range [0,4294967295] to get hash values with a\n" " predictable seed.\n" +"PYTHONINTMAXSTRDIGITS: limits the maximum digit characters in an int value\n" +" when converting from a string and when converting an int back to a str.\n" +" A value of 0 disables the limit. Conversions to or from bases 2, 4, 8,\n" +" 16, and 32 are never limited.\n" "PYTHONMALLOC: set the Python memory allocators and/or install debug hooks\n" " on Python memory allocators. Use PYTHONMALLOC=debug to install debug\n" " hooks.\n" @@ -769,6 +778,10 @@ config->code_debug_ranges = 1; } +/* Excluded from public struct PyConfig for backporting reasons. */ +/* default to unconfigured, _PyLong_InitTypes() does the rest */ +int _Py_global_config_int_max_str_digits = -1; + static void config_init_defaults(PyConfig *config) @@ -1717,6 +1730,48 @@ return _PyStatus_OK(); } +static PyStatus +config_init_int_max_str_digits(PyConfig *config) +{ + int maxdigits; + int valid = 0; + + const char *env = config_get_env(config, "PYTHONINTMAXSTRDIGITS"); + if (env) { + if (!_Py_str_to_int(env, &maxdigits)) { + valid = ((maxdigits == 0) || (maxdigits >= _PY_LONG_MAX_STR_DIGITS_THRESHOLD)); + } + if (!valid) { +#define STRINGIFY(VAL) _STRINGIFY(VAL) +#define _STRINGIFY(VAL) #VAL + return _PyStatus_ERR( + "PYTHONINTMAXSTRDIGITS: invalid limit; must be >= " + STRINGIFY(_PY_LONG_MAX_STR_DIGITS_THRESHOLD) + " or 0 for unlimited."); + } + _Py_global_config_int_max_str_digits = maxdigits; + } + + const wchar_t *xoption = config_get_xoption(config, L"int_max_str_digits"); + if (xoption) { + const wchar_t *sep = wcschr(xoption, L'='); + if (sep) { + if (!config_wstr_to_int(sep + 1, &maxdigits)) { + valid = ((maxdigits == 0) || (maxdigits >= _PY_LONG_MAX_STR_DIGITS_THRESHOLD)); + } + } + if (!valid) { + return _PyStatus_ERR( + "-X int_max_str_digits: invalid limit; must be >= " + STRINGIFY(_PY_LONG_MAX_STR_DIGITS_THRESHOLD) + " or 0 for unlimited."); +#undef _STRINGIFY +#undef STRINGIFY + } + _Py_global_config_int_max_str_digits = maxdigits; + } + return _PyStatus_OK(); +} static PyStatus config_init_pycache_prefix(PyConfig *config) @@ -1772,6 +1827,12 @@ if (_PyStatus_EXCEPTION(status)) { return status; } + } + if (_Py_global_config_int_max_str_digits < 0) { + status = config_init_int_max_str_digits(config); + if (_PyStatus_EXCEPTION(status)) { + return status; + } } if (config->pycache_prefix == NULL) { diff -Nru python3.11-3.11.0~rc1/Python/pystate.c python3.11-3.11.0~rc2/Python/pystate.c --- python3.11-3.11.0~rc1/Python/pystate.c 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Python/pystate.c 2022-09-11 19:23:30.000000000 +0000 @@ -795,7 +795,15 @@ { PyThreadState *tstate; _PyRuntimeState *runtime = interp->runtime; - + // We don't need to allocate a thread state for the main interpreter + // (the common case), but doing it later for the other case revealed a + // reentrancy problem (deadlock). So for now we always allocate before + // taking the interpreters lock. See GH-96071. + PyThreadState *new_tstate = alloc_threadstate(); + int used_newtstate; + if (new_tstate == NULL) { + return NULL; + } /* We serialize concurrent creation to protect global state. */ HEAD_LOCK(runtime); @@ -807,18 +815,15 @@ if (old_head == NULL) { // It's the interpreter's initial thread state. assert(id == 1); - + used_newtstate = 0; tstate = &interp->_initial_thread; } else { // Every valid interpreter must have at least one thread. assert(id > 1); assert(old_head->prev == NULL); - - tstate = alloc_threadstate(); - if (tstate == NULL) { - goto error; - } + used_newtstate = 1; + tstate = new_tstate; // Set to _PyThreadState_INIT. memcpy(tstate, &initial._main_interpreter._initial_thread, @@ -829,11 +834,11 @@ init_threadstate(tstate, interp, id, old_head); HEAD_UNLOCK(runtime); + if (!used_newtstate) { + // Must be called with lock unlocked to avoid re-entrancy deadlock. + PyMem_RawFree(new_tstate); + } return tstate; - -error: - HEAD_UNLOCK(runtime); - return NULL; } PyThreadState * @@ -1255,10 +1260,14 @@ PyThreadState_GetFrame(PyThreadState *tstate) { assert(tstate != NULL); - if (tstate->cframe->current_frame == NULL) { + _PyInterpreterFrame *f = tstate->cframe->current_frame; + while (f && _PyFrame_IsIncomplete(f)) { + f = f->previous; + } + if (f == NULL) { return NULL; } - PyFrameObject *frame = _PyFrame_GetFrameObject(tstate->cframe->current_frame); + PyFrameObject *frame = _PyFrame_GetFrameObject(f); if (frame == NULL) { PyErr_Clear(); } @@ -1389,7 +1398,12 @@ if (id == NULL) { goto fail; } - int stat = PyDict_SetItem(result, id, (PyObject *)_PyFrame_GetFrameObject(frame)); + PyObject *frameobj = (PyObject *)_PyFrame_GetFrameObject(frame); + if (frameobj == NULL) { + Py_DECREF(id); + goto fail; + } + int stat = PyDict_SetItem(result, id, frameobj); Py_DECREF(id); if (stat < 0) { goto fail; @@ -2169,16 +2183,16 @@ _PyInterpreterFrame * _PyThreadState_BumpFramePointerSlow(PyThreadState *tstate, size_t size) { - assert(size < INT_MAX/sizeof(PyObject *)); - PyObject **base = tstate->datastack_top; - PyObject **top = base + size; - if (top >= tstate->datastack_limit) { - base = push_chunk(tstate, (int)size); + if (_PyThreadState_HasStackSpace(tstate, size)) { + _PyInterpreterFrame *res = (_PyInterpreterFrame *)tstate->datastack_top; + tstate->datastack_top += size; + return res; } - else { - tstate->datastack_top = top; + if (size > INT_MAX/2) { + PyErr_NoMemory(); + return NULL; } - return (_PyInterpreterFrame *)base; + return (_PyInterpreterFrame *)push_chunk(tstate, (int)size); } void diff -Nru python3.11-3.11.0~rc1/Python/sysmodule.c python3.11-3.11.0~rc2/Python/sysmodule.c --- python3.11-3.11.0~rc1/Python/sysmodule.c 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Python/sysmodule.c 2022-09-11 19:23:30.000000000 +0000 @@ -20,6 +20,7 @@ #include "pycore_code.h" // _Py_QuickenedCount #include "pycore_frame.h" // _PyInterpreterFrame #include "pycore_initconfig.h" // _PyStatus_EXCEPTION() +#include "pycore_long.h" // _PY_LONG_MAX_STR_DIGITS_THRESHOLD #include "pycore_namespace.h" // _PyNamespace_New() #include "pycore_object.h" // _PyObject_IS_GC() #include "pycore_pathconfig.h" // _PyPathConfig_ComputeSysPath0() @@ -1620,6 +1621,45 @@ } #endif /* USE_MALLOPT */ + +/*[clinic input] +sys.get_int_max_str_digits + +Set the maximum string digits limit for non-binary int<->str conversions. +[clinic start generated code]*/ + +static PyObject * +sys_get_int_max_str_digits_impl(PyObject *module) +/*[clinic end generated code: output=0042f5e8ae0e8631 input=8dab13e2023e60d5]*/ +{ + PyInterpreterState *interp = _PyInterpreterState_GET(); + return PyLong_FromSsize_t(interp->int_max_str_digits); +} + +/*[clinic input] +sys.set_int_max_str_digits + + maxdigits: int + +Set the maximum string digits limit for non-binary int<->str conversions. +[clinic start generated code]*/ + +static PyObject * +sys_set_int_max_str_digits_impl(PyObject *module, int maxdigits) +/*[clinic end generated code: output=734d4c2511f2a56d input=d7e3f325db6910c5]*/ +{ + PyThreadState *tstate = _PyThreadState_GET(); + if ((!maxdigits) || (maxdigits >= _PY_LONG_MAX_STR_DIGITS_THRESHOLD)) { + tstate->interp->int_max_str_digits = maxdigits; + Py_RETURN_NONE; + } else { + PyErr_Format( + PyExc_ValueError, "maxdigits must be 0 or larger than %d", + _PY_LONG_MAX_STR_DIGITS_THRESHOLD); + return NULL; + } +} + size_t _PySys_GetSizeOf(PyObject *o) { @@ -1996,6 +2036,8 @@ SYS_GET_ASYNCGEN_HOOKS_METHODDEF SYS_GETANDROIDAPILEVEL_METHODDEF SYS_UNRAISABLEHOOK_METHODDEF + SYS_GET_INT_MAX_STR_DIGITS_METHODDEF + SYS_SET_INT_MAX_STR_DIGITS_METHODDEF {NULL, NULL} // sentinel }; @@ -2490,6 +2532,7 @@ {"utf8_mode", "-X utf8"}, {"warn_default_encoding", "-X warn_default_encoding"}, {"safe_path", "-P"}, + {"int_max_str_digits", "-X int_max_str_digits"}, {0} }; @@ -2497,7 +2540,7 @@ "sys.flags", /* name */ flags__doc__, /* doc */ flags_fields, /* fields */ - 17 + 18 }; static int @@ -2538,6 +2581,7 @@ SetFlag(preconfig->utf8_mode); SetFlag(config->warn_default_encoding); SetFlagObj(PyBool_FromLong(config->safe_path)); + SetFlag(_Py_global_config_int_max_str_digits); #undef SetFlagObj #undef SetFlag return 0; diff -Nru python3.11-3.11.0~rc1/Python/thread.c python3.11-3.11.0~rc2/Python/thread.c --- python3.11-3.11.0~rc1/Python/thread.c 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Python/thread.c 2022-09-11 19:23:30.000000000 +0000 @@ -97,7 +97,7 @@ # define PYTHREAD_NAME "pthread-stubs" # include "thread_pthread_stubs.h" #elif defined(_POSIX_THREADS) -# if defined(__EMSCRIPTEN__) || !defined(__EMSCRIPTEN_PTHREADS__) +# if defined(__EMSCRIPTEN__) && !defined(__EMSCRIPTEN_PTHREADS__) # define PYTHREAD_NAME "pthread-stubs" # else # define PYTHREAD_NAME "pthread" diff -Nru python3.11-3.11.0~rc1/README.rst python3.11-3.11.0~rc2/README.rst --- python3.11-3.11.0~rc1/README.rst 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/README.rst 2022-09-11 19:23:30.000000000 +0000 @@ -1,4 +1,4 @@ -This is Python version 3.11.0 release candidate 1 +This is Python version 3.11.0 release candidate 2 ================================================= .. image:: https://github.com/python/cpython/workflows/Tests/badge.svg diff -Nru python3.11-3.11.0~rc1/Tools/c-analyzer/c_parser/parser/__init__.py python3.11-3.11.0~rc2/Tools/c-analyzer/c_parser/parser/__init__.py --- python3.11-3.11.0~rc1/Tools/c-analyzer/c_parser/parser/__init__.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Tools/c-analyzer/c_parser/parser/__init__.py 2022-09-11 19:23:30.000000000 +0000 @@ -12,7 +12,7 @@ * ... -(see: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf) +(see: https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf) We have taken advantage of the elements of the C grammar that are used only in a few limited contexts, mostly as delimiters. They allow us to diff -Nru python3.11-3.11.0~rc1/Tools/msi/build.bat python3.11-3.11.0~rc2/Tools/msi/build.bat --- python3.11-3.11.0~rc1/Tools/msi/build.bat 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Tools/msi/build.bat 2022-09-11 19:23:30.000000000 +0000 @@ -12,15 +12,16 @@ set REBUILD= :CheckOpts -if "%~1" EQU "-h" goto Help -if "%~1" EQU "-x86" (set BUILDX86=1) && shift && goto CheckOpts -if "%~1" EQU "-x64" (set BUILDX64=1) && shift && goto CheckOpts -if "%~1" EQU "-arm64" (set BUILDARM64=1) && shift && goto CheckOpts -if "%~1" EQU "--doc" (set BUILDDOC=1) && shift && goto CheckOpts -if "%~1" EQU "--no-test-marker" (set BUILDTEST=) && shift && goto CheckOpts -if "%~1" EQU "--test-marker" (set BUILDTEST=--test-marker) && shift && goto CheckOpts -if "%~1" EQU "--pack" (set BUILDPACK=1) && shift && goto CheckOpts -if "%~1" EQU "-r" (set REBUILD=-r) && shift && goto CheckOpts +if "%~1" EQU "-h" goto Help +if /I "%~1" EQU "-x86" (set BUILDX86=1) && shift && goto CheckOpts +if /I "%~1" EQU "-Win32" (set BUILDX86=1) && shift && goto CheckOpts +if /I "%~1" EQU "-x64" (set BUILDX64=1) && shift && goto CheckOpts +if /I "%~1" EQU "-arm64" (set BUILDARM64=1) && shift && goto CheckOpts +if "%~1" EQU "--doc" (set BUILDDOC=1) && shift && goto CheckOpts +if "%~1" EQU "--no-test-marker" (set BUILDTEST=) && shift && goto CheckOpts +if "%~1" EQU "--test-marker" (set BUILDTEST=--test-marker) && shift && goto CheckOpts +if "%~1" EQU "--pack" (set BUILDPACK=1) && shift && goto CheckOpts +if "%~1" EQU "-r" (set REBUILD=-r) && shift && goto CheckOpts if not defined BUILDX86 if not defined BUILDX64 if not defined BUILDARM64 (set BUILDX86=1) && (set BUILDX64=1) diff -Nru python3.11-3.11.0~rc1/Tools/msi/buildrelease.bat python3.11-3.11.0~rc2/Tools/msi/buildrelease.bat --- python3.11-3.11.0~rc1/Tools/msi/buildrelease.bat 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Tools/msi/buildrelease.bat 2022-09-11 19:23:30.000000000 +0000 @@ -44,27 +44,28 @@ :CheckOpts -if "%1" EQU "-h" goto Help -if "%1" EQU "-c" (set CERTNAME=%~2) && shift && shift && goto CheckOpts -if "%1" EQU "--certificate" (set CERTNAME=%~2) && shift && shift && goto CheckOpts -if "%1" EQU "-o" (set OUTDIR=%~2) && shift && shift && goto CheckOpts -if "%1" EQU "--out" (set OUTDIR=%~2) && shift && shift && goto CheckOpts -if "%1" EQU "-D" (set SKIPDOC=1) && shift && goto CheckOpts -if "%1" EQU "--skip-doc" (set SKIPDOC=1) && shift && goto CheckOpts -if "%1" EQU "-B" (set SKIPBUILD=1) && shift && goto CheckOpts -if "%1" EQU "--skip-build" (set SKIPBUILD=1) && shift && goto CheckOpts -if "%1" EQU "--download" (set DOWNLOAD_URL=%~2) && shift && shift && goto CheckOpts -if "%1" EQU "--test" (set TESTTARGETDIR=%~2) && shift && shift && goto CheckOpts -if "%1" EQU "-b" (set TARGET=Build) && shift && goto CheckOpts -if "%1" EQU "--build" (set TARGET=Build) && shift && goto CheckOpts -if "%1" EQU "-x86" (set BUILDX86=1) && shift && goto CheckOpts -if "%1" EQU "-x64" (set BUILDX64=1) && shift && goto CheckOpts -if "%1" EQU "-arm64" (set BUILDARM64=1) && shift && goto CheckOpts -if "%1" EQU "--pgo" (set PGO=%~2) && shift && shift && goto CheckOpts -if "%1" EQU "--skip-pgo" (set PGO=) && shift && goto CheckOpts -if "%1" EQU "--skip-nuget" (set BUILDNUGET=) && shift && goto CheckOpts -if "%1" EQU "--skip-zip" (set BUILDZIP=) && shift && goto CheckOpts -if "%1" EQU "--skip-msi" (set BUILDMSI=) && shift && goto CheckOpts +if "%1" EQU "-h" goto Help +if "%1" EQU "-c" (set CERTNAME=%~2) && shift && shift && goto CheckOpts +if "%1" EQU "--certificate" (set CERTNAME=%~2) && shift && shift && goto CheckOpts +if "%1" EQU "-o" (set OUTDIR=%~2) && shift && shift && goto CheckOpts +if "%1" EQU "--out" (set OUTDIR=%~2) && shift && shift && goto CheckOpts +if "%1" EQU "-D" (set SKIPDOC=1) && shift && goto CheckOpts +if "%1" EQU "--skip-doc" (set SKIPDOC=1) && shift && goto CheckOpts +if "%1" EQU "-B" (set SKIPBUILD=1) && shift && goto CheckOpts +if "%1" EQU "--skip-build" (set SKIPBUILD=1) && shift && goto CheckOpts +if "%1" EQU "--download" (set DOWNLOAD_URL=%~2) && shift && shift && goto CheckOpts +if "%1" EQU "--test" (set TESTTARGETDIR=%~2) && shift && shift && goto CheckOpts +if "%1" EQU "-b" (set TARGET=Build) && shift && goto CheckOpts +if "%1" EQU "--build" (set TARGET=Build) && shift && goto CheckOpts +if /I "%1" EQU "-x86" (set BUILDX86=1) && shift && goto CheckOpts +if /I "%1" EQU "-Win32" (set BUILDX86=1) && shift && goto CheckOpts +if /I "%1" EQU "-x64" (set BUILDX64=1) && shift && goto CheckOpts +if /I "%1" EQU "-arm64" (set BUILDARM64=1) && shift && goto CheckOpts +if "%1" EQU "--pgo" (set PGO=%~2) && shift && shift && goto CheckOpts +if "%1" EQU "--skip-pgo" (set PGO=) && shift && goto CheckOpts +if "%1" EQU "--skip-nuget" (set BUILDNUGET=) && shift && goto CheckOpts +if "%1" EQU "--skip-zip" (set BUILDZIP=) && shift && goto CheckOpts +if "%1" EQU "--skip-msi" (set BUILDMSI=) && shift && goto CheckOpts if "%1" NEQ "" echo Invalid option: "%1" && exit /B 1 diff -Nru python3.11-3.11.0~rc1/Tools/ssl/multissltests.py python3.11-3.11.0~rc2/Tools/ssl/multissltests.py --- python3.11-3.11.0~rc1/Tools/ssl/multissltests.py 2022-08-05 14:45:18.000000000 +0000 +++ python3.11-3.11.0~rc2/Tools/ssl/multissltests.py 2022-09-11 19:23:30.000000000 +0000 @@ -47,8 +47,8 @@ ] OPENSSL_RECENT_VERSIONS = [ - "1.1.1n", - "3.0.2" + "1.1.1q", + "3.0.5" ] LIBRESSL_OLD_VERSIONS = [