From: DongHun Kwak Date: Tue, 25 Jan 2022 23:27:40 +0000 (+0900) Subject: Imported Upstream version 3.9.2 X-Git-Tag: upstream/3.9.2^0 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=83ea9d84a67584f1471efcccce9f80346bdc5683;p=platform%2Fupstream%2Fpython3.git Imported Upstream version 3.9.2 --- diff --git a/Doc/c-api/bool.rst b/Doc/c-api/bool.rst index ce8de6e2..c197d447 100644 --- a/Doc/c-api/bool.rst +++ b/Doc/c-api/bool.rst @@ -13,7 +13,8 @@ are available, however. .. c:function:: int PyBool_Check(PyObject *o) - Return true if *o* is of type :c:data:`PyBool_Type`. + Return true if *o* is of type :c:data:`PyBool_Type`. This function always + succeeds. .. c:var:: PyObject* Py_False diff --git a/Doc/c-api/bytearray.rst b/Doc/c-api/bytearray.rst index b2f409c1..30bcfc7c 100644 --- a/Doc/c-api/bytearray.rst +++ b/Doc/c-api/bytearray.rst @@ -25,13 +25,13 @@ Type check macros .. c:function:: int PyByteArray_Check(PyObject *o) Return true if the object *o* is a bytearray object or an instance of a - subtype of the bytearray type. + subtype of the bytearray type. This function always succeeds. .. c:function:: int PyByteArray_CheckExact(PyObject *o) Return true if the object *o* is a bytearray object, but not an instance of a - subtype of the bytearray type. + subtype of the bytearray type. This function always succeeds. Direct API functions diff --git a/Doc/c-api/bytes.rst b/Doc/c-api/bytes.rst index 0e33ed2c..de657010 100644 --- a/Doc/c-api/bytes.rst +++ b/Doc/c-api/bytes.rst @@ -25,13 +25,13 @@ called with a non-bytes parameter. .. c:function:: int PyBytes_Check(PyObject *o) Return true if the object *o* is a bytes object or an instance of a subtype - of the bytes type. + of the bytes type. This function always succeeds. .. c:function:: int PyBytes_CheckExact(PyObject *o) Return true if the object *o* is a bytes object, but not an instance of a - subtype of the bytes type. + subtype of the bytes type. This function always succeeds. .. c:function:: PyObject* PyBytes_FromString(const char *v) diff --git a/Doc/c-api/capsule.rst b/Doc/c-api/capsule.rst index 5eb313c8..908e9265 100644 --- a/Doc/c-api/capsule.rst +++ b/Doc/c-api/capsule.rst @@ -34,7 +34,8 @@ Refer to :ref:`using-capsules` for more information on using these objects. .. c:function:: int PyCapsule_CheckExact(PyObject *p) - Return true if its argument is a :c:type:`PyCapsule`. + Return true if its argument is a :c:type:`PyCapsule`. This function always + succeeds. .. c:function:: PyObject* PyCapsule_New(void *pointer, const char *name, PyCapsule_Destructor destructor) diff --git a/Doc/c-api/cell.rst b/Doc/c-api/cell.rst index 8408f7e3..ac4ef5ad 100644 --- a/Doc/c-api/cell.rst +++ b/Doc/c-api/cell.rst @@ -27,7 +27,8 @@ Cell objects are not likely to be useful elsewhere. .. c:function:: int PyCell_Check(ob) - Return true if *ob* is a cell object; *ob* must not be ``NULL``. + Return true if *ob* is a cell object; *ob* must not be ``NULL``. This + function always succeeds. .. c:function:: PyObject* PyCell_New(PyObject *ob) diff --git a/Doc/c-api/code.rst b/Doc/c-api/code.rst index 6f8c41cc..b3a17f18 100644 --- a/Doc/c-api/code.rst +++ b/Doc/c-api/code.rst @@ -27,7 +27,7 @@ bound into a function. .. c:function:: int PyCode_Check(PyObject *co) - Return true if *co* is a :class:`code` object. + Return true if *co* is a :class:`code` object. This function always succeeds. .. c:function:: int PyCode_GetNumFree(PyCodeObject *co) diff --git a/Doc/c-api/complex.rst b/Doc/c-api/complex.rst index 06dbb257..e2ea766b 100644 --- a/Doc/c-api/complex.rst +++ b/Doc/c-api/complex.rst @@ -94,13 +94,13 @@ Complex Numbers as Python Objects .. c:function:: int PyComplex_Check(PyObject *p) Return true if its argument is a :c:type:`PyComplexObject` or a subtype of - :c:type:`PyComplexObject`. + :c:type:`PyComplexObject`. This function always succeeds. .. c:function:: int PyComplex_CheckExact(PyObject *p) Return true if its argument is a :c:type:`PyComplexObject`, but not a subtype of - :c:type:`PyComplexObject`. + :c:type:`PyComplexObject`. This function always succeeds. .. c:function:: PyObject* PyComplex_FromCComplex(Py_complex v) diff --git a/Doc/c-api/conversion.rst b/Doc/c-api/conversion.rst index b310fcb5..ee76acca 100644 --- a/Doc/c-api/conversion.rst +++ b/Doc/c-api/conversion.rst @@ -11,14 +11,14 @@ Functions for number conversion and formatted string output. .. c:function:: int PyOS_snprintf(char *str, size_t size, const char *format, ...) Output not more than *size* bytes to *str* according to the format string - *format* and the extra arguments. See the Unix man page :manpage:`snprintf(2)`. + *format* and the extra arguments. See the Unix man page :manpage:`snprintf(3)`. .. c:function:: int PyOS_vsnprintf(char *str, size_t size, const char *format, va_list va) Output not more than *size* bytes to *str* according to the format string *format* and the variable argument list *va*. Unix man page - :manpage:`vsnprintf(2)`. + :manpage:`vsnprintf(3)`. :c:func:`PyOS_snprintf` and :c:func:`PyOS_vsnprintf` wrap the Standard C library functions :c:func:`snprintf` and :c:func:`vsnprintf`. Their purpose is to diff --git a/Doc/c-api/coro.rst b/Doc/c-api/coro.rst index 2260944a..caa855a1 100644 --- a/Doc/c-api/coro.rst +++ b/Doc/c-api/coro.rst @@ -24,6 +24,7 @@ return. .. c:function:: int PyCoro_CheckExact(PyObject *ob) Return true if *ob*'s type is :c:type:`PyCoro_Type`; *ob* must not be ``NULL``. + This function always succeeds. .. c:function:: PyObject* PyCoro_New(PyFrameObject *frame, PyObject *name, PyObject *qualname) diff --git a/Doc/c-api/datetime.rst b/Doc/c-api/datetime.rst index bd4f1ff4..66f148df 100644 --- a/Doc/c-api/datetime.rst +++ b/Doc/c-api/datetime.rst @@ -28,61 +28,66 @@ Type-check macros: .. c:function:: int PyDate_Check(PyObject *ob) Return true if *ob* is of type :c:data:`PyDateTime_DateType` or a subtype of - :c:data:`PyDateTime_DateType`. *ob* must not be ``NULL``. + :c:data:`PyDateTime_DateType`. *ob* must not be ``NULL``. This function always + succeeds. .. c:function:: int PyDate_CheckExact(PyObject *ob) Return true if *ob* is of type :c:data:`PyDateTime_DateType`. *ob* must not be - ``NULL``. + ``NULL``. This function always succeeds. .. c:function:: int PyDateTime_Check(PyObject *ob) Return true if *ob* is of type :c:data:`PyDateTime_DateTimeType` or a subtype of - :c:data:`PyDateTime_DateTimeType`. *ob* must not be ``NULL``. + :c:data:`PyDateTime_DateTimeType`. *ob* must not be ``NULL``. This function always + succeeds. .. c:function:: int PyDateTime_CheckExact(PyObject *ob) Return true if *ob* is of type :c:data:`PyDateTime_DateTimeType`. *ob* must not - be ``NULL``. + be ``NULL``. This function always succeeds. .. c:function:: int PyTime_Check(PyObject *ob) Return true if *ob* is of type :c:data:`PyDateTime_TimeType` or a subtype of - :c:data:`PyDateTime_TimeType`. *ob* must not be ``NULL``. + :c:data:`PyDateTime_TimeType`. *ob* must not be ``NULL``. This function always + succeeds. .. c:function:: int PyTime_CheckExact(PyObject *ob) Return true if *ob* is of type :c:data:`PyDateTime_TimeType`. *ob* must not be - ``NULL``. + ``NULL``. This function always succeeds. .. c:function:: int PyDelta_Check(PyObject *ob) Return true if *ob* is of type :c:data:`PyDateTime_DeltaType` or a subtype of - :c:data:`PyDateTime_DeltaType`. *ob* must not be ``NULL``. + :c:data:`PyDateTime_DeltaType`. *ob* must not be ``NULL``. This function always + succeeds. .. c:function:: int PyDelta_CheckExact(PyObject *ob) Return true if *ob* is of type :c:data:`PyDateTime_DeltaType`. *ob* must not be - ``NULL``. + ``NULL``. This function always succeeds. .. c:function:: int PyTZInfo_Check(PyObject *ob) Return true if *ob* is of type :c:data:`PyDateTime_TZInfoType` or a subtype of - :c:data:`PyDateTime_TZInfoType`. *ob* must not be ``NULL``. + :c:data:`PyDateTime_TZInfoType`. *ob* must not be ``NULL``. This function always + succeeds. .. c:function:: int PyTZInfo_CheckExact(PyObject *ob) Return true if *ob* is of type :c:data:`PyDateTime_TZInfoType`. *ob* must not be - ``NULL``. + ``NULL``. This function always succeeds. Macros to create objects: diff --git a/Doc/c-api/dict.rst b/Doc/c-api/dict.rst index 8c626c7d..5803ac2a 100644 --- a/Doc/c-api/dict.rst +++ b/Doc/c-api/dict.rst @@ -22,13 +22,13 @@ Dictionary Objects .. c:function:: int PyDict_Check(PyObject *p) Return true if *p* is a dict object or an instance of a subtype of the dict - type. + type. This function always succeeds. .. c:function:: int PyDict_CheckExact(PyObject *p) Return true if *p* is a dict object, but not an instance of a subtype of - the dict type. + the dict type. This function always succeeds. .. c:function:: PyObject* PyDict_New() diff --git a/Doc/c-api/float.rst b/Doc/c-api/float.rst index b29937db..c107243a 100644 --- a/Doc/c-api/float.rst +++ b/Doc/c-api/float.rst @@ -22,13 +22,13 @@ Floating Point Objects .. c:function:: int PyFloat_Check(PyObject *p) Return true if its argument is a :c:type:`PyFloatObject` or a subtype of - :c:type:`PyFloatObject`. + :c:type:`PyFloatObject`. This function always succeeds. .. c:function:: int PyFloat_CheckExact(PyObject *p) Return true if its argument is a :c:type:`PyFloatObject`, but not a subtype of - :c:type:`PyFloatObject`. + :c:type:`PyFloatObject`. This function always succeeds. .. c:function:: PyObject* PyFloat_FromString(PyObject *str) diff --git a/Doc/c-api/function.rst b/Doc/c-api/function.rst index bb416f4b..20968828 100644 --- a/Doc/c-api/function.rst +++ b/Doc/c-api/function.rst @@ -26,7 +26,7 @@ There are a few functions specific to Python functions. .. c:function:: int PyFunction_Check(PyObject *o) Return true if *o* is a function object (has type :c:data:`PyFunction_Type`). - The parameter must not be ``NULL``. + The parameter must not be ``NULL``. This function always succeeds. .. c:function:: PyObject* PyFunction_New(PyObject *code, PyObject *globals) diff --git a/Doc/c-api/gen.rst b/Doc/c-api/gen.rst index 74410927..0eb5922f 100644 --- a/Doc/c-api/gen.rst +++ b/Doc/c-api/gen.rst @@ -22,12 +22,14 @@ than explicitly calling :c:func:`PyGen_New` or :c:func:`PyGen_NewWithQualName`. .. c:function:: int PyGen_Check(PyObject *ob) - Return true if *ob* is a generator object; *ob* must not be ``NULL``. + Return true if *ob* is a generator object; *ob* must not be ``NULL``. This + function always succeeds. .. c:function:: int PyGen_CheckExact(PyObject *ob) - Return true if *ob*'s type is :c:type:`PyGen_Type`; *ob* must not be ``NULL``. + Return true if *ob*'s type is :c:type:`PyGen_Type`; *ob* must not be + ``NULL``. This function always succeeds. .. c:function:: PyObject* PyGen_New(PyFrameObject *frame) diff --git a/Doc/c-api/init.rst b/Doc/c-api/init.rst index 7f06648b..eb0bd145 100644 --- a/Doc/c-api/init.rst +++ b/Doc/c-api/init.rst @@ -1155,7 +1155,7 @@ All of the following functions must be called after :c:func:`Py_Initialize`. .. versionadded:: 3.9 -.. c:function:: void _PyInterpreterState_SetEvalFrameFunc(PyInterpreterState *interp, _PyFrameEvalFunction eval_frame); +.. c:function:: void _PyInterpreterState_SetEvalFrameFunc(PyInterpreterState *interp, _PyFrameEvalFunction eval_frame) Set the frame evaluation function. diff --git a/Doc/c-api/iter.rst b/Doc/c-api/iter.rst index a2992b34..189f80b9 100644 --- a/Doc/c-api/iter.rst +++ b/Doc/c-api/iter.rst @@ -9,7 +9,8 @@ There are two functions specifically for working with iterators. .. c:function:: int PyIter_Check(PyObject *o) - Return true if the object *o* supports the iterator protocol. + Return true if the object *o* supports the iterator protocol. This + function always succeeds. .. c:function:: PyObject* PyIter_Next(PyObject *o) diff --git a/Doc/c-api/iterator.rst b/Doc/c-api/iterator.rst index 4d91e4a2..3fcf0991 100644 --- a/Doc/c-api/iterator.rst +++ b/Doc/c-api/iterator.rst @@ -21,7 +21,8 @@ sentinel value is returned. .. c:function:: int PySeqIter_Check(op) - Return true if the type of *op* is :c:data:`PySeqIter_Type`. + Return true if the type of *op* is :c:data:`PySeqIter_Type`. This function + always succeeds. .. c:function:: PyObject* PySeqIter_New(PyObject *seq) @@ -39,7 +40,8 @@ sentinel value is returned. .. c:function:: int PyCallIter_Check(op) - Return true if the type of *op* is :c:data:`PyCallIter_Type`. + Return true if the type of *op* is :c:data:`PyCallIter_Type`. This + function always succeeds. .. c:function:: PyObject* PyCallIter_New(PyObject *callable, PyObject *sentinel) diff --git a/Doc/c-api/list.rst b/Doc/c-api/list.rst index 0bc0785f..f338e2ae 100644 --- a/Doc/c-api/list.rst +++ b/Doc/c-api/list.rst @@ -22,13 +22,13 @@ List Objects .. c:function:: int PyList_Check(PyObject *p) Return true if *p* is a list object or an instance of a subtype of the list - type. + type. This function always succeeds. .. c:function:: int PyList_CheckExact(PyObject *p) Return true if *p* is a list object, but not an instance of a subtype of - the list type. + the list type. This function always succeeds. .. c:function:: PyObject* PyList_New(Py_ssize_t len) diff --git a/Doc/c-api/long.rst b/Doc/c-api/long.rst index 22e59ce1..60e1791d 100644 --- a/Doc/c-api/long.rst +++ b/Doc/c-api/long.rst @@ -27,13 +27,13 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate. .. c:function:: int PyLong_Check(PyObject *p) Return true if its argument is a :c:type:`PyLongObject` or a subtype of - :c:type:`PyLongObject`. + :c:type:`PyLongObject`. This function always succeeds. .. c:function:: int PyLong_CheckExact(PyObject *p) Return true if its argument is a :c:type:`PyLongObject`, but not a subtype of - :c:type:`PyLongObject`. + :c:type:`PyLongObject`. This function always succeeds. .. c:function:: PyObject* PyLong_FromLong(long v) diff --git a/Doc/c-api/memoryview.rst b/Doc/c-api/memoryview.rst index de429e5c..24f8c935 100644 --- a/Doc/c-api/memoryview.rst +++ b/Doc/c-api/memoryview.rst @@ -45,7 +45,8 @@ any other object. .. c:function:: int PyMemoryView_Check(PyObject *obj) Return true if the object *obj* is a memoryview object. It is not - currently allowed to create subclasses of :class:`memoryview`. + currently allowed to create subclasses of :class:`memoryview`. This + function always succeeds. .. c:function:: Py_buffer *PyMemoryView_GET_BUFFER(PyObject *mview) diff --git a/Doc/c-api/method.rst b/Doc/c-api/method.rst index 0a5341cb..23852251 100644 --- a/Doc/c-api/method.rst +++ b/Doc/c-api/method.rst @@ -22,6 +22,7 @@ to bind a :c:data:`PyCFunction` to a class object. It replaces the former call Return true if *o* is an instance method object (has type :c:data:`PyInstanceMethod_Type`). The parameter must not be ``NULL``. + This function always succeeds. .. c:function:: PyObject* PyInstanceMethod_New(PyObject *func) @@ -64,7 +65,7 @@ no longer available. .. c:function:: int PyMethod_Check(PyObject *o) Return true if *o* is a method object (has type :c:data:`PyMethod_Type`). The - parameter must not be ``NULL``. + parameter must not be ``NULL``. This function always succeeds. .. c:function:: PyObject* PyMethod_New(PyObject *func, PyObject *self) diff --git a/Doc/c-api/module.rst b/Doc/c-api/module.rst index 6e9474bf..90766dca 100644 --- a/Doc/c-api/module.rst +++ b/Doc/c-api/module.rst @@ -19,12 +19,13 @@ Module Objects .. c:function:: int PyModule_Check(PyObject *p) Return true if *p* is a module object, or a subtype of a module object. + This function always succeeds. .. c:function:: int PyModule_CheckExact(PyObject *p) Return true if *p* is a module object, but not a subtype of - :c:data:`PyModule_Type`. + :c:data:`PyModule_Type`. This function always succeeds. .. c:function:: PyObject* PyModule_NewObject(PyObject *name) diff --git a/Doc/c-api/set.rst b/Doc/c-api/set.rst index 879f394d..84f34e7d 100644 --- a/Doc/c-api/set.rst +++ b/Doc/c-api/set.rst @@ -53,28 +53,29 @@ the constructor functions work with any iterable Python object. .. c:function:: int PySet_Check(PyObject *p) Return true if *p* is a :class:`set` object or an instance of a subtype. + This function always succeeds. .. c:function:: int PyFrozenSet_Check(PyObject *p) Return true if *p* is a :class:`frozenset` object or an instance of a - subtype. + subtype. This function always succeeds. .. c:function:: int PyAnySet_Check(PyObject *p) Return true if *p* is a :class:`set` object, a :class:`frozenset` object, or an - instance of a subtype. + instance of a subtype. This function always succeeds. .. c:function:: int PyAnySet_CheckExact(PyObject *p) Return true if *p* is a :class:`set` object or a :class:`frozenset` object but - not an instance of a subtype. + not an instance of a subtype. This function always succeeds. .. c:function:: int PyFrozenSet_CheckExact(PyObject *p) Return true if *p* is a :class:`frozenset` object but not an instance of a - subtype. + subtype. This function always succeeds. .. c:function:: PyObject* PySet_New(PyObject *iterable) diff --git a/Doc/c-api/slice.rst b/Doc/c-api/slice.rst index 48a58c6c..8271d9ac 100644 --- a/Doc/c-api/slice.rst +++ b/Doc/c-api/slice.rst @@ -14,7 +14,8 @@ Slice Objects .. c:function:: int PySlice_Check(PyObject *ob) - Return true if *ob* is a slice object; *ob* must not be ``NULL``. + Return true if *ob* is a slice object; *ob* must not be ``NULL``. This + function always succeeds. .. c:function:: PyObject* PySlice_New(PyObject *start, PyObject *stop, PyObject *step) diff --git a/Doc/c-api/tuple.rst b/Doc/c-api/tuple.rst index bf751e44..1dbf7dbb 100644 --- a/Doc/c-api/tuple.rst +++ b/Doc/c-api/tuple.rst @@ -21,14 +21,14 @@ Tuple Objects .. c:function:: int PyTuple_Check(PyObject *p) - Return true if *p* is a tuple object or an instance of a subtype of the tuple - type. + Return true if *p* is a tuple object or an instance of a subtype of the + tuple type. This function always succeeds. .. c:function:: int PyTuple_CheckExact(PyObject *p) Return true if *p* is a tuple object, but not an instance of a subtype of the - tuple type. + tuple type. This function always succeeds. .. c:function:: PyObject* PyTuple_New(Py_ssize_t len) diff --git a/Doc/c-api/type.rst b/Doc/c-api/type.rst index 822360e0..ee76f522 100644 --- a/Doc/c-api/type.rst +++ b/Doc/c-api/type.rst @@ -23,12 +23,14 @@ Type Objects Return non-zero if the object *o* is a type object, including instances of types derived from the standard type object. Return 0 in all other cases. + This function always succeeds. .. c:function:: int PyType_CheckExact(PyObject *o) - Return non-zero if the object *o* is a type object, but not a subtype of the - standard type object. Return 0 in all other cases. + Return non-zero if the object *o* is a type object, but not a subtype of + the standard type object. Return 0 in all other cases. This function + always succeeds. .. c:function:: unsigned int PyType_ClearCache() diff --git a/Doc/c-api/unicode.rst b/Doc/c-api/unicode.rst index 8a312ae9..62295b4c 100644 --- a/Doc/c-api/unicode.rst +++ b/Doc/c-api/unicode.rst @@ -91,13 +91,13 @@ access internal read-only data of Unicode objects: .. c:function:: int PyUnicode_Check(PyObject *o) Return true if the object *o* is a Unicode object or an instance of a Unicode - subtype. + subtype. This function always succeeds. .. c:function:: int PyUnicode_CheckExact(PyObject *o) Return true if the object *o* is a Unicode object, but not an instance of a - subtype. + subtype. This function always succeeds. .. c:function:: int PyUnicode_READY(PyObject *o) diff --git a/Doc/c-api/weakref.rst b/Doc/c-api/weakref.rst index e3a9bda5..67698de7 100644 --- a/Doc/c-api/weakref.rst +++ b/Doc/c-api/weakref.rst @@ -13,17 +13,18 @@ as much as it can. .. c:function:: int PyWeakref_Check(ob) - Return true if *ob* is either a reference or proxy object. + Return true if *ob* is either a reference or proxy object. This function + always succeeds. .. c:function:: int PyWeakref_CheckRef(ob) - Return true if *ob* is a reference object. + Return true if *ob* is a reference object. This function always succeeds. .. c:function:: int PyWeakref_CheckProxy(ob) - Return true if *ob* is a proxy object. + Return true if *ob* is a proxy object. This function always succeeds. .. c:function:: PyObject* PyWeakref_NewRef(PyObject *ob, PyObject *callback) diff --git a/Doc/copyright.rst b/Doc/copyright.rst index 1b90d9f1..4191c0bb 100644 --- a/Doc/copyright.rst +++ b/Doc/copyright.rst @@ -4,7 +4,7 @@ Copyright Python and this documentation is: -Copyright © 2001-2020 Python Software Foundation. All rights reserved. +Copyright © 2001-2021 Python Software Foundation. All rights reserved. Copyright © 2000 BeOpen.com. All rights reserved. diff --git a/Doc/howto/descriptor.rst b/Doc/howto/descriptor.rst index ab5a573c..0f999c95 100644 --- a/Doc/howto/descriptor.rst +++ b/Doc/howto/descriptor.rst @@ -497,7 +497,7 @@ Definition and introduction In general, a descriptor is an attribute value that has one of the methods in the descriptor protocol. Those methods are :meth:`__get__`, :meth:`__set__`, -and :meth:`__delete__`. If any of those methods are defined for an the +and :meth:`__delete__`. If any of those methods are defined for an attribute, it is said to be a :term:`descriptor`. The default behavior for attribute access is to get, set, or delete the diff --git a/Doc/howto/urllib2.rst b/Doc/howto/urllib2.rst index 046a88af..12d52577 100644 --- a/Doc/howto/urllib2.rst +++ b/Doc/howto/urllib2.rst @@ -97,7 +97,7 @@ schemes. For example, you can make an FTP request like so:: In the case of HTTP, there are two extra things that Request objects allow you to do: First, you can pass data to be sent to the server. Second, you can pass -extra information ("metadata") *about* the data or the about request itself, to +extra information ("metadata") *about* the data or about the request itself, to the server - this information is sent as HTTP "headers". Let's look at each of these in turn. diff --git a/Doc/install/index.rst b/Doc/install/index.rst index e6d5a3e6..ae0d0294 100644 --- a/Doc/install/index.rst +++ b/Doc/install/index.rst @@ -1064,8 +1064,7 @@ normal libraries do. .. [#] This also means you could replace all existing COFF-libraries with OMF-libraries of the same name. -.. [#] Check https://www.sourceware.org/cygwin/ and http://www.mingw.org/ for more - information +.. [#] Check https://www.sourceware.org/cygwin/ for more information .. [#] Then you have no POSIX emulation available, but you also don't need :file:`cygwin1.dll`. diff --git a/Doc/library/argparse.rst b/Doc/library/argparse.rst index 02cd70f4..4542961d 100644 --- a/Doc/library/argparse.rst +++ b/Doc/library/argparse.rst @@ -1050,63 +1050,70 @@ command-line argument was not present:: type ^^^^ -By default, :class:`ArgumentParser` objects read command-line arguments in as simple +By default, the parser reads command-line arguments in as simple strings. However, quite often the command-line string should instead be -interpreted as another type, like a :class:`float` or :class:`int`. The -``type`` keyword argument of :meth:`~ArgumentParser.add_argument` allows any -necessary type-checking and type conversions to be performed. Common built-in -types and functions can be used directly as the value of the ``type`` argument:: +interpreted as another type, such as a :class:`float` or :class:`int`. The +``type`` keyword for :meth:`~ArgumentParser.add_argument` allows any +necessary type-checking and type conversions to be performed. - >>> parser = argparse.ArgumentParser() - >>> parser.add_argument('foo', type=int) - >>> parser.add_argument('bar', type=open) - >>> parser.parse_args('2 temp.txt'.split()) - Namespace(bar=<_io.TextIOWrapper name='temp.txt' encoding='UTF-8'>, foo=2) +If the type_ keyword is used with the default_ keyword, the type converter +is only applied if the default is a string. -See the section on the default_ keyword argument for information on when the -``type`` argument is applied to default arguments. +The argument to ``type`` can be any callable that accepts a single string. +If the function raises :exc:`ArgumentTypeError`, :exc:`TypeError`, or +:exc:`ValueError`, the exception is caught and a nicely formatted error +message is displayed. No other exception types are handled. -To ease the use of various types of files, the argparse module provides the -factory FileType which takes the ``mode=``, ``bufsize=``, ``encoding=`` and -``errors=`` arguments of the :func:`open` function. For example, -``FileType('w')`` can be used to create a writable file:: +Common built-in types and functions can be used as type converters: - >>> parser = argparse.ArgumentParser() - >>> parser.add_argument('bar', type=argparse.FileType('w')) - >>> parser.parse_args(['out.txt']) - Namespace(bar=<_io.TextIOWrapper name='out.txt' encoding='UTF-8'>) - -``type=`` can take any callable that takes a single string argument and returns -the converted value:: - - >>> def perfect_square(string): - ... value = int(string) - ... sqrt = math.sqrt(value) - ... if sqrt != int(sqrt): - ... msg = "%r is not a perfect square" % string - ... raise argparse.ArgumentTypeError(msg) - ... return value +.. testcode:: + + import argparse + import pathlib + + parser = argparse.ArgumentParser() + parser.add_argument('count', type=int) + parser.add_argument('distance', type=float) + parser.add_argument('street', type=ascii) + parser.add_argument('code_point', type=ord) + parser.add_argument('source_file', type=open) + parser.add_argument('dest_file', type=argparse.FileType('w', encoding='latin-1')) + parser.add_argument('datapath', type=pathlib.Path) + +User defined functions can be used as well: + +.. doctest:: + + >>> def hyphenated(string): + ... return '-'.join([word[:4] for word in string.casefold().split()]) ... - >>> parser = argparse.ArgumentParser(prog='PROG') - >>> parser.add_argument('foo', type=perfect_square) - >>> parser.parse_args(['9']) - Namespace(foo=9) - >>> parser.parse_args(['7']) - usage: PROG [-h] foo - PROG: error: argument foo: '7' is not a perfect square + >>> parser = argparse.ArgumentParser() + >>> _ = parser.add_argument('short_title', type=hyphenated) + >>> parser.parse_args(['"The Tale of Two Cities"']) + Namespace(short_title='"the-tale-of-two-citi') -The choices_ keyword argument may be more convenient for type checkers that -simply check against a range of values:: +The :func:`bool` function is not recommended as a type converter. All it does +is convert empty strings to ``False`` and non-empty strings to ``True``. +This is usually not what is desired. - >>> parser = argparse.ArgumentParser(prog='PROG') - >>> parser.add_argument('foo', type=int, choices=range(5, 10)) - >>> parser.parse_args(['7']) - Namespace(foo=7) - >>> parser.parse_args(['11']) - usage: PROG [-h] {5,6,7,8,9} - PROG: error: argument foo: invalid choice: 11 (choose from 5, 6, 7, 8, 9) +In general, the ``type`` keyword is a convenience that should only be used for +simple conversions that can only raise one of the three supported exceptions. +Anything with more interesting error-handling or resource management should be +done downstream after the arguments are parsed. -See the choices_ section for more details. +For example, JSON or YAML conversions have complex error cases that require +better reporting than can be given by the ``type`` keyword. An +:exc:`~json.JSONDecodeError` would not be well formatted and a +:exc:`FileNotFound` exception would not be handled at all. + +Even :class:`~argparse.FileType` has its limitations for use with the ``type`` +keyword. If one argument uses *FileType* and then a subsequent argument fails, +an error is reported but the file is not automatically closed. In this case, it +would be better to wait until after the parser has run and then use the +:keyword:`with`-statement to manage the files. + +For type checkers that simply check against a fixed set of values, consider +using the choices_ keyword instead. choices @@ -1145,6 +1152,11 @@ Any container can be passed as the *choices* value, so :class:`list` objects, Use of :class:`enum.Enum` is not recommended because it is difficult to control its appearance in usage, help, and error messages. +Formatted choices overrides the default *metavar* which is normally derived +from *dest*. This is usually what you want because the user never sees the +*dest* parameter. If this display isn't desirable (perhaps because there are +many choices), just specify an explicit metavar_. + required ^^^^^^^^ diff --git a/Doc/library/asyncio-eventloop.rst b/Doc/library/asyncio-eventloop.rst index b1e73189..be43ca4a 100644 --- a/Doc/library/asyncio-eventloop.rst +++ b/Doc/library/asyncio-eventloop.rst @@ -321,7 +321,7 @@ Creating Futures and Tasks .. versionadded:: 3.5.2 -.. method:: loop.create_task(coro, \*, name=None) +.. method:: loop.create_task(coro, *, name=None) Schedule the execution of a :ref:`coroutine`. Return a :class:`Task` object. @@ -356,7 +356,7 @@ Opening network connections ^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. coroutinemethod:: loop.create_connection(protocol_factory, \ - host=None, port=None, \*, ssl=None, \ + host=None, port=None, *, ssl=None, \ family=0, proto=0, flags=0, sock=None, \ local_addr=None, server_hostname=None, \ ssl_handshake_timeout=None, \ @@ -482,7 +482,7 @@ Opening network connections that can be used directly in async/await code. .. coroutinemethod:: loop.create_datagram_endpoint(protocol_factory, \ - local_addr=None, remote_addr=None, \*, \ + local_addr=None, remote_addr=None, *, \ family=0, proto=0, flags=0, \ reuse_address=None, reuse_port=None, \ allow_broadcast=None, sock=None) @@ -559,7 +559,7 @@ Opening network connections Added support for Windows. .. coroutinemethod:: loop.create_unix_connection(protocol_factory, \ - path=None, \*, ssl=None, sock=None, \ + path=None, *, ssl=None, sock=None, \ server_hostname=None, ssl_handshake_timeout=None) Create a Unix connection. @@ -592,7 +592,7 @@ Creating network servers ^^^^^^^^^^^^^^^^^^^^^^^^ .. coroutinemethod:: loop.create_server(protocol_factory, \ - host=None, port=None, \*, \ + host=None, port=None, *, \ family=socket.AF_UNSPEC, \ flags=socket.AI_PASSIVE, \ sock=None, backlog=100, ssl=None, \ @@ -683,7 +683,7 @@ Creating network servers .. coroutinemethod:: loop.create_unix_server(protocol_factory, path=None, \ - \*, sock=None, backlog=100, ssl=None, \ + *, sock=None, backlog=100, ssl=None, \ ssl_handshake_timeout=None, start_serving=True) Similar to :meth:`loop.create_server` but works with the @@ -708,7 +708,7 @@ Creating network servers The *path* parameter can now be a :class:`~pathlib.Path` object. .. coroutinemethod:: loop.connect_accepted_socket(protocol_factory, \ - sock, \*, ssl=None, ssl_handshake_timeout=None) + sock, *, ssl=None, ssl_handshake_timeout=None) Wrap an already accepted connection into a transport/protocol pair. @@ -773,7 +773,7 @@ TLS Upgrade ^^^^^^^^^^^ .. coroutinemethod:: loop.start_tls(transport, protocol, \ - sslcontext, \*, server_side=False, \ + sslcontext, *, server_side=False, \ server_hostname=None, ssl_handshake_timeout=None) Upgrade an existing transport-based connection to TLS. @@ -806,7 +806,7 @@ TLS Upgrade Watching file descriptors ^^^^^^^^^^^^^^^^^^^^^^^^^ -.. method:: loop.add_reader(fd, callback, \*args) +.. method:: loop.add_reader(fd, callback, *args) Start monitoring the *fd* file descriptor for read availability and invoke *callback* with the specified arguments once *fd* is available for @@ -816,7 +816,7 @@ Watching file descriptors Stop monitoring the *fd* file descriptor for read availability. -.. method:: loop.add_writer(fd, callback, \*args) +.. method:: loop.add_writer(fd, callback, *args) Start monitoring the *fd* file descriptor for write availability and invoke *callback* with the specified arguments once *fd* is available for @@ -930,7 +930,7 @@ convenient. :meth:`loop.create_server` and :func:`start_server`. .. coroutinemethod:: loop.sock_sendfile(sock, file, offset=0, count=None, \ - \*, fallback=True) + *, fallback=True) Send a file using high-performance :mod:`os.sendfile` if possible. Return the total number of bytes sent. @@ -964,7 +964,7 @@ convenient. DNS ^^^ -.. coroutinemethod:: loop.getaddrinfo(host, port, \*, family=0, \ +.. coroutinemethod:: loop.getaddrinfo(host, port, *, family=0, \ type=0, proto=0, flags=0) Asynchronous version of :meth:`socket.getaddrinfo`. @@ -1029,7 +1029,7 @@ Working with pipes Unix signals ^^^^^^^^^^^^ -.. method:: loop.add_signal_handler(signum, callback, \*args) +.. method:: loop.add_signal_handler(signum, callback, *args) Set *callback* as the handler for the *signum* signal. @@ -1064,7 +1064,7 @@ Unix signals Executing code in thread or process pools ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. awaitablemethod:: loop.run_in_executor(executor, func, \*args) +.. awaitablemethod:: loop.run_in_executor(executor, func, *args) Arrange for *func* to be called in the specified executor. @@ -1234,9 +1234,9 @@ async/await code consider using the high-level subprocesses. See :ref:`Subprocess Support on Windows ` for details. -.. coroutinemethod:: loop.subprocess_exec(protocol_factory, \*args, \ +.. coroutinemethod:: loop.subprocess_exec(protocol_factory, *args, \ stdin=subprocess.PIPE, stdout=subprocess.PIPE, \ - stderr=subprocess.PIPE, \*\*kwargs) + stderr=subprocess.PIPE, **kwargs) Create a subprocess from one or more string arguments specified by *args*. @@ -1316,9 +1316,9 @@ async/await code consider using the high-level conforms to the :class:`asyncio.SubprocessTransport` base class and *protocol* is an object instantiated by the *protocol_factory*. -.. coroutinemethod:: loop.subprocess_shell(protocol_factory, cmd, \*, \ +.. coroutinemethod:: loop.subprocess_shell(protocol_factory, cmd, *, \ stdin=subprocess.PIPE, stdout=subprocess.PIPE, \ - stderr=subprocess.PIPE, \*\*kwargs) + stderr=subprocess.PIPE, **kwargs) Create a subprocess from *cmd*, which can be a :class:`str` or a :class:`bytes` string encoded to the diff --git a/Doc/library/asyncio-future.rst b/Doc/library/asyncio-future.rst index e1ac18ea..939d4c1a 100644 --- a/Doc/library/asyncio-future.rst +++ b/Doc/library/asyncio-future.rst @@ -31,7 +31,7 @@ Future Functions .. versionadded:: 3.5 -.. function:: ensure_future(obj, \*, loop=None) +.. function:: ensure_future(obj, *, loop=None) Return: @@ -58,7 +58,7 @@ Future Functions The function accepts any :term:`awaitable` object. -.. function:: wrap_future(future, \*, loop=None) +.. function:: wrap_future(future, *, loop=None) Wrap a :class:`concurrent.futures.Future` object in a :class:`asyncio.Future` object. @@ -67,7 +67,7 @@ Future Functions Future Object ============= -.. class:: Future(\*, loop=None) +.. class:: Future(*, loop=None) A Future represents an eventual result of an asynchronous operation. Not thread-safe. diff --git a/Doc/library/asyncio-policy.rst b/Doc/library/asyncio-policy.rst index 5e69525e..ef6a0588 100644 --- a/Doc/library/asyncio-policy.rst +++ b/Doc/library/asyncio-policy.rst @@ -159,7 +159,7 @@ implementation used by the asyncio event loop: .. class:: AbstractChildWatcher - .. method:: add_child_handler(pid, callback, \*args) + .. method:: add_child_handler(pid, callback, *args) Register a new child handler. diff --git a/Doc/library/asyncio-stream.rst b/Doc/library/asyncio-stream.rst index b76ed379..714de8d4 100644 --- a/Doc/library/asyncio-stream.rst +++ b/Doc/library/asyncio-stream.rst @@ -48,7 +48,7 @@ The following top-level asyncio functions can be used to create and work with streams: -.. coroutinefunction:: open_connection(host=None, port=None, \*, \ +.. coroutinefunction:: open_connection(host=None, port=None, *, \ loop=None, limit=None, ssl=None, family=0, \ proto=0, flags=0, sock=None, local_addr=None, \ server_hostname=None, ssl_handshake_timeout=None) @@ -74,7 +74,7 @@ and work with streams: The *ssl_handshake_timeout* parameter. .. coroutinefunction:: start_server(client_connected_cb, host=None, \ - port=None, \*, loop=None, limit=None, \ + port=None, *, loop=None, limit=None, \ family=socket.AF_UNSPEC, \ flags=socket.AI_PASSIVE, sock=None, \ backlog=100, ssl=None, reuse_address=None, \ @@ -109,7 +109,7 @@ and work with streams: .. rubric:: Unix Sockets -.. coroutinefunction:: open_unix_connection(path=None, \*, loop=None, \ +.. coroutinefunction:: open_unix_connection(path=None, *, loop=None, \ limit=None, ssl=None, sock=None, \ server_hostname=None, ssl_handshake_timeout=None) @@ -132,7 +132,7 @@ and work with streams: .. coroutinefunction:: start_unix_server(client_connected_cb, path=None, \ - \*, loop=None, limit=None, sock=None, \ + *, loop=None, limit=None, sock=None, \ backlog=100, ssl=None, ssl_handshake_timeout=None, \ start_serving=True) diff --git a/Doc/library/asyncio-subprocess.rst b/Doc/library/asyncio-subprocess.rst index b0330349..fb98552c 100644 --- a/Doc/library/asyncio-subprocess.rst +++ b/Doc/library/asyncio-subprocess.rst @@ -61,9 +61,9 @@ See also the `Examples`_ subsection. Creating Subprocesses ===================== -.. coroutinefunction:: create_subprocess_exec(program, \*args, stdin=None, \ +.. coroutinefunction:: create_subprocess_exec(program, *args, stdin=None, \ stdout=None, stderr=None, loop=None, \ - limit=None, \*\*kwds) + limit=None, **kwds) Create a subprocess. @@ -82,7 +82,7 @@ Creating Subprocesses .. coroutinefunction:: create_subprocess_shell(cmd, stdin=None, \ stdout=None, stderr=None, loop=None, \ - limit=None, \*\*kwds) + limit=None, **kwds) Run the *cmd* shell command. diff --git a/Doc/library/asyncio-sync.rst b/Doc/library/asyncio-sync.rst index 84a52cb2..f62ce670 100644 --- a/Doc/library/asyncio-sync.rst +++ b/Doc/library/asyncio-sync.rst @@ -104,8 +104,8 @@ Event that some event has happened. An Event object manages an internal flag that can be set to *true* - with the :meth:`set` method and reset to *false* with the - :meth:`clear` method. The :meth:`wait` method blocks until the + with the :meth:`~Event.set` method and reset to *false* with the + :meth:`clear` method. The :meth:`~Event.wait` method blocks until the flag is set to *true*. The flag is set to *false* initially. @@ -142,7 +142,7 @@ Event Wait until the event is set. If the event is set, return ``True`` immediately. - Otherwise block until another task calls :meth:`set`. + Otherwise block until another task calls :meth:`~Event.set`. .. method:: set() @@ -155,8 +155,8 @@ Event Clear (unset) the event. - Tasks awaiting on :meth:`wait` will now block until the - :meth:`set` method is called again. + Tasks awaiting on :meth:`~Event.wait` will now block until the + :meth:`~Event.set` method is called again. .. method:: is_set() diff --git a/Doc/library/asyncio-task.rst b/Doc/library/asyncio-task.rst index c638f126..201678b0 100644 --- a/Doc/library/asyncio-task.rst +++ b/Doc/library/asyncio-task.rst @@ -210,7 +210,7 @@ is :meth:`loop.run_in_executor`. Running an asyncio Program ========================== -.. function:: run(coro, \*, debug=False) +.. function:: run(coro, *, debug=False) Execute the :term:`coroutine` *coro* and return the result. @@ -247,7 +247,7 @@ Running an asyncio Program Creating Tasks ============== -.. function:: create_task(coro, \*, name=None) +.. function:: create_task(coro, *, name=None) Wrap the *coro* :ref:`coroutine ` into a :class:`Task` and schedule its execution. Return the Task object. @@ -283,7 +283,7 @@ Creating Tasks Sleeping ======== -.. coroutinefunction:: sleep(delay, result=None, \*, loop=None) +.. coroutinefunction:: sleep(delay, result=None, *, loop=None) Block for *delay* seconds. @@ -319,7 +319,7 @@ Sleeping Running Tasks Concurrently ========================== -.. awaitablefunction:: gather(\*aws, loop=None, return_exceptions=False) +.. awaitablefunction:: gather(*aws, loop=None, return_exceptions=False) Run :ref:`awaitable objects ` in the *aws* sequence *concurrently*. @@ -403,7 +403,7 @@ Running Tasks Concurrently Shielding From Cancellation =========================== -.. awaitablefunction:: shield(aw, \*, loop=None) +.. awaitablefunction:: shield(aw, *, loop=None) Protect an :ref:`awaitable object ` from being :meth:`cancelled `. @@ -443,7 +443,7 @@ Shielding From Cancellation Timeouts ======== -.. coroutinefunction:: wait_for(aw, timeout, \*, loop=None) +.. coroutinefunction:: wait_for(aw, timeout, *, loop=None) Wait for the *aw* :ref:`awaitable ` to complete with a timeout. @@ -500,7 +500,7 @@ Timeouts Waiting Primitives ================== -.. coroutinefunction:: wait(aws, \*, loop=None, timeout=None,\ +.. coroutinefunction:: wait(aws, *, loop=None, timeout=None,\ return_when=ALL_COMPLETED) Run :ref:`awaitable objects ` in the *aws* @@ -590,7 +590,7 @@ Waiting Primitives deprecated. -.. function:: as_completed(aws, \*, loop=None, timeout=None) +.. function:: as_completed(aws, *, loop=None, timeout=None) Run :ref:`awaitable objects ` in the *aws* iterable concurrently. Return an iterator of coroutines. @@ -613,7 +613,7 @@ Waiting Primitives Running in Threads ================== -.. coroutinefunction:: to_thread(func, /, \*args, \*\*kwargs) +.. coroutinefunction:: to_thread(func, /, *args, **kwargs) Asynchronously run function *func* in a separate thread. @@ -743,7 +743,7 @@ Introspection Task Object =========== -.. class:: Task(coro, \*, loop=None, name=None) +.. class:: Task(coro, *, loop=None, name=None) A :class:`Future-like ` object that runs a Python :ref:`coroutine `. Not thread-safe. @@ -909,7 +909,7 @@ Task Object See the documentation of :meth:`Future.remove_done_callback` for more details. - .. method:: get_stack(\*, limit=None) + .. method:: get_stack(*, limit=None) Return the list of stack frames for this Task. @@ -930,7 +930,7 @@ Task Object stack are returned, but the oldest frames of a traceback are returned. (This matches the behavior of the traceback module.) - .. method:: print_stack(\*, limit=None, file=None) + .. method:: print_stack(*, limit=None, file=None) Print the stack or traceback for this Task. diff --git a/Doc/library/cgi.rst b/Doc/library/cgi.rst index 4048592e..e60a3f13 100644 --- a/Doc/library/cgi.rst +++ b/Doc/library/cgi.rst @@ -277,14 +277,14 @@ These are useful if you want more control, or if you want to employ some of the algorithms implemented in this module in other circumstances. -.. function:: parse(fp=None, environ=os.environ, keep_blank_values=False, strict_parsing=False) +.. function:: parse(fp=None, environ=os.environ, keep_blank_values=False, strict_parsing=False, separator="&") Parse a query in the environment or from a file (the file defaults to - ``sys.stdin``). The *keep_blank_values* and *strict_parsing* parameters are + ``sys.stdin``). The *keep_blank_values*, *strict_parsing* and *separator* parameters are passed to :func:`urllib.parse.parse_qs` unchanged. -.. function:: parse_multipart(fp, pdict, encoding="utf-8", errors="replace") +.. function:: parse_multipart(fp, pdict, encoding="utf-8", errors="replace", separator="&") Parse input of type :mimetype:`multipart/form-data` (for file uploads). Arguments are *fp* for the input file, *pdict* for a dictionary containing @@ -303,6 +303,9 @@ algorithms implemented in this module in other circumstances. Added the *encoding* and *errors* parameters. For non-file fields, the value is now a list of strings, not bytes. + .. versionchanged:: 3.9.2 + Added the *separator* parameter. + .. function:: parse_header(string) diff --git a/Doc/library/codecs.rst b/Doc/library/codecs.rst index f0710572..6eb907af 100644 --- a/Doc/library/codecs.rst +++ b/Doc/library/codecs.rst @@ -694,7 +694,7 @@ compatible with the Python codec registry. .. method:: reset() - Flushes and resets the codec buffers used for keeping state. + Resets the codec buffers used for keeping internal state. Calling this method should ensure that the data on the output is put into a clean state that allows appending of new fresh data without having to @@ -789,7 +789,7 @@ compatible with the Python codec registry. .. method:: reset() - Resets the codec buffers used for keeping state. + Resets the codec buffers used for keeping internal state. Note that no stream repositioning should take place. This method is primarily intended to be able to recover from decoding errors. diff --git a/Doc/library/compileall.rst b/Doc/library/compileall.rst index 9b914b1f..5c6e68f9 100644 --- a/Doc/library/compileall.rst +++ b/Doc/library/compileall.rst @@ -148,7 +148,7 @@ runtime. Public functions ---------------- -.. function:: compile_dir(dir, maxlevels=sys.getrecursionlimit(), ddir=None, force=False, rx=None, quiet=0, legacy=False, optimize=-1, workers=1, invalidation_mode=None, \*, stripdir=None, prependdir=None, limit_sl_dest=None, hardlink_dupes=False) +.. function:: compile_dir(dir, maxlevels=sys.getrecursionlimit(), ddir=None, force=False, rx=None, quiet=0, legacy=False, optimize=-1, workers=1, invalidation_mode=None, *, stripdir=None, prependdir=None, limit_sl_dest=None, hardlink_dupes=False) Recursively descend the directory tree named by *dir*, compiling all :file:`.py` files along the way. Return a true value if all the files compiled successfully, @@ -231,7 +231,7 @@ Public functions Added *stripdir*, *prependdir*, *limit_sl_dest* and *hardlink_dupes* arguments. Default value of *maxlevels* was changed from ``10`` to ``sys.getrecursionlimit()`` -.. function:: compile_file(fullname, ddir=None, force=False, rx=None, quiet=0, legacy=False, optimize=-1, invalidation_mode=None, \*, stripdir=None, prependdir=None, limit_sl_dest=None, hardlink_dupes=False) +.. function:: compile_file(fullname, ddir=None, force=False, rx=None, quiet=0, legacy=False, optimize=-1, invalidation_mode=None, *, stripdir=None, prependdir=None, limit_sl_dest=None, hardlink_dupes=False) Compile the file with path *fullname*. Return a true value if the file compiled successfully, and a false value otherwise. diff --git a/Doc/library/concurrent.futures.rst b/Doc/library/concurrent.futures.rst index 61d6c114..d57f8ce2 100644 --- a/Doc/library/concurrent.futures.rst +++ b/Doc/library/concurrent.futures.rst @@ -67,7 +67,7 @@ Executor Objects .. versionchanged:: 3.5 Added the *chunksize* argument. - .. method:: shutdown(wait=True, \*, cancel_futures=False) + .. method:: shutdown(wait=True, *, cancel_futures=False) Signal the executor that it should free any resources that it is using when the currently pending futures are done executing. Calls to diff --git a/Doc/library/contextvars.rst b/Doc/library/contextvars.rst index 8805661c..14ac47f4 100644 --- a/Doc/library/contextvars.rst +++ b/Doc/library/contextvars.rst @@ -26,7 +26,7 @@ See also :pep:`567` for additional details. Context Variables ----------------- -.. class:: ContextVar(name, [\*, default]) +.. class:: ContextVar(name, [*, default]) This class is used to declare a new Context Variable, e.g.:: @@ -146,7 +146,7 @@ Manual Context Management Context implements the :class:`collections.abc.Mapping` interface. - .. method:: run(callable, \*args, \*\*kwargs) + .. method:: run(callable, *args, **kwargs) Execute ``callable(*args, **kwargs)`` code in the context object the *run* method is called on. Return the result of the execution diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index bf32d3e5..73131487 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -2508,7 +2508,7 @@ other data types containing pointer type fields. Arrays and pointers ^^^^^^^^^^^^^^^^^^^ -.. class:: Array(\*args) +.. class:: Array(*args) Abstract base class for arrays. diff --git a/Doc/library/curses.rst b/Doc/library/curses.rst index 7a13295f..c72840a0 100644 --- a/Doc/library/curses.rst +++ b/Doc/library/curses.rst @@ -112,14 +112,15 @@ The module :mod:`curses` defines the following functions: .. function:: color_content(color_number) Return the intensity of the red, green, and blue (RGB) components in the color - *color_number*, which must be between ``0`` and :const:`COLORS`. Return a 3-tuple, + *color_number*, which must be between ``0`` and ``COLORS - 1``. Return a 3-tuple, containing the R,G,B values for the given color, which will be between ``0`` (no component) and ``1000`` (maximum amount of component). -.. function:: color_pair(color_number) +.. function:: color_pair(pair_number) - Return the attribute value for displaying text in the specified color. This + Return the attribute value for displaying text in the specified color pair. + Only the first 256 color pairs are supported. This attribute value can be combined with :const:`A_STANDOUT`, :const:`A_REVERSE`, and the other :const:`A_\*` attributes. :func:`pair_number` is the counterpart to this function. @@ -278,7 +279,7 @@ The module :mod:`curses` defines the following functions: Change the definition of a color, taking the number of the color to be changed followed by three RGB values (for the amounts of red, green, and blue components). The value of *color_number* must be between ``0`` and - :const:`COLORS`. Each of *r*, *g*, *b*, must be a value between ``0`` and + `COLORS - 1`. Each of *r*, *g*, *b*, must be a value between ``0`` and ``1000``. When :func:`init_color` is used, all occurrences of that color on the screen immediately change to the new definition. This function is a no-op on most terminals; it is active only if :func:`can_change_color` returns ``True``. @@ -291,7 +292,8 @@ The module :mod:`curses` defines the following functions: color number. The value of *pair_number* must be between ``1`` and ``COLOR_PAIRS - 1`` (the ``0`` color pair is wired to white on black and cannot be changed). The value of *fg* and *bg* arguments must be between ``0`` and - :const:`COLORS`. If the color-pair was previously initialized, the screen is + ``COLORS - 1``, or, after calling :func:`use_default_colors`, ``-1``. + If the color-pair was previously initialized, the screen is refreshed and all occurrences of that color-pair are changed to the new definition. @@ -441,7 +443,7 @@ The module :mod:`curses` defines the following functions: .. function:: pair_content(pair_number) Return a tuple ``(fg, bg)`` containing the colors for the requested color pair. - The value of *pair_number* must be between ``1`` and ``COLOR_PAIRS - 1``. + The value of *pair_number* must be between ``0`` and ``COLOR_PAIRS - 1``. .. function:: pair_number(attr) diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst index 508bc88e..dae0dd7a 100644 --- a/Doc/library/datetime.rst +++ b/Doc/library/datetime.rst @@ -1219,7 +1219,7 @@ Instance methods: .. method:: datetime.replace(year=self.year, month=self.month, day=self.day, \ hour=self.hour, minute=self.minute, second=self.second, microsecond=self.microsecond, \ - tzinfo=self.tzinfo, * fold=0) + tzinfo=self.tzinfo, *, fold=0) Return a datetime with the same attributes, except for those attributes given new values by whichever keyword arguments are specified. Note that @@ -1783,7 +1783,7 @@ Other constructor: Instance methods: .. method:: time.replace(hour=self.hour, minute=self.minute, second=self.second, \ - microsecond=self.microsecond, tzinfo=self.tzinfo, * fold=0) + microsecond=self.microsecond, tzinfo=self.tzinfo, *, fold=0) Return a :class:`.time` with the same value, except for those attributes given new values by whichever keyword arguments are specified. Note that diff --git a/Doc/library/dialog.rst b/Doc/library/dialog.rst index dc82a974..53f98c10 100644 --- a/Doc/library/dialog.rst +++ b/Doc/library/dialog.rst @@ -198,7 +198,7 @@ These do not emulate the native look-and-feel of the platform. A subclass of FileDialog that creates a dialog window for selecting a destination file. - .. method:: ok_command() + .. method:: ok_command() Test whether or not the selection points to a valid file that is not a directory. Confirmation is required if an already existing file is diff --git a/Doc/library/email.contentmanager.rst b/Doc/library/email.contentmanager.rst index e09c7c0e..918fc556 100644 --- a/Doc/library/email.contentmanager.rst +++ b/Doc/library/email.contentmanager.rst @@ -116,7 +116,7 @@ Currently the email package provides only one concrete content manager, decoding the payload to unicode. The default error handler is ``replace``. - .. method:: set_content(msg, <'str'>, subtype="plain", charset='utf-8' \ + .. method:: set_content(msg, <'str'>, subtype="plain", charset='utf-8', \ cte=None, \ disposition=None, filename=None, cid=None, \ params=None, headers=None) diff --git a/Doc/library/email.headerregistry.rst b/Doc/library/email.headerregistry.rst index 9376da2b..3e1d97a0 100644 --- a/Doc/library/email.headerregistry.rst +++ b/Doc/library/email.headerregistry.rst @@ -289,7 +289,7 @@ variant, :attr:`~.BaseHeader.max_count` is set to 1. A :class:`ParameterizedMIMEHeader` class that handles the :mailheader:`Content-Disposition` header. - .. attribute:: content-disposition + .. attribute:: content_disposition ``inline`` and ``attachment`` are the only valid values in common use. diff --git a/Doc/library/email.policy.rst b/Doc/library/email.policy.rst index 8e707625..bf53b952 100644 --- a/Doc/library/email.policy.rst +++ b/Doc/library/email.policy.rst @@ -210,7 +210,7 @@ added matters. To illustrate:: :meth:`register_defect` method. - .. attribute:: mangle_from\_ + .. attribute:: mangle_from_ If :const:`True`, lines starting with *"From "* in the body are escaped by putting a ``>`` in front of them. This parameter is used when diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst index a3c51655..8836b249 100644 --- a/Doc/library/enum.rst +++ b/Doc/library/enum.rst @@ -56,7 +56,7 @@ helper, :class:`auto`. the bitwise operations without losing their :class:`Flag` membership. .. function:: unique - :noindex: + :noindex: Enum class decorator that ensures only one name is bound to any one value. @@ -1121,6 +1121,15 @@ and raise an error if the two do not match:: In Python 2 code the :attr:`_order_` attribute is necessary as definition order is lost before it can be recorded. + +_Private__names +""""""""""""""" + +Private names will be normal attributes in Python 3.10 instead of either an error +or a member (depending on if the name ends with an underscore). Using these names +in 3.9 will issue a :exc:`DeprecationWarning`. + + ``Enum`` member type """""""""""""""""""" diff --git a/Doc/library/fnmatch.rst b/Doc/library/fnmatch.rst index ce07d326..925f08e9 100644 --- a/Doc/library/fnmatch.rst +++ b/Doc/library/fnmatch.rst @@ -75,7 +75,7 @@ patterns. .. function:: filter(names, pattern) - Return the subset of the list of *names* that match *pattern*. It is the same as + Construct a list from those elements of the iterable *names* that match *pattern*. It is the same as ``[n for n in names if fnmatch(n, pattern)]``, but implemented more efficiently. diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index 9c12b6c4..990fc10c 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -508,7 +508,8 @@ are always available. They are listed here in alphabetical order. occurs). [#]_ If it is a code object, it is simply executed. In all cases, the code that's executed is expected to be valid as file input (see the section "File input" in the Reference Manual). Be aware that the - :keyword:`return` and :keyword:`yield` statements may not be used outside of + :keyword:`nonlocal`, :keyword:`yield`, and :keyword:`return` + statements may not be used outside of function definitions even within the context of code passed to the :func:`exec` function. The return value is ``None``. @@ -1687,18 +1688,19 @@ are always available. They are listed here in alphabetical order. With three arguments, return a new type object. This is essentially a - dynamic form of the :keyword:`class` statement. The *name* string is the - class name and becomes the :attr:`~definition.__name__` attribute; the *bases* - tuple itemizes the base classes and becomes the :attr:`~class.__bases__` - attribute; and the *dict* dictionary is the namespace containing definitions - for class body and is copied to a standard dictionary to become the - :attr:`~object.__dict__` attribute. For example, the following two - statements create identical :class:`type` objects: + dynamic form of the :keyword:`class` statement. The *name* string is + the class name and becomes the :attr:`~definition.__name__` attribute. + The *bases* tuple contains the base classes and becomes the + :attr:`~class.__bases__` attribute; if empty, :class:`object`, the + ultimate base of all classes, is added. The *dict* dictionary contains + attribute and method definitions for the class body; it may be copied + or wrapped before becoming the :attr:`~object.__dict__` attribute. + The following two statements create identical :class:`type` objects: >>> class X: ... a = 1 ... - >>> X = type('X', (object,), dict(a=1)) + >>> X = type('X', (), dict(a=1)) See also :ref:`bltin-type-objects`. diff --git a/Doc/library/functools.rst b/Doc/library/functools.rst index 4869b67c..85d4e746 100644 --- a/Doc/library/functools.rst +++ b/Doc/library/functools.rst @@ -62,16 +62,26 @@ The :mod:`functools` module defines the following functions: Example:: class DataSet: + def __init__(self, sequence_of_numbers): - self._data = sequence_of_numbers + self._data = tuple(sequence_of_numbers) @cached_property def stdev(self): return statistics.stdev(self._data) - @cached_property - def variance(self): - return statistics.variance(self._data) + The mechanics of :func:`cached_property` are somewhat different from + :func:`property`. A regular property blocks attribute writes unless a + setter is defined. In contrast, a *cached_property* allows writes. + + The *cached_property* decorator only runs on lookups and only when an + attribute of the same name doesn't exist. When it does run, the + *cached_property* writes to the attribute with the same name. Subsequent + attribute reads and writes take precedence over the *cached_property* + method and it works like a normal attribute. + + The cached value can be cleared by deleting the attribute. This + allows the *cached_property* method to run again. Note, this decorator interferes with the operation of :pep:`412` key-sharing dictionaries. This means that instance dictionaries diff --git a/Doc/library/idle.rst b/Doc/library/idle.rst index a59a5d3a..e7eaabd8 100644 --- a/Doc/library/idle.rst +++ b/Doc/library/idle.rst @@ -250,7 +250,7 @@ View Last Restart Scroll the shell window to the last Shell restart. Restart Shell - Restart the shell to clean the environment. + Restart the shell to clean the environment and reset display and exception handling. Previous History Cycle through earlier commands in history which match the current entry. diff --git a/Doc/library/importlib.metadata.rst b/Doc/library/importlib.metadata.rst index 21da143f..0dd3daaa 100644 --- a/Doc/library/importlib.metadata.rst +++ b/Doc/library/importlib.metadata.rst @@ -206,9 +206,9 @@ Thus, an alternative way to get the version number is through the There are all kinds of additional metadata available on the ``Distribution`` instance:: - >>> d.metadata['Requires-Python'] # doctest: +SKIP + >>> dist.metadata['Requires-Python'] # doctest: +SKIP '>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*' - >>> d.metadata['License'] # doctest: +SKIP + >>> dist.metadata['License'] # doctest: +SKIP 'MIT' The full set of available metadata is not described here. See :pep:`566` diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst index 9027ba58..cb2a5686 100644 --- a/Doc/library/importlib.rst +++ b/Doc/library/importlib.rst @@ -1135,7 +1135,7 @@ find and load modules. directory for ``''`` (i.e. the empty string). -.. class:: FileFinder(path, \*loader_details) +.. class:: FileFinder(path, *loader_details) A concrete implementation of :class:`importlib.abc.PathEntryFinder` which caches results from the file system. @@ -1178,7 +1178,7 @@ find and load modules. Clear out the internal cache. - .. classmethod:: path_hook(\*loader_details) + .. classmethod:: path_hook(*loader_details) A class method which returns a closure for use on :attr:`sys.path_hooks`. An instance of :class:`FileFinder` is returned by the closure using the @@ -1473,7 +1473,7 @@ an :term:`importer`. If **name** has no leading dots, then **name** is simply returned. This allows for usage such as - ``importlib.util.resolve_name('sys', __package__)`` without doing a + ``importlib.util.resolve_name('sys', __spec__.parent)`` without doing a check to see if the **package** argument is needed. :exc:`ImportError` is raised if **name** is a relative module name but diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst index d00a30ff..b53a9421 100644 --- a/Doc/library/inspect.rst +++ b/Doc/library/inspect.rst @@ -556,7 +556,7 @@ The Signature object represents the call signature of a callable object and its return annotation. To retrieve a Signature object, use the :func:`signature` function. -.. function:: signature(callable, \*, follow_wrapped=True) +.. function:: signature(callable, *, follow_wrapped=True) Return a :class:`Signature` object for the given ``callable``:: @@ -597,7 +597,7 @@ function. C provide no metadata about their arguments. -.. class:: Signature(parameters=None, \*, return_annotation=Signature.empty) +.. class:: Signature(parameters=None, *, return_annotation=Signature.empty) A Signature object represents the call signature of a function and its return annotation. For each parameter accepted by the function it stores a @@ -668,7 +668,7 @@ function. >>> str(new_sig) "(a, b) -> 'new return anno'" - .. classmethod:: Signature.from_callable(obj, \*, follow_wrapped=True) + .. classmethod:: Signature.from_callable(obj, *, follow_wrapped=True) Return a :class:`Signature` (or its subclass) object for a given callable ``obj``. Pass ``follow_wrapped=False`` to get a signature of ``obj`` @@ -684,7 +684,7 @@ function. .. versionadded:: 3.5 -.. class:: Parameter(name, kind, \*, default=Parameter.empty, annotation=Parameter.empty) +.. class:: Parameter(name, kind, *, default=Parameter.empty, annotation=Parameter.empty) Parameter objects are *immutable*. Instead of modifying a Parameter object, you can use :meth:`Parameter.replace` to create a modified copy. diff --git a/Doc/library/itertools.rst b/Doc/library/itertools.rst index 3de66c93..6da55f8a 100644 --- a/Doc/library/itertools.rst +++ b/Doc/library/itertools.rst @@ -565,7 +565,7 @@ loops that truncate the stream. Before :func:`product` runs, it completely consumes the input iterables, keeping pools of values in memory to generate the products. Accordingly, - it only useful with finite inputs. + it is only useful with finite inputs. .. function:: repeat(object[, times]) @@ -769,6 +769,18 @@ which incur interpreter overhead. def dotproduct(vec1, vec2): return sum(map(operator.mul, vec1, vec2)) + def convolve(signal, kernel): + # See: https://betterexplained.com/articles/intuitive-convolution/ + # convolve(data, [0.25, 0.25, 0.25, 0.25]) --> Moving average (blur) + # convolve(data, [1, -1]) --> 1st finite difference (1st derivative) + # convolve(data, [1, -2, 1]) --> 2nd finite difference (2nd derivative) + kernel = tuple(kernel)[::-1] + n = len(kernel) + window = collections.deque([0], maxlen=n) * n + for x in chain(signal, repeat(0, n-1)): + window.append(x) + yield sum(map(operator.mul, kernel, window)) + def flatten(list_of_lists): "Flatten one level of nesting" return chain.from_iterable(list_of_lists) diff --git a/Doc/library/logging.config.rst b/Doc/library/logging.config.rst index 683d6ed5..0b5e2fc2 100644 --- a/Doc/library/logging.config.rst +++ b/Doc/library/logging.config.rst @@ -35,45 +35,45 @@ in :mod:`logging` itself) and defining handlers which are declared either in .. function:: dictConfig(config) - Takes the logging configuration from a dictionary. The contents of - this dictionary are described in :ref:`logging-config-dictschema` - below. - - If an error is encountered during configuration, this function will - raise a :exc:`ValueError`, :exc:`TypeError`, :exc:`AttributeError` - or :exc:`ImportError` with a suitably descriptive message. The - following is a (possibly incomplete) list of conditions which will - raise an error: - - * A ``level`` which is not a string or which is a string not - corresponding to an actual logging level. - * A ``propagate`` value which is not a boolean. - * An id which does not have a corresponding destination. - * A non-existent handler id found during an incremental call. - * An invalid logger name. - * Inability to resolve to an internal or external object. - - Parsing is performed by the :class:`DictConfigurator` class, whose - constructor is passed the dictionary used for configuration, and - has a :meth:`configure` method. The :mod:`logging.config` module - has a callable attribute :attr:`dictConfigClass` - which is initially set to :class:`DictConfigurator`. - You can replace the value of :attr:`dictConfigClass` with a - suitable implementation of your own. - - :func:`dictConfig` calls :attr:`dictConfigClass` passing - the specified dictionary, and then calls the :meth:`configure` method on - the returned object to put the configuration into effect:: - - def dictConfig(config): - dictConfigClass(config).configure() - - For example, a subclass of :class:`DictConfigurator` could call - ``DictConfigurator.__init__()`` in its own :meth:`__init__()`, then - set up custom prefixes which would be usable in the subsequent - :meth:`configure` call. :attr:`dictConfigClass` would be bound to - this new subclass, and then :func:`dictConfig` could be called exactly as - in the default, uncustomized state. + Takes the logging configuration from a dictionary. The contents of + this dictionary are described in :ref:`logging-config-dictschema` + below. + + If an error is encountered during configuration, this function will + raise a :exc:`ValueError`, :exc:`TypeError`, :exc:`AttributeError` + or :exc:`ImportError` with a suitably descriptive message. The + following is a (possibly incomplete) list of conditions which will + raise an error: + + * A ``level`` which is not a string or which is a string not + corresponding to an actual logging level. + * A ``propagate`` value which is not a boolean. + * An id which does not have a corresponding destination. + * A non-existent handler id found during an incremental call. + * An invalid logger name. + * Inability to resolve to an internal or external object. + + Parsing is performed by the :class:`DictConfigurator` class, whose + constructor is passed the dictionary used for configuration, and + has a :meth:`configure` method. The :mod:`logging.config` module + has a callable attribute :attr:`dictConfigClass` + which is initially set to :class:`DictConfigurator`. + You can replace the value of :attr:`dictConfigClass` with a + suitable implementation of your own. + + :func:`dictConfig` calls :attr:`dictConfigClass` passing + the specified dictionary, and then calls the :meth:`configure` method on + the returned object to put the configuration into effect:: + + def dictConfig(config): + dictConfigClass(config).configure() + + For example, a subclass of :class:`DictConfigurator` could call + ``DictConfigurator.__init__()`` in its own :meth:`__init__()`, then + set up custom prefixes which would be usable in the subsequent + :meth:`configure` call. :attr:`dictConfigClass` would be bound to + this new subclass, and then :func:`dictConfig` could be called exactly as + in the default, uncustomized state. .. versionadded:: 3.2 diff --git a/Doc/library/logging.rst b/Doc/library/logging.rst index 7267f812..e63475d3 100644 --- a/Doc/library/logging.rst +++ b/Doc/library/logging.rst @@ -1089,8 +1089,8 @@ functions. suitable value. .. versionchanged:: 3.7 - The *level* parameter was defaulted to level ``CRITICAL``. See Issue - #28524 for more information about this change. + The *level* parameter was defaulted to level ``CRITICAL``. See + :issue:`28524` for more information about this change. .. function:: addLevelName(level, levelName) diff --git a/Doc/library/lzma.rst b/Doc/library/lzma.rst index 4bfff9c6..633c8787 100644 --- a/Doc/library/lzma.rst +++ b/Doc/library/lzma.rst @@ -33,7 +33,7 @@ from multiple threads, it is necessary to protect it with a lock. Reading and writing compressed files ------------------------------------ -.. function:: open(filename, mode="rb", \*, format=None, check=-1, preset=None, filters=None, encoding=None, errors=None, newline=None) +.. function:: open(filename, mode="rb", *, format=None, check=-1, preset=None, filters=None, encoding=None, errors=None, newline=None) Open an LZMA-compressed file in binary or text mode, returning a :term:`file object`. @@ -69,7 +69,7 @@ Reading and writing compressed files Accepts a :term:`path-like object`. -.. class:: LZMAFile(filename=None, mode="r", \*, format=None, check=-1, preset=None, filters=None) +.. class:: LZMAFile(filename=None, mode="r", *, format=None, check=-1, preset=None, filters=None) Open an LZMA-compressed file in binary mode. diff --git a/Doc/library/multiprocessing.rst b/Doc/library/multiprocessing.rst index ab84d39e..352f48f5 100644 --- a/Doc/library/multiprocessing.rst +++ b/Doc/library/multiprocessing.rst @@ -2661,6 +2661,46 @@ The :mod:`multiprocessing.dummy` module :mod:`multiprocessing.dummy` replicates the API of :mod:`multiprocessing` but is no more than a wrapper around the :mod:`threading` module. +.. currentmodule:: multiprocessing.pool + +In particular, the ``Pool`` function provided by :mod:`multiprocessing.dummy` +returns an instance of :class:`ThreadPool`, which is a subclass of +:class:`Pool` that supports all the same method calls but uses a pool of +worker threads rather than worker processes. + + +.. class:: ThreadPool([processes[, initializer[, initargs]]]) + + A thread pool object which controls a pool of worker threads to which jobs + can be submitted. :class:`ThreadPool` instances are fully interface + compatible with :class:`Pool` instances, and their resources must also be + properly managed, either by using the pool as a context manager or by + calling :meth:`~multiprocessing.pool.Pool.close` and + :meth:`~multiprocessing.pool.Pool.terminate` manually. + + *processes* is the number of worker threads to use. If *processes* is + ``None`` then the number returned by :func:`os.cpu_count` is used. + + If *initializer* is not ``None`` then each worker process will call + ``initializer(*initargs)`` when it starts. + + Unlike :class:`Pool`, *maxtasksperchild* and *context* cannot be provided. + + .. note:: + + A :class:`ThreadPool` shares the same interface as :class:`Pool`, which + is designed around a pool of processes and predates the introduction of + the :class:`concurrent.futures` module. As such, it inherits some + operations that don't make sense for a pool backed by threads, and it + has its own type for representing the status of asynchronous jobs, + :class:`AsyncResult`, that is not understood by any other libraries. + + Users should generally prefer to use + :class:`concurrent.futures.ThreadPoolExecutor`, which has a simpler + interface that was designed around threads from the start, and which + returns :class:`concurrent.futures.Future` instances that are + compatible with many other libraries, including :mod:`asyncio`. + .. _multiprocessing-programming: diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 6e287abb..9d206f46 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -1869,7 +1869,7 @@ features: Accepts a :term:`path-like object`. -.. function:: lstat(path, \*, dir_fd=None) +.. function:: lstat(path, *, dir_fd=None) Perform the equivalent of an :c:func:`lstat` system call on the given path. Similar to :func:`~os.stat`, but does not follow symbolic links. Return a @@ -2376,7 +2376,7 @@ features: On the first, uncached call, a system call is required on Windows but not on Unix. - .. method:: is_dir(\*, follow_symlinks=True) + .. method:: is_dir(*, follow_symlinks=True) Return ``True`` if this entry is a directory or a symbolic link pointing to a directory; return ``False`` if the entry is or points to any other @@ -2400,7 +2400,7 @@ features: This method can raise :exc:`OSError`, such as :exc:`PermissionError`, but :exc:`FileNotFoundError` is caught and not raised. - .. method:: is_file(\*, follow_symlinks=True) + .. method:: is_file(*, follow_symlinks=True) Return ``True`` if this entry is a file or a symbolic link pointing to a file; return ``False`` if the entry is or points to a directory or other @@ -2430,7 +2430,7 @@ features: This method can raise :exc:`OSError`, such as :exc:`PermissionError`, but :exc:`FileNotFoundError` is caught and not raised. - .. method:: stat(\*, follow_symlinks=True) + .. method:: stat(*, follow_symlinks=True) Return a :class:`stat_result` object for this entry. This method follows symbolic links by default; to stat a symbolic link add the @@ -2462,7 +2462,7 @@ features: for :class:`bytes` paths on Windows. -.. function:: stat(path, \*, dir_fd=None, follow_symlinks=True) +.. function:: stat(path, *, dir_fd=None, follow_symlinks=True) Get the status of a file or a file descriptor. Perform the equivalent of a :c:func:`stat` system call on the given path. *path* may be specified as diff --git a/Doc/library/pickle.rst b/Doc/library/pickle.rst index b7c34527..be48561e 100644 --- a/Doc/library/pickle.rst +++ b/Doc/library/pickle.rst @@ -213,7 +213,7 @@ The :mod:`pickle` module provides the following constants: The :mod:`pickle` module provides the following functions to make the pickling process more convenient: -.. function:: dump(obj, file, protocol=None, \*, fix_imports=True, buffer_callback=None) +.. function:: dump(obj, file, protocol=None, *, fix_imports=True, buffer_callback=None) Write the pickled representation of the object *obj* to the open :term:`file object` *file*. This is equivalent to @@ -225,7 +225,7 @@ process more convenient: .. versionchanged:: 3.8 The *buffer_callback* argument was added. -.. function:: dumps(obj, protocol=None, \*, fix_imports=True, buffer_callback=None) +.. function:: dumps(obj, protocol=None, *, fix_imports=True, buffer_callback=None) Return the pickled representation of the object *obj* as a :class:`bytes` object, instead of writing it to a file. @@ -236,7 +236,7 @@ process more convenient: .. versionchanged:: 3.8 The *buffer_callback* argument was added. -.. function:: load(file, \*, fix_imports=True, encoding="ASCII", errors="strict", buffers=None) +.. function:: load(file, *, fix_imports=True, encoding="ASCII", errors="strict", buffers=None) Read the pickled representation of an object from the open :term:`file object` *file* and return the reconstituted object hierarchy specified therein. @@ -252,7 +252,7 @@ process more convenient: .. versionchanged:: 3.8 The *buffers* argument was added. -.. function:: loads(data, /, \*, fix_imports=True, encoding="ASCII", errors="strict", buffers=None) +.. function:: loads(data, /, *, fix_imports=True, encoding="ASCII", errors="strict", buffers=None) Return the reconstituted object hierarchy of the pickled representation *data* of an object. *data* must be a :term:`bytes-like object`. @@ -296,7 +296,7 @@ The :mod:`pickle` module defines three exceptions: The :mod:`pickle` module exports three classes, :class:`Pickler`, :class:`Unpickler` and :class:`PickleBuffer`: -.. class:: Pickler(file, protocol=None, \*, fix_imports=True, buffer_callback=None) +.. class:: Pickler(file, protocol=None, *, fix_imports=True, buffer_callback=None) This takes a binary file for writing a pickle data stream. @@ -391,7 +391,7 @@ The :mod:`pickle` module exports three classes, :class:`Pickler`, Use :func:`pickletools.optimize` if you need more compact pickles. -.. class:: Unpickler(file, \*, fix_imports=True, encoding="ASCII", errors="strict", buffers=None) +.. class:: Unpickler(file, *, fix_imports=True, encoding="ASCII", errors="strict", buffers=None) This takes a binary file for reading a pickle data stream. diff --git a/Doc/library/plistlib.rst b/Doc/library/plistlib.rst index 6def72b3..ce6d4a85 100644 --- a/Doc/library/plistlib.rst +++ b/Doc/library/plistlib.rst @@ -52,7 +52,7 @@ or :class:`datetime.datetime` objects. This module defines the following functions: -.. function:: load(fp, \*, fmt=None, dict_type=dict) +.. function:: load(fp, *, fmt=None, dict_type=dict) Read a plist file. *fp* should be a readable and binary file object. Return the unpacked root object (which usually is a @@ -80,7 +80,7 @@ This module defines the following functions: .. versionadded:: 3.4 -.. function:: loads(data, \*, fmt=None, dict_type=dict) +.. function:: loads(data, *, fmt=None, dict_type=dict) Load a plist from a bytes object. See :func:`load` for an explanation of the keyword arguments. @@ -88,7 +88,7 @@ This module defines the following functions: .. versionadded:: 3.4 -.. function:: dump(value, fp, \*, fmt=FMT_XML, sort_keys=True, skipkeys=False) +.. function:: dump(value, fp, *, fmt=FMT_XML, sort_keys=True, skipkeys=False) Write *value* to a plist file. *Fp* should be a writable, binary file object. @@ -116,7 +116,7 @@ This module defines the following functions: .. versionadded:: 3.4 -.. function:: dumps(value, \*, fmt=FMT_XML, sort_keys=True, skipkeys=False) +.. function:: dumps(value, *, fmt=FMT_XML, sort_keys=True, skipkeys=False) Return *value* as a plist-formatted bytes object. See the documentation for :func:`dump` for an explanation of the keyword diff --git a/Doc/library/profile.rst b/Doc/library/profile.rst index 34525a96..774d46d0 100644 --- a/Doc/library/profile.rst +++ b/Doc/library/profile.rst @@ -525,16 +525,16 @@ Analysis of the profiler data is done using the :class:`~pstats.Stats` class. ordering are identical to the :meth:`~pstats.Stats.print_callers` method. - .. method:: get_stats_profile() + .. method:: get_stats_profile() This method returns an instance of StatsProfile, which contains a mapping of function names to instances of FunctionProfile. Each FunctionProfile instance holds information related to the function's profile such as how long the function took to run, how many times it was called, etc... - .. versionadded:: 3.9 - Added the following dataclasses: StatsProfile, FunctionProfile. - Added the following function: get_stats_profile. + .. versionadded:: 3.9 + Added the following dataclasses: StatsProfile, FunctionProfile. + Added the following function: get_stats_profile. .. _deterministic-profiling: diff --git a/Doc/library/pyexpat.rst b/Doc/library/pyexpat.rst index e43b9aec..034e5793 100644 --- a/Doc/library/pyexpat.rst +++ b/Doc/library/pyexpat.rst @@ -665,14 +665,14 @@ The ``errors`` module has the following attributes: .. data:: codes - A dictionary mapping numeric error codes to their string descriptions. + A dictionary mapping string descriptions to their error codes. .. versionadded:: 3.2 .. data:: messages - A dictionary mapping string descriptions to their error codes. + A dictionary mapping numeric error codes to their string descriptions. .. versionadded:: 3.2 diff --git a/Doc/library/random.rst b/Doc/library/random.rst index 8154dfc1..0e703251 100644 --- a/Doc/library/random.rst +++ b/Doc/library/random.rst @@ -142,10 +142,11 @@ Functions for integers .. function:: getrandbits(k) - Returns a Python integer with *k* random bits. This method is supplied with - the MersenneTwister generator and some other generators may also provide it - as an optional part of the API. When available, :meth:`getrandbits` enables - :meth:`randrange` to handle arbitrarily large ranges. + Returns a non-negative Python integer with *k* random bits. This method + is supplied with the MersenneTwister generator and some other generators + may also provide it as an optional part of the API. When available, + :meth:`getrandbits` enables :meth:`randrange` to handle arbitrarily large + ranges. .. versionchanged:: 3.9 This method now accepts zero for *k*. diff --git a/Doc/library/shutil.rst b/Doc/library/shutil.rst index 3f512276..d5080da1 100644 --- a/Doc/library/shutil.rst +++ b/Doc/library/shutil.rst @@ -218,7 +218,7 @@ Directory and files operations copy the file more efficiently. See :ref:`shutil-platform-dependent-efficient-copy-operations` section. -.. function:: ignore_patterns(\*patterns) +.. function:: ignore_patterns(*patterns) This factory function creates a function that can be used as a callable for :func:`copytree`\'s *ignore* argument, ignoring files and directories that @@ -443,8 +443,9 @@ Directory and files operations Platform-dependent efficient copy operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Starting from Python 3.8 all functions involving a file copy (:func:`copyfile`, -:func:`copy`, :func:`copy2`, :func:`copytree`, and :func:`move`) may use +Starting from Python 3.8, all functions involving a file copy +(:func:`copyfile`, :func:`~shutil.copy`, :func:`copy2`, +:func:`copytree`, and :func:`move`) may use platform-specific "fast-copy" syscalls in order to copy the file more efficiently (see :issue:`33671`). "fast-copy" means that the copying operation occurs within the kernel, avoiding diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst index faf8a762..2fc170a0 100755 --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -56,12 +56,12 @@ created. Socket addresses are represented as follows: bytes-like object can be used for either type of address when passing it as an argument. - .. versionchanged:: 3.3 - Previously, :const:`AF_UNIX` socket paths were assumed to use UTF-8 - encoding. + .. versionchanged:: 3.3 + Previously, :const:`AF_UNIX` socket paths were assumed to use UTF-8 + encoding. - .. versionchanged:: 3.5 - Writable :term:`bytes-like object` is now accepted. + .. versionchanged:: 3.5 + Writable :term:`bytes-like object` is now accepted. .. _host_port: diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index ccb82278..2e2e5e9c 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -99,10 +99,6 @@ This example uses the iterator form:: .. seealso:: - https://github.com/ghaering/pysqlite - The pysqlite web page -- sqlite3 is developed externally under the name - "pysqlite". - https://www.sqlite.org The SQLite web page; the documentation describes the syntax and the available data types for the supported SQL dialect. @@ -197,7 +193,9 @@ Module functions and constants *detect_types* defaults to 0 (i. e. off, no type detection), you can set it to any combination of :const:`PARSE_DECLTYPES` and :const:`PARSE_COLNAMES` to turn - type detection on. + type detection on. Due to SQLite behaviour, types can't be detected for generated + fields (for example ``max(data)``), even when *detect_types* parameter is set. In + such case, the returned type is :class:`str`. By default, *check_same_thread* is :const:`True` and only the creating thread may use the connection. If set :const:`False`, the returned connection may be shared @@ -543,7 +541,7 @@ Connection Objects con.close() - .. method:: backup(target, *, pages=0, progress=None, name="main", sleep=0.250) + .. method:: backup(target, *, pages=-1, progress=None, name="main", sleep=0.250) This method makes a backup of a SQLite database even while it's being accessed by other clients, or concurrently by the same connection. The copy will be @@ -1091,19 +1089,6 @@ committed: .. literalinclude:: ../includes/sqlite3/ctx_manager.py -Common issues -------------- - -Multithreading -^^^^^^^^^^^^^^ - -Older SQLite versions had issues with sharing connections between threads. -That's why the Python module disallows sharing connections and cursors between -threads. If you still try to do so, you will get an exception at runtime. - -The only exception is calling the :meth:`~Connection.interrupt` method, which -only makes sense to call from a different thread. - .. rubric:: Footnotes .. [#f1] The sqlite3 module is not built with loadable extension support by diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index a48cfa13..0667a601 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -478,7 +478,7 @@ class`. In addition, it provides a few more methods: .. versionadded:: 3.1 -.. method:: int.to_bytes(length, byteorder, \*, signed=False) +.. method:: int.to_bytes(length, byteorder, *, signed=False) Return an array of bytes representing an integer. @@ -510,7 +510,7 @@ class`. In addition, it provides a few more methods: .. versionadded:: 3.2 -.. classmethod:: int.from_bytes(bytes, byteorder, \*, signed=False) +.. classmethod:: int.from_bytes(bytes, byteorder, *, signed=False) Return the integer represented by the given array of bytes. @@ -5196,8 +5196,8 @@ types, where they are relevant. Some of these are not reported by the .. method:: class.__subclasses__ Each class keeps a list of weak references to its immediate subclasses. This - method returns a list of all those references still alive. - Example:: + method returns a list of all those references still alive. The list is in + definition order. Example:: >>> int.__subclasses__() [] diff --git a/Doc/library/string.rst b/Doc/library/string.rst index 5542e9b7..54786d0c 100644 --- a/Doc/library/string.rst +++ b/Doc/library/string.rst @@ -514,6 +514,8 @@ The available presentation types for :class:`float` and | | this rounds the number to ``p`` significant digits and | | | then formats the result in either fixed-point format | | | or in scientific notation, depending on its magnitude. | + | | A precision of ``0`` is treated as equivalent to a | + | | precision of ``1``. | | | | | | The precise rules are as follows: suppose that the | | | result formatted with presentation type ``'e'`` and | @@ -528,16 +530,19 @@ The available presentation types for :class:`float` and | | removed if there are no remaining digits following it, | | | unless the ``'#'`` option is used. | | | | + | | With no precision given, uses a precision of ``6`` | + | | significant digits for :class:`float`. For | + | | :class:`~decimal.Decimal`, the coefficient of the result | + | | is formed from the coefficient digits of the value; | + | | scientific notation is used for values smaller than | + | | ``1e-6`` in absolute value and values where the place | + | | value of the least significant digit is larger than 1, | + | | and fixed-point notation is used otherwise. | + | | | | | Positive and negative infinity, positive and negative | | | zero, and nans, are formatted as ``inf``, ``-inf``, | | | ``0``, ``-0`` and ``nan`` respectively, regardless of | | | the precision. | - | | | - | | A precision of ``0`` is treated as equivalent to a | - | | precision of ``1``. With no precision given, uses a | - | | precision of ``6`` significant digits for | - | | :class:`float`, and shows all coefficient digits | - | | for :class:`~decimal.Decimal`. | +---------+----------------------------------------------------------+ | ``'G'`` | General format. Same as ``'g'`` except switches to | | | ``'E'`` if the number gets too large. The | @@ -550,12 +555,18 @@ The available presentation types for :class:`float` and | ``'%'`` | Percentage. Multiplies the number by 100 and displays | | | in fixed (``'f'``) format, followed by a percent sign. | +---------+----------------------------------------------------------+ - | None | Similar to ``'g'``, except that fixed-point notation, | - | | when used, has at least one digit past the decimal point.| - | | The default precision is as high as needed to represent | - | | the particular value. The overall effect is to match the | - | | output of :func:`str` as altered by the other format | - | | modifiers. | + | None | For :class:`float` this is the same as ``'g'``, except | + | | that when fixed-point notation is used to format the | + | | result, it always includes at least one digit past the | + | | decimal point. The precision used is as large as needed | + | | to represent the given value faithfully. | + | | | + | | For :class:`~decimal.Decimal`, this is the same as | + | | either ``'g'`` or ``'G'`` depending on the value of | + | | ``context.capitals`` for the current decimal context. | + | | | + | | The overall effect is to match the output of :func:`str` | + | | as altered by the other format modifiers. | +---------+----------------------------------------------------------+ diff --git a/Doc/library/subprocess.rst b/Doc/library/subprocess.rst index e37cc980..3150aa60 100644 --- a/Doc/library/subprocess.rst +++ b/Doc/library/subprocess.rst @@ -339,7 +339,7 @@ functions. stderr=None, preexec_fn=None, close_fds=True, shell=False, \ cwd=None, env=None, universal_newlines=None, \ startupinfo=None, creationflags=0, restore_signals=True, \ - start_new_session=False, pass_fds=(), \*, group=None, \ + start_new_session=False, pass_fds=(), *, group=None, \ extra_groups=None, user=None, umask=-1, \ encoding=None, errors=None, text=None) @@ -1163,8 +1163,9 @@ calls these functions. The arguments shown above are merely some common ones. The full function signature is largely the same as that of :func:`run` - most arguments are passed directly through to that interface. - However, explicitly passing ``input=None`` to inherit the parent's - standard input file handle is not supported. + One API deviation from :func:`run` behavior exists: passing ``input=None`` + will behave the same as ``input=b''`` (or ``input=''``, depending on other + arguments) rather than using the parent's standard input file handle. By default, this function will return the data as encoded bytes. The actual encoding of the output data may depend on the command being invoked, so the diff --git a/Doc/library/symtable.rst b/Doc/library/symtable.rst index 3efdecb5..e3642322 100644 --- a/Doc/library/symtable.rst +++ b/Doc/library/symtable.rst @@ -156,6 +156,12 @@ Examining Symbol Tables Return ``True`` if the symbol is local to its block. + .. method:: is_annotated() + + Return ``True`` if the symbol is annotated. + + .. versionadded:: 3.6 + .. method:: is_free() Return ``True`` if the symbol is referenced in its block, but not assigned diff --git a/Doc/library/sysconfig.rst b/Doc/library/sysconfig.rst index 78a1dfce..c9306e9b 100644 --- a/Doc/library/sysconfig.rst +++ b/Doc/library/sysconfig.rst @@ -32,7 +32,7 @@ can be accessed using :func:`get_config_vars` or :func:`get_config_var`. Notice that on Windows, it's a much smaller set. -.. function:: get_config_vars(\*args) +.. function:: get_config_vars(*args) With no arguments, return a dictionary of all configuration variables relevant for the current platform. diff --git a/Doc/library/tarfile.rst b/Doc/library/tarfile.rst index 7a114fdf..13088a10 100644 --- a/Doc/library/tarfile.rst +++ b/Doc/library/tarfile.rst @@ -37,7 +37,7 @@ Some facts and figures: Added support for :mod:`lzma` compression. -.. function:: open(name=None, mode='r', fileobj=None, bufsize=10240, \*\*kwargs) +.. function:: open(name=None, mode='r', fileobj=None, bufsize=10240, **kwargs) Return a :class:`TarFile` object for the pathname *name*. For detailed information on :class:`TarFile` objects and the keyword arguments that are diff --git a/Doc/library/test.rst b/Doc/library/test.rst index e24f69cd..bb1bd29b 100644 --- a/Doc/library/test.rst +++ b/Doc/library/test.rst @@ -546,7 +546,7 @@ The :mod:`test.support` module defines the following functions: Define match test with regular expression *patterns*. -.. function:: run_unittest(\*classes) +.. function:: run_unittest(*classes) Execute :class:`unittest.TestCase` subclasses passed to the function. The function scans the classes for methods starting with the prefix ``test_`` diff --git a/Doc/library/trace.rst b/Doc/library/trace.rst index c2732d90..40cf198f 100644 --- a/Doc/library/trace.rst +++ b/Doc/library/trace.rst @@ -153,47 +153,47 @@ Programmatic Interface count information. *timing* enables a timestamp relative to when tracing was started to be displayed. - .. method:: run(cmd) + .. method:: run(cmd) - Execute the command and gather statistics from the execution with - the current tracing parameters. *cmd* must be a string or code object, - suitable for passing into :func:`exec`. + Execute the command and gather statistics from the execution with + the current tracing parameters. *cmd* must be a string or code object, + suitable for passing into :func:`exec`. - .. method:: runctx(cmd, globals=None, locals=None) + .. method:: runctx(cmd, globals=None, locals=None) - Execute the command and gather statistics from the execution with the - current tracing parameters, in the defined global and local - environments. If not defined, *globals* and *locals* default to empty - dictionaries. + Execute the command and gather statistics from the execution with the + current tracing parameters, in the defined global and local + environments. If not defined, *globals* and *locals* default to empty + dictionaries. - .. method:: runfunc(func, /, *args, **kwds) + .. method:: runfunc(func, /, *args, **kwds) - Call *func* with the given arguments under control of the :class:`Trace` - object with the current tracing parameters. + Call *func* with the given arguments under control of the :class:`Trace` + object with the current tracing parameters. - .. method:: results() + .. method:: results() - Return a :class:`CoverageResults` object that contains the cumulative - results of all previous calls to ``run``, ``runctx`` and ``runfunc`` - for the given :class:`Trace` instance. Does not reset the accumulated - trace results. + Return a :class:`CoverageResults` object that contains the cumulative + results of all previous calls to ``run``, ``runctx`` and ``runfunc`` + for the given :class:`Trace` instance. Does not reset the accumulated + trace results. .. class:: CoverageResults A container for coverage results, created by :meth:`Trace.results`. Should not be created directly by the user. - .. method:: update(other) + .. method:: update(other) - Merge in data from another :class:`CoverageResults` object. + Merge in data from another :class:`CoverageResults` object. - .. method:: write_results(show_missing=True, summary=False, coverdir=None) + .. method:: write_results(show_missing=True, summary=False, coverdir=None) - Write coverage results. Set *show_missing* to show lines that had no - hits. Set *summary* to include in the output the coverage summary per - module. *coverdir* specifies the directory into which the coverage - result files will be output. If ``None``, the results for each source - file are placed in its directory. + Write coverage results. Set *show_missing* to show lines that had no + hits. Set *summary* to include in the output the coverage summary per + module. *coverdir* specifies the directory into which the coverage + result files will be output. If ``None``, the results for each source + file are placed in its directory. A simple example demonstrating the use of the programmatic interface:: diff --git a/Doc/library/turtle.rst b/Doc/library/turtle.rst index d3487537..6a9d6191 100644 --- a/Doc/library/turtle.rst +++ b/Doc/library/turtle.rst @@ -662,7 +662,7 @@ Tell Turtle's state Return the angle between the line from turtle position to position specified by (x,y), the vector or the other turtle. This depends on the turtle's start - orientation which depends on the mode - "standard"/"world" or "logo"). + orientation which depends on the mode - "standard"/"world" or "logo". .. doctest:: :skipif: _tkinter is None @@ -913,8 +913,8 @@ Color control Set pencolor to the RGB color represented by *r*, *g*, and *b*. Each of *r*, *g*, and *b* must be in the range 0..colormode. - If turtleshape is a polygon, the outline of that polygon is drawn with the - newly set pencolor. + If turtleshape is a polygon, the outline of that polygon is drawn with the + newly set pencolor. .. doctest:: :skipif: _tkinter is None @@ -962,8 +962,8 @@ Color control Set fillcolor to the RGB color represented by *r*, *g*, and *b*. Each of *r*, *g*, and *b* must be in the range 0..colormode. - If turtleshape is a polygon, the interior of that polygon is drawn - with the newly set fillcolor. + If turtleshape is a polygon, the interior of that polygon is drawn + with the newly set fillcolor. .. doctest:: :skipif: _tkinter is None @@ -1001,8 +1001,8 @@ Color control Equivalent to ``pencolor(colorstring1)`` and ``fillcolor(colorstring2)`` and analogously if the other input format is used. - If turtleshape is a polygon, outline and interior of that polygon is drawn - with the newly set colors. + If turtleshape is a polygon, outline and interior of that polygon is drawn + with the newly set colors. .. doctest:: :skipif: _tkinter is None @@ -1105,7 +1105,7 @@ More drawing control :param font: a triple (fontname, fontsize, fonttype) Write text - the string representation of *arg* - at the current turtle - position according to *align* ("left", "center" or right") and with the given + position according to *align* ("left", "center" or "right") and with the given font. If *move* is true, the pen is moved to the bottom-right corner of the text. By default, *move* is ``False``. @@ -1192,7 +1192,7 @@ Appearance :func:`shapesize`. - "noresize": no adaption of the turtle's appearance takes place. - resizemode("user") is called by :func:`shapesize` when used with arguments. + ``resizemode("user")`` is called by :func:`shapesize` when used with arguments. .. doctest:: :skipif: _tkinter is None @@ -1330,7 +1330,7 @@ Appearance matrix as a tuple of 4 elements. Otherwise set the given elements and transform the turtleshape according to the matrix consisting of first row t11, t12 and - second row t21, 22. The determinant t11 * t22 - t12 * t21 must not be + second row t21, t22. The determinant t11 * t22 - t12 * t21 must not be zero, otherwise an error is raised. Modify stretchfactor, shearfactor and tiltangle according to the given matrix. @@ -1513,7 +1513,7 @@ Special Turtle methods :param size: an integer or ``None`` - Set or disable undobuffer. If *size* is an integer an empty undobuffer of + Set or disable undobuffer. If *size* is an integer, an empty undobuffer of given size is installed. *size* gives the maximum number of turtle actions that can be undone by the :func:`undo` method/function. If *size* is ``None``, the undobuffer is disabled. @@ -1821,7 +1821,7 @@ Using screen events existing bindings are removed. Example for a TurtleScreen instance named ``screen`` and a Turtle instance - named turtle: + named ``turtle``: .. doctest:: :skipif: _tkinter is None @@ -2048,7 +2048,7 @@ Methods specific to Screen, not inherited from TurtleScreen .. function:: exitonclick() - Bind bye() method to mouse clicks on the Screen. + Bind ``bye()`` method to mouse clicks on the Screen. If the value "using_IDLE" in the configuration dictionary is ``False`` diff --git a/Doc/library/types.rst b/Doc/library/types.rst index 0fe3822f..d83d9667 100644 --- a/Doc/library/types.rst +++ b/Doc/library/types.rst @@ -260,6 +260,9 @@ Standard names are defined for the following types: .. versionadded:: 3.9 + .. versionchanged:: 3.9.2 + This type can now be subclassed. + .. class:: TracebackType(tb_next, tb_frame, tb_lasti, tb_lineno) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index af2cafb8..688564f1 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -1714,6 +1714,8 @@ Introspection helpers ``list[ForwardRef("SomeClass")]``. This class should not be instantiated by a user, but may be used by introspection tools. + .. versionadded:: 3.7.4 + Constant -------- diff --git a/Doc/library/urllib.parse.rst b/Doc/library/urllib.parse.rst index 536cf952..38e29863 100644 --- a/Doc/library/urllib.parse.rst +++ b/Doc/library/urllib.parse.rst @@ -165,7 +165,7 @@ or on combining URL components into a URL string. now raise :exc:`ValueError`. -.. function:: parse_qs(qs, keep_blank_values=False, strict_parsing=False, encoding='utf-8', errors='replace', max_num_fields=None) +.. function:: parse_qs(qs, keep_blank_values=False, strict_parsing=False, encoding='utf-8', errors='replace', max_num_fields=None, separator='&') Parse a query string given as a string argument (data of type :mimetype:`application/x-www-form-urlencoded`). Data are returned as a @@ -190,6 +190,9 @@ or on combining URL components into a URL string. read. If set, then throws a :exc:`ValueError` if there are more than *max_num_fields* fields read. + The optional argument *separator* is the symbol to use for separating the + query arguments. It defaults to ``&``. + Use the :func:`urllib.parse.urlencode` function (with the ``doseq`` parameter set to ``True``) to convert such dictionaries into query strings. @@ -201,8 +204,14 @@ or on combining URL components into a URL string. .. versionchanged:: 3.8 Added *max_num_fields* parameter. + .. versionchanged:: 3.9.2 + Added *separator* parameter with the default value of ``&``. Python + versions earlier than Python 3.9.2 allowed using both ``;`` and ``&`` as + query parameter separator. This has been changed to allow only a single + separator key, with ``&`` as the default separator. + -.. function:: parse_qsl(qs, keep_blank_values=False, strict_parsing=False, encoding='utf-8', errors='replace', max_num_fields=None) +.. function:: parse_qsl(qs, keep_blank_values=False, strict_parsing=False, encoding='utf-8', errors='replace', max_num_fields=None, separator='&') Parse a query string given as a string argument (data of type :mimetype:`application/x-www-form-urlencoded`). Data are returned as a list of @@ -226,6 +235,8 @@ or on combining URL components into a URL string. read. If set, then throws a :exc:`ValueError` if there are more than *max_num_fields* fields read. + The optional argument *separator* is the symbol to use for separating the query arguments. It defaults to ``&``. + Use the :func:`urllib.parse.urlencode` function to convert such lists of pairs into query strings. @@ -235,6 +246,12 @@ or on combining URL components into a URL string. .. versionchanged:: 3.8 Added *max_num_fields* parameter. + .. versionchanged:: 3.9.2 + Added *separator* parameter with the default value of ``&``. Python + versions earlier than Python 3.9.2 allowed using both ``;`` and ``&`` as + query parameter separator. This has been changed to allow only a single + separator key, with ``&`` as the default separator. + .. function:: urlunparse(parts) diff --git a/Doc/library/warnings.rst b/Doc/library/warnings.rst index a481a350..9c1743ca 100644 --- a/Doc/library/warnings.rst +++ b/Doc/library/warnings.rst @@ -491,7 +491,7 @@ Available Functions Available Context Managers -------------------------- -.. class:: catch_warnings(\*, record=False, module=None) +.. class:: catch_warnings(*, record=False, module=None) A context manager that copies and, upon exit, restores the warnings filter and the :func:`showwarning` function. diff --git a/Doc/library/winreg.rst b/Doc/library/winreg.rst index dccb7db2..487856a3 100644 --- a/Doc/library/winreg.rst +++ b/Doc/library/winreg.rst @@ -791,7 +791,7 @@ integer handle, and also disconnect the Windows handle from the handle object. .. method:: PyHKEY.__enter__() - PyHKEY.__exit__(\*exc_info) + PyHKEY.__exit__(*exc_info) The HKEY object implements :meth:`~object.__enter__` and :meth:`~object.__exit__` and thus supports the context protocol for the diff --git a/Doc/license.rst b/Doc/license.rst index 4030825b..f487d98b 100644 --- a/Doc/license.rst +++ b/Doc/license.rst @@ -100,7 +100,7 @@ PSF LICENSE AGREEMENT FOR PYTHON |release| analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use Python |release| alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of - copyright, i.e., "Copyright © 2001-2020 Python Software Foundation; All Rights + copyright, i.e., "Copyright © 2001-2021 Python Software Foundation; All Rights Reserved" are retained in Python |release| alone or in any derivative version prepared by Licensee. diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst index b4e06e5b..7e666351 100644 --- a/Doc/reference/compound_stmts.rst +++ b/Doc/reference/compound_stmts.rst @@ -254,7 +254,8 @@ present, must be last; it matches any exception. For an except clause with an expression, that expression is evaluated, and the clause matches the exception if the resulting object is "compatible" with the exception. An object is compatible with an exception if it is the class or a base class of the exception -object or a tuple containing an item compatible with the exception. +object, or a tuple containing an item that is the class or a base class of +the exception object. If no except clause matches the exception, the search for an exception handler continues in the surrounding code and on the invocation stack. [#]_ @@ -799,12 +800,12 @@ The :keyword:`!async for` statement .. productionlist:: python-grammar async_for_stmt: "async" `for_stmt` -An :term:`asynchronous iterable` is able to call asynchronous code in its -*iter* implementation, and :term:`asynchronous iterator` can call asynchronous -code in its *next* method. +An :term:`asynchronous iterable` provides an ``__aiter__`` method that directly +returns an :term:`asynchronous iterator`, which can call asynchronous code in +its ``__anext__`` method. The ``async for`` statement allows convenient iteration over asynchronous -iterators. +iterables. The following code:: diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 89063876..3b3bd552 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -217,7 +217,6 @@ Ellipsis There are two types of integers: Integers (:class:`int`) - These represent numbers in an unlimited range, subject to available (virtual) memory only. For the purpose of shift and mask operations, a binary representation is assumed, and negative numbers are represented in a variant of diff --git a/Doc/tools/susp-ignored.csv b/Doc/tools/susp-ignored.csv index c9777c6b..9f0c42a9 100644 --- a/Doc/tools/susp-ignored.csv +++ b/Doc/tools/susp-ignored.csv @@ -171,6 +171,7 @@ library/ipaddress,,:db00,2001:db00::0/ffff:ff00:: library/ipaddress,,::,2001:db00::0/ffff:ff00:: library/itertools,,:step,elements from seq[start:stop:step] library/itertools,,:stop,elements from seq[start:stop:step] +library/itertools,,::,kernel = tuple(kernel)[::-1] library/logging.handlers,,:port,host:port library/mmap,,:i2,obj[i1:i2] library/multiprocessing,,`,# Add more tasks using `put()` diff --git a/Doc/using/venv-create.inc b/Doc/using/venv-create.inc index 5e724cd5..ddb36f94 100644 --- a/Doc/using/venv-create.inc +++ b/Doc/using/venv-create.inc @@ -126,6 +126,10 @@ directory containing the virtual environment): | | PowerShell | PS C:\\> \\Scripts\\Activate.ps1 | +-------------+-----------------+-----------------------------------------+ +When a virtual environment is active, the :envvar:`VIRTUAL_ENV` environment +variable is set to the path of the virtual environment. This can be used to +check if one is running inside a virtual environment. + You don't specifically *need* to activate an environment; activation just prepends the virtual environment's binary directory to your path, so that "python" invokes the virtual environment's Python interpreter and you can run diff --git a/Doc/using/windows.rst b/Doc/using/windows.rst index d0c342e1..4699e375 100644 --- a/Doc/using/windows.rst +++ b/Doc/using/windows.rst @@ -339,6 +339,11 @@ full write access to shared locations such as ``TEMP`` and the registry. Instead, it will write to a private copy. If your scripts must modify the shared locations, you will need to install the full installer. +For more detail on the technical basis for these limitations, please consult +Microsoft's documentation on packaged full-trust apps, currently available at +`docs.microsoft.com/en-us/windows/msix/desktop/desktop-to-uwp-behind-the-scenes +`_ + .. _windows-nuget: @@ -1150,8 +1155,6 @@ For extension modules, consult :ref:`building-on-windows`. MinGW gcc under Windows" or "Installing Python extension with distutils and without Microsoft Visual C++" by Sébastien Sauvage, 2003 - `MingW -- Python extensions `_ - Other Platforms =============== diff --git a/Doc/whatsnew/3.6.rst b/Doc/whatsnew/3.6.rst index 85a6657f..03a877a3 100644 --- a/Doc/whatsnew/3.6.rst +++ b/Doc/whatsnew/3.6.rst @@ -2443,3 +2443,16 @@ because of the behavior of the socket option ``SO_REUSEADDR`` in UDP. For more details, see the documentation for ``loop.create_datagram_endpoint()``. (Contributed by Kyle Stanley, Antoine Pitrou, and Yury Selivanov in :issue:`37228`.) + +Notable changes in Python 3.6.13 +================================ + +Earlier Python versions allowed using both ``;`` and ``&`` as +query parameter separators in :func:`urllib.parse.parse_qs` and +:func:`urllib.parse.parse_qsl`. Due to security concerns, and to conform with +newer W3C recommendations, this has been changed to allow only a single +separator key, with ``&`` as the default. This change also affects +:func:`cgi.parse` and :func:`cgi.parse_multipart` as they use the affected +functions internally. For more details, please see their respective +documentation. +(Contributed by Adam Goldschmidt, Senthil Kumaran and Ken Jin in :issue:`42967`.) diff --git a/Doc/whatsnew/3.7.rst b/Doc/whatsnew/3.7.rst index 25b1e1e3..9204cc7f 100644 --- a/Doc/whatsnew/3.7.rst +++ b/Doc/whatsnew/3.7.rst @@ -2556,3 +2556,16 @@ because of the behavior of the socket option ``SO_REUSEADDR`` in UDP. For more details, see the documentation for ``loop.create_datagram_endpoint()``. (Contributed by Kyle Stanley, Antoine Pitrou, and Yury Selivanov in :issue:`37228`.) + +Notable changes in Python 3.7.10 +================================ + +Earlier Python versions allowed using both ``;`` and ``&`` as +query parameter separators in :func:`urllib.parse.parse_qs` and +:func:`urllib.parse.parse_qsl`. Due to security concerns, and to conform with +newer W3C recommendations, this has been changed to allow only a single +separator key, with ``&`` as the default. This change also affects +:func:`cgi.parse` and :func:`cgi.parse_multipart` as they use the affected +functions internally. For more details, please see their respective +documentation. +(Contributed by Adam Goldschmidt, Senthil Kumaran and Ken Jin in :issue:`42967`.) diff --git a/Doc/whatsnew/3.8.rst b/Doc/whatsnew/3.8.rst index 0b4820f3..dbc3875a 100644 --- a/Doc/whatsnew/3.8.rst +++ b/Doc/whatsnew/3.8.rst @@ -2234,3 +2234,16 @@ because of the behavior of the socket option ``SO_REUSEADDR`` in UDP. For more details, see the documentation for ``loop.create_datagram_endpoint()``. (Contributed by Kyle Stanley, Antoine Pitrou, and Yury Selivanov in :issue:`37228`.) + +Notable changes in Python 3.8.8 +=============================== + +Earlier Python versions allowed using both ``;`` and ``&`` as +query parameter separators in :func:`urllib.parse.parse_qs` and +:func:`urllib.parse.parse_qsl`. Due to security concerns, and to conform with +newer W3C recommendations, this has been changed to allow only a single +separator key, with ``&`` as the default. This change also affects +:func:`cgi.parse` and :func:`cgi.parse_multipart` as they use the affected +functions internally. For more details, please see their respective +documentation. +(Contributed by Adam Goldschmidt, Senthil Kumaran and Ken Jin in :issue:`42967`.) diff --git a/Doc/whatsnew/3.9.rst b/Doc/whatsnew/3.9.rst index f8f421bd..30869305 100644 --- a/Doc/whatsnew/3.9.rst +++ b/Doc/whatsnew/3.9.rst @@ -1497,3 +1497,35 @@ functions and options conditionally available based on the operating system version in use at runtime ("weaklinking"). (Contributed by Ronald Oussoren and Lawrence D'Anna in :issue:`41100`.) + +Notable changes in Python 3.9.2 +=============================== + +collections.abc +--------------- + +:class:`collections.abc.Callable` generic now flattens type parameters, similar +to what :data:`typing.Callable` currently does. This means that +``collections.abc.Callable[[int, str], str]`` will have ``__args__`` of +``(int, str, str)``; previously this was ``([int, str], str)``. To allow this +change, :class:`types.GenericAlias` can now be subclassed, and a subclass will +be returned when subscripting the :class:`collections.abc.Callable` type. +Code which accesses the arguments via :func:`typing.get_args` or ``__args__`` +need to account for this change. A :exc:`DeprecationWarning` may be emitted for +invalid forms of parameterizing :class:`collections.abc.Callable` which may have +passed silently in Python 3.9.1. This :exc:`DeprecationWarning` will +become a :exc:`TypeError` in Python 3.10. +(Contributed by Ken Jin in :issue:`42195`.) + +urllib.parse +------------ + +Earlier Python versions allowed using both ``;`` and ``&`` as +query parameter separators in :func:`urllib.parse.parse_qs` and +:func:`urllib.parse.parse_qsl`. Due to security concerns, and to conform with +newer W3C recommendations, this has been changed to allow only a single +separator key, with ``&`` as the default. This change also affects +:func:`cgi.parse` and :func:`cgi.parse_multipart` as they use the affected +functions internally. For more details, please see their respective +documentation. +(Contributed by Adam Goldschmidt, Senthil Kumaran and Ken Jin in :issue:`42967`.) diff --git a/Grammar/python.gram b/Grammar/python.gram index ce783971..64e205e7 100644 --- a/Grammar/python.gram +++ b/Grammar/python.gram @@ -563,18 +563,23 @@ star_targets[expr_ty]: | a=star_target !',' { a } | a=star_target b=(',' c=star_target { c })* [','] { _Py_Tuple(CHECK(_PyPegen_seq_insert_in_front(p, a, b)), Store, EXTRA) } -star_targets_seq[asdl_seq*]: a=','.star_target+ [','] { a } +star_targets_list_seq[asdl_seq*]: a=','.star_target+ [','] { a } +star_targets_tuple_seq[asdl_seq*]: + | a=star_target b=(',' c=star_target { c })+ [','] { _PyPegen_seq_insert_in_front(p, a, b) } + | a=star_target ',' { _PyPegen_singleton_seq(p, a) } star_target[expr_ty] (memo): | '*' a=(!'*' star_target) { _Py_Starred(CHECK(_PyPegen_set_expr_context(p, a, Store)), Store, EXTRA) } + | target_with_star_atom +target_with_star_atom[expr_ty] (memo): | a=t_primary '.' b=NAME !t_lookahead { _Py_Attribute(a, b->v.Name.id, Store, EXTRA) } | a=t_primary '[' b=slices ']' !t_lookahead { _Py_Subscript(a, b, Store, EXTRA) } | star_atom star_atom[expr_ty]: | a=NAME { _PyPegen_set_expr_context(p, a, Store) } - | '(' a=star_target ')' { _PyPegen_set_expr_context(p, a, Store) } - | '(' a=[star_targets_seq] ')' { _Py_Tuple(a, Store, EXTRA) } - | '[' a=[star_targets_seq] ']' { _Py_List(a, Store, EXTRA) } + | '(' a=target_with_star_atom ')' { _PyPegen_set_expr_context(p, a, Store) } + | '(' a=[star_targets_tuple_seq] ')' { _Py_Tuple(a, Store, EXTRA) } + | '[' a=[star_targets_list_seq] ']' { _Py_List(a, Store, EXTRA) } single_target[expr_ty]: | single_subscript_attribute_target diff --git a/Include/cpython/abstract.h b/Include/cpython/abstract.h index 7bc80833..0f1304d2 100644 --- a/Include/cpython/abstract.h +++ b/Include/cpython/abstract.h @@ -67,7 +67,7 @@ PyVectorcall_Function(PyObject *callable) { PyTypeObject *tp; Py_ssize_t offset; - vectorcallfunc *ptr; + vectorcallfunc ptr; assert(callable != NULL); tp = Py_TYPE(callable); @@ -77,8 +77,8 @@ PyVectorcall_Function(PyObject *callable) assert(PyCallable_Check(callable)); offset = tp->tp_vectorcall_offset; assert(offset > 0); - ptr = (vectorcallfunc *)(((char *)callable) + offset); - return *ptr; + memcpy(&ptr, (char *) callable + offset, sizeof(ptr)); + return ptr; } /* Call the callable object 'callable' with the "vectorcall" calling diff --git a/Include/cpython/unicodeobject.h b/Include/cpython/unicodeobject.h index 1fc732ab..17db79cf 100644 --- a/Include/cpython/unicodeobject.h +++ b/Include/cpython/unicodeobject.h @@ -22,7 +22,7 @@ extern "C" { */ #define Py_UNICODE_ISSPACE(ch) \ - ((ch) < 128U ? _Py_ascii_whitespace[(ch)] : _PyUnicode_IsWhitespace(ch)) + ((Py_UCS4)(ch) < 128U ? _Py_ascii_whitespace[(ch)] : _PyUnicode_IsWhitespace(ch)) #define Py_UNICODE_ISLOWER(ch) _PyUnicode_IsLowercase(ch) #define Py_UNICODE_ISUPPER(ch) _PyUnicode_IsUppercase(ch) @@ -760,13 +760,6 @@ PyAPI_FUNC(const char *) PyUnicode_AsUTF8AndSize( Use of this API is DEPRECATED since no size information can be extracted from the returned data. - - *** This API is for interpreter INTERNAL USE ONLY and will likely - *** be removed or changed for Python 3.1. - - *** If you need to access the Unicode object as UTF-8 bytes string, - *** please use PyUnicode_AsUTF8String() instead. - */ PyAPI_FUNC(const char *) PyUnicode_AsUTF8(PyObject *unicode); diff --git a/Include/patchlevel.h b/Include/patchlevel.h index 0b5d280b..a23b2f15 100644 --- a/Include/patchlevel.h +++ b/Include/patchlevel.h @@ -18,12 +18,12 @@ /*--start constants--*/ #define PY_MAJOR_VERSION 3 #define PY_MINOR_VERSION 9 -#define PY_MICRO_VERSION 1 +#define PY_MICRO_VERSION 2 #define PY_RELEASE_LEVEL PY_RELEASE_LEVEL_FINAL #define PY_RELEASE_SERIAL 0 /* Version as a string */ -#define PY_VERSION "3.9.1" +#define PY_VERSION "3.9.2" /*--end constants--*/ /* Version as a single 4-byte hex number, e.g. 0x010502B2 == 1.5.2b2. diff --git a/Include/pylifecycle.h b/Include/pylifecycle.h index c5368b3c..783fcb45 100644 --- a/Include/pylifecycle.h +++ b/Include/pylifecycle.h @@ -32,6 +32,8 @@ PyAPI_FUNC(void) _Py_NO_RETURN Py_Exit(int); /* Bootstrap __main__ (defined in Modules/main.c) */ PyAPI_FUNC(int) Py_Main(int argc, wchar_t **argv); +PyAPI_FUNC(int) Py_FrozenMain(int argc, char **argv); + PyAPI_FUNC(int) Py_BytesMain(int argc, char **argv); /* In pathconfig.c */ diff --git a/LICENSE b/LICENSE index f42f8adb..473861da 100644 --- a/LICENSE +++ b/LICENSE @@ -84,7 +84,7 @@ analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use Python alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of copyright, i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, -2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Python Software Foundation; +2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021 Python Software Foundation; All Rights Reserved" are retained in Python alone or in any derivative version prepared by Licensee. diff --git a/Lib/_collections_abc.py b/Lib/_collections_abc.py index 36cd9930..b6ecf8ea 100644 --- a/Lib/_collections_abc.py +++ b/Lib/_collections_abc.py @@ -10,6 +10,10 @@ from abc import ABCMeta, abstractmethod import sys GenericAlias = type(list[int]) +EllipsisType = type(...) +def _f(): pass +FunctionType = type(_f) +del _f __all__ = ["Awaitable", "Coroutine", "AsyncIterable", "AsyncIterator", "AsyncGenerator", @@ -409,6 +413,86 @@ class Collection(Sized, Iterable, Container): return NotImplemented +class _CallableGenericAlias(GenericAlias): + """ Represent `Callable[argtypes, resulttype]`. + + This sets ``__args__`` to a tuple containing the flattened``argtypes`` + followed by ``resulttype``. + + Example: ``Callable[[int, str], float]`` sets ``__args__`` to + ``(int, str, float)``. + """ + + __slots__ = () + + def __new__(cls, origin, args): + try: + return cls.__create_ga(origin, args) + except TypeError as exc: + import warnings + warnings.warn(f'{str(exc)} ' + f'(This will raise a TypeError in Python 3.10.)', + DeprecationWarning) + return GenericAlias(origin, args) + + @classmethod + def __create_ga(cls, origin, args): + if not isinstance(args, tuple) or len(args) != 2: + raise TypeError( + "Callable must be used as Callable[[arg, ...], result].") + t_args, t_result = args + if isinstance(t_args, (list, tuple)): + ga_args = tuple(t_args) + (t_result,) + # This relaxes what t_args can be on purpose to allow things like + # PEP 612 ParamSpec. Responsibility for whether a user is using + # Callable[...] properly is deferred to static type checkers. + else: + ga_args = args + return super().__new__(cls, origin, ga_args) + + def __repr__(self): + if len(self.__args__) == 2 and self.__args__[0] is Ellipsis: + return super().__repr__() + return (f'collections.abc.Callable' + f'[[{", ".join([_type_repr(a) for a in self.__args__[:-1]])}], ' + f'{_type_repr(self.__args__[-1])}]') + + def __reduce__(self): + args = self.__args__ + if not (len(args) == 2 and args[0] is Ellipsis): + args = list(args[:-1]), args[-1] + return _CallableGenericAlias, (Callable, args) + + def __getitem__(self, item): + # Called during TypeVar substitution, returns the custom subclass + # rather than the default types.GenericAlias object. + ga = super().__getitem__(item) + args = ga.__args__ + t_result = args[-1] + t_args = args[:-1] + args = (t_args, t_result) + return _CallableGenericAlias(Callable, args) + + +def _type_repr(obj): + """Return the repr() of an object, special-casing types (internal helper). + + Copied from :mod:`typing` since collections.abc + shouldn't depend on that module. + """ + if isinstance(obj, GenericAlias): + return repr(obj) + if isinstance(obj, type): + if obj.__module__ == 'builtins': + return obj.__qualname__ + return f'{obj.__module__}.{obj.__qualname__}' + if obj is Ellipsis: + return '...' + if isinstance(obj, FunctionType): + return obj.__name__ + return repr(obj) + + class Callable(metaclass=ABCMeta): __slots__ = () @@ -423,7 +507,7 @@ class Callable(metaclass=ABCMeta): return _check_methods(C, "__call__") return NotImplemented - __class_getitem__ = classmethod(GenericAlias) + __class_getitem__ = classmethod(_CallableGenericAlias) ### SETS ### diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py index f486b672..d6262ae7 100644 --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -471,7 +471,10 @@ async def wait_for(fut, timeout, *, loop=None): return fut.result() else: fut.remove_done_callback(cb) - fut.cancel() + # We must ensure that the task is not running + # after wait_for() returns. + # See https://bugs.python.org/issue32751 + await _cancel_and_wait(fut, loop=loop) raise if fut.done(): diff --git a/Lib/asyncio/unix_events.py b/Lib/asyncio/unix_events.py index f34a5b4b..3efa6698 100644 --- a/Lib/asyncio/unix_events.py +++ b/Lib/asyncio/unix_events.py @@ -1230,13 +1230,15 @@ class MultiLoopChildWatcher(AbstractChildWatcher): def close(self): self._callbacks.clear() - if self._saved_sighandler is not None: - handler = signal.getsignal(signal.SIGCHLD) - if handler != self._sig_chld: - logger.warning("SIGCHLD handler was changed by outside code") - else: - signal.signal(signal.SIGCHLD, self._saved_sighandler) - self._saved_sighandler = None + if self._saved_sighandler is None: + return + + handler = signal.getsignal(signal.SIGCHLD) + if handler != self._sig_chld: + logger.warning("SIGCHLD handler was changed by outside code") + else: + signal.signal(signal.SIGCHLD, self._saved_sighandler) + self._saved_sighandler = None def __enter__(self): return self @@ -1263,15 +1265,17 @@ class MultiLoopChildWatcher(AbstractChildWatcher): # The reason to do it here is that attach_loop() is called from # unix policy only for the main thread. # Main thread is required for subscription on SIGCHLD signal + if self._saved_sighandler is not None: + return + + self._saved_sighandler = signal.signal(signal.SIGCHLD, self._sig_chld) if self._saved_sighandler is None: - self._saved_sighandler = signal.signal(signal.SIGCHLD, self._sig_chld) - if self._saved_sighandler is None: - logger.warning("Previous SIGCHLD handler was set by non-Python code, " - "restore to default handler on watcher close.") - self._saved_sighandler = signal.SIG_DFL + logger.warning("Previous SIGCHLD handler was set by non-Python code, " + "restore to default handler on watcher close.") + self._saved_sighandler = signal.SIG_DFL - # Set SA_RESTART to limit EINTR occurrences. - signal.siginterrupt(signal.SIGCHLD, False) + # Set SA_RESTART to limit EINTR occurrences. + signal.siginterrupt(signal.SIGCHLD, False) def _do_waitpid_all(self): for pid in list(self._callbacks): diff --git a/Lib/base64.py b/Lib/base64.py index a28109f8..ec3823b7 100755 --- a/Lib/base64.py +++ b/Lib/base64.py @@ -320,7 +320,7 @@ def a85encode(b, *, foldspaces=False, wrapcol=0, pad=False, adobe=False): global _a85chars, _a85chars2 # Delay the initialization of tables to not waste memory # if the function is never called - if _a85chars is None: + if _a85chars2 is None: _a85chars = [bytes((i,)) for i in range(33, 118)] _a85chars2 = [(a + b) for a in _a85chars for b in _a85chars] @@ -428,7 +428,7 @@ def b85encode(b, pad=False): global _b85chars, _b85chars2 # Delay the initialization of tables to not waste memory # if the function is never called - if _b85chars is None: + if _b85chars2 is None: _b85chars = [bytes((i,)) for i in _b85alphabet] _b85chars2 = [(a + b) for a in _b85chars for b in _b85chars] return _85encode(b, _b85chars, _b85chars2, pad) diff --git a/Lib/cProfile.py b/Lib/cProfile.py index 59b4699f..22a7d0aa 100755 --- a/Lib/cProfile.py +++ b/Lib/cProfile.py @@ -175,7 +175,12 @@ def main(): '__package__': None, '__cached__': None, } - runctx(code, globs, None, options.outfile, options.sort) + try: + runctx(code, globs, None, options.outfile, options.sort) + except BrokenPipeError as exc: + # Prevent "Exception ignored" during interpreter shutdown. + sys.stdout = None + sys.exit(exc.errno) else: parser.print_usage() return parser diff --git a/Lib/cgi.py b/Lib/cgi.py index 77ab703c..1e880e51 100755 --- a/Lib/cgi.py +++ b/Lib/cgi.py @@ -115,7 +115,8 @@ log = initlog # The current logging function # 0 ==> unlimited input maxlen = 0 -def parse(fp=None, environ=os.environ, keep_blank_values=0, strict_parsing=0): +def parse(fp=None, environ=os.environ, keep_blank_values=0, + strict_parsing=0, separator='&'): """Parse a query in the environment or from a file (default stdin) Arguments, all optional: @@ -134,6 +135,9 @@ def parse(fp=None, environ=os.environ, keep_blank_values=0, strict_parsing=0): strict_parsing: flag indicating what to do with parsing errors. If false (the default), errors are silently ignored. If true, errors raise a ValueError exception. + + separator: str. The symbol to use for separating the query arguments. + Defaults to &. """ if fp is None: fp = sys.stdin @@ -154,7 +158,7 @@ def parse(fp=None, environ=os.environ, keep_blank_values=0, strict_parsing=0): if environ['REQUEST_METHOD'] == 'POST': ctype, pdict = parse_header(environ['CONTENT_TYPE']) if ctype == 'multipart/form-data': - return parse_multipart(fp, pdict) + return parse_multipart(fp, pdict, separator=separator) elif ctype == 'application/x-www-form-urlencoded': clength = int(environ['CONTENT_LENGTH']) if maxlen and clength > maxlen: @@ -178,10 +182,10 @@ def parse(fp=None, environ=os.environ, keep_blank_values=0, strict_parsing=0): qs = "" environ['QUERY_STRING'] = qs # XXX Shouldn't, really return urllib.parse.parse_qs(qs, keep_blank_values, strict_parsing, - encoding=encoding) + encoding=encoding, separator=separator) -def parse_multipart(fp, pdict, encoding="utf-8", errors="replace"): +def parse_multipart(fp, pdict, encoding="utf-8", errors="replace", separator='&'): """Parse multipart input. Arguments: @@ -205,7 +209,7 @@ def parse_multipart(fp, pdict, encoding="utf-8", errors="replace"): except KeyError: pass fs = FieldStorage(fp, headers=headers, encoding=encoding, errors=errors, - environ={'REQUEST_METHOD': 'POST'}) + environ={'REQUEST_METHOD': 'POST'}, separator=separator) return {k: fs.getlist(k) for k in fs} def _parseparam(s): @@ -315,7 +319,7 @@ class FieldStorage: def __init__(self, fp=None, headers=None, outerboundary=b'', environ=os.environ, keep_blank_values=0, strict_parsing=0, limit=None, encoding='utf-8', errors='replace', - max_num_fields=None): + max_num_fields=None, separator='&'): """Constructor. Read multipart/* until last part. Arguments, all optional: @@ -363,6 +367,7 @@ class FieldStorage: self.keep_blank_values = keep_blank_values self.strict_parsing = strict_parsing self.max_num_fields = max_num_fields + self.separator = separator if 'REQUEST_METHOD' in environ: method = environ['REQUEST_METHOD'].upper() self.qs_on_post = None @@ -589,7 +594,7 @@ class FieldStorage: query = urllib.parse.parse_qsl( qs, self.keep_blank_values, self.strict_parsing, encoding=self.encoding, errors=self.errors, - max_num_fields=self.max_num_fields) + max_num_fields=self.max_num_fields, separator=self.separator) self.list = [MiniFieldStorage(key, value) for key, value in query] self.skip_lines() @@ -605,7 +610,7 @@ class FieldStorage: query = urllib.parse.parse_qsl( self.qs_on_post, self.keep_blank_values, self.strict_parsing, encoding=self.encoding, errors=self.errors, - max_num_fields=self.max_num_fields) + max_num_fields=self.max_num_fields, separator=self.separator) self.list.extend(MiniFieldStorage(key, value) for key, value in query) klass = self.FieldStorageClass or self.__class__ @@ -649,7 +654,7 @@ class FieldStorage: else self.limit - self.bytes_read part = klass(self.fp, headers, ib, environ, keep_blank_values, strict_parsing, limit, - self.encoding, self.errors, max_num_fields) + self.encoding, self.errors, max_num_fields, self.separator) if max_num_fields is not None: max_num_fields -= 1 diff --git a/Lib/codecs.py b/Lib/codecs.py index 7f23e977..d2edd148 100644 --- a/Lib/codecs.py +++ b/Lib/codecs.py @@ -386,7 +386,7 @@ class StreamWriter(Codec): def reset(self): - """ Flushes and resets the codec buffers used for keeping state. + """ Resets the codec buffers used for keeping internal state. Calling this method should ensure that the data on the output is put into a clean state, that allows appending @@ -620,7 +620,7 @@ class StreamReader(Codec): def reset(self): - """ Resets the codec buffers used for keeping state. + """ Resets the codec buffers used for keeping internal state. Note that no stream repositioning should take place. This method is primarily intended to be able to recover diff --git a/Lib/collections/__init__.py b/Lib/collections/__init__.py index bc69a675..5bdd3b35 100644 --- a/Lib/collections/__init__.py +++ b/Lib/collections/__init__.py @@ -424,7 +424,7 @@ def namedtuple(typename, field_names, *, rename=False, defaults=None, module=Non namespace = { '_tuple_new': tuple_new, - '__builtins__': None, + '__builtins__': {}, '__name__': f'namedtuple_{typename}', } code = f'lambda _cls, {arg_list}: _tuple_new(_cls, ({arg_list}))' diff --git a/Lib/collections/abc.py b/Lib/collections/abc.py index 891600d1..86ca8b8a 100644 --- a/Lib/collections/abc.py +++ b/Lib/collections/abc.py @@ -1,2 +1,3 @@ from _collections_abc import * from _collections_abc import __all__ +from _collections_abc import _CallableGenericAlias diff --git a/Lib/ctypes/test/test_parameters.py b/Lib/ctypes/test/test_parameters.py index e4c25fd8..38af7ac1 100644 --- a/Lib/ctypes/test/test_parameters.py +++ b/Lib/ctypes/test/test_parameters.py @@ -201,6 +201,49 @@ class SimpleTypesTestCase(unittest.TestCase): with self.assertRaises(ZeroDivisionError): WorseStruct().__setstate__({}, b'foo') + def test_parameter_repr(self): + from ctypes import ( + c_bool, + c_char, + c_wchar, + c_byte, + c_ubyte, + c_short, + c_ushort, + c_int, + c_uint, + c_long, + c_ulong, + c_longlong, + c_ulonglong, + c_float, + c_double, + c_longdouble, + c_char_p, + c_wchar_p, + c_void_p, + ) + self.assertRegex(repr(c_bool.from_param(True)), r"^$") + self.assertEqual(repr(c_char.from_param(97)), "") + self.assertRegex(repr(c_wchar.from_param('a')), r"^$") + self.assertEqual(repr(c_byte.from_param(98)), "") + self.assertEqual(repr(c_ubyte.from_param(98)), "") + self.assertEqual(repr(c_short.from_param(511)), "") + self.assertEqual(repr(c_ushort.from_param(511)), "") + self.assertRegex(repr(c_int.from_param(20000)), r"^$") + self.assertRegex(repr(c_uint.from_param(20000)), r"^$") + self.assertRegex(repr(c_long.from_param(20000)), r"^$") + self.assertRegex(repr(c_ulong.from_param(20000)), r"^$") + self.assertRegex(repr(c_longlong.from_param(20000)), r"^$") + self.assertRegex(repr(c_ulonglong.from_param(20000)), r"^$") + self.assertEqual(repr(c_float.from_param(1.5)), "") + self.assertEqual(repr(c_double.from_param(1.5)), "") + self.assertEqual(repr(c_double.from_param(1e300)), "") + self.assertRegex(repr(c_longdouble.from_param(1.5)), r"^$") + self.assertRegex(repr(c_char_p.from_param(b'hihi')), r"^$") + self.assertRegex(repr(c_wchar_p.from_param('hihi')), r"^$") + self.assertRegex(repr(c_void_p.from_param(0x12)), r"^$") + ################################################################ if __name__ == '__main__': diff --git a/Lib/datetime.py b/Lib/datetime.py index e508d996..23d2bf09 100644 --- a/Lib/datetime.py +++ b/Lib/datetime.py @@ -2358,7 +2358,7 @@ _EPOCH = datetime(1970, 1, 1, tzinfo=timezone.utc) # This is again a requirement for a sane tzinfo class. # # 4. (x+k).s = x.s -# This follows from #2, and that datimetimetz+timedelta preserves tzinfo. +# This follows from #2, and that datetime.timetz+timedelta preserves tzinfo. # # 5. (x+k).n = x.n + k # Again follows from how arithmetic is defined. diff --git a/Lib/distutils/spawn.py b/Lib/distutils/spawn.py index f50edd2d..0d1bd039 100644 --- a/Lib/distutils/spawn.py +++ b/Lib/distutils/spawn.py @@ -54,8 +54,8 @@ def spawn(cmd, search_path=1, verbose=0, dry_run=0): global _cfg_target, _cfg_target_split if _cfg_target is None: from distutils import sysconfig - _cfg_target = str(sysconfig.get_config_var( - 'MACOSX_DEPLOYMENT_TARGET') or '') + _cfg_target = sysconfig.get_config_var( + 'MACOSX_DEPLOYMENT_TARGET') or '' if _cfg_target: _cfg_target_split = [int(x) for x in _cfg_target.split('.')] if _cfg_target: diff --git a/Lib/distutils/tests/test_build_ext.py b/Lib/distutils/tests/test_build_ext.py index 1b034c93..808c0dc2 100644 --- a/Lib/distutils/tests/test_build_ext.py +++ b/Lib/distutils/tests/test_build_ext.py @@ -455,7 +455,7 @@ class BuildExtTestCase(TempdirManager, deptarget = sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET') if deptarget: # increment the minor version number (i.e. 10.6 -> 10.7) - deptarget = [int(x) for x in str(deptarget).split('.')] + deptarget = [int(x) for x in deptarget.split('.')] deptarget[-1] += 1 deptarget = '.'.join(str(i) for i in deptarget) self._try_compile_deployment_target('<', deptarget) @@ -488,7 +488,7 @@ class BuildExtTestCase(TempdirManager, # get the deployment target that the interpreter was built with target = sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET') - target = tuple(map(int, str(target).split('.')[0:2])) + target = tuple(map(int, target.split('.')[0:2])) # format the target value as defined in the Apple # Availability Macros. We can't use the macro names since # at least one value we test with will not exist yet. diff --git a/Lib/enum.py b/Lib/enum.py index ebadd9f6..1fddb1c7 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -10,31 +10,54 @@ __all__ = [ def _is_descriptor(obj): - """Returns True if obj is a descriptor, False otherwise.""" + """ + Returns True if obj is a descriptor, False otherwise. + """ return ( hasattr(obj, '__get__') or hasattr(obj, '__set__') or - hasattr(obj, '__delete__')) - + hasattr(obj, '__delete__') + ) def _is_dunder(name): - """Returns True if a __dunder__ name, False otherwise.""" - return (len(name) > 4 and + """ + Returns True if a __dunder__ name, False otherwise. + """ + return ( + len(name) > 4 and name[:2] == name[-2:] == '__' and name[2] != '_' and - name[-3] != '_') - + name[-3] != '_' + ) def _is_sunder(name): - """Returns True if a _sunder_ name, False otherwise.""" - return (len(name) > 2 and + """ + Returns True if a _sunder_ name, False otherwise. + """ + return ( + len(name) > 2 and name[0] == name[-1] == '_' and name[1:2] != '_' and - name[-2:-1] != '_') - + name[-2:-1] != '_' + ) + +def _is_private(cls_name, name): + # do not use `re` as `re` imports `enum` + pattern = '_%s__' % (cls_name, ) + if ( + len(name) >= 5 + and name.startswith(pattern) + and name[len(pattern)] != '_' + and (name[-1] != '_' or name[-2] != '_') + ): + return True + else: + return False def _make_class_unpicklable(cls): - """Make the given class un-picklable.""" + """ + Make the given class un-picklable. + """ def _break_on_call_reduce(self, proto): raise TypeError('%r cannot be pickled' % self) cls.__reduce_ex__ = _break_on_call_reduce @@ -49,11 +72,11 @@ class auto: class _EnumDict(dict): - """Track enum member order and ensure member names are not reused. + """ + Track enum member order and ensure member names are not reused. EnumMeta will use the names found in self._member_names as the enumeration member names. - """ def __init__(self): super().__init__() @@ -63,14 +86,22 @@ class _EnumDict(dict): self._auto_called = False def __setitem__(self, key, value): - """Changes anything not dundered or not a descriptor. + """ + Changes anything not dundered or not a descriptor. If an enum member name is used twice, an error is raised; duplicate values are not checked for. Single underscore (sunder) names are reserved. - """ + if _is_private(self._cls_name, key): + import warnings + warnings.warn( + "private variables, such as %r, will be normal attributes in 3.10" + % (key, ), + DeprecationWarning, + stacklevel=2, + ) if _is_sunder(key): if key not in ( '_order_', '_create_pseudo_member_', @@ -90,7 +121,10 @@ class _EnumDict(dict): self._ignore = value already = set(value) & set(self._member_names) if already: - raise ValueError('_ignore_ cannot specify already set names: %r' % (already, )) + raise ValueError( + '_ignore_ cannot specify already set names: %r' + % (already, ) + ) elif _is_dunder(key): if key == '__order__': key = '_order_' @@ -105,7 +139,12 @@ class _EnumDict(dict): raise TypeError('%r already defined as: %r' % (key, self[key])) if isinstance(value, auto): if value.value == _auto_null: - value.value = self._generate_next_value(key, 1, len(self._member_names), self._last_values[:]) + value.value = self._generate_next_value( + key, + 1, + len(self._member_names), + self._last_values[:], + ) self._auto_called = True value = value.value self._member_names.append(key) @@ -118,22 +157,26 @@ class _EnumDict(dict): # This is also why there are checks in EnumMeta like `if Enum is not None` Enum = None - class EnumMeta(type): - """Metaclass for Enum""" + """ + Metaclass for Enum + """ @classmethod - def __prepare__(metacls, cls, bases): + def __prepare__(metacls, cls, bases, **kwds): # check that previous enum members do not exist metacls._check_for_existing_members(cls, bases) # create the namespace dict enum_dict = _EnumDict() + enum_dict._cls_name = cls # inherit previous flags and _generate_next_value_ function member_type, first_enum = metacls._get_mixins_(cls, bases) if first_enum is not None: - enum_dict['_generate_next_value_'] = getattr(first_enum, '_generate_next_value_', None) + enum_dict['_generate_next_value_'] = getattr( + first_enum, '_generate_next_value_', None, + ) return enum_dict - def __new__(metacls, cls, bases, classdict): + def __new__(metacls, cls, bases, classdict, **kwds): # an Enum class is final once enumeration items have been defined; it # cannot be mixed with other types (int, float, etc.) if it has an # inherited __new__ unless a new __new__ is defined (or the resulting @@ -145,8 +188,9 @@ class EnumMeta(type): for key in ignore: classdict.pop(key, None) member_type, first_enum = metacls._get_mixins_(cls, bases) - __new__, save_new, use_args = metacls._find_new_(classdict, member_type, - first_enum) + __new__, save_new, use_args = metacls._find_new_( + classdict, member_type, first_enum, + ) # save enum items into separate mapping so they don't get baked into # the new class @@ -167,17 +211,18 @@ class EnumMeta(type): if '__doc__' not in classdict: classdict['__doc__'] = 'An enumeration.' - # create our new Enum type - enum_class = super().__new__(metacls, cls, bases, classdict) + enum_class = super().__new__(metacls, cls, bases, classdict, **kwds) enum_class._member_names_ = [] # names in definition order enum_class._member_map_ = {} # name->value map enum_class._member_type_ = member_type # save DynamicClassAttribute attributes from super classes so we know # if we can take the shortcut of storing members in the class dict - dynamic_attributes = {k for c in enum_class.mro() - for k, v in c.__dict__.items() - if isinstance(v, DynamicClassAttribute)} + dynamic_attributes = { + k for c in enum_class.mro() + for k, v in c.__dict__.items() + if isinstance(v, DynamicClassAttribute) + } # Reverse value->name map for hashable values. enum_class._value2member_map_ = {} @@ -287,7 +332,8 @@ class EnumMeta(type): return True def __call__(cls, value, names=None, *, module=None, qualname=None, type=None, start=1): - """Either returns an existing member, or creates a new enum class. + """ + Either returns an existing member, or creates a new enum class. This method is used both when an enum class is given a value to match to an enumeration member (i.e. Color(3)) and for the functional API @@ -309,12 +355,18 @@ class EnumMeta(type): not correct, unpickling will fail in some circumstances. `type`, if set, will be mixed in as the first base class. - """ if names is None: # simple value lookup return cls.__new__(cls, value) # otherwise, functional API: we're creating a new Enum type - return cls._create_(value, names, module=module, qualname=qualname, type=type, start=start) + return cls._create_( + value, + names, + module=module, + qualname=qualname, + type=type, + start=start, + ) def __contains__(cls, member): if not isinstance(member, Enum): @@ -327,22 +379,23 @@ class EnumMeta(type): # nicer error message when someone tries to delete an attribute # (see issue19025). if attr in cls._member_map_: - raise AttributeError( - "%s: cannot delete Enum member." % cls.__name__) + raise AttributeError("%s: cannot delete Enum member." % cls.__name__) super().__delattr__(attr) def __dir__(self): - return (['__class__', '__doc__', '__members__', '__module__'] + - self._member_names_) + return ( + ['__class__', '__doc__', '__members__', '__module__'] + + self._member_names_ + ) def __getattr__(cls, name): - """Return the enum member matching `name` + """ + Return the enum member matching `name` We use __getattr__ instead of descriptors or inserting into the enum class' __dict__ in order to support `name` and `value` being both properties for enum members (which live in the class' __dict__) and enum members themselves. - """ if _is_dunder(name): raise AttributeError(name) @@ -355,6 +408,9 @@ class EnumMeta(type): return cls._member_map_[name] def __iter__(cls): + """ + Returns members in definition order. + """ return (cls._member_map_[name] for name in cls._member_names_) def __len__(cls): @@ -362,11 +418,11 @@ class EnumMeta(type): @property def __members__(cls): - """Returns a mapping of member name->value. + """ + Returns a mapping of member name->value. This mapping lists all enum members, including aliases. Note that this is a read-only view of the internal mapping. - """ return MappingProxyType(cls._member_map_) @@ -374,15 +430,18 @@ class EnumMeta(type): return "" % cls.__name__ def __reversed__(cls): + """ + Returns members in reverse definition order. + """ return (cls._member_map_[name] for name in reversed(cls._member_names_)) def __setattr__(cls, name, value): - """Block attempts to reassign Enum members. + """ + Block attempts to reassign Enum members. A simple assignment to the class namespace only changes one of the several possible ways to get an Enum member from the Enum class, resulting in an inconsistent Enumeration. - """ member_map = cls.__dict__.get('_member_map_', {}) if name in member_map: @@ -390,7 +449,8 @@ class EnumMeta(type): super().__setattr__(name, value) def _create_(cls, class_name, names, *, module=None, qualname=None, type=None, start=1): - """Convenience method to create a new Enum class. + """ + Convenience method to create a new Enum class. `names` can be: @@ -399,7 +459,6 @@ class EnumMeta(type): * An iterable of member names. Values are incremented by 1 from `start`. * An iterable of (member name, value) pairs. * A mapping of member name -> value pairs. - """ metacls = cls.__class__ bases = (cls, ) if type is None else (type, cls) @@ -480,15 +539,18 @@ class EnumMeta(type): for chain in bases: for base in chain.__mro__: if issubclass(base, Enum) and base._member_names_: - raise TypeError("%s: cannot extend enumeration %r" % (class_name, base.__name__)) + raise TypeError( + "%s: cannot extend enumeration %r" + % (class_name, base.__name__) + ) @staticmethod def _get_mixins_(class_name, bases): - """Returns the type for creating enum members, and the first inherited + """ + Returns the type for creating enum members, and the first inherited enum class. bases: the tuple of bases that was given to __new__ - """ if not bases: return object, Enum @@ -500,12 +562,16 @@ class EnumMeta(type): for base in chain.__mro__: if base is object: continue + elif issubclass(base, Enum): + if base._member_type_ is not object: + data_types.append(base._member_type_) + break elif '__new__' in base.__dict__: if issubclass(base, Enum): continue data_types.append(candidate or base) break - elif not issubclass(base, Enum): + else: candidate = base if len(data_types) > 1: raise TypeError('%r: too many data types: %r' % (class_name, data_types)) @@ -527,12 +593,12 @@ class EnumMeta(type): @staticmethod def _find_new_(classdict, member_type, first_enum): - """Returns the __new__ to be used for creating the enum members. + """ + Returns the __new__ to be used for creating the enum members. classdict: the class dictionary given to __new__ member_type: the data type whose __new__ will be used by default first_enum: enumeration to check for an overriding __new__ - """ # now find the correct __new__, checking to see of one was defined # by the user; also check earlier enum classes in case a __new__ was @@ -572,10 +638,10 @@ class EnumMeta(type): class Enum(metaclass=EnumMeta): - """Generic enumeration. + """ + Generic enumeration. Derive from this class to define new enumerations. - """ def __new__(cls, value): # all enum instances are actually created during class construction @@ -618,6 +684,14 @@ class Enum(metaclass=EnumMeta): raise exc def _generate_next_value_(name, start, count, last_values): + """ + Generate the next value when not given. + + name: the name of the member + start: the initial start value or None + count: the number of existing members + last_value: the last value assigned or None + """ for last_value in reversed(last_values): try: return last_value + 1 @@ -638,21 +712,27 @@ class Enum(metaclass=EnumMeta): return "%s.%s" % (self.__class__.__name__, self._name_) def __dir__(self): + """ + Returns all members and all public methods + """ added_behavior = [ m for cls in self.__class__.mro() for m in cls.__dict__ if m[0] != '_' and m not in self._member_map_ - ] + ] + [m for m in self.__dict__ if m[0] != '_'] return (['__class__', '__doc__', '__module__'] + added_behavior) def __format__(self, format_spec): + """ + Returns format using actual value type unless __str__ has been overridden. + """ # mixed-in Enums should use the mixed-in type's __format__, otherwise # we can get strange results with the Enum name showing up instead of # the value # pure Enum branch, or branch with __str__ explicitly overridden - str_overridden = type(self).__str__ != Enum.__str__ + str_overridden = type(self).__str__ not in (Enum.__str__, Flag.__str__) if self._member_type_ is object or str_overridden: cls = str val = str(self) @@ -694,7 +774,9 @@ def _reduce_ex_by_name(self, proto): return self.name class Flag(Enum): - """Support for flags""" + """ + Support for flags + """ def _generate_next_value_(name, start, count, last_values): """ @@ -717,6 +799,9 @@ class Flag(Enum): @classmethod def _missing_(cls, value): + """ + Returns member (possibly creating it) if one can be found for value. + """ original_value = value if value < 0: value = ~value @@ -746,6 +831,9 @@ class Flag(Enum): return pseudo_member def __contains__(self, other): + """ + Returns True if self has at least the same flags set as other. + """ if not isinstance(other, self.__class__): raise TypeError( "unsupported operand type(s) for 'in': '%s' and '%s'" % ( @@ -804,10 +892,15 @@ class Flag(Enum): class IntFlag(int, Flag): - """Support for integer-based Flags""" + """ + Support for integer-based Flags + """ @classmethod def _missing_(cls, value): + """ + Returns member (possibly creating it) if one can be found for value. + """ if not isinstance(value, int): raise ValueError("%r is not a valid %s" % (value, cls.__qualname__)) new_member = cls._create_pseudo_member_(value) @@ -815,6 +908,9 @@ class IntFlag(int, Flag): @classmethod def _create_pseudo_member_(cls, value): + """ + Create a composite member iff value contains only members. + """ pseudo_member = cls._value2member_map_.get(value, None) if pseudo_member is None: need_to_create = [value] @@ -869,11 +965,15 @@ class IntFlag(int, Flag): def _high_bit(value): - """returns index of highest bit, or -1 if value is zero or negative""" + """ + returns index of highest bit, or -1 if value is zero or negative + """ return value.bit_length() - 1 def unique(enumeration): - """Class decorator for enumerations ensuring unique member values.""" + """ + Class decorator for enumerations ensuring unique member values. + """ duplicates = [] for name, member in enumeration.__members__.items(): if name != member.name: @@ -886,7 +986,9 @@ def unique(enumeration): return enumeration def _decompose(flag, value): - """Extract all members from the value.""" + """ + Extract all members from the value. + """ # _decompose is only called if the value is not named not_covered = value negative = value < 0 diff --git a/Lib/fnmatch.py b/Lib/fnmatch.py index 0eb1802b..7c52c230 100644 --- a/Lib/fnmatch.py +++ b/Lib/fnmatch.py @@ -52,7 +52,7 @@ def _compile_pattern(pat): return re.compile(res).match def filter(names, pat): - """Return the subset of the list NAMES that match PAT.""" + """Construct a list from those elements of the iterable NAMES that match PAT.""" result = [] pat = os.path.normcase(pat) match = _compile_pattern(pat) diff --git a/Lib/html/parser.py b/Lib/html/parser.py index 60830779..9e49effc 100644 --- a/Lib/html/parser.py +++ b/Lib/html/parser.py @@ -46,7 +46,7 @@ locatestarttagend_tolerant = re.compile(r""" |"[^"]*" # LIT-enclosed value |(?!['"])[^>\s]* # bare value ) - (?:\s*,)* # possibly followed by a comma + \s* # possibly followed by a space )?(?:\s|/(?!>))* )* )? diff --git a/Lib/http/client.py b/Lib/http/client.py index c2ad0471..16afc871 100644 --- a/Lib/http/client.py +++ b/Lib/http/client.py @@ -846,7 +846,7 @@ class HTTPConnection: the endpoint passed to `set_tunnel`. This done by sending an HTTP CONNECT request to the proxy server when the connection is established. - This method must be called before the HTML connection has been + This method must be called before the HTTP connection has been established. The headers argument should be a mapping of extra HTTP headers to send diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt index 869be0a6..4bc4aefd 100644 --- a/Lib/idlelib/NEWS.txt +++ b/Lib/idlelib/NEWS.txt @@ -1,8 +1,24 @@ -What's New in IDLE 3.9.1 -Released on 2020-12-07? +What's New in IDLE 3.9.2 +Released on 2021-02-15? ====================================== +bpo-23544: Disable Debug=>Stack Viewer when user code is running or +Debugger is active, to prevent hang or crash. Patch by Zackery Spytz. + +bpo-43008: Make IDLE invoke :func:`sys.excepthook` in normal, +2-process mode. Patch by Ken Hilton. + +bpo-33065: Fix problem debugging user classes with __repr__ method. + +bpo-32631: Finish zzdummy example extension module: make menu entries +work; add docstrings and tests with 100% coverage. + + +What's New in IDLE 3.9.1 +Released on 2020-12-07 +====================================== + bpo-42508: Keep IDLE running on macOS. Remove obsolete workaround that prevented running files with shortcuts when using new universal2 installers built on macOS 11. diff --git a/Lib/idlelib/codecontext.py b/Lib/idlelib/codecontext.py index 989b30e5..f2f44f5f 100644 --- a/Lib/idlelib/codecontext.py +++ b/Lib/idlelib/codecontext.py @@ -7,11 +7,14 @@ the lines which contain the block opening keywords, e.g. 'if', for the enclosing block. The number of hint lines is determined by the maxlines variable in the codecontext section of config-extensions.def. Lines which do not open blocks are not shown in the context hints pane. + +For EditorWindows, <> is bound to CodeContext(self). +toggle_code_context_event. """ import re from sys import maxsize as INFINITY -import tkinter +from tkinter import Frame, Text, TclError from tkinter.constants import NSEW, SUNKEN from idlelib.config import idleConf @@ -83,7 +86,7 @@ class CodeContext: if self.t1 is not None: try: self.text.after_cancel(self.t1) - except tkinter.TclError: # pragma: no cover + except TclError: # pragma: no cover pass self.t1 = None @@ -111,7 +114,7 @@ class CodeContext: padx += widget.tk.getint(info['padx']) padx += widget.tk.getint(widget.cget('padx')) border += widget.tk.getint(widget.cget('border')) - context = self.context = tkinter.Text( + context = self.context = Text( self.editwin.text_frame, height=1, width=1, # Don't request more than we get. @@ -127,7 +130,7 @@ class CodeContext: line_number_colors = idleConf.GetHighlight(idleConf.CurrentTheme(), 'linenumber') - self.cell00 = tkinter.Frame(self.editwin.text_frame, + self.cell00 = Frame(self.editwin.text_frame, bg=line_number_colors['background']) self.cell00.grid(row=0, column=0, sticky=NSEW) menu_status = 'Hide' @@ -139,7 +142,7 @@ class CodeContext: self.text.after_cancel(self.t1) self._reset() menu_status = 'Show' - self.editwin.update_menu_label(menu='options', index='* Code Context', + self.editwin.update_menu_label(menu='options', index='*ode*ontext', label=f'{menu_status} Code Context') return "break" @@ -221,7 +224,7 @@ class CodeContext: """ try: self.context.index("sel.first") - except tkinter.TclError: + except TclError: lines = len(self.info) if lines == 1: # No context lines are showing. newtop = 1 diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index a84e1c56..c52a04b5 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -18,8 +18,8 @@ from tkinter import (Toplevel, Listbox, Scale, Canvas, HORIZONTAL, VERTICAL, ANCHOR, ACTIVE, END) from tkinter.ttk import (Frame, LabelFrame, Button, Checkbutton, Entry, Label, OptionMenu, Notebook, Radiobutton, Scrollbar, Style) -import tkinter.colorchooser as tkColorChooser -import tkinter.font as tkFont +from tkinter import colorchooser +import tkinter.font as tkfont from tkinter import messagebox from idlelib.config import idleConf, ConfigChanges @@ -609,7 +609,7 @@ class FontPage(Frame): font_bold = configured_font[2]=='bold' # Set sorted no-duplicate editor font selection list and font_name. - fonts = sorted(set(tkFont.families(self))) + fonts = sorted(set(tkfont.families(self))) for font in fonts: self.fontlist.insert(END, font) self.font_name.set(font_name) @@ -663,7 +663,7 @@ class FontPage(Frame): Updates font_sample and highlight page highlight_sample. """ font_name = self.font_name.get() - font_weight = tkFont.BOLD if self.font_bold.get() else tkFont.NORMAL + font_weight = tkfont.BOLD if self.font_bold.get() else tkfont.NORMAL new_font = (font_name, self.font_size.get(), font_weight) self.font_sample['font'] = new_font self.highlight_sample['font'] = new_font @@ -1100,7 +1100,7 @@ class HighPage(Frame): target = self.highlight_target.get() prev_color = self.style.lookup(self.frame_color_set['style'], 'background') - rgbTuplet, color_string = tkColorChooser.askcolor( + rgbTuplet, color_string = colorchooser.askcolor( parent=self, title='Pick new color for : '+target, initialcolor=prev_color) if color_string and (color_string != prev_color): @@ -2316,7 +2316,15 @@ display when Code Context is turned on for an editor window. Shell Preferences: Auto-Squeeze Min. Lines is the minimum number of lines of output to automatically "squeeze". -''' +''', + 'Extensions': ''' +ZzDummy: This extension is provided as an example for how to create and +use an extension. Enable indicates whether the extension is active or +not; likewise enable_editor and enable_shell indicate which windows it +will be active on. For this extension, z-text is the text that will be +inserted at or removed from the beginning of the lines of selected text, +or the current line if no selection. +''', } diff --git a/Lib/idlelib/debugger_r.py b/Lib/idlelib/debugger_r.py index 9dcfc564..26204438 100644 --- a/Lib/idlelib/debugger_r.py +++ b/Lib/idlelib/debugger_r.py @@ -19,7 +19,7 @@ arguments and return values that cannot be transported through the RPC barrier, in particular frame and traceback objects. """ - +import reprlib import types from idlelib import debugger @@ -170,7 +170,7 @@ class IdbAdapter: def dict_item(self, did, key): dict = dicttable[did] value = dict[key] - value = repr(value) ### can't pickle module 'builtins' + value = reprlib.repr(value) ### can't pickle module 'builtins' return value #----------end class IdbAdapter---------- @@ -390,4 +390,4 @@ def restart_subprocess_debugger(rpcclt): if __name__ == "__main__": from unittest import main - main('idlelib.idle_test.test_debugger', verbosity=2, exit=False) + main('idlelib.idle_test.test_debugger_r', verbosity=2, exit=False) diff --git a/Lib/idlelib/editor.py b/Lib/idlelib/editor.py index a178eaf9..b9cb5026 100644 --- a/Lib/idlelib/editor.py +++ b/Lib/idlelib/editor.py @@ -12,8 +12,8 @@ import webbrowser from tkinter import * from tkinter.font import Font from tkinter.ttk import Scrollbar -import tkinter.simpledialog as tkSimpleDialog -import tkinter.messagebox as tkMessageBox +from tkinter import simpledialog +from tkinter import messagebox from idlelib.config import idleConf from idlelib import configdialog @@ -46,7 +46,7 @@ def _sphinx_version(): return release -class EditorWindow(object): +class EditorWindow: from idlelib.percolator import Percolator from idlelib.colorizer import ColorDelegator, color_config from idlelib.undo import UndoDelegator @@ -295,9 +295,9 @@ class EditorWindow(object): window.register_callback(self.postwindowsmenu) # Some abstractions so IDLE extensions are cross-IDE - self.askyesno = tkMessageBox.askyesno - self.askinteger = tkSimpleDialog.askinteger - self.showerror = tkMessageBox.showerror + self.askinteger = simpledialog.askinteger + self.askyesno = messagebox.askyesno + self.showerror = messagebox.showerror # Add pseudoevents for former extension fixed keys. # (This probably needs to be done once in the process.) @@ -339,7 +339,7 @@ class EditorWindow(object): text.bind("<>", self.code_context.toggle_code_context_event) else: - self.update_menu_state('options', '*Code Context', 'disabled') + self.update_menu_state('options', '*ode*ontext', 'disabled') if self.allow_line_numbers: self.line_numbers = self.LineNumbers(self) if idleConf.GetOption('main', 'EditorWindow', @@ -347,7 +347,7 @@ class EditorWindow(object): self.toggle_line_numbers_event() text.bind("<>", self.toggle_line_numbers_event) else: - self.update_menu_state('options', '*Line Numbers', 'disabled') + self.update_menu_state('options', '*ine*umbers', 'disabled') def handle_winconfig(self, event=None): self.set_width() @@ -450,7 +450,9 @@ class EditorWindow(object): self.menudict = menudict = {} for name, label in self.menu_specs: underline, label = prepstr(label) - menudict[name] = menu = Menu(mbar, name=name, tearoff=0) + postcommand = getattr(self, f'{name}_menu_postcommand', None) + menudict[name] = menu = Menu(mbar, name=name, tearoff=0, + postcommand=postcommand) mbar.add_cascade(label=label, menu=menu, underline=underline) if macosx.isCarbonTk(): # Insert the application menu @@ -596,7 +598,7 @@ class EditorWindow(object): try: os.startfile(self.help_url) except OSError as why: - tkMessageBox.showerror(title='Document Start Failure', + messagebox.showerror(title='Document Start Failure', message=str(why), parent=self.text) else: webbrowser.open(self.help_url) @@ -927,7 +929,7 @@ class EditorWindow(object): try: os.startfile(helpfile) except OSError as why: - tkMessageBox.showerror(title='Document Start Failure', + messagebox.showerror(title='Document Start Failure', message=str(why), parent=self.text) else: webbrowser.open(helpfile) @@ -963,7 +965,7 @@ class EditorWindow(object): except OSError as err: if not getattr(self.root, "recentfiles_message", False): self.root.recentfiles_message = True - tkMessageBox.showwarning(title='IDLE Warning', + messagebox.showwarning(title='IDLE Warning', message="Cannot save Recent Files list to disk.\n" f" {err}\n" "Select OK to continue.", @@ -1527,7 +1529,7 @@ class EditorWindow(object): else: self.line_numbers.show_sidebar() menu_label = "Hide" - self.update_menu_label(menu='options', index='*Line Numbers', + self.update_menu_label(menu='options', index='*ine*umbers', label=f'{menu_label} Line Numbers') # "line.col" -> line, as an int @@ -1546,7 +1548,7 @@ def get_line_indent(line, tabwidth): return m.end(), len(m.group().expandtabs(tabwidth)) -class IndentSearcher(object): +class IndentSearcher: # .run() chews over the Text widget, looking for a block opener # and the stmt following it. Returns a pair, diff --git a/Lib/idlelib/extend.txt b/Lib/idlelib/extend.txt index c9cb2e82..b482f76c 100644 --- a/Lib/idlelib/extend.txt +++ b/Lib/idlelib/extend.txt @@ -28,8 +28,8 @@ variables: (There are a few more, but they are rarely useful.) The extension class must not directly bind Window Manager (e.g. X) events. -Rather, it must define one or more virtual events, e.g. <>, and -corresponding methods, e.g. zoom_height_event(). The virtual events will be +Rather, it must define one or more virtual events, e.g. <>, and +corresponding methods, e.g. z_in_event(). The virtual events will be bound to the corresponding methods, and Window Manager events can then be bound to the virtual events. (This indirection is done so that the key bindings can easily be changed, and so that other sources of virtual events can exist, such @@ -54,21 +54,21 @@ Extensions are not required to define menu entries for all the events they implement. (They are also not required to create keybindings, but in that case there must be empty bindings in cofig-extensions.def) -Here is a complete example: +Here is a partial example from zzdummy.py: -class ZoomHeight: +class ZzDummy: menudefs = [ - ('edit', [ - None, # Separator - ('_Zoom Height', '<>'), - ]) + ('format', [ + ('Z in', '<>'), + ('Z out', '<>'), + ] ) ] def __init__(self, editwin): self.editwin = editwin - def zoom_height_event(self, event): + def z_in_event(self, event=None): "...Do what you want here..." The final piece of the puzzle is the file "config-extensions.def", which is diff --git a/Lib/idlelib/filelist.py b/Lib/idlelib/filelist.py index 0d200854..254f5caf 100644 --- a/Lib/idlelib/filelist.py +++ b/Lib/idlelib/filelist.py @@ -1,7 +1,7 @@ "idlelib.filelist" import os -from tkinter import messagebox as tkMessageBox +from tkinter import messagebox class FileList: @@ -20,7 +20,7 @@ class FileList: filename = self.canonize(filename) if os.path.isdir(filename): # This can happen when bad filename is passed on command line: - tkMessageBox.showerror( + messagebox.showerror( "File Error", "%r is a directory." % (filename,), master=self.root) @@ -88,7 +88,7 @@ class FileList: if newkey in self.dict: conflict = self.dict[newkey] self.inversedict[conflict] = None - tkMessageBox.showerror( + messagebox.showerror( "Name Conflict", "You now have multiple edit windows open for %r" % (filename,), master=self.root) diff --git a/Lib/idlelib/idle_test/mock_tk.py b/Lib/idlelib/idle_test/mock_tk.py index b736bd00..db583553 100644 --- a/Lib/idlelib/idle_test/mock_tk.py +++ b/Lib/idlelib/idle_test/mock_tk.py @@ -59,27 +59,26 @@ class Mbox_func: class Mbox: """Mock for tkinter.messagebox with an Mbox_func for each function. - This module was 'tkMessageBox' in 2.x; hence the 'import as' in 3.x. Example usage in test_module.py for testing functions in module.py: --- from idlelib.idle_test.mock_tk import Mbox import module -orig_mbox = module.tkMessageBox +orig_mbox = module.messagebox showerror = Mbox.showerror # example, for attribute access in test methods class Test(unittest.TestCase): @classmethod def setUpClass(cls): - module.tkMessageBox = Mbox + module.messagebox = Mbox @classmethod def tearDownClass(cls): - module.tkMessageBox = orig_mbox + module.messagebox = orig_mbox --- For 'ask' functions, set func.result return value before calling the method - that uses the message function. When tkMessageBox functions are the + that uses the message function. When messagebox functions are the only gui alls in a method, this replacement makes the method gui-free, """ askokcancel = Mbox_func() # True or False diff --git a/Lib/idlelib/idle_test/test_autocomplete.py b/Lib/idlelib/idle_test/test_autocomplete.py index 9c113bd8..642bb5db 100644 --- a/Lib/idlelib/idle_test/test_autocomplete.py +++ b/Lib/idlelib/idle_test/test_autocomplete.py @@ -195,7 +195,7 @@ class AutoCompleteTest(unittest.TestCase): self.assertFalse(acp.open_completions(ac.TAB)) self.text.delete('1.0', 'end') - class dummy_acw(): + class dummy_acw: __init__ = Func() show_window = Func(result=False) hide_window = Func() diff --git a/Lib/idlelib/idle_test/test_calltip.py b/Lib/idlelib/idle_test/test_calltip.py index a76829f3..b23915c5 100644 --- a/Lib/idlelib/idle_test/test_calltip.py +++ b/Lib/idlelib/idle_test/test_calltip.py @@ -10,7 +10,7 @@ from idlelib.idle_test.mock_tk import Text # Test Class TC is used in multiple get_argspec test methods -class TC(): +class TC: 'doc' tip = "(ai=None, *b)" def __init__(self, ai=None, *b): 'doc' @@ -268,7 +268,7 @@ class Get_entityTest(unittest.TestCase): # open_calltip is about half the code; the others are fairly trivial. # The default mocks are what are needed for open_calltip. -class mock_Shell(): +class mock_Shell: "Return mock sufficient to pass to hyperparser." def __init__(self, text): text.tag_prevrange = Mock(return_value=None) diff --git a/Lib/idlelib/idle_test/test_codecontext.py b/Lib/idlelib/idle_test/test_codecontext.py index 9578cc73..6969ad73 100644 --- a/Lib/idlelib/idle_test/test_codecontext.py +++ b/Lib/idlelib/idle_test/test_codecontext.py @@ -20,7 +20,7 @@ testcfg = { } code_sample = """\ -class C1(): +class C1: # Class comment. def __init__(self, a, b): self.a = a @@ -178,29 +178,29 @@ class CodeContextTest(unittest.TestCase): with self.assertRaises(AssertionError): gc(1, stopline=0) - eq(gc(3), ([(2, 0, 'class C1():', 'class')], 0)) + eq(gc(3), ([(2, 0, 'class C1:', 'class')], 0)) # Don't return comment. - eq(gc(4), ([(2, 0, 'class C1():', 'class')], 0)) + eq(gc(4), ([(2, 0, 'class C1:', 'class')], 0)) # Two indentation levels and no comment. - eq(gc(5), ([(2, 0, 'class C1():', 'class'), + eq(gc(5), ([(2, 0, 'class C1:', 'class'), (4, 4, ' def __init__(self, a, b):', 'def')], 0)) # Only one 'def' is returned, not both at the same indent level. - eq(gc(10), ([(2, 0, 'class C1():', 'class'), + eq(gc(10), ([(2, 0, 'class C1:', 'class'), (7, 4, ' def compare(self):', 'def'), (8, 8, ' if a > b:', 'if')], 0)) # With 'elif', also show the 'if' even though it's at the same level. - eq(gc(11), ([(2, 0, 'class C1():', 'class'), + eq(gc(11), ([(2, 0, 'class C1:', 'class'), (7, 4, ' def compare(self):', 'def'), (8, 8, ' if a > b:', 'if'), (10, 8, ' elif a < b:', 'elif')], 0)) # Set stop_line to not go back to first line in source code. # Return includes stop_line. - eq(gc(11, stopline=2), ([(2, 0, 'class C1():', 'class'), + eq(gc(11, stopline=2), ([(2, 0, 'class C1:', 'class'), (7, 4, ' def compare(self):', 'def'), (8, 8, ' if a > b:', 'if'), (10, 8, ' elif a < b:', 'elif')], 0)) @@ -240,37 +240,37 @@ class CodeContextTest(unittest.TestCase): # Scroll down to line 2. cc.text.yview(2) cc.update_code_context() - eq(cc.info, [(0, -1, '', False), (2, 0, 'class C1():', 'class')]) + eq(cc.info, [(0, -1, '', False), (2, 0, 'class C1:', 'class')]) eq(cc.topvisible, 3) - eq(cc.context.get('1.0', 'end-1c'), 'class C1():') + eq(cc.context.get('1.0', 'end-1c'), 'class C1:') # Scroll down to line 3. Since it's a comment, nothing changes. cc.text.yview(3) cc.update_code_context() - eq(cc.info, [(0, -1, '', False), (2, 0, 'class C1():', 'class')]) + eq(cc.info, [(0, -1, '', False), (2, 0, 'class C1:', 'class')]) eq(cc.topvisible, 4) - eq(cc.context.get('1.0', 'end-1c'), 'class C1():') + eq(cc.context.get('1.0', 'end-1c'), 'class C1:') # Scroll down to line 4. cc.text.yview(4) cc.update_code_context() eq(cc.info, [(0, -1, '', False), - (2, 0, 'class C1():', 'class'), + (2, 0, 'class C1:', 'class'), (4, 4, ' def __init__(self, a, b):', 'def')]) eq(cc.topvisible, 5) - eq(cc.context.get('1.0', 'end-1c'), 'class C1():\n' + eq(cc.context.get('1.0', 'end-1c'), 'class C1:\n' ' def __init__(self, a, b):') # Scroll down to line 11. Last 'def' is removed. cc.text.yview(11) cc.update_code_context() eq(cc.info, [(0, -1, '', False), - (2, 0, 'class C1():', 'class'), + (2, 0, 'class C1:', 'class'), (7, 4, ' def compare(self):', 'def'), (8, 8, ' if a > b:', 'if'), (10, 8, ' elif a < b:', 'elif')]) eq(cc.topvisible, 12) - eq(cc.context.get('1.0', 'end-1c'), 'class C1():\n' + eq(cc.context.get('1.0', 'end-1c'), 'class C1:\n' ' def compare(self):\n' ' if a > b:\n' ' elif a < b:') @@ -279,12 +279,12 @@ class CodeContextTest(unittest.TestCase): cc.update_code_context() cc.context_depth = 1 eq(cc.info, [(0, -1, '', False), - (2, 0, 'class C1():', 'class'), + (2, 0, 'class C1:', 'class'), (7, 4, ' def compare(self):', 'def'), (8, 8, ' if a > b:', 'if'), (10, 8, ' elif a < b:', 'elif')]) eq(cc.topvisible, 12) - eq(cc.context.get('1.0', 'end-1c'), 'class C1():\n' + eq(cc.context.get('1.0', 'end-1c'), 'class C1:\n' ' def compare(self):\n' ' if a > b:\n' ' elif a < b:') @@ -293,7 +293,7 @@ class CodeContextTest(unittest.TestCase): cc.text.yview(5) cc.update_code_context() eq(cc.info, [(0, -1, '', False), - (2, 0, 'class C1():', 'class'), + (2, 0, 'class C1:', 'class'), (4, 4, ' def __init__(self, a, b):', 'def')]) eq(cc.topvisible, 6) # context_depth is 1. @@ -440,7 +440,7 @@ class HelperFunctionText(unittest.TestCase): # Line 1 is not a BLOCKOPENER. eq(gli(lines[0]), (codecontext.INFINITY, '', False)) # Line 2 is a BLOCKOPENER without an indent. - eq(gli(lines[1]), (0, 'class C1():', 'class')) + eq(gli(lines[1]), (0, 'class C1:', 'class')) # Line 3 is not a BLOCKOPENER and does not return the indent level. eq(gli(lines[2]), (codecontext.INFINITY, ' # Class comment.', False)) # Line 4 is a BLOCKOPENER and is indented. diff --git a/Lib/idlelib/idle_test/test_configdialog.py b/Lib/idlelib/idle_test/test_configdialog.py index 1fea6d41..98ddc67a 100644 --- a/Lib/idlelib/idle_test/test_configdialog.py +++ b/Lib/idlelib/idle_test/test_configdialog.py @@ -423,7 +423,7 @@ class HighPageTest(unittest.TestCase): def test_color(self): d = self.page d.on_new_color_set = Func() - # self.color is only set in get_color through ColorChooser. + # self.color is only set in get_color through colorchooser. d.color.set('green') self.assertEqual(d.on_new_color_set.called, 1) del d.on_new_color_set @@ -540,8 +540,8 @@ class HighPageTest(unittest.TestCase): def test_get_color(self): eq = self.assertEqual d = self.page - orig_chooser = configdialog.tkColorChooser.askcolor - chooser = configdialog.tkColorChooser.askcolor = Func() + orig_chooser = configdialog.colorchooser.askcolor + chooser = configdialog.colorchooser.askcolor = Func() gntn = d.get_new_theme_name = Func() d.highlight_target.set('Editor Breakpoint') @@ -582,7 +582,7 @@ class HighPageTest(unittest.TestCase): eq(d.color.get(), '#de0000') del d.get_new_theme_name - configdialog.tkColorChooser.askcolor = orig_chooser + configdialog.colorchooser.askcolor = orig_chooser def test_on_new_color_set(self): d = self.page diff --git a/Lib/idlelib/idle_test/test_debugger_r.py b/Lib/idlelib/idle_test/test_debugger_r.py index 199f6344..638ebd36 100644 --- a/Lib/idlelib/idle_test/test_debugger_r.py +++ b/Lib/idlelib/idle_test/test_debugger_r.py @@ -25,5 +25,19 @@ class Test(unittest.TestCase): # Classes GUIProxy, IdbAdapter, FrameProxy, CodeProxy, DictProxy, # GUIAdapter, IdbProxy plus 7 module functions. +class IdbAdapterTest(unittest.TestCase): + + def test_dict_item_noattr(self): # Issue 33065. + + class BinData: + def __repr__(self): + return self.length + + debugger_r.dicttable[0] = {'BinData': BinData()} + idb = debugger_r.IdbAdapter(None) + self.assertTrue(idb.dict_item(0, 'BinData')) + debugger_r.dicttable.clear() + + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/Lib/idlelib/idle_test/test_format.py b/Lib/idlelib/idle_test/test_format.py index a79bb515..e5e90368 100644 --- a/Lib/idlelib/idle_test/test_format.py +++ b/Lib/idlelib/idle_test/test_format.py @@ -418,7 +418,7 @@ class FormatRegionTest(unittest.TestCase): code_sample = """\ # WS line needed for test. -class C1(): +class C1: # Class comment. def __init__(self, a, b): self.a = a diff --git a/Lib/idlelib/idle_test/test_help_about.py b/Lib/idlelib/idle_test/test_help_about.py index 7c148d23..b915535a 100644 --- a/Lib/idlelib/idle_test/test_help_about.py +++ b/Lib/idlelib/idle_test/test_help_about.py @@ -134,7 +134,7 @@ class CloseTest(unittest.TestCase): self.dialog.winfo_class() -class Dummy_about_dialog(): +class Dummy_about_dialog: # Dummy class for testing file display functions. idle_credits = About.show_idle_credits idle_readme = About.show_readme diff --git a/Lib/idlelib/idle_test/test_mainmenu.py b/Lib/idlelib/idle_test/test_mainmenu.py index 7ec03683..51d2accf 100644 --- a/Lib/idlelib/idle_test/test_mainmenu.py +++ b/Lib/idlelib/idle_test/test_mainmenu.py @@ -2,6 +2,7 @@ # Reported as 88%; mocking turtledemo absence would have no point. from idlelib import mainmenu +import re import unittest @@ -16,6 +17,26 @@ class MainMenuTest(unittest.TestCase): def test_default_keydefs(self): self.assertGreaterEqual(len(mainmenu.default_keydefs), 50) + def test_tcl_indexes(self): + # Test tcl patterns used to find menuitem to alter. + # On failure, change pattern here and in function(s). + # Patterns here have '.*' for re instead of '*' for tcl. + for menu, pattern in ( + ('debug', '.*tack.*iewer'), # PyShell.debug_menu_postcommand + ('options', '.*ode.*ontext'), # EW.__init__, CodeContext.toggle... + ('options', '.*ine.*umbers'), # EW.__init__, EW.toggle...event. + ): + with self.subTest(menu=menu, pattern=pattern): + for menutup in mainmenu.menudefs: + if menutup[0] == menu: + break + else: + self.assertTrue(0, f"{menu} not in menudefs") + self.assertTrue(any(re.search(pattern, menuitem[0]) + for menuitem in menutup[1] + if menuitem is not None), # Separator. + f"{pattern} not in {menu}") + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/Lib/idlelib/idle_test/test_pyparse.py b/Lib/idlelib/idle_test/test_pyparse.py index f21baf75..fb5726db 100644 --- a/Lib/idlelib/idle_test/test_pyparse.py +++ b/Lib/idlelib/idle_test/test_pyparse.py @@ -73,11 +73,12 @@ class PyParseTest(unittest.TestCase): # Split def across lines. setcode('"""This is a module docstring"""\n' - 'class C():\n' + 'class C:\n' ' def __init__(self, a,\n' ' b=True):\n' ' pass\n' ) + pos0, pos = 33, 42 # Start of 'class...', ' def' lines. # Passing no value or non-callable should fail (issue 32989). with self.assertRaises(TypeError): @@ -91,40 +92,41 @@ class PyParseTest(unittest.TestCase): # Make all text look like it's not in a string. This means that it # found a good start position. - eq(start(char_in_string_false), 44) + eq(start(char_in_string_false), pos) # If the beginning of the def line is not in a string, then it # returns that as the index. - eq(start(is_char_in_string=lambda index: index > 44), 44) + eq(start(is_char_in_string=lambda index: index > pos), pos) # If the beginning of the def line is in a string, then it # looks for a previous index. - eq(start(is_char_in_string=lambda index: index >= 44), 33) + eq(start(is_char_in_string=lambda index: index >= pos), pos0) # If everything before the 'def' is in a string, then returns None. # The non-continuation def line returns 44 (see below). - eq(start(is_char_in_string=lambda index: index < 44), None) + eq(start(is_char_in_string=lambda index: index < pos), None) # Code without extra line break in def line - mostly returns the same # values. setcode('"""This is a module docstring"""\n' - 'class C():\n' + 'class C:\n' ' def __init__(self, a, b=True):\n' ' pass\n' - ) - eq(start(char_in_string_false), 44) - eq(start(is_char_in_string=lambda index: index > 44), 44) - eq(start(is_char_in_string=lambda index: index >= 44), 33) + ) # Does not affect class, def positions. + eq(start(char_in_string_false), pos) + eq(start(is_char_in_string=lambda index: index > pos), pos) + eq(start(is_char_in_string=lambda index: index >= pos), pos0) # When the def line isn't split, this returns which doesn't match the # split line test. - eq(start(is_char_in_string=lambda index: index < 44), 44) + eq(start(is_char_in_string=lambda index: index < pos), pos) def test_set_lo(self): code = ( '"""This is a module docstring"""\n' - 'class C():\n' + 'class C:\n' ' def __init__(self, a,\n' ' b=True):\n' ' pass\n' ) + pos = 42 p = self.parser p.set_code(code) @@ -137,8 +139,8 @@ class PyParseTest(unittest.TestCase): self.assertEqual(p.code, code) # An index that is preceded by a newline. - p.set_lo(44) - self.assertEqual(p.code, code[44:]) + p.set_lo(pos) + self.assertEqual(p.code, code[pos:]) def test_study1(self): eq = self.assertEqual diff --git a/Lib/idlelib/idle_test/test_replace.py b/Lib/idlelib/idle_test/test_replace.py index c3c5d2ee..6c07389b 100644 --- a/Lib/idlelib/idle_test/test_replace.py +++ b/Lib/idlelib/idle_test/test_replace.py @@ -10,7 +10,7 @@ from unittest.mock import Mock from idlelib.idle_test.mock_tk import Mbox import idlelib.searchengine as se -orig_mbox = se.tkMessageBox +orig_mbox = se.messagebox showerror = Mbox.showerror @@ -20,7 +20,7 @@ class ReplaceDialogTest(unittest.TestCase): def setUpClass(cls): cls.root = Tk() cls.root.withdraw() - se.tkMessageBox = Mbox + se.messagebox = Mbox cls.engine = se.SearchEngine(cls.root) cls.dialog = ReplaceDialog(cls.root, cls.engine) cls.dialog.bell = lambda: None @@ -32,7 +32,7 @@ class ReplaceDialogTest(unittest.TestCase): @classmethod def tearDownClass(cls): - se.tkMessageBox = orig_mbox + se.messagebox = orig_mbox del cls.text, cls.dialog, cls.engine cls.root.destroy() del cls.root diff --git a/Lib/idlelib/idle_test/test_run.py b/Lib/idlelib/idle_test/test_run.py index 37c0d452..a31671ee 100644 --- a/Lib/idlelib/idle_test/test_run.py +++ b/Lib/idlelib/idle_test/test_run.py @@ -1,16 +1,18 @@ "Test run, coverage 49%." from idlelib import run +import io +import sys +from test.support import captured_output, captured_stderr import unittest from unittest import mock +import idlelib from idlelib.idle_test.mock_idle import Func -from test.support import captured_output, captured_stderr -import io -import sys +idlelib.testing = True # Use {} for executing test user code. -class RunTest(unittest.TestCase): +class PrintExceptionTest(unittest.TestCase): def test_print_exception_unhashable(self): class UnhashableException(Exception): @@ -351,5 +353,38 @@ class HandleErrorTest(unittest.TestCase): self.assertIn('IndexError', msg) eq(func.called, 2) + +class ExecRuncodeTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls.addClassCleanup(setattr,run,'print_exception',run.print_exception) + cls.prt = Func() # Need reference. + run.print_exception = cls.prt + mockrpc = mock.Mock() + mockrpc.console.getvar = Func(result=False) + cls.ex = run.Executive(mockrpc) + + @classmethod + def tearDownClass(cls): + assert sys.excepthook == sys.__excepthook__ + + def test_exceptions(self): + ex = self.ex + ex.runcode('1/0') + self.assertIs(ex.user_exc_info[0], ZeroDivisionError) + + self.addCleanup(setattr, sys, 'excepthook', sys.__excepthook__) + sys.excepthook = lambda t, e, tb: run.print_exception(t) + ex.runcode('1/0') + self.assertIs(self.prt.args[0], ZeroDivisionError) + + sys.excepthook = lambda: None + ex.runcode('1/0') + t, e, tb = ex.user_exc_info + self.assertIs(t, TypeError) + self.assertTrue(isinstance(e.__context__, ZeroDivisionError)) + + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/Lib/idlelib/idle_test/test_searchengine.py b/Lib/idlelib/idle_test/test_searchengine.py index f8401ce9..9d979839 100644 --- a/Lib/idlelib/idle_test/test_searchengine.py +++ b/Lib/idlelib/idle_test/test_searchengine.py @@ -4,7 +4,7 @@ from idlelib import searchengine as se import unittest # from test.support import requires from tkinter import BooleanVar, StringVar, TclError # ,Tk, Text -import tkinter.messagebox as tkMessageBox +from tkinter import messagebox from idlelib.idle_test.mock_tk import Var, Mbox from idlelib.idle_test.mock_tk import Text as mockText import re @@ -19,13 +19,13 @@ def setUpModule(): # Replace s-e module tkinter imports other than non-gui TclError. se.BooleanVar = Var se.StringVar = Var - se.tkMessageBox = Mbox + se.messagebox = Mbox def tearDownModule(): # Restore 'just in case', though other tests should also replace. se.BooleanVar = BooleanVar se.StringVar = StringVar - se.tkMessageBox = tkMessageBox + se.messagebox = messagebox class Mock: diff --git a/Lib/idlelib/idle_test/test_squeezer.py b/Lib/idlelib/idle_test/test_squeezer.py index e3912f4b..ee1bbd76 100644 --- a/Lib/idlelib/idle_test/test_squeezer.py +++ b/Lib/idlelib/idle_test/test_squeezer.py @@ -396,7 +396,7 @@ class ExpandingButtonTest(unittest.TestCase): expandingbutton.base_text = expandingbutton.text # Patch the message box module to always return False. - with patch('idlelib.squeezer.tkMessageBox') as mock_msgbox: + with patch('idlelib.squeezer.messagebox') as mock_msgbox: mock_msgbox.askokcancel.return_value = False mock_msgbox.askyesno.return_value = False # Trigger the expand event. @@ -407,7 +407,7 @@ class ExpandingButtonTest(unittest.TestCase): self.assertEqual(expandingbutton.text.get('1.0', 'end-1c'), '') # Patch the message box module to always return True. - with patch('idlelib.squeezer.tkMessageBox') as mock_msgbox: + with patch('idlelib.squeezer.messagebox') as mock_msgbox: mock_msgbox.askokcancel.return_value = True mock_msgbox.askyesno.return_value = True # Trigger the expand event. diff --git a/Lib/idlelib/idle_test/test_zzdummy.py b/Lib/idlelib/idle_test/test_zzdummy.py new file mode 100644 index 00000000..1013cdc3 --- /dev/null +++ b/Lib/idlelib/idle_test/test_zzdummy.py @@ -0,0 +1,152 @@ +"Test zzdummy, coverage 100%." + +from idlelib import zzdummy +import unittest +from test.support import requires +from tkinter import Tk, Text +from unittest import mock +from idlelib import config +from idlelib import editor +from idlelib import format + + +usercfg = zzdummy.idleConf.userCfg +testcfg = { + 'main': config.IdleUserConfParser(''), + 'highlight': config.IdleUserConfParser(''), + 'keys': config.IdleUserConfParser(''), + 'extensions': config.IdleUserConfParser(''), +} +code_sample = """\ + +class C1(): + # Class comment. + def __init__(self, a, b): + self.a = a + self.b = b +""" + + +class DummyEditwin: + get_selection_indices = editor.EditorWindow.get_selection_indices + def __init__(self, root, text): + self.root = root + self.top = root + self.text = text + self.fregion = format.FormatRegion(self) + self.text.undo_block_start = mock.Mock() + self.text.undo_block_stop = mock.Mock() + + +class ZZDummyTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + requires('gui') + root = cls.root = Tk() + root.withdraw() + text = cls.text = Text(cls.root) + cls.editor = DummyEditwin(root, text) + zzdummy.idleConf.userCfg = testcfg + + @classmethod + def tearDownClass(cls): + zzdummy.idleConf.userCfg = usercfg + del cls.editor, cls.text + cls.root.update_idletasks() + for id in cls.root.tk.call('after', 'info'): + cls.root.after_cancel(id) # Need for EditorWindow. + cls.root.destroy() + del cls.root + + def setUp(self): + text = self.text + text.insert('1.0', code_sample) + text.undo_block_start.reset_mock() + text.undo_block_stop.reset_mock() + zz = self.zz = zzdummy.ZzDummy(self.editor) + zzdummy.ZzDummy.ztext = '# ignore #' + + def tearDown(self): + self.text.delete('1.0', 'end') + del self.zz + + def checklines(self, text, value): + # Verify that there are lines being checked. + end_line = int(float(text.index('end'))) + + # Check each line for the starting text. + actual = [] + for line in range(1, end_line): + txt = text.get(f'{line}.0', f'{line}.end') + actual.append(txt.startswith(value)) + return actual + + def test_init(self): + zz = self.zz + self.assertEqual(zz.editwin, self.editor) + self.assertEqual(zz.text, self.editor.text) + + def test_reload(self): + self.assertEqual(self.zz.ztext, '# ignore #') + testcfg['extensions'].SetOption('ZzDummy', 'z-text', 'spam') + zzdummy.ZzDummy.reload() + self.assertEqual(self.zz.ztext, 'spam') + + def test_z_in_event(self): + eq = self.assertEqual + zz = self.zz + text = zz.text + eq(self.zz.ztext, '# ignore #') + + # No lines have the leading text. + expected = [False, False, False, False, False, False, False] + actual = self.checklines(text, zz.ztext) + eq(expected, actual) + + text.tag_add('sel', '2.0', '4.end') + eq(zz.z_in_event(), 'break') + expected = [False, True, True, True, False, False, False] + actual = self.checklines(text, zz.ztext) + eq(expected, actual) + + text.undo_block_start.assert_called_once() + text.undo_block_stop.assert_called_once() + + def test_z_out_event(self): + eq = self.assertEqual + zz = self.zz + text = zz.text + eq(self.zz.ztext, '# ignore #') + + # Prepend text. + text.tag_add('sel', '2.0', '5.end') + zz.z_in_event() + text.undo_block_start.reset_mock() + text.undo_block_stop.reset_mock() + + # Select a few lines to remove text. + text.tag_remove('sel', '1.0', 'end') + text.tag_add('sel', '3.0', '4.end') + eq(zz.z_out_event(), 'break') + expected = [False, True, False, False, True, False, False] + actual = self.checklines(text, zz.ztext) + eq(expected, actual) + + text.undo_block_start.assert_called_once() + text.undo_block_stop.assert_called_once() + + def test_roundtrip(self): + # Insert and remove to all code should give back original text. + zz = self.zz + text = zz.text + + text.tag_add('sel', '1.0', 'end-1c') + zz.z_in_event() + zz.z_out_event() + + self.assertEqual(text.get('1.0', 'end-1c'), code_sample) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/Lib/idlelib/iomenu.py b/Lib/idlelib/iomenu.py index 8bb2fa6a..5ebf7089 100644 --- a/Lib/idlelib/iomenu.py +++ b/Lib/idlelib/iomenu.py @@ -5,8 +5,8 @@ import sys import tempfile import tokenize -import tkinter.filedialog as tkFileDialog -import tkinter.messagebox as tkMessageBox +from tkinter import filedialog +from tkinter import messagebox from tkinter.simpledialog import askstring import idlelib @@ -147,10 +147,10 @@ class IOBinding: eol_convention = f.newlines converted = True except OSError as err: - tkMessageBox.showerror("I/O Error", str(err), parent=self.text) + messagebox.showerror("I/O Error", str(err), parent=self.text) return False except UnicodeDecodeError: - tkMessageBox.showerror("Decoding Error", + messagebox.showerror("Decoding Error", "File %s\nFailed to Decode" % filename, parent=self.text) return False @@ -159,7 +159,7 @@ class IOBinding: # If the file does not contain line separators, it is None. # If the file contains mixed line separators, it is a tuple. if eol_convention is not None: - tkMessageBox.showwarning("Mixed Newlines", + messagebox.showwarning("Mixed Newlines", "Mixed newlines detected.\n" "The file will be changed on save.", parent=self.text) @@ -187,10 +187,10 @@ class IOBinding: return "yes" message = "Do you want to save %s before closing?" % ( self.filename or "this untitled document") - confirm = tkMessageBox.askyesnocancel( + confirm = messagebox.askyesnocancel( title="Save On Close", message=message, - default=tkMessageBox.YES, + default=messagebox.YES, parent=self.text) if confirm: reply = "yes" @@ -249,7 +249,7 @@ class IOBinding: os.fsync(f.fileno()) return True except OSError as msg: - tkMessageBox.showerror("I/O Error", str(msg), + messagebox.showerror("I/O Error", str(msg), parent=self.text) return False @@ -286,7 +286,7 @@ class IOBinding: failed = str(err) except UnicodeEncodeError: failed = "Invalid encoding '%s'" % enc - tkMessageBox.showerror( + messagebox.showerror( "I/O Error", "%s.\nSaving as UTF-8" % failed, parent=self.text) @@ -295,10 +295,10 @@ class IOBinding: return chars.encode('utf-8-sig') def print_window(self, event): - confirm = tkMessageBox.askokcancel( + confirm = messagebox.askokcancel( title="Print", message="Print to Default Printer", - default=tkMessageBox.OK, + default=messagebox.OK, parent=self.text) if not confirm: self.text.focus_set() @@ -336,10 +336,10 @@ class IOBinding: status + output if output: output = "Printing command: %s\n" % repr(command) + output - tkMessageBox.showerror("Print status", output, parent=self.text) + messagebox.showerror("Print status", output, parent=self.text) else: #no printing for this platform message = "Printing is not enabled for this platform: %s" % platform - tkMessageBox.showinfo("Print status", message, parent=self.text) + messagebox.showinfo("Print status", message, parent=self.text) if tempfilename: os.unlink(tempfilename) return "break" @@ -358,7 +358,7 @@ class IOBinding: def askopenfile(self): dir, base = self.defaultfilename("open") if not self.opendialog: - self.opendialog = tkFileDialog.Open(parent=self.text, + self.opendialog = filedialog.Open(parent=self.text, filetypes=self.filetypes) filename = self.opendialog.show(initialdir=dir, initialfile=base) return filename @@ -378,7 +378,7 @@ class IOBinding: def asksavefile(self): dir, base = self.defaultfilename("save") if not self.savedialog: - self.savedialog = tkFileDialog.SaveAs( + self.savedialog = filedialog.SaveAs( parent=self.text, filetypes=self.filetypes, defaultextension=self.defaultextension) diff --git a/Lib/idlelib/pyshell.py b/Lib/idlelib/pyshell.py index adc30288..fea37624 100755 --- a/Lib/idlelib/pyshell.py +++ b/Lib/idlelib/pyshell.py @@ -21,13 +21,13 @@ if sys.platform == 'win32': except (ImportError, AttributeError, OSError): pass -import tkinter.messagebox as tkMessageBox +from tkinter import messagebox if TkVersion < 8.5: root = Tk() # otherwise create root in main root.withdraw() from idlelib.run import fix_scaling fix_scaling(root) - tkMessageBox.showerror("Idle Cannot Start", + messagebox.showerror("Idle Cannot Start", "Idle requires tcl/tk 8.5+, not %s." % TkVersion, parent=root) raise SystemExit(1) @@ -261,7 +261,7 @@ class PyShellEditorWindow(EditorWindow): except OSError as err: if not getattr(self.root, "breakpoint_error_displayed", False): self.root.breakpoint_error_displayed = True - tkMessageBox.showerror(title='IDLE Error', + messagebox.showerror(title='IDLE Error', message='Unable to update breakpoint list:\n%s' % str(err), parent=self.text) @@ -771,7 +771,7 @@ class ModifiedInterpreter(InteractiveInterpreter): exec(code, self.locals) except SystemExit: if not self.tkconsole.closing: - if tkMessageBox.askyesno( + if messagebox.askyesno( "Exit?", "Do you want to exit altogether?", default="yes", @@ -805,7 +805,7 @@ class ModifiedInterpreter(InteractiveInterpreter): return self.tkconsole.stderr.write(s) def display_port_binding_error(self): - tkMessageBox.showerror( + messagebox.showerror( "Port Binding Error", "IDLE can't bind to a TCP/IP port, which is necessary to " "communicate with its Python execution server. This might be " @@ -816,7 +816,7 @@ class ModifiedInterpreter(InteractiveInterpreter): parent=self.tkconsole.text) def display_no_subprocess_error(self): - tkMessageBox.showerror( + messagebox.showerror( "Subprocess Connection Error", "IDLE's subprocess didn't make connection.\n" "See the 'Startup failure' section of the IDLE doc, online at\n" @@ -824,7 +824,7 @@ class ModifiedInterpreter(InteractiveInterpreter): parent=self.tkconsole.text) def display_executing_dialog(self): - tkMessageBox.showerror( + messagebox.showerror( "Already executing", "The Python Shell window is already executing a command; " "please wait until it is finished.", @@ -945,7 +945,7 @@ class PyShell(OutputWindow): def toggle_debugger(self, event=None): if self.executing: - tkMessageBox.showerror("Don't debug now", + messagebox.showerror("Don't debug now", "You can only toggle the debugger when idle", parent=self.text) self.set_debugger_indicator() @@ -989,6 +989,10 @@ class PyShell(OutputWindow): self.showprompt() self.set_debugger_indicator() + def debug_menu_postcommand(self): + state = 'disabled' if self.executing else 'normal' + self.update_menu_state('debug', '*tack*iewer', state) + def beginexecuting(self): "Helper for ModifiedInterpreter" self.resetoutput() @@ -1003,7 +1007,7 @@ class PyShell(OutputWindow): def close(self): "Extend EditorWindow.close()" if self.executing: - response = tkMessageBox.askokcancel( + response = messagebox.askokcancel( "Kill?", "Your program is still running!\n Do you want to kill it?", default="ok", @@ -1061,8 +1065,10 @@ class PyShell(OutputWindow): (sys.version, sys.platform, self.COPYRIGHT, nosub)) self.text.focus_force() self.showprompt() + # User code should use separate default Tk root window import tkinter - tkinter._default_root = None # 03Jan04 KBK What's this? + tkinter._support_default_root = True + tkinter._default_root = None return True def stop_readline(self): @@ -1252,7 +1258,7 @@ class PyShell(OutputWindow): try: sys.last_traceback except: - tkMessageBox.showerror("No stack trace", + messagebox.showerror("No stack trace", "There is no stack trace yet.\n" "(sys.last_traceback is not defined)", parent=self.text) diff --git a/Lib/idlelib/rpc.py b/Lib/idlelib/rpc.py index aa8cbd36..8efcf048 100644 --- a/Lib/idlelib/rpc.py +++ b/Lib/idlelib/rpc.py @@ -125,7 +125,7 @@ request_queue = queue.Queue(0) response_queue = queue.Queue(0) -class SocketIO(object): +class SocketIO: nextseq = 0 @@ -486,7 +486,7 @@ class SocketIO(object): #----------------- end class SocketIO -------------------- -class RemoteObject(object): +class RemoteObject: # Token mix-in class pass @@ -497,7 +497,7 @@ def remoteref(obj): return RemoteProxy(oid) -class RemoteProxy(object): +class RemoteProxy: def __init__(self, oid): self.oid = oid @@ -547,7 +547,7 @@ class RPCClient(SocketIO): return RPCProxy(self, oid) -class RPCProxy(object): +class RPCProxy: __methods = None __attributes = None @@ -596,7 +596,7 @@ def _getattributes(obj, attributes): attributes[name] = 1 -class MethodProxy(object): +class MethodProxy: def __init__(self, sockio, oid, name): self.sockio = sockio diff --git a/Lib/idlelib/run.py b/Lib/idlelib/run.py index 1e84ecc6..07e9a2bf 100644 --- a/Lib/idlelib/run.py +++ b/Lib/idlelib/run.py @@ -16,6 +16,7 @@ import _thread as thread import threading import warnings +import idlelib # testing from idlelib import autocomplete # AutoComplete, fetch_encodings from idlelib import calltip # Calltip from idlelib import debugger_r # start_debugger @@ -538,18 +539,21 @@ class MyHandler(rpc.RPCHandler): thread.interrupt_main() -class Executive(object): +class Executive: def __init__(self, rpchandler): self.rpchandler = rpchandler - self.locals = __main__.__dict__ - self.calltip = calltip.Calltip() - self.autocomplete = autocomplete.AutoComplete() + if idlelib.testing is False: + self.locals = __main__.__dict__ + self.calltip = calltip.Calltip() + self.autocomplete = autocomplete.AutoComplete() + else: + self.locals = {} def runcode(self, code): global interruptable try: - self.usr_exc_info = None + self.user_exc_info = None interruptable = True try: exec(code, self.locals) @@ -562,10 +566,17 @@ class Executive(object): print('SystemExit: ' + str(ob), file=sys.stderr) # Return to the interactive prompt. except: - self.usr_exc_info = sys.exc_info() + self.user_exc_info = sys.exc_info() # For testing, hook, viewer. if quitting: exit() - print_exception() + if sys.excepthook is sys.__excepthook__: + print_exception() + else: + try: + sys.excepthook(*self.user_exc_info) + except: + self.user_exc_info = sys.exc_info() # For testing. + print_exception() jit = self.rpchandler.console.getvar("<>") if jit: self.rpchandler.interp.open_remote_stack_viewer() @@ -590,8 +601,8 @@ class Executive(object): return self.autocomplete.fetch_completions(what, mode) def stackviewer(self, flist_oid=None): - if self.usr_exc_info: - typ, val, tb = self.usr_exc_info + if self.user_exc_info: + typ, val, tb = self.user_exc_info else: return None flist = None diff --git a/Lib/idlelib/runscript.py b/Lib/idlelib/runscript.py index 028b0dbd..55712e90 100644 --- a/Lib/idlelib/runscript.py +++ b/Lib/idlelib/runscript.py @@ -14,7 +14,7 @@ import tabnanny import time import tokenize -import tkinter.messagebox as tkMessageBox +from tkinter import messagebox from idlelib.config import idleConf from idlelib import macosx @@ -195,15 +195,15 @@ class ScriptBinding: def ask_save_dialog(self): msg = "Source Must Be Saved\n" + 5*' ' + "OK to Save?" - confirm = tkMessageBox.askokcancel(title="Save Before Run or Check", + confirm = messagebox.askokcancel(title="Save Before Run or Check", message=msg, - default=tkMessageBox.OK, + default=messagebox.OK, parent=self.editwin.text) return confirm def errorbox(self, title, message): # XXX This should really be a function of EditorWindow... - tkMessageBox.showerror(title, message, parent=self.editwin.text) + messagebox.showerror(title, message, parent=self.editwin.text) self.editwin.text.focus_set() self.perf = time.perf_counter() diff --git a/Lib/idlelib/searchengine.py b/Lib/idlelib/searchengine.py index a50038e2..eddef581 100644 --- a/Lib/idlelib/searchengine.py +++ b/Lib/idlelib/searchengine.py @@ -2,7 +2,7 @@ import re from tkinter import StringVar, BooleanVar, TclError -import tkinter.messagebox as tkMessageBox +from tkinter import messagebox def get(root): '''Return the singleton SearchEngine instance for the process. @@ -96,7 +96,7 @@ class SearchEngine: msg = msg + "\nPattern: " + str(pat) if col is not None: msg = msg + "\nOffset: " + str(col) - tkMessageBox.showerror("Regular expression error", + messagebox.showerror("Regular expression error", msg, master=self.root) def search_text(self, text, prog=None, ok=0): diff --git a/Lib/idlelib/squeezer.py b/Lib/idlelib/squeezer.py index be1538a2..3046d803 100644 --- a/Lib/idlelib/squeezer.py +++ b/Lib/idlelib/squeezer.py @@ -17,7 +17,7 @@ messages and their tracebacks. import re import tkinter as tk -import tkinter.messagebox as tkMessageBox +from tkinter import messagebox from idlelib.config import idleConf from idlelib.textview import view_text @@ -147,7 +147,7 @@ class ExpandingButton(tk.Button): if self.is_dangerous is None: self.set_is_dangerous() if self.is_dangerous: - confirm = tkMessageBox.askokcancel( + confirm = messagebox.askokcancel( title="Expand huge output?", message="\n\n".join([ "The squeezed output is very long: %d lines, %d chars.", @@ -155,7 +155,7 @@ class ExpandingButton(tk.Button): "It is recommended to view or copy the output instead.", "Really expand?" ]) % (self.numoflines, len(self.s)), - default=tkMessageBox.CANCEL, + default=messagebox.CANCEL, parent=self.text) if not confirm: return "break" diff --git a/Lib/idlelib/tooltip.py b/Lib/idlelib/tooltip.py index 69658264..d714318d 100644 --- a/Lib/idlelib/tooltip.py +++ b/Lib/idlelib/tooltip.py @@ -7,7 +7,7 @@ This includes: from tkinter import * -class TooltipBase(object): +class TooltipBase: """abstract base class for tooltips""" def __init__(self, anchor_widget): diff --git a/Lib/idlelib/zzdummy.py b/Lib/idlelib/zzdummy.py index 3c4b1d23..1247e8f1 100644 --- a/Lib/idlelib/zzdummy.py +++ b/Lib/idlelib/zzdummy.py @@ -1,42 +1,73 @@ -"Example extension, also used for testing." +"""Example extension, also used for testing. + +See extend.txt for more details on creating an extension. +See config-extension.def for configuring an extension. +""" from idlelib.config import idleConf +from functools import wraps + + +def format_selection(format_line): + "Apply a formatting function to all of the selected lines." + + @wraps(format_line) + def apply(self, event=None): + head, tail, chars, lines = self.formatter.get_region() + for pos in range(len(lines) - 1): + line = lines[pos] + lines[pos] = format_line(self, line) + self.formatter.set_region(head, tail, chars, lines) + return 'break' -ztext = idleConf.GetOption('extensions', 'ZzDummy', 'z-text') + return apply class ZzDummy: + """Prepend or remove initial text from selected lines.""" -## menudefs = [ -## ('format', [ -## ('Z in', '<>'), -## ('Z out', '<>'), -## ] ) -## ] + # Extend the format menu. + menudefs = [ + ('format', [ + ('Z in', '<>'), + ('Z out', '<>'), + ] ) + ] def __init__(self, editwin): + "Initialize the settings for this extension." + self.editwin = editwin self.text = editwin.text - z_in = False + self.formatter = editwin.fregion @classmethod def reload(cls): + "Load class variables from config." cls.ztext = idleConf.GetOption('extensions', 'ZzDummy', 'z-text') - def z_in_event(self, event): + @format_selection + def z_in_event(self, line): + """Insert text at the beginning of each selected line. + + This is bound to the <> virtual event when the extensions + are loaded. """ + return f'{self.ztext}{line}' + + @format_selection + def z_out_event(self, line): + """Remove specific text from the beginning of each selected line. + + This is bound to the <> virtual event when the extensions + are loaded. """ - text = self.text - text.undo_block_start() - for line in range(1, text.index('end')): - text.insert('%d.0', ztext) - text.undo_block_stop() - return "break" + zlength = 0 if not line.startswith(self.ztext) else len(self.ztext) + return line[zlength:] - def z_out_event(self, event): pass ZzDummy.reload() -##if __name__ == "__main__": -## import unittest -## unittest.main('idlelib.idle_test.test_zzdummy', -## verbosity=2, exit=False) + +if __name__ == "__main__": + import unittest + unittest.main('idlelib.idle_test.test_zzdummy', verbosity=2, exit=False) diff --git a/Lib/lib2to3/Grammar.txt b/Lib/lib2to3/Grammar.txt index e007dc18..fa7b1506 100644 --- a/Lib/lib2to3/Grammar.txt +++ b/Lib/lib2to3/Grammar.txt @@ -18,15 +18,55 @@ decorated: decorators (classdef | funcdef | async_funcdef) async_funcdef: ASYNC funcdef funcdef: 'def' NAME parameters ['->' test] ':' suite parameters: '(' [typedargslist] ')' -typedargslist: ((tfpdef ['=' test] ',')* - ('*' [tname] (',' tname ['=' test])* [',' ['**' tname [',']]] | '**' tname [',']) - | tfpdef ['=' test] (',' tfpdef ['=' test])* [',']) + +# The following definition for typedarglist is equivalent to this set of rules: +# +# arguments = argument (',' argument)* +# argument = tfpdef ['=' test] +# kwargs = '**' tname [','] +# args = '*' [tname] +# kwonly_kwargs = (',' argument)* [',' [kwargs]] +# args_kwonly_kwargs = args kwonly_kwargs | kwargs +# poskeyword_args_kwonly_kwargs = arguments [',' [args_kwonly_kwargs]] +# typedargslist_no_posonly = poskeyword_args_kwonly_kwargs | args_kwonly_kwargs +# typedarglist = arguments ',' '/' [',' [typedargslist_no_posonly]])|(typedargslist_no_posonly)" +# +# It needs to be fully expanded to allow our LL(1) parser to work on it. + +typedargslist: tfpdef ['=' test] (',' tfpdef ['=' test])* ',' '/' [ + ',' [((tfpdef ['=' test] ',')* ('*' [tname] (',' tname ['=' test])* + [',' ['**' tname [',']]] | '**' tname [',']) + | tfpdef ['=' test] (',' tfpdef ['=' test])* [','])] + ] | ((tfpdef ['=' test] ',')* ('*' [tname] (',' tname ['=' test])* + [',' ['**' tname [',']]] | '**' tname [',']) + | tfpdef ['=' test] (',' tfpdef ['=' test])* [',']) + tname: NAME [':' test] tfpdef: tname | '(' tfplist ')' tfplist: tfpdef (',' tfpdef)* [','] -varargslist: ((vfpdef ['=' test] ',')* - ('*' [vname] (',' vname ['=' test])* [',' ['**' vname [',']]] | '**' vname [',']) - | vfpdef ['=' test] (',' vfpdef ['=' test])* [',']) + +# The following definition for varargslist is equivalent to this set of rules: +# +# arguments = argument (',' argument )* +# argument = vfpdef ['=' test] +# kwargs = '**' vname [','] +# args = '*' [vname] +# kwonly_kwargs = (',' argument )* [',' [kwargs]] +# args_kwonly_kwargs = args kwonly_kwargs | kwargs +# poskeyword_args_kwonly_kwargs = arguments [',' [args_kwonly_kwargs]] +# vararglist_no_posonly = poskeyword_args_kwonly_kwargs | args_kwonly_kwargs +# varargslist = arguments ',' '/' [','[(vararglist_no_posonly)]] | (vararglist_no_posonly) +# +# It needs to be fully expanded to allow our LL(1) parser to work on it. + +varargslist: vfpdef ['=' test ](',' vfpdef ['=' test])* ',' '/' [',' [ + ((vfpdef ['=' test] ',')* ('*' [vname] (',' vname ['=' test])* + [',' ['**' vname [',']]] | '**' vname [',']) + | vfpdef ['=' test] (',' vfpdef ['=' test])* [',']) + ]] | ((vfpdef ['=' test] ',')* + ('*' [vname] (',' vname ['=' test])* [',' ['**' vname [',']]]| '**' vname [',']) + | vfpdef ['=' test] (',' vfpdef ['=' test])* [',']) + vname: NAME vfpdef: vname | '(' vfplist ')' vfplist: vfpdef (',' vfpdef)* [','] diff --git a/Lib/lib2to3/tests/test_parser.py b/Lib/lib2to3/tests/test_parser.py index ba2bb787..d5db66b9 100644 --- a/Lib/lib2to3/tests/test_parser.py +++ b/Lib/lib2to3/tests/test_parser.py @@ -272,6 +272,12 @@ class TestUnpackingGeneralizations(GrammarTest): def test_dict_display_2(self): self.validate("""{**{}, 3:4, **{5:6, 7:8}}""") + def test_complex_star_expression(self): + self.validate("func(* [] or [1])") + + def test_complex_double_star_expression(self): + self.validate("func(**{1: 3} if False else {x: x for x in range(3)})") + def test_argument_unpacking_1(self): self.validate("""f(a, *b, *c, d)""") @@ -630,6 +636,7 @@ class TestLiterals(GrammarTest): class TestNamedAssignments(GrammarTest): + """Also known as the walrus operator.""" def test_named_assignment_if(self): driver.parse_string("if f := x(): pass\n") @@ -644,6 +651,30 @@ class TestNamedAssignments(GrammarTest): driver.parse_string("[(lastNum := num) == 1 for num in [1, 2, 3]]\n") +class TestPositionalOnlyArgs(GrammarTest): + + def test_one_pos_only_arg(self): + driver.parse_string("def one_pos_only_arg(a, /): pass\n") + + def test_all_markers(self): + driver.parse_string( + "def all_markers(a, b=2, /, c, d=4, *, e=5, f): pass\n") + + def test_all_with_args_and_kwargs(self): + driver.parse_string( + """def all_markers_with_args_and_kwargs( + aa, b, /, _cc, d, *args, e, f_f, **kwargs, + ): + pass\n""") + + def test_lambda_soup(self): + driver.parse_string( + "lambda a, b, /, c, d, *args, e, f, **kw: kw\n") + + def test_only_positional_or_keyword(self): + driver.parse_string("def func(a,b,/,*,g,e=3): pass\n") + + class TestPickleableException(unittest.TestCase): def test_ParseError(self): err = ParseError('msg', 2, None, (1, 'context')) diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py index 7b169a16..6920a7b6 100644 --- a/Lib/logging/__init__.py +++ b/Lib/logging/__init__.py @@ -1269,6 +1269,14 @@ class Manager(object): self.loggerClass = None self.logRecordFactory = None + @property + def disable(self): + return self._disable + + @disable.setter + def disable(self, value): + self._disable = _checkLevel(value) + def getLogger(self, name): """ Get a logger with the specified name (channel name), creating it diff --git a/Lib/pdb.py b/Lib/pdb.py index d7d95715..7a5192cb 100755 --- a/Lib/pdb.py +++ b/Lib/pdb.py @@ -1686,8 +1686,9 @@ def main(): sys.argv[:] = args # Hide "pdb.py" and pdb options from argument list - # Replace pdb's dir with script's dir in front of module search path. if not run_as_module: + mainpyfile = os.path.realpath(mainpyfile) + # Replace pdb's dir with script's dir in front of module search path. sys.path[0] = os.path.dirname(mainpyfile) # Note on saving/restoring sys.argv: it's a good idea when sys.argv was diff --git a/Lib/platform.py b/Lib/platform.py index e9f50ab6..6258827d 100755 --- a/Lib/platform.py +++ b/Lib/platform.py @@ -782,7 +782,7 @@ class uname_result( ): """ A uname_result that's largely compatible with a - simple namedtuple except that 'platform' is + simple namedtuple except that 'processor' is resolved late and cached to avoid calling "uname" except when needed. """ @@ -797,12 +797,25 @@ class uname_result( (self.processor,) ) + @classmethod + def _make(cls, iterable): + # override factory to affect length check + num_fields = len(cls._fields) + result = cls.__new__(cls, *iterable) + if len(result) != num_fields + 1: + msg = f'Expected {num_fields} arguments, got {len(result)}' + raise TypeError(msg) + return result + def __getitem__(self, key): - return tuple(iter(self))[key] + return tuple(self)[key] def __len__(self): return len(tuple(iter(self))) + def __reduce__(self): + return uname_result, tuple(self)[:len(self._fields)] + _uname_cache = None diff --git a/Lib/profile.py b/Lib/profile.py index 5cb017ed..d8599fb4 100755 --- a/Lib/profile.py +++ b/Lib/profile.py @@ -595,7 +595,12 @@ def main(): '__package__': None, '__cached__': None, } - runctx(code, globs, None, options.outfile, options.sort) + try: + runctx(code, globs, None, options.outfile, options.sort) + except BrokenPipeError as exc: + # Prevent "Exception ignored" during interpreter shutdown. + sys.stdout = None + sys.exit(exc.errno) else: parser.print_usage() return parser diff --git a/Lib/pydoc_data/topics.py b/Lib/pydoc_data/topics.py index d8dd8c53..ae896c2e 100644 --- a/Lib/pydoc_data/topics.py +++ b/Lib/pydoc_data/topics.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Autogenerated by Sphinx on Mon Dec 7 15:00:07 2020 +# Autogenerated by Sphinx on Fri Feb 19 13:29:38 2021 topics = {'assert': 'The "assert" statement\n' '**********************\n' '\n' @@ -459,13 +459,12 @@ topics = {'assert': 'The "assert" statement\n' '\n' ' async_for_stmt ::= "async" for_stmt\n' '\n' - 'An *asynchronous iterable* is able to call asynchronous code in ' - 'its\n' - '*iter* implementation, and *asynchronous iterator* can call\n' - 'asynchronous code in its *next* method.\n' + 'An *asynchronous iterable* provides an "__aiter__" method that\n' + 'directly returns an *asynchronous iterator*, which can call\n' + 'asynchronous code in its "__anext__" method.\n' '\n' 'The "async for" statement allows convenient iteration over\n' - 'asynchronous iterators.\n' + 'asynchronous iterables.\n' '\n' 'The following code:\n' '\n' @@ -2381,8 +2380,9 @@ topics = {'assert': 'The "assert" statement\n' 'compatible\n' 'with an exception if it is the class or a base class of the ' 'exception\n' - 'object or a tuple containing an item compatible with the ' - 'exception.\n' + 'object, or a tuple containing an item that is the class or a ' + 'base\n' + 'class of the exception object.\n' '\n' 'If no except clause matches the exception, the search for an ' 'exception\n' @@ -2991,13 +2991,12 @@ topics = {'assert': 'The "assert" statement\n' '\n' ' async_for_stmt ::= "async" for_stmt\n' '\n' - 'An *asynchronous iterable* is able to call asynchronous code in ' - 'its\n' - '*iter* implementation, and *asynchronous iterator* can call\n' - 'asynchronous code in its *next* method.\n' + 'An *asynchronous iterable* provides an "__aiter__" method that\n' + 'directly returns an *asynchronous iterator*, which can call\n' + 'asynchronous code in its "__anext__" method.\n' '\n' 'The "async for" statement allows convenient iteration over\n' - 'asynchronous iterators.\n' + 'asynchronous iterables.\n' '\n' 'The following code:\n' '\n' @@ -5530,44 +5529,51 @@ topics = {'assert': 'The "assert" statement\n' ' | | formats the result in either fixed-point ' 'format or in |\n' ' | | scientific notation, depending on its ' - 'magnitude. The |\n' - ' | | precise rules are as follows: suppose that ' - 'the result |\n' + 'magnitude. A |\n' + ' | | precision of "0" is treated as equivalent ' + 'to a precision |\n' + ' | | of "1". The precise rules are as follows: ' + 'suppose that |\n' + ' | | the result formatted with presentation ' + 'type "\'e\'" and |\n' + ' | | precision "p-1" would have exponent ' + '"exp". Then, if "m <= |\n' + ' | | exp < p", where "m" is -4 for floats and ' + '-6 for |\n' + ' | | "Decimals", the number is formatted with ' + 'presentation type |\n' + ' | | "\'f\'" and precision "p-1-exp". ' + 'Otherwise, the number is |\n' ' | | formatted with presentation type "\'e\'" ' - 'and precision "p-1" |\n' - ' | | would have exponent "exp". Then, if "m <= ' - 'exp < p", where |\n' - ' | | "m" is -4 for floats and -6 for ' - '"Decimals", the number is |\n' - ' | | formatted with presentation type "\'f\'" ' 'and precision |\n' - ' | | "p-1-exp". Otherwise, the number is ' - 'formatted with |\n' - ' | | presentation type "\'e\'" and precision ' - '"p-1". In both cases |\n' - ' | | insignificant trailing zeros are removed ' - 'from the |\n' - ' | | significand, and the decimal point is also ' - 'removed if |\n' - ' | | there are no remaining digits following ' - 'it, unless the |\n' - ' | | "\'#\'" option is used. Positive and ' - 'negative infinity, |\n' - ' | | positive and negative zero, and nans, are ' - 'formatted as |\n' - ' | | "inf", "-inf", "0", "-0" and "nan" ' - 'respectively, |\n' - ' | | regardless of the precision. A precision ' - 'of "0" is |\n' - ' | | treated as equivalent to a precision of ' - '"1". With no |\n' - ' | | precision given, uses a precision of "6" ' - 'significant |\n' - ' | | digits for "float", and shows all ' - 'coefficient digits for |\n' - ' | | ' - '"Decimal". ' - '|\n' + ' | | "p-1". In both cases insignificant ' + 'trailing zeros are |\n' + ' | | removed from the significand, and the ' + 'decimal point is |\n' + ' | | also removed if there are no remaining ' + 'digits following |\n' + ' | | it, unless the "\'#\'" option is used. ' + 'With no precision |\n' + ' | | given, uses a precision of "6" significant ' + 'digits for |\n' + ' | | "float". For "Decimal", the coefficient of ' + 'the result is |\n' + ' | | formed from the coefficient digits of the ' + 'value; |\n' + ' | | scientific notation is used for values ' + 'smaller than "1e-6" |\n' + ' | | in absolute value and values where the ' + 'place value of the |\n' + ' | | least significant digit is larger than 1, ' + 'and fixed-point |\n' + ' | | notation is used otherwise. Positive and ' + 'negative |\n' + ' | | infinity, positive and negative zero, and ' + 'nans, are |\n' + ' | | formatted as "inf", "-inf", "0", "-0" and ' + '"nan" |\n' + ' | | respectively, regardless of the ' + 'precision. |\n' ' ' '+-----------+------------------------------------------------------------+\n' ' | "\'G\'" | General format. Same as "\'g\'" except ' @@ -5592,19 +5598,24 @@ topics = {'assert': 'The "assert" statement\n' 'percent sign. |\n' ' ' '+-----------+------------------------------------------------------------+\n' - ' | None | Similar to "\'g\'", except that ' - 'fixed-point notation, when |\n' - ' | | used, has at least one digit past the ' - 'decimal point. The |\n' - ' | | default precision is as high as needed to ' - 'represent the |\n' - ' | | particular value. The overall effect is to ' - 'match the |\n' - ' | | output of "str()" as altered by the other ' - 'format |\n' - ' | | ' - 'modifiers. ' - '|\n' + ' | None | For "float" this is the same as "\'g\'", ' + 'except that when |\n' + ' | | fixed-point notation is used to format the ' + 'result, it |\n' + ' | | always includes at least one digit past ' + 'the decimal point. |\n' + ' | | The precision used is as large as needed ' + 'to represent the |\n' + ' | | given value faithfully. For "Decimal", ' + 'this is the same |\n' + ' | | as either "\'g\'" or "\'G\'" depending on ' + 'the value of |\n' + ' | | "context.capitals" for the current decimal ' + 'context. The |\n' + ' | | overall effect is to match the output of ' + '"str()" as |\n' + ' | | altered by the other format ' + 'modifiers. |\n' ' ' '+-----------+------------------------------------------------------------+\n' '\n' @@ -7950,7 +7961,7 @@ topics = {'assert': 'The "assert" statement\n' 'immediate\n' ' subclasses. This method returns a list of all those ' 'references\n' - ' still alive. Example:\n' + ' still alive. The list is in definition order. Example:\n' '\n' ' >>> int.__subclasses__()\n' " []\n" @@ -11259,7 +11270,8 @@ topics = {'assert': 'The "assert" statement\n' 'object is “compatible” with the exception. An object is compatible\n' 'with an exception if it is the class or a base class of the ' 'exception\n' - 'object or a tuple containing an item compatible with the exception.\n' + 'object, or a tuple containing an item that is the class or a base\n' + 'class of the exception object.\n' '\n' 'If no except clause matches the exception, the search for an ' 'exception\n' @@ -11480,7 +11492,6 @@ topics = {'assert': 'The "assert" statement\n' ' There are two types of integers:\n' '\n' ' Integers ("int")\n' - '\n' ' These represent numbers in an unlimited range, subject to\n' ' available (virtual) memory only. For the purpose of ' 'shift\n' diff --git a/Lib/random.py b/Lib/random.py index a6454f52..36e16a90 100644 --- a/Lib/random.py +++ b/Lib/random.py @@ -77,6 +77,7 @@ __all__ = [ "lognormvariate", "normalvariate", "paretovariate", + "randbytes", "randint", "random", "randrange", @@ -441,7 +442,7 @@ class Random(_random.Random): raise TypeError('Counts must be integers') if total <= 0: raise ValueError('Total of counts must be greater than zero') - selections = sample(range(total), k=k) + selections = self.sample(range(total), k=k) bisect = _bisect return [population[bisect(cum_counts, s)] for s in selections] randbelow = self._randbelow diff --git a/Lib/smtplib.py b/Lib/smtplib.py index 7808ba01..f0472317 100755 --- a/Lib/smtplib.py +++ b/Lib/smtplib.py @@ -1082,7 +1082,8 @@ class LMTP(SMTP): # Handle Unix-domain sockets. try: self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - self.sock.settimeout(self.timeout) + if self.timeout is not socket._GLOBAL_DEFAULT_TIMEOUT: + self.sock.settimeout(self.timeout) self.file = None self.sock.connect(host) except OSError: diff --git a/Lib/sqlite3/test/hooks.py b/Lib/sqlite3/test/hooks.py index d74e74bf..214205c1 100644 --- a/Lib/sqlite3/test/hooks.py +++ b/Lib/sqlite3/test/hooks.py @@ -265,6 +265,14 @@ class TraceCallbackTests(unittest.TestCase): cur.execute(queries[0]) con2.execute("create table bar(x)") cur.execute(queries[1]) + + # Extract from SQLite 3.7.15 changelog: + # Avoid invoking the sqlite3_trace() callback multiple times when a + # statement is automatically reprepared due to SQLITE_SCHEMA errors. + # + # See bpo-40810 + if sqlite.sqlite_version_info < (3, 7, 15): + queries.append(queries[-1]) self.assertEqual(traced_statements, queries) diff --git a/Lib/subprocess.py b/Lib/subprocess.py index f1d829a6..ddf1128f 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -415,7 +415,11 @@ def check_output(*popenargs, timeout=None, **kwargs): if 'input' in kwargs and kwargs['input'] is None: # Explicitly passing input=None was previously equivalent to passing an # empty string. That is maintained here for backwards compatibility. - kwargs['input'] = '' if kwargs.get('universal_newlines', False) else b'' + if kwargs.get('universal_newlines') or kwargs.get('text'): + empty = '' + else: + empty = b'' + kwargs['input'] = empty return run(*popenargs, stdout=PIPE, timeout=timeout, check=True, **kwargs).stdout diff --git a/Lib/sysconfig.py b/Lib/sysconfig.py index bf04ac54..e3f79bfd 100644 --- a/Lib/sysconfig.py +++ b/Lib/sysconfig.py @@ -18,6 +18,11 @@ __all__ = [ 'parse_config_h', ] +# Keys for get_config_var() that are never converted to Python integers. +_ALWAYS_STR = { + 'MACOSX_DEPLOYMENT_TARGET', +} + _INSTALL_SCHEMES = { 'posix_prefix': { 'stdlib': '{installed_base}/{platlibdir}/python{py_version_short}', @@ -240,6 +245,9 @@ def _parse_makefile(filename, vars=None): notdone[n] = v else: try: + if n in _ALWAYS_STR: + raise ValueError + v = int(v) except ValueError: # insert literal `$' @@ -298,6 +306,8 @@ def _parse_makefile(filename, vars=None): notdone[name] = value else: try: + if name in _ALWAYS_STR: + raise ValueError value = int(value) except ValueError: done[name] = value.strip() @@ -424,10 +434,11 @@ def _init_posix(vars): def _init_non_posix(vars): """Initialize the module as appropriate for NT""" # set basic install directories + import _imp vars['LIBDEST'] = get_path('stdlib') vars['BINLIBDEST'] = get_path('platstdlib') vars['INCLUDEPY'] = get_path('include') - vars['EXT_SUFFIX'] = '.pyd' + vars['EXT_SUFFIX'] = _imp.extension_suffixes()[0] vars['EXE'] = '.exe' vars['VERSION'] = _PY_VERSION_SHORT_NO_DOT vars['BINDIR'] = os.path.dirname(_safe_realpath(sys.executable)) @@ -458,6 +469,8 @@ def parse_config_h(fp, vars=None): if m: n, v = m.group(1, 2) try: + if n in _ALWAYS_STR: + raise ValueError v = int(v) except ValueError: pass diff --git a/Lib/test/crashers/bogus_code_obj.py b/Lib/test/crashers/bogus_code_obj.py index 198d2294..e71b3582 100644 --- a/Lib/test/crashers/bogus_code_obj.py +++ b/Lib/test/crashers/bogus_code_obj.py @@ -14,6 +14,6 @@ the user build or load random bytecodes anyway. Otherwise, this is a import types -co = types.CodeType(0, 0, 0, 0, 0, b'\x04\x71\x00\x00', +co = types.CodeType(0, 0, 0, 0, 0, 0, b'\x04\x00\x71\x00', (), (), (), '', '', 1, b'') exec(co) diff --git a/Lib/test/mock_socket.py b/Lib/test/mock_socket.py index cda4db25..c7abddcf 100644 --- a/Lib/test/mock_socket.py +++ b/Lib/test/mock_socket.py @@ -107,6 +107,9 @@ class MockSocket: def close(self): pass + def connect(self, host): + pass + def socket(family=None, type=None, proto=None): return MockSocket(family) @@ -152,8 +155,12 @@ error = socket_module.error # Constants +_GLOBAL_DEFAULT_TIMEOUT = socket_module._GLOBAL_DEFAULT_TIMEOUT AF_INET = socket_module.AF_INET AF_INET6 = socket_module.AF_INET6 SOCK_STREAM = socket_module.SOCK_STREAM SOL_SOCKET = None SO_REUSEADDR = None + +if hasattr(socket_module, 'AF_UNIX'): + AF_UNIX = socket_module.AF_UNIX diff --git a/Lib/test/pickletester.py b/Lib/test/pickletester.py index 3d54617f..7e279cc7 100644 --- a/Lib/test/pickletester.py +++ b/Lib/test/pickletester.py @@ -65,6 +65,10 @@ def count_opcode(code, pickle): return n +def identity(x): + return x + + class UnseekableIO(io.BytesIO): def peek(self, *args): raise NotImplementedError @@ -134,11 +138,12 @@ class E(C): def __getinitargs__(self): return () -class H(object): +# Simple mutable object. +class Object: pass -# Hashable mutable key -class K(object): +# Hashable immutable key object containing unheshable mutable data. +class K: def __init__(self, value): self.value = value @@ -153,10 +158,6 @@ __main__.D = D D.__module__ = "__main__" __main__.E = E E.__module__ = "__main__" -__main__.H = H -H.__module__ = "__main__" -__main__.K = K -K.__module__ = "__main__" class myint(int): def __init__(self, x): @@ -1492,54 +1493,182 @@ class AbstractPickleTests(unittest.TestCase): got = filelike.getvalue() self.assertEqual(expected, got) - def test_recursive_list(self): - l = [] + def _test_recursive_list(self, cls, aslist=identity, minprotocol=0): + # List containing itself. + l = cls() l.append(l) - for proto in protocols: + for proto in range(minprotocol, pickle.HIGHEST_PROTOCOL + 1): s = self.dumps(l, proto) x = self.loads(s) - self.assertIsInstance(x, list) - self.assertEqual(len(x), 1) - self.assertIs(x[0], x) + self.assertIsInstance(x, cls) + y = aslist(x) + self.assertEqual(len(y), 1) + self.assertIs(y[0], x) - def test_recursive_tuple_and_list(self): - t = ([],) + def test_recursive_list(self): + self._test_recursive_list(list) + + def test_recursive_list_subclass(self): + self._test_recursive_list(MyList, minprotocol=2) + + def test_recursive_list_like(self): + self._test_recursive_list(REX_six, aslist=lambda x: x.items) + + def _test_recursive_tuple_and_list(self, cls, aslist=identity, minprotocol=0): + # Tuple containing a list containing the original tuple. + t = (cls(),) t[0].append(t) - for proto in protocols: + for proto in range(minprotocol, pickle.HIGHEST_PROTOCOL + 1): s = self.dumps(t, proto) x = self.loads(s) self.assertIsInstance(x, tuple) self.assertEqual(len(x), 1) - self.assertIsInstance(x[0], list) - self.assertEqual(len(x[0]), 1) - self.assertIs(x[0][0], x) + self.assertIsInstance(x[0], cls) + y = aslist(x[0]) + self.assertEqual(len(y), 1) + self.assertIs(y[0], x) + + # List containing a tuple containing the original list. + t, = t + for proto in range(minprotocol, pickle.HIGHEST_PROTOCOL + 1): + s = self.dumps(t, proto) + x = self.loads(s) + self.assertIsInstance(x, cls) + y = aslist(x) + self.assertEqual(len(y), 1) + self.assertIsInstance(y[0], tuple) + self.assertEqual(len(y[0]), 1) + self.assertIs(y[0][0], x) - def test_recursive_dict(self): - d = {} + def test_recursive_tuple_and_list(self): + self._test_recursive_tuple_and_list(list) + + def test_recursive_tuple_and_list_subclass(self): + self._test_recursive_tuple_and_list(MyList, minprotocol=2) + + def test_recursive_tuple_and_list_like(self): + self._test_recursive_tuple_and_list(REX_six, aslist=lambda x: x.items) + + def _test_recursive_dict(self, cls, asdict=identity, minprotocol=0): + # Dict containing itself. + d = cls() d[1] = d - for proto in protocols: + for proto in range(minprotocol, pickle.HIGHEST_PROTOCOL + 1): s = self.dumps(d, proto) x = self.loads(s) - self.assertIsInstance(x, dict) - self.assertEqual(list(x.keys()), [1]) - self.assertIs(x[1], x) + self.assertIsInstance(x, cls) + y = asdict(x) + self.assertEqual(list(y.keys()), [1]) + self.assertIs(y[1], x) - def test_recursive_dict_key(self): - d = {} - k = K(d) - d[k] = 1 - for proto in protocols: + def test_recursive_dict(self): + self._test_recursive_dict(dict) + + def test_recursive_dict_subclass(self): + self._test_recursive_dict(MyDict, minprotocol=2) + + def test_recursive_dict_like(self): + self._test_recursive_dict(REX_seven, asdict=lambda x: x.table) + + def _test_recursive_tuple_and_dict(self, cls, asdict=identity, minprotocol=0): + # Tuple containing a dict containing the original tuple. + t = (cls(),) + t[0][1] = t + for proto in range(minprotocol, pickle.HIGHEST_PROTOCOL + 1): + s = self.dumps(t, proto) + x = self.loads(s) + self.assertIsInstance(x, tuple) + self.assertEqual(len(x), 1) + self.assertIsInstance(x[0], cls) + y = asdict(x[0]) + self.assertEqual(list(y), [1]) + self.assertIs(y[1], x) + + # Dict containing a tuple containing the original dict. + t, = t + for proto in range(minprotocol, pickle.HIGHEST_PROTOCOL + 1): + s = self.dumps(t, proto) + x = self.loads(s) + self.assertIsInstance(x, cls) + y = asdict(x) + self.assertEqual(list(y), [1]) + self.assertIsInstance(y[1], tuple) + self.assertEqual(len(y[1]), 1) + self.assertIs(y[1][0], x) + + def test_recursive_tuple_and_dict(self): + self._test_recursive_tuple_and_dict(dict) + + def test_recursive_tuple_and_dict_subclass(self): + self._test_recursive_tuple_and_dict(MyDict, minprotocol=2) + + def test_recursive_tuple_and_dict_like(self): + self._test_recursive_tuple_and_dict(REX_seven, asdict=lambda x: x.table) + + def _test_recursive_dict_key(self, cls, asdict=identity, minprotocol=0): + # Dict containing an immutable object (as key) containing the original + # dict. + d = cls() + d[K(d)] = 1 + for proto in range(minprotocol, pickle.HIGHEST_PROTOCOL + 1): s = self.dumps(d, proto) x = self.loads(s) - self.assertIsInstance(x, dict) - self.assertEqual(len(x.keys()), 1) - self.assertIsInstance(list(x.keys())[0], K) - self.assertIs(list(x.keys())[0].value, x) + self.assertIsInstance(x, cls) + y = asdict(x) + self.assertEqual(len(y.keys()), 1) + self.assertIsInstance(list(y.keys())[0], K) + self.assertIs(list(y.keys())[0].value, x) + + def test_recursive_dict_key(self): + self._test_recursive_dict_key(dict) + + def test_recursive_dict_subclass_key(self): + self._test_recursive_dict_key(MyDict, minprotocol=2) + + def test_recursive_dict_like_key(self): + self._test_recursive_dict_key(REX_seven, asdict=lambda x: x.table) + + def _test_recursive_tuple_and_dict_key(self, cls, asdict=identity, minprotocol=0): + # Tuple containing a dict containing an immutable object (as key) + # containing the original tuple. + t = (cls(),) + t[0][K(t)] = 1 + for proto in range(minprotocol, pickle.HIGHEST_PROTOCOL + 1): + s = self.dumps(t, proto) + x = self.loads(s) + self.assertIsInstance(x, tuple) + self.assertEqual(len(x), 1) + self.assertIsInstance(x[0], cls) + y = asdict(x[0]) + self.assertEqual(len(y), 1) + self.assertIsInstance(list(y.keys())[0], K) + self.assertIs(list(y.keys())[0].value, x) + + # Dict containing an immutable object (as key) containing a tuple + # containing the original dict. + t, = t + for proto in range(minprotocol, pickle.HIGHEST_PROTOCOL + 1): + s = self.dumps(t, proto) + x = self.loads(s) + self.assertIsInstance(x, cls) + y = asdict(x) + self.assertEqual(len(y), 1) + self.assertIsInstance(list(y.keys())[0], K) + self.assertIs(list(y.keys())[0].value[0], x) + + def test_recursive_tuple_and_dict_key(self): + self._test_recursive_tuple_and_dict_key(dict) + + def test_recursive_tuple_and_dict_subclass_key(self): + self._test_recursive_tuple_and_dict_key(MyDict, minprotocol=2) + + def test_recursive_tuple_and_dict_like_key(self): + self._test_recursive_tuple_and_dict_key(REX_seven, asdict=lambda x: x.table) def test_recursive_set(self): + # Set containing an immutable object containing the original set. y = set() - k = K(y) - y.add(k) + y.add(K(y)) for proto in range(4, pickle.HIGHEST_PROTOCOL + 1): s = self.dumps(y, proto) x = self.loads(s) @@ -1548,52 +1677,31 @@ class AbstractPickleTests(unittest.TestCase): self.assertIsInstance(list(x)[0], K) self.assertIs(list(x)[0].value, x) - def test_recursive_list_subclass(self): - y = MyList() - y.append(y) - for proto in range(2, pickle.HIGHEST_PROTOCOL + 1): + # Immutable object containing a set containing the original object. + y, = y + for proto in range(4, pickle.HIGHEST_PROTOCOL + 1): s = self.dumps(y, proto) x = self.loads(s) - self.assertIsInstance(x, MyList) - self.assertEqual(len(x), 1) - self.assertIs(x[0], x) - - def test_recursive_dict_subclass(self): - d = MyDict() - d[1] = d - for proto in range(2, pickle.HIGHEST_PROTOCOL + 1): - s = self.dumps(d, proto) - x = self.loads(s) - self.assertIsInstance(x, MyDict) - self.assertEqual(list(x.keys()), [1]) - self.assertIs(x[1], x) - - def test_recursive_dict_subclass_key(self): - d = MyDict() - k = K(d) - d[k] = 1 - for proto in range(2, pickle.HIGHEST_PROTOCOL + 1): - s = self.dumps(d, proto) - x = self.loads(s) - self.assertIsInstance(x, MyDict) - self.assertEqual(len(list(x.keys())), 1) - self.assertIsInstance(list(x.keys())[0], K) - self.assertIs(list(x.keys())[0].value, x) + self.assertIsInstance(x, K) + self.assertIsInstance(x.value, set) + self.assertEqual(len(x.value), 1) + self.assertIs(list(x.value)[0], x) def test_recursive_inst(self): - i = C() + # Mutable object containing itself. + i = Object() i.attr = i for proto in protocols: s = self.dumps(i, proto) x = self.loads(s) - self.assertIsInstance(x, C) + self.assertIsInstance(x, Object) self.assertEqual(dir(x), dir(i)) self.assertIs(x.attr, x) def test_recursive_multi(self): l = [] d = {1:l} - i = C() + i = Object() i.attr = d l.append(i) for proto in protocols: @@ -1603,49 +1711,94 @@ class AbstractPickleTests(unittest.TestCase): self.assertEqual(len(x), 1) self.assertEqual(dir(x[0]), dir(i)) self.assertEqual(list(x[0].attr.keys()), [1]) - self.assertTrue(x[0].attr[1] is x) - - def check_recursive_collection_and_inst(self, factory): - h = H() - y = factory([h]) - h.attr = y + self.assertIs(x[0].attr[1], x) + + def _test_recursive_collection_and_inst(self, factory): + # Mutable object containing a collection containing the original + # object. + o = Object() + o.attr = factory([o]) + t = type(o.attr) for proto in protocols: - s = self.dumps(y, proto) + s = self.dumps(o, proto) x = self.loads(s) - self.assertIsInstance(x, type(y)) + self.assertIsInstance(x.attr, t) + self.assertEqual(len(x.attr), 1) + self.assertIsInstance(list(x.attr)[0], Object) + self.assertIs(list(x.attr)[0], x) + + # Collection containing a mutable object containing the original + # collection. + o = o.attr + for proto in protocols: + s = self.dumps(o, proto) + x = self.loads(s) + self.assertIsInstance(x, t) self.assertEqual(len(x), 1) - self.assertIsInstance(list(x)[0], H) + self.assertIsInstance(list(x)[0], Object) self.assertIs(list(x)[0].attr, x) def test_recursive_list_and_inst(self): - self.check_recursive_collection_and_inst(list) + self._test_recursive_collection_and_inst(list) def test_recursive_tuple_and_inst(self): - self.check_recursive_collection_and_inst(tuple) + self._test_recursive_collection_and_inst(tuple) def test_recursive_dict_and_inst(self): - self.check_recursive_collection_and_inst(dict.fromkeys) + self._test_recursive_collection_and_inst(dict.fromkeys) def test_recursive_set_and_inst(self): - self.check_recursive_collection_and_inst(set) + self._test_recursive_collection_and_inst(set) def test_recursive_frozenset_and_inst(self): - self.check_recursive_collection_and_inst(frozenset) + self._test_recursive_collection_and_inst(frozenset) def test_recursive_list_subclass_and_inst(self): - self.check_recursive_collection_and_inst(MyList) + self._test_recursive_collection_and_inst(MyList) def test_recursive_tuple_subclass_and_inst(self): - self.check_recursive_collection_and_inst(MyTuple) + self._test_recursive_collection_and_inst(MyTuple) def test_recursive_dict_subclass_and_inst(self): - self.check_recursive_collection_and_inst(MyDict.fromkeys) + self._test_recursive_collection_and_inst(MyDict.fromkeys) def test_recursive_set_subclass_and_inst(self): - self.check_recursive_collection_and_inst(MySet) + self._test_recursive_collection_and_inst(MySet) def test_recursive_frozenset_subclass_and_inst(self): - self.check_recursive_collection_and_inst(MyFrozenSet) + self._test_recursive_collection_and_inst(MyFrozenSet) + + def test_recursive_inst_state(self): + # Mutable object containing itself. + y = REX_state() + y.state = y + for proto in protocols: + s = self.dumps(y, proto) + x = self.loads(s) + self.assertIsInstance(x, REX_state) + self.assertIs(x.state, x) + + def test_recursive_tuple_and_inst_state(self): + # Tuple containing a mutable object containing the original tuple. + t = (REX_state(),) + t[0].state = t + for proto in protocols: + s = self.dumps(t, proto) + x = self.loads(s) + self.assertIsInstance(x, tuple) + self.assertEqual(len(x), 1) + self.assertIsInstance(x[0], REX_state) + self.assertIs(x[0].state, x) + + # Mutable object containing a tuple containing the object. + t, = t + for proto in protocols: + s = self.dumps(t, proto) + x = self.loads(s) + self.assertIsInstance(x, REX_state) + self.assertIsInstance(x.state, tuple) + self.assertEqual(len(x.state), 1) + self.assertIs(x.state[0], x) def test_unicode(self): endcases = ['', '<\\u>', '<\\\u1234>', '<\n>', @@ -3058,6 +3211,19 @@ class REX_seven(object): def __reduce__(self): return type(self), (), None, None, iter(self.table.items()) +class REX_state(object): + """This class is used to check the 3th argument (state) of + the reduce protocol. + """ + def __init__(self, state=None): + self.state = state + def __eq__(self, other): + return type(self) is type(other) and self.state == other.state + def __setstate__(self, state): + self.state = state + def __reduce__(self): + return type(self), (), self.state + # Test classes for newobj diff --git a/Lib/test/test_asyncio/test_asyncio_waitfor.py b/Lib/test/test_asyncio/test_asyncio_waitfor.py new file mode 100644 index 00000000..2ca64abb --- /dev/null +++ b/Lib/test/test_asyncio/test_asyncio_waitfor.py @@ -0,0 +1,61 @@ +import asyncio +import unittest +import time + +def tearDownModule(): + asyncio.set_event_loop_policy(None) + + +class SlowTask: + """ Task will run for this defined time, ignoring cancel requests """ + TASK_TIMEOUT = 0.2 + + def __init__(self): + self.exited = False + + async def run(self): + exitat = time.monotonic() + self.TASK_TIMEOUT + + while True: + tosleep = exitat - time.monotonic() + if tosleep <= 0: + break + + try: + await asyncio.sleep(tosleep) + except asyncio.CancelledError: + pass + + self.exited = True + +class AsyncioWaitForTest(unittest.TestCase): + + async def atest_asyncio_wait_for_cancelled(self): + t = SlowTask() + + waitfortask = asyncio.create_task(asyncio.wait_for(t.run(), t.TASK_TIMEOUT * 2)) + await asyncio.sleep(0) + waitfortask.cancel() + await asyncio.wait({waitfortask}) + + self.assertTrue(t.exited) + + def test_asyncio_wait_for_cancelled(self): + asyncio.run(self.atest_asyncio_wait_for_cancelled()) + + async def atest_asyncio_wait_for_timeout(self): + t = SlowTask() + + try: + await asyncio.wait_for(t.run(), t.TASK_TIMEOUT / 2) + except asyncio.TimeoutError: + pass + + self.assertTrue(t.exited) + + def test_asyncio_wait_for_timeout(self): + asyncio.run(self.atest_asyncio_wait_for_timeout()) + + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 4df1b95b..d009f57e 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -6,6 +6,7 @@ import builtins import collections import decimal import fractions +import gc import io import locale import os @@ -1606,6 +1607,18 @@ class BuiltinTest(unittest.TestCase): self.assertIs(cm.exception, exception) + @support.cpython_only + def test_zip_result_gc(self): + # bpo-42536: zip's tuple-reuse speed trick breaks the GC's assumptions + # about what can be untracked. Make sure we re-track result tuples + # whenever we reuse them. + it = zip([[]]) + gc.collect() + # That GC collection probably untracked the recycled internal result + # tuple, which is initialized to (None,). Make sure it's re-tracked when + # it's mutated and returned from __next__: + self.assertTrue(gc.is_tracked(next(it))) + def test_format(self): # Test the basic machinery of the format() builtin. Don't test # the specifics of the various formatters diff --git a/Lib/test/test_cgi.py b/Lib/test/test_cgi.py index 101942de..4e1506a6 100644 --- a/Lib/test/test_cgi.py +++ b/Lib/test/test_cgi.py @@ -53,12 +53,9 @@ parse_strict_test_cases = [ ("", ValueError("bad query field: ''")), ("&", ValueError("bad query field: ''")), ("&&", ValueError("bad query field: ''")), - (";", ValueError("bad query field: ''")), - (";&;", ValueError("bad query field: ''")), # Should the next few really be valid? ("=", {}), ("=&=", {}), - ("=;=", {}), # This rest seem to make sense ("=a", {'': ['a']}), ("&=a", ValueError("bad query field: ''")), @@ -73,8 +70,6 @@ parse_strict_test_cases = [ ("a=a+b&b=b+c", {'a': ['a b'], 'b': ['b c']}), ("a=a+b&a=b+a", {'a': ['a b', 'b a']}), ("x=1&y=2.0&z=2-3.%2b0", {'x': ['1'], 'y': ['2.0'], 'z': ['2-3.+0']}), - ("x=1;y=2.0&z=2-3.%2b0", {'x': ['1'], 'y': ['2.0'], 'z': ['2-3.+0']}), - ("x=1;y=2.0;z=2-3.%2b0", {'x': ['1'], 'y': ['2.0'], 'z': ['2-3.+0']}), ("Hbc5161168c542333633315dee1182227:key_store_seqid=400006&cuyer=r&view=bustomer&order_id=0bb2e248638833d48cb7fed300000f1b&expire=964546263&lobale=en-US&kid=130003.300038&ss=env", {'Hbc5161168c542333633315dee1182227:key_store_seqid': ['400006'], 'cuyer': ['r'], @@ -201,6 +196,30 @@ Content-Length: 3 else: self.assertEqual(fs.getvalue(key), expect_val[0]) + def test_separator(self): + parse_semicolon = [ + ("x=1;y=2.0", {'x': ['1'], 'y': ['2.0']}), + ("x=1;y=2.0;z=2-3.%2b0", {'x': ['1'], 'y': ['2.0'], 'z': ['2-3.+0']}), + (";", ValueError("bad query field: ''")), + (";;", ValueError("bad query field: ''")), + ("=;a", ValueError("bad query field: 'a'")), + (";b=a", ValueError("bad query field: ''")), + ("b;=a", ValueError("bad query field: 'b'")), + ("a=a+b;b=b+c", {'a': ['a b'], 'b': ['b c']}), + ("a=a+b;a=b+a", {'a': ['a b', 'b a']}), + ] + for orig, expect in parse_semicolon: + env = {'QUERY_STRING': orig} + fs = cgi.FieldStorage(separator=';', environ=env) + if isinstance(expect, dict): + for key in expect.keys(): + expect_val = expect[key] + self.assertIn(key, fs) + if len(expect_val) > 1: + self.assertEqual(fs.getvalue(key), expect_val) + else: + self.assertEqual(fs.getvalue(key), expect_val[0]) + def test_log(self): cgi.log("Testing") diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py index 057ec920..3ff660cf 100644 --- a/Lib/test/test_collections.py +++ b/Lib/test/test_collections.py @@ -680,6 +680,11 @@ class TestNamedTuple(unittest.TestCase): self.assertEqual(np.x, 1) self.assertEqual(np.y, 2) + def test_new_builtins_issue_43102(self): + self.assertEqual( + namedtuple('C', ()).__new__.__globals__['__builtins__'], + {}) + ################################################################################ ### Abstract Base Classes diff --git a/Lib/test/test_curses.py b/Lib/test/test_curses.py index 5e619d13..c1ad23b4 100644 --- a/Lib/test/test_curses.py +++ b/Lib/test/test_curses.py @@ -1,14 +1,5 @@ -# -# Test script for the curses module -# -# This script doesn't actually display anything very coherent. but it -# does call (nearly) every method and function. -# -# Functions not tested: {def,reset}_{shell,prog}_mode, getch(), getstr(), -# init_color() -# Only called, not tested: getmouse(), ungetmouse() -# - +import functools +import inspect import os import string import sys @@ -20,7 +11,6 @@ from test.support import requires, import_module, verbose, SaveSignals # Optionally test curses module. This currently requires that the # 'curses' resource be given on the regrtest command line using the -u # option. If not available, nothing after this line will be executed. -import inspect requires('curses') # If either of these don't exist, skip the tests. @@ -36,7 +26,28 @@ def requires_curses_func(name): return unittest.skipUnless(hasattr(curses, name), 'requires curses.%s' % name) +def requires_curses_window_meth(name): + def deco(test): + @functools.wraps(test) + def wrapped(self, *args, **kwargs): + if not hasattr(self.stdscr, name): + raise unittest.SkipTest('requires curses.window.%s' % name) + test(self, *args, **kwargs) + return wrapped + return deco + + +def requires_colors(test): + @functools.wraps(test) + def wrapped(self, *args, **kwargs): + if not curses.has_colors(): + self.skipTest('requires colors support') + curses.start_color() + test(self, *args, **kwargs) + return wrapped + term = os.environ.get('TERM') +SHORT_MAX = 0x7fff # If newterm was supported we could use it instead of initscr and not exit @unittest.skipIf(not term or term == 'unknown', @@ -47,254 +58,941 @@ class TestCurses(unittest.TestCase): @classmethod def setUpClass(cls): - if not sys.__stdout__.isatty(): - # Temporary skip tests on non-tty - raise unittest.SkipTest('sys.__stdout__ is not a tty') - cls.tmp = tempfile.TemporaryFile() - fd = cls.tmp.fileno() - else: - cls.tmp = None - fd = sys.__stdout__.fileno() + if verbose: + print(f'TERM={term}', file=sys.stderr, flush=True) # testing setupterm() inside initscr/endwin # causes terminal breakage - curses.setupterm(fd=fd) - - @classmethod - def tearDownClass(cls): - if cls.tmp: - cls.tmp.close() - del cls.tmp + stdout_fd = sys.__stdout__.fileno() + curses.setupterm(fd=stdout_fd) def setUp(self): + self.isatty = True + self.output = sys.__stdout__ + stdout_fd = sys.__stdout__.fileno() + if not sys.__stdout__.isatty(): + # initstr() unconditionally uses C stdout. + # If it is redirected to file or pipe, try to attach it + # to terminal. + # First, save a copy of the file descriptor of stdout, so it + # can be restored after finishing the test. + dup_fd = os.dup(stdout_fd) + self.addCleanup(os.close, dup_fd) + self.addCleanup(os.dup2, dup_fd, stdout_fd) + + if sys.__stderr__.isatty(): + # If stderr is connected to terminal, use it. + tmp = sys.__stderr__ + self.output = sys.__stderr__ + else: + try: + # Try to open the terminal device. + tmp = open('/dev/tty', 'wb', buffering=0) + except OSError: + # As a fallback, use regular file to write control codes. + # Some functions (like savetty) will not work, but at + # least the garbage control sequences will not be mixed + # with the testing report. + tmp = tempfile.TemporaryFile(mode='wb', buffering=0) + self.isatty = False + self.addCleanup(tmp.close) + self.output = None + os.dup2(tmp.fileno(), stdout_fd) + self.save_signals = SaveSignals() self.save_signals.save() - if verbose: + self.addCleanup(self.save_signals.restore) + if verbose and self.output is not None: # just to make the test output a little more readable - print() + sys.stderr.flush() + sys.stdout.flush() + print(file=self.output, flush=True) self.stdscr = curses.initscr() - curses.savetty() + if self.isatty: + curses.savetty() + self.addCleanup(curses.endwin) + self.addCleanup(curses.resetty) + self.stdscr.erase() + + @requires_curses_func('filter') + def test_filter(self): + # TODO: Should be called before initscr() or newterm() are called. + # TODO: nofilter() + curses.filter() + + @requires_curses_func('use_env') + def test_use_env(self): + # TODO: Should be called before initscr() or newterm() are called. + # TODO: use_tioctl() + curses.use_env(False) + curses.use_env(True) + + def test_create_windows(self): + win = curses.newwin(5, 10) + self.assertEqual(win.getbegyx(), (0, 0)) + self.assertEqual(win.getparyx(), (-1, -1)) + self.assertEqual(win.getmaxyx(), (5, 10)) + + win = curses.newwin(10, 15, 2, 5) + self.assertEqual(win.getbegyx(), (2, 5)) + self.assertEqual(win.getparyx(), (-1, -1)) + self.assertEqual(win.getmaxyx(), (10, 15)) + + win2 = win.subwin(3, 7) + self.assertEqual(win2.getbegyx(), (3, 7)) + self.assertEqual(win2.getparyx(), (1, 2)) + self.assertEqual(win2.getmaxyx(), (9, 13)) + + win2 = win.subwin(5, 10, 3, 7) + self.assertEqual(win2.getbegyx(), (3, 7)) + self.assertEqual(win2.getparyx(), (1, 2)) + self.assertEqual(win2.getmaxyx(), (5, 10)) + + win3 = win.derwin(2, 3) + self.assertEqual(win3.getbegyx(), (4, 8)) + self.assertEqual(win3.getparyx(), (2, 3)) + self.assertEqual(win3.getmaxyx(), (8, 12)) + + win3 = win.derwin(6, 11, 2, 3) + self.assertEqual(win3.getbegyx(), (4, 8)) + self.assertEqual(win3.getparyx(), (2, 3)) + self.assertEqual(win3.getmaxyx(), (6, 11)) + + win.mvwin(0, 1) + self.assertEqual(win.getbegyx(), (0, 1)) + self.assertEqual(win.getparyx(), (-1, -1)) + self.assertEqual(win.getmaxyx(), (10, 15)) + self.assertEqual(win2.getbegyx(), (3, 7)) + self.assertEqual(win2.getparyx(), (1, 2)) + self.assertEqual(win2.getmaxyx(), (5, 10)) + self.assertEqual(win3.getbegyx(), (4, 8)) + self.assertEqual(win3.getparyx(), (2, 3)) + self.assertEqual(win3.getmaxyx(), (6, 11)) + + win2.mvderwin(2, 1) + self.assertEqual(win2.getbegyx(), (3, 7)) + self.assertEqual(win2.getparyx(), (2, 1)) + self.assertEqual(win2.getmaxyx(), (5, 10)) + + win3.mvderwin(2, 1) + self.assertEqual(win3.getbegyx(), (4, 8)) + self.assertEqual(win3.getparyx(), (2, 1)) + self.assertEqual(win3.getmaxyx(), (6, 11)) + + def test_move_cursor(self): + stdscr = self.stdscr + win = stdscr.subwin(10, 15, 2, 5) + stdscr.move(1, 2) + win.move(2, 4) + self.assertEqual(stdscr.getyx(), (1, 2)) + self.assertEqual(win.getyx(), (2, 4)) - def tearDown(self): - curses.resetty() - curses.endwin() - self.save_signals.restore() + win.cursyncup() + self.assertEqual(stdscr.getyx(), (4, 9)) + + def test_refresh_control(self): + stdscr = self.stdscr + # touchwin()/untouchwin()/is_wintouched() + stdscr.refresh() + self.assertIs(stdscr.is_wintouched(), False) + stdscr.touchwin() + self.assertIs(stdscr.is_wintouched(), True) + stdscr.refresh() + self.assertIs(stdscr.is_wintouched(), False) + stdscr.touchwin() + self.assertIs(stdscr.is_wintouched(), True) + stdscr.untouchwin() + self.assertIs(stdscr.is_wintouched(), False) + + # touchline()/untouchline()/is_linetouched() + stdscr.touchline(5, 2) + self.assertIs(stdscr.is_linetouched(5), True) + self.assertIs(stdscr.is_linetouched(6), True) + self.assertIs(stdscr.is_wintouched(), True) + stdscr.touchline(5, 1, False) + self.assertIs(stdscr.is_linetouched(5), False) + + # syncup() + win = stdscr.subwin(10, 15, 2, 5) + win2 = win.subwin(5, 10, 3, 7) + win2.touchwin() + stdscr.untouchwin() + win2.syncup() + self.assertIs(win.is_wintouched(), True) + self.assertIs(stdscr.is_wintouched(), True) + + # syncdown() + stdscr.touchwin() + win.untouchwin() + win2.untouchwin() + win2.syncdown() + self.assertIs(win2.is_wintouched(), True) + + # syncok() + if hasattr(stdscr, 'syncok') and not sys.platform.startswith("sunos"): + win.untouchwin() + stdscr.untouchwin() + for syncok in [False, True]: + win2.syncok(syncok) + win2.addch('a') + self.assertIs(win.is_wintouched(), syncok) + self.assertIs(stdscr.is_wintouched(), syncok) + + def test_output_character(self): + stdscr = self.stdscr + encoding = stdscr.encoding + # addch() + stdscr.refresh() + stdscr.move(0, 0) + stdscr.addch('A') + stdscr.addch(b'A') + stdscr.addch(65) + c = '\u20ac' + try: + stdscr.addch(c) + except UnicodeEncodeError: + self.assertRaises(UnicodeEncodeError, c.encode, encoding) + except OverflowError: + encoded = c.encode(encoding) + self.assertNotEqual(len(encoded), 1, repr(encoded)) + stdscr.addch('A', curses.A_BOLD) + stdscr.addch(1, 2, 'A') + stdscr.addch(2, 3, 'A', curses.A_BOLD) + self.assertIs(stdscr.is_wintouched(), True) + + # echochar() + stdscr.refresh() + stdscr.move(0, 0) + stdscr.echochar('A') + stdscr.echochar(b'A') + stdscr.echochar(65) + with self.assertRaises((UnicodeEncodeError, OverflowError)): + stdscr.echochar('\u20ac') + stdscr.echochar('A', curses.A_BOLD) + self.assertIs(stdscr.is_wintouched(), False) + + def test_output_string(self): + stdscr = self.stdscr + encoding = stdscr.encoding + # addstr()/insstr() + for func in [stdscr.addstr, stdscr.insstr]: + with self.subTest(func.__qualname__): + stdscr.move(0, 0) + func('abcd') + func(b'abcd') + s = 'àßçđ' + try: + func(s) + except UnicodeEncodeError: + self.assertRaises(UnicodeEncodeError, s.encode, encoding) + func('abcd', curses.A_BOLD) + func(1, 2, 'abcd') + func(2, 3, 'abcd', curses.A_BOLD) + + # addnstr()/insnstr() + for func in [stdscr.addnstr, stdscr.insnstr]: + with self.subTest(func.__qualname__): + stdscr.move(0, 0) + func('1234', 3) + func(b'1234', 3) + s = '\u0661\u0662\u0663\u0664' + try: + func(s, 3) + except UnicodeEncodeError: + self.assertRaises(UnicodeEncodeError, s.encode, encoding) + func('1234', 5) + func('1234', 3, curses.A_BOLD) + func(1, 2, '1234', 3) + func(2, 3, '1234', 3, curses.A_BOLD) + + def test_output_string_embedded_null_chars(self): + # reject embedded null bytes and characters + stdscr = self.stdscr + for arg in ['a\0', b'a\0']: + with self.subTest(arg=arg): + self.assertRaises(ValueError, stdscr.addstr, arg) + self.assertRaises(ValueError, stdscr.addnstr, arg, 1) + self.assertRaises(ValueError, stdscr.insstr, arg) + self.assertRaises(ValueError, stdscr.insnstr, arg, 1) - def test_window_funcs(self): - "Test the methods of windows" + def test_read_from_window(self): stdscr = self.stdscr - win = curses.newwin(10,10) - win = curses.newwin(5,5, 5,5) - win2 = curses.newwin(15,15, 5,5) - - for meth in [stdscr.addch, stdscr.addstr]: - for args in [('a',), ('a', curses.A_BOLD), - (4,4, 'a'), (5,5, 'a', curses.A_BOLD)]: - with self.subTest(meth=meth.__qualname__, args=args): - meth(*args) - - for meth in [stdscr.clear, stdscr.clrtobot, - stdscr.clrtoeol, stdscr.cursyncup, stdscr.delch, - stdscr.deleteln, stdscr.erase, stdscr.getbegyx, - stdscr.getbkgd, stdscr.getkey, stdscr.getmaxyx, - stdscr.getparyx, stdscr.getyx, stdscr.inch, - stdscr.insertln, stdscr.instr, stdscr.is_wintouched, - win.noutrefresh, stdscr.redrawwin, stdscr.refresh, - stdscr.standout, stdscr.standend, stdscr.syncdown, - stdscr.syncup, stdscr.touchwin, stdscr.untouchwin]: - with self.subTest(meth=meth.__qualname__): - meth() - - stdscr.addnstr('1234', 3) - stdscr.addnstr('1234', 3, curses.A_BOLD) - stdscr.addnstr(4,4, '1234', 3) - stdscr.addnstr(5,5, '1234', 3, curses.A_BOLD) - - stdscr.attron(curses.A_BOLD) - stdscr.attroff(curses.A_BOLD) - stdscr.attrset(curses.A_BOLD) - stdscr.bkgd(' ') - stdscr.bkgd(' ', curses.A_REVERSE) - stdscr.bkgdset(' ') - stdscr.bkgdset(' ', curses.A_REVERSE) + stdscr.addstr(0, 1, 'ABCD', curses.A_BOLD) + # inch() + stdscr.move(0, 1) + self.assertEqual(stdscr.inch(), 65 | curses.A_BOLD) + self.assertEqual(stdscr.inch(0, 3), 67 | curses.A_BOLD) + stdscr.move(0, 0) + # instr() + self.assertEqual(stdscr.instr()[:6], b' ABCD ') + self.assertEqual(stdscr.instr(3)[:6], b' AB') + self.assertEqual(stdscr.instr(0, 2)[:4], b'BCD ') + self.assertEqual(stdscr.instr(0, 2, 4), b'BCD ') + self.assertRaises(ValueError, stdscr.instr, -2) + self.assertRaises(ValueError, stdscr.instr, 0, 2, -2) + + def test_getch(self): + win = curses.newwin(5, 12, 5, 2) + + # TODO: Test with real input by writing to master fd. + for c in 'spam\n'[::-1]: + curses.ungetch(c) + self.assertEqual(win.getch(3, 1), b's'[0]) + self.assertEqual(win.getyx(), (3, 1)) + self.assertEqual(win.getch(3, 4), b'p'[0]) + self.assertEqual(win.getyx(), (3, 4)) + self.assertEqual(win.getch(), b'a'[0]) + self.assertEqual(win.getyx(), (3, 4)) + self.assertEqual(win.getch(), b'm'[0]) + self.assertEqual(win.getch(), b'\n'[0]) + + def test_getstr(self): + win = curses.newwin(5, 12, 5, 2) + curses.echo() + self.addCleanup(curses.noecho) + + self.assertRaises(ValueError, win.getstr, -400) + self.assertRaises(ValueError, win.getstr, 2, 3, -400) + + # TODO: Test with real input by writing to master fd. + for c in 'Lorem\nipsum\ndolor\nsit\namet\n'[::-1]: + curses.ungetch(c) + self.assertEqual(win.getstr(3, 1, 2), b'Lo') + self.assertEqual(win.instr(3, 0), b' Lo ') + self.assertEqual(win.getstr(3, 5, 10), b'ipsum') + self.assertEqual(win.instr(3, 0), b' Lo ipsum ') + self.assertEqual(win.getstr(1, 5), b'dolor') + self.assertEqual(win.instr(1, 0), b' dolor ') + self.assertEqual(win.getstr(2), b'si') + self.assertEqual(win.instr(1, 0), b'si dolor ') + self.assertEqual(win.getstr(), b'amet') + self.assertEqual(win.instr(1, 0), b'amet dolor ') + + def test_clear(self): + win = curses.newwin(5, 15, 5, 2) + lorem_ipsum(win) + + win.move(0, 8) + win.clrtoeol() + self.assertEqual(win.instr(0, 0).rstrip(), b'Lorem ip') + self.assertEqual(win.instr(1, 0).rstrip(), b'dolor sit amet,') + + win.move(0, 3) + win.clrtobot() + self.assertEqual(win.instr(0, 0).rstrip(), b'Lor') + self.assertEqual(win.instr(1, 0).rstrip(), b'') + + for func in [win.erase, win.clear]: + lorem_ipsum(win) + func() + self.assertEqual(win.instr(0, 0).rstrip(), b'') + self.assertEqual(win.instr(1, 0).rstrip(), b'') + + def test_insert_delete(self): + win = curses.newwin(5, 15, 5, 2) + lorem_ipsum(win) + + win.move(0, 2) + win.delch() + self.assertEqual(win.instr(0, 0), b'Loem ipsum ') + win.delch(0, 7) + self.assertEqual(win.instr(0, 0), b'Loem ipum ') + + win.move(1, 5) + win.deleteln() + self.assertEqual(win.instr(0, 0), b'Loem ipum ') + self.assertEqual(win.instr(1, 0), b'consectetur ') + self.assertEqual(win.instr(2, 0), b'adipiscing elit') + self.assertEqual(win.instr(3, 0), b'sed do eiusmod ') + self.assertEqual(win.instr(4, 0), b' ') + + win.move(1, 5) + win.insertln() + self.assertEqual(win.instr(0, 0), b'Loem ipum ') + self.assertEqual(win.instr(1, 0), b' ') + self.assertEqual(win.instr(2, 0), b'consectetur ') + + win.clear() + lorem_ipsum(win) + win.move(1, 5) + win.insdelln(2) + self.assertEqual(win.instr(0, 0), b'Lorem ipsum ') + self.assertEqual(win.instr(1, 0), b' ') + self.assertEqual(win.instr(2, 0), b' ') + self.assertEqual(win.instr(3, 0), b'dolor sit amet,') + + win.clear() + lorem_ipsum(win) + win.move(1, 5) + win.insdelln(-2) + self.assertEqual(win.instr(0, 0), b'Lorem ipsum ') + self.assertEqual(win.instr(1, 0), b'adipiscing elit') + self.assertEqual(win.instr(2, 0), b'sed do eiusmod ') + self.assertEqual(win.instr(3, 0), b' ') + + def test_scroll(self): + win = curses.newwin(5, 15, 5, 2) + lorem_ipsum(win) + win.scrollok(True) + win.scroll() + self.assertEqual(win.instr(0, 0), b'dolor sit amet,') + win.scroll(2) + self.assertEqual(win.instr(0, 0), b'adipiscing elit') + win.scroll(-3) + self.assertEqual(win.instr(0, 0), b' ') + self.assertEqual(win.instr(2, 0), b' ') + self.assertEqual(win.instr(3, 0), b'adipiscing elit') + win.scrollok(False) + + def test_attributes(self): + # TODO: attr_get(), attr_set(), ... + win = curses.newwin(5, 15, 5, 2) + win.attron(curses.A_BOLD) + win.attroff(curses.A_BOLD) + win.attrset(curses.A_BOLD) + + win.standout() + win.standend() + + @requires_curses_window_meth('chgat') + def test_chgat(self): + win = curses.newwin(5, 15, 5, 2) + win.addstr(2, 0, 'Lorem ipsum') + win.addstr(3, 0, 'dolor sit amet') + + win.move(2, 8) + win.chgat(curses.A_BLINK) + self.assertEqual(win.inch(2, 7), b'p'[0]) + self.assertEqual(win.inch(2, 8), b's'[0] | curses.A_BLINK) + self.assertEqual(win.inch(2, 14), b' '[0] | curses.A_BLINK) + + win.move(2, 1) + win.chgat(3, curses.A_BOLD) + self.assertEqual(win.inch(2, 0), b'L'[0]) + self.assertEqual(win.inch(2, 1), b'o'[0] | curses.A_BOLD) + self.assertEqual(win.inch(2, 3), b'e'[0] | curses.A_BOLD) + self.assertEqual(win.inch(2, 4), b'm'[0]) + + win.chgat(3, 2, curses.A_UNDERLINE) + self.assertEqual(win.inch(3, 1), b'o'[0]) + self.assertEqual(win.inch(3, 2), b'l'[0] | curses.A_UNDERLINE) + self.assertEqual(win.inch(3, 14), b' '[0] | curses.A_UNDERLINE) + + win.chgat(3, 4, 7, curses.A_BLINK) + self.assertEqual(win.inch(3, 3), b'o'[0] | curses.A_UNDERLINE) + self.assertEqual(win.inch(3, 4), b'r'[0] | curses.A_BLINK) + self.assertEqual(win.inch(3, 10), b'a'[0] | curses.A_BLINK) + self.assertEqual(win.inch(3, 11), b'm'[0] | curses.A_UNDERLINE) + self.assertEqual(win.inch(3, 14), b' '[0] | curses.A_UNDERLINE) + + def test_background(self): + win = curses.newwin(5, 15, 5, 2) + win.addstr(0, 0, 'Lorem ipsum') + + self.assertIn(win.getbkgd(), (0, 32)) + + # bkgdset() + win.bkgdset('_') + self.assertEqual(win.getbkgd(), b'_'[0]) + win.bkgdset(b'#') + self.assertEqual(win.getbkgd(), b'#'[0]) + win.bkgdset(65) + self.assertEqual(win.getbkgd(), 65) + win.bkgdset(0) + self.assertEqual(win.getbkgd(), 32) + + win.bkgdset('#', curses.A_REVERSE) + self.assertEqual(win.getbkgd(), b'#'[0] | curses.A_REVERSE) + self.assertEqual(win.inch(0, 0), b'L'[0]) + self.assertEqual(win.inch(0, 5), b' '[0]) + win.bkgdset(0) + + # bkgd() + win.bkgd('_') + self.assertEqual(win.getbkgd(), b'_'[0]) + self.assertEqual(win.inch(0, 0), b'L'[0]) + self.assertEqual(win.inch(0, 5), b'_'[0]) + + win.bkgd('#', curses.A_REVERSE) + self.assertEqual(win.getbkgd(), b'#'[0] | curses.A_REVERSE) + self.assertEqual(win.inch(0, 0), b'L'[0] | curses.A_REVERSE) + self.assertEqual(win.inch(0, 5), b'#'[0] | curses.A_REVERSE) + + def test_overlay(self): + srcwin = curses.newwin(5, 18, 3, 4) + lorem_ipsum(srcwin) + dstwin = curses.newwin(7, 17, 5, 7) + for i in range(6): + dstwin.addstr(i, 0, '_'*17) + + srcwin.overlay(dstwin) + self.assertEqual(dstwin.instr(0, 0), b'sectetur_________') + self.assertEqual(dstwin.instr(1, 0), b'piscing_elit,____') + self.assertEqual(dstwin.instr(2, 0), b'_do_eiusmod______') + self.assertEqual(dstwin.instr(3, 0), b'_________________') + + srcwin.overwrite(dstwin) + self.assertEqual(dstwin.instr(0, 0), b'sectetur __') + self.assertEqual(dstwin.instr(1, 0), b'piscing elit, __') + self.assertEqual(dstwin.instr(2, 0), b' do eiusmod __') + self.assertEqual(dstwin.instr(3, 0), b'_________________') + + srcwin.overlay(dstwin, 1, 4, 3, 2, 4, 11) + self.assertEqual(dstwin.instr(3, 0), b'__r_sit_amet_____') + self.assertEqual(dstwin.instr(4, 0), b'__ectetur________') + self.assertEqual(dstwin.instr(5, 0), b'_________________') + + srcwin.overwrite(dstwin, 1, 4, 3, 2, 4, 11) + self.assertEqual(dstwin.instr(3, 0), b'__r sit amet_____') + self.assertEqual(dstwin.instr(4, 0), b'__ectetur _____') + self.assertEqual(dstwin.instr(5, 0), b'_________________') + + def test_refresh(self): + win = curses.newwin(5, 15, 2, 5) + win.noutrefresh() + win.redrawln(1, 2) + win.redrawwin() + win.refresh() + curses.doupdate() + + @requires_curses_window_meth('resize') + def test_resize(self): + win = curses.newwin(5, 15, 2, 5) + win.resize(4, 20) + self.assertEqual(win.getmaxyx(), (4, 20)) + win.resize(5, 15) + self.assertEqual(win.getmaxyx(), (5, 15)) + + @requires_curses_window_meth('enclose') + def test_enclose(self): + win = curses.newwin(5, 15, 2, 5) + # TODO: Return bool instead of 1/0 + self.assertTrue(win.enclose(2, 5)) + self.assertFalse(win.enclose(1, 5)) + self.assertFalse(win.enclose(2, 4)) + self.assertTrue(win.enclose(6, 19)) + self.assertFalse(win.enclose(7, 19)) + self.assertFalse(win.enclose(6, 20)) + + def test_putwin(self): + win = curses.newwin(5, 12, 1, 2) + win.addstr(2, 1, 'Lorem ipsum') + with tempfile.TemporaryFile() as f: + win.putwin(f) + del win + f.seek(0) + win = curses.getwin(f) + self.assertEqual(win.getbegyx(), (1, 2)) + self.assertEqual(win.getmaxyx(), (5, 12)) + self.assertEqual(win.instr(2, 0), b' Lorem ipsum') - win.border(65, 66, 67, 68, - 69, 70, 71, 72) + def test_borders_and_lines(self): + win = curses.newwin(5, 10, 5, 2) win.border('|', '!', '-', '_', '+', '\\', '#', '/') - with self.assertRaises(TypeError, - msg="Expected win.border() to raise TypeError"): - win.border(65, 66, 67, 68, - 69, [], 71, 72) - - win.box(65, 67) - win.box('!', '_') + self.assertEqual(win.instr(0, 0), b'+--------\\') + self.assertEqual(win.instr(1, 0), b'| !') + self.assertEqual(win.instr(4, 0), b'#________/') + win.border(b'|', b'!', b'-', b'_', + b'+', b'\\', b'#', b'/') + win.border(65, 66, 67, 68, + 69, 70, 71, 72) + self.assertRaises(TypeError, win.border, + 65, 66, 67, 68, 69, [], 71, 72) + self.assertRaises(TypeError, win.border, + 65, 66, 67, 68, 69, 70, 71, 72, 73) + self.assertRaises(TypeError, win.border, + 65, 66, 67, 68, 69, 70, 71, 72, 73) + win.border(65, 66, 67, 68, 69, 70, 71) + win.border(65, 66, 67, 68, 69, 70) + win.border(65, 66, 67, 68, 69) + win.border(65, 66, 67, 68) + win.border(65, 66, 67) + win.border(65, 66) + win.border(65) + win.border() + + win.box(':', '~') + self.assertEqual(win.instr(0, 1, 8), b'~~~~~~~~') + self.assertEqual(win.instr(1, 0), b': :') + self.assertEqual(win.instr(4, 1, 8), b'~~~~~~~~') win.box(b':', b'~') + win.box(65, 67) self.assertRaises(TypeError, win.box, 65, 66, 67) self.assertRaises(TypeError, win.box, 65) win.box() - stdscr.clearok(1) + win.move(1, 2) + win.hline('-', 5) + self.assertEqual(win.instr(1, 1, 7), b' ----- ') + win.hline(b'-', 5) + win.hline(45, 5) + win.hline('-', 5, curses.A_BOLD) + win.hline(1, 1, '-', 5) + win.hline(1, 1, '-', 5, curses.A_BOLD) + + win.move(1, 2) + win.vline('a', 3) + win.vline(b'a', 3) + win.vline(97, 3) + win.vline('a', 3, curses.A_STANDOUT) + win.vline(1, 1, 'a', 3) + win.vline(1, 1, ';', 2, curses.A_STANDOUT) + self.assertEqual(win.inch(1, 1), b';'[0] | curses.A_STANDOUT) + self.assertEqual(win.inch(2, 1), b';'[0] | curses.A_STANDOUT) + self.assertEqual(win.inch(3, 1), b'a'[0]) + + def test_unctrl(self): + # TODO: wunctrl() + self.assertEqual(curses.unctrl(b'A'), b'A') + self.assertEqual(curses.unctrl('A'), b'A') + self.assertEqual(curses.unctrl(65), b'A') + self.assertEqual(curses.unctrl(b'\n'), b'^J') + self.assertEqual(curses.unctrl('\n'), b'^J') + self.assertEqual(curses.unctrl(10), b'^J') + self.assertRaises(TypeError, curses.unctrl, b'') + self.assertRaises(TypeError, curses.unctrl, b'AB') + self.assertRaises(TypeError, curses.unctrl, '') + self.assertRaises(TypeError, curses.unctrl, 'AB') + self.assertRaises(OverflowError, curses.unctrl, 2**64) + + def test_endwin(self): + if not self.isatty: + self.skipTest('requires terminal') + self.assertIs(curses.isendwin(), False) + curses.endwin() + self.assertIs(curses.isendwin(), True) + curses.doupdate() + self.assertIs(curses.isendwin(), False) + + def test_terminfo(self): + self.assertIsInstance(curses.tigetflag('hc'), int) + self.assertEqual(curses.tigetflag('cols'), -1) + self.assertEqual(curses.tigetflag('cr'), -1) + + self.assertIsInstance(curses.tigetnum('cols'), int) + self.assertEqual(curses.tigetnum('hc'), -2) + self.assertEqual(curses.tigetnum('cr'), -2) + + self.assertIsInstance(curses.tigetstr('cr'), (bytes, type(None))) + self.assertIsNone(curses.tigetstr('hc')) + self.assertIsNone(curses.tigetstr('cols')) + + cud = curses.tigetstr('cud') + if cud is not None: + # See issue10570. + self.assertIsInstance(cud, bytes) + curses.tparm(cud, 2) + cud_2 = curses.tparm(cud, 2) + self.assertIsInstance(cud_2, bytes) + curses.putp(cud_2) + + curses.putp(b'abc\n') + + def test_misc_module_funcs(self): + curses.delay_output(1) + curses.flushinp() + + curses.doupdate() + self.assertIs(curses.isendwin(), False) - win4 = stdscr.derwin(2,2) - win4 = stdscr.derwin(1,1, 5,5) - win4.mvderwin(9,9) + curses.napms(100) + + curses.newpad(50, 50) + + def test_env_queries(self): + # TODO: term_attrs(), erasewchar(), killwchar() + self.assertIsInstance(curses.termname(), bytes) + self.assertIsInstance(curses.longname(), bytes) + self.assertIsInstance(curses.baudrate(), int) + self.assertIsInstance(curses.has_ic(), bool) + self.assertIsInstance(curses.has_il(), bool) + self.assertIsInstance(curses.termattrs(), int) + + c = curses.killchar() + self.assertIsInstance(c, bytes) + self.assertEqual(len(c), 1) + c = curses.erasechar() + self.assertIsInstance(c, bytes) + self.assertEqual(len(c), 1) + + def test_output_options(self): + stdscr = self.stdscr + + stdscr.clearok(True) + stdscr.clearok(False) - stdscr.echochar('a') - stdscr.echochar('a', curses.A_BOLD) - stdscr.hline('-', 5) - stdscr.hline('-', 5, curses.A_BOLD) - stdscr.hline(1,1,'-', 5) - stdscr.hline(1,1,'-', 5, curses.A_BOLD) + stdscr.idcok(True) + stdscr.idcok(False) + + stdscr.idlok(False) + stdscr.idlok(True) - stdscr.idcok(1) - stdscr.idlok(1) if hasattr(stdscr, 'immedok'): - stdscr.immedok(1) - stdscr.immedok(0) - stdscr.insch('c') - stdscr.insdelln(1) - stdscr.insnstr('abc', 3) - stdscr.insnstr('abc', 3, curses.A_BOLD) - stdscr.insnstr(5, 5, 'abc', 3) - stdscr.insnstr(5, 5, 'abc', 3, curses.A_BOLD) - - stdscr.insstr('def') - stdscr.insstr('def', curses.A_BOLD) - stdscr.insstr(5, 5, 'def') - stdscr.insstr(5, 5, 'def', curses.A_BOLD) - stdscr.is_linetouched(0) - stdscr.keypad(1) - stdscr.leaveok(1) - stdscr.move(3,3) - win.mvwin(2,2) - stdscr.nodelay(1) - stdscr.notimeout(1) - win2.overlay(win) - win2.overwrite(win) - win2.overlay(win, 1, 2, 2, 1, 3, 3) - win2.overwrite(win, 1, 2, 2, 1, 3, 3) - stdscr.redrawln(1,2) - - stdscr.scrollok(1) - stdscr.scroll() - stdscr.scroll(2) - stdscr.scroll(-3) - - stdscr.move(12, 2) - stdscr.setscrreg(10,15) - win3 = stdscr.subwin(10,10) - win3 = stdscr.subwin(10,10, 5,5) - if hasattr(stdscr, 'syncok') and not sys.platform.startswith("sunos"): - stdscr.syncok(1) - stdscr.timeout(5) - stdscr.touchline(5,5) - stdscr.touchline(5,5,0) - stdscr.vline('a', 3) - stdscr.vline('a', 3, curses.A_STANDOUT) - if hasattr(stdscr, 'chgat'): - stdscr.chgat(5, 2, 3, curses.A_BLINK) - stdscr.chgat(3, curses.A_BOLD) - stdscr.chgat(5, 8, curses.A_UNDERLINE) - stdscr.chgat(curses.A_BLINK) - stdscr.refresh() + stdscr.immedok(True) + stdscr.immedok(False) - stdscr.vline(1,1, 'a', 3) - stdscr.vline(1,1, 'a', 3, curses.A_STANDOUT) + stdscr.leaveok(True) + stdscr.leaveok(False) - if hasattr(stdscr, 'resize'): - stdscr.resize(25, 80) - if hasattr(stdscr, 'enclose'): - stdscr.enclose(10, 10) + stdscr.scrollok(True) + stdscr.scrollok(False) - self.assertRaises(ValueError, stdscr.getstr, -400) - self.assertRaises(ValueError, stdscr.getstr, 2, 3, -400) - self.assertRaises(ValueError, stdscr.instr, -2) - self.assertRaises(ValueError, stdscr.instr, 2, 3, -2) + stdscr.setscrreg(5, 10) - def test_embedded_null_chars(self): - # reject embedded null bytes and characters + curses.nonl() + curses.nl(True) + curses.nl(False) + curses.nl() + + + def test_input_options(self): stdscr = self.stdscr - for arg in ['a', b'a']: - with self.subTest(arg=arg): - self.assertRaises(ValueError, stdscr.addstr, 'a\0') - self.assertRaises(ValueError, stdscr.addnstr, 'a\0', 1) - self.assertRaises(ValueError, stdscr.insstr, 'a\0') - self.assertRaises(ValueError, stdscr.insnstr, 'a\0', 1) - - def test_module_funcs(self): - "Test module-level functions" - for func in [curses.baudrate, curses.beep, curses.can_change_color, - curses.cbreak, curses.def_prog_mode, curses.doupdate, - curses.flash, curses.flushinp, - curses.has_colors, curses.has_ic, curses.has_il, - curses.isendwin, curses.killchar, curses.longname, - curses.nocbreak, curses.noecho, curses.nonl, - curses.noqiflush, curses.noraw, - curses.reset_prog_mode, curses.termattrs, - curses.termname, curses.erasechar]: - with self.subTest(func=func.__qualname__): - func() - if hasattr(curses, 'filter'): - curses.filter() - if hasattr(curses, 'getsyx'): - curses.getsyx() - - # Functions that actually need arguments - if curses.tigetstr("cnorm"): - curses.curs_set(1) - curses.delay_output(1) - curses.echo() ; curses.echo(1) - with tempfile.TemporaryFile() as f: - self.stdscr.putwin(f) - f.seek(0) - curses.getwin(f) + if self.isatty: + curses.nocbreak() + curses.cbreak() + curses.cbreak(False) + curses.cbreak(True) + + curses.intrflush(True) + curses.intrflush(False) + + curses.raw() + curses.raw(False) + curses.raw(True) + curses.noraw() + curses.noecho() + curses.echo() + curses.echo(False) + curses.echo(True) + + curses.halfdelay(255) curses.halfdelay(1) - curses.intrflush(1) - curses.meta(1) - curses.napms(100) - curses.newpad(50,50) - win = curses.newwin(5,5) - win = curses.newwin(5,5, 1,1) - curses.nl() ; curses.nl(1) - curses.putp(b'abc') + + stdscr.keypad(True) + stdscr.keypad(False) + + curses.meta(True) + curses.meta(False) + + stdscr.nodelay(True) + stdscr.nodelay(False) + + curses.noqiflush() + curses.qiflush(True) + curses.qiflush(False) curses.qiflush() - curses.raw() ; curses.raw(1) + + stdscr.notimeout(True) + stdscr.notimeout(False) + + stdscr.timeout(-1) + stdscr.timeout(0) + stdscr.timeout(5) + + @requires_curses_func('typeahead') + def test_typeahead(self): + curses.typeahead(sys.__stdin__.fileno()) + curses.typeahead(-1) + + def test_prog_mode(self): + if not self.isatty: + self.skipTest('requires terminal') + curses.def_prog_mode() + curses.reset_prog_mode() + + def test_beep(self): + if (curses.tigetstr("bel") is not None + or curses.tigetstr("flash") is not None): + curses.beep() + else: + try: + curses.beep() + except curses.error: + self.skipTest('beep() failed') + + def test_flash(self): + if (curses.tigetstr("bel") is not None + or curses.tigetstr("flash") is not None): + curses.flash() + else: + try: + curses.flash() + except curses.error: + self.skipTest('flash() failed') + + def test_curs_set(self): + for vis, cap in [(0, 'civis'), (2, 'cvvis'), (1, 'cnorm')]: + if curses.tigetstr(cap) is not None: + curses.curs_set(vis) + else: + try: + curses.curs_set(vis) + except curses.error: + pass + + @requires_curses_func('get_escdelay') + def test_escdelay(self): + escdelay = curses.get_escdelay() + self.assertIsInstance(escdelay, int) curses.set_escdelay(25) self.assertEqual(curses.get_escdelay(), 25) + curses.set_escdelay(escdelay) + + @requires_curses_func('get_tabsize') + def test_tabsize(self): + tabsize = curses.get_tabsize() + self.assertIsInstance(tabsize, int) curses.set_tabsize(4) self.assertEqual(curses.get_tabsize(), 4) - if hasattr(curses, 'setsyx'): - curses.setsyx(5,5) - curses.tigetflag('hc') - curses.tigetnum('co') - curses.tigetstr('cr') - curses.tparm(b'cr') - if hasattr(curses, 'typeahead'): - curses.typeahead(sys.__stdin__.fileno()) - curses.unctrl('a') - curses.ungetch('a') - if hasattr(curses, 'use_env'): - curses.use_env(1) - - # Functions only available on a few platforms - def test_colors_funcs(self): + curses.set_tabsize(tabsize) + + @requires_curses_func('getsyx') + def test_getsyx(self): + y, x = curses.getsyx() + self.assertIsInstance(y, int) + self.assertIsInstance(x, int) + curses.setsyx(4, 5) + self.assertEqual(curses.getsyx(), (4, 5)) + + def bad_colors(self): + return (-2**31 - 1, 2**31, -2**63 - 1, 2**63, 2**64) + + def bad_pairs(self): + return (-2**31 - 1, 2**31, -2**63 - 1, 2**63, 2**64) + + def test_has_colors(self): + self.assertIsInstance(curses.has_colors(), bool) + self.assertIsInstance(curses.can_change_color(), bool) + + def test_start_color(self): if not curses.has_colors(): self.skipTest('requires colors support') curses.start_color() - curses.init_pair(2, 1,1) - curses.color_content(1) - curses.color_pair(2) - curses.pair_content(curses.COLOR_PAIRS - 1) - curses.pair_number(0) - - if hasattr(curses, 'use_default_colors'): + if verbose: + print(f'COLORS = {curses.COLORS}', file=sys.stderr) + print(f'COLOR_PAIRS = {curses.COLOR_PAIRS}', file=sys.stderr) + + @requires_colors + def test_color_content(self): + self.assertEqual(curses.color_content(curses.COLOR_BLACK), (0, 0, 0)) + curses.color_content(0) + maxcolor = min(curses.COLORS - 1, SHORT_MAX) + curses.color_content(maxcolor) + + for color in self.bad_colors(): + self.assertRaises(OverflowError, curses.color_content, color) + if curses.COLORS <= SHORT_MAX: + self.assertRaises(curses.error, curses.color_content, curses.COLORS) + self.assertRaises(curses.error, curses.color_content, -1) + + @requires_colors + def test_init_color(self): + if not curses.can_change_color(): + self.skipTest('cannot change color') + + old = curses.color_content(0) + try: + curses.init_color(0, *old) + except curses.error: + self.skipTest('cannot change color (init_color() failed)') + self.addCleanup(curses.init_color, 0, *old) + curses.init_color(0, 0, 0, 0) + self.assertEqual(curses.color_content(0), (0, 0, 0)) + curses.init_color(0, 1000, 1000, 1000) + self.assertEqual(curses.color_content(0), (1000, 1000, 1000)) + + maxcolor = min(curses.COLORS - 1, SHORT_MAX) + old = curses.color_content(maxcolor) + curses.init_color(maxcolor, *old) + self.addCleanup(curses.init_color, maxcolor, *old) + curses.init_color(maxcolor, 0, 500, 1000) + self.assertEqual(curses.color_content(maxcolor), (0, 500, 1000)) + + for color in self.bad_colors(): + self.assertRaises(OverflowError, curses.init_color, color, 0, 0, 0) + if curses.COLORS <= SHORT_MAX: + self.assertRaises(curses.error, curses.init_color, curses.COLORS, 0, 0, 0) + self.assertRaises(curses.error, curses.init_color, -1, 0, 0, 0) + for comp in (-1, 1001): + self.assertRaises(curses.error, curses.init_color, 0, comp, 0, 0) + self.assertRaises(curses.error, curses.init_color, 0, 0, comp, 0) + self.assertRaises(curses.error, curses.init_color, 0, 0, 0, comp) + + def get_pair_limit(self): + return min(curses.COLOR_PAIRS, SHORT_MAX) + + @requires_colors + def test_pair_content(self): + if not hasattr(curses, 'use_default_colors'): + self.assertEqual(curses.pair_content(0), + (curses.COLOR_WHITE, curses.COLOR_BLACK)) + curses.pair_content(0) + maxpair = self.get_pair_limit() - 1 + if maxpair > 0: + curses.pair_content(maxpair) + + for pair in self.bad_pairs(): + self.assertRaises(OverflowError, curses.pair_content, pair) + self.assertRaises(curses.error, curses.pair_content, -1) + + @requires_colors + def test_init_pair(self): + old = curses.pair_content(1) + curses.init_pair(1, *old) + self.addCleanup(curses.init_pair, 1, *old) + + curses.init_pair(1, 0, 0) + self.assertEqual(curses.pair_content(1), (0, 0)) + maxcolor = min(curses.COLORS - 1, SHORT_MAX) + curses.init_pair(1, maxcolor, 0) + self.assertEqual(curses.pair_content(1), (maxcolor, 0)) + curses.init_pair(1, 0, maxcolor) + self.assertEqual(curses.pair_content(1), (0, maxcolor)) + maxpair = self.get_pair_limit() - 1 + if maxpair > 1: + curses.init_pair(maxpair, 0, 0) + self.assertEqual(curses.pair_content(maxpair), (0, 0)) + + for pair in self.bad_pairs(): + self.assertRaises(OverflowError, curses.init_pair, pair, 0, 0) + self.assertRaises(curses.error, curses.init_pair, -1, 0, 0) + for color in self.bad_colors(): + self.assertRaises(OverflowError, curses.init_pair, 1, color, 0) + self.assertRaises(OverflowError, curses.init_pair, 1, 0, color) + if curses.COLORS <= SHORT_MAX: + self.assertRaises(curses.error, curses.init_pair, 1, curses.COLORS, 0) + self.assertRaises(curses.error, curses.init_pair, 1, 0, curses.COLORS) + + @requires_colors + def test_color_attrs(self): + for pair in 0, 1, 255: + attr = curses.color_pair(pair) + self.assertEqual(curses.pair_number(attr), pair, attr) + self.assertEqual(curses.pair_number(attr | curses.A_BOLD), pair) + self.assertEqual(curses.color_pair(0), 0) + self.assertEqual(curses.pair_number(0), 0) + + @requires_curses_func('use_default_colors') + @requires_colors + def test_use_default_colors(self): + old = curses.pair_content(0) + try: curses.use_default_colors() + except curses.error: + self.skipTest('cannot change color (use_default_colors() failed)') + self.assertEqual(curses.pair_content(0), (-1, -1)) + self.assertIn(old, [(curses.COLOR_WHITE, curses.COLOR_BLACK), (-1, -1), (0, 0)]) - @requires_curses_func('keyname') def test_keyname(self): - curses.keyname(13) + # TODO: key_name() + self.assertEqual(curses.keyname(65), b'A') + self.assertEqual(curses.keyname(13), b'^M') + self.assertEqual(curses.keyname(127), b'^?') + self.assertEqual(curses.keyname(0), b'^@') + self.assertRaises(ValueError, curses.keyname, -1) + self.assertIsInstance(curses.keyname(256), bytes) @requires_curses_func('has_key') def test_has_key(self): @@ -350,23 +1048,46 @@ class TestCurses(unittest.TestCase): @requires_curses_func('is_term_resized') def test_is_term_resized(self): - curses.is_term_resized(*self.stdscr.getmaxyx()) + lines, cols = curses.LINES, curses.COLS + self.assertIs(curses.is_term_resized(lines, cols), False) + self.assertIs(curses.is_term_resized(lines-1, cols-1), True) @requires_curses_func('resize_term') def test_resize_term(self): - curses.resize_term(*self.stdscr.getmaxyx()) + curses.update_lines_cols() + lines, cols = curses.LINES, curses.COLS + new_lines = lines - 1 + new_cols = cols + 1 + curses.resize_term(new_lines, new_cols) + self.assertEqual(curses.LINES, new_lines) + self.assertEqual(curses.COLS, new_cols) + + curses.resize_term(lines, cols) + self.assertEqual(curses.LINES, lines) + self.assertEqual(curses.COLS, cols) @requires_curses_func('resizeterm') def test_resizeterm(self): - stdscr = self.stdscr + curses.update_lines_cols() lines, cols = curses.LINES, curses.COLS new_lines = lines - 1 new_cols = cols + 1 curses.resizeterm(new_lines, new_cols) - self.assertEqual(curses.LINES, new_lines) self.assertEqual(curses.COLS, new_cols) + curses.resizeterm(lines, cols) + self.assertEqual(curses.LINES, lines) + self.assertEqual(curses.COLS, cols) + + def test_ungetch(self): + curses.ungetch(b'A') + self.assertEqual(self.stdscr.getkey(), 'A') + curses.ungetch('B') + self.assertEqual(self.stdscr.getkey(), 'B') + curses.ungetch(67) + self.assertEqual(self.stdscr.getkey(), 'C') + def test_issue6243(self): curses.ungetch(1025) self.stdscr.getkey() @@ -395,10 +1116,6 @@ class TestCurses(unittest.TestCase): read = stdscr.get_wch() self.assertEqual(read, ch) - def test_issue10570(self): - b = curses.tparm(curses.tigetstr("cup"), 5, 3) - self.assertIs(type(b), bytes) - def test_encoding(self): stdscr = self.stdscr import codecs @@ -438,30 +1155,31 @@ class TestCurses(unittest.TestCase): human_readable_signature = stdscr.addch.__doc__.split("\n")[0] self.assertIn("[y, x,]", human_readable_signature) + @requires_curses_window_meth('resize') def test_issue13051(self): - stdscr = self.stdscr - if not hasattr(stdscr, 'resize'): - raise unittest.SkipTest('requires curses.window.resize') - box = curses.textpad.Textbox(stdscr, insert_mode=True) - lines, cols = stdscr.getmaxyx() - stdscr.resize(lines-2, cols-2) + win = curses.newwin(5, 15, 2, 5) + box = curses.textpad.Textbox(win, insert_mode=True) + lines, cols = win.getmaxyx() + win.resize(lines-2, cols-2) # this may cause infinite recursion, leading to a RuntimeError box._insert_printable_char('a') class MiscTests(unittest.TestCase): - @requires_curses_func('update_lines_cols') def test_update_lines_cols(self): - # this doesn't actually test that LINES and COLS are updated, - # because we can't automate changing them. See Issue #4254 for - # a manual test script. We can only test that the function - # can be called. curses.update_lines_cols() + lines, cols = curses.LINES, curses.COLS + curses.LINES = curses.COLS = 0 + curses.update_lines_cols() + self.assertEqual(curses.LINES, lines) + self.assertEqual(curses.COLS, cols) @requires_curses_func('ncurses_version') def test_ncurses_version(self): v = curses.ncurses_version + if verbose: + print(f'ncurses_version = {curses.ncurses_version}', flush=True) self.assertIsInstance(v[:], tuple) self.assertEqual(len(v), 3) self.assertIsInstance(v[0], int) @@ -477,6 +1195,7 @@ class MiscTests(unittest.TestCase): self.assertGreaterEqual(v.minor, 0) self.assertGreaterEqual(v.patch, 0) + class TestAscii(unittest.TestCase): def test_controlnames(self): @@ -565,5 +1284,21 @@ class TestAscii(unittest.TestCase): self.assertEqual(unctrl(ord('\xc1')), '!A') +def lorem_ipsum(win): + text = [ + 'Lorem ipsum', + 'dolor sit amet,', + 'consectetur', + 'adipiscing elit,', + 'sed do eiusmod', + 'tempor incididunt', + 'ut labore et', + 'dolore magna', + 'aliqua.', + ] + maxy, maxx = win.getmaxyx() + for y, line in enumerate(text[:maxy]): + win.addstr(y, 0, line[:maxx - (y == maxy - 1)]) + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index 9e875da3..c7a191bc 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -4,6 +4,8 @@ import gc import itertools import math import pickle +import random +import string import sys import types import unittest @@ -845,6 +847,14 @@ class ClassPropertiesAndMethods(unittest.TestCase): self.fail("inheriting from ModuleType and str at the same time " "should fail") + # Issue 34805: Verify that definition order is retained + def random_name(): + return ''.join(random.choices(string.ascii_letters, k=10)) + class A: + pass + subclasses = [type(random_name(), (A,), {}) for i in range(100)] + self.assertEqual(A.__subclasses__(), subclasses) + def test_multiple_inheritance(self): # Testing multiple inheritance... class C(object): diff --git a/Lib/test/test_dict.py b/Lib/test/test_dict.py index 6b8596ff..b10c0753 100644 --- a/Lib/test/test_dict.py +++ b/Lib/test/test_dict.py @@ -1422,6 +1422,25 @@ class DictTest(unittest.TestCase): d = CustomReversedDict(pairs) self.assertEqual(pairs[::-1], list(dict(d).items())) + @support.cpython_only + def test_dict_items_result_gc(self): + # bpo-42536: dict.items's tuple-reuse speed trick breaks the GC's + # assumptions about what can be untracked. Make sure we re-track result + # tuples whenever we reuse them. + it = iter({None: []}.items()) + gc.collect() + # That GC collection probably untracked the recycled internal result + # tuple, which is initialized to (None, None). Make sure it's re-tracked + # when it's mutated and returned from __next__: + self.assertTrue(gc.is_tracked(next(it))) + + @support.cpython_only + def test_dict_items_result_gc(self): + # Same as test_dict_items_result_gc above, but reversed. + it = reversed({None: []}.items()) + gc.collect() + self.assertTrue(gc.is_tracked(next(it))) + class CAPITest(unittest.TestCase): diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py index 0e6b1b16..4e229863 100644 --- a/Lib/test/test_enum.py +++ b/Lib/test/test_enum.py @@ -215,6 +215,18 @@ class TestEnum(unittest.TestCase): set(['__class__', '__doc__', '__module__', 'name', 'value', 'invisible']), ) + def test_dir_on_sub_with_behavior_including_instance_dict_on_super(self): + # see issue40084 + class SuperEnum(IntEnum): + def __new__(cls, value, description=""): + obj = int.__new__(cls, value) + obj._value_ = value + obj.description = description + return obj + class SubEnum(SuperEnum): + sample = 5 + self.assertTrue({'description'} <= set(dir(SubEnum.sample))) + def test_enum_in_enum_out(self): Season = self.Season self.assertIs(Season(Season.WINTER), Season.WINTER) @@ -570,12 +582,15 @@ class TestEnum(unittest.TestCase): class Test1Enum(MyMethodEnum, int, MyStrEnum): One = 1 Two = 2 + self.assertTrue(Test1Enum._member_type_ is int) self.assertEqual(str(Test1Enum.One), 'MyStr') + self.assertEqual(format(Test1Enum.One, ''), 'MyStr') # class Test2Enum(MyStrEnum, MyMethodEnum): One = 1 Two = 2 self.assertEqual(str(Test2Enum.One), 'MyStr') + self.assertEqual(format(Test1Enum.One, ''), 'MyStr') def test_inherited_data_type(self): class HexInt(int): @@ -1132,6 +1147,7 @@ class TestEnum(unittest.TestCase): class auto_enum(type(Enum)): def __new__(metacls, cls, bases, classdict): temp = type(classdict)() + temp._cls_name = cls names = set(classdict._member_names) i = 0 for k in classdict._member_names: @@ -1998,6 +2014,32 @@ class TestEnum(unittest.TestCase): REVERT_ALL = "REVERT_ALL" RETRY = "RETRY" + def test_multiple_mixin_inherited(self): + class MyInt(int): + def __new__(cls, value): + return super().__new__(cls, value) + + class HexMixin: + def __repr__(self): + return hex(self) + + class MyIntEnum(HexMixin, MyInt, enum.Enum): + pass + + class Foo(MyIntEnum): + TEST = 1 + self.assertTrue(isinstance(Foo.TEST, MyInt)) + self.assertEqual(repr(Foo.TEST), "0x1") + + class Fee(MyIntEnum): + TEST = 1 + def __new__(cls, value): + value += 1 + member = int.__new__(cls, value) + member._value_ = value + return member + self.assertEqual(Fee.TEST, 2) + def test_empty_globals(self): # bpo-35717: sys._getframe(2).f_globals['__name__'] fails with KeyError # when using compile and exec because f_globals is empty @@ -2007,6 +2049,22 @@ class TestEnum(unittest.TestCase): local_ls = {} exec(code, global_ns, local_ls) + @unittest.skipUnless( + sys.version_info[:2] == (3, 9), + 'private variables are now normal attributes', + ) + def test_warning_for_private_variables(self): + with self.assertWarns(DeprecationWarning): + class Private(Enum): + __corporal = 'Radar' + self.assertEqual(Private._Private__corporal.value, 'Radar') + try: + with self.assertWarns(DeprecationWarning): + class Private(Enum): + __major_ = 'Hoolihan' + except ValueError: + pass + class TestOrder(unittest.TestCase): @@ -2144,6 +2202,11 @@ class TestFlag(unittest.TestCase): self.assertEqual(repr(~(Open.RO | Open.CE)), '') self.assertEqual(repr(~(Open.WO | Open.CE)), '') + def test_format(self): + Perm = self.Perm + self.assertEqual(format(Perm.R, ''), 'Perm.R') + self.assertEqual(format(Perm.R | Perm.X, ''), 'Perm.R|X') + def test_or(self): Perm = self.Perm for i in Perm: @@ -2452,6 +2515,42 @@ class TestFlag(unittest.TestCase): 'at least one thread failed while creating composite members') self.assertEqual(256, len(seen), 'too many composite members created') + def test_init_subclass(self): + class MyEnum(Flag): + def __init_subclass__(cls, **kwds): + super().__init_subclass__(**kwds) + self.assertFalse(cls.__dict__.get('_test', False)) + cls._test1 = 'MyEnum' + # + class TheirEnum(MyEnum): + def __init_subclass__(cls, **kwds): + super(TheirEnum, cls).__init_subclass__(**kwds) + cls._test2 = 'TheirEnum' + class WhoseEnum(TheirEnum): + def __init_subclass__(cls, **kwds): + pass + class NoEnum(WhoseEnum): + ONE = 1 + self.assertEqual(TheirEnum.__dict__['_test1'], 'MyEnum') + self.assertEqual(WhoseEnum.__dict__['_test1'], 'MyEnum') + self.assertEqual(WhoseEnum.__dict__['_test2'], 'TheirEnum') + self.assertFalse(NoEnum.__dict__.get('_test1', False)) + self.assertFalse(NoEnum.__dict__.get('_test2', False)) + # + class OurEnum(MyEnum): + def __init_subclass__(cls, **kwds): + cls._test2 = 'OurEnum' + class WhereEnum(OurEnum): + def __init_subclass__(cls, **kwds): + pass + class NeverEnum(WhereEnum): + ONE = 1 + self.assertEqual(OurEnum.__dict__['_test1'], 'MyEnum') + self.assertFalse(WhereEnum.__dict__.get('_test1', False)) + self.assertEqual(WhereEnum.__dict__['_test2'], 'OurEnum') + self.assertFalse(NeverEnum.__dict__.get('_test1', False)) + self.assertFalse(NeverEnum.__dict__.get('_test2', False)) + class TestIntFlag(unittest.TestCase): """Tests of the IntFlags.""" @@ -2477,6 +2576,7 @@ class TestIntFlag(unittest.TestCase): def test_type(self): Perm = self.Perm + self.assertTrue(Perm._member_type_ is int) Open = self.Open for f in Perm: self.assertTrue(isinstance(f, Perm)) @@ -2556,6 +2656,11 @@ class TestIntFlag(unittest.TestCase): self.assertEqual(repr(~(Open.WO | Open.CE)), '') self.assertEqual(repr(Open(~4)), '') + def test_format(self): + Perm = self.Perm + self.assertEqual(format(Perm.R, ''), '4') + self.assertEqual(format(Perm.R | Perm.X, ''), '5') + def test_or(self): Perm = self.Perm for i in Perm: diff --git a/Lib/test/test_enumerate.py b/Lib/test/test_enumerate.py index 5785cb46..906bfc21 100644 --- a/Lib/test/test_enumerate.py +++ b/Lib/test/test_enumerate.py @@ -2,6 +2,7 @@ import unittest import operator import sys import pickle +import gc from test import support @@ -134,6 +135,18 @@ class EnumerateTestCase(unittest.TestCase, PickleTest): self.assertEqual(len(set(map(id, list(enumerate(self.seq))))), len(self.seq)) self.assertEqual(len(set(map(id, enumerate(self.seq)))), min(1,len(self.seq))) + @support.cpython_only + def test_enumerate_result_gc(self): + # bpo-42536: enumerate's tuple-reuse speed trick breaks the GC's + # assumptions about what can be untracked. Make sure we re-track result + # tuples whenever we reuse them. + it = self.enum([[]]) + gc.collect() + # That GC collection probably untracked the recycled internal result + # tuple, which is initialized to (None, None). Make sure it's re-tracked + # when it's mutated and returned from __next__: + self.assertTrue(gc.is_tracked(next(it))) + class MyEnum(enumerate): pass diff --git a/Lib/test/test_epoll.py b/Lib/test/test_epoll.py index 10f148fe..b623852f 100644 --- a/Lib/test/test_epoll.py +++ b/Lib/test/test_epoll.py @@ -160,44 +160,42 @@ class TestEPoll(unittest.TestCase): self.fail("epoll on closed fd didn't raise EBADF") def test_control_and_wait(self): + # create the epoll object client, server = self._connected_pair() - ep = select.epoll(16) ep.register(server.fileno(), select.EPOLLIN | select.EPOLLOUT | select.EPOLLET) ep.register(client.fileno(), select.EPOLLIN | select.EPOLLOUT | select.EPOLLET) + # EPOLLOUT now = time.monotonic() events = ep.poll(1, 4) then = time.monotonic() self.assertFalse(then - now > 0.1, then - now) - events.sort() expected = [(client.fileno(), select.EPOLLOUT), (server.fileno(), select.EPOLLOUT)] - expected.sort() - - self.assertEqual(events, expected) + self.assertEqual(sorted(events), sorted(expected)) - events = ep.poll(timeout=2.1, maxevents=4) + # no event + events = ep.poll(timeout=0.1, maxevents=4) self.assertFalse(events) - client.send(b"Hello!") - server.send(b"world!!!") + # send: EPOLLIN and EPOLLOUT + client.sendall(b"Hello!") + server.sendall(b"world!!!") now = time.monotonic() - events = ep.poll(1, 4) + events = ep.poll(1.0, 4) then = time.monotonic() self.assertFalse(then - now > 0.01) - events.sort() expected = [(client.fileno(), select.EPOLLIN | select.EPOLLOUT), (server.fileno(), select.EPOLLIN | select.EPOLLOUT)] - expected.sort() - - self.assertEqual(events, expected) + self.assertEqual(sorted(events), sorted(expected)) + # unregister, modify ep.unregister(client.fileno()) ep.modify(server.fileno(), select.EPOLLOUT) now = time.monotonic() diff --git a/Lib/test/test_fstring.py b/Lib/test/test_fstring.py index 2ae815aa..2f08d35f 100644 --- a/Lib/test/test_fstring.py +++ b/Lib/test/test_fstring.py @@ -332,6 +332,59 @@ non-important content self.assertEqual(binop.left.col_offset, 4) self.assertEqual(binop.right.col_offset, 7) + def test_ast_line_numbers_with_parentheses(self): + expr = """ +x = ( + f" {test(t)}" +)""" + t = ast.parse(expr) + self.assertEqual(type(t), ast.Module) + self.assertEqual(len(t.body), 1) + # check the test(t) location + call = t.body[0].value.values[1].value + self.assertEqual(type(call), ast.Call) + self.assertEqual(call.lineno, 3) + self.assertEqual(call.end_lineno, 3) + self.assertEqual(call.col_offset, 8) + self.assertEqual(call.end_col_offset, 15) + + expr = """ +x = ( + 'PERL_MM_OPT', ( + f'wat' + f'some_string={f(x)} ' + f'wat' + ), +) +""" + t = ast.parse(expr) + self.assertEqual(type(t), ast.Module) + self.assertEqual(len(t.body), 1) + # check the fstring + fstring = t.body[0].value.elts[1] + self.assertEqual(type(fstring), ast.JoinedStr) + self.assertEqual(len(fstring.values), 3) + wat1, middle, wat2 = fstring.values + # check the first wat + self.assertEqual(type(wat1), ast.Constant) + self.assertEqual(wat1.lineno, 4) + self.assertEqual(wat1.end_lineno, 6) + self.assertEqual(wat1.col_offset, 12) + self.assertEqual(wat1.end_col_offset, 18) + # check the call + call = middle.value + self.assertEqual(type(call), ast.Call) + self.assertEqual(call.lineno, 5) + self.assertEqual(call.end_lineno, 5) + self.assertEqual(call.col_offset, 27) + self.assertEqual(call.end_col_offset, 31) + # check the second wat + self.assertEqual(type(wat2), ast.Constant) + self.assertEqual(wat2.lineno, 4) + self.assertEqual(wat2.end_lineno, 6) + self.assertEqual(wat2.col_offset, 12) + self.assertEqual(wat2.end_col_offset, 18) + def test_docstring(self): def f(): f'''Not a docstring''' diff --git a/Lib/test/test_genericalias.py b/Lib/test/test_genericalias.py index c113e538..ccf40b13 100644 --- a/Lib/test/test_genericalias.py +++ b/Lib/test/test_genericalias.py @@ -62,7 +62,6 @@ class BaseTest(unittest.TestCase): Iterable, Iterator, Reversible, Container, Collection, - Callable, Mailbox, _PartialFile, ContextVar, Token, Field, @@ -307,6 +306,69 @@ class BaseTest(unittest.TestCase): with self.assertRaises(TypeError): GenericAlias(bad=float) + def test_subclassing_types_genericalias(self): + class SubClass(GenericAlias): ... + alias = SubClass(list, int) + class Bad(GenericAlias): + def __new__(cls, *args, **kwargs): + super().__new__(cls, *args, **kwargs) + + self.assertEqual(alias, list[int]) + with self.assertRaises(TypeError): + Bad(list, int, bad=int) + + def test_abc_callable(self): + # A separate test is needed for Callable since it uses a subclass of + # GenericAlias. + alias = Callable[[int, str], float] + with self.subTest("Testing subscription"): + self.assertIs(alias.__origin__, Callable) + self.assertEqual(alias.__args__, (int, str, float)) + self.assertEqual(alias.__parameters__, ()) + + with self.subTest("Testing instance checks"): + self.assertIsInstance(alias, GenericAlias) + + with self.subTest("Testing weakref"): + self.assertEqual(ref(alias)(), alias) + + with self.subTest("Testing pickling"): + s = pickle.dumps(alias) + loaded = pickle.loads(s) + self.assertEqual(alias.__origin__, loaded.__origin__) + self.assertEqual(alias.__args__, loaded.__args__) + self.assertEqual(alias.__parameters__, loaded.__parameters__) + + with self.subTest("Testing TypeVar substitution"): + C1 = Callable[[int, T], T] + C2 = Callable[[K, T], V] + C3 = Callable[..., T] + self.assertEqual(C1[str], Callable[[int, str], str]) + self.assertEqual(C2[int, float, str], Callable[[int, float], str]) + self.assertEqual(C3[int], Callable[..., int]) + + # multi chaining + C4 = C2[int, V, str] + self.assertEqual(repr(C4).split(".")[-1], "Callable[[int, ~V], str]") + self.assertEqual(repr(C4[dict]).split(".")[-1], "Callable[[int, dict], str]") + self.assertEqual(C4[dict], Callable[[int, dict], str]) + + with self.subTest("Testing type erasure"): + class C1(Callable): + def __call__(self): + return None + a = C1[[int], T] + self.assertIs(a().__class__, C1) + self.assertEqual(a().__orig_class__, C1[[int], T]) + + # bpo-42195 + with self.subTest("Testing collections.abc.Callable's consistency " + "with typing.Callable"): + c1 = typing.Callable[[int, str], dict] + c2 = Callable[[int, str], dict] + self.assertEqual(c1.__args__, c2.__args__) + self.assertEqual(hash(c1.__args__), hash(c2.__args__)) + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_htmlparser.py b/Lib/test/test_htmlparser.py index a2bfb39d..12917755 100644 --- a/Lib/test/test_htmlparser.py +++ b/Lib/test/test_htmlparser.py @@ -452,42 +452,6 @@ text self._run_check('', [('comment', 'spacer type="block" height="25"')]) - def test_with_unquoted_attributes(self): - # see #12008 - html = ("" - "" - "
" - "- software-and-i" - "- library
") - expected = [ - ('starttag', 'html', []), - ('starttag', 'body', [('bgcolor', 'd0ca90'), ('text', '181008')]), - ('starttag', 'table', - [('cellspacing', '0'), ('cellpadding', '1'), ('width', '100%')]), - ('starttag', 'tr', []), - ('starttag', 'td', [('align', 'left')]), - ('starttag', 'font', [('size', '-1')]), - ('data', '- '), ('starttag', 'a', [('href', '/rabota/')]), - ('starttag', 'span', [('class', 'en')]), ('data', ' software-and-i'), - ('endtag', 'span'), ('endtag', 'a'), - ('data', '- '), ('starttag', 'a', [('href', '/1/')]), - ('starttag', 'span', [('class', 'en')]), ('data', ' library'), - ('endtag', 'span'), ('endtag', 'a'), ('endtag', 'table') - ] - self._run_check(html, expected) - - def test_comma_between_attributes(self): - self._run_check('
', [ - ('starttag', 'form', - [('action', '/xxx.php?a=1&b=2&'), - (',', None), ('method', 'post')])]) - - def test_weird_chars_in_unquoted_attribute_values(self): - self._run_check('', [ - ('starttag', 'form', - [('action', 'bogus|&#()value')])]) - def test_invalid_end_tags(self): # A collection of broken end tags.
is used as separator. # see http://www.w3.org/TR/html5/tokenization.html#end-tag-open-state @@ -766,6 +730,62 @@ class AttributesTestCase(TestCaseBase): [("href", "http://www.example.org/\">;")]), ("data", "spam"), ("endtag", "a")]) + def test_with_unquoted_attributes(self): + # see #12008 + html = ("" + "" + "
" + "- software-and-i" + "- library
") + expected = [ + ('starttag', 'html', []), + ('starttag', 'body', [('bgcolor', 'd0ca90'), ('text', '181008')]), + ('starttag', 'table', + [('cellspacing', '0'), ('cellpadding', '1'), ('width', '100%')]), + ('starttag', 'tr', []), + ('starttag', 'td', [('align', 'left')]), + ('starttag', 'font', [('size', '-1')]), + ('data', '- '), ('starttag', 'a', [('href', '/rabota/')]), + ('starttag', 'span', [('class', 'en')]), ('data', ' software-and-i'), + ('endtag', 'span'), ('endtag', 'a'), + ('data', '- '), ('starttag', 'a', [('href', '/1/')]), + ('starttag', 'span', [('class', 'en')]), ('data', ' library'), + ('endtag', 'span'), ('endtag', 'a'), ('endtag', 'table') + ] + self._run_check(html, expected) + + def test_comma_between_attributes(self): + # see bpo 41478 + # HTMLParser preserves duplicate attributes, leaving the task of + # removing duplicate attributes to a conformant html tree builder + html = ('
' # between attrs (unquoted) + '
' # between attrs (quoted) + '
' # after values (unquoted) + '
' # after values (quoted) + '
' # one comma values (quoted) + '
' # before values (unquoted) + '
' # before values (quoted) + '
' # before names + '
' # after names + ) + expected = [ + ('starttag', 'div', [('class', 'bar,baz=asd'),]), + ('starttag', 'div', [('class', 'bar'), (',baz', 'asd')]), + ('starttag', 'div', [('class', 'bar,'), ('baz', 'asd,')]), + ('starttag', 'div', [('class', 'bar'), (',', None), + ('baz', 'asd'), (',', None)]), + ('starttag', 'div', [('class', 'bar'), (',', None)]), + ('starttag', 'div', [('class', ',bar'), ('baz', ',asd')]), + ('starttag', 'div', [('class', ',"bar"'), ('baz', ',"asd"')]), + ('starttag', 'div', [(',class', 'bar'), (',baz', 'asd')]), + ('starttag', 'div', [('class,', 'bar'), ('baz,', 'asd')]), + ] + self._run_check(html, expected) + + def test_weird_chars_in_unquoted_attribute_values(self): + self._run_check('', [ + ('starttag', 'form', + [('action', 'bogus|&#()value')])]) if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py index ed125893..28943f02 100644 --- a/Lib/test/test_httplib.py +++ b/Lib/test/test_httplib.py @@ -1,5 +1,5 @@ import errno -from http import client +from http import client, HTTPStatus import io import itertools import os @@ -516,6 +516,10 @@ class TransferEncodingTest(TestCase): class BasicTest(TestCase): + def test_dir_with_added_behavior_on_status(self): + # see issue40084 + self.assertTrue({'description', 'name', 'phrase', 'value'} <= set(dir(HTTPStatus(404)))) + def test_status_lines(self): # Test HTTP status lines diff --git a/Lib/test/test_idle.py b/Lib/test/test_idle.py index 8bc01dea..310b72c1 100644 --- a/Lib/test/test_idle.py +++ b/Lib/test/test_idle.py @@ -20,5 +20,5 @@ from idlelib.idle_test import load_tests if __name__ == '__main__': tk.NoDefaultRoot() unittest.main(exit=False) - tk._support_default_root = 1 + tk._support_default_root = True tk._default_root = None diff --git a/Lib/test/test_itertools.py b/Lib/test/test_itertools.py index 702cf082..71012642 100644 --- a/Lib/test/test_itertools.py +++ b/Lib/test/test_itertools.py @@ -12,6 +12,8 @@ from functools import reduce import sys import struct import threading +import gc + maxsize = support.MAX_Py_ssize_t minsize = -maxsize-1 @@ -1554,6 +1556,51 @@ class TestBasicOps(unittest.TestCase): self.assertRaises(StopIteration, next, f(lambda x:x, [])) self.assertRaises(StopIteration, next, f(lambda x:x, StopNow())) + @support.cpython_only + def test_combinations_result_gc(self): + # bpo-42536: combinations's tuple-reuse speed trick breaks the GC's + # assumptions about what can be untracked. Make sure we re-track result + # tuples whenever we reuse them. + it = combinations([None, []], 1) + next(it) + gc.collect() + # That GC collection probably untracked the recycled internal result + # tuple, which has the value (None,). Make sure it's re-tracked when + # it's mutated and returned from __next__: + self.assertTrue(gc.is_tracked(next(it))) + + @support.cpython_only + def test_combinations_with_replacement_result_gc(self): + # Ditto for combinations_with_replacement. + it = combinations_with_replacement([None, []], 1) + next(it) + gc.collect() + self.assertTrue(gc.is_tracked(next(it))) + + @support.cpython_only + def test_permutations_result_gc(self): + # Ditto for permutations. + it = permutations([None, []], 1) + next(it) + gc.collect() + self.assertTrue(gc.is_tracked(next(it))) + + @support.cpython_only + def test_product_result_gc(self): + # Ditto for product. + it = product([None, []]) + next(it) + gc.collect() + self.assertTrue(gc.is_tracked(next(it))) + + @support.cpython_only + def test_zip_longest_result_gc(self): + # Ditto for zip_longest. + it = zip_longest([[]]) + gc.collect() + self.assertTrue(gc.is_tracked(next(it))) + + class TestExamples(unittest.TestCase): def test_accumulate(self): diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index 410eae22..a6cd291c 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -4191,6 +4191,15 @@ class ModuleLevelMiscTest(BaseTest): logging.disable(83) self.assertEqual(logging.root.manager.disable, 83) + self.assertRaises(ValueError, logging.disable, "doesnotexists") + + class _NotAnIntOrString: + pass + + self.assertRaises(TypeError, logging.disable, _NotAnIntOrString()) + + logging.disable("WARN") + # test the default value introduced in 3.7 # (Issue #28524) logging.disable() diff --git a/Lib/test/test_nntplib.py b/Lib/test/test_nntplib.py index 1df64fa7..99386ddb 100644 --- a/Lib/test/test_nntplib.py +++ b/Lib/test/test_nntplib.py @@ -82,7 +82,7 @@ class NetworkedNNTPTestsMixin: desc = self.server.description(self.GROUP_NAME) _check_desc(desc) # Another sanity check - self.assertIn("Python", desc) + self.assertIn(self.DESC, desc) # With a pattern desc = self.server.description(self.GROUP_PAT) _check_desc(desc) @@ -309,6 +309,7 @@ class NetworkedNNTPTests(NetworkedNNTPTestsMixin, unittest.TestCase): NNTP_HOST = 'news.trigofacile.com' GROUP_NAME = 'fr.comp.lang.python' GROUP_PAT = 'fr.comp.lang.*' + DESC = 'Python' NNTP_CLASS = NNTP @@ -343,8 +344,11 @@ class NetworkedNNTP_SSLTests(NetworkedNNTPTests): # 400 connections per day are accepted from each IP address." NNTP_HOST = 'nntp.aioe.org' - GROUP_NAME = 'comp.lang.python' - GROUP_PAT = 'comp.lang.*' + # bpo-42794: aioe.test is one of the official groups on this server + # used for testing: https://news.aioe.org/manual/aioe-hierarchy/ + GROUP_NAME = 'aioe.test' + GROUP_PAT = 'aioe.*' + DESC = 'test' NNTP_CLASS = getattr(nntplib, 'NNTP_SSL', None) diff --git a/Lib/test/test_ordered_dict.py b/Lib/test/test_ordered_dict.py index fdea44e4..9df4efbf 100644 --- a/Lib/test/test_ordered_dict.py +++ b/Lib/test/test_ordered_dict.py @@ -697,6 +697,17 @@ class OrderedDictTests: with self.assertRaises(ValueError): a |= "BAD" + @support.cpython_only + def test_ordered_dict_items_result_gc(self): + # bpo-42536: OrderedDict.items's tuple-reuse speed trick breaks the GC's + # assumptions about what can be untracked. Make sure we re-track result + # tuples whenever we reuse them. + it = iter(self.OrderedDict({None: []}).items()) + gc.collect() + # That GC collection probably untracked the recycled internal result + # tuple, which is initialized to (None, None). Make sure it's re-tracked + # when it's mutated and returned from __next__: + self.assertTrue(gc.is_tracked(next(it))) class PurePythonOrderedDictTests(OrderedDictTests, unittest.TestCase): diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index bf1cb5f5..35933e9c 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -3659,6 +3659,33 @@ class FDInheritanceTests(unittest.TestCase): self.assertEqual(fcntl.fcntl(fd, fcntl.F_GETFD) & fcntl.FD_CLOEXEC, 0) + @unittest.skipUnless(hasattr(os, 'O_PATH'), "need os.O_PATH") + def test_get_set_inheritable_o_path(self): + fd = os.open(__file__, os.O_PATH) + self.addCleanup(os.close, fd) + self.assertEqual(os.get_inheritable(fd), False) + + os.set_inheritable(fd, True) + self.assertEqual(os.get_inheritable(fd), True) + + os.set_inheritable(fd, False) + self.assertEqual(os.get_inheritable(fd), False) + + def test_get_set_inheritable_badf(self): + fd = support.make_bad_fd() + + with self.assertRaises(OSError) as ctx: + os.get_inheritable(fd) + self.assertEqual(ctx.exception.errno, errno.EBADF) + + with self.assertRaises(OSError) as ctx: + os.set_inheritable(fd, True) + self.assertEqual(ctx.exception.errno, errno.EBADF) + + with self.assertRaises(OSError) as ctx: + os.set_inheritable(fd, False) + self.assertEqual(ctx.exception.errno, errno.EBADF) + def test_open(self): fd = os.open(__file__, os.O_RDONLY) self.addCleanup(os.close, fd) diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py index 8016f81e..6c4eaf31 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -1661,6 +1661,71 @@ def bœr(): '(Pdb) ', ]) + + def test_issue42384(self): + '''When running `python foo.py` sys.path[0] is an absolute path. `python -m pdb foo.py` should behave the same''' + script = textwrap.dedent(""" + import sys + print('sys.path[0] is', sys.path[0]) + """) + commands = 'c\nq' + + with support.temp_cwd() as cwd: + expected = f'(Pdb) sys.path[0] is {os.path.realpath(cwd)}' + + stdout, stderr = self.run_pdb_script(script, commands) + + self.assertEqual(stdout.split('\n')[2].rstrip('\r'), expected) + + @support.skip_unless_symlink + def test_issue42384_symlink(self): + '''When running `python foo.py` sys.path[0] resolves symlinks. `python -m pdb foo.py` should behave the same''' + script = textwrap.dedent(""" + import sys + print('sys.path[0] is', sys.path[0]) + """) + commands = 'c\nq' + + with support.temp_cwd() as cwd: + cwd = os.path.realpath(cwd) + dir_one = os.path.join(cwd, 'dir_one') + dir_two = os.path.join(cwd, 'dir_two') + expected = f'(Pdb) sys.path[0] is {dir_one}' + + os.mkdir(dir_one) + with open(os.path.join(dir_one, 'foo.py'), 'w') as f: + f.write(script) + os.mkdir(dir_two) + os.symlink(os.path.join(dir_one, 'foo.py'), os.path.join(dir_two, 'foo.py')) + + stdout, stderr = self._run_pdb([os.path.join('dir_two', 'foo.py')], commands) + + self.assertEqual(stdout.split('\n')[2].rstrip('\r'), expected) + + def test_issue42383(self): + with support.temp_cwd() as cwd: + with open('foo.py', 'w') as f: + s = textwrap.dedent(""" + print('The correct file was executed') + + import os + os.chdir("subdir") + """) + f.write(s) + + subdir = os.path.join(cwd, 'subdir') + os.mkdir(subdir) + os.mkdir(os.path.join(subdir, 'subdir')) + wrong_file = os.path.join(subdir, 'foo.py') + + with open(wrong_file, 'w') as f: + f.write('print("The wrong file was executed")') + + stdout, stderr = self._run_pdb(['foo.py'], 'c\nc\nq') + expected = '(Pdb) The correct file was executed' + self.assertEqual(stdout.split('\n')[6].rstrip('\r'), expected) + + def load_tests(*args): from test import test_pdb suites = [ diff --git a/Lib/test/test_platform.py b/Lib/test/test_platform.py index a3b06feb..9f04c79e 100644 --- a/Lib/test/test_platform.py +++ b/Lib/test/test_platform.py @@ -1,4 +1,6 @@ import os +import copy +import pickle import platform import subprocess import sys @@ -175,6 +177,38 @@ class PlatformTest(unittest.TestCase): ) self.assertEqual(tuple(res), expected) + def test_uname_replace(self): + res = platform.uname() + new = res._replace( + system='system', node='node', release='release', + version='version', machine='machine') + self.assertEqual(new.system, 'system') + self.assertEqual(new.node, 'node') + self.assertEqual(new.release, 'release') + self.assertEqual(new.version, 'version') + self.assertEqual(new.machine, 'machine') + # processor cannot be replaced + self.assertEqual(new.processor, res.processor) + + def test_uname_copy(self): + uname = platform.uname() + self.assertEqual(copy.copy(uname), uname) + self.assertEqual(copy.deepcopy(uname), uname) + + def test_uname_pickle(self): + orig = platform.uname() + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + with self.subTest(protocol=proto): + pickled = pickle.dumps(orig, proto) + restored = pickle.loads(pickled) + self.assertEqual(restored, orig) + + def test_uname_slices(self): + res = platform.uname() + expected = tuple(res) + self.assertEqual(res[:], expected) + self.assertEqual(res[:5], expected[:5]) + @unittest.skipIf(sys.platform in ['win32', 'OpenVMS'], "uname -p not used") def test_uname_processor(self): """ diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py index bfbcbab3..f4edb8bd 100644 --- a/Lib/test/test_posix.py +++ b/Lib/test/test_posix.py @@ -1045,7 +1045,7 @@ class PosixTester(unittest.TestCase): if sys.platform == 'darwin': import sysconfig dt = sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET') or '10.0' - if tuple(int(n) for n in str(dt).split('.')[0:2]) < (10, 6): + if tuple(int(n) for n in dt.split('.')[0:2]) < (10, 6): raise unittest.SkipTest("getgroups(2) is broken prior to 10.6") # 'id -G' and 'os.getgroups()' should return the same diff --git a/Lib/test/test_random.py b/Lib/test/test_random.py index a80e71e6..15a68418 100644 --- a/Lib/test/test_random.py +++ b/Lib/test/test_random.py @@ -207,33 +207,6 @@ class TestBasicOps: with self.assertRaises(ValueError): sample(['red', 'green', 'blue'], counts=[1, 2, 3, 4], k=2) # too many counts - def test_sample_counts_equivalence(self): - # Test the documented strong equivalence to a sample with repeated elements. - # We run this test on random.Random() which makes deterministic selections - # for a given seed value. - sample = random.sample - seed = random.seed - - colors = ['red', 'green', 'blue', 'orange', 'black', 'amber'] - counts = [500, 200, 20, 10, 5, 1 ] - k = 700 - seed(8675309) - s1 = sample(colors, counts=counts, k=k) - seed(8675309) - expanded = [color for (color, count) in zip(colors, counts) for i in range(count)] - self.assertEqual(len(expanded), sum(counts)) - s2 = sample(expanded, k=k) - self.assertEqual(s1, s2) - - pop = 'abcdefghi' - counts = [10, 9, 8, 7, 6, 5, 4, 3, 2] - seed(8675309) - s1 = ''.join(sample(pop, counts=counts, k=30)) - expanded = ''.join([letter for (letter, count) in zip(pop, counts) for i in range(count)]) - seed(8675309) - s2 = ''.join(sample(expanded, k=30)) - self.assertEqual(s1, s2) - def test_choices(self): choices = self.gen.choices data = ['red', 'green', 'blue', 'yellow'] @@ -888,6 +861,33 @@ class MersenneTwister_TestBasicOps(TestBasicOps, unittest.TestCase): self.assertEqual(self.gen.randbytes(n), gen2.getrandbits(n * 8).to_bytes(n, 'little')) + def test_sample_counts_equivalence(self): + # Test the documented strong equivalence to a sample with repeated elements. + # We run this test on random.Random() which makes deterministic selections + # for a given seed value. + sample = self.gen.sample + seed = self.gen.seed + + colors = ['red', 'green', 'blue', 'orange', 'black', 'amber'] + counts = [500, 200, 20, 10, 5, 1 ] + k = 700 + seed(8675309) + s1 = sample(colors, counts=counts, k=k) + seed(8675309) + expanded = [color for (color, count) in zip(colors, counts) for i in range(count)] + self.assertEqual(len(expanded), sum(counts)) + s2 = sample(expanded, k=k) + self.assertEqual(s1, s2) + + pop = 'abcdefghi' + counts = [10, 9, 8, 7, 6, 5, 4, 3, 2] + seed(8675309) + s1 = ''.join(sample(pop, counts=counts, k=30)) + expanded = ''.join([letter for (letter, count) in zip(pop, counts) for i in range(count)]) + seed(8675309) + s2 = ''.join(sample(expanded, k=30)) + self.assertEqual(s1, s2) + def gamma(z, sqrt2pi=(2.0*pi)**0.5): # Reflection to right half of complex plane diff --git a/Lib/test/test_smtplib.py b/Lib/test/test_smtplib.py index 57629990..3451f3a4 100644 --- a/Lib/test/test_smtplib.py +++ b/Lib/test/test_smtplib.py @@ -165,6 +165,17 @@ class LMTPGeneralTests(GeneralTests, unittest.TestCase): client = smtplib.LMTP + @unittest.skipUnless(hasattr(socket, 'AF_UNIX'), "test requires Unix domain socket") + def testUnixDomainSocketTimeoutDefault(self): + local_host = '/some/local/lmtp/delivery/program' + mock_socket.reply_with(b"220 Hello world") + try: + client = self.client(local_host, self.port) + finally: + mock_socket.setdefaulttimeout(None) + self.assertIsNone(client.sock.gettimeout()) + client.close() + def testTimeoutZero(self): super().testTimeoutZero() local_host = '/some/local/lmtp/delivery/program' diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index 7373fe29..e8f9699e 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -196,6 +196,28 @@ class ProcessTestCase(BaseTestCase): input=b'pear') self.assertIn(b'PEAR', output) + def test_check_output_input_none(self): + """input=None has a legacy meaning of input='' on check_output.""" + output = subprocess.check_output( + [sys.executable, "-c", + "import sys; print('XX' if sys.stdin.read() else '')"], + input=None) + self.assertNotIn(b'XX', output) + + def test_check_output_input_none_text(self): + output = subprocess.check_output( + [sys.executable, "-c", + "import sys; print('XX' if sys.stdin.read() else '')"], + input=None, text=True) + self.assertNotIn('XX', output) + + def test_check_output_input_none_universal_newlines(self): + output = subprocess.check_output( + [sys.executable, "-c", + "import sys; print('XX' if sys.stdin.read() else '')"], + input=None, universal_newlines=True) + self.assertNotIn('XX', output) + def test_check_output_stdout_arg(self): # check_output() refuses to accept 'stdout' argument with self.assertRaises(ValueError) as c: diff --git a/Lib/test/test_sysconfig.py b/Lib/test/test_sysconfig.py index 44e44bf5..0ca5c939 100644 --- a/Lib/test/test_sysconfig.py +++ b/Lib/test/test_sysconfig.py @@ -358,10 +358,12 @@ class TestSysConfig(unittest.TestCase): @unittest.skipIf(sysconfig.get_config_var('EXT_SUFFIX') is None, 'EXT_SUFFIX required for this test') - def test_SO_in_vars(self): + def test_EXT_SUFFIX_in_vars(self): + import _imp vars = sysconfig.get_config_vars() self.assertIsNotNone(vars['SO']) self.assertEqual(vars['SO'], vars['EXT_SUFFIX']) + self.assertEqual(vars['EXT_SUFFIX'], _imp.extension_suffixes()[0]) @unittest.skipUnless(sys.platform == 'linux' and hasattr(sys.implementation, '_multiarch'), diff --git a/Lib/test/test_tcl.py b/Lib/test/test_tcl.py index 1c5b9cf2..0bbb39b1 100644 --- a/Lib/test/test_tcl.py +++ b/Lib/test/test_tcl.py @@ -1,4 +1,5 @@ import unittest +import locale import re import subprocess import sys @@ -59,6 +60,10 @@ class TclTest(unittest.TestCase): tcl = self.interp self.assertEqual(tcl.eval('set a "a\\0b"'), 'a\x00b') + def test_eval_surrogates_in_result(self): + tcl = self.interp + self.assertIn(tcl.eval(r'set a "<\ud83d\udcbb>"'), '<\U0001f4bb>') + def testEvalException(self): tcl = self.interp self.assertRaises(TclError,tcl.eval,'set a') @@ -131,10 +136,14 @@ class TclTest(unittest.TestCase): def get_integers(self): integers = (0, 1, -1, 2**31-1, -2**31, 2**31, -2**31-1, 2**63-1, -2**63) - # bignum was added in Tcl 8.5, but its support is able only since 8.5.8 - if (get_tk_patchlevel() >= (8, 6, 0, 'final') or - (8, 5, 8) <= get_tk_patchlevel() < (8, 6)): - integers += (2**63, -2**63-1, 2**1000, -2**1000) + # bignum was added in Tcl 8.5, but its support is able only since 8.5.8. + # Actually it is determined at compile time, so using get_tk_patchlevel() + # is not reliable. + # TODO: expose full static version. + if tcl_version >= (8, 5): + v = get_tk_patchlevel() + if v >= (8, 6, 0, 'final') or (8, 5, 8) <= v < (8, 6): + integers += (2**63, -2**63-1, 2**1000, -2**1000) return integers def test_getint(self): @@ -191,29 +200,48 @@ class TclTest(unittest.TestCase): def testEvalFile(self): tcl = self.interp - with open(support.TESTFN, 'w') as f: - self.addCleanup(support.unlink, support.TESTFN) + filename = support.TESTFN_ASCII + self.addCleanup(support.unlink, filename) + with open(filename, 'w') as f: f.write("""set a 1 set b 2 set c [ expr $a + $b ] """) - tcl.evalfile(support.TESTFN) + tcl.evalfile(filename) self.assertEqual(tcl.eval('set a'),'1') self.assertEqual(tcl.eval('set b'),'2') self.assertEqual(tcl.eval('set c'),'3') def test_evalfile_null_in_result(self): tcl = self.interp - with open(support.TESTFN, 'w') as f: - self.addCleanup(support.unlink, support.TESTFN) + filename = support.TESTFN_ASCII + self.addCleanup(support.unlink, filename) + with open(filename, 'w') as f: f.write(""" set a "a\0b" set b "a\\0b" """) - tcl.evalfile(support.TESTFN) + tcl.evalfile(filename) self.assertEqual(tcl.eval('set a'), 'a\x00b') self.assertEqual(tcl.eval('set b'), 'a\x00b') + def test_evalfile_surrogates_in_result(self): + tcl = self.interp + encoding = tcl.call('encoding', 'system') + self.addCleanup(tcl.call, 'encoding', 'system', encoding) + tcl.call('encoding', 'system', 'utf-8') + + filename = support.TESTFN_ASCII + self.addCleanup(support.unlink, filename) + with open(filename, 'wb') as f: + f.write(b""" + set a "<\xed\xa0\xbd\xed\xb2\xbb>" + set b "<\\ud83d\\udcbb>" + """) + tcl.evalfile(filename) + self.assertEqual(tcl.eval('set a'), '<\U0001f4bb>') + self.assertEqual(tcl.eval('set b'), '<\U0001f4bb>') + def testEvalFileException(self): tcl = self.interp filename = "doesnotexists" @@ -419,7 +447,7 @@ class TclTest(unittest.TestCase): else: self.assertEqual(result, str(i)) self.assertIsInstance(result, str) - if tcl_version < (8, 5): # bignum was added in Tcl 8.5 + if get_tk_patchlevel() < (8, 5): # bignum was added in Tcl 8.5 self.assertRaises(TclError, tcl.call, 'expr', str(2**1000)) def test_passing_values(self): @@ -436,6 +464,11 @@ class TclTest(unittest.TestCase): self.assertEqual(passValue('str\x00ing\u20ac'), 'str\x00ing\u20ac') self.assertEqual(passValue('str\x00ing\U0001f4bb'), 'str\x00ing\U0001f4bb') + if sys.platform != 'win32': + self.assertEqual(passValue('<\udce2\udc82\udcac>'), + '<\u20ac>') + self.assertEqual(passValue('<\udced\udca0\udcbd\udced\udcb2\udcbb>'), + '<\U0001f4bb>') self.assertEqual(passValue(b'str\x00ing'), b'str\x00ing' if self.wantobjects else 'str\x00ing') self.assertEqual(passValue(b'str\xc0\x80ing'), @@ -495,6 +528,9 @@ class TclTest(unittest.TestCase): check('string\xbd') check('string\u20ac') check('string\U0001f4bb') + if sys.platform != 'win32': + check('<\udce2\udc82\udcac>', '<\u20ac>') + check('<\udced\udca0\udcbd\udced\udcb2\udcbb>', '<\U0001f4bb>') check('') check(b'string', 'string') check(b'string\xe2\x82\xac', 'string\xe2\x82\xac') @@ -538,6 +574,8 @@ class TclTest(unittest.TestCase): ('a \u20ac', ('a', '\u20ac')), ('a \U0001f4bb', ('a', '\U0001f4bb')), (b'a \xe2\x82\xac', ('a', '\u20ac')), + (b'a \xf0\x9f\x92\xbb', ('a', '\U0001f4bb')), + (b'a \xed\xa0\xbd\xed\xb2\xbb', ('a', '\U0001f4bb')), (b'a\xc0\x80b c\xc0\x80d', ('a\x00b', 'c\x00d')), ('a {b c}', ('a', 'b c')), (r'a b\ c', ('a', 'b c')), diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index 8549ba2b..5bb3a58b 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -667,6 +667,31 @@ class BaseExceptionReportingTests: msg = self.get_report(e).splitlines() self.assertEqual(msg[-2], ' ^') + def test_syntax_error_no_lineno(self): + # See #34463. + + # Without filename + e = SyntaxError('bad syntax') + msg = self.get_report(e).splitlines() + self.assertEqual(msg, + ['SyntaxError: bad syntax']) + e.lineno = 100 + msg = self.get_report(e).splitlines() + self.assertEqual(msg, + [' File "", line 100', 'SyntaxError: bad syntax']) + + # With filename + e = SyntaxError('bad syntax') + e.filename = 'myfile.py' + + msg = self.get_report(e).splitlines() + self.assertEqual(msg, + ['SyntaxError: bad syntax (myfile.py)']) + e.lineno = 100 + msg = self.get_report(e).splitlines() + self.assertEqual(msg, + [' File "myfile.py", line 100', 'SyntaxError: bad syntax']) + def test_message_none(self): # A message that looks like "None" should not be treated specially err = self.get_report(Exception(None)) diff --git a/Lib/test/test_tracemalloc.py b/Lib/test/test_tracemalloc.py index c5ae4e6d..b10d1798 100644 --- a/Lib/test/test_tracemalloc.py +++ b/Lib/test/test_tracemalloc.py @@ -84,6 +84,25 @@ def traceback_filename(filename): return traceback_lineno(filename, 0) +class TestTraceback(unittest.TestCase): + def test_repr(self): + def get_repr(*args) -> str: + return repr(tracemalloc.Traceback(*args)) + + self.assertEqual(get_repr(()), "") + self.assertEqual(get_repr((), 0), "") + + frames = (("f1", 1), ("f2", 2)) + exp_repr_frames = ( + "(," + " )" + ) + self.assertEqual(get_repr(frames), + f"") + self.assertEqual(get_repr(frames, 2), + f"") + + class TestTracemallocEnabled(unittest.TestCase): def setUp(self): if tracemalloc.is_tracing(): @@ -1064,6 +1083,7 @@ class TestCAPI(unittest.TestCase): def test_main(): support.run_unittest( + TestTraceback, TestTracemallocEnabled, TestSnapshot, TestFilters, diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 13cf20ee..4bdb2a0f 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -450,14 +450,6 @@ class CallableTests(BaseTestCase): type(c)() def test_callable_wrong_forms(self): - with self.assertRaises(TypeError): - Callable[[...], int] - with self.assertRaises(TypeError): - Callable[(), int] - with self.assertRaises(TypeError): - Callable[[()], int] - with self.assertRaises(TypeError): - Callable[[int, 1], 2] with self.assertRaises(TypeError): Callable[int] @@ -3079,6 +3071,11 @@ class GetUtilitiesTestCase(TestCase): self.assertEqual(get_args(Callable), ()) self.assertEqual(get_args(list[int]), (int,)) self.assertEqual(get_args(list), ()) + self.assertEqual(get_args(collections.abc.Callable[[int], str]), ([int], str)) + self.assertEqual(get_args(collections.abc.Callable[..., str]), (..., str)) + self.assertEqual(get_args(collections.abc.Callable[[], str]), ([], str)) + self.assertEqual(get_args(collections.abc.Callable[[int], str]), + get_args(Callable[[int], str])) class CollectionsAbcTests(BaseTestCase): @@ -3899,10 +3896,14 @@ class TypedDictTests(BaseTestCase): self.assertEqual(D(), {}) self.assertEqual(D(x=1), {'x': 1}) self.assertEqual(D.__total__, False) + self.assertEqual(D.__required_keys__, frozenset()) + self.assertEqual(D.__optional_keys__, {'x'}) self.assertEqual(Options(), {}) self.assertEqual(Options(log_level=2), {'log_level': 2}) self.assertEqual(Options.__total__, False) + self.assertEqual(Options.__required_keys__, frozenset()) + self.assertEqual(Options.__optional_keys__, {'log_level', 'log_path'}) def test_optional_keys(self): class Point2Dor3D(Point2D, total=False): diff --git a/Lib/test/test_unpack_ex.py b/Lib/test/test_unpack_ex.py index fcc93829..049e48b1 100644 --- a/Lib/test/test_unpack_ex.py +++ b/Lib/test/test_unpack_ex.py @@ -346,6 +346,31 @@ Now some general starred expressions (all fail). ... SyntaxError: can't use starred expression here + >>> (*x),y = 1, 2 # doctest:+ELLIPSIS + Traceback (most recent call last): + ... + SyntaxError: can't use starred expression here + + >>> (((*x))),y = 1, 2 # doctest:+ELLIPSIS + Traceback (most recent call last): + ... + SyntaxError: can't use starred expression here + + >>> z,(*x),y = 1, 2, 4 # doctest:+ELLIPSIS + Traceback (most recent call last): + ... + SyntaxError: can't use starred expression here + + >>> z,(*x) = 1, 2 # doctest:+ELLIPSIS + Traceback (most recent call last): + ... + SyntaxError: can't use starred expression here + + >>> ((*x),y) = 1, 2 # doctest:+ELLIPSIS + Traceback (most recent call last): + ... + SyntaxError: can't use starred expression here + Some size constraints (all fail.) >>> s = ", ".join("a%d" % i for i in range(1<<8)) + ", *rest = range(1<<8 + 1)" diff --git a/Lib/test/test_urllib2.py b/Lib/test/test_urllib2.py index b71be549..12c3f4db 100644 --- a/Lib/test/test_urllib2.py +++ b/Lib/test/test_urllib2.py @@ -1849,9 +1849,17 @@ class MiscTests(unittest.TestCase): ('ftp', 'joe', 'password', 'proxy.example.com')), # Test for no trailing '/' case ('http://joe:password@proxy.example.com', - ('http', 'joe', 'password', 'proxy.example.com')) + ('http', 'joe', 'password', 'proxy.example.com')), + # Testcases with '/' character in username, password + ('http://user/name:password@localhost:22', + ('http', 'user/name', 'password', 'localhost:22')), + ('http://username:pass/word@localhost:22', + ('http', 'username', 'pass/word', 'localhost:22')), + ('http://user/name:pass/word@localhost:22', + ('http', 'user/name', 'pass/word', 'localhost:22')), ] + for tc, expected in parse_proxy_test_cases: self.assertEqual(_parse_proxy(tc), expected) diff --git a/Lib/test/test_urlparse.py b/Lib/test/test_urlparse.py index 76250078..3b1c3606 100644 --- a/Lib/test/test_urlparse.py +++ b/Lib/test/test_urlparse.py @@ -32,16 +32,10 @@ parse_qsl_test_cases = [ (b"&a=b", [(b'a', b'b')]), (b"a=a+b&b=b+c", [(b'a', b'a b'), (b'b', b'b c')]), (b"a=1&a=2", [(b'a', b'1'), (b'a', b'2')]), - (";", []), - (";;", []), - (";a=b", [('a', 'b')]), - ("a=a+b;b=b+c", [('a', 'a b'), ('b', 'b c')]), - ("a=1;a=2", [('a', '1'), ('a', '2')]), - (b";", []), - (b";;", []), - (b";a=b", [(b'a', b'b')]), - (b"a=a+b;b=b+c", [(b'a', b'a b'), (b'b', b'b c')]), - (b"a=1;a=2", [(b'a', b'1'), (b'a', b'2')]), + (";a=b", [(';a', 'b')]), + ("a=a+b;b=b+c", [('a', 'a b;b=b c')]), + (b";a=b", [(b';a', b'b')]), + (b"a=a+b;b=b+c", [(b'a', b'a b;b=b c')]), ] # Each parse_qs testcase is a two-tuple that contains @@ -68,16 +62,10 @@ parse_qs_test_cases = [ (b"&a=b", {b'a': [b'b']}), (b"a=a+b&b=b+c", {b'a': [b'a b'], b'b': [b'b c']}), (b"a=1&a=2", {b'a': [b'1', b'2']}), - (";", {}), - (";;", {}), - (";a=b", {'a': ['b']}), - ("a=a+b;b=b+c", {'a': ['a b'], 'b': ['b c']}), - ("a=1;a=2", {'a': ['1', '2']}), - (b";", {}), - (b";;", {}), - (b";a=b", {b'a': [b'b']}), - (b"a=a+b;b=b+c", {b'a': [b'a b'], b'b': [b'b c']}), - (b"a=1;a=2", {b'a': [b'1', b'2']}), + (";a=b", {';a': ['b']}), + ("a=a+b;b=b+c", {'a': ['a b;b=b c']}), + (b";a=b", {b';a': [b'b']}), + (b"a=a+b;b=b+c", {b'a':[ b'a b;b=b c']}), ] class UrlParseTestCase(unittest.TestCase): @@ -886,10 +874,46 @@ class UrlParseTestCase(unittest.TestCase): def test_parse_qsl_max_num_fields(self): with self.assertRaises(ValueError): urllib.parse.parse_qs('&'.join(['a=a']*11), max_num_fields=10) - with self.assertRaises(ValueError): - urllib.parse.parse_qs(';'.join(['a=a']*11), max_num_fields=10) urllib.parse.parse_qs('&'.join(['a=a']*10), max_num_fields=10) + def test_parse_qs_separator(self): + parse_qs_semicolon_cases = [ + (";", {}), + (";;", {}), + (";a=b", {'a': ['b']}), + ("a=a+b;b=b+c", {'a': ['a b'], 'b': ['b c']}), + ("a=1;a=2", {'a': ['1', '2']}), + (b";", {}), + (b";;", {}), + (b";a=b", {b'a': [b'b']}), + (b"a=a+b;b=b+c", {b'a': [b'a b'], b'b': [b'b c']}), + (b"a=1;a=2", {b'a': [b'1', b'2']}), + ] + for orig, expect in parse_qs_semicolon_cases: + with self.subTest(f"Original: {orig!r}, Expected: {expect!r}"): + result = urllib.parse.parse_qs(orig, separator=';') + self.assertEqual(result, expect, "Error parsing %r" % orig) + + + def test_parse_qsl_separator(self): + parse_qsl_semicolon_cases = [ + (";", []), + (";;", []), + (";a=b", [('a', 'b')]), + ("a=a+b;b=b+c", [('a', 'a b'), ('b', 'b c')]), + ("a=1;a=2", [('a', '1'), ('a', '2')]), + (b";", []), + (b";;", []), + (b";a=b", [(b'a', b'b')]), + (b"a=a+b;b=b+c", [(b'a', b'a b'), (b'b', b'b c')]), + (b"a=1;a=2", [(b'a', b'1'), (b'a', b'2')]), + ] + for orig, expect in parse_qsl_semicolon_cases: + with self.subTest(f"Original: {orig!r}, Expected: {expect!r}"): + result = urllib.parse.parse_qsl(orig, separator=';') + self.assertEqual(result, expect, "Error parsing %r" % orig) + + def test_urlencode_sequences(self): # Other tests incidentally urlencode things; test non-covered cases: # Sequence and object values. diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py index 2175afcf..f15f6c5e 100644 --- a/Lib/tkinter/__init__.py +++ b/Lib/tkinter/__init__.py @@ -270,7 +270,7 @@ class Event: ) -_support_default_root = 1 +_support_default_root = True _default_root = None @@ -280,13 +280,26 @@ def NoDefaultRoot(): Call this function to inhibit that the first instance of Tk is used for windows without an explicit parent window. """ - global _support_default_root - _support_default_root = 0 - global _default_root + global _support_default_root, _default_root + _support_default_root = False + # Delete, so any use of _default_root will immediately raise an exception. + # Rebind before deletion, so repeated calls will not fail. _default_root = None del _default_root +def _get_default_root(what=None): + if not _support_default_root: + raise RuntimeError("No master specified and tkinter is " + "configured to not support default root") + if not _default_root: + if what: + raise RuntimeError(f"Too early to {what}: no default root window") + root = Tk() + assert _default_root is root + return _default_root + + def _tkerror(err): """Internal function.""" pass @@ -330,7 +343,7 @@ class Variable: raise TypeError("name must be a string") global _varnum if not master: - master = _default_root + master = _get_default_root('create variable') self._root = master._root() self._tk = master.tk if name: @@ -478,15 +491,11 @@ class Variable: self._tk.call("trace", "vinfo", self._name))] def __eq__(self, other): - """Comparison for equality (==). - - Note: if the Variable's master matters to behavior - also compare self._master == other._master - """ if not isinstance(other, Variable): return NotImplemented - return self.__class__.__name__ == other.__class__.__name__ \ - and self._name == other._name + return (self._name == other._name + and self.__class__.__name__ == other.__class__.__name__ + and self._tk == other._tk) class StringVar(Variable): @@ -591,7 +600,7 @@ class BooleanVar(Variable): def mainloop(n=0): """Run the main loop of Tcl.""" - _default_root.tk.mainloop(n) + _get_default_root('run the main loop').tk.mainloop(n) getint = int @@ -600,9 +609,9 @@ getdouble = float def getboolean(s): - """Convert true and false to integer values 1 and 0.""" + """Convert Tcl object to True or False.""" try: - return _default_root.tk.getboolean(s) + return _get_default_root('use getboolean()').tk.getboolean(s) except TclError: raise ValueError("invalid literal for getboolean()") @@ -1151,8 +1160,7 @@ class Misc: self.tk.call('winfo', 'reqwidth', self._w)) def winfo_rgb(self, color): - """Return tuple of decimal values for red, green, blue for - COLOR in this widget.""" + """Return a tuple of integer RGB values in range(65536) for color in this widget.""" return self._getints( self.tk.call('winfo', 'rgb', self._w, color)) @@ -2248,7 +2256,7 @@ class Tk(Misc, Wm): is the name of the widget class.""" self.master = None self.children = {} - self._tkloaded = 0 + self._tkloaded = False # to avoid recursions in the getattr code in case of failure, we # ensure that self.tk is always _something_. self.tk = None @@ -2272,7 +2280,7 @@ class Tk(Misc, Wm): self._loadtk() def _loadtk(self): - self._tkloaded = 1 + self._tkloaded = True global _default_root # Version sanity checks tk_version = self.tk.getvar('tk_version') @@ -2521,12 +2529,8 @@ class BaseWidget(Misc): def _setup(self, master, cnf): """Internal function. Sets up information about children.""" - if _support_default_root: - global _default_root - if not master: - if not _default_root: - _default_root = Tk() - master = _default_root + if not master: + master = _get_default_root() self.master = master self.tk = master.tk name = None @@ -3990,9 +3994,7 @@ class Image: def __init__(self, imgtype, name=None, cnf={}, master=None, **kw): self.name = None if not master: - master = _default_root - if not master: - raise RuntimeError('Too early to create image') + master = _get_default_root('create image') self.tk = getattr(master, 'tk', master) if not name: Image._last_id += 1 @@ -4146,11 +4148,13 @@ class BitmapImage(Image): def image_names(): - return _default_root.tk.splitlist(_default_root.tk.call('image', 'names')) + tk = _get_default_root('use image_names()').tk + return tk.splitlist(tk.call('image', 'names')) def image_types(): - return _default_root.tk.splitlist(_default_root.tk.call('image', 'types')) + tk = _get_default_root('use image_types()').tk + return tk.splitlist(tk.call('image', 'types')) class Spinbox(Widget, XView): diff --git a/Lib/tkinter/colorchooser.py b/Lib/tkinter/colorchooser.py index 3cfc06f6..e2fb69db 100644 --- a/Lib/tkinter/colorchooser.py +++ b/Lib/tkinter/colorchooser.py @@ -8,57 +8,69 @@ # fixed initialcolor handling in August 1998 # -# -# options (all have default values): -# -# - initialcolor: color to mark as selected when dialog is displayed -# (given as an RGB triplet or a Tk color string) -# -# - parent: which window to place the dialog on top of -# -# - title: dialog title -# from tkinter.commondialog import Dialog __all__ = ["Chooser", "askcolor"] -# -# color chooser class - class Chooser(Dialog): - "Ask for a color" + """Create a dialog for the tk_chooseColor command. + + Args: + master: The master widget for this dialog. If not provided, + defaults to options['parent'] (if defined). + options: Dictionary of options for the tk_chooseColor call. + initialcolor: Specifies the selected color when the + dialog is first displayed. This can be a tk color + string or a 3-tuple of ints in the range (0, 255) + for an RGB triplet. + parent: The parent window of the color dialog. The + color dialog is displayed on top of this. + title: A string for the title of the dialog box. + """ command = "tk_chooseColor" def _fixoptions(self): + """Ensure initialcolor is a tk color string. + + Convert initialcolor from a RGB triplet to a color string. + """ try: - # make sure initialcolor is a tk color string color = self.options["initialcolor"] if isinstance(color, tuple): - # assume an RGB triplet + # Assume an RGB triplet. self.options["initialcolor"] = "#%02x%02x%02x" % color except KeyError: pass def _fixresult(self, widget, result): - # result can be somethings: an empty tuple, an empty string or - # a Tcl_Obj, so this somewhat weird check handles that + """Adjust result returned from call to tk_chooseColor. + + Return both an RGB tuple of ints in the range (0, 255) and the + tk color string in the form #rrggbb. + """ + # Result can be many things: an empty tuple, an empty string, or + # a _tkinter.Tcl_Obj, so this somewhat weird check handles that. if not result or not str(result): - return None, None # canceled + return None, None # canceled - # to simplify application code, the color chooser returns - # an RGB tuple together with the Tk color string + # To simplify application code, the color chooser returns + # an RGB tuple together with the Tk color string. r, g, b = widget.winfo_rgb(result) - return (r/256, g/256, b/256), str(result) + return (r//256, g//256, b//256), str(result) # # convenience stuff -def askcolor(color = None, **options): - "Ask for a color" +def askcolor(color=None, **options): + """Display dialog window for selection of a color. + + Convenience wrapper for the Chooser class. Displays the color + chooser dialog with color as the initial value. + """ if color: options = options.copy() diff --git a/Lib/tkinter/commondialog.py b/Lib/tkinter/commondialog.py index e56b5baf..cc306984 100644 --- a/Lib/tkinter/commondialog.py +++ b/Lib/tkinter/commondialog.py @@ -18,10 +18,10 @@ class Dialog: command = None def __init__(self, master=None, **options): + if not master: + master = options.get('parent') self.master = master self.options = options - if not master and options.get('parent'): - self.master = options['parent'] def _fixoptions(self): pass # hook diff --git a/Lib/tkinter/font.py b/Lib/tkinter/font.py index 15ad7ab4..78b660c5 100644 --- a/Lib/tkinter/font.py +++ b/Lib/tkinter/font.py @@ -69,7 +69,7 @@ class Font: def __init__(self, root=None, font=None, name=None, exists=False, **options): if not root: - root = tkinter._default_root + root = tkinter._get_default_root('use font') tk = getattr(root, 'tk', root) if font: # get actual settings corresponding to the given font @@ -103,7 +103,7 @@ class Font: def __eq__(self, other): if not isinstance(other, Font): return NotImplemented - return self.name == other.name + return self.name == other.name and self._tk == other._tk def __getitem__(self, key): return self.cget(key) @@ -180,7 +180,7 @@ class Font: def families(root=None, displayof=None): "Get font families (as a tuple)" if not root: - root = tkinter._default_root + root = tkinter._get_default_root('use font.families()') args = () if displayof: args = ('-displayof', displayof) @@ -190,7 +190,7 @@ def families(root=None, displayof=None): def names(root=None): "Get names of defined fonts (as a tuple)" if not root: - root = tkinter._default_root + root = tkinter._get_default_root('use font.names()') return root.tk.splitlist(root.tk.call("font", "names")) diff --git a/Lib/tkinter/simpledialog.py b/Lib/tkinter/simpledialog.py index 85244171..b882d47c 100644 --- a/Lib/tkinter/simpledialog.py +++ b/Lib/tkinter/simpledialog.py @@ -24,9 +24,7 @@ askstring -- get a string from the user """ from tkinter import * -from tkinter import messagebox - -import tkinter # used at _QueryDialog for tkinter._default_root +from tkinter import messagebox, _get_default_root class SimpleDialog: @@ -128,13 +126,17 @@ class Dialog(Toplevel): title -- the dialog title ''' - Toplevel.__init__(self, parent) + master = parent + if not master: + master = _get_default_root('create dialog window') + + Toplevel.__init__(self, master) self.withdraw() # remain invisible for now - # If the master is not viewable, don't + # If the parent is not viewable, don't # make the child transient, or else it # would be opened withdrawn - if parent.winfo_viewable(): + if parent is not None and parent.winfo_viewable(): self.transient(parent) if title: @@ -155,7 +157,7 @@ class Dialog(Toplevel): self.protocol("WM_DELETE_WINDOW", self.cancel) - if self.parent is not None: + if parent is not None: self.geometry("+%d+%d" % (parent.winfo_rootx()+50, parent.winfo_rooty()+50)) @@ -259,9 +261,6 @@ class _QueryDialog(Dialog): minvalue = None, maxvalue = None, parent = None): - if not parent: - parent = tkinter._default_root - self.prompt = prompt self.minvalue = minvalue self.maxvalue = maxvalue diff --git a/Lib/tkinter/test/support.py b/Lib/tkinter/test/support.py index 467a0b66..dbc47a81 100644 --- a/Lib/tkinter/test/support.py +++ b/Lib/tkinter/test/support.py @@ -36,6 +36,33 @@ class AbstractTkTest: w.destroy() self.root.withdraw() + +class AbstractDefaultRootTest: + + def setUp(self): + self._old_support_default_root = tkinter._support_default_root + destroy_default_root() + tkinter._support_default_root = True + self.wantobjects = tkinter.wantobjects + + def tearDown(self): + destroy_default_root() + tkinter._default_root = None + tkinter._support_default_root = self._old_support_default_root + + def _test_widget(self, constructor): + # no master passing + x = constructor() + self.assertIsNotNone(tkinter._default_root) + self.assertIs(x.master, tkinter._default_root) + self.assertIs(x.tk, tkinter._default_root.tk) + x.destroy() + destroy_default_root() + tkinter.NoDefaultRoot() + self.assertRaises(RuntimeError, constructor) + self.assertFalse(hasattr(tkinter, '_default_root')) + + def destroy_default_root(): if getattr(tkinter, '_default_root', None): tkinter._default_root.update_idletasks() diff --git a/Lib/tkinter/test/test_tkinter/test_colorchooser.py b/Lib/tkinter/test/test_tkinter/test_colorchooser.py new file mode 100644 index 00000000..4798bc9c --- /dev/null +++ b/Lib/tkinter/test/test_tkinter/test_colorchooser.py @@ -0,0 +1,43 @@ +import unittest +import tkinter +from test.support import requires, run_unittest, swap_attr +from tkinter.test.support import AbstractTkTest +from tkinter import colorchooser + +requires('gui') + + +class ChooserTest(AbstractTkTest, unittest.TestCase): + + @classmethod + def setUpClass(cls): + AbstractTkTest.setUpClass.__func__(cls) + cls.cc = colorchooser.Chooser(initialcolor='dark blue slate') + + def test_fixoptions(self): + cc = self.cc + cc._fixoptions() + self.assertEqual(cc.options['initialcolor'], 'dark blue slate') + + cc.options['initialcolor'] = '#D2D269691E1E' + cc._fixoptions() + self.assertEqual(cc.options['initialcolor'], '#D2D269691E1E') + + cc.options['initialcolor'] = (210, 105, 30) + cc._fixoptions() + self.assertEqual(cc.options['initialcolor'], '#d2691e') + + def test_fixresult(self): + cc = self.cc + self.assertEqual(cc._fixresult(self.root, ()), (None, None)) + self.assertEqual(cc._fixresult(self.root, ''), (None, None)) + self.assertEqual(cc._fixresult(self.root, 'chocolate'), + ((210, 105, 30), 'chocolate')) + self.assertEqual(cc._fixresult(self.root, '#4a3c8c'), + ((74, 60, 140), '#4a3c8c')) + + +tests_gui = (ChooserTest,) + +if __name__ == "__main__": + run_unittest(*tests_gui) diff --git a/Lib/tkinter/test/test_tkinter/test_font.py b/Lib/tkinter/test/test_tkinter/test_font.py index a021ea33..0ea7924f 100644 --- a/Lib/tkinter/test/test_tkinter/test_font.py +++ b/Lib/tkinter/test/test_tkinter/test_font.py @@ -2,7 +2,7 @@ import unittest import tkinter from tkinter import font from test.support import requires, run_unittest, gc_collect, ALWAYS_EQ -from tkinter.test.support import AbstractTkTest +from tkinter.test.support import AbstractTkTest, AbstractDefaultRootTest requires('gui') @@ -63,15 +63,22 @@ class FontTest(AbstractTkTest, unittest.TestCase): self.assertEqual(self.font.name, fontname) self.assertEqual(str(self.font), fontname) - def test_eq(self): + def test_equality(self): font1 = font.Font(root=self.root, name=fontname, exists=True) font2 = font.Font(root=self.root, name=fontname, exists=True) self.assertIsNot(font1, font2) self.assertEqual(font1, font2) self.assertNotEqual(font1, font1.copy()) + self.assertNotEqual(font1, 0) self.assertEqual(font1, ALWAYS_EQ) + root2 = tkinter.Tk() + self.addCleanup(root2.destroy) + font3 = font.Font(root=root2, name=fontname, exists=True) + self.assertEqual(str(font1), str(font3)) + self.assertNotEqual(font1, font3) + def test_measure(self): self.assertIsInstance(self.font.measure('abc'), int) @@ -101,7 +108,38 @@ class FontTest(AbstractTkTest, unittest.TestCase): self.assertTrue(name) self.assertIn(fontname, names) -tests_gui = (FontTest, ) + +class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase): + + def test_families(self): + self.assertRaises(RuntimeError, font.families) + root = tkinter.Tk() + families = font.families() + self.assertIsInstance(families, tuple) + self.assertTrue(families) + for family in families: + self.assertIsInstance(family, str) + self.assertTrue(family) + root.destroy() + tkinter.NoDefaultRoot() + self.assertRaises(RuntimeError, font.families) + + def test_names(self): + self.assertRaises(RuntimeError, font.names) + root = tkinter.Tk() + names = font.names() + self.assertIsInstance(names, tuple) + self.assertTrue(names) + for name in names: + self.assertIsInstance(name, str) + self.assertTrue(name) + self.assertIn(fontname, names) + root.destroy() + tkinter.NoDefaultRoot() + self.assertRaises(RuntimeError, font.names) + + +tests_gui = (FontTest, DefaultRootTest) if __name__ == "__main__": run_unittest(*tests_gui) diff --git a/Lib/tkinter/test/test_tkinter/test_images.py b/Lib/tkinter/test/test_tkinter/test_images.py index 2805d35a..94bba851 100644 --- a/Lib/tkinter/test/test_tkinter/test_images.py +++ b/Lib/tkinter/test/test_tkinter/test_images.py @@ -1,7 +1,7 @@ import unittest import tkinter from test import support -from tkinter.test.support import AbstractTkTest, requires_tcl +from tkinter.test.support import AbstractTkTest, AbstractDefaultRootTest, requires_tcl support.requires('gui') @@ -19,6 +19,47 @@ class MiscTest(AbstractTkTest, unittest.TestCase): self.assertIsInstance(image_names, tuple) +class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase): + + def test_image_types(self): + self.assertRaises(RuntimeError, tkinter.image_types) + root = tkinter.Tk() + image_types = tkinter.image_types() + self.assertIsInstance(image_types, tuple) + self.assertIn('photo', image_types) + self.assertIn('bitmap', image_types) + root.destroy() + tkinter.NoDefaultRoot() + self.assertRaises(RuntimeError, tkinter.image_types) + + def test_image_names(self): + self.assertRaises(RuntimeError, tkinter.image_names) + root = tkinter.Tk() + image_names = tkinter.image_names() + self.assertIsInstance(image_names, tuple) + root.destroy() + tkinter.NoDefaultRoot() + self.assertRaises(RuntimeError, tkinter.image_names) + + def test_image_create_bitmap(self): + self.assertRaises(RuntimeError, tkinter.BitmapImage) + root = tkinter.Tk() + image = tkinter.BitmapImage() + self.assertIn(image.name, tkinter.image_names()) + root.destroy() + tkinter.NoDefaultRoot() + self.assertRaises(RuntimeError, tkinter.BitmapImage) + + def test_image_create_photo(self): + self.assertRaises(RuntimeError, tkinter.PhotoImage) + root = tkinter.Tk() + image = tkinter.PhotoImage() + self.assertIn(image.name, tkinter.image_names()) + root.destroy() + tkinter.NoDefaultRoot() + self.assertRaises(RuntimeError, tkinter.PhotoImage) + + class BitmapImageTest(AbstractTkTest, unittest.TestCase): @classmethod @@ -330,7 +371,7 @@ class PhotoImageTest(AbstractTkTest, unittest.TestCase): self.assertEqual(image.transparency_get(4, 6), False) -tests_gui = (MiscTest, BitmapImageTest, PhotoImageTest,) +tests_gui = (MiscTest, DefaultRootTest, BitmapImageTest, PhotoImageTest,) if __name__ == "__main__": support.run_unittest(*tests_gui) diff --git a/Lib/tkinter/test/test_tkinter/test_misc.py b/Lib/tkinter/test/test_tkinter/test_misc.py index b8eea254..f6e5b4db 100644 --- a/Lib/tkinter/test/test_tkinter/test_misc.py +++ b/Lib/tkinter/test/test_tkinter/test_misc.py @@ -1,7 +1,7 @@ import unittest import tkinter from test import support -from tkinter.test.support import AbstractTkTest +from tkinter.test.support import AbstractTkTest, AbstractDefaultRootTest support.requires('gui') @@ -192,6 +192,26 @@ class MiscTest(AbstractTkTest, unittest.TestCase): with self.assertRaises(tkinter.TclError): root.clipboard_get() + def test_winfo_rgb(self): + root = self.root + rgb = root.winfo_rgb + + # Color name. + self.assertEqual(rgb('red'), (65535, 0, 0)) + self.assertEqual(rgb('dark slate blue'), (18504, 15677, 35723)) + # #RGB - extends each 4-bit hex value to be 16-bit. + self.assertEqual(rgb('#F0F'), (0xFFFF, 0x0000, 0xFFFF)) + # #RRGGBB - extends each 8-bit hex value to be 16-bit. + self.assertEqual(rgb('#4a3c8c'), (0x4a4a, 0x3c3c, 0x8c8c)) + # #RRRRGGGGBBBB + self.assertEqual(rgb('#dede14143939'), (0xdede, 0x1414, 0x3939)) + # Invalid string. + with self.assertRaises(tkinter.TclError): + rgb('#123456789a') + # RGB triplet is invalid input. + with self.assertRaises(tkinter.TclError): + rgb((111, 78, 55)) + def test_event_repr_defaults(self): e = tkinter.Event() e.serial = 12345 @@ -241,7 +261,85 @@ class MiscTest(AbstractTkTest, unittest.TestCase): " num=3 delta=-1 focus=True" " x=10 y=20 width=300 height=200>") -tests_gui = (MiscTest, ) + def test_getboolean(self): + for v in 'true', 'yes', 'on', '1', 't', 'y', 1, True: + self.assertIs(self.root.getboolean(v), True) + for v in 'false', 'no', 'off', '0', 'f', 'n', 0, False: + self.assertIs(self.root.getboolean(v), False) + self.assertRaises(ValueError, self.root.getboolean, 'yea') + self.assertRaises(ValueError, self.root.getboolean, '') + self.assertRaises(TypeError, self.root.getboolean, None) + self.assertRaises(TypeError, self.root.getboolean, ()) + + def test_mainloop(self): + log = [] + def callback(): + log.append(1) + self.root.after(100, self.root.quit) + self.root.after(100, callback) + self.root.mainloop(1) + self.assertEqual(log, []) + self.root.mainloop(0) + self.assertEqual(log, [1]) + self.assertTrue(self.root.winfo_exists()) + + +class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase): + + def test_default_root(self): + self.assertIs(tkinter._support_default_root, True) + self.assertIsNone(tkinter._default_root) + root = tkinter.Tk() + root2 = tkinter.Tk() + root3 = tkinter.Tk() + self.assertIs(tkinter._default_root, root) + root2.destroy() + self.assertIs(tkinter._default_root, root) + root.destroy() + self.assertIsNone(tkinter._default_root) + root3.destroy() + self.assertIsNone(tkinter._default_root) + + def test_no_default_root(self): + self.assertIs(tkinter._support_default_root, True) + self.assertIsNone(tkinter._default_root) + root = tkinter.Tk() + self.assertIs(tkinter._default_root, root) + tkinter.NoDefaultRoot() + self.assertIs(tkinter._support_default_root, False) + self.assertFalse(hasattr(tkinter, '_default_root')) + # repeated call is no-op + tkinter.NoDefaultRoot() + self.assertIs(tkinter._support_default_root, False) + self.assertFalse(hasattr(tkinter, '_default_root')) + root.destroy() + self.assertIs(tkinter._support_default_root, False) + self.assertFalse(hasattr(tkinter, '_default_root')) + root = tkinter.Tk() + self.assertIs(tkinter._support_default_root, False) + self.assertFalse(hasattr(tkinter, '_default_root')) + root.destroy() + + def test_getboolean(self): + self.assertRaises(RuntimeError, tkinter.getboolean, '1') + root = tkinter.Tk() + self.assertIs(tkinter.getboolean('1'), True) + self.assertRaises(ValueError, tkinter.getboolean, 'yea') + root.destroy() + tkinter.NoDefaultRoot() + self.assertRaises(RuntimeError, tkinter.getboolean, '1') + + def test_mainloop(self): + self.assertRaises(RuntimeError, tkinter.mainloop) + root = tkinter.Tk() + root.after_idle(root.quit) + tkinter.mainloop() + root.destroy() + tkinter.NoDefaultRoot() + self.assertRaises(RuntimeError, tkinter.mainloop) + + +tests_gui = (MiscTest, DefaultRootTest) if __name__ == "__main__": support.run_unittest(*tests_gui) diff --git a/Lib/tkinter/test/test_tkinter/test_simpledialog.py b/Lib/tkinter/test/test_tkinter/test_simpledialog.py new file mode 100644 index 00000000..91191725 --- /dev/null +++ b/Lib/tkinter/test/test_tkinter/test_simpledialog.py @@ -0,0 +1,25 @@ +import unittest +import tkinter +from test.support import requires, run_unittest, swap_attr +from tkinter.test.support import AbstractDefaultRootTest +from tkinter.simpledialog import Dialog, askinteger + +requires('gui') + + +class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase): + + def test_askinteger(self): + self.assertRaises(RuntimeError, askinteger, "Go To Line", "Line number") + root = tkinter.Tk() + with swap_attr(Dialog, 'wait_window', lambda self, w: w.destroy()): + askinteger("Go To Line", "Line number") + root.destroy() + tkinter.NoDefaultRoot() + self.assertRaises(RuntimeError, askinteger, "Go To Line", "Line number") + + +tests_gui = (DefaultRootTest,) + +if __name__ == "__main__": + run_unittest(*tests_gui) diff --git a/Lib/tkinter/test/test_tkinter/test_variables.py b/Lib/tkinter/test/test_tkinter/test_variables.py index 08b7dedc..6aebe8d1 100644 --- a/Lib/tkinter/test/test_tkinter/test_variables.py +++ b/Lib/tkinter/test/test_tkinter/test_variables.py @@ -1,8 +1,10 @@ import unittest import gc +import tkinter from tkinter import (Variable, StringVar, IntVar, DoubleVar, BooleanVar, Tcl, TclError) from test.support import ALWAYS_EQ +from tkinter.test.support import AbstractDefaultRootTest class Var(Variable): @@ -56,22 +58,32 @@ class TestVariable(TestBase): del v2 self.assertFalse(self.info_exists("name")) - def test___eq__(self): + def test_equality(self): # values doesn't matter, only class and name are checked v1 = Variable(self.root, name="abc") v2 = Variable(self.root, name="abc") self.assertIsNot(v1, v2) self.assertEqual(v1, v2) - v3 = StringVar(self.root, name="abc") + v3 = Variable(self.root, name="cba") self.assertNotEqual(v1, v3) + v4 = StringVar(self.root, name="abc") + self.assertEqual(str(v1), str(v4)) + self.assertNotEqual(v1, v4) + V = type('Variable', (), {}) self.assertNotEqual(v1, V()) self.assertNotEqual(v1, object()) self.assertEqual(v1, ALWAYS_EQ) + root2 = tkinter.Tk() + self.addCleanup(root2.destroy) + v5 = Variable(root2, name="abc") + self.assertEqual(str(v1), str(v5)) + self.assertNotEqual(v1, v5) + def test_invalid_name(self): with self.assertRaises(TypeError): Variable(self.root, name=123) @@ -308,8 +320,21 @@ class TestBooleanVar(TestBase): v.get() +class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase): + + def test_variable(self): + self.assertRaises(RuntimeError, Variable) + root = tkinter.Tk() + v = Variable() + v.set("value") + self.assertEqual(v.get(), "value") + root.destroy() + tkinter.NoDefaultRoot() + self.assertRaises(RuntimeError, Variable) + + tests_gui = (TestVariable, TestStringVar, TestIntVar, - TestDoubleVar, TestBooleanVar) + TestDoubleVar, TestBooleanVar, DefaultRootTest) if __name__ == "__main__": diff --git a/Lib/tkinter/test/test_tkinter/test_widgets.py b/Lib/tkinter/test/test_tkinter/test_widgets.py index b6f792d6..39334de8 100644 --- a/Lib/tkinter/test/test_tkinter/test_widgets.py +++ b/Lib/tkinter/test/test_tkinter/test_widgets.py @@ -2,11 +2,11 @@ import unittest import tkinter from tkinter import TclError import os -import sys from test.support import requires from tkinter.test.support import (tcl_version, requires_tcl, - get_tk_patchlevel, widget_eq) + get_tk_patchlevel, widget_eq, + AbstractDefaultRootTest) from tkinter.test.widget_tests import ( add_standard_options, noconv, pixels_round, AbstractWidgetTest, StandardOptionsTests, IntegerSizeTests, PixelSizeTests, @@ -22,7 +22,7 @@ def float_round(x): class AbstractToplevelTest(AbstractWidgetTest, PixelSizeTests): _conv_pad_pixels = noconv - def test_class(self): + def test_configure_class(self): widget = self.create() self.assertEqual(widget['class'], widget.__class__.__name__.title()) @@ -31,7 +31,7 @@ class AbstractToplevelTest(AbstractWidgetTest, PixelSizeTests): widget2 = self.create(class_='Foo') self.assertEqual(widget2['class'], 'Foo') - def test_colormap(self): + def test_configure_colormap(self): widget = self.create() self.assertEqual(widget['colormap'], '') self.checkInvalidParam(widget, 'colormap', 'new', @@ -39,7 +39,7 @@ class AbstractToplevelTest(AbstractWidgetTest, PixelSizeTests): widget2 = self.create(colormap='new') self.assertEqual(widget2['colormap'], 'new') - def test_container(self): + def test_configure_container(self): widget = self.create() self.assertEqual(widget['container'], 0 if self.wantobjects else '0') self.checkInvalidParam(widget, 'container', 1, @@ -47,7 +47,7 @@ class AbstractToplevelTest(AbstractWidgetTest, PixelSizeTests): widget2 = self.create(container=True) self.assertEqual(widget2['container'], 1 if self.wantobjects else '1') - def test_visual(self): + def test_configure_visual(self): widget = self.create() self.assertEqual(widget['visual'], '') self.checkInvalidParam(widget, 'visual', 'default', @@ -69,13 +69,13 @@ class ToplevelTest(AbstractToplevelTest, unittest.TestCase): def create(self, **kwargs): return tkinter.Toplevel(self.root, **kwargs) - def test_menu(self): + def test_configure_menu(self): widget = self.create() menu = tkinter.Menu(self.root) self.checkParam(widget, 'menu', menu, eq=widget_eq) self.checkParam(widget, 'menu', '') - def test_screen(self): + def test_configure_screen(self): widget = self.create() self.assertEqual(widget['screen'], '') try: @@ -87,7 +87,7 @@ class ToplevelTest(AbstractToplevelTest, unittest.TestCase): widget2 = self.create(screen=display) self.assertEqual(widget2['screen'], display) - def test_use(self): + def test_configure_use(self): widget = self.create() self.assertEqual(widget['use'], '') parent = self.create(container=True) @@ -124,14 +124,14 @@ class LabelFrameTest(AbstractToplevelTest, unittest.TestCase): def create(self, **kwargs): return tkinter.LabelFrame(self.root, **kwargs) - def test_labelanchor(self): + def test_configure_labelanchor(self): widget = self.create() self.checkEnumParam(widget, 'labelanchor', 'e', 'en', 'es', 'n', 'ne', 'nw', 's', 'se', 'sw', 'w', 'wn', 'ws') self.checkInvalidParam(widget, 'labelanchor', 'center') - def test_labelwidget(self): + def test_configure_labelwidget(self): widget = self.create() label = tkinter.Label(self.root, text='Mupp', name='foo') self.checkParam(widget, 'labelwidget', label, expected='.foo') @@ -141,7 +141,7 @@ class LabelFrameTest(AbstractToplevelTest, unittest.TestCase): class AbstractLabelTest(AbstractWidgetTest, IntegerSizeTests): _conv_pixels = noconv - def test_highlightthickness(self): + def test_configure_highlightthickness(self): widget = self.create() self.checkPixelsParam(widget, 'highlightthickness', 0, 1.3, 2.6, 6, -2, '10p') @@ -179,7 +179,7 @@ class ButtonTest(AbstractLabelTest, unittest.TestCase): def create(self, **kwargs): return tkinter.Button(self.root, **kwargs) - def test_default(self): + def test_configure_default(self): widget = self.create() self.checkEnumParam(widget, 'default', 'active', 'disabled', 'normal') @@ -204,11 +204,11 @@ class CheckbuttonTest(AbstractLabelTest, unittest.TestCase): return tkinter.Checkbutton(self.root, **kwargs) - def test_offvalue(self): + def test_configure_offvalue(self): widget = self.create() self.checkParams(widget, 'offvalue', 1, 2.3, '', 'any string') - def test_onvalue(self): + def test_configure_onvalue(self): widget = self.create() self.checkParams(widget, 'onvalue', 1, 2.3, '', 'any string') @@ -231,7 +231,7 @@ class RadiobuttonTest(AbstractLabelTest, unittest.TestCase): def create(self, **kwargs): return tkinter.Radiobutton(self.root, **kwargs) - def test_value(self): + def test_configure_value(self): widget = self.create() self.checkParams(widget, 'value', 1, 2.3, '', 'any string') @@ -254,20 +254,19 @@ class MenubuttonTest(AbstractLabelTest, unittest.TestCase): def create(self, **kwargs): return tkinter.Menubutton(self.root, **kwargs) - def test_direction(self): + def test_configure_direction(self): widget = self.create() self.checkEnumParam(widget, 'direction', 'above', 'below', 'flush', 'left', 'right') - def test_height(self): + def test_configure_height(self): widget = self.create() self.checkIntegerParam(widget, 'height', 100, -100, 0, conv=str) - test_highlightthickness = StandardOptionsTests.test_highlightthickness + test_configure_highlightthickness = \ + StandardOptionsTests.test_configure_highlightthickness - @unittest.skipIf(sys.platform == 'darwin', - 'crashes with Cocoa Tk (issue19733)') - def test_image(self): + def test_configure_image(self): widget = self.create() image = tkinter.PhotoImage(master=self.root, name='image1') self.checkParam(widget, 'image', image, conv=str) @@ -281,23 +280,23 @@ class MenubuttonTest(AbstractLabelTest, unittest.TestCase): if errmsg is not None: self.assertEqual(str(cm.exception), errmsg) - def test_menu(self): + def test_configure_menu(self): widget = self.create() menu = tkinter.Menu(widget, name='menu') self.checkParam(widget, 'menu', menu, eq=widget_eq) menu.destroy() - def test_padx(self): + def test_configure_padx(self): widget = self.create() self.checkPixelsParam(widget, 'padx', 3, 4.4, 5.6, '12m') self.checkParam(widget, 'padx', -2, expected=0) - def test_pady(self): + def test_configure_pady(self): widget = self.create() self.checkPixelsParam(widget, 'pady', 3, 4.4, 5.6, '12m') self.checkParam(widget, 'pady', -2, expected=0) - def test_width(self): + def test_configure_width(self): widget = self.create() self.checkIntegerParam(widget, 'width', 402, -402, 0, conv=str) @@ -330,18 +329,18 @@ class EntryTest(AbstractWidgetTest, unittest.TestCase): def create(self, **kwargs): return tkinter.Entry(self.root, **kwargs) - def test_disabledbackground(self): + def test_configure_disabledbackground(self): widget = self.create() self.checkColorParam(widget, 'disabledbackground') - def test_insertborderwidth(self): + def test_configure_insertborderwidth(self): widget = self.create(insertwidth=100) self.checkPixelsParam(widget, 'insertborderwidth', 0, 1.3, 2.6, 6, -2, '10p') # insertborderwidth is bounded above by a half of insertwidth. self.checkParam(widget, 'insertborderwidth', 60, expected=100//2) - def test_insertwidth(self): + def test_configure_insertwidth(self): widget = self.create() self.checkPixelsParam(widget, 'insertwidth', 1.3, 3.6, '10p') self.checkParam(widget, 'insertwidth', 0.1, expected=2) @@ -351,32 +350,32 @@ class EntryTest(AbstractWidgetTest, unittest.TestCase): else: self.checkParam(widget, 'insertwidth', 0.9, expected=1) - def test_invalidcommand(self): + def test_configure_invalidcommand(self): widget = self.create() self.checkCommandParam(widget, 'invalidcommand') self.checkCommandParam(widget, 'invcmd') - def test_readonlybackground(self): + def test_configure_readonlybackground(self): widget = self.create() self.checkColorParam(widget, 'readonlybackground') - def test_show(self): + def test_configure_show(self): widget = self.create() self.checkParam(widget, 'show', '*') self.checkParam(widget, 'show', '') self.checkParam(widget, 'show', ' ') - def test_state(self): + def test_configure_state(self): widget = self.create() self.checkEnumParam(widget, 'state', 'disabled', 'normal', 'readonly') - def test_validate(self): + def test_configure_validate(self): widget = self.create() self.checkEnumParam(widget, 'validate', 'all', 'key', 'focus', 'focusin', 'focusout', 'none') - def test_validatecommand(self): + def test_configure_validatecommand(self): widget = self.create() self.checkCommandParam(widget, 'validatecommand') self.checkCommandParam(widget, 'vcmd') @@ -429,25 +428,25 @@ class SpinboxTest(EntryTest, unittest.TestCase): def create(self, **kwargs): return tkinter.Spinbox(self.root, **kwargs) - test_show = None + test_configure_show = None - def test_buttonbackground(self): + def test_configure_buttonbackground(self): widget = self.create() self.checkColorParam(widget, 'buttonbackground') - def test_buttoncursor(self): + def test_configure_buttoncursor(self): widget = self.create() self.checkCursorParam(widget, 'buttoncursor') - def test_buttondownrelief(self): + def test_configure_buttondownrelief(self): widget = self.create() self.checkReliefParam(widget, 'buttondownrelief') - def test_buttonuprelief(self): + def test_configure_buttonuprelief(self): widget = self.create() self.checkReliefParam(widget, 'buttonuprelief') - def test_format(self): + def test_configure_format(self): widget = self.create() self.checkParam(widget, 'format', '%2f') self.checkParam(widget, 'format', '%2.2f') @@ -462,25 +461,25 @@ class SpinboxTest(EntryTest, unittest.TestCase): self.checkParam(widget, 'format', '%09.200f') self.checkInvalidParam(widget, 'format', '%d') - def test_from(self): + def test_configure_from(self): widget = self.create() self.checkParam(widget, 'to', 100.0) self.checkFloatParam(widget, 'from', -10, 10.2, 11.7) self.checkInvalidParam(widget, 'from', 200, errmsg='-to value must be greater than -from value') - def test_increment(self): + def test_configure_increment(self): widget = self.create() self.checkFloatParam(widget, 'increment', -1, 1, 10.2, 12.8, 0) - def test_to(self): + def test_configure_to(self): widget = self.create() self.checkParam(widget, 'from', -100.0) self.checkFloatParam(widget, 'to', -10, 10.2, 11.7) self.checkInvalidParam(widget, 'to', -200, errmsg='-to value must be greater than -from value') - def test_values(self): + def test_configure_values(self): # XXX widget = self.create() self.assertEqual(widget['values'], '') @@ -491,7 +490,7 @@ class SpinboxTest(EntryTest, unittest.TestCase): expected='42 3.14 {} {any string}') self.checkParam(widget, 'values', '') - def test_wrap(self): + def test_configure_wrap(self): widget = self.create() self.checkBooleanParam(widget, 'wrap') @@ -557,17 +556,17 @@ class TextTest(AbstractWidgetTest, unittest.TestCase): def create(self, **kwargs): return tkinter.Text(self.root, **kwargs) - def test_autoseparators(self): + def test_configure_autoseparators(self): widget = self.create() self.checkBooleanParam(widget, 'autoseparators') @requires_tcl(8, 5) - def test_blockcursor(self): + def test_configure_blockcursor(self): widget = self.create() self.checkBooleanParam(widget, 'blockcursor') @requires_tcl(8, 5) - def test_endline(self): + def test_configure_endline(self): widget = self.create() text = '\n'.join('Line %d' for i in range(100)) widget.insert('end', text) @@ -580,50 +579,50 @@ class TextTest(AbstractWidgetTest, unittest.TestCase): self.checkInvalidParam(widget, 'endline', 10, errmsg='-startline must be less than or equal to -endline') - def test_height(self): + def test_configure_height(self): widget = self.create() self.checkPixelsParam(widget, 'height', 100, 101.2, 102.6, '3c') self.checkParam(widget, 'height', -100, expected=1) self.checkParam(widget, 'height', 0, expected=1) - def test_maxundo(self): + def test_configure_maxundo(self): widget = self.create() self.checkIntegerParam(widget, 'maxundo', 0, 5, -1) @requires_tcl(8, 5) - def test_inactiveselectbackground(self): + def test_configure_inactiveselectbackground(self): widget = self.create() self.checkColorParam(widget, 'inactiveselectbackground') @requires_tcl(8, 6) - def test_insertunfocussed(self): + def test_configure_insertunfocussed(self): widget = self.create() self.checkEnumParam(widget, 'insertunfocussed', 'hollow', 'none', 'solid') - def test_selectborderwidth(self): + def test_configure_selectborderwidth(self): widget = self.create() self.checkPixelsParam(widget, 'selectborderwidth', 1.3, 2.6, -2, '10p', conv=noconv, keep_orig=tcl_version >= (8, 5)) - def test_spacing1(self): + def test_configure_spacing1(self): widget = self.create() self.checkPixelsParam(widget, 'spacing1', 20, 21.4, 22.6, '0.5c') self.checkParam(widget, 'spacing1', -5, expected=0) - def test_spacing2(self): + def test_configure_spacing2(self): widget = self.create() self.checkPixelsParam(widget, 'spacing2', 5, 6.4, 7.6, '0.1c') self.checkParam(widget, 'spacing2', -1, expected=0) - def test_spacing3(self): + def test_configure_spacing3(self): widget = self.create() self.checkPixelsParam(widget, 'spacing3', 20, 21.4, 22.6, '0.5c') self.checkParam(widget, 'spacing3', -10, expected=0) @requires_tcl(8, 5) - def test_startline(self): + def test_configure_startline(self): widget = self.create() text = '\n'.join('Line %d' for i in range(100)) widget.insert('end', text) @@ -636,14 +635,14 @@ class TextTest(AbstractWidgetTest, unittest.TestCase): self.checkInvalidParam(widget, 'startline', 70, errmsg='-startline must be less than or equal to -endline') - def test_state(self): + def test_configure_state(self): widget = self.create() if tcl_version < (8, 5): self.checkParams(widget, 'state', 'disabled', 'normal') else: self.checkEnumParam(widget, 'state', 'disabled', 'normal') - def test_tabs(self): + def test_configure_tabs(self): widget = self.create() if get_tk_patchlevel() < (8, 5, 11): self.checkParam(widget, 'tabs', (10.2, 20.7, '1i', '2i'), @@ -659,21 +658,21 @@ class TextTest(AbstractWidgetTest, unittest.TestCase): keep_orig=tcl_version >= (8, 5)) @requires_tcl(8, 5) - def test_tabstyle(self): + def test_configure_tabstyle(self): widget = self.create() self.checkEnumParam(widget, 'tabstyle', 'tabular', 'wordprocessor') - def test_undo(self): + def test_configure_undo(self): widget = self.create() self.checkBooleanParam(widget, 'undo') - def test_width(self): + def test_configure_width(self): widget = self.create() self.checkIntegerParam(widget, 'width', 402) self.checkParam(widget, 'width', -402, expected=1) self.checkParam(widget, 'width', 0, expected=1) - def test_wrap(self): + def test_configure_wrap(self): widget = self.create() if tcl_version < (8, 5): self.checkParams(widget, 'wrap', 'char', 'none', 'word') @@ -711,16 +710,16 @@ class CanvasTest(AbstractWidgetTest, unittest.TestCase): def create(self, **kwargs): return tkinter.Canvas(self.root, **kwargs) - def test_closeenough(self): + def test_configure_closeenough(self): widget = self.create() self.checkFloatParam(widget, 'closeenough', 24, 2.4, 3.6, -3, conv=float) - def test_confine(self): + def test_configure_confine(self): widget = self.create() self.checkBooleanParam(widget, 'confine') - def test_offset(self): + def test_configure_offset(self): widget = self.create() self.assertEqual(widget['offset'], '0,0') self.checkParams(widget, 'offset', @@ -729,7 +728,7 @@ class CanvasTest(AbstractWidgetTest, unittest.TestCase): self.checkParam(widget, 'offset', '#5,6') self.checkInvalidParam(widget, 'offset', 'spam') - def test_scrollregion(self): + def test_configure_scrollregion(self): widget = self.create() self.checkParam(widget, 'scrollregion', '0 0 200 150') self.checkParam(widget, 'scrollregion', (0, 0, 200, 150), @@ -741,17 +740,17 @@ class CanvasTest(AbstractWidgetTest, unittest.TestCase): self.checkInvalidParam(widget, 'scrollregion', (0, 0, 200)) self.checkInvalidParam(widget, 'scrollregion', (0, 0, 200, 150, 0)) - def test_state(self): + def test_configure_state(self): widget = self.create() self.checkEnumParam(widget, 'state', 'disabled', 'normal', errmsg='bad state value "{}": must be normal or disabled') - def test_xscrollincrement(self): + def test_configure_xscrollincrement(self): widget = self.create() self.checkPixelsParam(widget, 'xscrollincrement', 40, 0, 41.2, 43.6, -40, '0.5i') - def test_yscrollincrement(self): + def test_configure_yscrollincrement(self): widget = self.create() self.checkPixelsParam(widget, 'yscrollincrement', 10, 0, 11.2, 13.6, -10, '0.1i') @@ -796,26 +795,26 @@ class ListboxTest(AbstractWidgetTest, unittest.TestCase): def create(self, **kwargs): return tkinter.Listbox(self.root, **kwargs) - def test_activestyle(self): + def test_configure_activestyle(self): widget = self.create() self.checkEnumParam(widget, 'activestyle', 'dotbox', 'none', 'underline') - test_justify = requires_tcl(8, 6, 5)(StandardOptionsTests.test_justify) + test_justify = requires_tcl(8, 6, 5)(StandardOptionsTests.test_configure_justify) - def test_listvariable(self): + def test_configure_listvariable(self): widget = self.create() var = tkinter.DoubleVar(self.root) self.checkVariableParam(widget, 'listvariable', var) - def test_selectmode(self): + def test_configure_selectmode(self): widget = self.create() self.checkParam(widget, 'selectmode', 'single') self.checkParam(widget, 'selectmode', 'browse') self.checkParam(widget, 'selectmode', 'multiple') self.checkParam(widget, 'selectmode', 'extended') - def test_state(self): + def test_configure_state(self): widget = self.create() self.checkEnumParam(widget, 'state', 'disabled', 'normal') @@ -930,53 +929,53 @@ class ScaleTest(AbstractWidgetTest, unittest.TestCase): def create(self, **kwargs): return tkinter.Scale(self.root, **kwargs) - def test_bigincrement(self): + def test_configure_bigincrement(self): widget = self.create() self.checkFloatParam(widget, 'bigincrement', 12.4, 23.6, -5) - def test_digits(self): + def test_configure_digits(self): widget = self.create() self.checkIntegerParam(widget, 'digits', 5, 0) - def test_from(self): + def test_configure_from(self): widget = self.create() conv = False if get_tk_patchlevel() >= (8, 6, 10) else float_round self.checkFloatParam(widget, 'from', 100, 14.9, 15.1, conv=conv) - def test_label(self): + def test_configure_label(self): widget = self.create() self.checkParam(widget, 'label', 'any string') self.checkParam(widget, 'label', '') - def test_length(self): + def test_configure_length(self): widget = self.create() self.checkPixelsParam(widget, 'length', 130, 131.2, 135.6, '5i') - def test_resolution(self): + def test_configure_resolution(self): widget = self.create() self.checkFloatParam(widget, 'resolution', 4.2, 0, 6.7, -2) - def test_showvalue(self): + def test_configure_showvalue(self): widget = self.create() self.checkBooleanParam(widget, 'showvalue') - def test_sliderlength(self): + def test_configure_sliderlength(self): widget = self.create() self.checkPixelsParam(widget, 'sliderlength', 10, 11.2, 15.6, -3, '3m') - def test_sliderrelief(self): + def test_configure_sliderrelief(self): widget = self.create() self.checkReliefParam(widget, 'sliderrelief') - def test_tickinterval(self): + def test_configure_tickinterval(self): widget = self.create() self.checkFloatParam(widget, 'tickinterval', 1, 4.3, 7.6, 0, conv=float_round) self.checkParam(widget, 'tickinterval', -2, expected=2, conv=float_round) - def test_to(self): + def test_configure_to(self): widget = self.create() self.checkFloatParam(widget, 'to', 300, 14.9, 15.1, -10, conv=float_round) @@ -1000,15 +999,15 @@ class ScrollbarTest(AbstractWidgetTest, unittest.TestCase): def create(self, **kwargs): return tkinter.Scrollbar(self.root, **kwargs) - def test_activerelief(self): + def test_configure_activerelief(self): widget = self.create() self.checkReliefParam(widget, 'activerelief') - def test_elementborderwidth(self): + def test_configure_elementborderwidth(self): widget = self.create() self.checkPixelsParam(widget, 'elementborderwidth', 4.3, 5.6, -2, '1m') - def test_orient(self): + def test_configure_orient(self): widget = self.create() self.checkEnumParam(widget, 'orient', 'vertical', 'horizontal', errmsg='bad orientation "{}": must be vertical or horizontal') @@ -1049,63 +1048,63 @@ class PanedWindowTest(AbstractWidgetTest, unittest.TestCase): def create(self, **kwargs): return tkinter.PanedWindow(self.root, **kwargs) - def test_handlepad(self): + def test_configure_handlepad(self): widget = self.create() self.checkPixelsParam(widget, 'handlepad', 5, 6.4, 7.6, -3, '1m') - def test_handlesize(self): + def test_configure_handlesize(self): widget = self.create() self.checkPixelsParam(widget, 'handlesize', 8, 9.4, 10.6, -3, '2m', conv=noconv) - def test_height(self): + def test_configure_height(self): widget = self.create() self.checkPixelsParam(widget, 'height', 100, 101.2, 102.6, -100, 0, '1i', conv=noconv) - def test_opaqueresize(self): + def test_configure_opaqueresize(self): widget = self.create() self.checkBooleanParam(widget, 'opaqueresize') @requires_tcl(8, 6, 5) - def test_proxybackground(self): + def test_configure_proxybackground(self): widget = self.create() self.checkColorParam(widget, 'proxybackground') @requires_tcl(8, 6, 5) - def test_proxyborderwidth(self): + def test_configure_proxyborderwidth(self): widget = self.create() self.checkPixelsParam(widget, 'proxyborderwidth', 0, 1.3, 2.9, 6, -2, '10p', conv=noconv) @requires_tcl(8, 6, 5) - def test_proxyrelief(self): + def test_configure_proxyrelief(self): widget = self.create() self.checkReliefParam(widget, 'proxyrelief') - def test_sashcursor(self): + def test_configure_sashcursor(self): widget = self.create() self.checkCursorParam(widget, 'sashcursor') - def test_sashpad(self): + def test_configure_sashpad(self): widget = self.create() self.checkPixelsParam(widget, 'sashpad', 8, 1.3, 2.6, -2, '2m') - def test_sashrelief(self): + def test_configure_sashrelief(self): widget = self.create() self.checkReliefParam(widget, 'sashrelief') - def test_sashwidth(self): + def test_configure_sashwidth(self): widget = self.create() self.checkPixelsParam(widget, 'sashwidth', 10, 11.1, 15.6, -3, '1m', conv=noconv) - def test_showhandle(self): + def test_configure_showhandle(self): widget = self.create() self.checkBooleanParam(widget, 'showhandle') - def test_width(self): + def test_configure_width(self): widget = self.create() self.checkPixelsParam(widget, 'width', 402, 403.4, 404.6, -402, 0, '5i', conv=noconv) @@ -1224,23 +1223,23 @@ class MenuTest(AbstractWidgetTest, unittest.TestCase): def create(self, **kwargs): return tkinter.Menu(self.root, **kwargs) - def test_postcommand(self): + def test_configure_postcommand(self): widget = self.create() self.checkCommandParam(widget, 'postcommand') - def test_tearoff(self): + def test_configure_tearoff(self): widget = self.create() self.checkBooleanParam(widget, 'tearoff') - def test_tearoffcommand(self): + def test_configure_tearoffcommand(self): widget = self.create() self.checkCommandParam(widget, 'tearoffcommand') - def test_title(self): + def test_configure_title(self): widget = self.create() self.checkParam(widget, 'title', 'any string') - def test_type(self): + def test_configure_type(self): widget = self.create() self.checkEnumParam(widget, 'type', 'normal', 'tearoff', 'menubar') @@ -1293,17 +1292,26 @@ class MessageTest(AbstractWidgetTest, unittest.TestCase): def create(self, **kwargs): return tkinter.Message(self.root, **kwargs) - def test_aspect(self): + def test_configure_aspect(self): widget = self.create() self.checkIntegerParam(widget, 'aspect', 250, 0, -300) +class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase): + + def test_frame(self): + self._test_widget(tkinter.Frame) + + def test_label(self): + self._test_widget(tkinter.Label) + + tests_gui = ( ButtonTest, CanvasTest, CheckbuttonTest, EntryTest, FrameTest, LabelFrameTest,LabelTest, ListboxTest, MenubuttonTest, MenuTest, MessageTest, OptionMenuTest, PanedWindowTest, RadiobuttonTest, ScaleTest, ScrollbarTest, - SpinboxTest, TextTest, ToplevelTest, + SpinboxTest, TextTest, ToplevelTest, DefaultRootTest, ) if __name__ == '__main__': diff --git a/Lib/tkinter/test/test_ttk/test_extensions.py b/Lib/tkinter/test/test_ttk/test_extensions.py index 6937ba1c..1a70e0be 100644 --- a/Lib/tkinter/test/test_ttk/test_extensions.py +++ b/Lib/tkinter/test/test_ttk/test_extensions.py @@ -2,8 +2,8 @@ import sys import unittest import tkinter from tkinter import ttk -from test.support import requires, run_unittest, swap_attr -from tkinter.test.support import AbstractTkTest, destroy_default_root +from test.support import requires, run_unittest +from tkinter.test.support import AbstractTkTest, AbstractDefaultRootTest requires('gui') @@ -46,20 +46,6 @@ class LabeledScaleTest(AbstractTkTest, unittest.TestCase): if hasattr(sys, 'last_type'): self.assertNotEqual(sys.last_type, tkinter.TclError) - - def test_initialization_no_master(self): - # no master passing - with swap_attr(tkinter, '_default_root', None), \ - swap_attr(tkinter, '_support_default_root', True): - try: - x = ttk.LabeledScale() - self.assertIsNotNone(tkinter._default_root) - self.assertEqual(x.master, tkinter._default_root) - self.assertEqual(x.tk, tkinter._default_root.tk) - x.destroy() - finally: - destroy_default_root() - def test_initialization(self): # master passing master = tkinter.Frame(self.root) @@ -311,7 +297,13 @@ class OptionMenuTest(AbstractTkTest, unittest.TestCase): optmenu2.destroy() -tests_gui = (LabeledScaleTest, OptionMenuTest) +class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase): + + def test_labeledscale(self): + self._test_widget(ttk.LabeledScale) + + +tests_gui = (LabeledScaleTest, OptionMenuTest, DefaultRootTest) if __name__ == "__main__": run_unittest(*tests_gui) diff --git a/Lib/tkinter/test/test_ttk/test_widgets.py b/Lib/tkinter/test/test_ttk/test_widgets.py index 157ef0e8..1fac83a0 100644 --- a/Lib/tkinter/test/test_ttk/test_widgets.py +++ b/Lib/tkinter/test/test_ttk/test_widgets.py @@ -6,7 +6,7 @@ import sys from tkinter.test.test_ttk.test_functions import MockTclObj from tkinter.test.support import (AbstractTkTest, tcl_version, get_tk_patchlevel, - simulate_mouse_click) + simulate_mouse_click, AbstractDefaultRootTest) from tkinter.test.widget_tests import (add_standard_options, noconv, AbstractWidgetTest, StandardOptionsTests, IntegerSizeTests, PixelSizeTests, setUpModule) @@ -16,7 +16,7 @@ requires('gui') class StandardTtkOptionsTests(StandardOptionsTests): - def test_class(self): + def test_configure_class(self): widget = self.create() self.assertEqual(widget['class'], '') errmsg='attempt to change read-only option' @@ -26,7 +26,7 @@ class StandardTtkOptionsTests(StandardOptionsTests): widget2 = self.create(class_='Foo') self.assertEqual(widget2['class'], 'Foo') - def test_padding(self): + def test_configure_padding(self): widget = self.create() self.checkParam(widget, 'padding', 0, expected=('0',)) self.checkParam(widget, 'padding', 5, expected=('5',)) @@ -38,7 +38,7 @@ class StandardTtkOptionsTests(StandardOptionsTests): self.checkParam(widget, 'padding', ('5p', '6p', '7p', '8p')) self.checkParam(widget, 'padding', (), expected='') - def test_style(self): + def test_configure_style(self): widget = self.create() self.assertEqual(widget['style'], '') errmsg = 'Layout Foo not found' @@ -139,14 +139,14 @@ class LabelFrameTest(AbstractToplevelTest, unittest.TestCase): def create(self, **kwargs): return ttk.LabelFrame(self.root, **kwargs) - def test_labelanchor(self): + def test_configure_labelanchor(self): widget = self.create() self.checkEnumParam(widget, 'labelanchor', 'e', 'en', 'es', 'n', 'ne', 'nw', 's', 'se', 'sw', 'w', 'wn', 'ws', errmsg='Bad label anchor specification {}') self.checkInvalidParam(widget, 'labelanchor', 'center') - def test_labelwidget(self): + def test_configure_labelwidget(self): widget = self.create() label = ttk.Label(self.root, text='Mupp', name='foo') self.checkParam(widget, 'labelwidget', label, expected='.foo') @@ -168,17 +168,17 @@ class AbstractLabelTest(AbstractWidgetTest): self.checkInvalidParam(widget, name, 'spam', errmsg='image "spam" doesn\'t exist') - def test_compound(self): + def test_configure_compound(self): widget = self.create() self.checkEnumParam(widget, 'compound', 'none', 'text', 'image', 'center', 'top', 'bottom', 'left', 'right') - def test_state(self): + def test_configure_state(self): widget = self.create() self.checkParams(widget, 'state', 'active', 'disabled', 'normal') - def test_width(self): + def test_configure_width(self): widget = self.create() self.checkParams(widget, 'width', 402, -402, 0) @@ -197,7 +197,7 @@ class LabelTest(AbstractLabelTest, unittest.TestCase): def create(self, **kwargs): return ttk.Label(self.root, **kwargs) - def test_font(self): + def test_configure_font(self): widget = self.create() self.checkParam(widget, 'font', '-Adobe-Helvetica-Medium-R-Normal--*-120-*-*-*-*-*-*') @@ -215,7 +215,7 @@ class ButtonTest(AbstractLabelTest, unittest.TestCase): def create(self, **kwargs): return ttk.Button(self.root, **kwargs) - def test_default(self): + def test_configure_default(self): widget = self.create() self.checkEnumParam(widget, 'default', 'normal', 'active', 'disabled') @@ -240,11 +240,11 @@ class CheckbuttonTest(AbstractLabelTest, unittest.TestCase): def create(self, **kwargs): return ttk.Checkbutton(self.root, **kwargs) - def test_offvalue(self): + def test_configure_offvalue(self): widget = self.create() self.checkParams(widget, 'offvalue', 1, 2.3, '', 'any string') - def test_onvalue(self): + def test_configure_onvalue(self): widget = self.create() self.checkParams(widget, 'onvalue', 1, 2.3, '', 'any string') @@ -292,27 +292,27 @@ class EntryTest(AbstractWidgetTest, unittest.TestCase): def create(self, **kwargs): return ttk.Entry(self.root, **kwargs) - def test_invalidcommand(self): + def test_configure_invalidcommand(self): widget = self.create() self.checkCommandParam(widget, 'invalidcommand') - def test_show(self): + def test_configure_show(self): widget = self.create() self.checkParam(widget, 'show', '*') self.checkParam(widget, 'show', '') self.checkParam(widget, 'show', ' ') - def test_state(self): + def test_configure_state(self): widget = self.create() self.checkParams(widget, 'state', 'disabled', 'normal', 'readonly') - def test_validate(self): + def test_configure_validate(self): widget = self.create() self.checkEnumParam(widget, 'validate', 'all', 'key', 'focus', 'focusin', 'focusout', 'none') - def test_validatecommand(self): + def test_configure_validatecommand(self): widget = self.create() self.checkCommandParam(widget, 'validatecommand') @@ -429,7 +429,7 @@ class ComboboxTest(EntryTest, unittest.TestCase): def create(self, **kwargs): return ttk.Combobox(self.root, **kwargs) - def test_height(self): + def test_configure_height(self): widget = self.create() self.checkParams(widget, 'height', 100, 101.2, 102.6, -100, 0, '1i') @@ -459,7 +459,7 @@ class ComboboxTest(EntryTest, unittest.TestCase): self.assertTrue(success) - def test_postcommand(self): + def test_configure_postcommand(self): success = [] self.combo['postcommand'] = lambda: success.append(True) @@ -475,7 +475,7 @@ class ComboboxTest(EntryTest, unittest.TestCase): self.assertEqual(len(success), 1) - def test_values(self): + def test_configure_values(self): def check_get_current(getval, currval): self.assertEqual(self.combo.get(), getval) self.assertEqual(self.combo.current(), currval) @@ -551,7 +551,7 @@ class PanedWindowTest(AbstractWidgetTest, unittest.TestCase): def create(self, **kwargs): return ttk.PanedWindow(self.root, **kwargs) - def test_orient(self): + def test_configure_orient(self): widget = self.create() self.assertEqual(str(widget['orient']), 'vertical') errmsg='attempt to change read-only option' @@ -684,11 +684,11 @@ class RadiobuttonTest(AbstractLabelTest, unittest.TestCase): def create(self, **kwargs): return ttk.Radiobutton(self.root, **kwargs) - def test_value(self): + def test_configure_value(self): widget = self.create() self.checkParams(widget, 'value', 1, 2.3, '', 'any string') - def test_invoke(self): + def test_configure_invoke(self): success = [] def cb_test(): success.append(1) @@ -739,7 +739,7 @@ class MenubuttonTest(AbstractLabelTest, unittest.TestCase): self.checkEnumParam(widget, 'direction', 'above', 'below', 'left', 'right', 'flush') - def test_menu(self): + def test_configure_menu(self): widget = self.create() menu = tkinter.Menu(widget, name='menu') self.checkParam(widget, 'menu', menu, conv=str) @@ -764,19 +764,19 @@ class ScaleTest(AbstractWidgetTest, unittest.TestCase): def create(self, **kwargs): return ttk.Scale(self.root, **kwargs) - def test_from(self): + def test_configure_from(self): widget = self.create() self.checkFloatParam(widget, 'from', 100, 14.9, 15.1, conv=False) - def test_length(self): + def test_configure_length(self): widget = self.create() self.checkPixelsParam(widget, 'length', 130, 131.2, 135.6, '5i') - def test_to(self): + def test_configure_to(self): widget = self.create() self.checkFloatParam(widget, 'to', 300, 14.9, 15.1, -10, conv=False) - def test_value(self): + def test_configure_value(self): widget = self.create() self.checkFloatParam(widget, 'value', 300, 14.9, 15.1, -10, conv=False) @@ -866,23 +866,23 @@ class ProgressbarTest(AbstractWidgetTest, unittest.TestCase): def create(self, **kwargs): return ttk.Progressbar(self.root, **kwargs) - def test_length(self): + def test_configure_length(self): widget = self.create() self.checkPixelsParam(widget, 'length', 100.1, 56.7, '2i') - def test_maximum(self): + def test_configure_maximum(self): widget = self.create() self.checkFloatParam(widget, 'maximum', 150.2, 77.7, 0, -10, conv=False) - def test_mode(self): + def test_configure_mode(self): widget = self.create() self.checkEnumParam(widget, 'mode', 'determinate', 'indeterminate') - def test_phase(self): + def test_configure_phase(self): # XXX pass - def test_value(self): + def test_configure_value(self): widget = self.create() self.checkFloatParam(widget, 'value', 150.2, 77.7, 0, -10, conv=False) @@ -1071,7 +1071,7 @@ class NotebookTest(AbstractWidgetTest, unittest.TestCase): self.assertEqual(self.nb.tab(self.child1, 'text'), 'abc') - def test_tabs(self): + def test_configure_tabs(self): self.assertEqual(len(self.nb.tabs()), 2) self.nb.forget(self.child1) @@ -1147,7 +1147,7 @@ class SpinboxTest(EntryTest, unittest.TestCase): self.spin.event_generate('', x=x, y=y) self.spin.update_idletasks() - def test_command(self): + def test_configure_command(self): success = [] self.spin['command'] = lambda: success.append(True) @@ -1167,7 +1167,7 @@ class SpinboxTest(EntryTest, unittest.TestCase): self.spin.update() self.assertEqual(len(success), 2) - def test_to(self): + def test_configure_to(self): self.spin['from'] = 0 self.spin['to'] = 5 self.spin.set(4) @@ -1179,7 +1179,7 @@ class SpinboxTest(EntryTest, unittest.TestCase): self._click_increment_arrow() # 5 self.assertEqual(self.spin.get(), '5') - def test_from(self): + def test_configure_from(self): self.spin['from'] = 1 self.spin['to'] = 10 self.spin.set(2) @@ -1189,7 +1189,7 @@ class SpinboxTest(EntryTest, unittest.TestCase): self._click_decrement_arrow() # 1 self.assertEqual(self.spin.get(), '1') - def test_increment(self): + def test_configure_increment(self): self.spin['from'] = 0 self.spin['to'] = 10 self.spin['increment'] = 4 @@ -1203,7 +1203,7 @@ class SpinboxTest(EntryTest, unittest.TestCase): self._click_decrement_arrow() # 3 self.assertEqual(self.spin.get(), '3') - def test_format(self): + def test_configure_format(self): self.spin.set(1) self.spin['format'] = '%10.3f' self.spin.update() @@ -1220,7 +1220,7 @@ class SpinboxTest(EntryTest, unittest.TestCase): self.assertTrue('.' not in value) self.assertEqual(len(value), 1) - def test_wrap(self): + def test_configure_wrap(self): self.spin['to'] = 10 self.spin['from'] = 1 self.spin.set(1) @@ -1239,7 +1239,7 @@ class SpinboxTest(EntryTest, unittest.TestCase): self._click_decrement_arrow() self.assertEqual(self.spin.get(), '1') - def test_values(self): + def test_configure_values(self): self.assertEqual(self.spin['values'], () if tcl_version < (8, 5) else '') self.checkParam(self.spin, 'values', 'mon tue wed thur', @@ -1299,14 +1299,14 @@ class TreeviewTest(AbstractWidgetTest, unittest.TestCase): def create(self, **kwargs): return ttk.Treeview(self.root, **kwargs) - def test_columns(self): + def test_configure_columns(self): widget = self.create() self.checkParam(widget, 'columns', 'a b c', expected=('a', 'b', 'c')) self.checkParam(widget, 'columns', ('a', 'b', 'c')) self.checkParam(widget, 'columns', '') - def test_displaycolumns(self): + def test_configure_displaycolumns(self): widget = self.create() widget['columns'] = ('a', 'b', 'c') self.checkParam(widget, 'displaycolumns', 'b a c', @@ -1322,17 +1322,17 @@ class TreeviewTest(AbstractWidgetTest, unittest.TestCase): self.checkInvalidParam(widget, 'displaycolumns', (1, -2), errmsg='Column index -2 out of bounds') - def test_height(self): + def test_configure_height(self): widget = self.create() self.checkPixelsParam(widget, 'height', 100, -100, 0, '3c', conv=False) self.checkPixelsParam(widget, 'height', 101.2, 102.6, conv=noconv) - def test_selectmode(self): + def test_configure_selectmode(self): widget = self.create() self.checkEnumParam(widget, 'selectmode', 'none', 'browse', 'extended') - def test_show(self): + def test_configure_show(self): widget = self.create() self.checkParam(widget, 'show', 'tree headings', expected=('tree', 'headings')) @@ -1860,12 +1860,22 @@ class SizegripTest(AbstractWidgetTest, unittest.TestCase): def create(self, **kwargs): return ttk.Sizegrip(self.root, **kwargs) + +class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase): + + def test_frame(self): + self._test_widget(ttk.Frame) + + def test_label(self): + self._test_widget(ttk.Label) + + tests_gui = ( ButtonTest, CheckbuttonTest, ComboboxTest, EntryTest, FrameTest, LabelFrameTest, LabelTest, MenubuttonTest, NotebookTest, PanedWindowTest, ProgressbarTest, RadiobuttonTest, ScaleTest, ScrollbarTest, SeparatorTest, - SizegripTest, SpinboxTest, TreeviewTest, WidgetTest, + SizegripTest, SpinboxTest, TreeviewTest, WidgetTest, DefaultRootTest, ) if __name__ == "__main__": diff --git a/Lib/tkinter/test/widget_tests.py b/Lib/tkinter/test/widget_tests.py index b42ff521..9702ff45 100644 --- a/Lib/tkinter/test/widget_tests.py +++ b/Lib/tkinter/test/widget_tests.py @@ -1,7 +1,6 @@ # Common tests for test_tkinter/test_widgets.py and test_ttk/test_widgets.py import unittest -import sys import tkinter from tkinter.test.support import (AbstractTkTest, tcl_version, requires_tcl, get_tk_patchlevel, pixels_conv, tcl_obj_eq) @@ -243,31 +242,31 @@ class StandardOptionsTests: 'underline', 'wraplength', 'xscrollcommand', 'yscrollcommand', ) - def test_activebackground(self): + def test_configure_activebackground(self): widget = self.create() self.checkColorParam(widget, 'activebackground') - def test_activeborderwidth(self): + def test_configure_activeborderwidth(self): widget = self.create() self.checkPixelsParam(widget, 'activeborderwidth', 0, 1.3, 2.9, 6, -2, '10p') - def test_activeforeground(self): + def test_configure_activeforeground(self): widget = self.create() self.checkColorParam(widget, 'activeforeground') - def test_anchor(self): + def test_configure_anchor(self): widget = self.create() self.checkEnumParam(widget, 'anchor', 'n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw', 'center') - def test_background(self): + def test_configure_background(self): widget = self.create() self.checkColorParam(widget, 'background') if 'bg' in self.OPTIONS: self.checkColorParam(widget, 'bg') - def test_bitmap(self): + def test_configure_bitmap(self): widget = self.create() self.checkParam(widget, 'bitmap', 'questhead') self.checkParam(widget, 'bitmap', 'gray50') @@ -280,90 +279,88 @@ class StandardOptionsTests: self.checkInvalidParam(widget, 'bitmap', 'spam', errmsg='bitmap "spam" not defined') - def test_borderwidth(self): + def test_configure_borderwidth(self): widget = self.create() self.checkPixelsParam(widget, 'borderwidth', 0, 1.3, 2.6, 6, -2, '10p') if 'bd' in self.OPTIONS: self.checkPixelsParam(widget, 'bd', 0, 1.3, 2.6, 6, -2, '10p') - def test_compound(self): + def test_configure_compound(self): widget = self.create() self.checkEnumParam(widget, 'compound', 'bottom', 'center', 'left', 'none', 'right', 'top') - def test_cursor(self): + def test_configure_cursor(self): widget = self.create() self.checkCursorParam(widget, 'cursor') - def test_disabledforeground(self): + def test_configure_disabledforeground(self): widget = self.create() self.checkColorParam(widget, 'disabledforeground') - def test_exportselection(self): + def test_configure_exportselection(self): widget = self.create() self.checkBooleanParam(widget, 'exportselection') - def test_font(self): + def test_configure_font(self): widget = self.create() self.checkParam(widget, 'font', '-Adobe-Helvetica-Medium-R-Normal--*-120-*-*-*-*-*-*') self.checkInvalidParam(widget, 'font', '', errmsg='font "" doesn\'t exist') - def test_foreground(self): + def test_configure_foreground(self): widget = self.create() self.checkColorParam(widget, 'foreground') if 'fg' in self.OPTIONS: self.checkColorParam(widget, 'fg') - def test_highlightbackground(self): + def test_configure_highlightbackground(self): widget = self.create() self.checkColorParam(widget, 'highlightbackground') - def test_highlightcolor(self): + def test_configure_highlightcolor(self): widget = self.create() self.checkColorParam(widget, 'highlightcolor') - def test_highlightthickness(self): + def test_configure_highlightthickness(self): widget = self.create() self.checkPixelsParam(widget, 'highlightthickness', 0, 1.3, 2.6, 6, '10p') self.checkParam(widget, 'highlightthickness', -2, expected=0, conv=self._conv_pixels) - @unittest.skipIf(sys.platform == 'darwin', - 'crashes with Cocoa Tk (issue19733)') - def test_image(self): + def test_configure_image(self): widget = self.create() self.checkImageParam(widget, 'image') - def test_insertbackground(self): + def test_configure_insertbackground(self): widget = self.create() self.checkColorParam(widget, 'insertbackground') - def test_insertborderwidth(self): + def test_configure_insertborderwidth(self): widget = self.create() self.checkPixelsParam(widget, 'insertborderwidth', 0, 1.3, 2.6, 6, -2, '10p') - def test_insertofftime(self): + def test_configure_insertofftime(self): widget = self.create() self.checkIntegerParam(widget, 'insertofftime', 100) - def test_insertontime(self): + def test_configure_insertontime(self): widget = self.create() self.checkIntegerParam(widget, 'insertontime', 100) - def test_insertwidth(self): + def test_configure_insertwidth(self): widget = self.create() self.checkPixelsParam(widget, 'insertwidth', 1.3, 2.6, -2, '10p') - def test_jump(self): + def test_configure_jump(self): widget = self.create() self.checkBooleanParam(widget, 'jump') - def test_justify(self): + def test_configure_justify(self): widget = self.create() self.checkEnumParam(widget, 'justify', 'left', 'right', 'center', errmsg='bad justification "{}": must be ' @@ -372,154 +369,155 @@ class StandardOptionsTests: errmsg='ambiguous justification "": must be ' 'left, right, or center') - def test_orient(self): + def test_configure_orient(self): widget = self.create() self.assertEqual(str(widget['orient']), self.default_orient) self.checkEnumParam(widget, 'orient', 'horizontal', 'vertical') - def test_padx(self): + def test_configure_padx(self): widget = self.create() self.checkPixelsParam(widget, 'padx', 3, 4.4, 5.6, -2, '12m', conv=self._conv_pad_pixels) - def test_pady(self): + def test_configure_pady(self): widget = self.create() self.checkPixelsParam(widget, 'pady', 3, 4.4, 5.6, -2, '12m', conv=self._conv_pad_pixels) - def test_relief(self): + def test_configure_relief(self): widget = self.create() self.checkReliefParam(widget, 'relief') - def test_repeatdelay(self): + def test_configure_repeatdelay(self): widget = self.create() self.checkIntegerParam(widget, 'repeatdelay', -500, 500) - def test_repeatinterval(self): + def test_configure_repeatinterval(self): widget = self.create() self.checkIntegerParam(widget, 'repeatinterval', -500, 500) - def test_selectbackground(self): + def test_configure_selectbackground(self): widget = self.create() self.checkColorParam(widget, 'selectbackground') - def test_selectborderwidth(self): + def test_configure_selectborderwidth(self): widget = self.create() self.checkPixelsParam(widget, 'selectborderwidth', 1.3, 2.6, -2, '10p') - def test_selectforeground(self): + def test_configure_selectforeground(self): widget = self.create() self.checkColorParam(widget, 'selectforeground') - def test_setgrid(self): + def test_configure_setgrid(self): widget = self.create() self.checkBooleanParam(widget, 'setgrid') - def test_state(self): + def test_configure_state(self): widget = self.create() self.checkEnumParam(widget, 'state', 'active', 'disabled', 'normal') - def test_takefocus(self): + def test_configure_takefocus(self): widget = self.create() self.checkParams(widget, 'takefocus', '0', '1', '') - def test_text(self): + def test_configure_text(self): widget = self.create() self.checkParams(widget, 'text', '', 'any string') - def test_textvariable(self): + def test_configure_textvariable(self): widget = self.create() var = tkinter.StringVar(self.root) self.checkVariableParam(widget, 'textvariable', var) - def test_troughcolor(self): + def test_configure_troughcolor(self): widget = self.create() self.checkColorParam(widget, 'troughcolor') - def test_underline(self): + def test_configure_underline(self): widget = self.create() self.checkIntegerParam(widget, 'underline', 0, 1, 10) - def test_wraplength(self): + def test_configure_wraplength(self): widget = self.create() self.checkPixelsParam(widget, 'wraplength', 100) - def test_xscrollcommand(self): + def test_configure_xscrollcommand(self): widget = self.create() self.checkCommandParam(widget, 'xscrollcommand') - def test_yscrollcommand(self): + def test_configure_yscrollcommand(self): widget = self.create() self.checkCommandParam(widget, 'yscrollcommand') # non-standard but common options - def test_command(self): + def test_configure_command(self): widget = self.create() self.checkCommandParam(widget, 'command') - def test_indicatoron(self): + def test_configure_indicatoron(self): widget = self.create() self.checkBooleanParam(widget, 'indicatoron') - def test_offrelief(self): + def test_configure_offrelief(self): widget = self.create() self.checkReliefParam(widget, 'offrelief') - def test_overrelief(self): + def test_configure_overrelief(self): widget = self.create() self.checkReliefParam(widget, 'overrelief') - def test_selectcolor(self): + def test_configure_selectcolor(self): widget = self.create() self.checkColorParam(widget, 'selectcolor') - def test_selectimage(self): + def test_configure_selectimage(self): widget = self.create() self.checkImageParam(widget, 'selectimage') @requires_tcl(8, 5) - def test_tristateimage(self): + def test_configure_tristateimage(self): widget = self.create() self.checkImageParam(widget, 'tristateimage') @requires_tcl(8, 5) - def test_tristatevalue(self): + def test_configure_tristatevalue(self): widget = self.create() self.checkParam(widget, 'tristatevalue', 'unknowable') - def test_variable(self): + def test_configure_variable(self): widget = self.create() var = tkinter.DoubleVar(self.root) self.checkVariableParam(widget, 'variable', var) class IntegerSizeTests: - def test_height(self): + def test_configure_height(self): widget = self.create() self.checkIntegerParam(widget, 'height', 100, -100, 0) - def test_width(self): + def test_configure_width(self): widget = self.create() self.checkIntegerParam(widget, 'width', 402, -402, 0) class PixelSizeTests: - def test_height(self): + def test_configure_height(self): widget = self.create() self.checkPixelsParam(widget, 'height', 100, 101.2, 102.6, -100, 0, '3c') - def test_width(self): + def test_configure_width(self): widget = self.create() self.checkPixelsParam(widget, 'width', 402, 403.4, 404.6, -402, 0, '5i') def add_standard_options(*source_classes): - # This decorator adds test_xxx methods from source classes for every xxx - # option in the OPTIONS class attribute if they are not defined explicitly. + # This decorator adds test_configure_xxx methods from source classes for + # every xxx option in the OPTIONS class attribute if they are not defined + # explicitly. def decorator(cls): for option in cls.OPTIONS: - methodname = 'test_' + option + methodname = 'test_configure_' + option if not hasattr(cls, methodname): for source_class in source_classes: if hasattr(source_class, methodname): diff --git a/Lib/tkinter/tix.py b/Lib/tkinter/tix.py index ac545502..ef1e7406 100644 --- a/Lib/tkinter/tix.py +++ b/Lib/tkinter/tix.py @@ -387,9 +387,7 @@ class TixWidget(tkinter.Widget): # These are missing from Tkinter def image_create(self, imgtype, cnf={}, master=None, **kw): if not master: - master = tkinter._default_root - if not master: - raise RuntimeError('Too early to create image') + master = self if kw and cnf: cnf = _cnfmerge((cnf, kw)) elif kw: cnf = kw options = () @@ -475,10 +473,7 @@ class DisplayStyle: elif 'refwindow' in cnf: master = cnf['refwindow'] else: - master = tkinter._default_root - if not master: - raise RuntimeError("Too early to create display style: " - "no root window") + master = tkinter._get_default_root('create display style') self.tk = master.tk self.stylename = self.tk.call('tixDisplayStyle', itemtype, *self._options(cnf,kw) ) diff --git a/Lib/tkinter/ttk.py b/Lib/tkinter/ttk.py index 968fd54d..ab7aeb15 100644 --- a/Lib/tkinter/ttk.py +++ b/Lib/tkinter/ttk.py @@ -349,12 +349,7 @@ def setup_master(master=None): If it is not allowed to use the default root and master is None, RuntimeError is raised.""" if master is None: - if tkinter._support_default_root: - master = tkinter._default_root or tkinter.Tk() - else: - raise RuntimeError( - "No master specified and tkinter is " - "configured to not support default root") + master = tkinter._get_default_root() return master @@ -1538,7 +1533,10 @@ class LabeledScale(Frame): scale_side = 'bottom' if self._label_top else 'top' label_side = 'top' if scale_side == 'bottom' else 'bottom' self.scale.pack(side=scale_side, fill='x') - tmp = Label(self).pack(side=label_side) # place holder + # Dummy required to make frame correct height + dummy = Label(self) + dummy.pack(side=label_side) + dummy.lower() self.label.place(anchor='n' if label_side == 'top' else 's') # update the label as scale or variable changes diff --git a/Lib/traceback.py b/Lib/traceback.py index fb34de94..d7fbdae6 100644 --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -515,7 +515,8 @@ class TracebackException: if exc_type and issubclass(exc_type, SyntaxError): # Handle SyntaxError's specially self.filename = exc_value.filename - self.lineno = str(exc_value.lineno) + lno = exc_value.lineno + self.lineno = str(lno) if lno is not None else None self.text = exc_value.text self.offset = exc_value.offset self.msg = exc_value.msg @@ -574,9 +575,12 @@ class TracebackException: def _format_syntax_error(self, stype): """Format SyntaxError exceptions (internal helper).""" # Show exactly where the problem was found. - filename = self.filename or "" - lineno = str(self.lineno) or '?' - yield ' File "{}", line {}\n'.format(filename, lineno) + filename_suffix = '' + if self.lineno is not None: + yield ' File "{}", line {}\n'.format( + self.filename or "", self.lineno) + elif self.filename is not None: + filename_suffix = ' ({})'.format(self.filename) text = self.text if text is not None: @@ -594,7 +598,7 @@ class TracebackException: caretspace = ((c if c.isspace() else ' ') for c in ltext[:caret]) yield ' {}^\n'.format(''.join(caretspace)) msg = self.msg or "" - yield "{}: {}\n".format(stype, msg) + yield "{}: {}{}\n".format(stype, msg, filename_suffix) def format(self, *, chain=True): """Format the exception. diff --git a/Lib/tracemalloc.py b/Lib/tracemalloc.py index 69b4170e..cec99c59 100644 --- a/Lib/tracemalloc.py +++ b/Lib/tracemalloc.py @@ -226,7 +226,7 @@ class Traceback(Sequence): return str(self[0]) def __repr__(self): - s = " "SIXTY_FOUR_BIT_LONG", - perlasm_scheme => "macosx", - }, -+ "darwin64-arm64-cc" => { -+ inherit_from => [ "darwin-common", asm("aarch64_asm") ], -+ CFLAGS => add("-Wall"), -+ cflags => add("-arch arm64"), -+ lib_cppflags => add("-DL_ENDIAN"), -+ bn_ops => "SIXTY_FOUR_BIT_LONG", -+ perlasm_scheme => "ios64", -+ }, - - ##### GNU Hurd - "hurd-x86" => { -diff -ur openssl-1.1.1g-orig/config openssl-1.1.1g/config ---- openssl-1.1.1g-orig/config 2020-04-21 14:22:39.000000000 +0200 -+++ openssl-1.1.1g/config 2020-07-26 12:21:59.000000000 +0200 -@@ -255,6 +255,9 @@ - ;; - x86_64) - echo "x86_64-apple-darwin${VERSION}" -+ ;; -+ arm64) -+ echo "arm64-apple-darwin${VERSION}" - ;; - *) - echo "i686-apple-darwin${VERSION}" -@@ -497,6 +500,9 @@ - else - OUT="darwin64-x86_64-cc" - fi ;; -+ x86_64-apple-darwin*) -+ OUT="darwin64-arm64-cc" -+ ;; - armv6+7-*-iphoneos) - __CNF_CFLAGS="$__CNF_CFLAGS -arch armv6 -arch armv7" - __CNF_CXXFLAGS="$__CNF_CXXFLAGS -arch armv6 -arch armv7" diff --git a/Mac/BuildScript/scripts/postflight.patch-profile b/Mac/BuildScript/scripts/postflight.patch-profile index 0a62e327..68b8e4bb 100755 --- a/Mac/BuildScript/scripts/postflight.patch-profile +++ b/Mac/BuildScript/scripts/postflight.patch-profile @@ -20,7 +20,7 @@ fi # Make sure the directory ${PYTHON_ROOT}/bin is on the users PATH. BSH="`basename "${theShell}"`" case "${BSH}" in -bash|ksh|sh|*csh|zsh) +bash|ksh|sh|*csh|zsh|fish) if [ `id -ur` = 0 ]; then P=`su - ${USER} -c 'echo A-X-4-X@@$PATH@@X-4-X-A' | grep 'A-X-4-X@@.*@@X-4-X-A' | sed -e 's/^A-X-4-X@@//g' -e 's/@@X-4-X-A$//g'` else @@ -76,6 +76,22 @@ bash) PR="${HOME}/.bash_profile" fi ;; +fish) + CONFIG_DIR="${HOME}/.config/fish" + RC="${CONFIG_DIR}/config.fish" + mkdir -p "$CONFIG_DIR" + if [ -f "${RC}" ]; then + cp -fp "${RC}" "${RC}.pysave" + fi + echo "" >> "${RC}" + echo "# Setting PATH for Python ${PYVER}" >> "${RC}" + echo "# The original version is saved in ${RC}.pysave" >> "${RC}" + echo "set -x PATH \"${PYTHON_ROOT}/bin\" \"\$PATH\"" >> "${RC}" + if [ `id -ur` = 0 ]; then + chown "${USER}" "${RC}" + fi + exit 0 + ;; zsh) PR="${HOME}/.zprofile" ;; diff --git a/Mac/IDLE/IDLE.app/Contents/Info.plist b/Mac/IDLE/IDLE.app/Contents/Info.plist index dcc48abd..f6b5cfe8 100644 --- a/Mac/IDLE/IDLE.app/Contents/Info.plist +++ b/Mac/IDLE/IDLE.app/Contents/Info.plist @@ -36,7 +36,7 @@ CFBundleExecutable IDLE CFBundleGetInfoString - %version%, © 2001-2020 Python Software Foundation + %version%, © 2001-2021 Python Software Foundation CFBundleIconFile IDLE.icns CFBundleIdentifier diff --git a/Mac/PythonLauncher/Info.plist.in b/Mac/PythonLauncher/Info.plist.in index 21a05153..3d8bc3e4 100644 --- a/Mac/PythonLauncher/Info.plist.in +++ b/Mac/PythonLauncher/Info.plist.in @@ -40,7 +40,7 @@ CFBundleExecutable Python Launcher CFBundleGetInfoString - %VERSION%, © 2001-2020 Python Software Foundation + %VERSION%, © 2001-2021 Python Software Foundation CFBundleIconFile PythonLauncher.icns CFBundleIdentifier diff --git a/Mac/Resources/app/Info.plist.in b/Mac/Resources/app/Info.plist.in index 1d624984..2c801332 100644 --- a/Mac/Resources/app/Info.plist.in +++ b/Mac/Resources/app/Info.plist.in @@ -37,7 +37,7 @@ CFBundleInfoDictionaryVersion 6.0 CFBundleLongVersionString - %version%, (c) 2001-2020 Python Software Foundation. + %version%, (c) 2001-2021 Python Software Foundation. CFBundleName Python CFBundlePackageType diff --git a/Misc/ACKS b/Misc/ACKS index 12a5ac14..73d35c2d 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -192,6 +192,7 @@ Gawain Bolton Carl Friedrich Bolz-Tereick Forest Bond Gregory Bond +Angelin Booz Médéric Boquien Matias Bordese Jonas Borgström @@ -1443,6 +1444,7 @@ Mark Roddy Kevin Rodgers Sean Rodman Giampaolo Rodola +Dustin Rodrigues Mauro S. M. Rodrigues Elson Rodriguez Adi Roiban @@ -1640,6 +1642,7 @@ Quentin Stafford-Fraser Frank Stajano Joel Stanley Kyle Stanley +Brandon Stansbury Anthony Starks David Steele Oliver Steele @@ -1828,6 +1831,7 @@ Zachary Ware Barry Warsaw Steve Waterbury Bob Watson +Colin Watson David Watson Aaron Watters Henrik Weber diff --git a/Misc/NEWS b/Misc/NEWS index 03c84f31..c438e3f8 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -2,6 +2,318 @@ Python News +++++++++++ +What's New in Python 3.9.2 final? +================================= + +*Release date: 2021-02-19* + +Windows +------- + +- bpo-43155: :c:func:`PyCMethod_New` is now present in ``python3.lib``. + + +What's New in Python 3.9.2 release candidate 1? +=============================================== + +*Release date: 2021-02-16* + +Security +-------- + +- bpo-42967: Fix web cache poisoning vulnerability by defaulting the query + args separator to ``&``, and allowing the user to choose a custom + separator. + +- bpo-42938: Avoid static buffers when computing the repr of + :class:`ctypes.c_double` and :class:`ctypes.c_longdouble` values. + +Core and Builtins +----------------- + +- bpo-42819: :mod:`readline`: Explicitly disable bracketed paste in the + interactive interpreter, even if it's set in the inputrc, is enabled by + default (eg GNU Readline 8.1), or a user calls + ``readline.read_init_file()``. The Python REPL has not implemented + bracketed paste support. Also, bracketed mode writes the ``"\x1b[?2004h"`` + escape sequence into stdout which causes test failures in applications + that don't support it. It can still be explicitly enabled by calling + ``readline.parse_and_bind("set enable-bracketed-paste on")``. Patch by + Dustin Rodrigues. + +- bpo-42806: Fix the column offsets for f-strings :mod:`ast` nodes + surrounded by parentheses and for nodes that spawn multiple lines. Patch + by Pablo Galindo. + +- bpo-40631: Fix regression where a single parenthesized starred expression + was a valid assignment target. + +- bpo-32381: Fix encoding name when running a ``.pyc`` file on Windows: + :c:func:`PyRun_SimpleFileExFlags()` now uses the correct encoding to + decode the filename. + +- bpo-42536: Several built-in and standard library types now ensure that + their internal result tuples are always tracked by the :term:`garbage + collector `: + + - :meth:`collections.OrderedDict.items() ` + + - :meth:`dict.items` + + - :func:`enumerate` + + - :func:`functools.reduce` + + - :func:`itertools.combinations` + + - :func:`itertools.combinations_with_replacement` + + - :func:`itertools.permutations` + + - :func:`itertools.product` + + - :func:`itertools.zip_longest` + + - :func:`zip` + + Previously, they could have become untracked by a prior garbage + collection. Patch by Brandt Bucher. + +- bpo-42195: The ``__args__`` of the parameterized generics for + :data:`typing.Callable` and :class:`collections.abc.Callable` are now + consistent. The ``__args__`` for :class:`collections.abc.Callable` are + now flattened while :data:`typing.Callable`'s have not changed. To allow + this change, :class:`types.GenericAlias` can now be subclassed and + ``collections.abc.Callable``'s ``__class_getitem__`` will now return a + subclass of ``types.GenericAlias``. Tests for typing were also updated to + not subclass things like ``Callable[..., T]`` as that is not a valid base + class. Finally, both types no longer validate their ``argtypes``, in + ``Callable[[argtypes], resulttype]`` to prepare for :pep:`612`. Patch by + Ken Jin. + +Library +------- + +- bpo-43102: The namedtuple __new__ method had its __builtins__ set to None + instead of an actual dictionary. This created problems for introspection + tools. + +- bpo-43108: Fixed a reference leak in the :mod:`curses` module. Patch by + Pablo Galindo + +- bpo-42944: Fix ``random.Random.sample`` when ``counts`` argument is not + ``None``. + +- bpo-42931: Add :func:`randbytes` to ``random.__all__``. + +- bpo-42780: Fix os.set_inheritable() for O_PATH file descriptors on Linux. + +- bpo-42851: remove __init_subclass__ support for Enum members + +- bpo-41748: Fix HTMLParser parsing rules for element attributes containing + commas with spaces. Patch by Karl Dubost. + +- bpo-42759: Fixed equality comparison of :class:`tkinter.Variable` and + :class:`tkinter.font.Font`. Objects which belong to different Tcl + interpreters are now always different, even if they have the same name. + +- bpo-42756: Configure LMTP Unix-domain socket to use socket global default + timeout when a timeout is not explicitly provided. + +- bpo-23328: Allow / character in username, password fields on _PROXY + envars. + +- bpo-42655: :mod:`subprocess` *extra_groups* is now correctly passed into + setgroups() system call. + +- bpo-42727: ``EnumMeta.__prepare__`` now accepts ``**kwds`` to properly + support ``__init_subclass__`` + +- bpo-42681: Fixed range checks for color and pair numbers in :mod:`curses`. + +- bpo-37961: Fix crash in :func:`tracemalloc.Traceback.__repr__` (regressed + in Python 3.9). + +- bpo-42630: :mod:`tkinter` functions and constructors which need a default + root window raise now :exc:`RuntimeError` with descriptive message instead + of obscure :exc:`AttributeError` or :exc:`NameError` if it is not created + yet or cannot be created automatically. + +- bpo-42644: `logging.disable` will now validate the types and value of its + parameter. It also now accepts strings representing the levels (as does + `loging.setLevel`) instead of only the numerical values. + +- bpo-36541: Fixed lib2to3.pgen2 to be able to parse PEP-570 positional only + argument syntax. + +- bpo-42517: Enum: private names will raise a DeprecationWarning; in 3.10 + they will become normal attributes + +- bpo-42678: `Enum`: call `__init_subclass__` after members have been added + +- bpo-42532: Remove unexpected call of ``__bool__`` when passing a + ``spec_arg`` argument to a Mock. + +- bpo-42388: Fix subprocess.check_output(..., input=None) behavior when + text=True to be consistent with that of the documentation and + universal_newlines=True. + +- bpo-34463: Fixed discrepancy between :mod:`traceback` and the interpreter + in formatting of SyntaxError with lineno not set (:mod:`traceback` was + changed to match interpreter). + +- bpo-42375: subprocess module update for DragonFlyBSD support. + +- bpo-42384: Make pdb populate sys.path[0] exactly the same as regular + python execution. + +- bpo-42383: Fix pdb: previously pdb would fail to restart the debugging + target if it was specified using a relative path and the current directory + changed. + +- bpo-42318: Fixed support of non-BMP characters in :mod:`tkinter` on macOS. + +- bpo-42163: Restore compatibility for ``uname_result`` around deepcopy and + _replace. + +- bpo-39825: Windows: Change ``sysconfig.get_config_var('EXT_SUFFIX')`` to + the expected full ``platform_tag.extension`` format. Previously it was + hard-coded to ``.pyd``, now it is compatible with ``distutils.sysconfig`` + and will result in something like ``.cp38-win_amd64.pyd``. This brings + windows into conformance with the other platforms. + +- bpo-42059: :class:`typing.TypedDict` types created using the alternative + call-style syntax now correctly respect the ``total`` keyword argument + when setting their ``__required_keys__`` and ``__optional_keys__`` class + attributes. + +- bpo-39101: Fixed tests using IsolatedAsyncioTestCase from hanging on + BaseExceptions. + +- bpo-42005: Fix CLI of :mod:`cProfile` and :mod:`profile` to catch + :exc:`BrokenPipeError`. + +- bpo-41907: fix `format()` behavior for `IntFlag` + +- bpo-41889: Enum: fix regression involving inheriting a multiply-inherited + enum + +- bpo-41891: Ensure asyncio.wait_for waits for task completion + +- bpo-41604: Don't decrement the reference count of the previous user_ptr + when set_panel_userptr fails. + +- bpo-40219: Lowered :class:`tkinter.ttk.LabeledScale` dummy widget to + prevent hiding part of the content label. + +- bpo-40084: Fix ``Enum.__dir__``: dir(Enum.member) now includes attributes + as well as methods. + +- bpo-39068: Fix initialization race condition in :func:`a85encode` and + :func:`b85encode` in :mod:`base64`. Patch by Brandon Stansbury. + +- bpo-33289: Correct call to :mod:`tkinter.colorchooser` to return RGB + triplet of ints instead of floats. Patch by Cheryl Sabella. + +Documentation +------------- + +- bpo-40304: Fix doc for type(name, bases, dict). Patch by Boris + Verkhovskiy and Éric Araujo. + +- bpo-42811: Updated importlib.utils.resolve_name() doc to use + __spec__.parent instead of __package__. (Thanks Yair Frid.) + +- bpo-17140: Add documentation for the + :class:`multiprocessing.pool.ThreadPool` class. + +Tests +----- + +- bpo-42794: Update test_nntplib to use offical group name of news.aioe.org + for testing. Patch by Dong-hee Na. + +- bpo-40810: In :mod:`sqlite3`, fix ``CheckTraceCallbackContent`` for SQLite + pre 3.7.15. + +Build +----- + +- bpo-43174: Windows build now uses ``/utf-8`` compiler option. + +- bpo-42692: Fix __builtin_available check on older compilers. Patch by + Joshua Root. + +- bpo-42604: Now all platforms use a value for the "EXT_SUFFIX" build + variable derived from SOABI (for instance in freeBSD, "EXT_SUFFIX" is now + ".cpython-310d.so" instead of ".so"). Previosuly only Linux, Mac and + VxWorks were using a value for "EXT_SUFFIX" that included "SOABI". + +- bpo-42598: Fix implicit function declarations in configure which could + have resulted in incorrect configuration checks. Patch contributed by + Joshua Root. + +- bpo-29076: Add fish shell support to macOS installer. + +Windows +------- + +- bpo-41837: Updated Windows installer to include OpenSSL 1.1.1i + +- bpo-42584: Upgrade Windows installer to use SQLite 3.34.0. + +macOS +----- + +- bpo-42504: Ensure that the value of + sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET') is always a string, + even in when the value is parsable as an integer. + +- bpo-42361: Update macOS installer build to use Tcl/Tk 8.6.11 (rc2, + expected to be final release). + +- bpo-41837: Update macOS installer build to use OpenSSL 1.1.1i. + +- bpo-42584: Update macOS installer to use SQLite 3.34.0. + +IDLE +---- + +- bpo-43008: Make IDLE invoke :func:`sys.excepthook` in normal, 2-process + mode. Patch by Ken Hilton. + +- bpo-33065: Fix problem debugging user classes with __repr__ method. + +- bpo-23544: Disable Debug=>Stack Viewer when user code is running or + Debugger is active, to prevent hang or crash. Patch by Zackery Spytz. + +- bpo-32631: Finish zzdummy example extension module: make menu entries + work; add docstrings and tests with 100% coverage. + +Tools/Demos +----------- + +- bpo-42726: Fixed Python 3 compatibility issue with gdb/libpython.py + handling of attribute dictionaries. + +- bpo-42613: Fix ``freeze.py`` tool to use the prope config and library + directories. Patch by Victor Stinner. + +C API +----- + +- bpo-43030: Fixed a compiler warning in :c:func:`Py_UNICODE_ISSPACE()` on + platforms with signed ``wchar_t``. + +- bpo-42591: Export the :c:func:`Py_FrozenMain` function: fix a Python 3.9.0 + regression. Python 3.9 uses ``-fvisibility=hidden`` and the function was + not exported explicitly and so not exported. + +- bpo-40052: Fix an alignment build warning/error in function + ``PyVectorcall_Function()``. Patch by Andreas Schneider, Antoine Pitrou + and Petr Viktorin. + + What's New in Python 3.9.1 final? ================================= @@ -148,8 +460,6 @@ Core and Builtins rules that only generate better error messages to gain performance. If there's a parse failure, run the parser a second time with those enabled. -- bpo-41910: Document the default implementation of `object.__eq__`. - - bpo-42057: Fix peephole optimizer misoptimize conditional jump + JUMP_IF_NOT_EXC_MATCH pair. @@ -265,7 +575,7 @@ Library - bpo-41316: Fix the :mod:`tarfile` module to write only basename of TAR file to GZIP compression header. -- bpo-16936: Allow ``ctypes.wintypes`` to be imported on non-Windows +- bpo-16396: Allow ``ctypes.wintypes`` to be imported on non-Windows systems. - bpo-40592: :func:`shutil.which` now ignores empty entries in @@ -291,6 +601,8 @@ Documentation - bpo-42061: Document __format__ functionality for IP addresses. +- bpo-41910: Document the default implementation of `object.__eq__`. + - bpo-42010: Clarify that subscription expressions are also valid for certain :term:`classes ` and :term:`types ` in the standard library, and for user-defined classes and types if the classmethod diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c index b0a36a30..f2506de5 100644 --- a/Modules/_ctypes/callproc.c +++ b/Modules/_ctypes/callproc.c @@ -489,58 +489,47 @@ is_literal_char(unsigned char c) static PyObject * PyCArg_repr(PyCArgObject *self) { - char buffer[256]; switch(self->tag) { case 'b': case 'B': - sprintf(buffer, "", + return PyUnicode_FromFormat("", self->tag, self->value.b); - break; case 'h': case 'H': - sprintf(buffer, "", + return PyUnicode_FromFormat("", self->tag, self->value.h); - break; case 'i': case 'I': - sprintf(buffer, "", + return PyUnicode_FromFormat("", self->tag, self->value.i); - break; case 'l': case 'L': - sprintf(buffer, "", + return PyUnicode_FromFormat("", self->tag, self->value.l); - break; case 'q': case 'Q': - sprintf(buffer, -#ifdef MS_WIN32 - "", -#else - "", -#endif + return PyUnicode_FromFormat("", self->tag, self->value.q); - break; case 'd': - sprintf(buffer, "", - self->tag, self->value.d); - break; - case 'f': - sprintf(buffer, "", - self->tag, self->value.f); - break; - + case 'f': { + PyObject *f = PyFloat_FromDouble((self->tag == 'f') ? self->value.f : self->value.d); + if (f == NULL) { + return NULL; + } + PyObject *result = PyUnicode_FromFormat("", self->tag, f); + Py_DECREF(f); + return result; + } case 'c': if (is_literal_char((unsigned char)self->value.c)) { - sprintf(buffer, "", + return PyUnicode_FromFormat("", self->tag, self->value.c); } else { - sprintf(buffer, "", + return PyUnicode_FromFormat("", self->tag, (unsigned char)self->value.c); } - break; /* Hm, are these 'z' and 'Z' codes useful at all? Shouldn't they be replaced by the functionality of c_string @@ -549,22 +538,20 @@ PyCArg_repr(PyCArgObject *self) case 'z': case 'Z': case 'P': - sprintf(buffer, "", + return PyUnicode_FromFormat("", self->tag, self->value.p); break; default: if (is_literal_char((unsigned char)self->tag)) { - sprintf(buffer, "", + return PyUnicode_FromFormat("", (unsigned char)self->tag, (void *)self); } else { - sprintf(buffer, "", + return PyUnicode_FromFormat("", (unsigned char)self->tag, (void *)self); } - break; } - return PyUnicode_FromString(buffer); } static PyMemberDef PyCArgType_members[] = { diff --git a/Modules/_ctypes/malloc_closure.c b/Modules/_ctypes/malloc_closure.c index 4f220e42..788bae6a 100644 --- a/Modules/_ctypes/malloc_closure.c +++ b/Modules/_ctypes/malloc_closure.c @@ -91,11 +91,15 @@ static void more_core(void) /* put the item back into the free list */ void Py_ffi_closure_free(void *p) { -#if USING_APPLE_OS_LIBFFI && HAVE_FFI_CLOSURE_ALLOC +#if HAVE_FFI_CLOSURE_ALLOC +#if USING_APPLE_OS_LIBFFI if (__builtin_available(macos 10.15, ios 13, watchos 6, tvos 13, *)) { +#endif ffi_closure_free(p); return; +#if USING_APPLE_OS_LIBFFI } +#endif #endif ITEM *item = (ITEM *)p; item->next = free_list; @@ -105,10 +109,14 @@ void Py_ffi_closure_free(void *p) /* return one item from the free list, allocating more if needed */ void *Py_ffi_closure_alloc(size_t size, void** codeloc) { -#if USING_APPLE_OS_LIBFFI && HAVE_FFI_CLOSURE_ALLOC +#if HAVE_FFI_CLOSURE_ALLOC +#if USING_APPLE_OS_LIBFFI if (__builtin_available(macos 10.15, ios 13, watchos 6, tvos 13, *)) { +#endif return ffi_closure_alloc(size, codeloc); +#if USING_APPLE_OS_LIBFFI } +#endif #endif ITEM *item; if (!free_list) diff --git a/Modules/_curses_panel.c b/Modules/_curses_panel.c index f1248034..4f026794 100644 --- a/Modules/_curses_panel.c +++ b/Modules/_curses_panel.c @@ -440,7 +440,9 @@ _curses_panel_panel_set_userptr(PyCursesPanelObject *self, PyObject *obj) /* In case of an ncurses error, decref the new object again */ Py_DECREF(obj); } - Py_XDECREF(oldobj); + else { + Py_XDECREF(oldobj); + } return PyCursesCheckERR(rc, "set_panel_userptr"); } diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index c70b0e2a..c84f8382 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -176,18 +176,6 @@ static char *screen_encoding = NULL; /* Utility Functions */ -static inline int -color_pair_to_attr(short color_number) -{ - return ((int)color_number << 8); -} - -static inline short -attr_to_color_pair(int attr) -{ - return (short)((attr & A_COLOR) >> 8); -} - /* * Check the return code from a curses function and return None * or raise an exception as appropriate. These are exported using the @@ -377,6 +365,7 @@ PyCurses_ConvertToString(PyCursesWindowObject *win, PyObject *obj, *bytes = obj; /* check for embedded null bytes */ if (PyBytes_AsStringAndSize(*bytes, &str, NULL) < 0) { + Py_DECREF(obj); return 0; } return 1; @@ -618,7 +607,7 @@ _curses_window_addch_impl(PyCursesWindowObject *self, int group_left_1, if (type == 2) { funcname = "add_wch"; wstr[1] = L'\0'; - setcchar(&wcval, wstr, attr, attr_to_color_pair(attr), NULL); + setcchar(&wcval, wstr, attr, PAIR_NUMBER(attr), NULL); if (coordinates_group) rtn = mvwadd_wch(self->win,y,x, &wcval); else { @@ -691,8 +680,9 @@ _curses_window_addstr_impl(PyCursesWindowObject *self, int group_left_1, #else strtype = PyCurses_ConvertToString(self, str, &bytesobj, NULL); #endif - if (strtype == 0) + if (strtype == 0) { return NULL; + } if (use_attr) { attr_old = getattrs(self->win); (void)wattrset(self->win,attr); @@ -2586,7 +2576,7 @@ NoArgOrFlagNoReturnFunctionBody(cbreak, flag) _curses.color_content color_number: short - The number of the color (0 - COLORS). + The number of the color (0 - (COLORS-1)). / Return the red, green, and blue (RGB) components of the specified color. @@ -2597,27 +2587,32 @@ which will be between 0 (no component) and 1000 (maximum amount of component). static PyObject * _curses_color_content_impl(PyObject *module, short color_number) -/*[clinic end generated code: output=cb15cf3120d4bfc1 input=5555abb1c11e11b7]*/ +/*[clinic end generated code: output=cb15cf3120d4bfc1 input=630f6737514db6ad]*/ { short r,g,b; PyCursesInitialised; PyCursesInitialisedColor; - if (color_content(color_number, &r, &g, &b) != ERR) - return Py_BuildValue("(iii)", r, g, b); - else { - PyErr_SetString(PyCursesError, - "Argument 1 was out of range. Check value of COLORS."); + if (color_content(color_number, &r, &g, &b) == ERR) { + if (color_number >= COLORS) { + PyErr_SetString(PyCursesError, + "Argument 1 was out of range. Check value of COLORS."); + } + else { + PyErr_SetString(PyCursesError, "color_content() returned ERR"); + } return NULL; } + + return Py_BuildValue("(iii)", r, g, b); } /*[clinic input] _curses.color_pair - color_number: short - The number of the color (0 - COLORS). + pair_number: short + The number of the color pair. / Return the attribute value for displaying text in the specified color. @@ -2627,13 +2622,13 @@ other A_* attributes. pair_number() is the counterpart to this function. [clinic start generated code]*/ static PyObject * -_curses_color_pair_impl(PyObject *module, short color_number) -/*[clinic end generated code: output=6a84cb6b29ecaf9a input=a9d3eb6f50e4dc12]*/ +_curses_color_pair_impl(PyObject *module, short pair_number) +/*[clinic end generated code: output=ce609d238b70dc11 input=8dd0d5da94cb15b5]*/ { PyCursesInitialised; PyCursesInitialisedColor; - return PyLong_FromLong(color_pair_to_attr(color_number)); + return PyLong_FromLong(COLOR_PAIR(pair_number)); } /*[clinic input] @@ -3028,7 +3023,7 @@ _curses_has_key_impl(PyObject *module, int key) _curses.init_color color_number: short - The number of the color to be changed (0 - COLORS). + The number of the color to be changed (0 - (COLORS-1)). r: short Red component (0 - 1000). g: short @@ -3041,13 +3036,13 @@ Change the definition of a color. When init_color() is used, all occurrences of that color on the screen immediately change to the new definition. This function is a no-op on -most terminals; it is active only if can_change_color() returns 1. +most terminals; it is active only if can_change_color() returns true. [clinic start generated code]*/ static PyObject * _curses_init_color_impl(PyObject *module, short color_number, short r, short g, short b) -/*[clinic end generated code: output=280236f5efe9776a input=f3a05bd38f619175]*/ +/*[clinic end generated code: output=280236f5efe9776a input=128601b5dc76d548]*/ { PyCursesInitialised; PyCursesInitialisedColor; @@ -3061,9 +3056,9 @@ _curses.init_pair pair_number: short The number of the color-pair to be changed (1 - (COLOR_PAIRS-1)). fg: short - Foreground color number (0 - COLORS). + Foreground color number (-1 - (COLORS-1)). bg: short - Background color number (0 - COLORS). + Background color number (-1 - (COLORS-1)). / Change the definition of a color-pair. @@ -3075,7 +3070,7 @@ all occurrences of that color-pair are changed to the new definition. static PyObject * _curses_init_pair_impl(PyObject *module, short pair_number, short fg, short bg) -/*[clinic end generated code: output=9c2ce39c22f376b6 input=c9f0b11b17a2ac6d]*/ +/*[clinic end generated code: output=9c2ce39c22f376b6 input=12c320ec14396ea2]*/ { PyCursesInitialised; PyCursesInitialisedColor; @@ -3713,9 +3708,14 @@ _curses_pair_content_impl(PyObject *module, short pair_number) PyCursesInitialised; PyCursesInitialisedColor; - if (pair_content(pair_number, &f, &b)==ERR) { - PyErr_SetString(PyCursesError, - "Argument 1 was out of range. (1..COLOR_PAIRS-1)"); + if (pair_content(pair_number, &f, &b) == ERR) { + if (pair_number >= COLOR_PAIRS) { + PyErr_SetString(PyCursesError, + "Argument 1 was out of range. (0..COLOR_PAIRS-1)"); + } + else { + PyErr_SetString(PyCursesError, "pair_content() returned ERR"); + } return NULL; } @@ -3740,7 +3740,7 @@ _curses_pair_number_impl(PyObject *module, int attr) PyCursesInitialised; PyCursesInitialisedColor; - return PyLong_FromLong(attr_to_color_pair(attr)); + return PyLong_FromLong(PAIR_NUMBER(attr)); } /*[clinic input] diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index d158d3ba..42764a18 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -1,4 +1,5 @@ #include "Python.h" +#include "pycore_object.h" // _PyObject_GC_TRACK #include "pycore_pystate.h" // _PyThreadState_GET() #include "pycore_tupleobject.h" #include "structmember.h" // PyMemberDef @@ -673,6 +674,11 @@ functools_reduce(PyObject *self, PyObject *args) if ((result = PyObject_Call(func, args, NULL)) == NULL) { goto Fail; } + // bpo-42536: The GC may have untracked this args tuple. Since we're + // recycling it, make sure it's tracked again: + if (!_PyObject_GC_IS_TRACKED(args)) { + _PyObject_GC_TRACK(args); + } } } diff --git a/Modules/_posixsubprocess.c b/Modules/_posixsubprocess.c index 5356417d..d64e0a1c 100644 --- a/Modules/_posixsubprocess.c +++ b/Modules/_posixsubprocess.c @@ -46,7 +46,7 @@ # endif #endif -#if defined(__FreeBSD__) || (defined(__APPLE__) && defined(__MACH__)) +#if defined(__FreeBSD__) || (defined(__APPLE__) && defined(__MACH__)) || defined(__DragonFly__) # define FD_DIR "/dev/fd" #else # define FD_DIR "/proc/self/fd" @@ -116,9 +116,9 @@ _pos_int_from_ascii(const char *name) } -#if defined(__FreeBSD__) +#if defined(__FreeBSD__) || defined(__DragonFly__) /* When /dev/fd isn't mounted it is often a static directory populated - * with 0 1 2 or entries for 0 .. 63 on FreeBSD, NetBSD and OpenBSD. + * with 0 1 2 or entries for 0 .. 63 on FreeBSD, NetBSD, OpenBSD and DragonFlyBSD. * NetBSD and OpenBSD have a /proc fs available (though not necessarily * mounted) and do not have fdescfs for /dev/fd. MacOS X has a devfs * that properly supports /dev/fd. @@ -377,7 +377,7 @@ _close_open_fds_maybe_unsafe(long start_fd, PyObject* py_fds_to_keep) ++start_fd; #endif -#if defined(__FreeBSD__) +#if defined(__FreeBSD__) || defined(__DragonFly__) if (!_is_fdescfs_mounted_on_dev_fd()) proc_fd_dir = NULL; else @@ -753,7 +753,7 @@ subprocess_fork_exec(PyObject* self, PyObject *args) if (groups_list != Py_None) { #ifdef HAVE_SETGROUPS Py_ssize_t i; - unsigned long gid; + gid_t gid; if (!PyList_Check(groups_list)) { PyErr_SetString(PyExc_TypeError, @@ -787,10 +787,6 @@ subprocess_fork_exec(PyObject* self, PyObject *args) Py_DECREF(elem); goto cleanup; } else { - /* In posixmodule.c UnsignedLong is used as a fallback value - * if the value provided does not fit in a Long. Since we are - * already doing the bounds checking on the Python side, we - * can go directly to an UnsignedLong here. */ if (!_Py_Gid_Converter(elem, &gid)) { Py_DECREF(elem); PyErr_SetString(PyExc_ValueError, "invalid group id"); diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index b8003734..d20339ca 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -1704,7 +1704,11 @@ pysqlite_connection_create_collation(pysqlite_Connection* self, PyObject* args) (callable != Py_None) ? callable : NULL, (callable != Py_None) ? pysqlite_collation_callback : NULL); if (rc != SQLITE_OK) { - PyDict_DelItem(self->collations, uppercase_name); + if (callable != Py_None) { + if (PyDict_DelItem(self->collations, uppercase_name) < 0) { + PyErr_Clear(); + } + } _pysqlite_seterror(self->db, NULL); goto finally; } diff --git a/Modules/_sqlite/cursor.c b/Modules/_sqlite/cursor.c index 5cfb4b97..dd0ce7e1 100644 --- a/Modules/_sqlite/cursor.c +++ b/Modules/_sqlite/cursor.c @@ -569,11 +569,13 @@ _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* args) } if (!multiple) { - Py_DECREF(self->lastrowid); Py_BEGIN_ALLOW_THREADS lastrowid = sqlite3_last_insert_rowid(self->connection->db); Py_END_ALLOW_THREADS - self->lastrowid = PyLong_FromLongLong(lastrowid); + Py_SETREF(self->lastrowid, PyLong_FromLongLong(lastrowid)); + if (self->lastrowid == NULL) { + goto error; + } } if (rc == SQLITE_ROW) { @@ -802,8 +804,11 @@ PyObject* pysqlite_cursor_fetchmany(pysqlite_Cursor* self, PyObject* args, PyObj } while ((row = pysqlite_cursor_iternext(self))) { - PyList_Append(list, row); - Py_XDECREF(row); + if (PyList_Append(list, row) < 0) { + Py_DECREF(row); + break; + } + Py_DECREF(row); if (++counter == maxrows) { break; @@ -829,8 +834,11 @@ PyObject* pysqlite_cursor_fetchall(pysqlite_Cursor* self, PyObject* args) } while ((row = pysqlite_cursor_iternext(self))) { - PyList_Append(list, row); - Py_XDECREF(row); + if (PyList_Append(list, row) < 0) { + Py_DECREF(row); + break; + } + Py_DECREF(row); } if (PyErr_Occurred()) { diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c index 793c5e71..b30141d4 100644 --- a/Modules/_tkinter.c +++ b/Modules/_tkinter.c @@ -395,7 +395,8 @@ unicodeFromTclStringAndSize(const char *s, Py_ssize_t size) char *buf = NULL; PyErr_Clear(); - /* Tcl encodes null character as \xc0\x80 */ + /* Tcl encodes null character as \xc0\x80. + https://en.wikipedia.org/wiki/UTF-8#Modified_UTF-8 */ if (memchr(s, '\xc0', size)) { char *q; const char *e = s + size; @@ -419,6 +420,57 @@ unicodeFromTclStringAndSize(const char *s, Py_ssize_t size) if (buf != NULL) { PyMem_Free(buf); } + if (r == NULL || PyUnicode_KIND(r) == PyUnicode_1BYTE_KIND) { + return r; + } + + /* In CESU-8 non-BMP characters are represented as a surrogate pair, + like in UTF-16, and then each surrogate code point is encoded in UTF-8. + https://en.wikipedia.org/wiki/CESU-8 */ + Py_ssize_t len = PyUnicode_GET_LENGTH(r); + Py_ssize_t i, j; + /* All encoded surrogate characters start with \xED. */ + i = PyUnicode_FindChar(r, 0xdcED, 0, len, 1); + if (i == -2) { + Py_DECREF(r); + return NULL; + } + if (i == -1) { + return r; + } + Py_UCS4 *u = PyUnicode_AsUCS4Copy(r); + Py_DECREF(r); + if (u == NULL) { + return NULL; + } + Py_UCS4 ch; + for (j = i; i < len; i++, u[j++] = ch) { + Py_UCS4 ch1, ch2, ch3, high, low; + /* Low surrogates U+D800 - U+DBFF are encoded as + \xED\xA0\x80 - \xED\xAF\xBF. */ + ch1 = ch = u[i]; + if (ch1 != 0xdcED) continue; + ch2 = u[i + 1]; + if (!(0xdcA0 <= ch2 && ch2 <= 0xdcAF)) continue; + ch3 = u[i + 2]; + if (!(0xdc80 <= ch3 && ch3 <= 0xdcBF)) continue; + high = 0xD000 | ((ch2 & 0x3F) << 6) | (ch3 & 0x3F); + assert(Py_UNICODE_IS_HIGH_SURROGATE(high)); + /* High surrogates U+DC00 - U+DFFF are encoded as + \xED\xB0\x80 - \xED\xBF\xBF. */ + ch1 = u[i + 3]; + if (ch1 != 0xdcED) continue; + ch2 = u[i + 4]; + if (!(0xdcB0 <= ch2 && ch2 <= 0xdcBF)) continue; + ch3 = u[i + 5]; + if (!(0xdc80 <= ch3 && ch3 <= 0xdcBF)) continue; + low = 0xD000 | ((ch2 & 0x3F) << 6) | (ch3 & 0x3F); + assert(Py_UNICODE_IS_HIGH_SURROGATE(high)); + ch = Py_UNICODE_JOIN_SURROGATES(high, low); + i += 5; + } + r = PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, u, j); + PyMem_Free(u); return r; } diff --git a/Modules/_zoneinfo.c b/Modules/_zoneinfo.c index 2cee65fa..f6557684 100644 --- a/Modules/_zoneinfo.c +++ b/Modules/_zoneinfo.c @@ -163,7 +163,7 @@ is_leap_year(int year); static size_t _bisect(const int64_t value, const int64_t *arr, size_t size); -static void +static int eject_from_strong_cache(const PyTypeObject *const type, PyObject *key); static void clear_strong_cache(const PyTypeObject *const type); @@ -171,7 +171,7 @@ static void update_strong_cache(const PyTypeObject *const type, PyObject *key, PyObject *zone); static PyObject * -zone_from_strong_cache(const PyTypeObject *const type, PyObject *key); +zone_from_strong_cache(const PyTypeObject *const type, PyObject *const key); static PyObject * zoneinfo_new_instance(PyTypeObject *type, PyObject *key) @@ -265,7 +265,7 @@ zoneinfo_new(PyTypeObject *type, PyObject *args, PyObject *kw) } PyObject *instance = zone_from_strong_cache(type, key); - if (instance != NULL) { + if (instance != NULL || PyErr_Occurred()) { return instance; } @@ -428,7 +428,10 @@ zoneinfo_clear_cache(PyObject *cls, PyObject *args, PyObject *kwargs) while ((item = PyIter_Next(iter))) { // Remove from strong cache - eject_from_strong_cache(type, item); + if (eject_from_strong_cache(type, item) < 0) { + Py_DECREF(item); + break; + } // Remove from weak cache PyObject *tmp = PyObject_CallMethodObjArgs(weak_cache, pop, item, @@ -909,7 +912,13 @@ load_data(PyZoneInfo_ZoneInfo *self, PyObject *file_obj) // Load the transition indices and list self->trans_list_utc = PyMem_Malloc(self->num_transitions * sizeof(int64_t)); + if (self->trans_list_utc == NULL) { + goto error; + } trans_idx = PyMem_Malloc(self->num_transitions * sizeof(Py_ssize_t)); + if (trans_idx == NULL) { + goto error; + } for (size_t i = 0; i < self->num_transitions; ++i) { PyObject *num = PyTuple_GetItem(trans_utc, i); @@ -991,6 +1000,9 @@ load_data(PyZoneInfo_ZoneInfo *self, PyObject *file_obj) // Build _ttinfo objects from utcoff, dstoff and abbr self->_ttinfos = PyMem_Malloc(self->num_ttinfos * sizeof(_ttinfo)); + if (self->_ttinfos == NULL) { + goto error; + } for (size_t i = 0; i < self->num_ttinfos; ++i) { PyObject *tzname = PyTuple_GetItem(abbr, i); if (tzname == NULL) { @@ -1006,6 +1018,9 @@ load_data(PyZoneInfo_ZoneInfo *self, PyObject *file_obj) // Build our mapping from transition to the ttinfo that applies self->trans_ttinfos = PyMem_Calloc(self->num_transitions, sizeof(_ttinfo *)); + if (self->trans_ttinfos == NULL) { + goto error; + } for (size_t i = 0; i < self->num_transitions; ++i) { size_t ttinfo_idx = trans_idx[i]; assert(ttinfo_idx < self->num_ttinfos); @@ -1219,15 +1234,9 @@ calendarrule_new(uint8_t month, uint8_t week, uint8_t day, int8_t hour, return -1; } - // day is an unsigned integer, so day < 0 should always return false, but - // if day's type changes to a signed integer *without* changing this value, - // it may create a bug. Considering that the compiler should be able to - // optimize out the first comparison if day is an unsigned integer anyway, - // we will leave this comparison in place and disable the compiler warning. -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wtype-limits" - if (day < 0 || day > 6) { -#pragma GCC diagnostic pop + // If the 'day' parameter type is changed to a signed type, + // "day < 0" check must be added. + if (/* day < 0 || */ day > 6) { PyErr_Format(PyExc_ValueError, "Day must be in [0, 6]"); return -1; } @@ -2341,7 +2350,11 @@ find_in_strong_cache(const StrongCacheNode *const root, PyObject *const key) { const StrongCacheNode *node = root; while (node != NULL) { - if (PyObject_RichCompareBool(key, node->key, Py_EQ)) { + int rv = PyObject_RichCompareBool(key, node->key, Py_EQ); + if (rv < 0) { + return NULL; + } + if (rv) { return (StrongCacheNode *)node; } @@ -2355,11 +2368,11 @@ find_in_strong_cache(const StrongCacheNode *const root, PyObject *const key) * * This function is used to enable the per-key functionality in clear_cache. */ -static void +static int eject_from_strong_cache(const PyTypeObject *const type, PyObject *key) { if (type != &PyZoneInfo_ZoneInfoType) { - return; + return 0; } StrongCacheNode *node = find_in_strong_cache(ZONEINFO_STRONG_CACHE, key); @@ -2368,6 +2381,10 @@ eject_from_strong_cache(const PyTypeObject *const type, PyObject *key) strong_cache_node_free(node); } + else if (PyErr_Occurred()) { + return -1; + } + return 0; } /* Moves a node to the front of the LRU cache. @@ -2524,7 +2541,11 @@ zoneinfo_init_subclass(PyTypeObject *cls, PyObject *args, PyObject **kwargs) return NULL; } - PyObject_SetAttrString((PyObject *)cls, "_weak_cache", weak_cache); + if (PyObject_SetAttrString((PyObject *)cls, "_weak_cache", + weak_cache) < 0) { + Py_DECREF(weak_cache); + return NULL; + } Py_DECREF(weak_cache); Py_RETURN_NONE; } @@ -2624,6 +2645,9 @@ static int zoneinfomodule_exec(PyObject *m) { PyDateTime_IMPORT; + if (PyDateTimeAPI == NULL) { + goto error; + } PyZoneInfo_ZoneInfoType.tp_base = PyDateTimeAPI->TZInfoType; if (PyType_Ready(&PyZoneInfo_ZoneInfoType) < 0) { goto error; diff --git a/Modules/clinic/_cursesmodule.c.h b/Modules/clinic/_cursesmodule.c.h index 50d7f213..66ffcdfd 100644 --- a/Modules/clinic/_cursesmodule.c.h +++ b/Modules/clinic/_cursesmodule.c.h @@ -2028,7 +2028,7 @@ PyDoc_STRVAR(_curses_color_content__doc__, "Return the red, green, and blue (RGB) components of the specified color.\n" "\n" " color_number\n" -" The number of the color (0 - COLORS).\n" +" The number of the color (0 - (COLORS-1)).\n" "\n" "A 3-tuple is returned, containing the R, G, B values for the given color,\n" "which will be between 0 (no component) and 1000 (maximum amount of component)."); @@ -2076,13 +2076,13 @@ exit: } PyDoc_STRVAR(_curses_color_pair__doc__, -"color_pair($module, color_number, /)\n" +"color_pair($module, pair_number, /)\n" "--\n" "\n" "Return the attribute value for displaying text in the specified color.\n" "\n" -" color_number\n" -" The number of the color (0 - COLORS).\n" +" pair_number\n" +" The number of the color pair.\n" "\n" "This attribute value can be combined with A_STANDOUT, A_REVERSE, and the\n" "other A_* attributes. pair_number() is the counterpart to this function."); @@ -2091,13 +2091,13 @@ PyDoc_STRVAR(_curses_color_pair__doc__, {"color_pair", (PyCFunction)_curses_color_pair, METH_O, _curses_color_pair__doc__}, static PyObject * -_curses_color_pair_impl(PyObject *module, short color_number); +_curses_color_pair_impl(PyObject *module, short pair_number); static PyObject * _curses_color_pair(PyObject *module, PyObject *arg) { PyObject *return_value = NULL; - short color_number; + short pair_number; if (PyFloat_Check(arg)) { PyErr_SetString(PyExc_TypeError, @@ -2120,10 +2120,10 @@ _curses_color_pair(PyObject *module, PyObject *arg) goto exit; } else { - color_number = (short) ival; + pair_number = (short) ival; } } - return_value = _curses_color_pair_impl(module, color_number); + return_value = _curses_color_pair_impl(module, pair_number); exit: return return_value; @@ -2699,7 +2699,7 @@ PyDoc_STRVAR(_curses_init_color__doc__, "Change the definition of a color.\n" "\n" " color_number\n" -" The number of the color to be changed (0 - COLORS).\n" +" The number of the color to be changed (0 - (COLORS-1)).\n" " r\n" " Red component (0 - 1000).\n" " g\n" @@ -2709,7 +2709,7 @@ PyDoc_STRVAR(_curses_init_color__doc__, "\n" "When init_color() is used, all occurrences of that color on the screen\n" "immediately change to the new definition. This function is a no-op on\n" -"most terminals; it is active only if can_change_color() returns 1."); +"most terminals; it is active only if can_change_color() returns true."); #define _CURSES_INIT_COLOR_METHODDEF \ {"init_color", (PyCFunction)(void(*)(void))_curses_init_color, METH_FASTCALL, _curses_init_color__doc__}, @@ -2841,9 +2841,9 @@ PyDoc_STRVAR(_curses_init_pair__doc__, " pair_number\n" " The number of the color-pair to be changed (1 - (COLOR_PAIRS-1)).\n" " fg\n" -" Foreground color number (0 - COLORS).\n" +" Foreground color number (-1 - (COLORS-1)).\n" " bg\n" -" Background color number (0 - COLORS).\n" +" Background color number (-1 - (COLORS-1)).\n" "\n" "If the color-pair was previously initialized, the screen is refreshed and\n" "all occurrences of that color-pair are changed to the new definition."); @@ -4713,4 +4713,4 @@ _curses_use_default_colors(PyObject *module, PyObject *Py_UNUSED(ignored)) #ifndef _CURSES_USE_DEFAULT_COLORS_METHODDEF #define _CURSES_USE_DEFAULT_COLORS_METHODDEF #endif /* !defined(_CURSES_USE_DEFAULT_COLORS_METHODDEF) */ -/*[clinic end generated code: output=b53652f8acafd817 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=5e739120041df368 input=a9049054013a1b77]*/ diff --git a/Modules/itertoolsmodule.c b/Modules/itertoolsmodule.c index 18fcebdf..95ef8d79 100644 --- a/Modules/itertoolsmodule.c +++ b/Modules/itertoolsmodule.c @@ -2,6 +2,7 @@ #define PY_SSIZE_T_CLEAN #include "Python.h" #include "pycore_tupleobject.h" +#include "pycore_object.h" // _PyObject_GC_TRACK() #include // offsetof() /* Itertools module written and maintained @@ -2245,6 +2246,11 @@ product_next(productobject *lz) lz->result = result; Py_DECREF(old_result); } + // bpo-42536: The GC may have untracked this result tuple. Since we're + // recycling it, make sure it's tracked again: + else if (!_PyObject_GC_IS_TRACKED(result)) { + _PyObject_GC_TRACK(result); + } /* Now, we've got the only copy so we can update it in-place */ assert (npools==0 || Py_REFCNT(result) == 1); @@ -2568,6 +2574,11 @@ combinations_next(combinationsobject *co) co->result = result; Py_DECREF(old_result); } + // bpo-42536: The GC may have untracked this result tuple. Since we're + // recycling it, make sure it's tracked again: + else if (!_PyObject_GC_IS_TRACKED(result)) { + _PyObject_GC_TRACK(result); + } /* Now, we've got the only copy so we can update it in-place * CPython's empty tuple is a singleton and cached in * PyTuple's freelist. @@ -2902,6 +2913,11 @@ cwr_next(cwrobject *co) co->result = result; Py_DECREF(old_result); } + // bpo-42536: The GC may have untracked this result tuple. Since we're + // recycling it, make sure it's tracked again: + else if (!_PyObject_GC_IS_TRACKED(result)) { + _PyObject_GC_TRACK(result); + } /* Now, we've got the only copy so we can update it in-place CPython's empty tuple is a singleton and cached in PyTuple's freelist. */ assert(r == 0 || Py_REFCNT(result) == 1); @@ -3246,6 +3262,11 @@ permutations_next(permutationsobject *po) po->result = result; Py_DECREF(old_result); } + // bpo-42536: The GC may have untracked this result tuple. Since we're + // recycling it, make sure it's tracked again: + else if (!_PyObject_GC_IS_TRACKED(result)) { + _PyObject_GC_TRACK(result); + } /* Now, we've got the only copy so we can update it in-place */ assert(r == 0 || Py_REFCNT(result) == 1); @@ -4515,6 +4536,11 @@ zip_longest_next(ziplongestobject *lz) PyTuple_SET_ITEM(result, i, item); Py_DECREF(olditem); } + // bpo-42536: The GC may have untracked this result tuple. Since we're + // recycling it, make sure it's tracked again: + if (!_PyObject_GC_IS_TRACKED(result)) { + _PyObject_GC_TRACK(result); + } } else { result = PyTuple_New(tuplesize); if (result == NULL) diff --git a/Modules/md5module.c b/Modules/md5module.c index e4d9db40..6ed84337 100644 --- a/Modules/md5module.c +++ b/Modules/md5module.c @@ -74,7 +74,7 @@ typedef struct { * The library is free for all purposes without any express * guarantee it works. * - * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + * Tom St Denis, tomstdenis@gmail.com, https://www.libtom.net */ /* rotate the hard way (platform optimizations could be done) */ diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 12f72f52..5e335027 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -56,7 +56,13 @@ */ #if defined(__APPLE__) -#if defined(__has_builtin) && __has_builtin(__builtin_available) +#if defined(__has_builtin) +#if __has_builtin(__builtin_available) +#define HAVE_BUILTIN_AVAILABLE 1 +#endif +#endif + +#ifdef HAVE_BUILTIN_AVAILABLE # define HAVE_FSTATAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) # define HAVE_FACCESSAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) # define HAVE_FCHMODAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) @@ -632,7 +638,7 @@ _PyLong_FromGid(gid_t gid) } int -_Py_Uid_Converter(PyObject *obj, void *p) +_Py_Uid_Converter(PyObject *obj, uid_t *p) { uid_t uid; PyObject *index; @@ -719,7 +725,7 @@ _Py_Uid_Converter(PyObject *obj, void *p) success: Py_DECREF(index); - *(uid_t *)p = uid; + *p = uid; return 1; underflow: @@ -738,7 +744,7 @@ fail: } int -_Py_Gid_Converter(PyObject *obj, void *p) +_Py_Gid_Converter(PyObject *obj, gid_t *p) { gid_t gid; PyObject *index; @@ -826,7 +832,7 @@ _Py_Gid_Converter(PyObject *obj, void *p) success: Py_DECREF(index); - *(gid_t *)p = gid; + *p = gid; return 1; underflow: diff --git a/Modules/posixmodule.h b/Modules/posixmodule.h index 1e00562a..711ac686 100644 --- a/Modules/posixmodule.h +++ b/Modules/posixmodule.h @@ -14,8 +14,8 @@ extern "C" { #ifndef MS_WINDOWS PyAPI_FUNC(PyObject *) _PyLong_FromUid(uid_t); PyAPI_FUNC(PyObject *) _PyLong_FromGid(gid_t); -PyAPI_FUNC(int) _Py_Uid_Converter(PyObject *, void *); -PyAPI_FUNC(int) _Py_Gid_Converter(PyObject *, void *); +PyAPI_FUNC(int) _Py_Uid_Converter(PyObject *, uid_t *); +PyAPI_FUNC(int) _Py_Gid_Converter(PyObject *, gid_t *); #endif /* MS_WINDOWS */ #if defined(PYPTHREAD_SIGMASK) || defined(HAVE_SIGWAIT) || \ diff --git a/Modules/readline.c b/Modules/readline.c index 12d6cc78..1e74f997 100644 --- a/Modules/readline.c +++ b/Modules/readline.c @@ -146,6 +146,26 @@ decode(const char *s) } +/* +Explicitly disable bracketed paste in the interactive interpreter, even if it's +set in the inputrc, is enabled by default (eg GNU Readline 8.1), or a user calls +readline.read_init_file(). The Python REPL has not implemented bracketed +paste support. Also, bracketed mode writes the "\x1b[?2004h" escape sequence +into stdout which causes test failures in applications that don't support it. +It can still be explicitly enabled by calling readline.parse_and_bind("set +enable-bracketed-paste on"). See bpo-42819 for more details. + +This should be removed if bracketed paste mode is implemented (bpo-39820). +*/ + +static void +disable_bracketed_paste(void) +{ + if (!using_libedit_emulation) { + rl_variable_bind ("enable-bracketed-paste", "off"); + } +} + /* Exported function to send one line to readline's init file parser */ static PyObject * @@ -192,6 +212,7 @@ read_init_file(PyObject *self, PyObject *args) errno = rl_read_init_file(NULL); if (errno) return PyErr_SetFromErrno(PyExc_OSError); + disable_bracketed_paste(); Py_RETURN_NONE; } @@ -1152,6 +1173,8 @@ setup_readline(readlinestate *mod_state) else rl_initialize(); + disable_bracketed_paste(); + RESTORE_LOCALE(saved_locale) return 0; } diff --git a/Modules/sha1module.c b/Modules/sha1module.c index b0656d83..9c75cc99 100644 --- a/Modules/sha1module.c +++ b/Modules/sha1module.c @@ -74,7 +74,7 @@ typedef struct { * The library is free for all purposes without any express * guarantee it works. * - * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + * Tom St Denis, tomstdenis@gmail.com, https://www.libtom.net */ /* rotate the hard way (platform optimizations could be done) */ diff --git a/Modules/sha256module.c b/Modules/sha256module.c index 8edb1d53..9b885c72 100644 --- a/Modules/sha256module.c +++ b/Modules/sha256module.c @@ -93,7 +93,7 @@ static void SHAcopy(SHAobject *src, SHAobject *dest) * The library is free for all purposes without any express * guarantee it works. * - * Tom St Denis, tomstdenis@iahu.ca, http://libtom.org + * Tom St Denis, tomstdenis@iahu.ca, https://www.libtom.net */ diff --git a/Modules/sha512module.c b/Modules/sha512module.c index 561ef8ef..831160c3 100644 --- a/Modules/sha512module.c +++ b/Modules/sha512module.c @@ -94,7 +94,7 @@ static void SHAcopy(SHAobject *src, SHAobject *dest) * The library is free for all purposes without any express * guarantee it works. * - * Tom St Denis, tomstdenis@iahu.ca, http://libtom.org + * Tom St Denis, tomstdenis@iahu.ca, https://www.libtom.net */ diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 76ef606e..be75e681 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -3407,8 +3407,9 @@ sock_getsockname(PySocketSockObject *s, PyObject *Py_UNUSED(ignored)) PyDoc_STRVAR(getsockname_doc, "getsockname() -> address info\n\ \n\ -Return the address of the local endpoint. For IP sockets, the address\n\ -info is a pair (hostaddr, port)."); +Return the address of the local endpoint. The format depends on the\n\ +address family. For IPv4 sockets, the address info is a pair\n\ +(hostaddr, port)."); #ifdef HAVE_GETPEERNAME /* Cray APP doesn't have this :-( */ diff --git a/Objects/call.c b/Objects/call.c index 61426c7e..87dc0dbb 100644 --- a/Objects/call.c +++ b/Objects/call.c @@ -205,6 +205,7 @@ PyObject * PyVectorcall_Call(PyObject *callable, PyObject *tuple, PyObject *kwargs) { PyThreadState *tstate = _PyThreadState_GET(); + vectorcallfunc func; /* get vectorcallfunc as in PyVectorcall_Function, but without * the Py_TPFLAGS_HAVE_VECTORCALL check */ @@ -215,7 +216,7 @@ PyVectorcall_Call(PyObject *callable, PyObject *tuple, PyObject *kwargs) Py_TYPE(callable)->tp_name); return NULL; } - vectorcallfunc func = *(vectorcallfunc *)(((char *)callable) + offset); + memcpy(&func, (char *) callable + offset, sizeof(func)); if (func == NULL) { _PyErr_Format(tstate, PyExc_TypeError, "'%.200s' object does not support vectorcall", diff --git a/Objects/dictobject.c b/Objects/dictobject.c index faee6bc9..8a056530 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -3861,6 +3861,11 @@ dictiter_iternextitem(dictiterobject *di) Py_INCREF(result); Py_DECREF(oldkey); Py_DECREF(oldvalue); + // bpo-42536: The GC may have untracked this result tuple. Since we're + // recycling it, make sure it's tracked again: + if (!_PyObject_GC_IS_TRACKED(result)) { + _PyObject_GC_TRACK(result); + } } else { result = PyTuple_New(2); @@ -3976,6 +3981,11 @@ dictreviter_iternext(dictiterobject *di) Py_INCREF(result); Py_DECREF(oldkey); Py_DECREF(oldvalue); + // bpo-42536: The GC may have untracked this result tuple. Since + // we're recycling it, make sure it's tracked again: + if (!_PyObject_GC_IS_TRACKED(result)) { + _PyObject_GC_TRACK(result); + } } else { result = PyTuple_New(2); diff --git a/Objects/enumobject.c b/Objects/enumobject.c index 4a83bb45..bdd0ea5f 100644 --- a/Objects/enumobject.c +++ b/Objects/enumobject.c @@ -1,6 +1,7 @@ /* enumerate object */ #include "Python.h" +#include "pycore_object.h" // _PyObject_GC_TRACK() #include "clinic/enumobject.c.h" @@ -130,6 +131,11 @@ enum_next_long(enumobject *en, PyObject* next_item) PyTuple_SET_ITEM(result, 1, next_item); Py_DECREF(old_index); Py_DECREF(old_item); + // bpo-42536: The GC may have untracked this result tuple. Since we're + // recycling it, make sure it's tracked again: + if (!_PyObject_GC_IS_TRACKED(result)) { + _PyObject_GC_TRACK(result); + } return result; } result = PyTuple_New(2); @@ -175,6 +181,11 @@ enum_next(enumobject *en) PyTuple_SET_ITEM(result, 1, next_item); Py_DECREF(old_index); Py_DECREF(old_item); + // bpo-42536: The GC may have untracked this result tuple. Since we're + // recycling it, make sure it's tracked again: + if (!_PyObject_GC_IS_TRACKED(result)) { + _PyObject_GC_TRACK(result); + } return result; } result = PyTuple_New(2); diff --git a/Objects/exceptions.c b/Objects/exceptions.c index e44ce727..eb72de53 100644 --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -2531,8 +2531,10 @@ _PyExc_Init(void) do { \ PyObject *_code = PyLong_FromLong(CODE); \ assert(_PyObject_RealIsSubclass(PyExc_ ## TYPE, PyExc_OSError)); \ - if (!_code || PyDict_SetItem(errnomap, _code, PyExc_ ## TYPE)) \ + if (!_code || PyDict_SetItem(errnomap, _code, PyExc_ ## TYPE)) { \ + Py_XDECREF(_code); \ return _PyStatus_ERR("errmap insertion problem."); \ + } \ Py_DECREF(_code); \ } while (0) diff --git a/Objects/genericaliasobject.c b/Objects/genericaliasobject.c index c5a81a5c..945d2059 100644 --- a/Objects/genericaliasobject.c +++ b/Objects/genericaliasobject.c @@ -428,8 +428,8 @@ ga_getattro(PyObject *self, PyObject *name) static PyObject * ga_richcompare(PyObject *a, PyObject *b, int op) { - if (!Py_IS_TYPE(a, &Py_GenericAliasType) || - !Py_IS_TYPE(b, &Py_GenericAliasType) || + if (!PyObject_TypeCheck(a, &Py_GenericAliasType) || + !PyObject_TypeCheck(b, &Py_GenericAliasType) || (op != Py_EQ && op != Py_NE)) { Py_RETURN_NOTIMPLEMENTED; @@ -563,6 +563,29 @@ static PyGetSetDef ga_properties[] = { {0} }; +/* A helper function to create GenericAlias' args tuple and set its attributes. + * Returns 1 on success, 0 on failure. + */ +static inline int +setup_ga(gaobject *alias, PyObject *origin, PyObject *args) { + if (!PyTuple_Check(args)) { + args = PyTuple_Pack(1, args); + if (args == NULL) { + return 0; + } + } + else { + Py_INCREF(args); + } + + Py_INCREF(origin); + alias->origin = origin; + alias->args = args; + alias->parameters = NULL; + alias->weakreflist = NULL; + return 1; +} + static PyObject * ga_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { @@ -574,7 +597,15 @@ ga_new(PyTypeObject *type, PyObject *args, PyObject *kwds) } PyObject *origin = PyTuple_GET_ITEM(args, 0); PyObject *arguments = PyTuple_GET_ITEM(args, 1); - return Py_GenericAlias(origin, arguments); + gaobject *self = (gaobject *)type->tp_alloc(type, 0); + if (self == NULL) { + return NULL; + } + if (!setup_ga(self, origin, arguments)) { + type->tp_free((PyObject *)self); + return NULL; + } + return (PyObject *)self; } // TODO: @@ -594,7 +625,7 @@ PyTypeObject Py_GenericAliasType = { .tp_hash = ga_hash, .tp_call = ga_call, .tp_getattro = ga_getattro, - .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE, .tp_traverse = ga_traverse, .tp_richcompare = ga_richcompare, .tp_weaklistoffset = offsetof(gaobject, weakreflist), @@ -609,27 +640,14 @@ PyTypeObject Py_GenericAliasType = { PyObject * Py_GenericAlias(PyObject *origin, PyObject *args) { - if (!PyTuple_Check(args)) { - args = PyTuple_Pack(1, args); - if (args == NULL) { - return NULL; - } - } - else { - Py_INCREF(args); - } - gaobject *alias = PyObject_GC_New(gaobject, &Py_GenericAliasType); if (alias == NULL) { - Py_DECREF(args); return NULL; } - - Py_INCREF(origin); - alias->origin = origin; - alias->args = args; - alias->parameters = NULL; - alias->weakreflist = NULL; + if (!setup_ga(alias, origin, args)) { + PyObject_GC_Del((PyObject *)alias); + return NULL; + } _PyObject_GC_TRACK(alias); return (PyObject *)alias; } diff --git a/Objects/odictobject.c b/Objects/odictobject.c index d5bf4995..b9f2169a 100644 --- a/Objects/odictobject.c +++ b/Objects/odictobject.c @@ -1817,6 +1817,11 @@ odictiter_iternext(odictiterobject *di) Py_INCREF(result); Py_DECREF(PyTuple_GET_ITEM(result, 0)); /* borrowed */ Py_DECREF(PyTuple_GET_ITEM(result, 1)); /* borrowed */ + // bpo-42536: The GC may have untracked this result tuple. Since we're + // recycling it, make sure it's tracked again: + if (!_PyObject_GC_IS_TRACKED(result)) { + _PyObject_GC_TRACK(result); + } } else { result = PyTuple_New(2); diff --git a/PC/python3.def b/PC/python3.def index 66d1595c..d27d7d07 100644 --- a/PC/python3.def +++ b/PC/python3.def @@ -40,6 +40,7 @@ EXPORTS PyCFunction_GetSelf=python39.PyCFunction_GetSelf PyCFunction_New=python39.PyCFunction_New PyCFunction_NewEx=python39.PyCFunction_NewEx + PyCMethod_New=python39.PyCMethod_New PyCFunction_Type=python39.PyCFunction_Type DATA PyCallIter_New=python39.PyCallIter_New PyCallIter_Type=python39.PyCallIter_Type DATA @@ -266,6 +267,8 @@ EXPORTS PyFloat_GetMax=python39.PyFloat_GetMax PyFloat_GetMin=python39.PyFloat_GetMin PyFloat_Type=python39.PyFloat_Type DATA + PyFrame_GetCode=python39.PyFrame_GetCode + PyFrame_GetLineNumber=python39.PyFrame_GetLineNumber PyFrozenSet_New=python39.PyFrozenSet_New PyFrozenSet_Type=python39.PyFrozenSet_Type DATA PyGC_Collect=python39.PyGC_Collect @@ -453,6 +456,7 @@ EXPORTS PyObject_CallFunctionObjArgs=python39.PyObject_CallFunctionObjArgs PyObject_CallMethod=python39.PyObject_CallMethod PyObject_CallMethodObjArgs=python39.PyObject_CallMethodObjArgs + PyObject_CallNoArgs=python39.PyObject_CallNoArgs PyObject_CallObject=python39.PyObject_CallObject PyObject_Calloc=python39.PyObject_Calloc PyObject_CheckReadBuffer=python39.PyObject_CheckReadBuffer @@ -569,6 +573,9 @@ EXPORTS PyThreadState_DeleteCurrent=python39.PyThreadState_DeleteCurrent PyThreadState_Get=python39.PyThreadState_Get PyThreadState_GetDict=python39.PyThreadState_GetDict + PyThreadState_GetFrame=python39.PyThreadState_GetFrame + PyThreadState_GetID=python39.PyThreadState_GetID + PyThreadState_GetInterpreter=python39.PyThreadState_GetInterpreter PyThreadState_New=python39.PyThreadState_New PyThreadState_SetAsyncExc=python39.PyThreadState_SetAsyncExc PyThreadState_Swap=python39.PyThreadState_Swap diff --git a/PC/python_ver_rc.h b/PC/python_ver_rc.h index 060aecdc..90fc6ba1 100644 --- a/PC/python_ver_rc.h +++ b/PC/python_ver_rc.h @@ -5,7 +5,7 @@ #include "winver.h" #define PYTHON_COMPANY "Python Software Foundation" -#define PYTHON_COPYRIGHT "Copyright \xA9 2001-2016 Python Software Foundation. Copyright \xA9 2000 BeOpen.com. Copyright \xA9 1995-2001 CNRI. Copyright \xA9 1991-1995 SMC." +#define PYTHON_COPYRIGHT "Copyright \xA9 2001-2021 Python Software Foundation. Copyright \xA9 2000 BeOpen.com. Copyright \xA9 1995-2001 CNRI. Copyright \xA9 1991-1995 SMC." #define MS_WINDOWS #include "modsupport.h" diff --git a/PCbuild/get_externals.bat b/PCbuild/get_externals.bat index 9dc84a6c..395aa0d4 100644 --- a/PCbuild/get_externals.bat +++ b/PCbuild/get_externals.bat @@ -53,8 +53,8 @@ echo.Fetching external libraries... set libraries= set libraries=%libraries% bzip2-1.0.6 if NOT "%IncludeLibffiSrc%"=="false" set libraries=%libraries% libffi -if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-1.1.1g -set libraries=%libraries% sqlite-3.33.0.0 +if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-1.1.1i +set libraries=%libraries% sqlite-3.34.0.0 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tcl-core-8.6.9.0 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tk-8.6.9.0 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tix-8.4.3.6 @@ -77,7 +77,7 @@ echo.Fetching external binaries... set binaries= if NOT "%IncludeLibffi%"=="false" set binaries=%binaries% libffi -if NOT "%IncludeSSL%"=="false" set binaries=%binaries% openssl-bin-1.1.1g +if NOT "%IncludeSSL%"=="false" set binaries=%binaries% openssl-bin-1.1.1i if NOT "%IncludeTkinter%"=="false" set binaries=%binaries% tcltk-8.6.9.0 if NOT "%IncludeSSLSrc%"=="false" set binaries=%binaries% nasm-2.11.06 diff --git a/PCbuild/pyproject.props b/PCbuild/pyproject.props index 95649ea1..db5f9016 100644 --- a/PCbuild/pyproject.props +++ b/PCbuild/pyproject.props @@ -46,6 +46,7 @@ true OnlyExplicitInline OnlyExplicitInline + /utf-8 %(AdditionalOptions) Disabled diff --git a/PCbuild/python.props b/PCbuild/python.props index 3a3a7873..be31a158 100644 --- a/PCbuild/python.props +++ b/PCbuild/python.props @@ -56,14 +56,14 @@ $(EXTERNALS_DIR) $([System.IO.Path]::GetFullPath(`$(PySourcePath)externals`)) $(ExternalsDir)\ - $(ExternalsDir)sqlite-3.33.0.0\ + $(ExternalsDir)sqlite-3.34.0.0\ $(ExternalsDir)bzip2-1.0.6\ $(ExternalsDir)xz-5.2.2\ $(ExternalsDir)libffi\ $(ExternalsDir)libffi\$(ArchName)\ $(libffiOutDir)include - $(ExternalsDir)openssl-1.1.1g\ - $(ExternalsDir)openssl-bin-1.1.1g\$(ArchName)\ + $(ExternalsDir)openssl-1.1.1i\ + $(ExternalsDir)openssl-bin-1.1.1i\$(ArchName)\ $(opensslOutDir)include $(ExternalsDir)\nasm-2.11.06\ $(ExternalsDir)\zlib-1.2.11\ diff --git a/PCbuild/readme.txt b/PCbuild/readme.txt index e07cdd21..c06644aa 100644 --- a/PCbuild/readme.txt +++ b/PCbuild/readme.txt @@ -166,7 +166,7 @@ _lzma Homepage: http://tukaani.org/xz/ _ssl - Python wrapper for version 1.1.1c of the OpenSSL secure sockets + Python wrapper for version 1.1.1i of the OpenSSL secure sockets library, which is downloaded from our binaries repository at https://github.com/python/cpython-bin-deps. @@ -185,7 +185,7 @@ _ssl again when building. _sqlite3 - Wraps SQLite 3.33.0, which is itself built by sqlite3.vcxproj + Wraps SQLite 3.34.0, which is itself built by sqlite3.vcxproj Homepage: http://www.sqlite.org/ _tkinter diff --git a/Parser/pegen/parse.c b/Parser/pegen/parse.c index 0eb61db3..98008a5f 100644 --- a/Parser/pegen/parse.c +++ b/Parser/pegen/parse.c @@ -204,192 +204,196 @@ static KeywordToken *reserved_keywords[] = { #define kwarg_or_starred_type 1126 #define kwarg_or_double_starred_type 1127 #define star_targets_type 1128 -#define star_targets_seq_type 1129 -#define star_target_type 1130 -#define star_atom_type 1131 -#define single_target_type 1132 -#define single_subscript_attribute_target_type 1133 -#define del_targets_type 1134 -#define del_target_type 1135 -#define del_t_atom_type 1136 -#define targets_type 1137 -#define target_type 1138 -#define t_primary_type 1139 // Left-recursive -#define t_lookahead_type 1140 -#define t_atom_type 1141 -#define invalid_arguments_type 1142 -#define invalid_kwarg_type 1143 -#define invalid_named_expression_type 1144 -#define invalid_assignment_type 1145 -#define invalid_ann_assign_target_type 1146 -#define invalid_del_stmt_type 1147 -#define invalid_block_type 1148 -#define invalid_primary_type 1149 // Left-recursive -#define invalid_comprehension_type 1150 -#define invalid_dict_comprehension_type 1151 -#define invalid_parameters_type 1152 -#define invalid_lambda_parameters_type 1153 -#define invalid_star_etc_type 1154 -#define invalid_lambda_star_etc_type 1155 -#define invalid_double_type_comments_type 1156 -#define invalid_with_item_type 1157 -#define invalid_for_target_type 1158 -#define invalid_group_type 1159 -#define invalid_import_from_targets_type 1160 -#define _loop0_1_type 1161 -#define _loop0_2_type 1162 -#define _loop0_4_type 1163 -#define _gather_3_type 1164 -#define _loop0_6_type 1165 -#define _gather_5_type 1166 -#define _loop0_8_type 1167 -#define _gather_7_type 1168 -#define _loop0_10_type 1169 -#define _gather_9_type 1170 -#define _loop1_11_type 1171 -#define _loop0_13_type 1172 -#define _gather_12_type 1173 -#define _tmp_14_type 1174 -#define _tmp_15_type 1175 -#define _tmp_16_type 1176 -#define _tmp_17_type 1177 -#define _tmp_18_type 1178 -#define _tmp_19_type 1179 -#define _tmp_20_type 1180 -#define _tmp_21_type 1181 -#define _loop1_22_type 1182 -#define _tmp_23_type 1183 -#define _tmp_24_type 1184 -#define _loop0_26_type 1185 -#define _gather_25_type 1186 -#define _loop0_28_type 1187 -#define _gather_27_type 1188 -#define _tmp_29_type 1189 -#define _tmp_30_type 1190 -#define _loop0_31_type 1191 -#define _loop1_32_type 1192 -#define _loop0_34_type 1193 -#define _gather_33_type 1194 -#define _tmp_35_type 1195 -#define _loop0_37_type 1196 -#define _gather_36_type 1197 -#define _tmp_38_type 1198 -#define _loop0_40_type 1199 -#define _gather_39_type 1200 -#define _loop0_42_type 1201 -#define _gather_41_type 1202 -#define _loop0_44_type 1203 -#define _gather_43_type 1204 -#define _loop0_46_type 1205 -#define _gather_45_type 1206 -#define _tmp_47_type 1207 -#define _loop1_48_type 1208 -#define _tmp_49_type 1209 -#define _tmp_50_type 1210 -#define _tmp_51_type 1211 -#define _tmp_52_type 1212 -#define _tmp_53_type 1213 -#define _loop0_54_type 1214 -#define _loop0_55_type 1215 -#define _loop0_56_type 1216 -#define _loop1_57_type 1217 -#define _loop0_58_type 1218 -#define _loop1_59_type 1219 -#define _loop1_60_type 1220 -#define _loop1_61_type 1221 -#define _loop0_62_type 1222 -#define _loop1_63_type 1223 -#define _loop0_64_type 1224 -#define _loop1_65_type 1225 -#define _loop0_66_type 1226 -#define _loop1_67_type 1227 -#define _loop1_68_type 1228 -#define _tmp_69_type 1229 -#define _loop1_70_type 1230 -#define _loop0_72_type 1231 -#define _gather_71_type 1232 -#define _loop1_73_type 1233 -#define _loop0_74_type 1234 -#define _loop0_75_type 1235 -#define _loop0_76_type 1236 -#define _loop1_77_type 1237 -#define _loop0_78_type 1238 -#define _loop1_79_type 1239 -#define _loop1_80_type 1240 -#define _loop1_81_type 1241 -#define _loop0_82_type 1242 -#define _loop1_83_type 1243 -#define _loop0_84_type 1244 -#define _loop1_85_type 1245 -#define _loop0_86_type 1246 -#define _loop1_87_type 1247 -#define _loop1_88_type 1248 -#define _loop1_89_type 1249 -#define _loop1_90_type 1250 -#define _tmp_91_type 1251 -#define _loop0_93_type 1252 -#define _gather_92_type 1253 -#define _tmp_94_type 1254 -#define _tmp_95_type 1255 -#define _tmp_96_type 1256 -#define _tmp_97_type 1257 -#define _loop1_98_type 1258 -#define _tmp_99_type 1259 -#define _tmp_100_type 1260 -#define _loop0_102_type 1261 -#define _gather_101_type 1262 -#define _loop1_103_type 1263 -#define _loop0_104_type 1264 -#define _loop0_105_type 1265 -#define _loop0_107_type 1266 -#define _gather_106_type 1267 -#define _tmp_108_type 1268 -#define _loop0_110_type 1269 -#define _gather_109_type 1270 -#define _loop0_112_type 1271 -#define _gather_111_type 1272 -#define _loop0_114_type 1273 -#define _gather_113_type 1274 -#define _loop0_116_type 1275 -#define _gather_115_type 1276 -#define _loop0_117_type 1277 -#define _loop0_119_type 1278 -#define _gather_118_type 1279 -#define _tmp_120_type 1280 -#define _loop0_122_type 1281 -#define _gather_121_type 1282 -#define _loop0_124_type 1283 -#define _gather_123_type 1284 -#define _tmp_125_type 1285 -#define _loop0_126_type 1286 -#define _loop0_127_type 1287 -#define _loop0_128_type 1288 -#define _tmp_129_type 1289 -#define _tmp_130_type 1290 -#define _loop0_131_type 1291 -#define _tmp_132_type 1292 -#define _loop0_133_type 1293 -#define _tmp_134_type 1294 -#define _tmp_135_type 1295 -#define _tmp_136_type 1296 -#define _tmp_137_type 1297 -#define _tmp_138_type 1298 -#define _tmp_139_type 1299 -#define _tmp_140_type 1300 -#define _tmp_141_type 1301 -#define _tmp_142_type 1302 -#define _tmp_143_type 1303 -#define _tmp_144_type 1304 -#define _tmp_145_type 1305 -#define _tmp_146_type 1306 -#define _tmp_147_type 1307 -#define _tmp_148_type 1308 -#define _tmp_149_type 1309 -#define _tmp_150_type 1310 -#define _loop1_151_type 1311 -#define _loop1_152_type 1312 -#define _tmp_153_type 1313 -#define _tmp_154_type 1314 +#define star_targets_list_seq_type 1129 +#define star_targets_tuple_seq_type 1130 +#define star_target_type 1131 +#define target_with_star_atom_type 1132 +#define star_atom_type 1133 +#define single_target_type 1134 +#define single_subscript_attribute_target_type 1135 +#define del_targets_type 1136 +#define del_target_type 1137 +#define del_t_atom_type 1138 +#define targets_type 1139 +#define target_type 1140 +#define t_primary_type 1141 // Left-recursive +#define t_lookahead_type 1142 +#define t_atom_type 1143 +#define invalid_arguments_type 1144 +#define invalid_kwarg_type 1145 +#define invalid_named_expression_type 1146 +#define invalid_assignment_type 1147 +#define invalid_ann_assign_target_type 1148 +#define invalid_del_stmt_type 1149 +#define invalid_block_type 1150 +#define invalid_primary_type 1151 // Left-recursive +#define invalid_comprehension_type 1152 +#define invalid_dict_comprehension_type 1153 +#define invalid_parameters_type 1154 +#define invalid_lambda_parameters_type 1155 +#define invalid_star_etc_type 1156 +#define invalid_lambda_star_etc_type 1157 +#define invalid_double_type_comments_type 1158 +#define invalid_with_item_type 1159 +#define invalid_for_target_type 1160 +#define invalid_group_type 1161 +#define invalid_import_from_targets_type 1162 +#define _loop0_1_type 1163 +#define _loop0_2_type 1164 +#define _loop0_4_type 1165 +#define _gather_3_type 1166 +#define _loop0_6_type 1167 +#define _gather_5_type 1168 +#define _loop0_8_type 1169 +#define _gather_7_type 1170 +#define _loop0_10_type 1171 +#define _gather_9_type 1172 +#define _loop1_11_type 1173 +#define _loop0_13_type 1174 +#define _gather_12_type 1175 +#define _tmp_14_type 1176 +#define _tmp_15_type 1177 +#define _tmp_16_type 1178 +#define _tmp_17_type 1179 +#define _tmp_18_type 1180 +#define _tmp_19_type 1181 +#define _tmp_20_type 1182 +#define _tmp_21_type 1183 +#define _loop1_22_type 1184 +#define _tmp_23_type 1185 +#define _tmp_24_type 1186 +#define _loop0_26_type 1187 +#define _gather_25_type 1188 +#define _loop0_28_type 1189 +#define _gather_27_type 1190 +#define _tmp_29_type 1191 +#define _tmp_30_type 1192 +#define _loop0_31_type 1193 +#define _loop1_32_type 1194 +#define _loop0_34_type 1195 +#define _gather_33_type 1196 +#define _tmp_35_type 1197 +#define _loop0_37_type 1198 +#define _gather_36_type 1199 +#define _tmp_38_type 1200 +#define _loop0_40_type 1201 +#define _gather_39_type 1202 +#define _loop0_42_type 1203 +#define _gather_41_type 1204 +#define _loop0_44_type 1205 +#define _gather_43_type 1206 +#define _loop0_46_type 1207 +#define _gather_45_type 1208 +#define _tmp_47_type 1209 +#define _loop1_48_type 1210 +#define _tmp_49_type 1211 +#define _tmp_50_type 1212 +#define _tmp_51_type 1213 +#define _tmp_52_type 1214 +#define _tmp_53_type 1215 +#define _loop0_54_type 1216 +#define _loop0_55_type 1217 +#define _loop0_56_type 1218 +#define _loop1_57_type 1219 +#define _loop0_58_type 1220 +#define _loop1_59_type 1221 +#define _loop1_60_type 1222 +#define _loop1_61_type 1223 +#define _loop0_62_type 1224 +#define _loop1_63_type 1225 +#define _loop0_64_type 1226 +#define _loop1_65_type 1227 +#define _loop0_66_type 1228 +#define _loop1_67_type 1229 +#define _loop1_68_type 1230 +#define _tmp_69_type 1231 +#define _loop1_70_type 1232 +#define _loop0_72_type 1233 +#define _gather_71_type 1234 +#define _loop1_73_type 1235 +#define _loop0_74_type 1236 +#define _loop0_75_type 1237 +#define _loop0_76_type 1238 +#define _loop1_77_type 1239 +#define _loop0_78_type 1240 +#define _loop1_79_type 1241 +#define _loop1_80_type 1242 +#define _loop1_81_type 1243 +#define _loop0_82_type 1244 +#define _loop1_83_type 1245 +#define _loop0_84_type 1246 +#define _loop1_85_type 1247 +#define _loop0_86_type 1248 +#define _loop1_87_type 1249 +#define _loop1_88_type 1250 +#define _loop1_89_type 1251 +#define _loop1_90_type 1252 +#define _tmp_91_type 1253 +#define _loop0_93_type 1254 +#define _gather_92_type 1255 +#define _tmp_94_type 1256 +#define _tmp_95_type 1257 +#define _tmp_96_type 1258 +#define _tmp_97_type 1259 +#define _loop1_98_type 1260 +#define _tmp_99_type 1261 +#define _tmp_100_type 1262 +#define _loop0_102_type 1263 +#define _gather_101_type 1264 +#define _loop1_103_type 1265 +#define _loop0_104_type 1266 +#define _loop0_105_type 1267 +#define _loop0_107_type 1268 +#define _gather_106_type 1269 +#define _tmp_108_type 1270 +#define _loop0_110_type 1271 +#define _gather_109_type 1272 +#define _loop0_112_type 1273 +#define _gather_111_type 1274 +#define _loop0_114_type 1275 +#define _gather_113_type 1276 +#define _loop0_116_type 1277 +#define _gather_115_type 1278 +#define _loop0_117_type 1279 +#define _loop0_119_type 1280 +#define _gather_118_type 1281 +#define _loop1_120_type 1282 +#define _tmp_121_type 1283 +#define _loop0_123_type 1284 +#define _gather_122_type 1285 +#define _loop0_125_type 1286 +#define _gather_124_type 1287 +#define _tmp_126_type 1288 +#define _loop0_127_type 1289 +#define _loop0_128_type 1290 +#define _loop0_129_type 1291 +#define _tmp_130_type 1292 +#define _tmp_131_type 1293 +#define _loop0_132_type 1294 +#define _tmp_133_type 1295 +#define _loop0_134_type 1296 +#define _tmp_135_type 1297 +#define _tmp_136_type 1298 +#define _tmp_137_type 1299 +#define _tmp_138_type 1300 +#define _tmp_139_type 1301 +#define _tmp_140_type 1302 +#define _tmp_141_type 1303 +#define _tmp_142_type 1304 +#define _tmp_143_type 1305 +#define _tmp_144_type 1306 +#define _tmp_145_type 1307 +#define _tmp_146_type 1308 +#define _tmp_147_type 1309 +#define _tmp_148_type 1310 +#define _tmp_149_type 1311 +#define _tmp_150_type 1312 +#define _tmp_151_type 1313 +#define _tmp_152_type 1314 +#define _loop1_153_type 1315 +#define _loop1_154_type 1316 +#define _tmp_155_type 1317 +#define _tmp_156_type 1318 static mod_ty file_rule(Parser *p); static mod_ty interactive_rule(Parser *p); @@ -520,8 +524,10 @@ static expr_ty starred_expression_rule(Parser *p); static KeywordOrStarred* kwarg_or_starred_rule(Parser *p); static KeywordOrStarred* kwarg_or_double_starred_rule(Parser *p); static expr_ty star_targets_rule(Parser *p); -static asdl_seq* star_targets_seq_rule(Parser *p); +static asdl_seq* star_targets_list_seq_rule(Parser *p); +static asdl_seq* star_targets_tuple_seq_rule(Parser *p); static expr_ty star_target_rule(Parser *p); +static expr_ty target_with_star_atom_rule(Parser *p); static expr_ty star_atom_rule(Parser *p); static expr_ty single_target_rule(Parser *p); static expr_ty single_subscript_attribute_target_rule(Parser *p); @@ -671,21 +677,21 @@ static asdl_seq *_gather_115_rule(Parser *p); static asdl_seq *_loop0_117_rule(Parser *p); static asdl_seq *_loop0_119_rule(Parser *p); static asdl_seq *_gather_118_rule(Parser *p); -static void *_tmp_120_rule(Parser *p); -static asdl_seq *_loop0_122_rule(Parser *p); -static asdl_seq *_gather_121_rule(Parser *p); -static asdl_seq *_loop0_124_rule(Parser *p); -static asdl_seq *_gather_123_rule(Parser *p); -static void *_tmp_125_rule(Parser *p); -static asdl_seq *_loop0_126_rule(Parser *p); +static asdl_seq *_loop1_120_rule(Parser *p); +static void *_tmp_121_rule(Parser *p); +static asdl_seq *_loop0_123_rule(Parser *p); +static asdl_seq *_gather_122_rule(Parser *p); +static asdl_seq *_loop0_125_rule(Parser *p); +static asdl_seq *_gather_124_rule(Parser *p); +static void *_tmp_126_rule(Parser *p); static asdl_seq *_loop0_127_rule(Parser *p); static asdl_seq *_loop0_128_rule(Parser *p); -static void *_tmp_129_rule(Parser *p); +static asdl_seq *_loop0_129_rule(Parser *p); static void *_tmp_130_rule(Parser *p); -static asdl_seq *_loop0_131_rule(Parser *p); -static void *_tmp_132_rule(Parser *p); -static asdl_seq *_loop0_133_rule(Parser *p); -static void *_tmp_134_rule(Parser *p); +static void *_tmp_131_rule(Parser *p); +static asdl_seq *_loop0_132_rule(Parser *p); +static void *_tmp_133_rule(Parser *p); +static asdl_seq *_loop0_134_rule(Parser *p); static void *_tmp_135_rule(Parser *p); static void *_tmp_136_rule(Parser *p); static void *_tmp_137_rule(Parser *p); @@ -702,10 +708,12 @@ static void *_tmp_147_rule(Parser *p); static void *_tmp_148_rule(Parser *p); static void *_tmp_149_rule(Parser *p); static void *_tmp_150_rule(Parser *p); -static asdl_seq *_loop1_151_rule(Parser *p); -static asdl_seq *_loop1_152_rule(Parser *p); -static void *_tmp_153_rule(Parser *p); -static void *_tmp_154_rule(Parser *p); +static void *_tmp_151_rule(Parser *p); +static void *_tmp_152_rule(Parser *p); +static asdl_seq *_loop1_153_rule(Parser *p); +static asdl_seq *_loop1_154_rule(Parser *p); +static void *_tmp_155_rule(Parser *p); +static void *_tmp_156_rule(Parser *p); // file: statements? $ @@ -12791,9 +12799,9 @@ star_targets_rule(Parser *p) return _res; } -// star_targets_seq: ','.star_target+ ','? +// star_targets_list_seq: ','.star_target+ ','? static asdl_seq* -star_targets_seq_rule(Parser *p) +star_targets_list_seq_rule(Parser *p) { D(p->level++); if (p->error_indicator) { @@ -12807,7 +12815,7 @@ star_targets_seq_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> star_targets_seq[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','.star_target+ ','?")); + D(fprintf(stderr, "%*c> star_targets_list_seq[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','.star_target+ ','?")); void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings asdl_seq * a; @@ -12817,7 +12825,7 @@ star_targets_seq_rule(Parser *p) (_opt_var = _PyPegen_expect_token(p, 12), 1) // ','? ) { - D(fprintf(stderr, "%*c+ star_targets_seq[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.star_target+ ','?")); + D(fprintf(stderr, "%*c+ star_targets_list_seq[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.star_target+ ','?")); _res = a; if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -12827,7 +12835,7 @@ star_targets_seq_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s star_targets_seq[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s star_targets_list_seq[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','.star_target+ ','?")); } _res = NULL; @@ -12836,11 +12844,82 @@ star_targets_seq_rule(Parser *p) return _res; } -// star_target: -// | '*' (!'*' star_target) -// | t_primary '.' NAME !t_lookahead -// | t_primary '[' slices ']' !t_lookahead -// | star_atom +// star_targets_tuple_seq: star_target ((',' star_target))+ ','? | star_target ',' +static asdl_seq* +star_targets_tuple_seq_rule(Parser *p) +{ + D(p->level++); + if (p->error_indicator) { + D(p->level--); + return NULL; + } + asdl_seq* _res = NULL; + int _mark = p->mark; + { // star_target ((',' star_target))+ ','? + if (p->error_indicator) { + D(p->level--); + return NULL; + } + D(fprintf(stderr, "%*c> star_targets_tuple_seq[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_target ((',' star_target))+ ','?")); + void *_opt_var; + UNUSED(_opt_var); // Silence compiler warnings + expr_ty a; + asdl_seq * b; + if ( + (a = star_target_rule(p)) // star_target + && + (b = _loop1_120_rule(p)) // ((',' star_target))+ + && + (_opt_var = _PyPegen_expect_token(p, 12), 1) // ','? + ) + { + D(fprintf(stderr, "%*c+ star_targets_tuple_seq[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_target ((',' star_target))+ ','?")); + _res = _PyPegen_seq_insert_in_front ( p , a , b ); + if (_res == NULL && PyErr_Occurred()) { + p->error_indicator = 1; + D(p->level--); + return NULL; + } + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s star_targets_tuple_seq[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_target ((',' star_target))+ ','?")); + } + { // star_target ',' + if (p->error_indicator) { + D(p->level--); + return NULL; + } + D(fprintf(stderr, "%*c> star_targets_tuple_seq[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_target ','")); + Token * _literal; + expr_ty a; + if ( + (a = star_target_rule(p)) // star_target + && + (_literal = _PyPegen_expect_token(p, 12)) // token=',' + ) + { + D(fprintf(stderr, "%*c+ star_targets_tuple_seq[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_target ','")); + _res = _PyPegen_singleton_seq ( p , a ); + if (_res == NULL && PyErr_Occurred()) { + p->error_indicator = 1; + D(p->level--); + return NULL; + } + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s star_targets_tuple_seq[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_target ','")); + } + _res = NULL; + done: + D(p->level--); + return _res; +} + +// star_target: '*' (!'*' star_target) | target_with_star_atom static expr_ty star_target_rule(Parser *p) { @@ -12875,7 +12954,7 @@ star_target_rule(Parser *p) if ( (_literal = _PyPegen_expect_token(p, 16)) // token='*' && - (a = _tmp_120_rule(p)) // !'*' star_target + (a = _tmp_121_rule(p)) // !'*' star_target ) { D(fprintf(stderr, "%*c+ star_target[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*' (!'*' star_target)")); @@ -12900,12 +12979,65 @@ star_target_rule(Parser *p) D(fprintf(stderr, "%*c%s star_target[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'*' (!'*' star_target)")); } + { // target_with_star_atom + if (p->error_indicator) { + D(p->level--); + return NULL; + } + D(fprintf(stderr, "%*c> star_target[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "target_with_star_atom")); + expr_ty target_with_star_atom_var; + if ( + (target_with_star_atom_var = target_with_star_atom_rule(p)) // target_with_star_atom + ) + { + D(fprintf(stderr, "%*c+ star_target[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "target_with_star_atom")); + _res = target_with_star_atom_var; + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s star_target[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "target_with_star_atom")); + } + _res = NULL; + done: + _PyPegen_insert_memo(p, _mark, star_target_type, _res); + D(p->level--); + return _res; +} + +// target_with_star_atom: +// | t_primary '.' NAME !t_lookahead +// | t_primary '[' slices ']' !t_lookahead +// | star_atom +static expr_ty +target_with_star_atom_rule(Parser *p) +{ + D(p->level++); + if (p->error_indicator) { + D(p->level--); + return NULL; + } + expr_ty _res = NULL; + if (_PyPegen_is_memoized(p, target_with_star_atom_type, &_res)) { + D(p->level--); + return _res; + } + int _mark = p->mark; + if (p->mark == p->fill && _PyPegen_fill_token(p) < 0) { + p->error_indicator = 1; + D(p->level--); + return NULL; + } + int _start_lineno = p->tokens[_mark]->lineno; + UNUSED(_start_lineno); // Only used by EXTRA macro + int _start_col_offset = p->tokens[_mark]->col_offset; + UNUSED(_start_col_offset); // Only used by EXTRA macro { // t_primary '.' NAME !t_lookahead if (p->error_indicator) { D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> star_target[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "t_primary '.' NAME !t_lookahead")); + D(fprintf(stderr, "%*c> target_with_star_atom[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "t_primary '.' NAME !t_lookahead")); Token * _literal; expr_ty a; expr_ty b; @@ -12919,7 +13051,7 @@ star_target_rule(Parser *p) _PyPegen_lookahead(0, t_lookahead_rule, p) ) { - D(fprintf(stderr, "%*c+ star_target[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "t_primary '.' NAME !t_lookahead")); + D(fprintf(stderr, "%*c+ target_with_star_atom[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "t_primary '.' NAME !t_lookahead")); Token *_token = _PyPegen_get_last_nonnwhitespace_token(p); if (_token == NULL) { D(p->level--); @@ -12938,7 +13070,7 @@ star_target_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s star_target[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s target_with_star_atom[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "t_primary '.' NAME !t_lookahead")); } { // t_primary '[' slices ']' !t_lookahead @@ -12946,7 +13078,7 @@ star_target_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> star_target[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "t_primary '[' slices ']' !t_lookahead")); + D(fprintf(stderr, "%*c> target_with_star_atom[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "t_primary '[' slices ']' !t_lookahead")); Token * _literal; Token * _literal_1; expr_ty a; @@ -12963,7 +13095,7 @@ star_target_rule(Parser *p) _PyPegen_lookahead(0, t_lookahead_rule, p) ) { - D(fprintf(stderr, "%*c+ star_target[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "t_primary '[' slices ']' !t_lookahead")); + D(fprintf(stderr, "%*c+ target_with_star_atom[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "t_primary '[' slices ']' !t_lookahead")); Token *_token = _PyPegen_get_last_nonnwhitespace_token(p); if (_token == NULL) { D(p->level--); @@ -12982,7 +13114,7 @@ star_target_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s star_target[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s target_with_star_atom[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "t_primary '[' slices ']' !t_lookahead")); } { // star_atom @@ -12990,32 +13122,32 @@ star_target_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> star_target[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_atom")); + D(fprintf(stderr, "%*c> target_with_star_atom[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_atom")); expr_ty star_atom_var; if ( (star_atom_var = star_atom_rule(p)) // star_atom ) { - D(fprintf(stderr, "%*c+ star_target[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_atom")); + D(fprintf(stderr, "%*c+ target_with_star_atom[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_atom")); _res = star_atom_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s star_target[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s target_with_star_atom[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_atom")); } _res = NULL; done: - _PyPegen_insert_memo(p, _mark, star_target_type, _res); + _PyPegen_insert_memo(p, _mark, target_with_star_atom_type, _res); D(p->level--); return _res; } // star_atom: // | NAME -// | '(' star_target ')' -// | '(' star_targets_seq? ')' -// | '[' star_targets_seq? ']' +// | '(' target_with_star_atom ')' +// | '(' star_targets_tuple_seq? ')' +// | '[' star_targets_list_seq? ']' static expr_ty star_atom_rule(Parser *p) { @@ -13059,24 +13191,24 @@ star_atom_rule(Parser *p) D(fprintf(stderr, "%*c%s star_atom[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "NAME")); } - { // '(' star_target ')' + { // '(' target_with_star_atom ')' if (p->error_indicator) { D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> star_atom[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'(' star_target ')'")); + D(fprintf(stderr, "%*c> star_atom[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'(' target_with_star_atom ')'")); Token * _literal; Token * _literal_1; expr_ty a; if ( (_literal = _PyPegen_expect_token(p, 7)) // token='(' && - (a = star_target_rule(p)) // star_target + (a = target_with_star_atom_rule(p)) // target_with_star_atom && (_literal_1 = _PyPegen_expect_token(p, 8)) // token=')' ) { - D(fprintf(stderr, "%*c+ star_atom[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'(' star_target ')'")); + D(fprintf(stderr, "%*c+ star_atom[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'(' target_with_star_atom ')'")); _res = _PyPegen_set_expr_context ( p , a , Store ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -13087,26 +13219,26 @@ star_atom_rule(Parser *p) } p->mark = _mark; D(fprintf(stderr, "%*c%s star_atom[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'(' star_target ')'")); + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'(' target_with_star_atom ')'")); } - { // '(' star_targets_seq? ')' + { // '(' star_targets_tuple_seq? ')' if (p->error_indicator) { D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> star_atom[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'(' star_targets_seq? ')'")); + D(fprintf(stderr, "%*c> star_atom[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'(' star_targets_tuple_seq? ')'")); Token * _literal; Token * _literal_1; void *a; if ( (_literal = _PyPegen_expect_token(p, 7)) // token='(' && - (a = star_targets_seq_rule(p), 1) // star_targets_seq? + (a = star_targets_tuple_seq_rule(p), 1) // star_targets_tuple_seq? && (_literal_1 = _PyPegen_expect_token(p, 8)) // token=')' ) { - D(fprintf(stderr, "%*c+ star_atom[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'(' star_targets_seq? ')'")); + D(fprintf(stderr, "%*c+ star_atom[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'(' star_targets_tuple_seq? ')'")); Token *_token = _PyPegen_get_last_nonnwhitespace_token(p); if (_token == NULL) { D(p->level--); @@ -13126,26 +13258,26 @@ star_atom_rule(Parser *p) } p->mark = _mark; D(fprintf(stderr, "%*c%s star_atom[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'(' star_targets_seq? ')'")); + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'(' star_targets_tuple_seq? ')'")); } - { // '[' star_targets_seq? ']' + { // '[' star_targets_list_seq? ']' if (p->error_indicator) { D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> star_atom[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'[' star_targets_seq? ']'")); + D(fprintf(stderr, "%*c> star_atom[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'[' star_targets_list_seq? ']'")); Token * _literal; Token * _literal_1; void *a; if ( (_literal = _PyPegen_expect_token(p, 9)) // token='[' && - (a = star_targets_seq_rule(p), 1) // star_targets_seq? + (a = star_targets_list_seq_rule(p), 1) // star_targets_list_seq? && (_literal_1 = _PyPegen_expect_token(p, 10)) // token=']' ) { - D(fprintf(stderr, "%*c+ star_atom[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'[' star_targets_seq? ']'")); + D(fprintf(stderr, "%*c+ star_atom[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'[' star_targets_list_seq? ']'")); Token *_token = _PyPegen_get_last_nonnwhitespace_token(p); if (_token == NULL) { D(p->level--); @@ -13165,7 +13297,7 @@ star_atom_rule(Parser *p) } p->mark = _mark; D(fprintf(stderr, "%*c%s star_atom[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'[' star_targets_seq? ']'")); + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'[' star_targets_list_seq? ']'")); } _res = NULL; done: @@ -13397,7 +13529,7 @@ del_targets_rule(Parser *p) UNUSED(_opt_var); // Silence compiler warnings asdl_seq * a; if ( - (a = _gather_121_rule(p)) // ','.del_target+ + (a = _gather_122_rule(p)) // ','.del_target+ && (_opt_var = _PyPegen_expect_token(p, 12), 1) // ','? ) @@ -13738,7 +13870,7 @@ targets_rule(Parser *p) UNUSED(_opt_var); // Silence compiler warnings asdl_seq * a; if ( - (a = _gather_123_rule(p)) // ','.target+ + (a = _gather_124_rule(p)) // ','.target+ && (_opt_var = _PyPegen_expect_token(p, 12), 1) // ','? ) @@ -14452,7 +14584,7 @@ invalid_arguments_rule(Parser *p) && (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (_opt_var = _tmp_125_rule(p), 1) // [args | expression for_if_clauses] + (_opt_var = _tmp_126_rule(p), 1) // [args | expression for_if_clauses] ) { D(fprintf(stderr, "%*c+ invalid_arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression for_if_clauses ',' [args | expression for_if_clauses]")); @@ -14710,7 +14842,7 @@ invalid_assignment_rule(Parser *p) D(fprintf(stderr, "%*c> invalid_assignment[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_named_expression ',' star_named_expressions* ':' expression")); Token * _literal; Token * _literal_1; - asdl_seq * _loop0_126_var; + asdl_seq * _loop0_127_var; expr_ty a; expr_ty expression_var; if ( @@ -14718,7 +14850,7 @@ invalid_assignment_rule(Parser *p) && (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (_loop0_126_var = _loop0_126_rule(p)) // star_named_expressions* + (_loop0_127_var = _loop0_127_rule(p)) // star_named_expressions* && (_literal_1 = _PyPegen_expect_token(p, 11)) // token=':' && @@ -14775,10 +14907,10 @@ invalid_assignment_rule(Parser *p) } D(fprintf(stderr, "%*c> invalid_assignment[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "((star_targets '='))* star_expressions '='")); Token * _literal; - asdl_seq * _loop0_127_var; + asdl_seq * _loop0_128_var; expr_ty a; if ( - (_loop0_127_var = _loop0_127_rule(p)) // ((star_targets '='))* + (_loop0_128_var = _loop0_128_rule(p)) // ((star_targets '='))* && (a = star_expressions_rule(p)) // star_expressions && @@ -14805,10 +14937,10 @@ invalid_assignment_rule(Parser *p) } D(fprintf(stderr, "%*c> invalid_assignment[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "((star_targets '='))* yield_expr '='")); Token * _literal; - asdl_seq * _loop0_128_var; + asdl_seq * _loop0_129_var; expr_ty a; if ( - (_loop0_128_var = _loop0_128_rule(p)) // ((star_targets '='))* + (_loop0_129_var = _loop0_129_rule(p)) // ((star_targets '='))* && (a = yield_expr_rule(p)) // yield_expr && @@ -14834,7 +14966,7 @@ invalid_assignment_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_assignment[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_expressions augassign (yield_expr | star_expressions)")); - void *_tmp_129_var; + void *_tmp_130_var; expr_ty a; AugOperator* augassign_var; if ( @@ -14842,7 +14974,7 @@ invalid_assignment_rule(Parser *p) && (augassign_var = augassign_rule(p)) // augassign && - (_tmp_129_var = _tmp_129_rule(p)) // yield_expr | star_expressions + (_tmp_130_var = _tmp_130_rule(p)) // yield_expr | star_expressions ) { D(fprintf(stderr, "%*c+ invalid_assignment[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_expressions augassign (yield_expr | star_expressions)")); @@ -15098,11 +15230,11 @@ invalid_comprehension_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_comprehension[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('[' | '(' | '{') starred_expression for_if_clauses")); - void *_tmp_130_var; + void *_tmp_131_var; expr_ty a; asdl_seq* for_if_clauses_var; if ( - (_tmp_130_var = _tmp_130_rule(p)) // '[' | '(' | '{' + (_tmp_131_var = _tmp_131_rule(p)) // '[' | '(' | '{' && (a = starred_expression_rule(p)) // starred_expression && @@ -15199,13 +15331,13 @@ invalid_parameters_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_parameters[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_no_default* (slash_with_default | param_with_default+) param_no_default")); - asdl_seq * _loop0_131_var; - void *_tmp_132_var; + asdl_seq * _loop0_132_var; + void *_tmp_133_var; arg_ty param_no_default_var; if ( - (_loop0_131_var = _loop0_131_rule(p)) // param_no_default* + (_loop0_132_var = _loop0_132_rule(p)) // param_no_default* && - (_tmp_132_var = _tmp_132_rule(p)) // slash_with_default | param_with_default+ + (_tmp_133_var = _tmp_133_rule(p)) // slash_with_default | param_with_default+ && (param_no_default_var = param_no_default_rule(p)) // param_no_default ) @@ -15247,13 +15379,13 @@ invalid_lambda_parameters_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_lambda_parameters[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default* (lambda_slash_with_default | lambda_param_with_default+) lambda_param_no_default")); - asdl_seq * _loop0_133_var; - void *_tmp_134_var; + asdl_seq * _loop0_134_var; + void *_tmp_135_var; arg_ty lambda_param_no_default_var; if ( - (_loop0_133_var = _loop0_133_rule(p)) // lambda_param_no_default* + (_loop0_134_var = _loop0_134_rule(p)) // lambda_param_no_default* && - (_tmp_134_var = _tmp_134_rule(p)) // lambda_slash_with_default | lambda_param_with_default+ + (_tmp_135_var = _tmp_135_rule(p)) // lambda_slash_with_default | lambda_param_with_default+ && (lambda_param_no_default_var = lambda_param_no_default_rule(p)) // lambda_param_no_default ) @@ -15295,11 +15427,11 @@ invalid_star_etc_rule(Parser *p) } D(fprintf(stderr, "%*c> invalid_star_etc[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'*' (')' | ',' (')' | '**'))")); Token * _literal; - void *_tmp_135_var; + void *_tmp_136_var; if ( (_literal = _PyPegen_expect_token(p, 16)) // token='*' && - (_tmp_135_var = _tmp_135_rule(p)) // ')' | ',' (')' | '**') + (_tmp_136_var = _tmp_136_rule(p)) // ')' | ',' (')' | '**') ) { D(fprintf(stderr, "%*c+ invalid_star_etc[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*' (')' | ',' (')' | '**'))")); @@ -15369,11 +15501,11 @@ invalid_lambda_star_etc_rule(Parser *p) } D(fprintf(stderr, "%*c> invalid_lambda_star_etc[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'*' (':' | ',' (':' | '**'))")); Token * _literal; - void *_tmp_136_var; + void *_tmp_137_var; if ( (_literal = _PyPegen_expect_token(p, 16)) // token='*' && - (_tmp_136_var = _tmp_136_rule(p)) // ':' | ',' (':' | '**') + (_tmp_137_var = _tmp_137_rule(p)) // ':' | ',' (':' | '**') ) { D(fprintf(stderr, "%*c+ invalid_lambda_star_etc[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*' (':' | ',' (':' | '**'))")); @@ -16882,12 +17014,12 @@ _loop1_22_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop1_22[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(star_targets '=')")); - void *_tmp_137_var; + void *_tmp_138_var; while ( - (_tmp_137_var = _tmp_137_rule(p)) // star_targets '=' + (_tmp_138_var = _tmp_138_rule(p)) // star_targets '=' ) { - _res = _tmp_137_var; + _res = _tmp_138_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -17390,12 +17522,12 @@ _loop0_31_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop0_31[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('.' | '...')")); - void *_tmp_138_var; + void *_tmp_139_var; while ( - (_tmp_138_var = _tmp_138_rule(p)) // '.' | '...' + (_tmp_139_var = _tmp_139_rule(p)) // '.' | '...' ) { - _res = _tmp_138_var; + _res = _tmp_139_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -17456,12 +17588,12 @@ _loop1_32_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop1_32[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('.' | '...')")); - void *_tmp_139_var; + void *_tmp_140_var; while ( - (_tmp_139_var = _tmp_139_rule(p)) // '.' | '...' + (_tmp_140_var = _tmp_140_rule(p)) // '.' | '...' ) { - _res = _tmp_139_var; + _res = _tmp_140_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -19618,12 +19750,12 @@ _loop1_68_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop1_68[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('@' named_expression NEWLINE)")); - void *_tmp_140_var; + void *_tmp_141_var; while ( - (_tmp_140_var = _tmp_140_rule(p)) // '@' named_expression NEWLINE + (_tmp_141_var = _tmp_141_rule(p)) // '@' named_expression NEWLINE ) { - _res = _tmp_140_var; + _res = _tmp_141_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -19736,12 +19868,12 @@ _loop1_70_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop1_70[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(',' star_expression)")); - void *_tmp_141_var; + void *_tmp_142_var; while ( - (_tmp_141_var = _tmp_141_rule(p)) // ',' star_expression + (_tmp_142_var = _tmp_142_rule(p)) // ',' star_expression ) { - _res = _tmp_141_var; + _res = _tmp_142_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -19921,12 +20053,12 @@ _loop1_73_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop1_73[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(',' expression)")); - void *_tmp_142_var; + void *_tmp_143_var; while ( - (_tmp_142_var = _tmp_142_rule(p)) // ',' expression + (_tmp_143_var = _tmp_143_rule(p)) // ',' expression ) { - _res = _tmp_142_var; + _res = _tmp_143_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -20951,12 +21083,12 @@ _loop1_88_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop1_88[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('or' conjunction)")); - void *_tmp_143_var; + void *_tmp_144_var; while ( - (_tmp_143_var = _tmp_143_rule(p)) // 'or' conjunction + (_tmp_144_var = _tmp_144_rule(p)) // 'or' conjunction ) { - _res = _tmp_143_var; + _res = _tmp_144_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -21022,12 +21154,12 @@ _loop1_89_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop1_89[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('and' inversion)")); - void *_tmp_144_var; + void *_tmp_145_var; while ( - (_tmp_144_var = _tmp_144_rule(p)) // 'and' inversion + (_tmp_145_var = _tmp_145_rule(p)) // 'and' inversion ) { - _res = _tmp_144_var; + _res = _tmp_145_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -21943,12 +22075,12 @@ _loop0_104_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop0_104[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('if' disjunction)")); - void *_tmp_145_var; + void *_tmp_146_var; while ( - (_tmp_145_var = _tmp_145_rule(p)) // 'if' disjunction + (_tmp_146_var = _tmp_146_rule(p)) // 'if' disjunction ) { - _res = _tmp_145_var; + _res = _tmp_146_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -22009,12 +22141,12 @@ _loop0_105_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop0_105[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('if' disjunction)")); - void *_tmp_146_var; + void *_tmp_147_var; while ( - (_tmp_146_var = _tmp_146_rule(p)) // 'if' disjunction + (_tmp_147_var = _tmp_147_rule(p)) // 'if' disjunction ) { - _res = _tmp_146_var; + _res = _tmp_147_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -22080,7 +22212,7 @@ _loop0_107_rule(Parser *p) while ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (elem = _tmp_147_rule(p)) // starred_expression | named_expression !'=' + (elem = _tmp_148_rule(p)) // starred_expression | named_expression !'=' ) { _res = elem; @@ -22143,7 +22275,7 @@ _gather_106_rule(Parser *p) void *elem; asdl_seq * seq; if ( - (elem = _tmp_147_rule(p)) // starred_expression | named_expression !'=' + (elem = _tmp_148_rule(p)) // starred_expression | named_expression !'=' && (seq = _loop0_107_rule(p)) // _loop0_107 ) @@ -22689,12 +22821,12 @@ _loop0_117_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop0_117[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(',' star_target)")); - void *_tmp_148_var; + void *_tmp_149_var; while ( - (_tmp_148_var = _tmp_148_rule(p)) // ',' star_target + (_tmp_149_var = _tmp_149_rule(p)) // ',' star_target ) { - _res = _tmp_148_var; + _res = _tmp_149_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -22842,9 +22974,80 @@ _gather_118_rule(Parser *p) return _res; } -// _tmp_120: !'*' star_target +// _loop1_120: (',' star_target) +static asdl_seq * +_loop1_120_rule(Parser *p) +{ + D(p->level++); + if (p->error_indicator) { + D(p->level--); + return NULL; + } + void *_res = NULL; + int _mark = p->mark; + int _start_mark = p->mark; + void **_children = PyMem_Malloc(sizeof(void *)); + if (!_children) { + p->error_indicator = 1; + PyErr_NoMemory(); + D(p->level--); + return NULL; + } + ssize_t _children_capacity = 1; + ssize_t _n = 0; + { // (',' star_target) + if (p->error_indicator) { + D(p->level--); + return NULL; + } + D(fprintf(stderr, "%*c> _loop1_120[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(',' star_target)")); + void *_tmp_150_var; + while ( + (_tmp_150_var = _tmp_150_rule(p)) // ',' star_target + ) + { + _res = _tmp_150_var; + if (_n == _children_capacity) { + _children_capacity *= 2; + void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); + if (!_new_children) { + p->error_indicator = 1; + PyErr_NoMemory(); + D(p->level--); + return NULL; + } + _children = _new_children; + } + _children[_n++] = _res; + _mark = p->mark; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s _loop1_120[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(',' star_target)")); + } + if (_n == 0 || p->error_indicator) { + PyMem_Free(_children); + D(p->level--); + return NULL; + } + asdl_seq *_seq = _Py_asdl_seq_new(_n, p->arena); + if (!_seq) { + PyMem_Free(_children); + p->error_indicator = 1; + PyErr_NoMemory(); + D(p->level--); + return NULL; + } + for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]); + PyMem_Free(_children); + _PyPegen_insert_memo(p, _start_mark, _loop1_120_type, _seq); + D(p->level--); + return _seq; +} + +// _tmp_121: !'*' star_target static void * -_tmp_120_rule(Parser *p) +_tmp_121_rule(Parser *p) { D(p->level++); if (p->error_indicator) { @@ -22858,7 +23061,7 @@ _tmp_120_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> _tmp_120[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "!'*' star_target")); + D(fprintf(stderr, "%*c> _tmp_121[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "!'*' star_target")); expr_ty star_target_var; if ( _PyPegen_lookahead_with_int(0, _PyPegen_expect_token, p, 16) // token='*' @@ -22866,12 +23069,12 @@ _tmp_120_rule(Parser *p) (star_target_var = star_target_rule(p)) // star_target ) { - D(fprintf(stderr, "%*c+ _tmp_120[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "!'*' star_target")); + D(fprintf(stderr, "%*c+ _tmp_121[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "!'*' star_target")); _res = star_target_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_120[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_121[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "!'*' star_target")); } _res = NULL; @@ -22880,9 +23083,9 @@ _tmp_120_rule(Parser *p) return _res; } -// _loop0_122: ',' del_target +// _loop0_123: ',' del_target static asdl_seq * -_loop0_122_rule(Parser *p) +_loop0_123_rule(Parser *p) { D(p->level++); if (p->error_indicator) { @@ -22906,7 +23109,7 @@ _loop0_122_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> _loop0_122[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' del_target")); + D(fprintf(stderr, "%*c> _loop0_123[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' del_target")); Token * _literal; expr_ty elem; while ( @@ -22937,7 +23140,7 @@ _loop0_122_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_122[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_123[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' del_target")); } asdl_seq *_seq = _Py_asdl_seq_new(_n, p->arena); @@ -22950,14 +23153,14 @@ _loop0_122_rule(Parser *p) } for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]); PyMem_Free(_children); - _PyPegen_insert_memo(p, _start_mark, _loop0_122_type, _seq); + _PyPegen_insert_memo(p, _start_mark, _loop0_123_type, _seq); D(p->level--); return _seq; } -// _gather_121: del_target _loop0_122 +// _gather_122: del_target _loop0_123 static asdl_seq * -_gather_121_rule(Parser *p) +_gather_122_rule(Parser *p) { D(p->level++); if (p->error_indicator) { @@ -22966,27 +23169,27 @@ _gather_121_rule(Parser *p) } asdl_seq * _res = NULL; int _mark = p->mark; - { // del_target _loop0_122 + { // del_target _loop0_123 if (p->error_indicator) { D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> _gather_121[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "del_target _loop0_122")); + D(fprintf(stderr, "%*c> _gather_122[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "del_target _loop0_123")); expr_ty elem; asdl_seq * seq; if ( (elem = del_target_rule(p)) // del_target && - (seq = _loop0_122_rule(p)) // _loop0_122 + (seq = _loop0_123_rule(p)) // _loop0_123 ) { - D(fprintf(stderr, "%*c+ _gather_121[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "del_target _loop0_122")); + D(fprintf(stderr, "%*c+ _gather_122[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "del_target _loop0_123")); _res = _PyPegen_seq_insert_in_front(p, elem, seq); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _gather_121[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "del_target _loop0_122")); + D(fprintf(stderr, "%*c%s _gather_122[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "del_target _loop0_123")); } _res = NULL; done: @@ -22994,9 +23197,9 @@ _gather_121_rule(Parser *p) return _res; } -// _loop0_124: ',' target +// _loop0_125: ',' target static asdl_seq * -_loop0_124_rule(Parser *p) +_loop0_125_rule(Parser *p) { D(p->level++); if (p->error_indicator) { @@ -23020,7 +23223,7 @@ _loop0_124_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> _loop0_124[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' target")); + D(fprintf(stderr, "%*c> _loop0_125[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' target")); Token * _literal; expr_ty elem; while ( @@ -23051,7 +23254,7 @@ _loop0_124_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_124[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_125[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' target")); } asdl_seq *_seq = _Py_asdl_seq_new(_n, p->arena); @@ -23064,14 +23267,14 @@ _loop0_124_rule(Parser *p) } for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]); PyMem_Free(_children); - _PyPegen_insert_memo(p, _start_mark, _loop0_124_type, _seq); + _PyPegen_insert_memo(p, _start_mark, _loop0_125_type, _seq); D(p->level--); return _seq; } -// _gather_123: target _loop0_124 +// _gather_124: target _loop0_125 static asdl_seq * -_gather_123_rule(Parser *p) +_gather_124_rule(Parser *p) { D(p->level++); if (p->error_indicator) { @@ -23080,27 +23283,27 @@ _gather_123_rule(Parser *p) } asdl_seq * _res = NULL; int _mark = p->mark; - { // target _loop0_124 + { // target _loop0_125 if (p->error_indicator) { D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> _gather_123[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "target _loop0_124")); + D(fprintf(stderr, "%*c> _gather_124[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "target _loop0_125")); expr_ty elem; asdl_seq * seq; if ( (elem = target_rule(p)) // target && - (seq = _loop0_124_rule(p)) // _loop0_124 + (seq = _loop0_125_rule(p)) // _loop0_125 ) { - D(fprintf(stderr, "%*c+ _gather_123[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "target _loop0_124")); + D(fprintf(stderr, "%*c+ _gather_124[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "target _loop0_125")); _res = _PyPegen_seq_insert_in_front(p, elem, seq); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _gather_123[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "target _loop0_124")); + D(fprintf(stderr, "%*c%s _gather_124[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "target _loop0_125")); } _res = NULL; done: @@ -23108,9 +23311,9 @@ _gather_123_rule(Parser *p) return _res; } -// _tmp_125: args | expression for_if_clauses +// _tmp_126: args | expression for_if_clauses static void * -_tmp_125_rule(Parser *p) +_tmp_126_rule(Parser *p) { D(p->level++); if (p->error_indicator) { @@ -23124,18 +23327,18 @@ _tmp_125_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> _tmp_125[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "args")); + D(fprintf(stderr, "%*c> _tmp_126[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "args")); expr_ty args_var; if ( (args_var = args_rule(p)) // args ) { - D(fprintf(stderr, "%*c+ _tmp_125[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "args")); + D(fprintf(stderr, "%*c+ _tmp_126[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "args")); _res = args_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_125[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_126[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "args")); } { // expression for_if_clauses @@ -23143,7 +23346,7 @@ _tmp_125_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> _tmp_125[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression for_if_clauses")); + D(fprintf(stderr, "%*c> _tmp_126[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression for_if_clauses")); expr_ty expression_var; asdl_seq* for_if_clauses_var; if ( @@ -23152,12 +23355,12 @@ _tmp_125_rule(Parser *p) (for_if_clauses_var = for_if_clauses_rule(p)) // for_if_clauses ) { - D(fprintf(stderr, "%*c+ _tmp_125[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression for_if_clauses")); + D(fprintf(stderr, "%*c+ _tmp_126[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression for_if_clauses")); _res = _PyPegen_dummy_name(p, expression_var, for_if_clauses_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_125[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_126[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression for_if_clauses")); } _res = NULL; @@ -23166,9 +23369,9 @@ _tmp_125_rule(Parser *p) return _res; } -// _loop0_126: star_named_expressions +// _loop0_127: star_named_expressions static asdl_seq * -_loop0_126_rule(Parser *p) +_loop0_127_rule(Parser *p) { D(p->level++); if (p->error_indicator) { @@ -23192,7 +23395,7 @@ _loop0_126_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> _loop0_126[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_named_expressions")); + D(fprintf(stderr, "%*c> _loop0_127[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_named_expressions")); asdl_seq* star_named_expressions_var; while ( (star_named_expressions_var = star_named_expressions_rule(p)) // star_named_expressions @@ -23214,7 +23417,7 @@ _loop0_126_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_126[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_127[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_named_expressions")); } asdl_seq *_seq = _Py_asdl_seq_new(_n, p->arena); @@ -23227,14 +23430,14 @@ _loop0_126_rule(Parser *p) } for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]); PyMem_Free(_children); - _PyPegen_insert_memo(p, _start_mark, _loop0_126_type, _seq); + _PyPegen_insert_memo(p, _start_mark, _loop0_127_type, _seq); D(p->level--); return _seq; } -// _loop0_127: (star_targets '=') +// _loop0_128: (star_targets '=') static asdl_seq * -_loop0_127_rule(Parser *p) +_loop0_128_rule(Parser *p) { D(p->level++); if (p->error_indicator) { @@ -23258,13 +23461,13 @@ _loop0_127_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> _loop0_127[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(star_targets '=')")); - void *_tmp_149_var; + D(fprintf(stderr, "%*c> _loop0_128[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(star_targets '=')")); + void *_tmp_151_var; while ( - (_tmp_149_var = _tmp_149_rule(p)) // star_targets '=' + (_tmp_151_var = _tmp_151_rule(p)) // star_targets '=' ) { - _res = _tmp_149_var; + _res = _tmp_151_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -23280,7 +23483,7 @@ _loop0_127_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_127[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_128[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(star_targets '=')")); } asdl_seq *_seq = _Py_asdl_seq_new(_n, p->arena); @@ -23293,14 +23496,14 @@ _loop0_127_rule(Parser *p) } for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]); PyMem_Free(_children); - _PyPegen_insert_memo(p, _start_mark, _loop0_127_type, _seq); + _PyPegen_insert_memo(p, _start_mark, _loop0_128_type, _seq); D(p->level--); return _seq; } -// _loop0_128: (star_targets '=') +// _loop0_129: (star_targets '=') static asdl_seq * -_loop0_128_rule(Parser *p) +_loop0_129_rule(Parser *p) { D(p->level++); if (p->error_indicator) { @@ -23324,13 +23527,13 @@ _loop0_128_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> _loop0_128[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(star_targets '=')")); - void *_tmp_150_var; + D(fprintf(stderr, "%*c> _loop0_129[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(star_targets '=')")); + void *_tmp_152_var; while ( - (_tmp_150_var = _tmp_150_rule(p)) // star_targets '=' + (_tmp_152_var = _tmp_152_rule(p)) // star_targets '=' ) { - _res = _tmp_150_var; + _res = _tmp_152_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -23346,7 +23549,7 @@ _loop0_128_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_128[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_129[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(star_targets '=')")); } asdl_seq *_seq = _Py_asdl_seq_new(_n, p->arena); @@ -23359,14 +23562,14 @@ _loop0_128_rule(Parser *p) } for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]); PyMem_Free(_children); - _PyPegen_insert_memo(p, _start_mark, _loop0_128_type, _seq); + _PyPegen_insert_memo(p, _start_mark, _loop0_129_type, _seq); D(p->level--); return _seq; } -// _tmp_129: yield_expr | star_expressions +// _tmp_130: yield_expr | star_expressions static void * -_tmp_129_rule(Parser *p) +_tmp_130_rule(Parser *p) { D(p->level++); if (p->error_indicator) { @@ -23380,18 +23583,18 @@ _tmp_129_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> _tmp_129[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "yield_expr")); + D(fprintf(stderr, "%*c> _tmp_130[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "yield_expr")); expr_ty yield_expr_var; if ( (yield_expr_var = yield_expr_rule(p)) // yield_expr ) { - D(fprintf(stderr, "%*c+ _tmp_129[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "yield_expr")); + D(fprintf(stderr, "%*c+ _tmp_130[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "yield_expr")); _res = yield_expr_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_129[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_130[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "yield_expr")); } { // star_expressions @@ -23399,18 +23602,18 @@ _tmp_129_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> _tmp_129[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_expressions")); + D(fprintf(stderr, "%*c> _tmp_130[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_expressions")); expr_ty star_expressions_var; if ( (star_expressions_var = star_expressions_rule(p)) // star_expressions ) { - D(fprintf(stderr, "%*c+ _tmp_129[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_expressions")); + D(fprintf(stderr, "%*c+ _tmp_130[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_expressions")); _res = star_expressions_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_129[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_130[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_expressions")); } _res = NULL; @@ -23419,9 +23622,9 @@ _tmp_129_rule(Parser *p) return _res; } -// _tmp_130: '[' | '(' | '{' +// _tmp_131: '[' | '(' | '{' static void * -_tmp_130_rule(Parser *p) +_tmp_131_rule(Parser *p) { D(p->level++); if (p->error_indicator) { @@ -23435,18 +23638,18 @@ _tmp_130_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> _tmp_130[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'['")); + D(fprintf(stderr, "%*c> _tmp_131[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'['")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 9)) // token='[' ) { - D(fprintf(stderr, "%*c+ _tmp_130[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'['")); + D(fprintf(stderr, "%*c+ _tmp_131[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'['")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_130[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_131[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'['")); } { // '(' @@ -23454,18 +23657,18 @@ _tmp_130_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> _tmp_130[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'('")); + D(fprintf(stderr, "%*c> _tmp_131[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'('")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 7)) // token='(' ) { - D(fprintf(stderr, "%*c+ _tmp_130[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'('")); + D(fprintf(stderr, "%*c+ _tmp_131[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'('")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_130[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_131[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'('")); } { // '{' @@ -23473,18 +23676,18 @@ _tmp_130_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> _tmp_130[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{'")); + D(fprintf(stderr, "%*c> _tmp_131[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 25)) // token='{' ) { - D(fprintf(stderr, "%*c+ _tmp_130[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{'")); + D(fprintf(stderr, "%*c+ _tmp_131[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_130[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_131[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{'")); } _res = NULL; @@ -23493,9 +23696,9 @@ _tmp_130_rule(Parser *p) return _res; } -// _loop0_131: param_no_default +// _loop0_132: param_no_default static asdl_seq * -_loop0_131_rule(Parser *p) +_loop0_132_rule(Parser *p) { D(p->level++); if (p->error_indicator) { @@ -23519,7 +23722,7 @@ _loop0_131_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> _loop0_131[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_no_default")); + D(fprintf(stderr, "%*c> _loop0_132[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_no_default")); arg_ty param_no_default_var; while ( (param_no_default_var = param_no_default_rule(p)) // param_no_default @@ -23541,7 +23744,7 @@ _loop0_131_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_131[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_132[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "param_no_default")); } asdl_seq *_seq = _Py_asdl_seq_new(_n, p->arena); @@ -23554,14 +23757,14 @@ _loop0_131_rule(Parser *p) } for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]); PyMem_Free(_children); - _PyPegen_insert_memo(p, _start_mark, _loop0_131_type, _seq); + _PyPegen_insert_memo(p, _start_mark, _loop0_132_type, _seq); D(p->level--); return _seq; } -// _tmp_132: slash_with_default | param_with_default+ +// _tmp_133: slash_with_default | param_with_default+ static void * -_tmp_132_rule(Parser *p) +_tmp_133_rule(Parser *p) { D(p->level++); if (p->error_indicator) { @@ -23575,18 +23778,18 @@ _tmp_132_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> _tmp_132[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "slash_with_default")); + D(fprintf(stderr, "%*c> _tmp_133[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "slash_with_default")); SlashWithDefault* slash_with_default_var; if ( (slash_with_default_var = slash_with_default_rule(p)) // slash_with_default ) { - D(fprintf(stderr, "%*c+ _tmp_132[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "slash_with_default")); + D(fprintf(stderr, "%*c+ _tmp_133[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "slash_with_default")); _res = slash_with_default_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_132[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_133[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "slash_with_default")); } { // param_with_default+ @@ -23594,18 +23797,18 @@ _tmp_132_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> _tmp_132[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_with_default+")); - asdl_seq * _loop1_151_var; + D(fprintf(stderr, "%*c> _tmp_133[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_with_default+")); + asdl_seq * _loop1_153_var; if ( - (_loop1_151_var = _loop1_151_rule(p)) // param_with_default+ + (_loop1_153_var = _loop1_153_rule(p)) // param_with_default+ ) { - D(fprintf(stderr, "%*c+ _tmp_132[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "param_with_default+")); - _res = _loop1_151_var; + D(fprintf(stderr, "%*c+ _tmp_133[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "param_with_default+")); + _res = _loop1_153_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_132[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_133[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "param_with_default+")); } _res = NULL; @@ -23614,9 +23817,9 @@ _tmp_132_rule(Parser *p) return _res; } -// _loop0_133: lambda_param_no_default +// _loop0_134: lambda_param_no_default static asdl_seq * -_loop0_133_rule(Parser *p) +_loop0_134_rule(Parser *p) { D(p->level++); if (p->error_indicator) { @@ -23640,7 +23843,7 @@ _loop0_133_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> _loop0_133[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default")); + D(fprintf(stderr, "%*c> _loop0_134[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default")); arg_ty lambda_param_no_default_var; while ( (lambda_param_no_default_var = lambda_param_no_default_rule(p)) // lambda_param_no_default @@ -23662,7 +23865,7 @@ _loop0_133_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_133[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_134[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_no_default")); } asdl_seq *_seq = _Py_asdl_seq_new(_n, p->arena); @@ -23675,14 +23878,14 @@ _loop0_133_rule(Parser *p) } for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]); PyMem_Free(_children); - _PyPegen_insert_memo(p, _start_mark, _loop0_133_type, _seq); + _PyPegen_insert_memo(p, _start_mark, _loop0_134_type, _seq); D(p->level--); return _seq; } -// _tmp_134: lambda_slash_with_default | lambda_param_with_default+ +// _tmp_135: lambda_slash_with_default | lambda_param_with_default+ static void * -_tmp_134_rule(Parser *p) +_tmp_135_rule(Parser *p) { D(p->level++); if (p->error_indicator) { @@ -23696,18 +23899,18 @@ _tmp_134_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> _tmp_134[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_slash_with_default")); + D(fprintf(stderr, "%*c> _tmp_135[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_slash_with_default")); SlashWithDefault* lambda_slash_with_default_var; if ( (lambda_slash_with_default_var = lambda_slash_with_default_rule(p)) // lambda_slash_with_default ) { - D(fprintf(stderr, "%*c+ _tmp_134[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_slash_with_default")); + D(fprintf(stderr, "%*c+ _tmp_135[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_slash_with_default")); _res = lambda_slash_with_default_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_134[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_135[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_slash_with_default")); } { // lambda_param_with_default+ @@ -23715,18 +23918,18 @@ _tmp_134_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> _tmp_134[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_with_default+")); - asdl_seq * _loop1_152_var; + D(fprintf(stderr, "%*c> _tmp_135[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_with_default+")); + asdl_seq * _loop1_154_var; if ( - (_loop1_152_var = _loop1_152_rule(p)) // lambda_param_with_default+ + (_loop1_154_var = _loop1_154_rule(p)) // lambda_param_with_default+ ) { - D(fprintf(stderr, "%*c+ _tmp_134[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_param_with_default+")); - _res = _loop1_152_var; + D(fprintf(stderr, "%*c+ _tmp_135[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_param_with_default+")); + _res = _loop1_154_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_134[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_135[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_with_default+")); } _res = NULL; @@ -23735,9 +23938,9 @@ _tmp_134_rule(Parser *p) return _res; } -// _tmp_135: ')' | ',' (')' | '**') +// _tmp_136: ')' | ',' (')' | '**') static void * -_tmp_135_rule(Parser *p) +_tmp_136_rule(Parser *p) { D(p->level++); if (p->error_indicator) { @@ -23751,18 +23954,18 @@ _tmp_135_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> _tmp_135[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "')'")); + D(fprintf(stderr, "%*c> _tmp_136[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "')'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 8)) // token=')' ) { - D(fprintf(stderr, "%*c+ _tmp_135[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "')'")); + D(fprintf(stderr, "%*c+ _tmp_136[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "')'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_135[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_136[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "')'")); } { // ',' (')' | '**') @@ -23770,21 +23973,21 @@ _tmp_135_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> _tmp_135[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (')' | '**')")); + D(fprintf(stderr, "%*c> _tmp_136[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (')' | '**')")); Token * _literal; - void *_tmp_153_var; + void *_tmp_155_var; if ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (_tmp_153_var = _tmp_153_rule(p)) // ')' | '**' + (_tmp_155_var = _tmp_155_rule(p)) // ')' | '**' ) { - D(fprintf(stderr, "%*c+ _tmp_135[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' (')' | '**')")); - _res = _PyPegen_dummy_name(p, _literal, _tmp_153_var); + D(fprintf(stderr, "%*c+ _tmp_136[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' (')' | '**')")); + _res = _PyPegen_dummy_name(p, _literal, _tmp_155_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_135[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_136[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' (')' | '**')")); } _res = NULL; @@ -23793,9 +23996,9 @@ _tmp_135_rule(Parser *p) return _res; } -// _tmp_136: ':' | ',' (':' | '**') +// _tmp_137: ':' | ',' (':' | '**') static void * -_tmp_136_rule(Parser *p) +_tmp_137_rule(Parser *p) { D(p->level++); if (p->error_indicator) { @@ -23809,18 +24012,18 @@ _tmp_136_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> _tmp_136[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); + D(fprintf(stderr, "%*c> _tmp_137[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 11)) // token=':' ) { - D(fprintf(stderr, "%*c+ _tmp_136[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); + D(fprintf(stderr, "%*c+ _tmp_137[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_136[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_137[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "':'")); } { // ',' (':' | '**') @@ -23828,21 +24031,21 @@ _tmp_136_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> _tmp_136[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (':' | '**')")); + D(fprintf(stderr, "%*c> _tmp_137[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (':' | '**')")); Token * _literal; - void *_tmp_154_var; + void *_tmp_156_var; if ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (_tmp_154_var = _tmp_154_rule(p)) // ':' | '**' + (_tmp_156_var = _tmp_156_rule(p)) // ':' | '**' ) { - D(fprintf(stderr, "%*c+ _tmp_136[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' (':' | '**')")); - _res = _PyPegen_dummy_name(p, _literal, _tmp_154_var); + D(fprintf(stderr, "%*c+ _tmp_137[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' (':' | '**')")); + _res = _PyPegen_dummy_name(p, _literal, _tmp_156_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_136[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_137[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' (':' | '**')")); } _res = NULL; @@ -23851,9 +24054,9 @@ _tmp_136_rule(Parser *p) return _res; } -// _tmp_137: star_targets '=' +// _tmp_138: star_targets '=' static void * -_tmp_137_rule(Parser *p) +_tmp_138_rule(Parser *p) { D(p->level++); if (p->error_indicator) { @@ -23867,7 +24070,7 @@ _tmp_137_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> _tmp_137[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_targets '='")); + D(fprintf(stderr, "%*c> _tmp_138[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_targets '='")); Token * _literal; expr_ty z; if ( @@ -23876,7 +24079,7 @@ _tmp_137_rule(Parser *p) (_literal = _PyPegen_expect_token(p, 22)) // token='=' ) { - D(fprintf(stderr, "%*c+ _tmp_137[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_targets '='")); + D(fprintf(stderr, "%*c+ _tmp_138[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_targets '='")); _res = z; if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -23886,7 +24089,7 @@ _tmp_137_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_137[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_138[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_targets '='")); } _res = NULL; @@ -23895,9 +24098,9 @@ _tmp_137_rule(Parser *p) return _res; } -// _tmp_138: '.' | '...' +// _tmp_139: '.' | '...' static void * -_tmp_138_rule(Parser *p) +_tmp_139_rule(Parser *p) { D(p->level++); if (p->error_indicator) { @@ -23911,18 +24114,18 @@ _tmp_138_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> _tmp_138[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'.'")); + D(fprintf(stderr, "%*c> _tmp_139[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'.'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 23)) // token='.' ) { - D(fprintf(stderr, "%*c+ _tmp_138[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'.'")); + D(fprintf(stderr, "%*c+ _tmp_139[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'.'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_138[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_139[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'.'")); } { // '...' @@ -23930,18 +24133,18 @@ _tmp_138_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> _tmp_138[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'...'")); + D(fprintf(stderr, "%*c> _tmp_139[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'...'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 52)) // token='...' ) { - D(fprintf(stderr, "%*c+ _tmp_138[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'...'")); + D(fprintf(stderr, "%*c+ _tmp_139[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'...'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_138[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_139[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'...'")); } _res = NULL; @@ -23950,9 +24153,9 @@ _tmp_138_rule(Parser *p) return _res; } -// _tmp_139: '.' | '...' +// _tmp_140: '.' | '...' static void * -_tmp_139_rule(Parser *p) +_tmp_140_rule(Parser *p) { D(p->level++); if (p->error_indicator) { @@ -23966,18 +24169,18 @@ _tmp_139_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> _tmp_139[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'.'")); + D(fprintf(stderr, "%*c> _tmp_140[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'.'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 23)) // token='.' ) { - D(fprintf(stderr, "%*c+ _tmp_139[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'.'")); + D(fprintf(stderr, "%*c+ _tmp_140[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'.'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_139[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_140[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'.'")); } { // '...' @@ -23985,18 +24188,18 @@ _tmp_139_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> _tmp_139[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'...'")); + D(fprintf(stderr, "%*c> _tmp_140[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'...'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 52)) // token='...' ) { - D(fprintf(stderr, "%*c+ _tmp_139[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'...'")); + D(fprintf(stderr, "%*c+ _tmp_140[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'...'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_139[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_140[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'...'")); } _res = NULL; @@ -24005,9 +24208,9 @@ _tmp_139_rule(Parser *p) return _res; } -// _tmp_140: '@' named_expression NEWLINE +// _tmp_141: '@' named_expression NEWLINE static void * -_tmp_140_rule(Parser *p) +_tmp_141_rule(Parser *p) { D(p->level++); if (p->error_indicator) { @@ -24021,7 +24224,7 @@ _tmp_140_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> _tmp_140[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'@' named_expression NEWLINE")); + D(fprintf(stderr, "%*c> _tmp_141[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'@' named_expression NEWLINE")); Token * _literal; expr_ty f; Token * newline_var; @@ -24033,7 +24236,7 @@ _tmp_140_rule(Parser *p) (newline_var = _PyPegen_expect_token(p, NEWLINE)) // token='NEWLINE' ) { - D(fprintf(stderr, "%*c+ _tmp_140[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'@' named_expression NEWLINE")); + D(fprintf(stderr, "%*c+ _tmp_141[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'@' named_expression NEWLINE")); _res = f; if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -24043,7 +24246,7 @@ _tmp_140_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_140[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_141[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'@' named_expression NEWLINE")); } _res = NULL; @@ -24052,9 +24255,9 @@ _tmp_140_rule(Parser *p) return _res; } -// _tmp_141: ',' star_expression +// _tmp_142: ',' star_expression static void * -_tmp_141_rule(Parser *p) +_tmp_142_rule(Parser *p) { D(p->level++); if (p->error_indicator) { @@ -24068,7 +24271,7 @@ _tmp_141_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> _tmp_141[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' star_expression")); + D(fprintf(stderr, "%*c> _tmp_142[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' star_expression")); Token * _literal; expr_ty c; if ( @@ -24077,7 +24280,7 @@ _tmp_141_rule(Parser *p) (c = star_expression_rule(p)) // star_expression ) { - D(fprintf(stderr, "%*c+ _tmp_141[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' star_expression")); + D(fprintf(stderr, "%*c+ _tmp_142[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' star_expression")); _res = c; if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -24087,7 +24290,7 @@ _tmp_141_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_141[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_142[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' star_expression")); } _res = NULL; @@ -24096,9 +24299,9 @@ _tmp_141_rule(Parser *p) return _res; } -// _tmp_142: ',' expression +// _tmp_143: ',' expression static void * -_tmp_142_rule(Parser *p) +_tmp_143_rule(Parser *p) { D(p->level++); if (p->error_indicator) { @@ -24112,7 +24315,7 @@ _tmp_142_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> _tmp_142[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' expression")); + D(fprintf(stderr, "%*c> _tmp_143[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' expression")); Token * _literal; expr_ty c; if ( @@ -24121,7 +24324,7 @@ _tmp_142_rule(Parser *p) (c = expression_rule(p)) // expression ) { - D(fprintf(stderr, "%*c+ _tmp_142[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' expression")); + D(fprintf(stderr, "%*c+ _tmp_143[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' expression")); _res = c; if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -24131,7 +24334,7 @@ _tmp_142_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_142[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_143[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' expression")); } _res = NULL; @@ -24140,9 +24343,9 @@ _tmp_142_rule(Parser *p) return _res; } -// _tmp_143: 'or' conjunction +// _tmp_144: 'or' conjunction static void * -_tmp_143_rule(Parser *p) +_tmp_144_rule(Parser *p) { D(p->level++); if (p->error_indicator) { @@ -24156,7 +24359,7 @@ _tmp_143_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> _tmp_143[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'or' conjunction")); + D(fprintf(stderr, "%*c> _tmp_144[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'or' conjunction")); Token * _keyword; expr_ty c; if ( @@ -24165,7 +24368,7 @@ _tmp_143_rule(Parser *p) (c = conjunction_rule(p)) // conjunction ) { - D(fprintf(stderr, "%*c+ _tmp_143[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'or' conjunction")); + D(fprintf(stderr, "%*c+ _tmp_144[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'or' conjunction")); _res = c; if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -24175,7 +24378,7 @@ _tmp_143_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_143[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_144[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'or' conjunction")); } _res = NULL; @@ -24184,9 +24387,9 @@ _tmp_143_rule(Parser *p) return _res; } -// _tmp_144: 'and' inversion +// _tmp_145: 'and' inversion static void * -_tmp_144_rule(Parser *p) +_tmp_145_rule(Parser *p) { D(p->level++); if (p->error_indicator) { @@ -24200,7 +24403,7 @@ _tmp_144_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> _tmp_144[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'and' inversion")); + D(fprintf(stderr, "%*c> _tmp_145[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'and' inversion")); Token * _keyword; expr_ty c; if ( @@ -24209,7 +24412,7 @@ _tmp_144_rule(Parser *p) (c = inversion_rule(p)) // inversion ) { - D(fprintf(stderr, "%*c+ _tmp_144[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'and' inversion")); + D(fprintf(stderr, "%*c+ _tmp_145[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'and' inversion")); _res = c; if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -24219,7 +24422,7 @@ _tmp_144_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_144[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_145[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'and' inversion")); } _res = NULL; @@ -24228,9 +24431,9 @@ _tmp_144_rule(Parser *p) return _res; } -// _tmp_145: 'if' disjunction +// _tmp_146: 'if' disjunction static void * -_tmp_145_rule(Parser *p) +_tmp_146_rule(Parser *p) { D(p->level++); if (p->error_indicator) { @@ -24244,7 +24447,7 @@ _tmp_145_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> _tmp_145[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'if' disjunction")); + D(fprintf(stderr, "%*c> _tmp_146[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'if' disjunction")); Token * _keyword; expr_ty z; if ( @@ -24253,7 +24456,7 @@ _tmp_145_rule(Parser *p) (z = disjunction_rule(p)) // disjunction ) { - D(fprintf(stderr, "%*c+ _tmp_145[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'if' disjunction")); + D(fprintf(stderr, "%*c+ _tmp_146[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'if' disjunction")); _res = z; if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -24263,7 +24466,7 @@ _tmp_145_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_145[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_146[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'if' disjunction")); } _res = NULL; @@ -24272,9 +24475,9 @@ _tmp_145_rule(Parser *p) return _res; } -// _tmp_146: 'if' disjunction +// _tmp_147: 'if' disjunction static void * -_tmp_146_rule(Parser *p) +_tmp_147_rule(Parser *p) { D(p->level++); if (p->error_indicator) { @@ -24288,7 +24491,7 @@ _tmp_146_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> _tmp_146[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'if' disjunction")); + D(fprintf(stderr, "%*c> _tmp_147[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'if' disjunction")); Token * _keyword; expr_ty z; if ( @@ -24297,7 +24500,7 @@ _tmp_146_rule(Parser *p) (z = disjunction_rule(p)) // disjunction ) { - D(fprintf(stderr, "%*c+ _tmp_146[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'if' disjunction")); + D(fprintf(stderr, "%*c+ _tmp_147[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'if' disjunction")); _res = z; if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -24307,7 +24510,7 @@ _tmp_146_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_146[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_147[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'if' disjunction")); } _res = NULL; @@ -24316,9 +24519,9 @@ _tmp_146_rule(Parser *p) return _res; } -// _tmp_147: starred_expression | named_expression !'=' +// _tmp_148: starred_expression | named_expression !'=' static void * -_tmp_147_rule(Parser *p) +_tmp_148_rule(Parser *p) { D(p->level++); if (p->error_indicator) { @@ -24332,18 +24535,18 @@ _tmp_147_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> _tmp_147[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "starred_expression")); + D(fprintf(stderr, "%*c> _tmp_148[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "starred_expression")); expr_ty starred_expression_var; if ( (starred_expression_var = starred_expression_rule(p)) // starred_expression ) { - D(fprintf(stderr, "%*c+ _tmp_147[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "starred_expression")); + D(fprintf(stderr, "%*c+ _tmp_148[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "starred_expression")); _res = starred_expression_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_147[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_148[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "starred_expression")); } { // named_expression !'=' @@ -24351,7 +24554,7 @@ _tmp_147_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> _tmp_147[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "named_expression !'='")); + D(fprintf(stderr, "%*c> _tmp_148[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "named_expression !'='")); expr_ty named_expression_var; if ( (named_expression_var = named_expression_rule(p)) // named_expression @@ -24359,12 +24562,12 @@ _tmp_147_rule(Parser *p) _PyPegen_lookahead_with_int(0, _PyPegen_expect_token, p, 22) // token='=' ) { - D(fprintf(stderr, "%*c+ _tmp_147[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "named_expression !'='")); + D(fprintf(stderr, "%*c+ _tmp_148[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "named_expression !'='")); _res = named_expression_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_147[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_148[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "named_expression !'='")); } _res = NULL; @@ -24373,9 +24576,9 @@ _tmp_147_rule(Parser *p) return _res; } -// _tmp_148: ',' star_target +// _tmp_149: ',' star_target static void * -_tmp_148_rule(Parser *p) +_tmp_149_rule(Parser *p) { D(p->level++); if (p->error_indicator) { @@ -24389,7 +24592,7 @@ _tmp_148_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> _tmp_148[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' star_target")); + D(fprintf(stderr, "%*c> _tmp_149[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' star_target")); Token * _literal; expr_ty c; if ( @@ -24398,7 +24601,7 @@ _tmp_148_rule(Parser *p) (c = star_target_rule(p)) // star_target ) { - D(fprintf(stderr, "%*c+ _tmp_148[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' star_target")); + D(fprintf(stderr, "%*c+ _tmp_149[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' star_target")); _res = c; if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -24408,7 +24611,7 @@ _tmp_148_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_148[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_149[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' star_target")); } _res = NULL; @@ -24417,9 +24620,53 @@ _tmp_148_rule(Parser *p) return _res; } -// _tmp_149: star_targets '=' +// _tmp_150: ',' star_target static void * -_tmp_149_rule(Parser *p) +_tmp_150_rule(Parser *p) +{ + D(p->level++); + if (p->error_indicator) { + D(p->level--); + return NULL; + } + void * _res = NULL; + int _mark = p->mark; + { // ',' star_target + if (p->error_indicator) { + D(p->level--); + return NULL; + } + D(fprintf(stderr, "%*c> _tmp_150[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' star_target")); + Token * _literal; + expr_ty c; + if ( + (_literal = _PyPegen_expect_token(p, 12)) // token=',' + && + (c = star_target_rule(p)) // star_target + ) + { + D(fprintf(stderr, "%*c+ _tmp_150[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' star_target")); + _res = c; + if (_res == NULL && PyErr_Occurred()) { + p->error_indicator = 1; + D(p->level--); + return NULL; + } + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s _tmp_150[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' star_target")); + } + _res = NULL; + done: + D(p->level--); + return _res; +} + +// _tmp_151: star_targets '=' +static void * +_tmp_151_rule(Parser *p) { D(p->level++); if (p->error_indicator) { @@ -24433,7 +24680,7 @@ _tmp_149_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> _tmp_149[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_targets '='")); + D(fprintf(stderr, "%*c> _tmp_151[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_targets '='")); Token * _literal; expr_ty star_targets_var; if ( @@ -24442,12 +24689,12 @@ _tmp_149_rule(Parser *p) (_literal = _PyPegen_expect_token(p, 22)) // token='=' ) { - D(fprintf(stderr, "%*c+ _tmp_149[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_targets '='")); + D(fprintf(stderr, "%*c+ _tmp_151[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_targets '='")); _res = _PyPegen_dummy_name(p, star_targets_var, _literal); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_149[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_151[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_targets '='")); } _res = NULL; @@ -24456,9 +24703,9 @@ _tmp_149_rule(Parser *p) return _res; } -// _tmp_150: star_targets '=' +// _tmp_152: star_targets '=' static void * -_tmp_150_rule(Parser *p) +_tmp_152_rule(Parser *p) { D(p->level++); if (p->error_indicator) { @@ -24472,7 +24719,7 @@ _tmp_150_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> _tmp_150[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_targets '='")); + D(fprintf(stderr, "%*c> _tmp_152[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_targets '='")); Token * _literal; expr_ty star_targets_var; if ( @@ -24481,12 +24728,12 @@ _tmp_150_rule(Parser *p) (_literal = _PyPegen_expect_token(p, 22)) // token='=' ) { - D(fprintf(stderr, "%*c+ _tmp_150[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_targets '='")); + D(fprintf(stderr, "%*c+ _tmp_152[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_targets '='")); _res = _PyPegen_dummy_name(p, star_targets_var, _literal); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_150[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_152[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_targets '='")); } _res = NULL; @@ -24495,9 +24742,9 @@ _tmp_150_rule(Parser *p) return _res; } -// _loop1_151: param_with_default +// _loop1_153: param_with_default static asdl_seq * -_loop1_151_rule(Parser *p) +_loop1_153_rule(Parser *p) { D(p->level++); if (p->error_indicator) { @@ -24521,7 +24768,7 @@ _loop1_151_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> _loop1_151[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_with_default")); + D(fprintf(stderr, "%*c> _loop1_153[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_with_default")); NameDefaultPair* param_with_default_var; while ( (param_with_default_var = param_with_default_rule(p)) // param_with_default @@ -24543,7 +24790,7 @@ _loop1_151_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop1_151[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop1_153[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "param_with_default")); } if (_n == 0 || p->error_indicator) { @@ -24561,14 +24808,14 @@ _loop1_151_rule(Parser *p) } for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]); PyMem_Free(_children); - _PyPegen_insert_memo(p, _start_mark, _loop1_151_type, _seq); + _PyPegen_insert_memo(p, _start_mark, _loop1_153_type, _seq); D(p->level--); return _seq; } -// _loop1_152: lambda_param_with_default +// _loop1_154: lambda_param_with_default static asdl_seq * -_loop1_152_rule(Parser *p) +_loop1_154_rule(Parser *p) { D(p->level++); if (p->error_indicator) { @@ -24592,7 +24839,7 @@ _loop1_152_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> _loop1_152[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_with_default")); + D(fprintf(stderr, "%*c> _loop1_154[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_with_default")); NameDefaultPair* lambda_param_with_default_var; while ( (lambda_param_with_default_var = lambda_param_with_default_rule(p)) // lambda_param_with_default @@ -24614,7 +24861,7 @@ _loop1_152_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop1_152[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop1_154[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_with_default")); } if (_n == 0 || p->error_indicator) { @@ -24632,14 +24879,14 @@ _loop1_152_rule(Parser *p) } for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]); PyMem_Free(_children); - _PyPegen_insert_memo(p, _start_mark, _loop1_152_type, _seq); + _PyPegen_insert_memo(p, _start_mark, _loop1_154_type, _seq); D(p->level--); return _seq; } -// _tmp_153: ')' | '**' +// _tmp_155: ')' | '**' static void * -_tmp_153_rule(Parser *p) +_tmp_155_rule(Parser *p) { D(p->level++); if (p->error_indicator) { @@ -24653,18 +24900,18 @@ _tmp_153_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> _tmp_153[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "')'")); + D(fprintf(stderr, "%*c> _tmp_155[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "')'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 8)) // token=')' ) { - D(fprintf(stderr, "%*c+ _tmp_153[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "')'")); + D(fprintf(stderr, "%*c+ _tmp_155[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "')'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_153[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_155[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "')'")); } { // '**' @@ -24672,18 +24919,18 @@ _tmp_153_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> _tmp_153[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'**'")); + D(fprintf(stderr, "%*c> _tmp_155[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'**'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 35)) // token='**' ) { - D(fprintf(stderr, "%*c+ _tmp_153[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**'")); + D(fprintf(stderr, "%*c+ _tmp_155[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_153[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_155[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'**'")); } _res = NULL; @@ -24692,9 +24939,9 @@ _tmp_153_rule(Parser *p) return _res; } -// _tmp_154: ':' | '**' +// _tmp_156: ':' | '**' static void * -_tmp_154_rule(Parser *p) +_tmp_156_rule(Parser *p) { D(p->level++); if (p->error_indicator) { @@ -24708,18 +24955,18 @@ _tmp_154_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> _tmp_154[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); + D(fprintf(stderr, "%*c> _tmp_156[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 11)) // token=':' ) { - D(fprintf(stderr, "%*c+ _tmp_154[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); + D(fprintf(stderr, "%*c+ _tmp_156[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_154[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_156[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "':'")); } { // '**' @@ -24727,18 +24974,18 @@ _tmp_154_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> _tmp_154[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'**'")); + D(fprintf(stderr, "%*c> _tmp_156[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'**'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 35)) // token='**' ) { - D(fprintf(stderr, "%*c+ _tmp_154[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**'")); + D(fprintf(stderr, "%*c+ _tmp_156[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_154[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_156[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'**'")); } _res = NULL; diff --git a/Parser/pegen/parse_string.c b/Parser/pegen/parse_string.c index fb0c4aff..c852e5b8 100644 --- a/Parser/pegen/parse_string.c +++ b/Parser/pegen/parse_string.c @@ -410,7 +410,7 @@ fstring_compile_expr(Parser *p, const char *expr_start, const char *expr_end, Parser *p2 = _PyPegen_Parser_New(tok, Py_fstring_input, p->flags, p->feature_version, NULL, p->arena); p2->starting_lineno = t->lineno + lines - 1; - p2->starting_col_offset = p->tok->first_lineno == p->tok->lineno ? t->col_offset + cols : cols; + p2->starting_col_offset = t->col_offset + cols; expr = _PyPegen_run_parser(p2); diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 199b09c4..614012df 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -2619,6 +2619,11 @@ zip_next(zipobject *lz) PyTuple_SET_ITEM(result, i, item); Py_DECREF(olditem); } + // bpo-42536: The GC may have untracked this result tuple. Since we're + // recycling it, make sure it's tracked again: + if (!_PyObject_GC_IS_TRACKED(result)) { + _PyObject_GC_TRACK(result); + } } else { result = PyTuple_New(tuplesize); if (result == NULL) diff --git a/Python/fileutils.c b/Python/fileutils.c index 397ac34e..ddc09098 100644 --- a/Python/fileutils.c +++ b/Python/fileutils.c @@ -1165,6 +1165,13 @@ set_inheritable(int fd, int inheritable, int raise, int *atomic_flag_works) return 0; } +#ifdef __linux__ + if (errno == EBADF) { + // On Linux, ioctl(FIOCLEX) will fail with EBADF for O_PATH file descriptors + // Fall through to the fcntl() path + } + else +#endif if (errno != ENOTTY && errno != EACCES) { if (raise) PyErr_SetFromErrno(PyExc_OSError); diff --git a/Python/getcopyright.c b/Python/getcopyright.c index 299ccc08..7fdeb314 100644 --- a/Python/getcopyright.c +++ b/Python/getcopyright.c @@ -4,7 +4,7 @@ static const char cprt[] = "\ -Copyright (c) 2001-2020 Python Software Foundation.\n\ +Copyright (c) 2001-2021 Python Software Foundation.\n\ All Rights Reserved.\n\ \n\ Copyright (c) 2000 BeOpen.com.\n\ diff --git a/Python/pythonrun.c b/Python/pythonrun.c index 70748dc5..04540989 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -64,11 +64,15 @@ extern Py_EXPORTED_SYMBOL grammar _PyParser_Grammar; /* From graminit.c */ static void flush_io(void); static PyObject *run_mod(mod_ty, PyObject *, PyObject *, PyObject *, PyCompilerFlags *, PyArena *); -static PyObject *run_pyc_file(FILE *, const char *, PyObject *, PyObject *, +static PyObject *run_pyc_file(FILE *, PyObject *, PyObject *, PyCompilerFlags *); static void err_input(perrdetail *); static void err_free(perrdetail *); static int PyRun_InteractiveOneObjectEx(FILE *, PyObject *, PyCompilerFlags *); +static PyObject* pyrun_file(FILE *fp, PyObject *filename, int start, + PyObject *globals, PyObject *locals, int closeit, + PyCompilerFlags *flags); + /* Parse input from a file and execute it */ int @@ -310,82 +314,89 @@ PyRun_InteractiveOneFlags(FILE *fp, const char *filename_str, PyCompilerFlags *f the file type, and, if we may close it, at the first few bytes. */ static int -maybe_pyc_file(FILE *fp, const char* filename, const char* ext, int closeit) +maybe_pyc_file(FILE *fp, PyObject *filename, int closeit) { - if (strcmp(ext, ".pyc") == 0) + PyObject *ext = PyUnicode_FromString(".pyc"); + if (ext == NULL) { + return -1; + } + Py_ssize_t endswith = PyUnicode_Tailmatch(filename, ext, 0, PY_SSIZE_T_MAX, +1); + Py_DECREF(ext); + if (endswith) { return 1; + } /* Only look into the file if we are allowed to close it, since it then should also be seekable. */ - if (closeit) { - /* Read only two bytes of the magic. If the file was opened in - text mode, the bytes 3 and 4 of the magic (\r\n) might not - be read as they are on disk. */ - unsigned int halfmagic = PyImport_GetMagicNumber() & 0xFFFF; - unsigned char buf[2]; - /* Mess: In case of -x, the stream is NOT at its start now, - and ungetc() was used to push back the first newline, - which makes the current stream position formally undefined, - and a x-platform nightmare. - Unfortunately, we have no direct way to know whether -x - was specified. So we use a terrible hack: if the current - stream position is not 0, we assume -x was specified, and - give up. Bug 132850 on SourceForge spells out the - hopelessness of trying anything else (fseek and ftell - don't work predictably x-platform for text-mode files). - */ - int ispyc = 0; - if (ftell(fp) == 0) { - if (fread(buf, 1, 2, fp) == 2 && - ((unsigned int)buf[1]<<8 | buf[0]) == halfmagic) - ispyc = 1; - rewind(fp); - } - return ispyc; + if (!closeit) { + return 0; } - return 0; + + /* Read only two bytes of the magic. If the file was opened in + text mode, the bytes 3 and 4 of the magic (\r\n) might not + be read as they are on disk. */ + unsigned int halfmagic = PyImport_GetMagicNumber() & 0xFFFF; + unsigned char buf[2]; + /* Mess: In case of -x, the stream is NOT at its start now, + and ungetc() was used to push back the first newline, + which makes the current stream position formally undefined, + and a x-platform nightmare. + Unfortunately, we have no direct way to know whether -x + was specified. So we use a terrible hack: if the current + stream position is not 0, we assume -x was specified, and + give up. Bug 132850 on SourceForge spells out the + hopelessness of trying anything else (fseek and ftell + don't work predictably x-platform for text-mode files). + */ + int ispyc = 0; + if (ftell(fp) == 0) { + if (fread(buf, 1, 2, fp) == 2 && + ((unsigned int)buf[1]<<8 | buf[0]) == halfmagic) + ispyc = 1; + rewind(fp); + } + return ispyc; } + static int -set_main_loader(PyObject *d, const char *filename, const char *loader_name) +set_main_loader(PyObject *d, PyObject *filename, const char *loader_name) { - PyObject *filename_obj, *bootstrap, *loader_type = NULL, *loader; - int result = 0; - - filename_obj = PyUnicode_DecodeFSDefault(filename); - if (filename_obj == NULL) - return -1; PyInterpreterState *interp = _PyInterpreterState_GET(); - bootstrap = PyObject_GetAttrString(interp->importlib, - "_bootstrap_external"); - if (bootstrap != NULL) { - loader_type = PyObject_GetAttrString(bootstrap, loader_name); - Py_DECREF(bootstrap); + PyObject *bootstrap = PyObject_GetAttrString(interp->importlib, + "_bootstrap_external"); + if (bootstrap == NULL) { + return -1; } + + PyObject *loader_type = PyObject_GetAttrString(bootstrap, loader_name); + Py_DECREF(bootstrap); if (loader_type == NULL) { - Py_DECREF(filename_obj); return -1; } - loader = PyObject_CallFunction(loader_type, "sN", "__main__", filename_obj); + + PyObject *loader = PyObject_CallFunction(loader_type, + "sO", "__main__", filename); Py_DECREF(loader_type); if (loader == NULL) { return -1; } + if (PyDict_SetItemString(d, "__loader__", loader) < 0) { - result = -1; + Py_DECREF(loader); + return -1; } Py_DECREF(loader); - return result; + return 0; } -int -PyRun_SimpleFileExFlags(FILE *fp, const char *filename, int closeit, - PyCompilerFlags *flags) + +static int +pyrun_simple_file(FILE *fp, PyObject *filename, int closeit, + PyCompilerFlags *flags) { PyObject *m, *d, *v; - const char *ext; int set_file_name = 0, ret = -1; - size_t len; m = PyImport_AddModule("__main__"); if (m == NULL) @@ -393,29 +404,29 @@ PyRun_SimpleFileExFlags(FILE *fp, const char *filename, int closeit, Py_INCREF(m); d = PyModule_GetDict(m); if (PyDict_GetItemString(d, "__file__") == NULL) { - PyObject *f; - f = PyUnicode_DecodeFSDefault(filename); - if (f == NULL) - goto done; - if (PyDict_SetItemString(d, "__file__", f) < 0) { - Py_DECREF(f); + if (PyDict_SetItemString(d, "__file__", filename) < 0) { goto done; } if (PyDict_SetItemString(d, "__cached__", Py_None) < 0) { - Py_DECREF(f); goto done; } set_file_name = 1; - Py_DECREF(f); } - len = strlen(filename); - ext = filename + len - (len > 4 ? 4 : 0); - if (maybe_pyc_file(fp, filename, ext, closeit)) { + + int pyc = maybe_pyc_file(fp, filename, closeit); + if (pyc < 0) { + goto done; + } + + if (pyc) { FILE *pyc_fp; /* Try to run a pyc file. First, re-open in binary */ - if (closeit) + if (closeit) { fclose(fp); - if ((pyc_fp = _Py_fopen(filename, "rb")) == NULL) { + } + + pyc_fp = _Py_fopen_obj(filename, "rb"); + if (pyc_fp == NULL) { fprintf(stderr, "python: Can't reopen .pyc file\n"); goto done; } @@ -426,17 +437,17 @@ PyRun_SimpleFileExFlags(FILE *fp, const char *filename, int closeit, fclose(pyc_fp); goto done; } - v = run_pyc_file(pyc_fp, filename, d, d, flags); + v = run_pyc_file(pyc_fp, d, d, flags); } else { /* When running from stdin, leave __main__.__loader__ alone */ - if (strcmp(filename, "") != 0 && + if (PyUnicode_CompareWithASCIIString(filename, "") != 0 && set_main_loader(d, filename, "SourceFileLoader") < 0) { fprintf(stderr, "python: failed to set __main__.__loader__\n"); ret = -1; goto done; } - v = PyRun_FileExFlags(fp, filename, Py_file_input, d, d, - closeit, flags); + v = pyrun_file(fp, filename, Py_file_input, d, d, + closeit, flags); } flush_io(); if (v == NULL) { @@ -459,6 +470,21 @@ PyRun_SimpleFileExFlags(FILE *fp, const char *filename, int closeit, return ret; } + +int +PyRun_SimpleFileExFlags(FILE *fp, const char *filename, int closeit, + PyCompilerFlags *flags) +{ + PyObject *filename_obj = PyUnicode_DecodeFSDefault(filename); + if (filename_obj == NULL) { + return -1; + } + int res = pyrun_simple_file(fp, filename_obj, closeit, flags); + Py_DECREF(filename_obj); + return res; +} + + int PyRun_SimpleStringFlags(const char *command, PyCompilerFlags *flags) { @@ -1081,24 +1107,18 @@ PyRun_StringFlags(const char *str, int start, PyObject *globals, return ret; } -PyObject * -PyRun_FileExFlags(FILE *fp, const char *filename_str, int start, PyObject *globals, - PyObject *locals, int closeit, PyCompilerFlags *flags) + +static PyObject * +pyrun_file(FILE *fp, PyObject *filename, int start, PyObject *globals, + PyObject *locals, int closeit, PyCompilerFlags *flags) { - PyObject *ret = NULL; + PyArena *arena = PyArena_New(); + if (arena == NULL) { + return NULL; + } + mod_ty mod; - PyArena *arena = NULL; - PyObject *filename; int use_peg = _PyInterpreterState_GET()->config._use_peg_parser; - - filename = PyUnicode_DecodeFSDefault(filename_str); - if (filename == NULL) - goto exit; - - arena = PyArena_New(); - if (arena == NULL) - goto exit; - if (use_peg) { mod = PyPegen_ASTFromFileObject(fp, filename, start, NULL, NULL, NULL, flags, NULL, arena); @@ -1108,20 +1128,40 @@ PyRun_FileExFlags(FILE *fp, const char *filename_str, int start, PyObject *globa flags, NULL, arena); } - if (closeit) + if (closeit) { fclose(fp); - if (mod == NULL) { - goto exit; } - ret = run_mod(mod, filename, globals, locals, flags, arena); -exit: - Py_XDECREF(filename); - if (arena != NULL) - PyArena_Free(arena); + PyObject *ret; + if (mod != NULL) { + ret = run_mod(mod, filename, globals, locals, flags, arena); + } + else { + ret = NULL; + } + PyArena_Free(arena); + return ret; } + +PyObject * +PyRun_FileExFlags(FILE *fp, const char *filename, int start, PyObject *globals, + PyObject *locals, int closeit, PyCompilerFlags *flags) +{ + PyObject *filename_obj = PyUnicode_DecodeFSDefault(filename); + if (filename_obj == NULL) { + return NULL; + } + + PyObject *res = pyrun_file(fp, filename_obj, start, globals, + locals, closeit, flags); + Py_DECREF(filename_obj); + return res; + +} + + static void flush_io(void) { @@ -1202,8 +1242,8 @@ run_mod(mod_ty mod, PyObject *filename, PyObject *globals, PyObject *locals, } static PyObject * -run_pyc_file(FILE *fp, const char *filename, PyObject *globals, - PyObject *locals, PyCompilerFlags *flags) +run_pyc_file(FILE *fp, PyObject *globals, PyObject *locals, + PyCompilerFlags *flags) { PyThreadState *tstate = _PyThreadState_GET(); PyCodeObject *co; diff --git a/Python/traceback.c b/Python/traceback.c index 99b63af1..5d3a65cc 100644 --- a/Python/traceback.c +++ b/Python/traceback.c @@ -622,7 +622,8 @@ PyTraceBack_Print(PyObject *v, PyObject *f) return err; } -/* Reverse a string. For example, "abcd" becomes "dcba". +/* Format an integer in range [0; 0xffffffff] to decimal and write it + into the file fd. This function is signal safe. */ diff --git a/README.rst b/README.rst index a9ab2605..0a36bc16 100644 --- a/README.rst +++ b/README.rst @@ -1,4 +1,4 @@ -This is Python version 3.9.1 +This is Python version 3.9.2 ============================ .. image:: https://travis-ci.org/python/cpython.svg?branch=3.9 @@ -22,7 +22,7 @@ This is Python version 3.9.1 :target: https://python.zulipchat.com -Copyright (c) 2001-2020 Python Software Foundation. All rights reserved. +Copyright (c) 2001-2021 Python Software Foundation. All rights reserved. See the end of this file for further copyright and license information. @@ -250,7 +250,7 @@ See :pep:`596` for Python 3.9 release details. Copyright and License Information --------------------------------- -Copyright (c) 2001-2020 Python Software Foundation. All rights reserved. +Copyright (c) 2001-2021 Python Software Foundation. All rights reserved. Copyright (c) 2000 BeOpen.com. All rights reserved. diff --git a/Tools/freeze/freeze.py b/Tools/freeze/freeze.py index 83aa508a..d66e1e27 100755 --- a/Tools/freeze/freeze.py +++ b/Tools/freeze/freeze.py @@ -93,6 +93,7 @@ import modulefinder import getopt import os import sys +import sysconfig # Import the freeze-private modules @@ -226,7 +227,7 @@ def main(): extensions_c = 'frozen_extensions.c' if ishome: print("(Using Python source directory)") - binlib = exec_prefix + configdir = exec_prefix incldir = os.path.join(prefix, 'Include') config_h_dir = exec_prefix config_c_in = os.path.join(prefix, 'Modules', 'config.c.in') @@ -235,22 +236,21 @@ def main(): if win: frozendllmain_c = os.path.join(exec_prefix, 'Pc\\frozen_dllmain.c') else: - binlib = os.path.join(exec_prefix, - 'lib', 'python%s' % version, - 'config-%s' % flagged_version) + configdir = sysconfig.get_config_var('LIBPL') incldir = os.path.join(prefix, 'include', 'python%s' % flagged_version) config_h_dir = os.path.join(exec_prefix, 'include', 'python%s' % flagged_version) - config_c_in = os.path.join(binlib, 'config.c.in') - frozenmain_c = os.path.join(binlib, 'frozenmain.c') - makefile_in = os.path.join(binlib, 'Makefile') - frozendllmain_c = os.path.join(binlib, 'frozen_dllmain.c') + config_c_in = os.path.join(configdir, 'config.c.in') + frozenmain_c = os.path.join(configdir, 'frozenmain.c') + makefile_in = os.path.join(configdir, 'Makefile') + frozendllmain_c = os.path.join(configdir, 'frozen_dllmain.c') + libdir = sysconfig.get_config_var('LIBDIR') supp_sources = [] defines = [] includes = ['-I' + incldir, '-I' + config_h_dir] # sanity check of directories and files - check_dirs = [prefix, exec_prefix, binlib, incldir] + check_dirs = [prefix, exec_prefix, configdir, incldir] if not win: # These are not directories on Windows. check_dirs = check_dirs + extensions @@ -457,7 +457,7 @@ def main(): cflags = ['$(OPT)'] cppflags = defines + includes - libs = [os.path.join(binlib, '$(LDLIBRARY)')] + libs = [os.path.join(libdir, '$(LDLIBRARY)')] somevars = {} if os.path.exists(makefile_in): diff --git a/Tools/gdb/libpython.py b/Tools/gdb/libpython.py index 33bf5ac8..aeaa63e5 100755 --- a/Tools/gdb/libpython.py +++ b/Tools/gdb/libpython.py @@ -468,7 +468,7 @@ class InstanceProxy(object): def __repr__(self): if isinstance(self.attrdict, dict): kwargs = ', '.join(["%s=%r" % (arg, val) - for arg, val in self.attrdict.iteritems()]) + for arg, val in self.attrdict.items()]) return '<%s(%s) at remote 0x%x>' % (self.cl_name, kwargs, self.address) else: diff --git a/Tools/msi/uploadrelease.ps1 b/Tools/msi/uploadrelease.ps1 index 518acbe8..8ed376a7 100644 --- a/Tools/msi/uploadrelease.ps1 +++ b/Tools/msi/uploadrelease.ps1 @@ -90,7 +90,7 @@ if (-not $skipupload) { $d = "$target/$($p[0])/" & $plink -batch $user@$server mkdir $d & $plink -batch $user@$server chgrp downloads $d - & $plink -batch $user@$server chmod g-x,o+rx $d + & $plink -batch $user@$server chmod o+rx $d & $pscp -batch $chm.FullName "$user@${server}:$d" if (-not $?) { throw "Failed to upload $chm" } @@ -115,7 +115,7 @@ if (-not $skipupload) { $sd = "$d$($a.Name)$($p[1])/" & $plink -batch $user@$server mkdir $sd & $plink -batch $user@$server chgrp downloads $sd - & $plink -batch $user@$server chmod g-x,o+rx $sd + & $plink -batch $user@$server chmod o+rx $sd & $pscp -batch $msi.FullName "$user@${server}:$sd" if (-not $?) { throw "Failed to upload $msi" } & $plink -batch $user@$server chgrp downloads $sd* diff --git a/Tools/pynche/PyncheWidget.py b/Tools/pynche/PyncheWidget.py index ef12198a..ea456e57 100644 --- a/Tools/pynche/PyncheWidget.py +++ b/Tools/pynche/PyncheWidget.py @@ -36,15 +36,11 @@ class PyncheWidget: else: # Is there already a default root for Tk, say because we're # running under Guido's IDE? :-) Two conditions say no, either the - # import fails or _default_root is None. - tkroot = None - try: - from Tkinter import _default_root - tkroot = self.__tkroot = _default_root - except ImportError: - pass + # _default_root is None or it is unset. + tkroot = getattr(tkinter, '_default_root', None) if not tkroot: - tkroot = self.__tkroot = Tk(className='Pynche') + tkroot = Tk(className='Pynche') + self.__tkroot = tkroot # but this isn't our top level widget, so make it invisible tkroot.withdraw() # create the menubar diff --git a/configure b/configure index 2d379feb..12523354 100755 --- a/configure +++ b/configure @@ -11072,10 +11072,10 @@ else main() { pthread_attr_t attr; pthread_t id; - if (pthread_attr_init(&attr)) exit(-1); - if (pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM)) exit(-1); - if (pthread_create(&id, &attr, foo, NULL)) exit(-1); - exit(0); + if (pthread_attr_init(&attr)) return (-1); + if (pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM)) return (-1); + if (pthread_create(&id, &attr, foo, NULL)) return (-1); + return (0); } _ACEOF if ac_fn_c_try_run "$LINENO"; then : @@ -15083,7 +15083,7 @@ else int main() { /* Success: exit code 0 */ - exit((((wchar_t) -1) < ((wchar_t) 0)) ? 0 : 1); + return ((((wchar_t) -1) < ((wchar_t) 0)) ? 0 : 1); } _ACEOF @@ -15382,13 +15382,7 @@ _ACEOF fi - -case $ac_sys_system in - Linux*|GNU*|Darwin|VxWorks) - EXT_SUFFIX=.${SOABI}${SHLIB_SUFFIX};; - *) - EXT_SUFFIX=${SHLIB_SUFFIX};; -esac +EXT_SUFFIX=.${SOABI}${SHLIB_SUFFIX} { $as_echo "$as_me:${as_lineno-$LINENO}: checking LDVERSION" >&5 $as_echo_n "checking LDVERSION... " >&6; } @@ -15464,7 +15458,7 @@ else int main() { - exit(((-1)>>3 == -1) ? 0 : 1); + return (((-1)>>3 == -1) ? 0 : 1); } _ACEOF @@ -15934,6 +15928,7 @@ else /* end confdefs.h. */ #include +#include int main() { diff --git a/configure.ac b/configure.ac index c968d149..972287a9 100644 --- a/configure.ac +++ b/configure.ac @@ -3313,10 +3313,10 @@ if test "$posix_threads" = "yes"; then main() { pthread_attr_t attr; pthread_t id; - if (pthread_attr_init(&attr)) exit(-1); - if (pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM)) exit(-1); - if (pthread_create(&id, &attr, foo, NULL)) exit(-1); - exit(0); + if (pthread_attr_init(&attr)) return (-1); + if (pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM)) return (-1); + if (pthread_create(&id, &attr, foo, NULL)) return (-1); + return (0); }]])], [ac_cv_pthread_system_supported=yes], [ac_cv_pthread_system_supported=no], @@ -4725,7 +4725,7 @@ then int main() { /* Success: exit code 0 */ - exit((((wchar_t) -1) < ((wchar_t) 0)) ? 0 : 1); + return ((((wchar_t) -1) < ((wchar_t) 0)) ? 0 : 1); } ]])], [ac_cv_wchar_t_signed=yes], @@ -4783,12 +4783,7 @@ if test "$Py_DEBUG" = 'true' -a "$with_trace_refs" != "yes"; then fi AC_SUBST(EXT_SUFFIX) -case $ac_sys_system in - Linux*|GNU*|Darwin|VxWorks) - EXT_SUFFIX=.${SOABI}${SHLIB_SUFFIX};; - *) - EXT_SUFFIX=${SHLIB_SUFFIX};; -esac +EXT_SUFFIX=.${SOABI}${SHLIB_SUFFIX} AC_MSG_CHECKING(LDVERSION) LDVERSION='$(VERSION)$(ABIFLAGS)' @@ -4847,7 +4842,7 @@ AC_CACHE_VAL(ac_cv_rshift_extends_sign, [ AC_RUN_IFELSE([AC_LANG_SOURCE([[ int main() { - exit(((-1)>>3 == -1) ? 0 : 1); + return (((-1)>>3 == -1) ? 0 : 1); } ]])], [ac_cv_rshift_extends_sign=yes], @@ -4994,6 +4989,7 @@ AC_MSG_CHECKING(for broken poll()) AC_CACHE_VAL(ac_cv_broken_poll, AC_RUN_IFELSE([AC_LANG_SOURCE([[ #include +#include int main() { diff --git a/setup.py b/setup.py index bd5f7369..d42eb9d1 100644 --- a/setup.py +++ b/setup.py @@ -1013,7 +1013,7 @@ class PyBuildExt(build_ext): os_release = int(os.uname()[2].split('.')[0]) dep_target = sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET') if (dep_target and - (tuple(int(n) for n in str(dep_target).split('.')[0:2]) + (tuple(int(n) for n in dep_target.split('.')[0:2]) < (10, 5) ) ): os_release = 8 if os_release < 9: