From webhook-mailer at python.org Thu Oct 1 00:50:48 2020 From: webhook-mailer at python.org (Dong-hee Na) Date: Thu, 01 Oct 2020 04:50:48 -0000 Subject: [Python-checkins] bpo-41870: Avoid the test when nargs=0 (GH-22462) Message-ID: https://github.com/python/cpython/commit/fa7ce080175f65d678a7d5756c94f82887fc9803 commit: fa7ce080175f65d678a7d5756c94f82887fc9803 branch: master author: Dong-hee Na committer: GitHub date: 2020-10-01T13:50:40+09:00 summary: bpo-41870: Avoid the test when nargs=0 (GH-22462) files: M Objects/boolobject.c diff --git a/Objects/boolobject.c b/Objects/boolobject.c index ab7669cb240de..b786966533e1d 100644 --- a/Objects/boolobject.c +++ b/Objects/boolobject.c @@ -72,9 +72,9 @@ bool_vectorcall(PyObject *type, PyObject * const*args, assert(PyType_Check(type)); if (nargs) { ok = PyObject_IsTrue(args[0]); - } - if (ok < 0) { - return NULL; + if (ok < 0) { + return NULL; + } } return PyBool_FromLong(ok); } From webhook-mailer at python.org Thu Oct 1 09:24:48 2020 From: webhook-mailer at python.org (Erlend Egeberg Aasland) Date: Thu, 01 Oct 2020 13:24:48 -0000 Subject: [Python-checkins] bpo-41861: Convert _sqlite3 RowType and StatementType to heap types (GH-22444) Message-ID: https://github.com/python/cpython/commit/9031bd4fa42dbb0f25aee9286154ad4bf60df3f8 commit: 9031bd4fa42dbb0f25aee9286154ad4bf60df3f8 branch: master author: Erlend Egeberg Aasland committer: GitHub date: 2020-10-01T15:24:31+02:00 summary: bpo-41861: Convert _sqlite3 RowType and StatementType to heap types (GH-22444) files: M Modules/_sqlite/cache.c M Modules/_sqlite/connection.c M Modules/_sqlite/cursor.c M Modules/_sqlite/module.c M Modules/_sqlite/prepare_protocol.c M Modules/_sqlite/row.c M Modules/_sqlite/row.h M Modules/_sqlite/statement.c M Modules/_sqlite/statement.h diff --git a/Modules/_sqlite/cache.c b/Modules/_sqlite/cache.c index c417ce872d368..0b02be4f0bec9 100644 --- a/Modules/_sqlite/cache.c +++ b/Modules/_sqlite/cache.c @@ -267,7 +267,7 @@ static PyType_Slot pysqlite_NodeType_slots[] = { static PyType_Spec pysqlite_NodeType_spec = { .name = MODULE_NAME ".Node", .basicsize = sizeof(pysqlite_Node), - .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE, + .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, .slots = pysqlite_NodeType_slots, }; PyTypeObject *pysqlite_NodeType = NULL; @@ -291,7 +291,7 @@ static PyType_Slot pysqlite_CacheType_slots[] = { static PyType_Spec pysqlite_CacheType_spec = { .name = MODULE_NAME ".Cache", .basicsize = sizeof(pysqlite_Cache), - .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE, + .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, .slots = pysqlite_CacheType_slots, }; PyTypeObject *pysqlite_CacheType = NULL; diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index 121850ae7e1f3..91e9046f50cb0 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -1234,7 +1234,7 @@ PyObject* pysqlite_connection_call(pysqlite_Connection* self, PyObject* args, Py _pysqlite_drop_unused_statement_references(self); - statement = PyObject_New(pysqlite_Statement, &pysqlite_StatementType); + statement = PyObject_New(pysqlite_Statement, pysqlite_StatementType); if (!statement) { return NULL; } diff --git a/Modules/_sqlite/cursor.c b/Modules/_sqlite/cursor.c index 5cfb4b97d61df..dbbdb12caddd6 100644 --- a/Modules/_sqlite/cursor.c +++ b/Modules/_sqlite/cursor.c @@ -472,7 +472,7 @@ _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* args) if (self->statement->in_use) { Py_SETREF(self->statement, - PyObject_New(pysqlite_Statement, &pysqlite_StatementType)); + PyObject_New(pysqlite_Statement, pysqlite_StatementType)); if (!self->statement) { goto error; } diff --git a/Modules/_sqlite/module.c b/Modules/_sqlite/module.c index d0a546c008de2..8ac9ea8477363 100644 --- a/Modules/_sqlite/module.c +++ b/Modules/_sqlite/module.c @@ -352,11 +352,11 @@ PyMODINIT_FUNC PyInit__sqlite3(void) module = PyModule_Create(&_sqlite3module); if (!module || - (pysqlite_row_setup_types() < 0) || + (pysqlite_row_setup_types(module) < 0) || (pysqlite_cursor_setup_types() < 0) || (pysqlite_connection_setup_types() < 0) || (pysqlite_cache_setup_types(module) < 0) || - (pysqlite_statement_setup_types() < 0) || + (pysqlite_statement_setup_types(module) < 0) || (pysqlite_prepare_protocol_setup_types(module) < 0) ) { Py_XDECREF(module); @@ -366,7 +366,7 @@ PyMODINIT_FUNC PyInit__sqlite3(void) ADD_TYPE(module, pysqlite_ConnectionType); ADD_TYPE(module, pysqlite_CursorType); ADD_TYPE(module, *pysqlite_PrepareProtocolType); - ADD_TYPE(module, pysqlite_RowType); + ADD_TYPE(module, *pysqlite_RowType); if (!(dict = PyModule_GetDict(module))) { goto error; diff --git a/Modules/_sqlite/prepare_protocol.c b/Modules/_sqlite/prepare_protocol.c index 7daf8a620096a..089d66b981085 100644 --- a/Modules/_sqlite/prepare_protocol.c +++ b/Modules/_sqlite/prepare_protocol.c @@ -46,7 +46,7 @@ static PyType_Slot type_slots[] = { static PyType_Spec type_spec = { .name = MODULE_NAME ".PrepareProtocol", .basicsize = sizeof(pysqlite_PrepareProtocol), - .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HEAPTYPE, + .flags = Py_TPFLAGS_DEFAULT, .slots = type_slots, }; diff --git a/Modules/_sqlite/row.c b/Modules/_sqlite/row.c index 4b47108278a0a..85179e7f4df05 100644 --- a/Modules/_sqlite/row.c +++ b/Modules/_sqlite/row.c @@ -26,10 +26,13 @@ void pysqlite_row_dealloc(pysqlite_Row* self) { + PyTypeObject *tp = Py_TYPE(self); + Py_XDECREF(self->data); Py_XDECREF(self->description); - Py_TYPE(self)->tp_free((PyObject*)self); + tp->tp_free(self); + Py_DECREF(tp); } static PyObject * @@ -192,7 +195,7 @@ static PyObject* pysqlite_row_richcompare(pysqlite_Row *self, PyObject *_other, if (opid != Py_EQ && opid != Py_NE) Py_RETURN_NOTIMPLEMENTED; - if (PyObject_TypeCheck(_other, &pysqlite_RowType)) { + if (PyObject_TypeCheck(_other, pysqlite_RowType)) { pysqlite_Row *other = (pysqlite_Row *)_other; int eq = PyObject_RichCompareBool(self->description, other->description, Py_EQ); if (eq < 0) { @@ -206,73 +209,40 @@ static PyObject* pysqlite_row_richcompare(pysqlite_Row *self, PyObject *_other, Py_RETURN_NOTIMPLEMENTED; } -PyMappingMethods pysqlite_row_as_mapping = { - /* mp_length */ (lenfunc)pysqlite_row_length, - /* mp_subscript */ (binaryfunc)pysqlite_row_subscript, - /* mp_ass_subscript */ (objobjargproc)0, -}; - -static PySequenceMethods pysqlite_row_as_sequence = { - /* sq_length */ (lenfunc)pysqlite_row_length, - /* sq_concat */ 0, - /* sq_repeat */ 0, - /* sq_item */ (ssizeargfunc)pysqlite_row_item, -}; - - -static PyMethodDef pysqlite_row_methods[] = { +static PyMethodDef row_methods[] = { {"keys", (PyCFunction)pysqlite_row_keys, METH_NOARGS, PyDoc_STR("Returns the keys of the row.")}, {NULL, NULL} }; +static PyType_Slot row_slots[] = { + {Py_tp_dealloc, pysqlite_row_dealloc}, + {Py_tp_hash, pysqlite_row_hash}, + {Py_tp_methods, row_methods}, + {Py_tp_richcompare, pysqlite_row_richcompare}, + {Py_tp_iter, pysqlite_iter}, + {Py_mp_length, pysqlite_row_length}, + {Py_mp_subscript, pysqlite_row_subscript}, + {Py_sq_length, pysqlite_row_length}, + {Py_sq_item, pysqlite_row_item}, + {Py_tp_new, pysqlite_row_new}, + {0, NULL}, +}; -PyTypeObject pysqlite_RowType = { - PyVarObject_HEAD_INIT(NULL, 0) - MODULE_NAME ".Row", /* tp_name */ - sizeof(pysqlite_Row), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)pysqlite_row_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - (hashfunc)pysqlite_row_hash, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /* tp_flags */ - 0, /* tp_doc */ - (traverseproc)0, /* tp_traverse */ - 0, /* tp_clear */ - (richcmpfunc)pysqlite_row_richcompare, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - (getiterfunc)pysqlite_iter, /* tp_iter */ - 0, /* tp_iternext */ - pysqlite_row_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - 0, /* tp_new */ - 0 /* tp_free */ +static PyType_Spec row_spec = { + .name = MODULE_NAME ".Row", + .basicsize = sizeof(pysqlite_Row), + .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + .slots = row_slots, }; -extern int pysqlite_row_setup_types(void) +PyTypeObject *pysqlite_RowType = NULL; + +extern int pysqlite_row_setup_types(PyObject *module) { - pysqlite_RowType.tp_new = pysqlite_row_new; - pysqlite_RowType.tp_as_mapping = &pysqlite_row_as_mapping; - pysqlite_RowType.tp_as_sequence = &pysqlite_row_as_sequence; - return PyType_Ready(&pysqlite_RowType); + pysqlite_RowType = (PyTypeObject *)PyType_FromModuleAndSpec(module, &row_spec, NULL); + if (pysqlite_RowType == NULL) { + return -1; + } + return 0; } diff --git a/Modules/_sqlite/row.h b/Modules/_sqlite/row.h index 4ad506f8dd968..2dac41e89e12d 100644 --- a/Modules/_sqlite/row.h +++ b/Modules/_sqlite/row.h @@ -33,8 +33,8 @@ typedef struct _Row PyObject* description; } pysqlite_Row; -extern PyTypeObject pysqlite_RowType; +extern PyTypeObject *pysqlite_RowType; -int pysqlite_row_setup_types(void); +int pysqlite_row_setup_types(PyObject *module); #endif diff --git a/Modules/_sqlite/statement.c b/Modules/_sqlite/statement.c index 0458e1171ebdd..4682d286c581c 100644 --- a/Modules/_sqlite/statement.c +++ b/Modules/_sqlite/statement.c @@ -371,6 +371,8 @@ void pysqlite_statement_mark_dirty(pysqlite_Statement* self) void pysqlite_statement_dealloc(pysqlite_Statement* self) { + PyTypeObject *tp = Py_TYPE(self); + if (self->st) { Py_BEGIN_ALLOW_THREADS sqlite3_finalize(self->st); @@ -385,7 +387,8 @@ void pysqlite_statement_dealloc(pysqlite_Statement* self) PyObject_ClearWeakRefs((PyObject*)self); } - Py_TYPE(self)->tp_free((PyObject*)self); + tp->tp_free(self); + Py_DECREF(tp); } /* @@ -458,50 +461,30 @@ static int pysqlite_check_remaining_sql(const char* tail) return 0; } -PyTypeObject pysqlite_StatementType = { - PyVarObject_HEAD_INIT(NULL, 0) - MODULE_NAME ".Statement", /* tp_name */ - sizeof(pysqlite_Statement), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)pysqlite_statement_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ - 0, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - offsetof(pysqlite_Statement, in_weakreflist), /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - 0, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)0, /* tp_init */ - 0, /* tp_alloc */ - 0, /* tp_new */ - 0 /* tp_free */ +static PyMemberDef stmt_members[] = { + {"__weaklistoffset__", T_PYSSIZET, offsetof(pysqlite_Statement, in_weakreflist), READONLY}, + {NULL}, +}; +static PyType_Slot stmt_slots[] = { + {Py_tp_members, stmt_members}, + {Py_tp_dealloc, pysqlite_statement_dealloc}, + {Py_tp_new, PyType_GenericNew}, + {0, NULL}, +}; + +static PyType_Spec stmt_spec = { + .name = MODULE_NAME ".Statement", + .basicsize = sizeof(pysqlite_Statement), + .flags = Py_TPFLAGS_DEFAULT, + .slots = stmt_slots, }; +PyTypeObject *pysqlite_StatementType = NULL; -extern int pysqlite_statement_setup_types(void) +extern int pysqlite_statement_setup_types(PyObject *module) { - pysqlite_StatementType.tp_new = PyType_GenericNew; - return PyType_Ready(&pysqlite_StatementType); + pysqlite_StatementType = (PyTypeObject *)PyType_FromModuleAndSpec(module, &stmt_spec, NULL); + if (pysqlite_StatementType == NULL) { + return -1; + } + return 0; } diff --git a/Modules/_sqlite/statement.h b/Modules/_sqlite/statement.h index 5002f02dc5b39..b426036002815 100644 --- a/Modules/_sqlite/statement.h +++ b/Modules/_sqlite/statement.h @@ -43,7 +43,7 @@ typedef struct PyObject* in_weakreflist; /* List of weak references */ } pysqlite_Statement; -extern PyTypeObject pysqlite_StatementType; +extern PyTypeObject *pysqlite_StatementType; int pysqlite_statement_create(pysqlite_Statement* self, pysqlite_Connection* connection, PyObject* sql); void pysqlite_statement_dealloc(pysqlite_Statement* self); @@ -55,6 +55,6 @@ int pysqlite_statement_finalize(pysqlite_Statement* self); int pysqlite_statement_reset(pysqlite_Statement* self); void pysqlite_statement_mark_dirty(pysqlite_Statement* self); -int pysqlite_statement_setup_types(void); +int pysqlite_statement_setup_types(PyObject *module); #endif From webhook-mailer at python.org Thu Oct 1 10:03:43 2020 From: webhook-mailer at python.org (Erlend Egeberg Aasland) Date: Thu, 01 Oct 2020 14:03:43 -0000 Subject: [Python-checkins] bpo-41861: Convert _sqlite3 CursorType and ConnectionType to heap types (GH-22478) Message-ID: https://github.com/python/cpython/commit/256e54acdbdb26745d4bbb5cf366454151e42773 commit: 256e54acdbdb26745d4bbb5cf366454151e42773 branch: master author: Erlend Egeberg Aasland committer: GitHub date: 2020-10-01T16:03:21+02:00 summary: bpo-41861: Convert _sqlite3 CursorType and ConnectionType to heap types (GH-22478) files: M Modules/_sqlite/connection.c M Modules/_sqlite/connection.h M Modules/_sqlite/cursor.c M Modules/_sqlite/cursor.h M Modules/_sqlite/module.c M Modules/_sqlite/row.c diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index 91e9046f50cb0..69203f85e0555 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -220,6 +220,8 @@ void pysqlite_do_all_statements(pysqlite_Connection* self, int action, int reset void pysqlite_connection_dealloc(pysqlite_Connection* self) { + PyTypeObject *tp = Py_TYPE(self); + Py_XDECREF(self->statement_cache); /* Clean up if user has not called .close() explicitly. */ @@ -236,7 +238,9 @@ void pysqlite_connection_dealloc(pysqlite_Connection* self) Py_XDECREF(self->collations); Py_XDECREF(self->statements); Py_XDECREF(self->cursors); - Py_TYPE(self)->tp_free((PyObject*)self); + + tp->tp_free(self); + Py_DECREF(tp); } /* @@ -281,13 +285,13 @@ PyObject* pysqlite_connection_cursor(pysqlite_Connection* self, PyObject* args, } if (factory == NULL) { - factory = (PyObject*)&pysqlite_CursorType; + factory = (PyObject*)pysqlite_CursorType; } cursor = PyObject_CallOneArg(factory, (PyObject *)self); if (cursor == NULL) return NULL; - if (!PyObject_TypeCheck(cursor, &pysqlite_CursorType)) { + if (!PyObject_TypeCheck(cursor, pysqlite_CursorType)) { PyErr_Format(PyExc_TypeError, "factory must return a cursor, not %.100s", Py_TYPE(cursor)->tp_name); @@ -1494,7 +1498,7 @@ pysqlite_connection_backup(pysqlite_Connection *self, PyObject *args, PyObject * static char *keywords[] = {"target", "pages", "progress", "name", "sleep", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|$iOsO:backup", keywords, - &pysqlite_ConnectionType, &target, + pysqlite_ConnectionType, &target, &pages, &progress, &name, &sleep_obj)) { return NULL; } @@ -1831,50 +1835,32 @@ static struct PyMemberDef connection_members[] = {NULL} }; -PyTypeObject pysqlite_ConnectionType = { - PyVarObject_HEAD_INIT(NULL, 0) - MODULE_NAME ".Connection", /* tp_name */ - sizeof(pysqlite_Connection), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)pysqlite_connection_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - (ternaryfunc)pysqlite_connection_call, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /* tp_flags */ - connection_doc, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - connection_methods, /* tp_methods */ - connection_members, /* tp_members */ - connection_getset, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)pysqlite_connection_init, /* tp_init */ - 0, /* tp_alloc */ - 0, /* tp_new */ - 0 /* tp_free */ +static PyType_Slot connection_slots[] = { + {Py_tp_dealloc, pysqlite_connection_dealloc}, + {Py_tp_doc, (void *)connection_doc}, + {Py_tp_methods, connection_methods}, + {Py_tp_members, connection_members}, + {Py_tp_getset, connection_getset}, + {Py_tp_new, PyType_GenericNew}, + {Py_tp_init, pysqlite_connection_init}, + {Py_tp_call, pysqlite_connection_call}, + {0, NULL}, +}; + +static PyType_Spec connection_spec = { + .name = MODULE_NAME ".Connection", + .basicsize = sizeof(pysqlite_Connection), + .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + .slots = connection_slots, }; -extern int pysqlite_connection_setup_types(void) +PyTypeObject *pysqlite_ConnectionType = NULL; + +extern int pysqlite_connection_setup_types(PyObject *module) { - pysqlite_ConnectionType.tp_new = PyType_GenericNew; - return PyType_Ready(&pysqlite_ConnectionType); + pysqlite_ConnectionType = (PyTypeObject *)PyType_FromModuleAndSpec(module, &connection_spec, NULL); + if (pysqlite_ConnectionType == NULL) { + return -1; + } + return 0; } diff --git a/Modules/_sqlite/connection.h b/Modules/_sqlite/connection.h index 206085e00a00c..aadf439034fe2 100644 --- a/Modules/_sqlite/connection.h +++ b/Modules/_sqlite/connection.h @@ -106,7 +106,7 @@ typedef struct PyObject* NotSupportedError; } pysqlite_Connection; -extern PyTypeObject pysqlite_ConnectionType; +extern PyTypeObject *pysqlite_ConnectionType; PyObject* pysqlite_connection_alloc(PyTypeObject* type, int aware); void pysqlite_connection_dealloc(pysqlite_Connection* self); @@ -122,6 +122,6 @@ int pysqlite_connection_register_cursor(pysqlite_Connection* connection, PyObjec int pysqlite_check_thread(pysqlite_Connection* self); int pysqlite_check_connection(pysqlite_Connection* con); -int pysqlite_connection_setup_types(void); +int pysqlite_connection_setup_types(PyObject *module); #endif diff --git a/Modules/_sqlite/cursor.c b/Modules/_sqlite/cursor.c index dbbdb12caddd6..3c09c1c6b7e50 100644 --- a/Modules/_sqlite/cursor.c +++ b/Modules/_sqlite/cursor.c @@ -33,7 +33,7 @@ static int pysqlite_cursor_init(pysqlite_Cursor* self, PyObject* args, PyObject* { pysqlite_Connection* connection; - if (!PyArg_ParseTuple(args, "O!", &pysqlite_ConnectionType, &connection)) + if (!PyArg_ParseTuple(args, "O!", pysqlite_ConnectionType, &connection)) { return -1; } @@ -74,6 +74,8 @@ static int pysqlite_cursor_init(pysqlite_Cursor* self, PyObject* args, PyObject* static void pysqlite_cursor_dealloc(pysqlite_Cursor* self) { + PyTypeObject *tp = Py_TYPE(self); + /* Reset the statement if the user has not closed the cursor */ if (self->statement) { pysqlite_statement_reset(self->statement); @@ -91,7 +93,8 @@ static void pysqlite_cursor_dealloc(pysqlite_Cursor* self) PyObject_ClearWeakRefs((PyObject*)self); } - Py_TYPE(self)->tp_free((PyObject*)self); + tp->tp_free(self); + Py_DECREF(tp); } static PyObject * @@ -898,56 +901,39 @@ static struct PyMemberDef cursor_members[] = {"lastrowid", T_OBJECT, offsetof(pysqlite_Cursor, lastrowid), READONLY}, {"rowcount", T_LONG, offsetof(pysqlite_Cursor, rowcount), READONLY}, {"row_factory", T_OBJECT, offsetof(pysqlite_Cursor, row_factory), 0}, + {"__weaklistoffset__", T_PYSSIZET, offsetof(pysqlite_Cursor, in_weakreflist), READONLY}, {NULL} }; static const char cursor_doc[] = PyDoc_STR("SQLite database cursor class."); -PyTypeObject pysqlite_CursorType = { - PyVarObject_HEAD_INIT(NULL, 0) - MODULE_NAME ".Cursor", /* tp_name */ - sizeof(pysqlite_Cursor), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)pysqlite_cursor_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /* tp_flags */ - cursor_doc, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - offsetof(pysqlite_Cursor, in_weakreflist), /* tp_weaklistoffset */ - PyObject_SelfIter, /* tp_iter */ - (iternextfunc)pysqlite_cursor_iternext, /* tp_iternext */ - cursor_methods, /* tp_methods */ - cursor_members, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)pysqlite_cursor_init, /* tp_init */ - 0, /* tp_alloc */ - 0, /* tp_new */ - 0 /* tp_free */ +static PyType_Slot cursor_slots[] = { + {Py_tp_dealloc, pysqlite_cursor_dealloc}, + {Py_tp_doc, (void *)cursor_doc}, + {Py_tp_iter, PyObject_SelfIter}, + {Py_tp_iternext, pysqlite_cursor_iternext}, + {Py_tp_methods, cursor_methods}, + {Py_tp_members, cursor_members}, + {Py_tp_new, PyType_GenericNew}, + {Py_tp_init, pysqlite_cursor_init}, + {0, NULL}, +}; + +static PyType_Spec cursor_spec = { + .name = MODULE_NAME ".Cursor", + .basicsize = sizeof(pysqlite_Cursor), + .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + .slots = cursor_slots, }; -extern int pysqlite_cursor_setup_types(void) +PyTypeObject *pysqlite_CursorType = NULL; + +extern int pysqlite_cursor_setup_types(PyObject *module) { - pysqlite_CursorType.tp_new = PyType_GenericNew; - return PyType_Ready(&pysqlite_CursorType); + pysqlite_CursorType = (PyTypeObject *)PyType_FromModuleAndSpec(module, &cursor_spec, NULL); + if (pysqlite_CursorType == NULL) { + return -1; + } + return 0; } diff --git a/Modules/_sqlite/cursor.h b/Modules/_sqlite/cursor.h index 4a20e756f7829..3e6cde167f94c 100644 --- a/Modules/_sqlite/cursor.h +++ b/Modules/_sqlite/cursor.h @@ -52,7 +52,7 @@ typedef struct PyObject* in_weakreflist; /* List of weak references */ } pysqlite_Cursor; -extern PyTypeObject pysqlite_CursorType; +extern PyTypeObject *pysqlite_CursorType; PyObject* pysqlite_cursor_execute(pysqlite_Cursor* self, PyObject* args); PyObject* pysqlite_cursor_executemany(pysqlite_Cursor* self, PyObject* args); @@ -64,7 +64,7 @@ PyObject* pysqlite_cursor_fetchall(pysqlite_Cursor* self, PyObject* args); PyObject* pysqlite_noop(pysqlite_Connection* self, PyObject* args); PyObject* pysqlite_cursor_close(pysqlite_Cursor* self, PyObject* args); -int pysqlite_cursor_setup_types(void); +int pysqlite_cursor_setup_types(PyObject *module); #define UNKNOWN (-1) #endif diff --git a/Modules/_sqlite/module.c b/Modules/_sqlite/module.c index 8ac9ea8477363..102026663abd8 100644 --- a/Modules/_sqlite/module.c +++ b/Modules/_sqlite/module.c @@ -82,7 +82,7 @@ static PyObject* module_connect(PyObject* self, PyObject* args, PyObject* } if (factory == NULL) { - factory = (PyObject*)&pysqlite_ConnectionType; + factory = (PyObject*)pysqlite_ConnectionType; } if (PySys_Audit("sqlite3.connect", "O", database) < 0) { @@ -353,8 +353,8 @@ PyMODINIT_FUNC PyInit__sqlite3(void) if (!module || (pysqlite_row_setup_types(module) < 0) || - (pysqlite_cursor_setup_types() < 0) || - (pysqlite_connection_setup_types() < 0) || + (pysqlite_cursor_setup_types(module) < 0) || + (pysqlite_connection_setup_types(module) < 0) || (pysqlite_cache_setup_types(module) < 0) || (pysqlite_statement_setup_types(module) < 0) || (pysqlite_prepare_protocol_setup_types(module) < 0) @@ -363,8 +363,8 @@ PyMODINIT_FUNC PyInit__sqlite3(void) return NULL; } - ADD_TYPE(module, pysqlite_ConnectionType); - ADD_TYPE(module, pysqlite_CursorType); + ADD_TYPE(module, *pysqlite_ConnectionType); + ADD_TYPE(module, *pysqlite_CursorType); ADD_TYPE(module, *pysqlite_PrepareProtocolType); ADD_TYPE(module, *pysqlite_RowType); diff --git a/Modules/_sqlite/row.c b/Modules/_sqlite/row.c index 85179e7f4df05..76b6f04f0ccbf 100644 --- a/Modules/_sqlite/row.c +++ b/Modules/_sqlite/row.c @@ -49,7 +49,7 @@ pysqlite_row_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) if (!PyArg_ParseTuple(args, "OO", &cursor, &data)) return NULL; - if (!PyObject_TypeCheck((PyObject*)cursor, &pysqlite_CursorType)) { + if (!PyObject_TypeCheck((PyObject*)cursor, pysqlite_CursorType)) { PyErr_SetString(PyExc_TypeError, "instance of cursor required for first argument"); return NULL; } From webhook-mailer at python.org Thu Oct 1 12:30:18 2020 From: webhook-mailer at python.org (Robert Smallshire) Date: Thu, 01 Oct 2020 16:30:18 -0000 Subject: [Python-checkins] bpo-26680: Incorporate is_integer in all built-in and standard library numeric types (GH-6121) Message-ID: https://github.com/python/cpython/commit/58a7da9e125422323f79c4ee95ac5549989d8162 commit: 58a7da9e125422323f79c4ee95ac5549989d8162 branch: master author: Robert Smallshire committer: GitHub date: 2020-10-01T17:30:08+01:00 summary: bpo-26680: Incorporate is_integer in all built-in and standard library numeric types (GH-6121) * bpo-26680: Adds support for int.is_integer() for compatibility with float.is_integer(). The int.is_integer() method always returns True. * bpo-26680: Adds a test to ensure that False.is_integer() and True.is_integer() are always True. * bpo-26680: Adds Real.is_integer() with a trivial implementation using conversion to int. This default implementation is intended to reduce the workload for subclass implementers. It is not robust in the presence of infinities or NaNs and may have suboptimal performance for other types. * bpo-26680: Adds Rational.is_integer which returns True if the denominator is one. This implementation assumes the Rational is represented in it's lowest form, as required by the class docstring. * bpo-26680: Adds Integral.is_integer which always returns True. * bpo-26680: Adds tests for Fraction.is_integer called as an instance method. The tests for the Rational abstract base class use an unbound method to sidestep the inability to directly instantiate Rational. These tests check that everything works correct as an instance method. * bpo-26680: Updates documentation for Real.is_integer and built-ins int and float. The call x.is_integer() is now listed in the table of operations which apply to all numeric types except complex, with a reference to the full documentation for Real.is_integer(). Mention of is_integer() has been removed from the section 'Additional Methods on Float'. The documentation for Real.is_integer() describes its purpose, and mentions that it should be overridden for performance reasons, or to handle special values like NaN. * bpo-26680: Adds Decimal.is_integer to the Python and C implementations. The C implementation of Decimal already implements and uses mpd_isinteger internally, we just expose the existing function to Python. The Python implementation uses internal conversion to integer using to_integral_value(). In both cases, the corresponding context methods are also implemented. Tests and documentation are included. * bpo-26680: Updates the ACKS file. * bpo-26680: NEWS entries for int, the numeric ABCs and Decimal. Co-authored-by: Robert Smallshire files: A Misc/NEWS.d/next/Core and Builtins/2018-03-15-11-51-36.bpo-26680.wOWYps.rst A Misc/NEWS.d/next/Library/2018-03-15-11-55-04.bpo-26680.eKAi85.rst A Misc/NEWS.d/next/Library/2018-03-15-11-56-48.bpo-26680.Udkhn4.rst M Doc/library/decimal.rst M Doc/library/numbers.rst M Doc/library/stdtypes.rst M Lib/_pydecimal.py M Lib/numbers.py M Lib/test/decimaltestdata/extra.decTest M Lib/test/test_bool.py M Lib/test/test_decimal.py M Lib/test/test_fractions.py M Lib/test/test_long.py M Lib/test/test_numeric_tower.py M Misc/ACKS M Modules/_decimal/_decimal.c M Modules/_decimal/docstrings.h M Objects/clinic/longobject.c.h M Objects/longobject.c diff --git a/Doc/library/decimal.rst b/Doc/library/decimal.rst index e194649e30d85..7a6497305952f 100644 --- a/Doc/library/decimal.rst +++ b/Doc/library/decimal.rst @@ -621,6 +621,13 @@ Decimal objects Return :const:`True` if the argument is either positive or negative infinity and :const:`False` otherwise. + .. method:: is_integer() + + Return :const:`True` if the argument is a finite integral value and + :const:`False` otherwise. + + .. versionadded:: 3.10 + .. method:: is_nan() Return :const:`True` if the argument is a (quiet or signaling) NaN and @@ -1215,6 +1222,13 @@ In addition to the three supplied contexts, new contexts can be created with the Returns ``True`` if *x* is infinite; otherwise returns ``False``. + .. method:: is_integer(x) + + Returns ``True`` if *x* is finite and integral; otherwise + returns ``False``. + + .. versionadded:: 3.10 + .. method:: is_nan(x) Returns ``True`` if *x* is a qNaN or sNaN; otherwise returns ``False``. diff --git a/Doc/library/numbers.rst b/Doc/library/numbers.rst index 1b594952ead72..5d49f5eb96b7a 100644 --- a/Doc/library/numbers.rst +++ b/Doc/library/numbers.rst @@ -49,19 +49,30 @@ The numeric tower numbers. In short, those are: a conversion to :class:`float`, :func:`math.trunc`, - :func:`round`, :func:`math.floor`, :func:`math.ceil`, :func:`divmod`, ``//``, - ``%``, ``<``, ``<=``, ``>``, and ``>=``. + :func:`round`, :func:`math.floor`, :func:`math.ceil`, :func:`divmod`, + :func:`~Real.is_integer`, ``//``, ``%``, ``<``, ``<=``, ``>``, and ``>=``. Real also provides defaults for :func:`complex`, :attr:`~Complex.real`, :attr:`~Complex.imag`, and :meth:`~Complex.conjugate`. + .. method:: is_integer() + + Returns :const:`True` if this number has a finite and integral value, + otherwise :const:`False`. This is a default implementation which + relies on successful conversion to :class:`int`. It may be overridden + in subclasses (such as it is in :class:`float`) for better performance, + or to handle special values such as NaN which are not + convertible to :class:`int`. + + .. versionadded:: 3.10 + .. class:: Rational Subtypes :class:`Real` and adds :attr:`~Rational.numerator` and :attr:`~Rational.denominator` properties, which - should be in lowest terms. With these, it provides a default for - :func:`float`. + should be in lowest terms. With these, it provides defaults for + :func:`float` and :func:`~Real.is_integer`. .. attribute:: numerator @@ -75,9 +86,10 @@ The numeric tower .. class:: Integral Subtypes :class:`Rational` and adds a conversion to :class:`int`. Provides - defaults for :func:`float`, :attr:`~Rational.numerator`, and - :attr:`~Rational.denominator`. Adds abstract methods for ``**`` and - bit-string operations: ``<<``, ``>>``, ``&``, ``^``, ``|``, ``~``. + defaults for :func:`float`, :attr:`~Rational.numerator`, + :attr:`~Rational.denominator`, and :func:`~Real.is_integer`. Adds abstract + methods for ``**`` and bit-string operations: ``<<``, ``>>``, ``&``, ``^``, + ``|``, ``~``. Notes for type implementors diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 0ffe7b7526fa7..62f39da2a72a2 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -310,6 +310,10 @@ the operations, see :ref:`operator-summary`): +---------------------+---------------------------------+---------+--------------------+ | ``x ** y`` | *x* to the power *y* | \(5) | | +---------------------+---------------------------------+---------+--------------------+ +| ``x.is_integer()`` | ``True`` if *x* has a finite | | :func:`~numbers\ | +| | and integral value, otherwise | | .Real.is_integer` | +| | ``False``. | | | ++---------------------+---------------------------------+---------+--------------------+ .. index:: triple: operations on; numeric; types @@ -583,16 +587,6 @@ class`. float also has the following additional methods. :exc:`OverflowError` on infinities and a :exc:`ValueError` on NaNs. -.. method:: float.is_integer() - - Return ``True`` if the float instance is finite with integral - value, and ``False`` otherwise:: - - >>> (-2.0).is_integer() - True - >>> (3.2).is_integer() - False - Two methods support conversion to and from hexadecimal strings. Since Python's floats are stored internally as binary numbers, converting a float to or from a diff --git a/Lib/_pydecimal.py b/Lib/_pydecimal.py index ab989e5206a9e..8c0ef57092219 100644 --- a/Lib/_pydecimal.py +++ b/Lib/_pydecimal.py @@ -3164,6 +3164,12 @@ def is_zero(self): """Return True if self is a zero; otherwise return False.""" return not self._is_special and self._int == '0' + def is_integer(self): + """Return True is self is finite and integral; otherwise False.""" + if self._is_special: + return False + return self.to_integral_value(rounding=ROUND_FLOOR) == self + def _ln_exp_bound(self): """Compute a lower bound for the adjusted exponent of self.ln(). In other words, compute r such that self.ln() >= 10**r. Assumes @@ -4659,6 +4665,25 @@ def is_zero(self, a): a = _convert_other(a, raiseit=True) return a.is_zero() + def is_integer(self, a): + """Return True if the operand is integral; otherwise return False. + + >>> ExtendedContext.is_integer(Decimal('0')) + True + >>> ExtendedContext.is_integer(Decimal('2.50')) + False + >>> ExtendedContext.is_integer(Decimal('-0E+2')) + True + >>> ExtendedContext.is_integer(Decimal('-0.5')) + False + >>> ExtendedContext.is_integer(Decimal('NaN')) + False + >>> ExtendedContext.is_integer(10) + True + """ + a = _convert_other(a, raiseit=True) + return a.is_integer() + def ln(self, a): """Returns the natural (base e) logarithm of the operand. diff --git a/Lib/numbers.py b/Lib/numbers.py index ed815ef41ebe1..0634f62ff123c 100644 --- a/Lib/numbers.py +++ b/Lib/numbers.py @@ -148,7 +148,7 @@ class Real(Complex): """To Complex, Real adds the operations that work on real numbers. In short, those are: a conversion to float, trunc(), divmod, - %, <, <=, >, and >=. + is_integer, %, <, <=, >, and >=. Real also provides defaults for the derived operations. """ @@ -242,6 +242,17 @@ def __le__(self, other): """self <= other""" raise NotImplementedError + def is_integer(self): + """Return True if the Real is integral; otherwise return False. + + This default implementation can be overridden in subclasses + for performance reasons or to deal with values such as NaN, + which would otherwise cause an exception to be raised. + """ + # Although __int__ is not defined at this level, the int + # constructor falls back to __trunc__, which we do have. + return self == int(self) + # Concrete implementations of Complex abstract methods. def __complex__(self): """complex(self) == complex(float(self), 0)""" @@ -290,6 +301,10 @@ def __float__(self): """ return self.numerator / self.denominator + def is_integer(self): + """Return True if the Rational is integral; otherwise return False.""" + return self.denominator == 1 + class Integral(Rational): """Integral adds a conversion to int and the bit-string operations.""" @@ -386,4 +401,8 @@ def denominator(self): """Integers have a denominator of 1.""" return 1 + def is_integer(self): + """Return True; all Integrals represent an integral value.""" + return True + Integral.register(int) diff --git a/Lib/test/decimaltestdata/extra.decTest b/Lib/test/decimaltestdata/extra.decTest index b630d8e3f9d45..2f0719ed22342 100644 --- a/Lib/test/decimaltestdata/extra.decTest +++ b/Lib/test/decimaltestdata/extra.decTest @@ -2346,6 +2346,24 @@ bool2096 iszero sNaN -> 0 bool2097 iszero -sNaN -> 0 bool2098 iszero sNaN123 -> 0 bool2099 iszero -sNaN123 -> 0 +bool2100 is_integer -1.0 -> 1 +bool2101 is_integer 0.0 -> 1 +bool2102 is_integer 1.0 -> 1 +bool2103 is_integer 42 -> 1 +bool2104 is_integer 1e2 -> 1 +bool2105 is_integer 1.5 -> 0 +bool2106 is_integer 1e-2 -> 0 +bool2107 is_integer NaN -> 0 +bool2109 is_integer -NaN -> 0 +bool2110 is_integer NaN123 -> 0 +bool2111 is_integer -NaN123 -> 0 +bool2112 is_integer sNaN -> 0 +bool2113 is_integer -sNaN -> 0 +bool2114 is_integer sNaN123 -> 0 +bool2115 is_integer -sNaN123 -> 0 +bool2116 is_integer Infinity -> 0 +bool2117 is_integer -Infinity -> 0 + ------------------------------------------------------------------------ -- The following tests (pwmx0 through pwmx440) are for the -- diff --git a/Lib/test/test_bool.py b/Lib/test/test_bool.py index 7b3a3859e0893..bc201e10ff267 100644 --- a/Lib/test/test_bool.py +++ b/Lib/test/test_bool.py @@ -354,6 +354,11 @@ def test_real_and_imag(self): self.assertIs(type(False.real), int) self.assertIs(type(False.imag), int) + def test_always_is_integer(self): + # Issue #26680: Incorporating number.is_integer into bool + self.assertTrue(all(b.is_integer() for b in (False, True))) + + def test_main(): support.run_unittest(BoolTest) diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py index dbd58e8a6519b..efb41fd465056 100644 --- a/Lib/test/test_decimal.py +++ b/Lib/test/test_decimal.py @@ -276,6 +276,7 @@ def setUp(self): 'is_snan', 'is_subnormal', 'is_zero', + 'is_integer', 'same_quantum') def read_unlimited(self, v, context): @@ -2726,6 +2727,7 @@ def test_named_parameters(self): self.assertRaises(TypeError, D(1).is_snan, context=xc) self.assertRaises(TypeError, D(1).is_signed, context=xc) self.assertRaises(TypeError, D(1).is_zero, context=xc) + self.assertRaises(TypeError, D(1).is_integer, context=xc) self.assertFalse(D("0.01").is_normal(context=xc)) self.assertTrue(D("0.01").is_subnormal(context=xc)) @@ -3197,6 +3199,15 @@ def test_is_zero(self): self.assertEqual(c.is_zero(10), d) self.assertRaises(TypeError, c.is_zero, '10') + def test_is_integer(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + b = c.is_integer(Decimal(10)) + self.assertEqual(c.is_integer(10), b) + self.assertRaises(TypeError, c.is_integer, '10') + def test_ln(self): Decimal = self.decimal.Decimal Context = self.decimal.Context @@ -4360,6 +4371,19 @@ def test_implicit_context(self): self.assertTrue(Decimal("-1").is_signed()) self.assertTrue(Decimal("0").is_zero()) self.assertTrue(Decimal("0").is_zero()) + self.assertTrue(Decimal("-1").is_integer()) + self.assertTrue(Decimal("0").is_integer()) + self.assertTrue(Decimal("1").is_integer()) + self.assertTrue(Decimal("42").is_integer()) + self.assertTrue(Decimal("1e2").is_integer()) + self.assertFalse(Decimal("1.5").is_integer()) + self.assertFalse(Decimal("1e-2").is_integer()) + self.assertFalse(Decimal("NaN").is_integer()) + self.assertFalse(Decimal("-NaN").is_integer()) + self.assertFalse(Decimal("sNaN").is_integer()) + self.assertFalse(Decimal("-sNaN").is_integer()) + self.assertFalse(Decimal("Inf").is_integer()) + self.assertFalse(Decimal("-Inf").is_integer()) # Copy with localcontext() as c: diff --git a/Lib/test/test_fractions.py b/Lib/test/test_fractions.py index 0845f7921c39e..811b58fd8f56a 100644 --- a/Lib/test/test_fractions.py +++ b/Lib/test/test_fractions.py @@ -724,6 +724,17 @@ def denominator(self): self.assertEqual(type(f.numerator), myint) self.assertEqual(type(f.denominator), myint) + def test_is_integer(self): + # Issue #26680: Incorporating number.is_integer into Fraction + self.assertTrue(F(-1, 1).is_integer()) + self.assertTrue(F(0, 1).is_integer()) + self.assertTrue(F(1, 1).is_integer()) + self.assertTrue(F(42, 1).is_integer()) + self.assertTrue(F(2, 2).is_integer()) + self.assertTrue(F(8, 4).is_integer()) + self.assertFalse(F(1, 2).is_integer()) + self.assertFalse(F(1, 3).is_integer()) + self.assertFalse(F(2, 3).is_integer()) if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_long.py b/Lib/test/test_long.py index c97842b5bfd23..669826c0fa3c1 100644 --- a/Lib/test/test_long.py +++ b/Lib/test/test_long.py @@ -1381,6 +1381,10 @@ class myint(int): self.assertEqual(type(numerator), int) self.assertEqual(type(denominator), int) + def test_int_always_is_integer(self): + # Issue #26680: Incorporating number.is_integer into int + self.assertTrue(all(x.is_integer() for x in (-1, 0, 1, 42))) + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_numeric_tower.py b/Lib/test/test_numeric_tower.py index c54dedb8b793a..4e46aacad82b6 100644 --- a/Lib/test/test_numeric_tower.py +++ b/Lib/test/test_numeric_tower.py @@ -6,6 +6,7 @@ import sys import operator +from numbers import Real, Rational, Integral from decimal import Decimal as D from fractions import Fraction as F @@ -198,5 +199,35 @@ def test_complex(self): self.assertRaises(TypeError, op, v, z) +class IsIntegerTest(unittest.TestCase): + + def test_real_is_integer(self): + self.assertTrue(Real.is_integer(-1.0)) + self.assertTrue(Real.is_integer(0.0)) + self.assertTrue(Real.is_integer(1.0)) + self.assertTrue(Real.is_integer(42.0)) + + self.assertFalse(Real.is_integer(-0.5)) + self.assertFalse(Real.is_integer(4.2)) + + def test_rational_is_integer(self): + self.assertTrue(Rational.is_integer(F(-1, 1))) + self.assertTrue(Rational.is_integer(F(0, 1))) + self.assertTrue(Rational.is_integer(F(1, 1))) + self.assertTrue(Rational.is_integer(F(42, 1))) + self.assertTrue(Rational.is_integer(F(2, 2))) + self.assertTrue(Rational.is_integer(F(8, 4))) + + self.assertFalse(Rational.is_integer(F(1, 2))) + self.assertFalse(Rational.is_integer(F(1, 3))) + self.assertFalse(Rational.is_integer(F(2, 3))) + + def test_integral_is_integer(self): + self.assertTrue(Integral.is_integer(-1)) + self.assertTrue(Integral.is_integer(0)) + self.assertTrue(Integral.is_integer(1)) + self.assertTrue(Integral.is_integer(1729)) + + if __name__ == '__main__': unittest.main() diff --git a/Misc/ACKS b/Misc/ACKS index 85001daf67d23..9be0e777ca294 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1611,6 +1611,7 @@ Roman Skurikhin Ville Skytt? Michael Sloan Nick Sloan +Robert Smallshire V?clav ?milauer Allen W. Smith Christopher Smith diff --git a/Misc/NEWS.d/next/Core and Builtins/2018-03-15-11-51-36.bpo-26680.wOWYps.rst b/Misc/NEWS.d/next/Core and Builtins/2018-03-15-11-51-36.bpo-26680.wOWYps.rst new file mode 100644 index 0000000000000..93325ffffcbfc --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2018-03-15-11-51-36.bpo-26680.wOWYps.rst @@ -0,0 +1,2 @@ +The int type now supports the x.is_integer() method for compatibility with +float. diff --git a/Misc/NEWS.d/next/Library/2018-03-15-11-55-04.bpo-26680.eKAi85.rst b/Misc/NEWS.d/next/Library/2018-03-15-11-55-04.bpo-26680.eKAi85.rst new file mode 100644 index 0000000000000..8b2e818383041 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-03-15-11-55-04.bpo-26680.eKAi85.rst @@ -0,0 +1,3 @@ +The x.is_integer() method is incorporated into the abstract types of the +numeric tower, Real, Rational and Integral, with appropriate default +implementations. diff --git a/Misc/NEWS.d/next/Library/2018-03-15-11-56-48.bpo-26680.Udkhn4.rst b/Misc/NEWS.d/next/Library/2018-03-15-11-56-48.bpo-26680.Udkhn4.rst new file mode 100644 index 0000000000000..df75e080fa6ee --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-03-15-11-56-48.bpo-26680.Udkhn4.rst @@ -0,0 +1,2 @@ +The d.is_integer() method is added to the Decimal type, for compatibility +with other number types. diff --git a/Modules/_decimal/_decimal.c b/Modules/_decimal/_decimal.c index e7c44acba02fc..5200b1a48e4bf 100644 --- a/Modules/_decimal/_decimal.c +++ b/Modules/_decimal/_decimal.c @@ -4138,6 +4138,7 @@ Dec_BoolFunc(mpd_isqnan) Dec_BoolFunc(mpd_issnan) Dec_BoolFunc(mpd_issigned) Dec_BoolFunc(mpd_iszero) +Dec_BoolFunc(mpd_isinteger) /* Boolean functions, optional context arg */ Dec_BoolFuncVA(mpd_isnormal) @@ -4772,6 +4773,7 @@ static PyMethodDef dec_methods [] = { "is_snan", dec_mpd_issnan, METH_NOARGS, doc_is_snan }, { "is_signed", dec_mpd_issigned, METH_NOARGS, doc_is_signed }, { "is_zero", dec_mpd_iszero, METH_NOARGS, doc_is_zero }, + { "is_integer", dec_mpd_isinteger, METH_NOARGS, doc_is_integer}, /* Boolean functions, optional context arg */ { "is_normal", (PyCFunction)(void(*)(void))dec_mpd_isnormal, METH_VARARGS|METH_KEYWORDS, doc_is_normal }, @@ -5183,6 +5185,7 @@ DecCtx_BoolFunc_NO_CTX(mpd_isqnan) DecCtx_BoolFunc_NO_CTX(mpd_issigned) DecCtx_BoolFunc_NO_CTX(mpd_issnan) DecCtx_BoolFunc_NO_CTX(mpd_iszero) +DecCtx_BoolFunc_NO_CTX(mpd_isinteger) static PyObject * ctx_iscanonical(PyObject *context UNUSED, PyObject *v) @@ -5464,6 +5467,7 @@ static PyMethodDef context_methods [] = { "is_snan", ctx_mpd_issnan, METH_O, doc_ctx_is_snan }, { "is_subnormal", ctx_mpd_issubnormal, METH_O, doc_ctx_is_subnormal }, { "is_zero", ctx_mpd_iszero, METH_O, doc_ctx_is_zero }, + { "is_integer", ctx_mpd_isinteger, METH_O, doc_ctx_is_integer }, /* Functions with a single decimal argument */ { "_apply", PyDecContext_Apply, METH_O, NULL }, /* alias for apply */ @@ -6097,5 +6101,3 @@ PyInit__decimal(void) return NULL; /* GCOV_NOT_REACHED */ } - - diff --git a/Modules/_decimal/docstrings.h b/Modules/_decimal/docstrings.h index f7fd6e7952998..bd602ab278e0e 100644 --- a/Modules/_decimal/docstrings.h +++ b/Modules/_decimal/docstrings.h @@ -260,6 +260,11 @@ Return True if the argument is a (positive or negative) zero and False\n\ otherwise.\n\ \n"); +PyDoc_STRVAR(doc_is_integer, +"is_integer($self, /)\n--\n\n\ +Return True if the argument is finite and integral, otherwise False.\n\ +\n"); + PyDoc_STRVAR(doc_ln, "ln($self, /, context=None)\n--\n\n\ Return the natural (base e) logarithm of the operand. The function always\n\ @@ -685,6 +690,11 @@ PyDoc_STRVAR(doc_ctx_is_zero, Return True if x is a zero, False otherwise.\n\ \n"); +PyDoc_STRVAR(doc_ctx_is_integer, +"is_integer($self, x, /)\n--\n\n\ ++Return True if x is finite and integral, False otherwise.\n\ ++\n"); + PyDoc_STRVAR(doc_ctx_ln, "ln($self, x, /)\n--\n\n\ Return the natural (base e) logarithm of x.\n\ @@ -879,6 +889,3 @@ Convert a number to a string using scientific notation.\n\ #endif /* DOCSTRINGS_H */ - - - diff --git a/Objects/clinic/longobject.c.h b/Objects/clinic/longobject.c.h index 4bd47b116f883..16e6f7e619e87 100644 --- a/Objects/clinic/longobject.c.h +++ b/Objects/clinic/longobject.c.h @@ -121,6 +121,24 @@ int___round__(PyObject *self, PyObject *const *args, Py_ssize_t nargs) return return_value; } +PyDoc_STRVAR(int_is_integer__doc__, +"is_integer($self, /)\n" +"--\n" +"\n" +"Returns True for all integers."); + +#define INT_IS_INTEGER_METHODDEF \ + {"is_integer", (PyCFunction)int_is_integer, METH_NOARGS, int_is_integer__doc__}, + +static PyObject * +int_is_integer_impl(PyObject *self); + +static PyObject * +int_is_integer(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return int_is_integer_impl(self); +} + PyDoc_STRVAR(int___sizeof____doc__, "__sizeof__($self, /)\n" "--\n" @@ -367,4 +385,4 @@ int_from_bytes(PyTypeObject *type, PyObject *const *args, Py_ssize_t nargs, PyOb exit: return return_value; } -/*[clinic end generated code: output=ea18e51af5b53591 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=022614978e2fcdf3 input=a9049054013a1b77]*/ diff --git a/Objects/longobject.c b/Objects/longobject.c index 92514d4154e2c..bc5b49dcf8b56 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -5233,6 +5233,19 @@ int___round___impl(PyObject *self, PyObject *o_ndigits) return result; } +/*[clinic input] +int.is_integer + +Returns True for all integers. +[clinic start generated code]*/ + +static PyObject * +int_is_integer_impl(PyObject *self) +/*[clinic end generated code: output=90f8e794ce5430ef input=1c1a86957301d26d]*/ +{ + Py_RETURN_TRUE; +} + /*[clinic input] int.__sizeof__ -> Py_ssize_t @@ -5547,6 +5560,7 @@ static PyMethodDef long_methods[] = { {"__ceil__", long_long_meth, METH_NOARGS, "Ceiling of an Integral returns itself."}, INT___ROUND___METHODDEF + INT_IS_INTEGER_METHODDEF INT___GETNEWARGS___METHODDEF INT___FORMAT___METHODDEF INT___SIZEOF___METHODDEF From webhook-mailer at python.org Thu Oct 1 12:57:45 2020 From: webhook-mailer at python.org (Victor Stinner) Date: Thu, 01 Oct 2020 16:57:45 -0000 Subject: [Python-checkins] bpo-21955: Change my nickname in BINARY_ADD comment (GH-22481) Message-ID: https://github.com/python/cpython/commit/bd0a08ea90e4c7a2ebf29697937e9786d4d8e5ee commit: bd0a08ea90e4c7a2ebf29697937e9786d4d8e5ee branch: master author: Victor Stinner committer: GitHub date: 2020-10-01T18:57:37+02:00 summary: bpo-21955: Change my nickname in BINARY_ADD comment (GH-22481) files: M Python/ceval.c diff --git a/Python/ceval.c b/Python/ceval.c index 6bd2d6bc13d86..7c6cf83bc9ac0 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1701,7 +1701,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) PyObject *right = POP(); PyObject *left = TOP(); PyObject *sum; - /* NOTE(haypo): Please don't try to micro-optimize int+int on + /* NOTE(vstinner): Please don't try to micro-optimize int+int on CPython using bytecode, it is simply worthless. See http://bugs.python.org/issue21955 and http://bugs.python.org/issue10044 for the discussion. In short, From webhook-mailer at python.org Thu Oct 1 19:22:24 2020 From: webhook-mailer at python.org (Andre Delfino) Date: Thu, 01 Oct 2020 23:22:24 -0000 Subject: [Python-checkins] [doc] Update references to NumPy (GH-22458) Message-ID: https://github.com/python/cpython/commit/c8bb24166e367d449158015cb9b1093f03c7175d commit: c8bb24166e367d449158015cb9b1093f03c7175d branch: master author: Andre Delfino committer: GitHub date: 2020-10-01T16:22:14-07:00 summary: [doc] Update references to NumPy (GH-22458) Numeric(al) Python to NumPy. It seems the old name hasn't been used for some time. files: M Doc/faq/programming.rst M Doc/library/array.rst M Doc/library/functions.rst M Doc/tutorial/floatingpoint.rst diff --git a/Doc/faq/programming.rst b/Doc/faq/programming.rst index 76ae4d260fad4..0b486d7e7e254 100644 --- a/Doc/faq/programming.rst +++ b/Doc/faq/programming.rst @@ -1191,7 +1191,7 @@ difference is that a Python list can contain objects of many different types. The ``array`` module also provides methods for creating arrays of fixed types with compact representations, but they are slower to index than lists. Also -note that the Numeric extensions and others define array-like structures with +note that NumPy and other third party packages define array-like structures with various characteristics as well. To get Lisp-style linked lists, you can emulate cons cells using tuples:: diff --git a/Doc/library/array.rst b/Doc/library/array.rst index 78020738bf4f7..ff3ec6b1fd723 100644 --- a/Doc/library/array.rst +++ b/Doc/library/array.rst @@ -257,7 +257,6 @@ Examples:: Packing and unpacking of External Data Representation (XDR) data as used in some remote procedure call systems. - `The Numerical Python Documentation `_ - The Numeric Python extension (NumPy) defines another array type; see - http://www.numpy.org/ for further information about Numerical Python. + `NumPy `_ + The NumPy package defines another array type. diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index 7543fc4b10d46..c49bb0c9de70c 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -1512,14 +1512,12 @@ are always available. They are listed here in alphabetical order. .. class:: slice(stop) slice(start, stop[, step]) - .. index:: single: Numerical Python - Return a :term:`slice` object representing the set of indices specified by ``range(start, stop, step)``. The *start* and *step* arguments default to ``None``. Slice objects have read-only data attributes :attr:`~slice.start`, :attr:`~slice.stop` and :attr:`~slice.step` which merely return the argument values (or their default). They have no other explicit functionality; - however they are used by Numerical Python and other third party extensions. + however they are used by NumPy and other third party packages. Slice objects are also generated when extended indexing syntax is used. For example: ``a[start:stop:step]`` or ``a[start:stop, i]``. See :func:`itertools.islice` for an alternate version that returns an iterator. diff --git a/Doc/tutorial/floatingpoint.rst b/Doc/tutorial/floatingpoint.rst index 0c0eb526fa9ed..b98de6e56a003 100644 --- a/Doc/tutorial/floatingpoint.rst +++ b/Doc/tutorial/floatingpoint.rst @@ -158,7 +158,7 @@ which implements arithmetic based on rational numbers (so the numbers like 1/3 can be represented exactly). If you are a heavy user of floating point operations you should take a look -at the Numerical Python package and many other packages for mathematical and +at the NumPy package and many other packages for mathematical and statistical operations supplied by the SciPy project. See . Python provides tools that may help on those rare occasions when you really From webhook-mailer at python.org Thu Oct 1 22:31:00 2020 From: webhook-mailer at python.org (Raymond Hettinger) Date: Fri, 02 Oct 2020 02:31:00 -0000 Subject: [Python-checkins] Update link to supporting references (GH-22488) Message-ID: https://github.com/python/cpython/commit/497126f7ea955ee005e78f2cdd61f175d4fcdbb2 commit: 497126f7ea955ee005e78f2cdd61f175d4fcdbb2 branch: master author: Raymond Hettinger committer: GitHub date: 2020-10-01T19:30:54-07:00 summary: Update link to supporting references (GH-22488) files: M Modules/mathmodule.c diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 5cd31b7dd4acf..45b03028753a3 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -2494,7 +2494,7 @@ exactly equal) was verified for 1 billion random inputs with n=5. [7] 3. Square root differential correction: https://arxiv.org/pdf/1904.09481.pdf 4. Data dependency graph: https://bugs.python.org/file49439/hypot.png 5. https://www.wolframalpha.com/input/?i=Maclaurin+series+sqrt%28h**2+%2B+x%29+at+x%3D0 -6. Analysis of internal accuracy: https://bugs.python.org/file49435/best_frac.py +6. Analysis of internal accuracy: https://bugs.python.org/file49484/best_frac.py 7. Commutativity test: https://bugs.python.org/file49448/test_hypot_commutativity.py */ From webhook-mailer at python.org Fri Oct 2 08:49:09 2020 From: webhook-mailer at python.org (Victor Stinner) Date: Fri, 02 Oct 2020 12:49:09 -0000 Subject: [Python-checkins] bpo-41692: Deprecate PyUnicode_InternImmortal() (GH-22486) Message-ID: https://github.com/python/cpython/commit/583ee5a5b1971a18ebeb877948ce6264da0cc8aa commit: 583ee5a5b1971a18ebeb877948ce6264da0cc8aa branch: master author: Victor Stinner committer: GitHub date: 2020-10-02T14:49:00+02:00 summary: bpo-41692: Deprecate PyUnicode_InternImmortal() (GH-22486) The PyUnicode_InternImmortal() function is now deprecated and will be removed in Python 3.12: use PyUnicode_InternInPlace() instead. files: A Misc/NEWS.d/next/C API/2020-10-02-00-57-34.bpo-41692.fDScsF.rst M Doc/whatsnew/3.10.rst M Include/unicodeobject.h M Objects/unicodeobject.c diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index f74dd1aa247a3..957a3e791ecb6 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -299,6 +299,14 @@ Porting to Python 3.10 Unicode object without initial data. (Contributed by Inada Naoki in :issue:`36346`.) +Deprecated +---------- + +* The ``PyUnicode_InternImmortal()`` function is now deprecated + and will be removed in Python 3.12: use :c:func:`PyUnicode_InternInPlace` + instead. + (Contributed by Victor Stinner in :issue:`41692`.) + Removed ------- diff --git a/Include/unicodeobject.h b/Include/unicodeobject.h index 500ce242e9f0e..90b3299fd26ce 100644 --- a/Include/unicodeobject.h +++ b/Include/unicodeobject.h @@ -261,11 +261,14 @@ PyAPI_FUNC(PyObject *) PyUnicode_FromFormat( ); PyAPI_FUNC(void) PyUnicode_InternInPlace(PyObject **); -PyAPI_FUNC(void) PyUnicode_InternImmortal(PyObject **); PyAPI_FUNC(PyObject *) PyUnicode_InternFromString( const char *u /* UTF-8 encoded string */ ); +// PyUnicode_InternImmortal() is deprecated since Python 3.10 +// and will be removed in Python 3.12. Use PyUnicode_InternInPlace() instead. +Py_DEPRECATED(3.10) PyAPI_FUNC(void) PyUnicode_InternImmortal(PyObject **); + /* Use only if you know it's a string */ #define PyUnicode_CHECK_INTERNED(op) \ (((PyASCIIObject *)(op))->state.interned) diff --git a/Misc/NEWS.d/next/C API/2020-10-02-00-57-34.bpo-41692.fDScsF.rst b/Misc/NEWS.d/next/C API/2020-10-02-00-57-34.bpo-41692.fDScsF.rst new file mode 100644 index 0000000000000..1be37c6572271 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2020-10-02-00-57-34.bpo-41692.fDScsF.rst @@ -0,0 +1,3 @@ +The ``PyUnicode_InternImmortal()`` function is now deprecated and will be +removed in Python 3.12: use :c:func:`PyUnicode_InternInPlace` instead. +Patch by Victor Stinner. diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index f32ab417c364c..cf72238a8d058 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -15764,6 +15764,15 @@ PyUnicode_InternInPlace(PyObject **p) void PyUnicode_InternImmortal(PyObject **p) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "PyUnicode_InternImmortal() is deprecated; " + "use PyUnicode_InternInPlace() instead", 1) < 0) + { + // The function has no return value, the exception cannot + // be reported to the caller, so just log it. + PyErr_WriteUnraisable(NULL); + } + PyUnicode_InternInPlace(p); if (PyUnicode_CHECK_INTERNED(*p) != SSTATE_INTERNED_IMMORTAL) { _PyUnicode_STATE(*p).interned = SSTATE_INTERNED_IMMORTAL; From webhook-mailer at python.org Fri Oct 2 12:18:12 2020 From: webhook-mailer at python.org (Campbell Barton) Date: Fri, 02 Oct 2020 16:18:12 -0000 Subject: [Python-checkins] bpo-41802: Document 'PyDict_DelItem' can raise a 'KeyError' (GH-22291) Message-ID: https://github.com/python/cpython/commit/20ce62f00957d11f24f6449cd5c0ef5dd67174d4 commit: 20ce62f00957d11f24f6449cd5c0ef5dd67174d4 branch: master author: Campbell Barton committer: GitHub date: 2020-10-02T19:18:01+03:00 summary: bpo-41802: Document 'PyDict_DelItem' can raise a 'KeyError' (GH-22291) files: M Doc/c-api/dict.rst diff --git a/Doc/c-api/dict.rst b/Doc/c-api/dict.rst index 769484134ed51..8e0d684546349 100644 --- a/Doc/c-api/dict.rst +++ b/Doc/c-api/dict.rst @@ -81,14 +81,16 @@ Dictionary Objects .. c:function:: int PyDict_DelItem(PyObject *p, PyObject *key) Remove the entry in dictionary *p* with key *key*. *key* must be hashable; - if it isn't, :exc:`TypeError` is raised. Return ``0`` on success or ``-1`` - on failure. + if it isn't, :exc:`TypeError` is raised. + If *key* is not in the dictionary, :exc:`KeyError` is raised. + Return ``0`` on success or ``-1`` on failure. .. c:function:: int PyDict_DelItemString(PyObject *p, const char *key) - Remove the entry in dictionary *p* which has a key specified by the string - *key*. Return ``0`` on success or ``-1`` on failure. + Remove the entry in dictionary *p* which has a key specified by the string *key*. + If *key* is not in the dictionary, :exc:`KeyError` is raised. + Return ``0`` on success or ``-1`` on failure. .. c:function:: PyObject* PyDict_GetItem(PyObject *p, PyObject *key) From webhook-mailer at python.org Fri Oct 2 12:28:05 2020 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Fri, 02 Oct 2020 16:28:05 -0000 Subject: [Python-checkins] bpo-41802: Document 'PyDict_DelItem' can raise a 'KeyError' (GH-22291) Message-ID: https://github.com/python/cpython/commit/ab32ea8d79d6bfb6580d35bfc4aa42d2729c0bcf commit: ab32ea8d79d6bfb6580d35bfc4aa42d2729c0bcf branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-02T09:27:38-07:00 summary: bpo-41802: Document 'PyDict_DelItem' can raise a 'KeyError' (GH-22291) (cherry picked from commit 20ce62f00957d11f24f6449cd5c0ef5dd67174d4) Co-authored-by: Campbell Barton files: M Doc/c-api/dict.rst diff --git a/Doc/c-api/dict.rst b/Doc/c-api/dict.rst index 30905271c396a..e910324ef8b5a 100644 --- a/Doc/c-api/dict.rst +++ b/Doc/c-api/dict.rst @@ -81,14 +81,16 @@ Dictionary Objects .. c:function:: int PyDict_DelItem(PyObject *p, PyObject *key) Remove the entry in dictionary *p* with key *key*. *key* must be hashable; - if it isn't, :exc:`TypeError` is raised. Return ``0`` on success or ``-1`` - on failure. + if it isn't, :exc:`TypeError` is raised. + If *key* is not in the dictionary, :exc:`KeyError` is raised. + Return ``0`` on success or ``-1`` on failure. .. c:function:: int PyDict_DelItemString(PyObject *p, const char *key) - Remove the entry in dictionary *p* which has a key specified by the string - *key*. Return ``0`` on success or ``-1`` on failure. + Remove the entry in dictionary *p* which has a key specified by the string *key*. + If *key* is not in the dictionary, :exc:`KeyError` is raised. + Return ``0`` on success or ``-1`` on failure. .. c:function:: PyObject* PyDict_GetItem(PyObject *p, PyObject *key) From webhook-mailer at python.org Fri Oct 2 12:39:25 2020 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Fri, 02 Oct 2020 16:39:25 -0000 Subject: [Python-checkins] bpo-41802: Document 'PyDict_DelItem' can raise a 'KeyError' (GH-22291) Message-ID: https://github.com/python/cpython/commit/6dc8e0eb9b471f6665f841216a6b2d83741b3973 commit: 6dc8e0eb9b471f6665f841216a6b2d83741b3973 branch: 3.9 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-02T09:39:20-07:00 summary: bpo-41802: Document 'PyDict_DelItem' can raise a 'KeyError' (GH-22291) (cherry picked from commit 20ce62f00957d11f24f6449cd5c0ef5dd67174d4) Co-authored-by: Campbell Barton files: M Doc/c-api/dict.rst diff --git a/Doc/c-api/dict.rst b/Doc/c-api/dict.rst index 6b3f45e27a3cb..8c626c7d4a940 100644 --- a/Doc/c-api/dict.rst +++ b/Doc/c-api/dict.rst @@ -81,14 +81,16 @@ Dictionary Objects .. c:function:: int PyDict_DelItem(PyObject *p, PyObject *key) Remove the entry in dictionary *p* with key *key*. *key* must be hashable; - if it isn't, :exc:`TypeError` is raised. Return ``0`` on success or ``-1`` - on failure. + if it isn't, :exc:`TypeError` is raised. + If *key* is not in the dictionary, :exc:`KeyError` is raised. + Return ``0`` on success or ``-1`` on failure. .. c:function:: int PyDict_DelItemString(PyObject *p, const char *key) - Remove the entry in dictionary *p* which has a key specified by the string - *key*. Return ``0`` on success or ``-1`` on failure. + Remove the entry in dictionary *p* which has a key specified by the string *key*. + If *key* is not in the dictionary, :exc:`KeyError` is raised. + Return ``0`` on success or ``-1`` on failure. .. c:function:: PyObject* PyDict_GetItem(PyObject *p, PyObject *key) From webhook-mailer at python.org Fri Oct 2 16:22:11 2020 From: webhook-mailer at python.org (Hansraj Das) Date: Fri, 02 Oct 2020 20:22:11 -0000 Subject: [Python-checkins] Typo fix - "mesasge" should be "message" (GH-22498) Message-ID: https://github.com/python/cpython/commit/9cd01ece78e63bf98a1d25f70d5a020adf07ca4a commit: 9cd01ece78e63bf98a1d25f70d5a020adf07ca4a branch: master author: Hansraj Das committer: GitHub date: 2020-10-02T13:21:45-07:00 summary: Typo fix - "mesasge" should be "message" (GH-22498) * Correct at 2 places in email module files: M Lib/email/message.py diff --git a/Lib/email/message.py b/Lib/email/message.py index 1262602617960..3701b305532b4 100644 --- a/Lib/email/message.py +++ b/Lib/email/message.py @@ -141,7 +141,7 @@ def as_string(self, unixfrom=False, maxheaderlen=0, policy=None): header. For backward compatibility reasons, if maxheaderlen is not specified it defaults to 0, so you must override it explicitly if you want a different maxheaderlen. 'policy' is passed to the - Generator instance used to serialize the mesasge; if it is not + Generator instance used to serialize the message; if it is not specified the policy associated with the message instance is used. If the message object contains binary data that is not encoded @@ -958,7 +958,7 @@ def as_string(self, unixfrom=False, maxheaderlen=None, policy=None): header. maxheaderlen is retained for backward compatibility with the base Message class, but defaults to None, meaning that the policy value for max_line_length controls the header maximum length. 'policy' is - passed to the Generator instance used to serialize the mesasge; if it + passed to the Generator instance used to serialize the message; if it is not specified the policy associated with the message instance is used. """ From webhook-mailer at python.org Fri Oct 2 16:43:52 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Fri, 02 Oct 2020 20:43:52 -0000 Subject: [Python-checkins] Typo fix - "mesasge" should be "message" (GH-22498) Message-ID: https://github.com/python/cpython/commit/6d40943e34af71fa7b9e10a66517325dd72c5516 commit: 6d40943e34af71fa7b9e10a66517325dd72c5516 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-02T13:43:44-07:00 summary: Typo fix - "mesasge" should be "message" (GH-22498) * Correct at 2 places in email module (cherry picked from commit 9cd01ece78e63bf98a1d25f70d5a020adf07ca4a) Co-authored-by: Hansraj Das files: M Lib/email/message.py diff --git a/Lib/email/message.py b/Lib/email/message.py index 1262602617960..3701b305532b4 100644 --- a/Lib/email/message.py +++ b/Lib/email/message.py @@ -141,7 +141,7 @@ def as_string(self, unixfrom=False, maxheaderlen=0, policy=None): header. For backward compatibility reasons, if maxheaderlen is not specified it defaults to 0, so you must override it explicitly if you want a different maxheaderlen. 'policy' is passed to the - Generator instance used to serialize the mesasge; if it is not + Generator instance used to serialize the message; if it is not specified the policy associated with the message instance is used. If the message object contains binary data that is not encoded @@ -958,7 +958,7 @@ def as_string(self, unixfrom=False, maxheaderlen=None, policy=None): header. maxheaderlen is retained for backward compatibility with the base Message class, but defaults to None, meaning that the policy value for max_line_length controls the header maximum length. 'policy' is - passed to the Generator instance used to serialize the mesasge; if it + passed to the Generator instance used to serialize the message; if it is not specified the policy associated with the message instance is used. """ From webhook-mailer at python.org Fri Oct 2 17:05:42 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Fri, 02 Oct 2020 21:05:42 -0000 Subject: [Python-checkins] Typo fix - "mesasge" should be "message" (GH-22498) Message-ID: https://github.com/python/cpython/commit/d5c5f7955280db3ec8aa49c50b4f31a4bd5c50b7 commit: d5c5f7955280db3ec8aa49c50b4f31a4bd5c50b7 branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-02T14:05:36-07:00 summary: Typo fix - "mesasge" should be "message" (GH-22498) * Correct at 2 places in email module (cherry picked from commit 9cd01ece78e63bf98a1d25f70d5a020adf07ca4a) Co-authored-by: Hansraj Das files: M Lib/email/message.py diff --git a/Lib/email/message.py b/Lib/email/message.py index 1262602617960..3701b305532b4 100644 --- a/Lib/email/message.py +++ b/Lib/email/message.py @@ -141,7 +141,7 @@ def as_string(self, unixfrom=False, maxheaderlen=0, policy=None): header. For backward compatibility reasons, if maxheaderlen is not specified it defaults to 0, so you must override it explicitly if you want a different maxheaderlen. 'policy' is passed to the - Generator instance used to serialize the mesasge; if it is not + Generator instance used to serialize the message; if it is not specified the policy associated with the message instance is used. If the message object contains binary data that is not encoded @@ -958,7 +958,7 @@ def as_string(self, unixfrom=False, maxheaderlen=None, policy=None): header. maxheaderlen is retained for backward compatibility with the base Message class, but defaults to None, meaning that the policy value for max_line_length controls the header maximum length. 'policy' is - passed to the Generator instance used to serialize the mesasge; if it + passed to the Generator instance used to serialize the message; if it is not specified the policy associated with the message instance is used. """ From webhook-mailer at python.org Fri Oct 2 18:36:33 2020 From: webhook-mailer at python.org (Andre Delfino) Date: Fri, 02 Oct 2020 22:36:33 -0000 Subject: [Python-checkins] Fix is_typeddict markup (#22501) Message-ID: https://github.com/python/cpython/commit/bd71a433408901e9705ebb0498693b3290dc8540 commit: bd71a433408901e9705ebb0498693b3290dc8540 branch: master author: Andre Delfino committer: GitHub date: 2020-10-02T15:36:26-07:00 summary: Fix is_typeddict markup (#22501) files: M Doc/library/typing.rst diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 3b824d0a4a8da..cbb18954a6764 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -1666,12 +1666,13 @@ Introspection helpers Check if a type is a :class:`TypedDict`. For example:: - class Film(TypedDict): - title: str - year: int - is_typeddict(Film) # => True - is_typeddict(Union[list, str]) # => False + class Film(TypedDict): + title: str + year: int + + is_typeddict(Film) # => True + is_typeddict(Union[list, str]) # => False .. versionadded:: 3.10 From webhook-mailer at python.org Fri Oct 2 19:15:37 2020 From: webhook-mailer at python.org (Andre Delfino) Date: Fri, 02 Oct 2020 23:15:37 -0000 Subject: [Python-checkins] [doc] Fix link to abc.collections.Iterable (GH-22502) Message-ID: https://github.com/python/cpython/commit/d4b9edd5052a2d9ae3d2be69975cc933afb37737 commit: d4b9edd5052a2d9ae3d2be69975cc933afb37737 branch: master author: Andre Delfino committer: GitHub date: 2020-10-02T16:15:28-07:00 summary: [doc] Fix link to abc.collections.Iterable (GH-22502) Automerge-Triggered-By: @gvanrossum files: M Doc/library/typing.rst diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index cbb18954a6764..35e0445889b17 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -903,7 +903,7 @@ These are not used in annotations. They are building blocks for creating generic Such a protocol can be used with :func:`isinstance` and :func:`issubclass`. This raises :exc:`TypeError` when applied to a non-protocol class. This allows a simple-minded structural check, very similar to "one trick ponies" - in :mod:`collections.abc` such as :class:`Iterable`. For example:: + in :mod:`collections.abc` such as :class:`~collections.abc.Iterable`. For example:: @runtime_checkable class Closable(Protocol): From webhook-mailer at python.org Fri Oct 2 19:36:59 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Fri, 02 Oct 2020 23:36:59 -0000 Subject: [Python-checkins] [doc] Fix link to abc.collections.Iterable (GH-22502) Message-ID: https://github.com/python/cpython/commit/ebc8072c3dbcee983db4c709095426b4dc6d0516 commit: ebc8072c3dbcee983db4c709095426b4dc6d0516 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-02T16:36:51-07:00 summary: [doc] Fix link to abc.collections.Iterable (GH-22502) Automerge-Triggered-By: @gvanrossum (cherry picked from commit d4b9edd5052a2d9ae3d2be69975cc933afb37737) Co-authored-by: Andre Delfino files: M Doc/library/typing.rst diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 78d90d19f74e0..a4343d7e39374 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -903,7 +903,7 @@ These are not used in annotations. They are building blocks for creating generic Such a protocol can be used with :func:`isinstance` and :func:`issubclass`. This raises :exc:`TypeError` when applied to a non-protocol class. This allows a simple-minded structural check, very similar to "one trick ponies" - in :mod:`collections.abc` such as :class:`Iterable`. For example:: + in :mod:`collections.abc` such as :class:`~collections.abc.Iterable`. For example:: @runtime_checkable class Closable(Protocol): From webhook-mailer at python.org Sat Oct 3 02:07:21 2020 From: webhook-mailer at python.org (scoder) Date: Sat, 03 Oct 2020 06:07:21 -0000 Subject: [Python-checkins] bpo-41900: C14N 2.0 serialisation failed for unprefixed attributes when a default namespace was defined. (GH-22474) Message-ID: https://github.com/python/cpython/commit/6a412c94b6b68e7e3632562dc7358a12ffd1447f commit: 6a412c94b6b68e7e3632562dc7358a12ffd1447f branch: master author: scoder committer: GitHub date: 2020-10-03T08:07:07+02:00 summary: bpo-41900: C14N 2.0 serialisation failed for unprefixed attributes when a default namespace was defined. (GH-22474) files: A Misc/NEWS.d/next/Library/2020-10-01-10-50-12.bpo-41900.Cho7oh.rst M Lib/test/test_xml_etree.py M Lib/xml/etree/ElementTree.py diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py index d22c35d92c4e3..3f1f3781e488c 100644 --- a/Lib/test/test_xml_etree.py +++ b/Lib/test/test_xml_etree.py @@ -3899,6 +3899,14 @@ def test_simple_roundtrip(self): #self.assertEqual(c14n_roundtrip(""), #'') + # Namespace issues + xml = '' + self.assertEqual(c14n_roundtrip(xml), xml) + xml = '' + self.assertEqual(c14n_roundtrip(xml), xml) + xml = '' + self.assertEqual(c14n_roundtrip(xml), xml) + def test_c14n_exclusion(self): xml = textwrap.dedent("""\ diff --git a/Lib/xml/etree/ElementTree.py b/Lib/xml/etree/ElementTree.py index da2bcad0b4d62..7a269001d6e18 100644 --- a/Lib/xml/etree/ElementTree.py +++ b/Lib/xml/etree/ElementTree.py @@ -1876,6 +1876,11 @@ def _qname(self, qname, uri=None): self._declared_ns_stack[-1].append((uri, prefix)) return f'{prefix}:{tag}' if prefix else tag, tag, uri + if not uri: + # As soon as a default namespace is defined, + # anything that has no namespace (and thus, no prefix) goes there. + return tag, tag, uri + raise ValueError(f'Namespace "{uri}" is not declared in scope') def data(self, data): diff --git a/Misc/NEWS.d/next/Library/2020-10-01-10-50-12.bpo-41900.Cho7oh.rst b/Misc/NEWS.d/next/Library/2020-10-01-10-50-12.bpo-41900.Cho7oh.rst new file mode 100644 index 0000000000000..6586c09ec985d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-10-01-10-50-12.bpo-41900.Cho7oh.rst @@ -0,0 +1,2 @@ +C14N 2.0 serialisation in xml.etree.ElementTree failed for unprefixed attributes +when a default namespace was defined. From webhook-mailer at python.org Sat Oct 3 02:41:39 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Sat, 03 Oct 2020 06:41:39 -0000 Subject: [Python-checkins] bpo-41900: C14N 2.0 serialisation failed for unprefixed attributes when a default namespace was defined. (GH-22474) (GH-22507) Message-ID: https://github.com/python/cpython/commit/a0f2b664335eb689abdecc677e09a193f503af59 commit: a0f2b664335eb689abdecc677e09a193f503af59 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-03T08:41:35+02:00 summary: bpo-41900: C14N 2.0 serialisation failed for unprefixed attributes when a default namespace was defined. (GH-22474) (GH-22507) (cherry picked from commit 6a412c94b6b68e7e3632562dc7358a12ffd1447f) files: A Misc/NEWS.d/next/Library/2020-10-01-10-50-12.bpo-41900.Cho7oh.rst M Lib/test/test_xml_etree.py M Lib/xml/etree/ElementTree.py diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py index d01649d1c31b2..5632b8b503cf3 100644 --- a/Lib/test/test_xml_etree.py +++ b/Lib/test/test_xml_etree.py @@ -3894,6 +3894,14 @@ def test_simple_roundtrip(self): #self.assertEqual(c14n_roundtrip(""), #'') + # Namespace issues + xml = '' + self.assertEqual(c14n_roundtrip(xml), xml) + xml = '' + self.assertEqual(c14n_roundtrip(xml), xml) + xml = '' + self.assertEqual(c14n_roundtrip(xml), xml) + def test_c14n_exclusion(self): xml = textwrap.dedent("""\ diff --git a/Lib/xml/etree/ElementTree.py b/Lib/xml/etree/ElementTree.py index da2bcad0b4d62..7a269001d6e18 100644 --- a/Lib/xml/etree/ElementTree.py +++ b/Lib/xml/etree/ElementTree.py @@ -1876,6 +1876,11 @@ def _qname(self, qname, uri=None): self._declared_ns_stack[-1].append((uri, prefix)) return f'{prefix}:{tag}' if prefix else tag, tag, uri + if not uri: + # As soon as a default namespace is defined, + # anything that has no namespace (and thus, no prefix) goes there. + return tag, tag, uri + raise ValueError(f'Namespace "{uri}" is not declared in scope') def data(self, data): diff --git a/Misc/NEWS.d/next/Library/2020-10-01-10-50-12.bpo-41900.Cho7oh.rst b/Misc/NEWS.d/next/Library/2020-10-01-10-50-12.bpo-41900.Cho7oh.rst new file mode 100644 index 0000000000000..6586c09ec985d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-10-01-10-50-12.bpo-41900.Cho7oh.rst @@ -0,0 +1,2 @@ +C14N 2.0 serialisation in xml.etree.ElementTree failed for unprefixed attributes +when a default namespace was defined. From webhook-mailer at python.org Sat Oct 3 02:42:42 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Sat, 03 Oct 2020 06:42:42 -0000 Subject: [Python-checkins] bpo-41900: C14N 2.0 serialisation failed for unprefixed attributes when a default namespace was defined. (GH-22474) (GH-22508) Message-ID: https://github.com/python/cpython/commit/cfed5343331350c5737b464970a31f7588319e8b commit: cfed5343331350c5737b464970a31f7588319e8b branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-03T08:42:38+02:00 summary: bpo-41900: C14N 2.0 serialisation failed for unprefixed attributes when a default namespace was defined. (GH-22474) (GH-22508) (cherry picked from commit 6a412c94b6b68e7e3632562dc7358a12ffd1447f) files: A Misc/NEWS.d/next/Library/2020-10-01-10-50-12.bpo-41900.Cho7oh.rst M Lib/test/test_xml_etree.py M Lib/xml/etree/ElementTree.py diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py index b2492cda848f9..341a3c7cd7660 100644 --- a/Lib/test/test_xml_etree.py +++ b/Lib/test/test_xml_etree.py @@ -3701,6 +3701,14 @@ def test_simple_roundtrip(self): #self.assertEqual(c14n_roundtrip(""), #'') + # Namespace issues + xml = '' + self.assertEqual(c14n_roundtrip(xml), xml) + xml = '' + self.assertEqual(c14n_roundtrip(xml), xml) + xml = '' + self.assertEqual(c14n_roundtrip(xml), xml) + def test_c14n_exclusion(self): xml = textwrap.dedent("""\ diff --git a/Lib/xml/etree/ElementTree.py b/Lib/xml/etree/ElementTree.py index 645e999a0be6c..598b569ea958b 100644 --- a/Lib/xml/etree/ElementTree.py +++ b/Lib/xml/etree/ElementTree.py @@ -1849,6 +1849,11 @@ def _qname(self, qname, uri=None): self._declared_ns_stack[-1].append((uri, prefix)) return f'{prefix}:{tag}' if prefix else tag, tag, uri + if not uri: + # As soon as a default namespace is defined, + # anything that has no namespace (and thus, no prefix) goes there. + return tag, tag, uri + raise ValueError(f'Namespace "{uri}" is not declared in scope') def data(self, data): diff --git a/Misc/NEWS.d/next/Library/2020-10-01-10-50-12.bpo-41900.Cho7oh.rst b/Misc/NEWS.d/next/Library/2020-10-01-10-50-12.bpo-41900.Cho7oh.rst new file mode 100644 index 0000000000000..6586c09ec985d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-10-01-10-50-12.bpo-41900.Cho7oh.rst @@ -0,0 +1,2 @@ +C14N 2.0 serialisation in xml.etree.ElementTree failed for unprefixed attributes +when a default namespace was defined. From webhook-mailer at python.org Sat Oct 3 05:52:18 2020 From: webhook-mailer at python.org (Ram Rachum) Date: Sat, 03 Oct 2020 09:52:18 -0000 Subject: [Python-checkins] bpo-40833: Clarify Path.rename doc-string regarding relative paths (GH-20554) Message-ID: https://github.com/python/cpython/commit/f97e42ef4d97dee64f45ed65170a6e77c8e46fdf commit: f97e42ef4d97dee64f45ed65170a6e77c8e46fdf branch: master author: Ram Rachum committer: GitHub date: 2020-10-03T12:52:13+03:00 summary: bpo-40833: Clarify Path.rename doc-string regarding relative paths (GH-20554) files: M Doc/library/pathlib.rst M Lib/pathlib.py diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst index 23486b625072f..9526a03b05398 100644 --- a/Doc/library/pathlib.rst +++ b/Doc/library/pathlib.rst @@ -1008,6 +1008,10 @@ call fails (for example because the path doesn't exist). >>> target.open().read() 'some text' + The target path may be absolute or relative. Relative paths are interpreted + relative to the current working directory, *not* the directory of the Path + object. + .. versionchanged:: 3.8 Added return value, return the new Path instance. @@ -1018,6 +1022,10 @@ call fails (for example because the path doesn't exist). instance pointing to *target*. If *target* points to an existing file or directory, it will be unconditionally replaced. + The target path may be absolute or relative. Relative paths are interpreted + relative to the current working directory, *not* the directory of the Path + object. + .. versionchanged:: 3.8 Added return value, return the new Path instance. diff --git a/Lib/pathlib.py b/Lib/pathlib.py index babc443dd3b30..147be2ff0dddf 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -1366,17 +1366,26 @@ def link_to(self, target): def rename(self, target): """ - Rename this path to the given path, - and return a new Path instance pointing to the given path. + Rename this path to the target path. + + The target path may be absolute or relative. Relative paths are + interpreted relative to the current working directory, *not* the + directory of the Path object. + + Returns the new Path instance pointing to the target path. """ self._accessor.rename(self, target) return self.__class__(target) def replace(self, target): """ - Rename this path to the given path, clobbering the existing - destination if it exists, and return a new Path instance - pointing to the given path. + Rename this path to the target path, overwriting if that path exists. + + The target path may be absolute or relative. Relative paths are + interpreted relative to the current working directory, *not* the + directory of the Path object. + + Returns the new Path instance pointing to the target path. """ self._accessor.replace(self, target) return self.__class__(target) From webhook-mailer at python.org Sat Oct 3 06:11:44 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Sat, 03 Oct 2020 10:11:44 -0000 Subject: [Python-checkins] bpo-40833: Clarify Path.rename doc-string regarding relative paths (GH-20554) Message-ID: https://github.com/python/cpython/commit/4b982e0dd7354c9103a0cae8e6e78b5e57794e77 commit: 4b982e0dd7354c9103a0cae8e6e78b5e57794e77 branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-03T03:11:39-07:00 summary: bpo-40833: Clarify Path.rename doc-string regarding relative paths (GH-20554) (cherry picked from commit f97e42ef4d97dee64f45ed65170a6e77c8e46fdf) Co-authored-by: Ram Rachum files: M Doc/library/pathlib.rst M Lib/pathlib.py diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst index c855a6e11dac1..4518f56004929 100644 --- a/Doc/library/pathlib.rst +++ b/Doc/library/pathlib.rst @@ -954,6 +954,10 @@ call fails (for example because the path doesn't exist). >>> target.open().read() 'some text' + The target path may be absolute or relative. Relative paths are interpreted + relative to the current working directory, *not* the directory of the Path + object. + .. versionchanged:: 3.8 Added return value, return the new Path instance. @@ -964,6 +968,10 @@ call fails (for example because the path doesn't exist). instance pointing to *target*. If *target* points to an existing file or directory, it will be unconditionally replaced. + The target path may be absolute or relative. Relative paths are interpreted + relative to the current working directory, *not* the directory of the Path + object. + .. versionchanged:: 3.8 Added return value, return the new Path instance. diff --git a/Lib/pathlib.py b/Lib/pathlib.py index 4f72bab3bdbbc..171d8b8252106 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -1353,8 +1353,13 @@ def link_to(self, target): def rename(self, target): """ - Rename this path to the given path, - and return a new Path instance pointing to the given path. + Rename this path to the target path. + + The target path may be absolute or relative. Relative paths are + interpreted relative to the current working directory, *not* the + directory of the Path object. + + Returns the new Path instance pointing to the target path. """ if self._closed: self._raise_closed() @@ -1363,9 +1368,13 @@ def rename(self, target): def replace(self, target): """ - Rename this path to the given path, clobbering the existing - destination if it exists, and return a new Path instance - pointing to the given path. + Rename this path to the target path, overwriting if that path exists. + + The target path may be absolute or relative. Relative paths are + interpreted relative to the current working directory, *not* the + directory of the Path object. + + Returns the new Path instance pointing to the target path. """ if self._closed: self._raise_closed() From webhook-mailer at python.org Sat Oct 3 06:43:51 2020 From: webhook-mailer at python.org (Ram Rachum) Date: Sat, 03 Oct 2020 10:43:51 -0000 Subject: [Python-checkins] bpo-41867: List options for timespec in docstrings of isoformat methods (GH-22418) Message-ID: https://github.com/python/cpython/commit/52301312bb9de2299b0c42468fd1936d869e651c commit: 52301312bb9de2299b0c42468fd1936d869e651c branch: master author: Ram Rachum committer: GitHub date: 2020-10-03T13:43:47+03:00 summary: bpo-41867: List options for timespec in docstrings of isoformat methods (GH-22418) files: M Lib/datetime.py M Modules/_datetimemodule.c diff --git a/Lib/datetime.py b/Lib/datetime.py index 3090978508c92..ea86bcb8b2388 100644 --- a/Lib/datetime.py +++ b/Lib/datetime.py @@ -1421,7 +1421,8 @@ def isoformat(self, timespec='auto'): part is omitted if self.microsecond == 0. The optional argument timespec specifies the number of additional - terms of the time to include. + terms of the time to include. Valid options are 'auto', 'hours', + 'minutes', 'seconds', 'milliseconds' and 'microseconds'. """ s = _format_time(self._hour, self._minute, self._second, self._microsecond, timespec) @@ -1906,7 +1907,8 @@ def isoformat(self, sep='T', timespec='auto'): time, default 'T'. The optional argument timespec specifies the number of additional - terms of the time to include. + terms of the time to include. Valid options are 'auto', 'hours', + 'minutes', 'seconds', 'milliseconds' and 'microseconds'. """ s = ("%04d-%02d-%02d%c" % (self._year, self._month, self._day, sep) + _format_time(self._hour, self._minute, self._second, diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 0631272429f4f..94868717e6a04 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -4663,7 +4663,10 @@ static PyMethodDef time_methods[] = { {"isoformat", (PyCFunction)(void(*)(void))time_isoformat, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("Return string in ISO 8601 format, [HH[:MM[:SS[.mmm[uuu]]]]]" "[+HH:MM].\n\n" - "timespec specifies what components of the time to include.\n")}, + "The optional argument timespec specifies the number " + "of additional terms\nof the time to include. Valid " + "options are 'auto', 'hours', 'minutes',\n'seconds', " + "'milliseconds' and 'microseconds'.\n")}, {"strftime", (PyCFunction)(void(*)(void))time_strftime, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("format -> strftime() style string.")}, @@ -6370,9 +6373,10 @@ static PyMethodDef datetime_methods[] = { "YYYY-MM-DDT[HH[:MM[:SS[.mmm[uuu]]]]][+HH:MM].\n" "sep is used to separate the year from the time, and " "defaults to 'T'.\n" - "timespec specifies what components of the time to include" - " (allowed values are 'auto', 'hours', 'minutes', 'seconds'," - " 'milliseconds', and 'microseconds').\n")}, + "The optional argument timespec specifies the number " + "of additional terms\nof the time to include. Valid " + "options are 'auto', 'hours', 'minutes',\n'seconds', " + "'milliseconds' and 'microseconds'.\n")}, {"utcoffset", (PyCFunction)datetime_utcoffset, METH_NOARGS, PyDoc_STR("Return self.tzinfo.utcoffset(self).")}, From webhook-mailer at python.org Sat Oct 3 06:44:18 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Sat, 03 Oct 2020 10:44:18 -0000 Subject: [Python-checkins] [3.9] bpo-40833: Clarify Path.rename doc-string regarding relative paths (GH-20554) Message-ID: https://github.com/python/cpython/commit/ef7b937b8fe94f4c59dfcbe7567c3ca76097a41d commit: ef7b937b8fe94f4c59dfcbe7567c3ca76097a41d branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-03T13:44:14+03:00 summary: [3.9] bpo-40833: Clarify Path.rename doc-string regarding relative paths (GH-20554) (cherry picked from commit f97e42ef4d97dee64f45ed65170a6e77c8e46fdf) Co-authored-by: Ram Rachum files: M Doc/library/pathlib.rst M Lib/pathlib.py diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst index 23486b625072f..9526a03b05398 100644 --- a/Doc/library/pathlib.rst +++ b/Doc/library/pathlib.rst @@ -1008,6 +1008,10 @@ call fails (for example because the path doesn't exist). >>> target.open().read() 'some text' + The target path may be absolute or relative. Relative paths are interpreted + relative to the current working directory, *not* the directory of the Path + object. + .. versionchanged:: 3.8 Added return value, return the new Path instance. @@ -1018,6 +1022,10 @@ call fails (for example because the path doesn't exist). instance pointing to *target*. If *target* points to an existing file or directory, it will be unconditionally replaced. + The target path may be absolute or relative. Relative paths are interpreted + relative to the current working directory, *not* the directory of the Path + object. + .. versionchanged:: 3.8 Added return value, return the new Path instance. diff --git a/Lib/pathlib.py b/Lib/pathlib.py index babc443dd3b30..147be2ff0dddf 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -1366,17 +1366,26 @@ def link_to(self, target): def rename(self, target): """ - Rename this path to the given path, - and return a new Path instance pointing to the given path. + Rename this path to the target path. + + The target path may be absolute or relative. Relative paths are + interpreted relative to the current working directory, *not* the + directory of the Path object. + + Returns the new Path instance pointing to the target path. """ self._accessor.rename(self, target) return self.__class__(target) def replace(self, target): """ - Rename this path to the given path, clobbering the existing - destination if it exists, and return a new Path instance - pointing to the given path. + Rename this path to the target path, overwriting if that path exists. + + The target path may be absolute or relative. Relative paths are + interpreted relative to the current working directory, *not* the + directory of the Path object. + + Returns the new Path instance pointing to the target path. """ self._accessor.replace(self, target) return self.__class__(target) From webhook-mailer at python.org Sat Oct 3 07:01:52 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Sat, 03 Oct 2020 11:01:52 -0000 Subject: [Python-checkins] bpo-41867: List options for timespec in docstrings of isoformat methods (GH-22418) Message-ID: https://github.com/python/cpython/commit/9aa34f1bbbe1ca6376539f9ba436ff75d2141888 commit: 9aa34f1bbbe1ca6376539f9ba436ff75d2141888 branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-03T04:01:41-07:00 summary: bpo-41867: List options for timespec in docstrings of isoformat methods (GH-22418) (cherry picked from commit 52301312bb9de2299b0c42468fd1936d869e651c) Co-authored-by: Ram Rachum files: M Lib/datetime.py M Modules/_datetimemodule.c diff --git a/Lib/datetime.py b/Lib/datetime.py index 0adf1dd67dfb6..96d112c197069 100644 --- a/Lib/datetime.py +++ b/Lib/datetime.py @@ -1418,7 +1418,8 @@ def isoformat(self, timespec='auto'): part is omitted if self.microsecond == 0. The optional argument timespec specifies the number of additional - terms of the time to include. + terms of the time to include. Valid options are 'auto', 'hours', + 'minutes', 'seconds', 'milliseconds' and 'microseconds'. """ s = _format_time(self._hour, self._minute, self._second, self._microsecond, timespec) @@ -1902,7 +1903,8 @@ def isoformat(self, sep='T', timespec='auto'): time, default 'T'. The optional argument timespec specifies the number of additional - terms of the time to include. + terms of the time to include. Valid options are 'auto', 'hours', + 'minutes', 'seconds', 'milliseconds' and 'microseconds'. """ s = ("%04d-%02d-%02d%c" % (self._year, self._month, self._day, sep) + _format_time(self._hour, self._minute, self._second, diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 41c3f34269ddc..d428210542d41 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -4539,7 +4539,10 @@ static PyMethodDef time_methods[] = { {"isoformat", (PyCFunction)(void(*)(void))time_isoformat, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("Return string in ISO 8601 format, [HH[:MM[:SS[.mmm[uuu]]]]]" "[+HH:MM].\n\n" - "timespec specifies what components of the time to include.\n")}, + "The optional argument timespec specifies the number " + "of additional terms\nof the time to include. Valid " + "options are 'auto', 'hours', 'minutes',\n'seconds', " + "'milliseconds' and 'microseconds'.\n")}, {"strftime", (PyCFunction)(void(*)(void))time_strftime, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("format -> strftime() style string.")}, @@ -6246,9 +6249,10 @@ static PyMethodDef datetime_methods[] = { "YYYY-MM-DDT[HH[:MM[:SS[.mmm[uuu]]]]][+HH:MM].\n" "sep is used to separate the year from the time, and " "defaults to 'T'.\n" - "timespec specifies what components of the time to include" - " (allowed values are 'auto', 'hours', 'minutes', 'seconds'," - " 'milliseconds', and 'microseconds').\n")}, + "The optional argument timespec specifies the number " + "of additional terms\nof the time to include. Valid " + "options are 'auto', 'hours', 'minutes',\n'seconds', " + "'milliseconds' and 'microseconds'.\n")}, {"utcoffset", (PyCFunction)datetime_utcoffset, METH_NOARGS, PyDoc_STR("Return self.tzinfo.utcoffset(self).")}, From webhook-mailer at python.org Sat Oct 3 07:42:32 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Sat, 03 Oct 2020 11:42:32 -0000 Subject: [Python-checkins] [3.9] bpo-41867: List options for timespec in docstrings of isoformat methods (GH-22418) Message-ID: https://github.com/python/cpython/commit/8ca08310a882b7db6469fe732465610b92f4665e commit: 8ca08310a882b7db6469fe732465610b92f4665e branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-03T14:42:16+03:00 summary: [3.9] bpo-41867: List options for timespec in docstrings of isoformat methods (GH-22418) (cherry picked from commit 52301312bb9de2299b0c42468fd1936d869e651c) Co-authored-by: Ram Rachum files: M Lib/datetime.py M Modules/_datetimemodule.c diff --git a/Lib/datetime.py b/Lib/datetime.py index 952aebfdec0a7..2294ac2b6873a 100644 --- a/Lib/datetime.py +++ b/Lib/datetime.py @@ -1452,7 +1452,8 @@ def isoformat(self, timespec='auto'): part is omitted if self.microsecond == 0. The optional argument timespec specifies the number of additional - terms of the time to include. + terms of the time to include. Valid options are 'auto', 'hours', + 'minutes', 'seconds', 'milliseconds' and 'microseconds'. """ s = _format_time(self._hour, self._minute, self._second, self._microsecond, timespec) @@ -1937,7 +1938,8 @@ def isoformat(self, sep='T', timespec='auto'): time, default 'T'. The optional argument timespec specifies the number of additional - terms of the time to include. + terms of the time to include. Valid options are 'auto', 'hours', + 'minutes', 'seconds', 'milliseconds' and 'microseconds'. """ s = ("%04d-%02d-%02d%c" % (self._year, self._month, self._day, sep) + _format_time(self._hour, self._minute, self._second, diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index acdde83dc845a..4e0c3783d91eb 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -4673,7 +4673,10 @@ static PyMethodDef time_methods[] = { {"isoformat", (PyCFunction)(void(*)(void))time_isoformat, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("Return string in ISO 8601 format, [HH[:MM[:SS[.mmm[uuu]]]]]" "[+HH:MM].\n\n" - "timespec specifies what components of the time to include.\n")}, + "The optional argument timespec specifies the number " + "of additional terms\nof the time to include. Valid " + "options are 'auto', 'hours', 'minutes',\n'seconds', " + "'milliseconds' and 'microseconds'.\n")}, {"strftime", (PyCFunction)(void(*)(void))time_strftime, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("format -> strftime() style string.")}, @@ -6380,9 +6383,10 @@ static PyMethodDef datetime_methods[] = { "YYYY-MM-DDT[HH[:MM[:SS[.mmm[uuu]]]]][+HH:MM].\n" "sep is used to separate the year from the time, and " "defaults to 'T'.\n" - "timespec specifies what components of the time to include" - " (allowed values are 'auto', 'hours', 'minutes', 'seconds'," - " 'milliseconds', and 'microseconds').\n")}, + "The optional argument timespec specifies the number " + "of additional terms\nof the time to include. Valid " + "options are 'auto', 'hours', 'minutes',\n'seconds', " + "'milliseconds' and 'microseconds'.\n")}, {"utcoffset", (PyCFunction)datetime_utcoffset, METH_NOARGS, PyDoc_STR("Return self.tzinfo.utcoffset(self).")}, From webhook-mailer at python.org Sat Oct 3 10:29:00 2020 From: webhook-mailer at python.org (Pablo Galindo) Date: Sat, 03 Oct 2020 14:29:00 -0000 Subject: [Python-checkins] bpo-41826: Fix compiler warnings in test_peg_generator (GH-22455) Message-ID: https://github.com/python/cpython/commit/c111355480ff51b50a671679c5099f534cb01cae commit: c111355480ff51b50a671679c5099f534cb01cae branch: master author: Pablo Galindo committer: GitHub date: 2020-10-03T17:28:51+03:00 summary: bpo-41826: Fix compiler warnings in test_peg_generator (GH-22455) Co-authored-by: Skip Montanaro files: M Lib/test/test_peg_generator/test_c_parser.py diff --git a/Lib/test/test_peg_generator/test_c_parser.py b/Lib/test/test_peg_generator/test_c_parser.py index 2c13635d37dea..0dffedca789c5 100644 --- a/Lib/test/test_peg_generator/test_c_parser.py +++ b/Lib/test/test_peg_generator/test_c_parser.py @@ -90,7 +90,7 @@ def run_test(self, grammar_source, test_source): def test_c_parser(self) -> None: grammar_source = """ - start[mod_ty]: a=stmt* $ { Module(a, NULL, p->arena) } + start[mod_ty]: a[asdl_stmt_seq*]=stmt* $ { Module(a, NULL, p->arena) } stmt[stmt_ty]: a=expr_stmt { a } expr_stmt[stmt_ty]: a=expression NEWLINE { _Py_Expr(a, EXTRA) } expression[expr_ty]: ( l=expression '+' r=term { _Py_BinOp(l, Add, r, EXTRA) } @@ -232,7 +232,7 @@ def test_nasty_mutually_left_recursive(self) -> None: def test_return_stmt_noexpr_action(self) -> None: grammar_source = """ start[mod_ty]: a=[statements] ENDMARKER { Module(a, NULL, p->arena) } - statements[asdl_seq*]: a=statement+ { a } + statements[asdl_stmt_seq*]: a[asdl_stmt_seq*]=statement+ { a } statement[stmt_ty]: simple_stmt simple_stmt[stmt_ty]: small_stmt small_stmt[stmt_ty]: return_stmt @@ -246,7 +246,7 @@ def test_return_stmt_noexpr_action(self) -> None: def test_gather_action_ast(self) -> None: grammar_source = """ - start[mod_ty]: a=';'.pass_stmt+ NEWLINE ENDMARKER { Module(a, NULL, p->arena) } + start[mod_ty]: a[asdl_stmt_seq*]=';'.pass_stmt+ NEWLINE ENDMARKER { Module(a, NULL, p->arena) } pass_stmt[stmt_ty]: a='pass' { _Py_Pass(EXTRA)} """ test_source = """ @@ -258,7 +258,7 @@ def test_gather_action_ast(self) -> None: def test_pass_stmt_action(self) -> None: grammar_source = """ start[mod_ty]: a=[statements] ENDMARKER { Module(a, NULL, p->arena) } - statements[asdl_seq*]: a=statement+ { a } + statements[asdl_stmt_seq*]: a[asdl_stmt_seq*]=statement+ { a } statement[stmt_ty]: simple_stmt simple_stmt[stmt_ty]: small_stmt small_stmt[stmt_ty]: pass_stmt @@ -273,10 +273,11 @@ def test_pass_stmt_action(self) -> None: def test_if_stmt_action(self) -> None: grammar_source = """ start[mod_ty]: a=[statements] ENDMARKER { Module(a, NULL, p->arena) } - statements[asdl_seq*]: a=statement+ { _PyPegen_seq_flatten(p, a) } - statement[asdl_seq*]: a=compound_stmt { _PyPegen_singleton_seq(p, a) } | simple_stmt + statements[asdl_stmt_seq*]: a=statement+ { (asdl_stmt_seq*)_PyPegen_seq_flatten(p, a) } + statement[asdl_stmt_seq*]: a=compound_stmt { (asdl_stmt_seq*)_PyPegen_singleton_seq(p, a) } | simple_stmt - simple_stmt[asdl_seq*]: a=small_stmt b=further_small_stmt* [';'] NEWLINE { _PyPegen_seq_insert_in_front(p, a, b) } + simple_stmt[asdl_stmt_seq*]: a=small_stmt b=further_small_stmt* [';'] NEWLINE { + (asdl_stmt_seq*)_PyPegen_seq_insert_in_front(p, a, b) } further_small_stmt[stmt_ty]: ';' a=small_stmt { a } block: simple_stmt | NEWLINE INDENT a=statements DEDENT { a } @@ -299,14 +300,14 @@ def test_if_stmt_action(self) -> None: def test_same_name_different_types(self) -> None: grammar_source = """ - start[mod_ty]: a=import_from+ NEWLINE ENDMARKER { Module(a, NULL, p->arena)} + start[mod_ty]: a[asdl_stmt_seq*]=import_from+ NEWLINE ENDMARKER { Module(a, NULL, p->arena)} import_from[stmt_ty]: ( a='from' !'import' c=simple_name 'import' d=import_as_names_from { _Py_ImportFrom(c->v.Name.id, d, 0, EXTRA) } | a='from' '.' 'import' c=import_as_names_from { _Py_ImportFrom(NULL, c, 1, EXTRA) } ) simple_name[expr_ty]: NAME - import_as_names_from[asdl_seq*]: a=','.import_as_name_from+ { a } + import_as_names_from[asdl_alias_seq*]: a[asdl_alias_seq*]=','.import_as_name_from+ { a } import_as_name_from[alias_ty]: a=NAME 'as' b=NAME { _Py_alias(((expr_ty) a)->v.Name.id, ((expr_ty) b)->v.Name.id, p->arena) } """ test_source = """ @@ -320,12 +321,12 @@ def test_same_name_different_types(self) -> None: def test_with_stmt_with_paren(self) -> None: grammar_source = """ start[mod_ty]: a=[statements] ENDMARKER { Module(a, NULL, p->arena) } - statements[asdl_seq*]: a=statement+ { _PyPegen_seq_flatten(p, a) } - statement[asdl_seq*]: a=compound_stmt { _PyPegen_singleton_seq(p, a) } + statements[asdl_stmt_seq*]: a=statement+ { (asdl_stmt_seq*)_PyPegen_seq_flatten(p, a) } + statement[asdl_stmt_seq*]: a=compound_stmt { (asdl_stmt_seq*)_PyPegen_singleton_seq(p, a) } compound_stmt[stmt_ty]: with_stmt with_stmt[stmt_ty]: ( - a='with' '(' b=','.with_item+ ')' ':' c=block { - _Py_With(b, _PyPegen_singleton_seq(p, c), NULL, EXTRA) } + a='with' '(' b[asdl_withitem_seq*]=','.with_item+ ')' ':' c=block { + _Py_With(b, (asdl_stmt_seq*) _PyPegen_singleton_seq(p, c), NULL, EXTRA) } ) with_item[withitem_ty]: ( e=NAME o=['as' t=NAME { t }] { _Py_withitem(e, _PyPegen_set_expr_context(p, o, Store), p->arena) } @@ -346,12 +347,12 @@ def test_with_stmt_with_paren(self) -> None: def test_ternary_operator(self) -> None: grammar_source = """ start[mod_ty]: a=expr ENDMARKER { Module(a, NULL, p->arena) } - expr[asdl_seq*]: a=listcomp NEWLINE { _PyPegen_singleton_seq(p, _Py_Expr(a, EXTRA)) } + expr[asdl_stmt_seq*]: a=listcomp NEWLINE { (asdl_stmt_seq*)_PyPegen_singleton_seq(p, _Py_Expr(a, EXTRA)) } listcomp[expr_ty]: ( a='[' b=NAME c=for_if_clauses d=']' { _Py_ListComp(b, c, EXTRA) } ) - for_if_clauses[asdl_seq*]: ( - a=(y=[ASYNC] 'for' a=NAME 'in' b=NAME c=('if' z=NAME { z })* + for_if_clauses[asdl_comprehension_seq*]: ( + a[asdl_comprehension_seq*]=(y=[ASYNC] 'for' a=NAME 'in' b=NAME c[asdl_expr_seq*]=('if' z=NAME { z })* { _Py_comprehension(_Py_Name(((expr_ty) a)->v.Name.id, Store, EXTRA), b, c, (y == NULL) ? 0 : 1, p->arena) })+ { a } ) """ From webhook-mailer at python.org Sat Oct 3 10:58:44 2020 From: webhook-mailer at python.org (Jason R. Coombs) Date: Sat, 03 Oct 2020 14:58:44 -0000 Subject: [Python-checkins] bpo-40564: Avoid copying state from extant ZipFile. (GH-22371) Message-ID: https://github.com/python/cpython/commit/ebbe8033b1c61854c4b623aaf9c3e170d179f875 commit: ebbe8033b1c61854c4b623aaf9c3e170d179f875 branch: master author: Jason R. Coombs committer: GitHub date: 2020-10-03T10:58:39-04:00 summary: bpo-40564: Avoid copying state from extant ZipFile. (GH-22371) bpo-40564: Avoid copying state from extant ZipFile. files: A Misc/NEWS.d/next/Library/2020-09-23-03-33-37.bpo-40564.iXQqMq.rst M Lib/test/test_zipfile.py M Lib/zipfile.py diff --git a/Lib/test/test_zipfile.py b/Lib/test/test_zipfile.py index 687e43df780d6..3bb9ce995c2a1 100644 --- a/Lib/test/test_zipfile.py +++ b/Lib/test/test_zipfile.py @@ -2889,6 +2889,33 @@ def test_open(self): data = strm.read() assert data == "content of a" + def test_open_write(self): + """ + If the zipfile is open for write, it should be possible to + write bytes or text to it. + """ + zf = zipfile.Path(zipfile.ZipFile(io.BytesIO(), mode='w')) + with zf.joinpath('file.bin').open('wb') as strm: + strm.write(b'binary contents') + with zf.joinpath('file.txt').open('w') as strm: + strm.write('text file') + + def test_open_extant_directory(self): + """ + Attempting to open a directory raises IsADirectoryError. + """ + zf = zipfile.Path(add_dirs(build_alpharep_fixture())) + with self.assertRaises(IsADirectoryError): + zf.joinpath('b').open() + + def test_open_missing_directory(self): + """ + Attempting to open a missing directory raises FileNotFoundError. + """ + zf = zipfile.Path(add_dirs(build_alpharep_fixture())) + with self.assertRaises(FileNotFoundError): + zf.joinpath('z').open() + def test_read(self): for alpharep in self.zipfile_alpharep(): root = zipfile.Path(alpharep) @@ -2986,6 +3013,12 @@ def test_implied_dirs_performance(self): data = ['/'.join(string.ascii_lowercase + str(n)) for n in range(10000)] zipfile.CompleteDirs._implied_dirs(data) + def test_read_does_not_close(self): + for alpharep in self.zipfile_ondisk(): + with zipfile.ZipFile(alpharep) as file: + for rep in range(2): + zipfile.Path(file, 'a.txt').read_text() + if __name__ == "__main__": unittest.main() diff --git a/Lib/zipfile.py b/Lib/zipfile.py index 816f8582bbf6d..da3e40e5dbd41 100644 --- a/Lib/zipfile.py +++ b/Lib/zipfile.py @@ -2197,13 +2197,12 @@ def make(cls, source): if not isinstance(source, ZipFile): return cls(source) - # Only allow for FastPath when supplied zipfile is read-only + # Only allow for FastLookup when supplied zipfile is read-only if 'r' not in source.mode: cls = CompleteDirs - res = cls.__new__(cls) - vars(res).update(vars(source)) - return res + source.__class__ = cls + return source class FastLookup(CompleteDirs): @@ -2292,17 +2291,29 @@ class Path: __repr = "{self.__class__.__name__}({self.root.filename!r}, {self.at!r})" def __init__(self, root, at=""): + """ + Construct a Path from a ZipFile or filename. + + Note: When the source is an existing ZipFile object, + its type (__class__) will be mutated to a + specialized type. If the caller wishes to retain the + original type, the caller should either create a + separate ZipFile object or pass a filename. + """ self.root = FastLookup.make(root) self.at = at - def open(self, mode='r', *args, **kwargs): + def open(self, mode='r', *args, pwd=None, **kwargs): """ Open this entry as text or binary following the semantics of ``pathlib.Path.open()`` by passing arguments through to io.TextIOWrapper(). """ - pwd = kwargs.pop('pwd', None) + if self.is_dir(): + raise IsADirectoryError(self) zip_mode = mode[0] + if not self.exists() and zip_mode == 'r': + raise FileNotFoundError(self) stream = self.root.open(self.at, zip_mode, pwd=pwd) if 'b' in mode: if args or kwargs: diff --git a/Misc/NEWS.d/next/Library/2020-09-23-03-33-37.bpo-40564.iXQqMq.rst b/Misc/NEWS.d/next/Library/2020-09-23-03-33-37.bpo-40564.iXQqMq.rst new file mode 100644 index 0000000000000..085534734ec94 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-09-23-03-33-37.bpo-40564.iXQqMq.rst @@ -0,0 +1 @@ +In ``zipfile.Path``, mutate the passed ZipFile object type instead of making a copy. Prevents issues when both the local copy and the caller?s copy attempt to close the same file handle. \ No newline at end of file From webhook-mailer at python.org Sat Oct 3 11:51:21 2020 From: webhook-mailer at python.org (Andre Delfino) Date: Sat, 03 Oct 2020 15:51:21 -0000 Subject: [Python-checkins] [doc] Fix link to abc.collections.Iterable (GH-22520) Message-ID: https://github.com/python/cpython/commit/3fe614893742faee3c64e6d974e11329a496424f commit: 3fe614893742faee3c64e6d974e11329a496424f branch: master author: Andre Delfino committer: GitHub date: 2020-10-03T08:51:13-07:00 summary: [doc] Fix link to abc.collections.Iterable (GH-22520) Missed this occurrence before, sorry. Also changed "the PEP" to "PEP". Automerge-Triggered-By: @gvanrossum files: M Doc/library/typing.rst diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 35e0445889b17..e2ae539d957e6 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -406,10 +406,10 @@ Initially :pep:`484` defined Python static type system as using a class ``B`` is expected if and only if ``A`` is a subclass of ``B``. This requirement previously also applied to abstract base classes, such as -:class:`Iterable`. The problem with this approach is that a class had +:class:`~collections.abc.Iterable`. The problem with this approach is that a class had to be explicitly marked to support them, which is unpythonic and unlike what one would normally do in idiomatic dynamically typed Python code. -For example, this conforms to the :pep:`484`:: +For example, this conforms to :pep:`484`:: from collections.abc import Sized, Iterable, Iterator From webhook-mailer at python.org Sat Oct 3 12:00:42 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Sat, 03 Oct 2020 16:00:42 -0000 Subject: [Python-checkins] [doc] Fix link to abc.collections.Iterable (GH-22520) Message-ID: https://github.com/python/cpython/commit/c3cd440e7b2156cc8990427700eb580d839211a3 commit: c3cd440e7b2156cc8990427700eb580d839211a3 branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-03T09:00:38-07:00 summary: [doc] Fix link to abc.collections.Iterable (GH-22520) Missed this occurrence before, sorry. Also changed "the PEP" to "PEP". Automerge-Triggered-By: @gvanrossum (cherry picked from commit 3fe614893742faee3c64e6d974e11329a496424f) Co-authored-by: Andre Delfino files: M Doc/library/typing.rst diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 0706bc870ea3d..9785f79287674 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -402,10 +402,10 @@ Initially :pep:`484` defined Python static type system as using a class ``B`` is expected if and only if ``A`` is a subclass of ``B``. This requirement previously also applied to abstract base classes, such as -:class:`Iterable`. The problem with this approach is that a class had +:class:`~collections.abc.Iterable`. The problem with this approach is that a class had to be explicitly marked to support them, which is unpythonic and unlike what one would normally do in idiomatic dynamically typed Python code. -For example, this conforms to the :pep:`484`:: +For example, this conforms to :pep:`484`:: from typing import Sized, Iterable, Iterator From webhook-mailer at python.org Sat Oct 3 12:13:18 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Sat, 03 Oct 2020 16:13:18 -0000 Subject: [Python-checkins] [doc] Fix link to abc.collections.Iterable (GH-22520) Message-ID: https://github.com/python/cpython/commit/e89253756c00ca71ff6476114a052d30d6de3af2 commit: e89253756c00ca71ff6476114a052d30d6de3af2 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-03T09:13:14-07:00 summary: [doc] Fix link to abc.collections.Iterable (GH-22520) Missed this occurrence before, sorry. Also changed "the PEP" to "PEP". Automerge-Triggered-By: @gvanrossum (cherry picked from commit 3fe614893742faee3c64e6d974e11329a496424f) Co-authored-by: Andre Delfino files: M Doc/library/typing.rst diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index a4343d7e39374..bd6814f820120 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -406,10 +406,10 @@ Initially :pep:`484` defined Python static type system as using a class ``B`` is expected if and only if ``A`` is a subclass of ``B``. This requirement previously also applied to abstract base classes, such as -:class:`Iterable`. The problem with this approach is that a class had +:class:`~collections.abc.Iterable`. The problem with this approach is that a class had to be explicitly marked to support them, which is unpythonic and unlike what one would normally do in idiomatic dynamically typed Python code. -For example, this conforms to the :pep:`484`:: +For example, this conforms to :pep:`484`:: from collections.abc import Sized, Iterable, Iterator From webhook-mailer at python.org Sat Oct 3 13:17:03 2020 From: webhook-mailer at python.org (Dong-hee Na) Date: Sat, 03 Oct 2020 17:17:03 -0000 Subject: [Python-checkins] bpo-41922: Use PEP 590 vectorcall to speed up reversed() (GH-22523) Message-ID: https://github.com/python/cpython/commit/d646e91f5c4f4b76f96494103d440ed0b6257425 commit: d646e91f5c4f4b76f96494103d440ed0b6257425 branch: master author: Dong-hee Na committer: GitHub date: 2020-10-04T02:16:56+09:00 summary: bpo-41922: Use PEP 590 vectorcall to speed up reversed() (GH-22523) files: A Misc/NEWS.d/next/Core and Builtins/2020-10-04-01-02-58.bpo-41922.kHGT8I.rst M Objects/enumobject.c diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-10-04-01-02-58.bpo-41922.kHGT8I.rst b/Misc/NEWS.d/next/Core and Builtins/2020-10-04-01-02-58.bpo-41922.kHGT8I.rst new file mode 100644 index 0000000000000..3c4de2c93555f --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2020-10-04-01-02-58.bpo-41922.kHGT8I.rst @@ -0,0 +1,2 @@ +Speed up calls to ``reversed()`` by using the :pep:`590` ``vectorcall`` +calling convention. Patch by Dong-hee Na. diff --git a/Objects/enumobject.c b/Objects/enumobject.c index 4a83bb45aa667..9d8449bb30f2a 100644 --- a/Objects/enumobject.c +++ b/Objects/enumobject.c @@ -314,6 +314,24 @@ reversed_new_impl(PyTypeObject *type, PyObject *seq) return (PyObject *)ro; } +static PyObject * +reversed_vectorcall(PyObject *type, PyObject * const*args, + size_t nargsf, PyObject *kwnames) +{ + assert(PyType_Check(type)); + + if (!_PyArg_NoKwnames("reversed", kwnames)) { + return NULL; + } + + Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); + if (!_PyArg_CheckPositional("reversed", nargs, 1, 1)) { + return NULL; + } + + return reversed_new_impl((PyTypeObject *)type, args[0]); +} + static void reversed_dealloc(reversedobject *ro) { @@ -445,4 +463,5 @@ PyTypeObject PyReversed_Type = { PyType_GenericAlloc, /* tp_alloc */ reversed_new, /* tp_new */ PyObject_GC_Del, /* tp_free */ + .tp_vectorcall = (vectorcallfunc)reversed_vectorcall, }; From webhook-mailer at python.org Sat Oct 3 15:45:59 2020 From: webhook-mailer at python.org (Pablo Galindo) Date: Sat, 03 Oct 2020 19:45:59 -0000 Subject: [Python-checkins] bpo-41840: Report module-level globals as both local and global in the symtable module (GH-22391) Message-ID: https://github.com/python/cpython/commit/fb0a4651f1be4ad936f8277478f73f262d8eeb72 commit: fb0a4651f1be4ad936f8277478f73f262d8eeb72 branch: master author: Pablo Galindo committer: GitHub date: 2020-10-03T20:45:55+01:00 summary: bpo-41840: Report module-level globals as both local and global in the symtable module (GH-22391) files: A Misc/NEWS.d/next/Library/2020-09-23-23-17-59.bpo-41840.QRFr4L.rst M Lib/symtable.py M Lib/test/test_symtable.py diff --git a/Lib/symtable.py b/Lib/symtable.py index 9ff27ef74ffe8..98db1e2557d37 100644 --- a/Lib/symtable.py +++ b/Lib/symtable.py @@ -39,7 +39,7 @@ def __call__(self, table, filename): _newSymbolTable = SymbolTableFactory() -class SymbolTable(object): +class SymbolTable: def __init__(self, raw_table, filename): self._table = raw_table @@ -52,7 +52,7 @@ def __repr__(self): else: kind = "%s " % self.__class__.__name__ - if self._table.name == "global": + if self._table.name == "top": return "<{0}SymbolTable for module {1}>".format(kind, self._filename) else: return "<{0}SymbolTable for {1} in {2}>".format(kind, @@ -124,7 +124,9 @@ def lookup(self, name): if sym is None: flags = self._table.symbols[name] namespaces = self.__check_children(name) - sym = self._symbols[name] = Symbol(name, flags, namespaces) + module_scope = (self._table.name == "top") + sym = self._symbols[name] = Symbol(name, flags, namespaces, + module_scope=module_scope) return sym def get_symbols(self): @@ -214,13 +216,14 @@ def get_methods(self): return self.__methods -class Symbol(object): +class Symbol: - def __init__(self, name, flags, namespaces=None): + def __init__(self, name, flags, namespaces=None, *, module_scope=False): self.__name = name self.__flags = flags self.__scope = (flags >> SCOPE_OFF) & SCOPE_MASK # like PyST_GetScope() self.__namespaces = namespaces or () + self.__module_scope = module_scope def __repr__(self): return "".format(self.__name) @@ -244,7 +247,8 @@ def is_parameter(self): def is_global(self): """Return *True* if the sysmbol is global. """ - return bool(self.__scope in (GLOBAL_IMPLICIT, GLOBAL_EXPLICIT)) + return bool(self.__scope in (GLOBAL_IMPLICIT, GLOBAL_EXPLICIT) + or (self.__module_scope and self.__flags & DEF_BOUND)) def is_nonlocal(self): """Return *True* if the symbol is nonlocal.""" @@ -258,7 +262,8 @@ def is_declared_global(self): def is_local(self): """Return *True* if the symbol is local. """ - return bool(self.__scope in (LOCAL, CELL)) + return bool(self.__scope in (LOCAL, CELL) + or (self.__module_scope and self.__flags & DEF_BOUND)) def is_annotated(self): """Return *True* if the symbol is annotated. diff --git a/Lib/test/test_symtable.py b/Lib/test/test_symtable.py index fa514917a1f02..a30e53496039b 100644 --- a/Lib/test/test_symtable.py +++ b/Lib/test/test_symtable.py @@ -11,6 +11,8 @@ glob = 42 some_var = 12 +some_non_assigned_global_var = 11 +some_assigned_global_var = 11 class Mine: instance_var = 24 @@ -19,6 +21,8 @@ def a_method(p1, p2): def spam(a, b, *var, **kw): global bar + global some_assigned_global_var + some_assigned_global_var = 12 bar = 47 some_var = 10 x = 23 @@ -88,14 +92,14 @@ def test_children(self): def test_lineno(self): self.assertEqual(self.top.get_lineno(), 0) - self.assertEqual(self.spam.get_lineno(), 12) + self.assertEqual(self.spam.get_lineno(), 14) def test_function_info(self): func = self.spam self.assertEqual(sorted(func.get_parameters()), ["a", "b", "kw", "var"]) expected = ['a', 'b', 'internal', 'kw', 'other_internal', 'some_var', 'var', 'x'] self.assertEqual(sorted(func.get_locals()), expected) - self.assertEqual(sorted(func.get_globals()), ["bar", "glob"]) + self.assertEqual(sorted(func.get_globals()), ["bar", "glob", "some_assigned_global_var"]) self.assertEqual(self.internal.get_frees(), ("x",)) def test_globals(self): @@ -106,6 +110,9 @@ def test_globals(self): self.assertFalse(self.internal.lookup("x").is_global()) self.assertFalse(self.Mine.lookup("instance_var").is_global()) self.assertTrue(self.spam.lookup("bar").is_global()) + # Module-scope globals are both global and local + self.assertTrue(self.top.lookup("some_non_assigned_global_var").is_global()) + self.assertTrue(self.top.lookup("some_assigned_global_var").is_global()) def test_nonlocal(self): self.assertFalse(self.spam.lookup("some_var").is_nonlocal()) @@ -116,6 +123,9 @@ def test_nonlocal(self): def test_local(self): self.assertTrue(self.spam.lookup("x").is_local()) self.assertFalse(self.spam.lookup("bar").is_local()) + # Module-scope globals are both global and local + self.assertTrue(self.top.lookup("some_non_assigned_global_var").is_local()) + self.assertTrue(self.top.lookup("some_assigned_global_var").is_local()) def test_free(self): self.assertTrue(self.internal.lookup("x").is_free()) @@ -234,6 +244,10 @@ def test_bytes(self): top = symtable.symtable(code, "?", "exec") self.assertIsNotNone(find_block(top, "\u017d")) + def test_symtable_repr(self): + self.assertEqual(str(self.top), "") + self.assertEqual(str(self.spam), "") + if __name__ == '__main__': unittest.main() diff --git a/Misc/NEWS.d/next/Library/2020-09-23-23-17-59.bpo-41840.QRFr4L.rst b/Misc/NEWS.d/next/Library/2020-09-23-23-17-59.bpo-41840.QRFr4L.rst new file mode 100644 index 0000000000000..e96942d8ebd07 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-09-23-23-17-59.bpo-41840.QRFr4L.rst @@ -0,0 +1,3 @@ +Fix a bug in the :mod:`symtable` module that was causing module-scope global +variables to not be reported as both local and global. Patch by Pablo +Galindo. From webhook-mailer at python.org Sat Oct 3 16:23:07 2020 From: webhook-mailer at python.org (Pablo Galindo) Date: Sat, 03 Oct 2020 20:23:07 -0000 Subject: [Python-checkins] [3.9] bpo-41840: Report module-level globals as both local and global in the symtable module (GH-22391). (GH-22528) Message-ID: https://github.com/python/cpython/commit/7b7aa94e51a396365df41f86b7b64a3b74481a6c commit: 7b7aa94e51a396365df41f86b7b64a3b74481a6c branch: 3.9 author: Pablo Galindo committer: GitHub date: 2020-10-03T21:23:03+01:00 summary: [3.9] bpo-41840: Report module-level globals as both local and global in the symtable module (GH-22391). (GH-22528) (cherry picked from commit fb0a4651f1be4ad936f8277478f73f262d8eeb72) Co-authored-by: Pablo Galindo files: A Misc/NEWS.d/next/Library/2020-09-23-23-17-59.bpo-41840.QRFr4L.rst M Lib/symtable.py M Lib/test/test_symtable.py diff --git a/Lib/symtable.py b/Lib/symtable.py index a711676582f64..521540fe9eeec 100644 --- a/Lib/symtable.py +++ b/Lib/symtable.py @@ -34,7 +34,7 @@ def __call__(self, table, filename): _newSymbolTable = SymbolTableFactory() -class SymbolTable(object): +class SymbolTable: def __init__(self, raw_table, filename): self._table = raw_table @@ -47,7 +47,7 @@ def __repr__(self): else: kind = "%s " % self.__class__.__name__ - if self._table.name == "global": + if self._table.name == "top": return "<{0}SymbolTable for module {1}>".format(kind, self._filename) else: return "<{0}SymbolTable for {1} in {2}>".format(kind, @@ -90,7 +90,9 @@ def lookup(self, name): if sym is None: flags = self._table.symbols[name] namespaces = self.__check_children(name) - sym = self._symbols[name] = Symbol(name, flags, namespaces) + module_scope = (self._table.name == "top") + sym = self._symbols[name] = Symbol(name, flags, namespaces, + module_scope=module_scope) return sym def get_symbols(self): @@ -163,13 +165,14 @@ def get_methods(self): return self.__methods -class Symbol(object): +class Symbol: - def __init__(self, name, flags, namespaces=None): + def __init__(self, name, flags, namespaces=None, *, module_scope=False): self.__name = name self.__flags = flags self.__scope = (flags >> SCOPE_OFF) & SCOPE_MASK # like PyST_GetScope() self.__namespaces = namespaces or () + self.__module_scope = module_scope def __repr__(self): return "".format(self.__name) @@ -184,7 +187,10 @@ def is_parameter(self): return bool(self.__flags & DEF_PARAM) def is_global(self): - return bool(self.__scope in (GLOBAL_IMPLICIT, GLOBAL_EXPLICIT)) + """Return *True* if the sysmbol is global. + """ + return bool(self.__scope in (GLOBAL_IMPLICIT, GLOBAL_EXPLICIT) + or (self.__module_scope and self.__flags & DEF_BOUND)) def is_nonlocal(self): return bool(self.__flags & DEF_NONLOCAL) @@ -193,7 +199,10 @@ def is_declared_global(self): return bool(self.__scope == GLOBAL_EXPLICIT) def is_local(self): - return bool(self.__scope in (LOCAL, CELL)) + """Return *True* if the symbol is local. + """ + return bool(self.__scope in (LOCAL, CELL) + or (self.__module_scope and self.__flags & DEF_BOUND)) def is_annotated(self): return bool(self.__flags & DEF_ANNOT) diff --git a/Lib/test/test_symtable.py b/Lib/test/test_symtable.py index d19355888e4b3..6585071912a0d 100644 --- a/Lib/test/test_symtable.py +++ b/Lib/test/test_symtable.py @@ -11,6 +11,8 @@ glob = 42 some_var = 12 +some_non_assigned_global_var = 11 +some_assigned_global_var = 11 class Mine: instance_var = 24 @@ -19,6 +21,8 @@ def a_method(p1, p2): def spam(a, b, *var, **kw): global bar + global some_assigned_global_var + some_assigned_global_var = 12 bar = 47 some_var = 10 x = 23 @@ -81,14 +85,14 @@ def test_children(self): def test_lineno(self): self.assertEqual(self.top.get_lineno(), 0) - self.assertEqual(self.spam.get_lineno(), 12) + self.assertEqual(self.spam.get_lineno(), 14) def test_function_info(self): func = self.spam self.assertEqual(sorted(func.get_parameters()), ["a", "b", "kw", "var"]) expected = ['a', 'b', 'internal', 'kw', 'other_internal', 'some_var', 'var', 'x'] self.assertEqual(sorted(func.get_locals()), expected) - self.assertEqual(sorted(func.get_globals()), ["bar", "glob"]) + self.assertEqual(sorted(func.get_globals()), ["bar", "glob", "some_assigned_global_var"]) self.assertEqual(self.internal.get_frees(), ("x",)) def test_globals(self): @@ -99,6 +103,9 @@ def test_globals(self): self.assertFalse(self.internal.lookup("x").is_global()) self.assertFalse(self.Mine.lookup("instance_var").is_global()) self.assertTrue(self.spam.lookup("bar").is_global()) + # Module-scope globals are both global and local + self.assertTrue(self.top.lookup("some_non_assigned_global_var").is_global()) + self.assertTrue(self.top.lookup("some_assigned_global_var").is_global()) def test_nonlocal(self): self.assertFalse(self.spam.lookup("some_var").is_nonlocal()) @@ -109,6 +116,9 @@ def test_nonlocal(self): def test_local(self): self.assertTrue(self.spam.lookup("x").is_local()) self.assertFalse(self.spam.lookup("bar").is_local()) + # Module-scope globals are both global and local + self.assertTrue(self.top.lookup("some_non_assigned_global_var").is_local()) + self.assertTrue(self.top.lookup("some_assigned_global_var").is_local()) def test_free(self): self.assertTrue(self.internal.lookup("x").is_free()) @@ -227,6 +237,10 @@ def test_bytes(self): top = symtable.symtable(code, "?", "exec") self.assertIsNotNone(find_block(top, "\u017d")) + def test_symtable_repr(self): + self.assertEqual(str(self.top), "") + self.assertEqual(str(self.spam), "") + if __name__ == '__main__': unittest.main() diff --git a/Misc/NEWS.d/next/Library/2020-09-23-23-17-59.bpo-41840.QRFr4L.rst b/Misc/NEWS.d/next/Library/2020-09-23-23-17-59.bpo-41840.QRFr4L.rst new file mode 100644 index 0000000000000..e96942d8ebd07 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-09-23-23-17-59.bpo-41840.QRFr4L.rst @@ -0,0 +1,3 @@ +Fix a bug in the :mod:`symtable` module that was causing module-scope global +variables to not be reported as both local and global. Patch by Pablo +Galindo. From webhook-mailer at python.org Sat Oct 3 17:50:04 2020 From: webhook-mailer at python.org (Pablo Galindo) Date: Sat, 03 Oct 2020 21:50:04 -0000 Subject: [Python-checkins] [3.8] bpo-41840: Report module-level globals as both local and global in the symtable module (GH-22391). (GH-22529) Message-ID: https://github.com/python/cpython/commit/fd22823991380de2a309221748fc41cf0ba6ed40 commit: fd22823991380de2a309221748fc41cf0ba6ed40 branch: 3.8 author: Pablo Galindo committer: GitHub date: 2020-10-03T22:50:00+01:00 summary: [3.8] bpo-41840: Report module-level globals as both local and global in the symtable module (GH-22391). (GH-22529) (cherry picked from commit fb0a4651f1be4ad936f8277478f73f262d8eeb72) Co-authored-by: Pablo Galindo files: A Misc/NEWS.d/next/Library/2020-09-23-23-17-59.bpo-41840.QRFr4L.rst M Lib/symtable.py M Lib/test/test_symtable.py diff --git a/Lib/symtable.py b/Lib/symtable.py index ac0a64ff58f79..c78b5821408da 100644 --- a/Lib/symtable.py +++ b/Lib/symtable.py @@ -34,7 +34,7 @@ def __call__(self, table, filename): _newSymbolTable = SymbolTableFactory() -class SymbolTable(object): +class SymbolTable: def __init__(self, raw_table, filename): self._table = raw_table @@ -47,7 +47,7 @@ def __repr__(self): else: kind = "%s " % self.__class__.__name__ - if self._table.name == "global": + if self._table.name == "top": return "<{0}SymbolTable for module {1}>".format(kind, self._filename) else: return "<{0}SymbolTable for {1} in {2}>".format(kind, @@ -94,7 +94,9 @@ def lookup(self, name): if sym is None: flags = self._table.symbols[name] namespaces = self.__check_children(name) - sym = self._symbols[name] = Symbol(name, flags, namespaces) + module_scope = (self._table.name == "top") + sym = self._symbols[name] = Symbol(name, flags, namespaces, + module_scope=module_scope) return sym def get_symbols(self): @@ -167,13 +169,14 @@ def get_methods(self): return self.__methods -class Symbol(object): +class Symbol: - def __init__(self, name, flags, namespaces=None): + def __init__(self, name, flags, namespaces=None, *, module_scope=False): self.__name = name self.__flags = flags self.__scope = (flags >> SCOPE_OFF) & SCOPE_MASK # like PyST_GetScope() self.__namespaces = namespaces or () + self.__module_scope = module_scope def __repr__(self): return "".format(self.__name) @@ -188,7 +191,10 @@ def is_parameter(self): return bool(self.__flags & DEF_PARAM) def is_global(self): - return bool(self.__scope in (GLOBAL_IMPLICIT, GLOBAL_EXPLICIT)) + """Return *True* if the sysmbol is global. + """ + return bool(self.__scope in (GLOBAL_IMPLICIT, GLOBAL_EXPLICIT) + or (self.__module_scope and self.__flags & DEF_BOUND)) def is_nonlocal(self): return bool(self.__flags & DEF_NONLOCAL) @@ -197,7 +203,10 @@ def is_declared_global(self): return bool(self.__scope == GLOBAL_EXPLICIT) def is_local(self): - return bool(self.__scope in (LOCAL, CELL)) + """Return *True* if the symbol is local. + """ + return bool(self.__scope in (LOCAL, CELL) + or (self.__module_scope and self.__flags & DEF_BOUND)) def is_annotated(self): return bool(self.__flags & DEF_ANNOT) diff --git a/Lib/test/test_symtable.py b/Lib/test/test_symtable.py index 98d718c8ab1b1..adf1728dc6215 100644 --- a/Lib/test/test_symtable.py +++ b/Lib/test/test_symtable.py @@ -11,6 +11,8 @@ glob = 42 some_var = 12 +some_non_assigned_global_var = 11 +some_assigned_global_var = 11 class Mine: instance_var = 24 @@ -19,6 +21,8 @@ def a_method(p1, p2): def spam(a, b, *var, **kw): global bar + global some_assigned_global_var + some_assigned_global_var = 12 bar = 47 some_var = 10 x = 23 @@ -82,14 +86,14 @@ def test_children(self): def test_lineno(self): self.assertEqual(self.top.get_lineno(), 0) - self.assertEqual(self.spam.get_lineno(), 12) + self.assertEqual(self.spam.get_lineno(), 14) def test_function_info(self): func = self.spam self.assertEqual(sorted(func.get_parameters()), ["a", "b", "kw", "var"]) expected = ['a', 'b', 'internal', 'kw', 'other_internal', 'some_var', 'var', 'x'] self.assertEqual(sorted(func.get_locals()), expected) - self.assertEqual(sorted(func.get_globals()), ["bar", "glob"]) + self.assertEqual(sorted(func.get_globals()), ["bar", "glob", "some_assigned_global_var"]) self.assertEqual(self.internal.get_frees(), ("x",)) def test_globals(self): @@ -100,6 +104,9 @@ def test_globals(self): self.assertFalse(self.internal.lookup("x").is_global()) self.assertFalse(self.Mine.lookup("instance_var").is_global()) self.assertTrue(self.spam.lookup("bar").is_global()) + # Module-scope globals are both global and local + self.assertTrue(self.top.lookup("some_non_assigned_global_var").is_global()) + self.assertTrue(self.top.lookup("some_assigned_global_var").is_global()) def test_nonlocal(self): self.assertFalse(self.spam.lookup("some_var").is_nonlocal()) @@ -110,6 +117,9 @@ def test_nonlocal(self): def test_local(self): self.assertTrue(self.spam.lookup("x").is_local()) self.assertFalse(self.spam.lookup("bar").is_local()) + # Module-scope globals are both global and local + self.assertTrue(self.top.lookup("some_non_assigned_global_var").is_local()) + self.assertTrue(self.top.lookup("some_assigned_global_var").is_local()) def test_free(self): self.assertTrue(self.internal.lookup("x").is_free()) @@ -228,6 +238,10 @@ def test_bytes(self): top = symtable.symtable(code, "?", "exec") self.assertIsNotNone(find_block(top, "\u017d")) + def test_symtable_repr(self): + self.assertEqual(str(self.top), "") + self.assertEqual(str(self.spam), "") + if __name__ == '__main__': unittest.main() diff --git a/Misc/NEWS.d/next/Library/2020-09-23-23-17-59.bpo-41840.QRFr4L.rst b/Misc/NEWS.d/next/Library/2020-09-23-23-17-59.bpo-41840.QRFr4L.rst new file mode 100644 index 0000000000000..e96942d8ebd07 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-09-23-23-17-59.bpo-41840.QRFr4L.rst @@ -0,0 +1,3 @@ +Fix a bug in the :mod:`symtable` module that was causing module-scope global +variables to not be reported as both local and global. Patch by Pablo +Galindo. From webhook-mailer at python.org Sat Oct 3 18:11:05 2020 From: webhook-mailer at python.org (Andre Delfino) Date: Sat, 03 Oct 2020 22:11:05 -0000 Subject: [Python-checkins] [doc] Use list[int] instead of List[int] (etc.) in a few more places (GH-22524) Message-ID: https://github.com/python/cpython/commit/7f54e563dc150cd670ca8df678437455c3a7f2cd commit: 7f54e563dc150cd670ca8df678437455c3a7f2cd branch: master author: Andre Delfino committer: GitHub date: 2020-10-03T15:10:59-07:00 summary: [doc] Use list[int] instead of List[int] (etc.) in a few more places (GH-22524) This changes a few occurrences left behind by #22340. Automerge-Triggered-By: @gvanrossum files: M Doc/library/dataclasses.rst M Doc/library/typing.rst diff --git a/Doc/library/dataclasses.rst b/Doc/library/dataclasses.rst index 6e74af062d9e7..e706f7fcc566d 100644 --- a/Doc/library/dataclasses.rst +++ b/Doc/library/dataclasses.rst @@ -188,7 +188,7 @@ Module-level decorators, classes, and functions @dataclass class C: - mylist: List[int] = field(default_factory=list) + mylist: list[int] = field(default_factory=list) c = C() c.mylist += [1, 2, 3] @@ -301,7 +301,7 @@ Module-level decorators, classes, and functions @dataclass class C: - mylist: List[Point] + mylist: list[Point] p = Point(10, 20) assert asdict(p) == {'x': 10, 'y': 20} diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index e2ae539d957e6..f712dfea13f2c 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -661,7 +661,7 @@ These can be used as types in annotations using ``[]``, each having a unique syn and should not be set on instances of that class. Usage:: class Starship: - stats: ClassVar[Dict[str, int]] = {} # class variable + stats: ClassVar[dict[str, int]] = {} # class variable damage: int = 10 # instance variable :data:`ClassVar` accepts only types and cannot be further subscribed. @@ -774,10 +774,10 @@ These can be used as types in annotations using ``[]``, each having a unique syn * ``Annotated`` can be used with nested and generic aliases:: T = TypeVar('T') - Vec = Annotated[List[Tuple[T, T]], MaxLen(10)] + Vec = Annotated[list[tuple[T, T]], MaxLen(10)] V = Vec[int] - V == Annotated[List[Tuple[int, int]], MaxLen(10)] + V == Annotated[list[tuple[int, int]], MaxLen(10)] .. versionadded:: 3.9 @@ -1540,7 +1540,7 @@ Functions and decorators def process(response: None) -> None: ... @overload - def process(response: int) -> Tuple[int, str]: + def process(response: int) -> tuple[int, str]: ... @overload def process(response: bytes) -> str: @@ -1679,8 +1679,8 @@ Introspection helpers .. class:: ForwardRef A class used for internal typing representation of string forward references. - For example, ``List["SomeClass"]`` is implicitly transformed into - ``List[ForwardRef("SomeClass")]``. This class should not be instantiated by + For example, ``list["SomeClass"]`` is implicitly transformed into + ``list[ForwardRef("SomeClass")]``. This class should not be instantiated by a user, but may be used by introspection tools. Constant From webhook-mailer at python.org Sat Oct 3 18:33:45 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Sat, 03 Oct 2020 22:33:45 -0000 Subject: [Python-checkins] [doc] Use list[int] instead of List[int] (etc.) in a few more places (GH-22524) Message-ID: https://github.com/python/cpython/commit/5f9a08d95f5c37dad901d5321e0b35928328d8d8 commit: 5f9a08d95f5c37dad901d5321e0b35928328d8d8 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-03T15:33:30-07:00 summary: [doc] Use list[int] instead of List[int] (etc.) in a few more places (GH-22524) This changes a few occurrences left behind by GH-22340. Automerge-Triggered-By: @gvanrossum (cherry picked from commit 7f54e563dc150cd670ca8df678437455c3a7f2cd) Co-authored-by: Andre Delfino files: M Doc/library/dataclasses.rst M Doc/library/typing.rst diff --git a/Doc/library/dataclasses.rst b/Doc/library/dataclasses.rst index 6e74af062d9e7..e706f7fcc566d 100644 --- a/Doc/library/dataclasses.rst +++ b/Doc/library/dataclasses.rst @@ -188,7 +188,7 @@ Module-level decorators, classes, and functions @dataclass class C: - mylist: List[int] = field(default_factory=list) + mylist: list[int] = field(default_factory=list) c = C() c.mylist += [1, 2, 3] @@ -301,7 +301,7 @@ Module-level decorators, classes, and functions @dataclass class C: - mylist: List[Point] + mylist: list[Point] p = Point(10, 20) assert asdict(p) == {'x': 10, 'y': 20} diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index bd6814f820120..3900e49679a38 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -661,7 +661,7 @@ These can be used as types in annotations using ``[]``, each having a unique syn and should not be set on instances of that class. Usage:: class Starship: - stats: ClassVar[Dict[str, int]] = {} # class variable + stats: ClassVar[dict[str, int]] = {} # class variable damage: int = 10 # instance variable :data:`ClassVar` accepts only types and cannot be further subscribed. @@ -774,10 +774,10 @@ These can be used as types in annotations using ``[]``, each having a unique syn * ``Annotated`` can be used with nested and generic aliases:: T = TypeVar('T') - Vec = Annotated[List[Tuple[T, T]], MaxLen(10)] + Vec = Annotated[list[tuple[T, T]], MaxLen(10)] V = Vec[int] - V == Annotated[List[Tuple[int, int]], MaxLen(10)] + V == Annotated[list[tuple[int, int]], MaxLen(10)] .. versionadded:: 3.9 @@ -1540,7 +1540,7 @@ Functions and decorators def process(response: None) -> None: ... @overload - def process(response: int) -> Tuple[int, str]: + def process(response: int) -> tuple[int, str]: ... @overload def process(response: bytes) -> str: @@ -1664,8 +1664,8 @@ Introspection helpers .. class:: ForwardRef A class used for internal typing representation of string forward references. - For example, ``List["SomeClass"]`` is implicitly transformed into - ``List[ForwardRef("SomeClass")]``. This class should not be instantiated by + For example, ``list["SomeClass"]`` is implicitly transformed into + ``list[ForwardRef("SomeClass")]``. This class should not be instantiated by a user, but may be used by introspection tools. Constant From webhook-mailer at python.org Sat Oct 3 20:46:48 2020 From: webhook-mailer at python.org (Batuhan Taskaya) Date: Sun, 04 Oct 2020 00:46:48 -0000 Subject: [Python-checkins] bpo-41887: omit leading spaces/tabs on ast.literal_eval (#22469) Message-ID: https://github.com/python/cpython/commit/e799aa8b92c195735f379940acd9925961ad04ec commit: e799aa8b92c195735f379940acd9925961ad04ec branch: master author: Batuhan Taskaya committer: GitHub date: 2020-10-03T17:46:44-07:00 summary: bpo-41887: omit leading spaces/tabs on ast.literal_eval (#22469) Also document that eval() does this (the same way). files: A Misc/NEWS.d/next/Library/2020-09-30-23-49-42.bpo-41887.-ee2S-.rst M Doc/library/ast.rst M Doc/library/functions.rst M Lib/ast.py M Lib/test/test_ast.py diff --git a/Doc/library/ast.rst b/Doc/library/ast.rst index 755c60fba6411..62138efcce911 100644 --- a/Doc/library/ast.rst +++ b/Doc/library/ast.rst @@ -1586,6 +1586,9 @@ and classes for traversing abstract syntax trees: .. versionchanged:: 3.9 Now supports creating empty sets with ``'set()'``. + .. versionchanged:: 3.10 + For string inputs, leading spaces and tabs are now stripped. + .. function:: get_docstring(node, clean=True) @@ -1820,4 +1823,4 @@ to stdout. Otherwise, the content is read from stdin. `Parso `_ is a Python parser that supports error recovery and round-trip parsing for different Python versions (in multiple Python versions). Parso is also able to list multiple syntax errors - in your python file. \ No newline at end of file + in your python file. diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index c49bb0c9de70c..263c52a63dea8 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -506,6 +506,9 @@ are always available. They are listed here in alphabetical order. returns the current global and local dictionary, respectively, which may be useful to pass around for use by :func:`eval` or :func:`exec`. + If the given source is a string, then leading and trailing spaces and tabs + are stripped. + See :func:`ast.literal_eval` for a function that can safely evaluate strings with expressions containing only literals. diff --git a/Lib/ast.py b/Lib/ast.py index d860917f4d03a..d8bd3373701de 100644 --- a/Lib/ast.py +++ b/Lib/ast.py @@ -59,7 +59,7 @@ def literal_eval(node_or_string): sets, booleans, and None. """ if isinstance(node_or_string, str): - node_or_string = parse(node_or_string, mode='eval') + node_or_string = parse(node_or_string.lstrip(" \t"), mode='eval') if isinstance(node_or_string, Expression): node_or_string = node_or_string.body def _raise_malformed_node(node): diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py index 5f57ce8724482..be4b0f78ce905 100644 --- a/Lib/test/test_ast.py +++ b/Lib/test/test_ast.py @@ -1005,6 +1005,12 @@ def test_literal_eval_malformed_dict_nodes(self): malformed = ast.Dict(keys=[ast.Constant(1)], values=[ast.Constant(2), ast.Constant(3)]) self.assertRaises(ValueError, ast.literal_eval, malformed) + def test_literal_eval_trailing_ws(self): + self.assertEqual(ast.literal_eval(" -1"), -1) + self.assertEqual(ast.literal_eval("\t\t-1"), -1) + self.assertEqual(ast.literal_eval(" \t -1"), -1) + self.assertRaises(IndentationError, ast.literal_eval, "\n -1") + def test_bad_integer(self): # issue13436: Bad error message with invalid numeric values body = [ast.ImportFrom(module='time', diff --git a/Misc/NEWS.d/next/Library/2020-09-30-23-49-42.bpo-41887.-ee2S-.rst b/Misc/NEWS.d/next/Library/2020-09-30-23-49-42.bpo-41887.-ee2S-.rst new file mode 100644 index 0000000000000..2a43ab3f2890c --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-09-30-23-49-42.bpo-41887.-ee2S-.rst @@ -0,0 +1,2 @@ +Strip leading spaces and tabs on :func:`ast.literal_eval`. Also document +stripping of spaces and tabs for :func:`eval`. From webhook-mailer at python.org Sun Oct 4 09:16:17 2020 From: webhook-mailer at python.org (Irit Katriel) Date: Sun, 04 Oct 2020 13:16:17 -0000 Subject: [Python-checkins] bpo-41898: add caveat on root logger seeing all messages in assertLogs doc (GH-22526) Message-ID: https://github.com/python/cpython/commit/1ed54435268b285964141fb74d47ceaa33ea79ab commit: 1ed54435268b285964141fb74d47ceaa33ea79ab branch: master author: Irit Katriel committer: GitHub date: 2020-10-04T14:16:04+01:00 summary: bpo-41898: add caveat on root logger seeing all messages in assertLogs doc (GH-22526) files: M Doc/library/unittest.rst diff --git a/Doc/library/unittest.rst b/Doc/library/unittest.rst index a52df9ee01578..f04ec91270eeb 100644 --- a/Doc/library/unittest.rst +++ b/Doc/library/unittest.rst @@ -1091,7 +1091,8 @@ Test cases If given, *logger* should be a :class:`logging.Logger` object or a :class:`str` giving the name of a logger. The default is the root - logger, which will catch all messages. + logger, which will catch all messages that were not blocked by a + non-propagating descendent logger. If given, *level* should be either a numeric logging level or its string equivalent (for example either ``"ERROR"`` or From webhook-mailer at python.org Sun Oct 4 09:27:22 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Sun, 04 Oct 2020 13:27:22 -0000 Subject: [Python-checkins] bpo-41898: add caveat on root logger seeing all messages in assertLogs doc (GH-22526) (GH-22537) Message-ID: https://github.com/python/cpython/commit/b2611fac05ed391d68f09781903aae7394fab832 commit: b2611fac05ed391d68f09781903aae7394fab832 branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-04T14:27:14+01:00 summary: bpo-41898: add caveat on root logger seeing all messages in assertLogs doc (GH-22526) (GH-22537) (cherry picked from commit 1ed54435268b285964141fb74d47ceaa33ea79ab) files: M Doc/library/unittest.rst diff --git a/Doc/library/unittest.rst b/Doc/library/unittest.rst index 67c5fa6117374..7626a402f375f 100644 --- a/Doc/library/unittest.rst +++ b/Doc/library/unittest.rst @@ -1088,7 +1088,8 @@ Test cases If given, *logger* should be a :class:`logging.Logger` object or a :class:`str` giving the name of a logger. The default is the root - logger, which will catch all messages. + logger, which will catch all messages that were not blocked by a + non-propagating descendent logger. If given, *level* should be either a numeric logging level or its string equivalent (for example either ``"ERROR"`` or From webhook-mailer at python.org Sun Oct 4 11:38:11 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Sun, 04 Oct 2020 15:38:11 -0000 Subject: [Python-checkins] bpo-41898: add caveat on root logger seeing all messages in assertLogs doc (GH-22526) (GH-22540) Message-ID: https://github.com/python/cpython/commit/faa8c6a8f1fe9c3bac31061e8a59a686b983ebb8 commit: faa8c6a8f1fe9c3bac31061e8a59a686b983ebb8 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-04T16:38:03+01:00 summary: bpo-41898: add caveat on root logger seeing all messages in assertLogs doc (GH-22526) (GH-22540) (cherry picked from commit 1ed54435268b285964141fb74d47ceaa33ea79ab) files: M Doc/library/unittest.rst diff --git a/Doc/library/unittest.rst b/Doc/library/unittest.rst index 285bb9e8cbe62..8a4fd25322e33 100644 --- a/Doc/library/unittest.rst +++ b/Doc/library/unittest.rst @@ -1088,7 +1088,8 @@ Test cases If given, *logger* should be a :class:`logging.Logger` object or a :class:`str` giving the name of a logger. The default is the root - logger, which will catch all messages. + logger, which will catch all messages that were not blocked by a + non-propagating descendent logger. If given, *level* should be either a numeric logging level or its string equivalent (for example either ``"ERROR"`` or From webhook-mailer at python.org Sun Oct 4 12:09:33 2020 From: webhook-mailer at python.org (Hansraj Das) Date: Sun, 04 Oct 2020 16:09:33 -0000 Subject: [Python-checkins] Delete extra 'the' from `Formatter` class docstring (GH-22530) Message-ID: https://github.com/python/cpython/commit/a619af43ccd10bab363b1e59814a2e2727422545 commit: a619af43ccd10bab363b1e59814a2e2727422545 branch: master author: Hansraj Das committer: GitHub date: 2020-10-04T17:09:26+01:00 summary: Delete extra 'the' from `Formatter` class docstring (GH-22530) files: M Lib/logging/__init__.py diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py index d8a88db378436..787cb4eefa106 100644 --- a/Lib/logging/__init__.py +++ b/Lib/logging/__init__.py @@ -523,7 +523,7 @@ class Formatter(object): responsible for converting a LogRecord to (usually) a string which can be interpreted by either a human or an external system. The base Formatter allows a formatting string to be specified. If none is supplied, the - the style-dependent default value, "%(message)s", "{message}", or + style-dependent default value, "%(message)s", "{message}", or "${message}", is used. The Formatter can be initialized with a format string which makes use of From webhook-mailer at python.org Sun Oct 4 12:32:00 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Sun, 04 Oct 2020 16:32:00 -0000 Subject: [Python-checkins] Delete extra 'the' from `Formatter` class docstring (GH-22530) (GH-22542) Message-ID: https://github.com/python/cpython/commit/9c2d46ac20e87dc82db155b0e01b2511934e7f63 commit: 9c2d46ac20e87dc82db155b0e01b2511934e7f63 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-04T17:31:52+01:00 summary: Delete extra 'the' from `Formatter` class docstring (GH-22530) (GH-22542) (cherry picked from commit a619af43ccd10bab363b1e59814a2e2727422545) files: M Lib/logging/__init__.py diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py index 403dc81b13ef4..4013e56fc3de4 100644 --- a/Lib/logging/__init__.py +++ b/Lib/logging/__init__.py @@ -509,7 +509,7 @@ class Formatter(object): responsible for converting a LogRecord to (usually) a string which can be interpreted by either a human or an external system. The base Formatter allows a formatting string to be specified. If none is supplied, the - the style-dependent default value, "%(message)s", "{message}", or + style-dependent default value, "%(message)s", "{message}", or "${message}", is used. The Formatter can be initialized with a format string which makes use of From webhook-mailer at python.org Sun Oct 4 12:32:19 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Sun, 04 Oct 2020 16:32:19 -0000 Subject: [Python-checkins] Delete extra 'the' from `Formatter` class docstring (GH-22530) (GH-22543) Message-ID: https://github.com/python/cpython/commit/c5f12126e98f223811dfc5ffd5db6b6f7636804c commit: c5f12126e98f223811dfc5ffd5db6b6f7636804c branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-04T17:32:15+01:00 summary: Delete extra 'the' from `Formatter` class docstring (GH-22530) (GH-22543) (cherry picked from commit a619af43ccd10bab363b1e59814a2e2727422545) files: M Lib/logging/__init__.py diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py index 0cfaec84bac13..f5b7e2eb38910 100644 --- a/Lib/logging/__init__.py +++ b/Lib/logging/__init__.py @@ -515,7 +515,7 @@ class Formatter(object): responsible for converting a LogRecord to (usually) a string which can be interpreted by either a human or an external system. The base Formatter allows a formatting string to be specified. If none is supplied, the - the style-dependent default value, "%(message)s", "{message}", or + style-dependent default value, "%(message)s", "{message}", or "${message}", is used. The Formatter can be initialized with a format string which makes use of From webhook-mailer at python.org Sun Oct 4 12:45:39 2020 From: webhook-mailer at python.org (Pablo Galindo) Date: Sun, 04 Oct 2020 16:45:39 -0000 Subject: [Python-checkins] bpo-41490: Bump vendored pip to version 20.2.3 (#22527) Message-ID: https://github.com/python/cpython/commit/2cc6dc9896771ef3615abbb5ba80939a2f644a08 commit: 2cc6dc9896771ef3615abbb5ba80939a2f644a08 branch: master author: Pablo Galindo committer: GitHub date: 2020-10-04T18:45:31+02:00 summary: bpo-41490: Bump vendored pip to version 20.2.3 (#22527) files: A Lib/ensurepip/_bundled/pip-20.2.3-py2.py3-none-any.whl D Lib/ensurepip/_bundled/pip-20.1.1-py2.py3-none-any.whl M Lib/ensurepip/__init__.py diff --git a/Lib/ensurepip/__init__.py b/Lib/ensurepip/__init__.py index 21320a83198ca..cb2882e3360fc 100644 --- a/Lib/ensurepip/__init__.py +++ b/Lib/ensurepip/__init__.py @@ -3,6 +3,7 @@ import sys import runpy import tempfile +import subprocess from importlib import resources from . import _bundled @@ -14,7 +15,7 @@ _SETUPTOOLS_VERSION = "47.1.0" -_PIP_VERSION = "20.1.1" +_PIP_VERSION = "20.2.3" _PROJECTS = [ ("setuptools", _SETUPTOOLS_VERSION, "py3"), @@ -23,22 +24,18 @@ def _run_pip(args, additional_paths=None): - # Add our bundled software to the sys.path so we can import it - if additional_paths is not None: - sys.path = additional_paths + sys.path - - # Invoke pip as if it's the main module, and catch the exit. - backup_argv = sys.argv[:] - sys.argv[1:] = args - try: - # run_module() alters sys.modules and sys.argv, but restores them at exit - runpy.run_module("pip", run_name="__main__", alter_sys=True) - except SystemExit as exc: - return exc.code - finally: - sys.argv[:] = backup_argv - - raise SystemError("pip did not exit, this should never happen") + # Run the bootstraping in a subprocess to avoid leaking any state that happens + # after pip has executed. Particulary, this avoids the case when pip holds onto + # the files in *additional_paths*, preventing us to remove them at the end of the + # invocation. + code = f""" +import runpy +import sys +sys.path = {additional_paths or []} + sys.path +sys.argv[1:] = {args} +runpy.run_module("pip", run_name="__main__", alter_sys=True) +""" + return subprocess.run([sys.executable, "-c", code], check=True).returncode def version(): diff --git a/Lib/ensurepip/_bundled/pip-20.1.1-py2.py3-none-any.whl b/Lib/ensurepip/_bundled/pip-20.2.3-py2.py3-none-any.whl similarity index 54% rename from Lib/ensurepip/_bundled/pip-20.1.1-py2.py3-none-any.whl rename to Lib/ensurepip/_bundled/pip-20.2.3-py2.py3-none-any.whl index ea1d0f7c8604a..7ebdc0f31d4e3 100644 Binary files a/Lib/ensurepip/_bundled/pip-20.1.1-py2.py3-none-any.whl and b/Lib/ensurepip/_bundled/pip-20.2.3-py2.py3-none-any.whl differ From webhook-mailer at python.org Sun Oct 4 13:11:14 2020 From: webhook-mailer at python.org (Pablo Galindo) Date: Sun, 04 Oct 2020 17:11:14 -0000 Subject: [Python-checkins] [3.9] bpo-41490: Bump vendored pip to version 20.2.3 (GH-22527). (GH-22544) Message-ID: https://github.com/python/cpython/commit/4b4d60f9287e0e52c2569876456f40efc11529b4 commit: 4b4d60f9287e0e52c2569876456f40efc11529b4 branch: 3.9 author: Pablo Galindo committer: GitHub date: 2020-10-04T19:11:10+02:00 summary: [3.9] bpo-41490: Bump vendored pip to version 20.2.3 (GH-22527). (GH-22544) (cherry picked from commit 2cc6dc9896771ef3615abbb5ba80939a2f644a08) Co-authored-by: Pablo Galindo files: A Lib/ensurepip/_bundled/pip-20.2.3-py2.py3-none-any.whl M Lib/ensurepip/__init__.py diff --git a/Lib/ensurepip/__init__.py b/Lib/ensurepip/__init__.py index 8f504e7f49b37..97dfa7ea71f8f 100644 --- a/Lib/ensurepip/__init__.py +++ b/Lib/ensurepip/__init__.py @@ -3,6 +3,7 @@ import sys import runpy import tempfile +import subprocess from importlib import resources from . import _bundled @@ -14,7 +15,7 @@ _SETUPTOOLS_VERSION = "49.2.1" -_PIP_VERSION = "20.2.1" +_PIP_VERSION = "20.2.3" _PROJECTS = [ ("setuptools", _SETUPTOOLS_VERSION, "py3"), @@ -23,22 +24,18 @@ def _run_pip(args, additional_paths=None): - # Add our bundled software to the sys.path so we can import it - if additional_paths is not None: - sys.path = additional_paths + sys.path - - # Invoke pip as if it's the main module, and catch the exit. - backup_argv = sys.argv[:] - sys.argv[1:] = args - try: - # run_module() alters sys.modules and sys.argv, but restores them at exit - runpy.run_module("pip", run_name="__main__", alter_sys=True) - except SystemExit as exc: - return exc.code - finally: - sys.argv[:] = backup_argv - - raise SystemError("pip did not exit, this should never happen") + # Run the bootstraping in a subprocess to avoid leaking any state that happens + # after pip has executed. Particulary, this avoids the case when pip holds onto + # the files in *additional_paths*, preventing us to remove them at the end of the + # invocation. + code = f""" +import runpy +import sys +sys.path = {additional_paths or []} + sys.path +sys.argv[1:] = {args} +runpy.run_module("pip", run_name="__main__", alter_sys=True) +""" + return subprocess.run([sys.executable, "-c", code], check=True).returncode def version(): diff --git a/Lib/ensurepip/_bundled/pip-20.2.3-py2.py3-none-any.whl b/Lib/ensurepip/_bundled/pip-20.2.3-py2.py3-none-any.whl new file mode 100644 index 0000000000000..7ebdc0f31d4e3 Binary files /dev/null and b/Lib/ensurepip/_bundled/pip-20.2.3-py2.py3-none-any.whl differ From webhook-mailer at python.org Sun Oct 4 13:11:35 2020 From: webhook-mailer at python.org (Pablo Galindo) Date: Sun, 04 Oct 2020 17:11:35 -0000 Subject: [Python-checkins] [3.8] bpo-41490: Bump vendored pip to version 20.2.3 (GH-22527). (GH-22545) Message-ID: https://github.com/python/cpython/commit/28cd96f2e5cfb16566a75dca8473b71889746f10 commit: 28cd96f2e5cfb16566a75dca8473b71889746f10 branch: 3.8 author: Pablo Galindo committer: GitHub date: 2020-10-04T19:11:31+02:00 summary: [3.8] bpo-41490: Bump vendored pip to version 20.2.3 (GH-22527). (GH-22545) (cherry picked from commit 2cc6dc9896771ef3615abbb5ba80939a2f644a08) Co-authored-by: Pablo Galindo files: A Lib/ensurepip/_bundled/pip-20.2.3-py2.py3-none-any.whl M Lib/ensurepip/__init__.py diff --git a/Lib/ensurepip/__init__.py b/Lib/ensurepip/__init__.py index 9415fd73b80dd..38bb42104b550 100644 --- a/Lib/ensurepip/__init__.py +++ b/Lib/ensurepip/__init__.py @@ -4,6 +4,7 @@ import sys import runpy import tempfile +import subprocess __all__ = ["version", "bootstrap"] @@ -11,7 +12,7 @@ _SETUPTOOLS_VERSION = "49.2.1" -_PIP_VERSION = "20.2.1" +_PIP_VERSION = "20.2.3" _PROJECTS = [ ("setuptools", _SETUPTOOLS_VERSION, "py3"), @@ -20,22 +21,18 @@ def _run_pip(args, additional_paths=None): - # Add our bundled software to the sys.path so we can import it - if additional_paths is not None: - sys.path = additional_paths + sys.path - - # Invoke pip as if it's the main module, and catch the exit. - backup_argv = sys.argv[:] - sys.argv[1:] = args - try: - # run_module() alters sys.modules and sys.argv, but restores them at exit - runpy.run_module("pip", run_name="__main__", alter_sys=True) - except SystemExit as exc: - return exc.code - finally: - sys.argv[:] = backup_argv - - raise SystemError("pip did not exit, this should never happen") + # Run the bootstraping in a subprocess to avoid leaking any state that happens + # after pip has executed. Particulary, this avoids the case when pip holds onto + # the files in *additional_paths*, preventing us to remove them at the end of the + # invocation. + code = f""" +import runpy +import sys +sys.path = {additional_paths or []} + sys.path +sys.argv[1:] = {args} +runpy.run_module("pip", run_name="__main__", alter_sys=True) +""" + return subprocess.run([sys.executable, "-c", code], check=True).returncode def version(): diff --git a/Lib/ensurepip/_bundled/pip-20.2.3-py2.py3-none-any.whl b/Lib/ensurepip/_bundled/pip-20.2.3-py2.py3-none-any.whl new file mode 100644 index 0000000000000..7ebdc0f31d4e3 Binary files /dev/null and b/Lib/ensurepip/_bundled/pip-20.2.3-py2.py3-none-any.whl differ From webhook-mailer at python.org Sun Oct 4 17:28:59 2020 From: webhook-mailer at python.org (Manan Kumar Garg) Date: Sun, 04 Oct 2020 21:28:59 -0000 Subject: [Python-checkins] Typo fix (GH-22496) Message-ID: https://github.com/python/cpython/commit/619f98045d73859688e5cfdb946d183e6d493a64 commit: 619f98045d73859688e5cfdb946d183e6d493a64 branch: master author: Manan Kumar Garg <62146744+MananKGarg at users.noreply.github.com> committer: GitHub date: 2020-10-04T14:28:43-07:00 summary: Typo fix (GH-22496) Multiple typo fixes in code comments Automerge-Triggered-By: @Mariatta files: M setup.py diff --git a/setup.py b/setup.py index 04b1358bc916e..476f8c414978e 100644 --- a/setup.py +++ b/setup.py @@ -1881,9 +1881,9 @@ def detect_tkinter_darwin(self): # you want to build and link with a framework build of Tcl and Tk # that is not in /Library/Frameworks, say, in your private # $HOME/Library/Frameworks directory or elsewhere. It turns - # out to be difficult to make that work automtically here + # out to be difficult to make that work automatically here # without bringing into play more tools and magic. That case - # can be hamdled using a recipe with the right arguments + # can be handled using a recipe with the right arguments # to detect_tkinter_explicitly(). # # Note also that the fallback case here is to try to use the @@ -1891,7 +1891,7 @@ def detect_tkinter_darwin(self): # be forewarned that they are deprecated by Apple and typically # out-of-date and buggy; their use should be avoided if at # all possible by installing a newer version of Tcl and Tk in - # /Library/Frameworks before bwfore building Python without + # /Library/Frameworks before building Python without # an explicit SDK or by configuring build arguments explicitly. from os.path import join, exists @@ -1908,7 +1908,7 @@ def detect_tkinter_darwin(self): else: # Use case #1: no explicit SDK selected. # Search the local system-wide /Library/Frameworks, - # not the one in the default SDK, othewise fall back to + # not the one in the default SDK, otherwise fall back to # /System/Library/Frameworks whose header files may be in # the default SDK or, on older systems, actually installed. framework_dirs = [ @@ -1924,7 +1924,7 @@ def detect_tkinter_darwin(self): if not exists(join(F, fw + '.framework')): break else: - # ok, F is now directory with both frameworks. Continure + # ok, F is now directory with both frameworks. Continue # building break else: From webhook-mailer at python.org Sun Oct 4 17:51:55 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Sun, 04 Oct 2020 21:51:55 -0000 Subject: [Python-checkins] Typo fix (GH-22496) Message-ID: https://github.com/python/cpython/commit/f3a6b7fc0b46afbe5d4d450b94a9d077e50b15d5 commit: f3a6b7fc0b46afbe5d4d450b94a9d077e50b15d5 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-04T14:51:38-07:00 summary: Typo fix (GH-22496) Multiple typo fixes in code comments Automerge-Triggered-By: @Mariatta (cherry picked from commit 619f98045d73859688e5cfdb946d183e6d493a64) Co-authored-by: Manan Kumar Garg <62146744+MananKGarg at users.noreply.github.com> files: M setup.py diff --git a/setup.py b/setup.py index 770866bca76b3..0a6cd8ca2e9ac 100644 --- a/setup.py +++ b/setup.py @@ -1882,9 +1882,9 @@ def detect_tkinter_darwin(self): # you want to build and link with a framework build of Tcl and Tk # that is not in /Library/Frameworks, say, in your private # $HOME/Library/Frameworks directory or elsewhere. It turns - # out to be difficult to make that work automtically here + # out to be difficult to make that work automatically here # without bringing into play more tools and magic. That case - # can be hamdled using a recipe with the right arguments + # can be handled using a recipe with the right arguments # to detect_tkinter_explicitly(). # # Note also that the fallback case here is to try to use the @@ -1892,7 +1892,7 @@ def detect_tkinter_darwin(self): # be forewarned that they are deprecated by Apple and typically # out-of-date and buggy; their use should be avoided if at # all possible by installing a newer version of Tcl and Tk in - # /Library/Frameworks before bwfore building Python without + # /Library/Frameworks before building Python without # an explicit SDK or by configuring build arguments explicitly. from os.path import join, exists @@ -1909,7 +1909,7 @@ def detect_tkinter_darwin(self): else: # Use case #1: no explicit SDK selected. # Search the local system-wide /Library/Frameworks, - # not the one in the default SDK, othewise fall back to + # not the one in the default SDK, otherwise fall back to # /System/Library/Frameworks whose header files may be in # the default SDK or, on older systems, actually installed. framework_dirs = [ @@ -1925,7 +1925,7 @@ def detect_tkinter_darwin(self): if not exists(join(F, fw + '.framework')): break else: - # ok, F is now directory with both frameworks. Continure + # ok, F is now directory with both frameworks. Continue # building break else: From webhook-mailer at python.org Sun Oct 4 17:56:01 2020 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Sun, 04 Oct 2020 21:56:01 -0000 Subject: [Python-checkins] bpo-41909: Enable previously disabled recursion checks. (GH-22536) Message-ID: https://github.com/python/cpython/commit/9ece9cd65cdeb0a1f6e60475bbd0219161c348ac commit: 9ece9cd65cdeb0a1f6e60475bbd0219161c348ac branch: master author: Serhiy Storchaka committer: GitHub date: 2020-10-05T00:55:57+03:00 summary: bpo-41909: Enable previously disabled recursion checks. (GH-22536) Enable recursion checks which were disabled when get __bases__ of non-type objects in issubclass() and isinstance() and when intern strings. It fixes a stack overflow when getting __bases__ leads to infinite recursion. Originally recursion checks was disabled for PyDict_GetItem() which silences all errors including the one raised in case of detected recursion and can return incorrect result. But now the code uses PyDict_GetItemWithError() and PyDict_SetDefault() instead. files: A Misc/NEWS.d/next/Core and Builtins/2020-10-04-10-55-12.bpo-41909.BqHPcm.rst M Lib/test/test_isinstance.py M Objects/abstract.c M Objects/unicodeobject.c diff --git a/Lib/test/test_isinstance.py b/Lib/test/test_isinstance.py index 91e79c295481d..109c3f84a5c42 100644 --- a/Lib/test/test_isinstance.py +++ b/Lib/test/test_isinstance.py @@ -303,6 +303,16 @@ def __bases__(self): self.assertEqual(True, issubclass(B(), int)) + def test_infinite_recursion_in_bases(self): + class X: + @property + def __bases__(self): + return self.__bases__ + + self.assertRaises(RecursionError, issubclass, X(), int) + self.assertRaises(RecursionError, issubclass, int, X()) + self.assertRaises(RecursionError, isinstance, 1, X()) + def blowstack(fxn, arg, compare_to): # Make sure that calling isinstance with a deeply nested tuple for its diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-10-04-10-55-12.bpo-41909.BqHPcm.rst b/Misc/NEWS.d/next/Core and Builtins/2020-10-04-10-55-12.bpo-41909.BqHPcm.rst new file mode 100644 index 0000000000000..388cfea065eed --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2020-10-04-10-55-12.bpo-41909.BqHPcm.rst @@ -0,0 +1,2 @@ +Fixed stack overflow in :func:`issubclass` and :func:`isinstance` when +getting the ``__bases__`` attribute leads to infinite recursion. diff --git a/Objects/abstract.c b/Objects/abstract.c index c471f184f6c84..c30fb4eb6a604 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -2336,9 +2336,7 @@ abstract_get_bases(PyObject *cls) _Py_IDENTIFIER(__bases__); PyObject *bases; - Py_ALLOW_RECURSION (void)_PyObject_LookupAttrId(cls, &PyId___bases__, &bases); - Py_END_ALLOW_RECURSION if (bases != NULL && !PyTuple_Check(bases)) { Py_DECREF(bases); return NULL; diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index cf72238a8d058..6ae06a508c614 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -15734,9 +15734,7 @@ PyUnicode_InternInPlace(PyObject **p) } PyObject *t; - Py_ALLOW_RECURSION t = PyDict_SetDefault(interned, s, s); - Py_END_ALLOW_RECURSION if (t == NULL) { PyErr_Clear(); From webhook-mailer at python.org Sun Oct 4 18:27:44 2020 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Sun, 04 Oct 2020 22:27:44 -0000 Subject: [Python-checkins] [3.9] bpo-41909: Enable previously disabled recursion checks. (GH-22536) (GH-22550) Message-ID: https://github.com/python/cpython/commit/7aa22ba923509af1dbf115c090964f503c84ca8d commit: 7aa22ba923509af1dbf115c090964f503c84ca8d branch: 3.9 author: Serhiy Storchaka committer: GitHub date: 2020-10-05T01:27:38+03:00 summary: [3.9] bpo-41909: Enable previously disabled recursion checks. (GH-22536) (GH-22550) Enable recursion checks which were disabled when get __bases__ of non-type objects in issubclass() and isinstance() and when intern strings. It fixes a stack overflow when getting __bases__ leads to infinite recursion. Originally recursion checks was disabled for PyDict_GetItem() which silences all errors including the one raised in case of detected recursion and can return incorrect result. But now the code uses PyDict_GetItemWithError() and PyDict_SetDefault() instead. (cherry picked from commit 9ece9cd65cdeb0a1f6e60475bbd0219161c348ac) files: A Misc/NEWS.d/next/Core and Builtins/2020-10-04-10-55-12.bpo-41909.BqHPcm.rst M Lib/test/test_isinstance.py M Objects/abstract.c M Objects/unicodeobject.c diff --git a/Lib/test/test_isinstance.py b/Lib/test/test_isinstance.py index 53639e984e48a..31b38996930cc 100644 --- a/Lib/test/test_isinstance.py +++ b/Lib/test/test_isinstance.py @@ -271,6 +271,16 @@ def __bases__(self): self.assertEqual(True, issubclass(B(), int)) + def test_infinite_recursion_in_bases(self): + class X: + @property + def __bases__(self): + return self.__bases__ + + self.assertRaises(RecursionError, issubclass, X(), int) + self.assertRaises(RecursionError, issubclass, int, X()) + self.assertRaises(RecursionError, isinstance, 1, X()) + def blowstack(fxn, arg, compare_to): # Make sure that calling isinstance with a deeply nested tuple for its diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-10-04-10-55-12.bpo-41909.BqHPcm.rst b/Misc/NEWS.d/next/Core and Builtins/2020-10-04-10-55-12.bpo-41909.BqHPcm.rst new file mode 100644 index 0000000000000..388cfea065eed --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2020-10-04-10-55-12.bpo-41909.BqHPcm.rst @@ -0,0 +1,2 @@ +Fixed stack overflow in :func:`issubclass` and :func:`isinstance` when +getting the ``__bases__`` attribute leads to infinite recursion. diff --git a/Objects/abstract.c b/Objects/abstract.c index 1d671c9e3334a..1922619ad85db 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -2375,9 +2375,7 @@ abstract_get_bases(PyObject *cls) _Py_IDENTIFIER(__bases__); PyObject *bases; - Py_ALLOW_RECURSION (void)_PyObject_LookupAttrId(cls, &PyId___bases__, &bases); - Py_END_ALLOW_RECURSION if (bases != NULL && !PyTuple_Check(bases)) { Py_DECREF(bases); return NULL; diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 4c8c880657974..3c2383d57c863 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -15602,9 +15602,7 @@ PyUnicode_InternInPlace(PyObject **p) } PyObject *t; - Py_ALLOW_RECURSION t = PyDict_SetDefault(interned, s, s); - Py_END_ALLOW_RECURSION if (t == NULL) { PyErr_Clear(); From webhook-mailer at python.org Sun Oct 4 18:28:04 2020 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Sun, 04 Oct 2020 22:28:04 -0000 Subject: [Python-checkins] [3.8] bpo-41909: Enable previously disabled recursion checks. (GH-22536) (GH-22551) Message-ID: https://github.com/python/cpython/commit/09a7b3b618cd02694a0bc8abfa24c75f0e659407 commit: 09a7b3b618cd02694a0bc8abfa24c75f0e659407 branch: 3.8 author: Serhiy Storchaka committer: GitHub date: 2020-10-05T01:28:00+03:00 summary: [3.8] bpo-41909: Enable previously disabled recursion checks. (GH-22536) (GH-22551) Enable recursion checks which were disabled when get __bases__ of non-type objects in issubclass() and isinstance() and when intern strings. It fixes a stack overflow when getting __bases__ leads to infinite recursion. Originally recursion checks was disabled for PyDict_GetItem() which silences all errors including the one raised in case of detected recursion and can return incorrect result. But now the code uses PyDict_GetItemWithError() and PyDict_SetDefault() instead. (cherry picked from commit 9ece9cd65cdeb0a1f6e60475bbd0219161c348ac) files: A Misc/NEWS.d/next/Core and Builtins/2020-10-04-10-55-12.bpo-41909.BqHPcm.rst M Lib/test/test_isinstance.py M Objects/abstract.c M Objects/unicodeobject.c diff --git a/Lib/test/test_isinstance.py b/Lib/test/test_isinstance.py index 53639e984e48a..31b38996930cc 100644 --- a/Lib/test/test_isinstance.py +++ b/Lib/test/test_isinstance.py @@ -271,6 +271,16 @@ def __bases__(self): self.assertEqual(True, issubclass(B(), int)) + def test_infinite_recursion_in_bases(self): + class X: + @property + def __bases__(self): + return self.__bases__ + + self.assertRaises(RecursionError, issubclass, X(), int) + self.assertRaises(RecursionError, issubclass, int, X()) + self.assertRaises(RecursionError, isinstance, 1, X()) + def blowstack(fxn, arg, compare_to): # Make sure that calling isinstance with a deeply nested tuple for its diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-10-04-10-55-12.bpo-41909.BqHPcm.rst b/Misc/NEWS.d/next/Core and Builtins/2020-10-04-10-55-12.bpo-41909.BqHPcm.rst new file mode 100644 index 0000000000000..388cfea065eed --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2020-10-04-10-55-12.bpo-41909.BqHPcm.rst @@ -0,0 +1,2 @@ +Fixed stack overflow in :func:`issubclass` and :func:`isinstance` when +getting the ``__bases__`` attribute leads to infinite recursion. diff --git a/Objects/abstract.c b/Objects/abstract.c index 12237d570f743..9c42d817b4211 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -2316,9 +2316,7 @@ abstract_get_bases(PyObject *cls) _Py_IDENTIFIER(__bases__); PyObject *bases; - Py_ALLOW_RECURSION (void)_PyObject_LookupAttrId(cls, &PyId___bases__, &bases); - Py_END_ALLOW_RECURSION if (bases != NULL && !PyTuple_Check(bases)) { Py_DECREF(bases); return NULL; diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 4c2b42f959b83..01df54da0051d 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -15285,9 +15285,7 @@ PyUnicode_InternInPlace(PyObject **p) return; } } - Py_ALLOW_RECURSION t = PyDict_SetDefault(interned, s, s); - Py_END_ALLOW_RECURSION if (t == NULL) { PyErr_Clear(); return; From webhook-mailer at python.org Sun Oct 4 19:13:53 2020 From: webhook-mailer at python.org (scoder) Date: Sun, 04 Oct 2020 23:13:53 -0000 Subject: [Python-checkins] bpo-41892: Clarify that an example in the ElementTree docs explicitly avoids modifying an XML tree while iterating over it. (GH-22464) Message-ID: https://github.com/python/cpython/commit/40db798692ca783fc2163656f196ac77e8b9e792 commit: 40db798692ca783fc2163656f196ac77e8b9e792 branch: master author: scoder committer: GitHub date: 2020-10-04T19:13:46-04:00 summary: bpo-41892: Clarify that an example in the ElementTree docs explicitly avoids modifying an XML tree while iterating over it. (GH-22464) files: M Doc/library/xml.etree.elementtree.rst diff --git a/Doc/library/xml.etree.elementtree.rst b/Doc/library/xml.etree.elementtree.rst index 7725e4d158d42..f4bccf6609810 100644 --- a/Doc/library/xml.etree.elementtree.rst +++ b/Doc/library/xml.etree.elementtree.rst @@ -251,12 +251,18 @@ We can remove elements using :meth:`Element.remove`. Let's say we want to remove all countries with a rank higher than 50:: >>> for country in root.findall('country'): + ... # using root.findall() to avoid removal during traversal ... rank = int(country.find('rank').text) ... if rank > 50: ... root.remove(country) ... >>> tree.write('output.xml') +Note that concurrent modification while iterating can lead to problems, +just like when iterating and modifying Python lists or dicts. +Therefore, the example first collects all matching elements with +``root.findall()``, and only then iterates over the list of matches. + Our XML now looks like this: .. code-block:: xml From webhook-mailer at python.org Sun Oct 4 19:23:52 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Sun, 04 Oct 2020 23:23:52 -0000 Subject: [Python-checkins] bpo-41892: Clarify that an example in the ElementTree docs explicitly avoids modifying an XML tree while iterating over it. (GH-22464) Message-ID: https://github.com/python/cpython/commit/6bd058e0ff5d4a63fb35f6d45161cdf51cb68c58 commit: 6bd058e0ff5d4a63fb35f6d45161cdf51cb68c58 branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-04T16:23:43-07:00 summary: bpo-41892: Clarify that an example in the ElementTree docs explicitly avoids modifying an XML tree while iterating over it. (GH-22464) (cherry picked from commit 40db798692ca783fc2163656f196ac77e8b9e792) Co-authored-by: scoder files: M Doc/library/xml.etree.elementtree.rst diff --git a/Doc/library/xml.etree.elementtree.rst b/Doc/library/xml.etree.elementtree.rst index 44ac52aa83079..e4aedd1f9f082 100644 --- a/Doc/library/xml.etree.elementtree.rst +++ b/Doc/library/xml.etree.elementtree.rst @@ -249,12 +249,18 @@ We can remove elements using :meth:`Element.remove`. Let's say we want to remove all countries with a rank higher than 50:: >>> for country in root.findall('country'): + ... # using root.findall() to avoid removal during traversal ... rank = int(country.find('rank').text) ... if rank > 50: ... root.remove(country) ... >>> tree.write('output.xml') +Note that concurrent modification while iterating can lead to problems, +just like when iterating and modifying Python lists or dicts. +Therefore, the example first collects all matching elements with +``root.findall()``, and only then iterates over the list of matches. + Our XML now looks like this: .. code-block:: xml From webhook-mailer at python.org Sun Oct 4 19:57:06 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Sun, 04 Oct 2020 23:57:06 -0000 Subject: [Python-checkins] bpo-41892: Clarify that an example in the ElementTree docs explicitly avoids modifying an XML tree while iterating over it. (GH-22464) (GH-22554) Message-ID: https://github.com/python/cpython/commit/d5719247ba32b3ac700454bad9a9e2cc7edeac6a commit: d5719247ba32b3ac700454bad9a9e2cc7edeac6a branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-04T19:56:56-04:00 summary: bpo-41892: Clarify that an example in the ElementTree docs explicitly avoids modifying an XML tree while iterating over it. (GH-22464) (GH-22554) (cherry picked from commit 40db798692ca783fc2163656f196ac77e8b9e792) Co-authored-by: scoder Co-authored-by: scoder files: M Doc/library/xml.etree.elementtree.rst diff --git a/Doc/library/xml.etree.elementtree.rst b/Doc/library/xml.etree.elementtree.rst index 7725e4d158d42..f4bccf6609810 100644 --- a/Doc/library/xml.etree.elementtree.rst +++ b/Doc/library/xml.etree.elementtree.rst @@ -251,12 +251,18 @@ We can remove elements using :meth:`Element.remove`. Let's say we want to remove all countries with a rank higher than 50:: >>> for country in root.findall('country'): + ... # using root.findall() to avoid removal during traversal ... rank = int(country.find('rank').text) ... if rank > 50: ... root.remove(country) ... >>> tree.write('output.xml') +Note that concurrent modification while iterating can lead to problems, +just like when iterating and modifying Python lists or dicts. +Therefore, the example first collects all matching elements with +``root.findall()``, and only then iterates over the list of matches. + Our XML now looks like this: .. code-block:: xml From webhook-mailer at python.org Mon Oct 5 00:40:56 2020 From: webhook-mailer at python.org (Fidget-Spinner) Date: Mon, 05 Oct 2020 04:40:56 -0000 Subject: [Python-checkins] bpo-41428: Documentation for PEP 604 (gh-22517) Message-ID: https://github.com/python/cpython/commit/8e1dd55e63d18d40e78d941fc9233d2c77bcd3de commit: 8e1dd55e63d18d40e78d941fc9233d2c77bcd3de branch: master author: Fidget-Spinner <28750310+Fidget-Spinner at users.noreply.github.com> committer: GitHub date: 2020-10-04T21:40:52-07:00 summary: bpo-41428: Documentation for PEP 604 (gh-22517) files: A Misc/NEWS.d/next/Documentation/2020-10-03-18-20-46.bpo-41428._ju1NE.rst M Doc/library/stdtypes.rst M Doc/library/types.rst M Doc/library/typing.rst M Doc/whatsnew/3.10.rst M Misc/ACKS diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 62f39da2a72a2..04dfea276d2b1 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -4743,6 +4743,128 @@ define these methods must provide them as a normal Python accessible method. Compared to the overhead of setting up the runtime context, the overhead of a single class dictionary lookup is negligible. +.. _types-union: + +Union Type +========== + +.. index:: + object: Union + pair: union; type + +A union object holds the value of the ``|`` (bitwise or) operation on +multiple :ref:`type objects`. These types are intended +primarily for type annotations. The union type expression enables cleaner +type hinting syntax compared to :data:`typing.Union`. + +.. describe:: X | Y | ... + + Defines a union object which holds types *X*, *Y*, and so forth. ``X | Y`` + means either X or Y. It is equivalent to ``typing.Union[X, Y]``. + Example:: + + def square(number: int | float) -> int | float: + return number ** 2 + +.. describe:: union_object == other + + Union objects can be tested for equality with other union objects. Details: + + * Unions of unions are flattened, e.g.:: + + (int | str) | float == int | str | float + + * Redundant types are removed, e.g.:: + + int | str | int == int | str + + * When comparing unions, the order is ignored, e.g.:: + + int | str == str | int + + * It is compatible with :data:`typing.Union`:: + + int | str == typing.Union[int, str] + + * Optional types can be spelled as a union with ``None``:: + + str | None == typing.Optional[str] + +.. describe:: isinstance(obj, union_object) + + Calls to :func:`isinstance` are also supported with a Union object:: + + >>> isinstance("", int | str) + True + + .. + At the time of writing this, there is no documentation for parameterized + generics or PEP 585. Thus the link currently points to PEP 585 itself. + Please change the link for parameterized generics to reference the correct + documentation once documentation for PEP 585 becomes available. + + However, union objects containing `parameterized generics + `_ cannot be used:: + + >>> isinstance(1, int | list[int]) + Traceback (most recent call last): + File "", line 1, in + TypeError: isinstance() argument 2 cannot contain a parameterized generic + +.. describe:: issubclass(obj, union_object) + + Calls to :func:`issubclass` are also supported with a Union Object.:: + + >>> issubclass(bool, int | str) + True + + .. + Once again, please change the link below for parameterized generics to + reference the correct documentation once documentation for PEP 585 + becomes available. + + However, union objects containing `parameterized generics + `_ cannot be used:: + + >>> issubclass(bool, bool | list[str]) + Traceback (most recent call last): + File "", line 1, in + TypeError: issubclass() argument 2 cannot contain a parameterized generic + +The type for the Union object is :data:`types.Union`. An object cannot be +instantiated from the type:: + + >>> import types + >>> isinstance(int | str, types.Union) + True + >>> types.Union() + Traceback (most recent call last): + File "", line 1, in + TypeError: cannot create 'types.Union' instances + +.. note:: + The :meth:`__or__` method for type objects was added to support the syntax + ``X | Y``. If a metaclass implements :meth:`__or__`, the Union may + override it:: + + >>> class M(type): + ... def __or__(self, other): + ... return "Hello" + ... + >>> class C(metaclass=M): + ... pass + ... + >>> C | int + 'Hello' + >>> int | C + int | __main__.C + +.. seealso:: + + :pep:`604` -- PEP proposing the ``X | Y`` syntax and the Union type. + +.. versionadded:: 3.10 + .. _typesother: diff --git a/Doc/library/types.rst b/Doc/library/types.rst index 25fa750f2ccac..e4a8dec5cb95a 100644 --- a/Doc/library/types.rst +++ b/Doc/library/types.rst @@ -256,6 +256,11 @@ Standard names are defined for the following types: .. versionadded:: 3.10 +.. data:: Union + + The type of :ref:`union type expressions`. + + .. versionadded:: 3.10 .. class:: TracebackType(tb_next, tb_frame, tb_lasti, tb_lineno) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index f712dfea13f2c..a72632e61b073 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -544,6 +544,10 @@ These can be used as types in annotations using ``[]``, each having a unique syn .. versionchanged:: 3.7 Don't remove explicit subclasses from unions at runtime. + .. versionchanged:: 3.10 + Unions can now be written as ``X | Y``. See + :ref:`union type expressions`. + .. data:: Optional Optional type. diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index 957a3e791ecb6..9c3a0287d5509 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -82,6 +82,29 @@ New Features * :pep:`618`: The :func:`zip` function now has an optional ``strict`` flag, used to require that all the iterables have an equal length. +PEP604: New Type Operator +------------------------- + +A new type union operator was introduced which enables the syntax ``X | Y``. +This provides a cleaner way of expressing 'either type X or type Y' instead of +using :data:`typing.Union`, especially in type hints (annotations). + +In previous versions of Python, to apply a type hint for functions accepting +arguments of multiple types, :data:`typing.Union` was used:: + + def square(number: Union[int, float]) -> Union[int, float]: + return number ** 2 + + +Now, type hints can be written in a more succinct manner:: + + def square(number: int | float) -> int | float: + return number ** 2 + + +See :pep:`604` for more details. + +(Contributed by Maggie Moss and Philippe Prados in :issue:`41428`.) Other Language Changes ====================== diff --git a/Misc/ACKS b/Misc/ACKS index 9be0e777ca294..08449fe08269b 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1257,6 +1257,7 @@ Grant Olson Furkan Onder Koray Oner Ethan Onstott +Ken Jin Ooi Piet van Oostrum Tomas Oppelstrup Jason Orendorff diff --git a/Misc/NEWS.d/next/Documentation/2020-10-03-18-20-46.bpo-41428._ju1NE.rst b/Misc/NEWS.d/next/Documentation/2020-10-03-18-20-46.bpo-41428._ju1NE.rst new file mode 100644 index 0000000000000..2c33393456019 --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2020-10-03-18-20-46.bpo-41428._ju1NE.rst @@ -0,0 +1 @@ +Add documentation for :pep:`604` (Allow writing union types as ``X | Y``). From webhook-mailer at python.org Mon Oct 5 04:09:45 2020 From: webhook-mailer at python.org (Erlend Egeberg Aasland) Date: Mon, 05 Oct 2020 08:09:45 -0000 Subject: [Python-checkins] bpo-41557: Update macOS installer to use SQLite 3.33.0 (GH-21959) Message-ID: https://github.com/python/cpython/commit/9a7642667a71a27b38b96eb63df45f17f48b3467 commit: 9a7642667a71a27b38b96eb63df45f17f48b3467 branch: master author: Erlend Egeberg Aasland committer: GitHub date: 2020-10-05T01:09:16-07:00 summary: bpo-41557: Update macOS installer to use SQLite 3.33.0 (GH-21959) https://sqlite.org/releaselog/3_33_0.html files: A Misc/NEWS.d/next/macOS/2020-08-26-09-31-37.bpo-41557.mcQ75z.rst M Mac/BuildScript/build-installer.py diff --git a/Mac/BuildScript/build-installer.py b/Mac/BuildScript/build-installer.py index a58b922ce30b8..2548b212d9ea4 100755 --- a/Mac/BuildScript/build-installer.py +++ b/Mac/BuildScript/build-installer.py @@ -307,9 +307,9 @@ def library_recipes(): ), ), dict( - name="SQLite 3.32.3", - url="https://sqlite.org/2020/sqlite-autoconf-3320300.tar.gz", - checksum='2e3911a3c15e85c2f2d040154bbe5ce3', + name="SQLite 3.33.0", + url="https://sqlite.org/2020/sqlite-autoconf-3330000.tar.gz", + checksum='842a8a100d7b01b09e543deb2b7951dd', extra_cflags=('-Os ' '-DSQLITE_ENABLE_FTS5 ' '-DSQLITE_ENABLE_FTS4 ' diff --git a/Misc/NEWS.d/next/macOS/2020-08-26-09-31-37.bpo-41557.mcQ75z.rst b/Misc/NEWS.d/next/macOS/2020-08-26-09-31-37.bpo-41557.mcQ75z.rst new file mode 100644 index 0000000000000..5f2d9937c0606 --- /dev/null +++ b/Misc/NEWS.d/next/macOS/2020-08-26-09-31-37.bpo-41557.mcQ75z.rst @@ -0,0 +1 @@ +Update macOS installer to use SQLite 3.33.0. From webhook-mailer at python.org Mon Oct 5 04:27:58 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Mon, 05 Oct 2020 08:27:58 -0000 Subject: [Python-checkins] bpo-41557: Update macOS installer to use SQLite 3.33.0 (GH-21959) Message-ID: https://github.com/python/cpython/commit/a859680a5bef0bced197454f4b67080694979252 commit: a859680a5bef0bced197454f4b67080694979252 branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-05T01:27:53-07:00 summary: bpo-41557: Update macOS installer to use SQLite 3.33.0 (GH-21959) https://sqlite.org/releaselog/3_33_0.html (cherry picked from commit 9a7642667a71a27b38b96eb63df45f17f48b3467) Co-authored-by: Erlend Egeberg Aasland files: A Misc/NEWS.d/next/macOS/2020-08-26-09-31-37.bpo-41557.mcQ75z.rst M Mac/BuildScript/build-installer.py diff --git a/Mac/BuildScript/build-installer.py b/Mac/BuildScript/build-installer.py index a58b922ce30b8..2548b212d9ea4 100755 --- a/Mac/BuildScript/build-installer.py +++ b/Mac/BuildScript/build-installer.py @@ -307,9 +307,9 @@ def library_recipes(): ), ), dict( - name="SQLite 3.32.3", - url="https://sqlite.org/2020/sqlite-autoconf-3320300.tar.gz", - checksum='2e3911a3c15e85c2f2d040154bbe5ce3', + name="SQLite 3.33.0", + url="https://sqlite.org/2020/sqlite-autoconf-3330000.tar.gz", + checksum='842a8a100d7b01b09e543deb2b7951dd', extra_cflags=('-Os ' '-DSQLITE_ENABLE_FTS5 ' '-DSQLITE_ENABLE_FTS4 ' diff --git a/Misc/NEWS.d/next/macOS/2020-08-26-09-31-37.bpo-41557.mcQ75z.rst b/Misc/NEWS.d/next/macOS/2020-08-26-09-31-37.bpo-41557.mcQ75z.rst new file mode 100644 index 0000000000000..5f2d9937c0606 --- /dev/null +++ b/Misc/NEWS.d/next/macOS/2020-08-26-09-31-37.bpo-41557.mcQ75z.rst @@ -0,0 +1 @@ +Update macOS installer to use SQLite 3.33.0. From webhook-mailer at python.org Mon Oct 5 05:02:18 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Mon, 05 Oct 2020 09:02:18 -0000 Subject: [Python-checkins] bpo-41557: Update macOS installer to use SQLite 3.33.0 (GH-21959) (GH-22560) Message-ID: https://github.com/python/cpython/commit/b6d37e15cfa32e04f750d78a03a68ec5bef00a59 commit: b6d37e15cfa32e04f750d78a03a68ec5bef00a59 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-05T05:01:51-04:00 summary: bpo-41557: Update macOS installer to use SQLite 3.33.0 (GH-21959) (GH-22560) (cherry picked from commit 9a7642667a71a27b38b96eb63df45f17f48b3467) Co-authored-by: Erlend Egeberg Aasland files: A Misc/NEWS.d/next/macOS/2020-08-26-09-31-37.bpo-41557.mcQ75z.rst M Mac/BuildScript/build-installer.py diff --git a/Mac/BuildScript/build-installer.py b/Mac/BuildScript/build-installer.py index a58b922ce30b8..2548b212d9ea4 100755 --- a/Mac/BuildScript/build-installer.py +++ b/Mac/BuildScript/build-installer.py @@ -307,9 +307,9 @@ def library_recipes(): ), ), dict( - name="SQLite 3.32.3", - url="https://sqlite.org/2020/sqlite-autoconf-3320300.tar.gz", - checksum='2e3911a3c15e85c2f2d040154bbe5ce3', + name="SQLite 3.33.0", + url="https://sqlite.org/2020/sqlite-autoconf-3330000.tar.gz", + checksum='842a8a100d7b01b09e543deb2b7951dd', extra_cflags=('-Os ' '-DSQLITE_ENABLE_FTS5 ' '-DSQLITE_ENABLE_FTS4 ' diff --git a/Misc/NEWS.d/next/macOS/2020-08-26-09-31-37.bpo-41557.mcQ75z.rst b/Misc/NEWS.d/next/macOS/2020-08-26-09-31-37.bpo-41557.mcQ75z.rst new file mode 100644 index 0000000000000..5f2d9937c0606 --- /dev/null +++ b/Misc/NEWS.d/next/macOS/2020-08-26-09-31-37.bpo-41557.mcQ75z.rst @@ -0,0 +1 @@ +Update macOS installer to use SQLite 3.33.0. From webhook-mailer at python.org Mon Oct 5 05:32:08 2020 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Mon, 05 Oct 2020 09:32:08 -0000 Subject: [Python-checkins] bpo-41936. Remove macros Py_ALLOW_RECURSION/Py_END_ALLOW_RECURSION (GH-22552) Message-ID: https://github.com/python/cpython/commit/dcc54215ac1eb4b6fab2a9ffe1abcdf3ac4bb77e commit: dcc54215ac1eb4b6fab2a9ffe1abcdf3ac4bb77e branch: master author: Serhiy Storchaka committer: GitHub date: 2020-10-05T12:32:00+03:00 summary: bpo-41936. Remove macros Py_ALLOW_RECURSION/Py_END_ALLOW_RECURSION (GH-22552) files: A Misc/NEWS.d/next/C API/2020-10-05-01-25-23.bpo-41936.1gb5ra.rst M Doc/whatsnew/3.10.rst M Include/ceval.h M Include/cpython/pystate.h M Python/ceval.c M Python/pystate.c diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index 9c3a0287d5509..1ea5aeac8a3c6 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -365,3 +365,8 @@ Removed * Removed ``_Py_CheckRecursionLimit`` variable: it has been replaced by ``ceval.recursion_limit`` of the :c:type:`PyInterpreterState` structure. (Contributed by Victor Stinner in :issue:`41834`.) + +* Removed undocumented macros ``Py_ALLOW_RECURSION`` and + ``Py_END_ALLOW_RECURSION`` and the ``recursion_critical`` field of the + :c:type:`PyInterpreterState` structure. + (Contributed by Serhiy Storchaka in :issue:`41936`.) diff --git a/Include/ceval.h b/Include/ceval.h index 0f372e2044a1c..0f687666e2bcc 100644 --- a/Include/ceval.h +++ b/Include/ceval.h @@ -67,14 +67,6 @@ PyAPI_FUNC(int) Py_GetRecursionLimit(void); PyAPI_FUNC(int) Py_EnterRecursiveCall(const char *where); PyAPI_FUNC(void) Py_LeaveRecursiveCall(void); -#define Py_ALLOW_RECURSION \ - do { unsigned char _old = PyThreadState_GET()->recursion_critical;\ - PyThreadState_GET()->recursion_critical = 1; - -#define Py_END_ALLOW_RECURSION \ - PyThreadState_GET()->recursion_critical = _old; \ - } while(0); - PyAPI_FUNC(const char *) PyEval_GetFuncName(PyObject *); PyAPI_FUNC(const char *) PyEval_GetFuncDesc(PyObject *); diff --git a/Include/cpython/pystate.h b/Include/cpython/pystate.h index 42a7fc163064d..5d5e4e331978a 100644 --- a/Include/cpython/pystate.h +++ b/Include/cpython/pystate.h @@ -56,8 +56,6 @@ struct _ts { int recursion_depth; char overflowed; /* The stack has overflowed. Allow 50 more calls to handle the runtime error. */ - char recursion_critical; /* The current calls must not cause - a stack overflow. */ int stackcheck_counter; /* 'tracing' keeps track of the execution depth when tracing/profiling. diff --git a/Misc/NEWS.d/next/C API/2020-10-05-01-25-23.bpo-41936.1gb5ra.rst b/Misc/NEWS.d/next/C API/2020-10-05-01-25-23.bpo-41936.1gb5ra.rst new file mode 100644 index 0000000000000..6461353308616 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2020-10-05-01-25-23.bpo-41936.1gb5ra.rst @@ -0,0 +1,3 @@ +Removed undocumented macros ``Py_ALLOW_RECURSION`` and +``Py_END_ALLOW_RECURSION`` and the ``recursion_critical`` field of the +:c:type:`PyInterpreterState` structure. diff --git a/Python/ceval.c b/Python/ceval.c index 7c6cf83bc9ac0..500c588e3c2af 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -814,9 +814,6 @@ _Py_CheckRecursiveCall(PyThreadState *tstate, const char *where) return -1; } #endif - if (tstate->recursion_critical) - /* Somebody asked that we don't check for recursion. */ - return 0; if (tstate->overflowed) { if (tstate->recursion_depth > recursion_limit + 50) { /* Overflowing while handling an overflow. Give up. */ diff --git a/Python/pystate.c b/Python/pystate.c index f6d1956e9dce9..eb24f2b800607 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -581,7 +581,6 @@ new_threadstate(PyInterpreterState *interp, int init) tstate->frame = NULL; tstate->recursion_depth = 0; tstate->overflowed = 0; - tstate->recursion_critical = 0; tstate->stackcheck_counter = 0; tstate->tracing = 0; tstate->use_tracing = 0; From webhook-mailer at python.org Mon Oct 5 10:31:54 2020 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Mon, 05 Oct 2020 14:31:54 -0000 Subject: [Python-checkins] bpo-41774: Tweak new programming FAQ entry (GH-22562) Message-ID: https://github.com/python/cpython/commit/060937da988347a887a5f165b023d972fcb97802 commit: 060937da988347a887a5f165b023d972fcb97802 branch: master author: Terry Jan Reedy committer: GitHub date: 2020-10-05T10:31:44-04:00 summary: bpo-41774: Tweak new programming FAQ entry (GH-22562) Remove mention of space in "remove multiple items from list". files: M Doc/faq/programming.rst diff --git a/Doc/faq/programming.rst b/Doc/faq/programming.rst index 0b486d7e7e254..7bcedb0b5d75b 100644 --- a/Doc/faq/programming.rst +++ b/Doc/faq/programming.rst @@ -1176,7 +1176,7 @@ Here are three variations.:: mylist[:] = (x for x in mylist if keep_condition) mylist[:] = [x for x in mylist if keep_condition] -If space is not an issue, the list comprehension may be fastest. +The list comprehension may be fastest. How do you make an array in Python? From webhook-mailer at python.org Mon Oct 5 10:42:18 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Mon, 05 Oct 2020 14:42:18 -0000 Subject: [Python-checkins] bpo-41774: Tweak new programming FAQ entry (GH-22562) Message-ID: https://github.com/python/cpython/commit/7e941fa8e0454c7814ce3ec646136758c0db5a25 commit: 7e941fa8e0454c7814ce3ec646136758c0db5a25 branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-05T07:42:10-07:00 summary: bpo-41774: Tweak new programming FAQ entry (GH-22562) Remove mention of space in "remove multiple items from list". (cherry picked from commit 060937da988347a887a5f165b023d972fcb97802) Co-authored-by: Terry Jan Reedy files: M Doc/faq/programming.rst diff --git a/Doc/faq/programming.rst b/Doc/faq/programming.rst index 1af04483eb329..a64e8aca94468 100644 --- a/Doc/faq/programming.rst +++ b/Doc/faq/programming.rst @@ -1175,7 +1175,7 @@ Here are three variations.:: mylist[:] = (x for x in mylist if keep_condition) mylist[:] = [x for x in mylist if keep_condition] -If space is not an issue, the list comprehension may be fastest. +The list comprehension may be fastest. How do you make an array in Python? From webhook-mailer at python.org Mon Oct 5 10:53:35 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Mon, 05 Oct 2020 14:53:35 -0000 Subject: [Python-checkins] bpo-41774: Tweak new programming FAQ entry (GH-22562) Message-ID: https://github.com/python/cpython/commit/75dd70e1ce0b5ce50c572802c17b7fa427d9ce23 commit: 75dd70e1ce0b5ce50c572802c17b7fa427d9ce23 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-05T07:53:26-07:00 summary: bpo-41774: Tweak new programming FAQ entry (GH-22562) Remove mention of space in "remove multiple items from list". (cherry picked from commit 060937da988347a887a5f165b023d972fcb97802) Co-authored-by: Terry Jan Reedy files: M Doc/faq/programming.rst diff --git a/Doc/faq/programming.rst b/Doc/faq/programming.rst index 1435765063f73..7c6fc51c6b6eb 100644 --- a/Doc/faq/programming.rst +++ b/Doc/faq/programming.rst @@ -1176,7 +1176,7 @@ Here are three variations.:: mylist[:] = (x for x in mylist if keep_condition) mylist[:] = [x for x in mylist if keep_condition] -If space is not an issue, the list comprehension may be fastest. +The list comprehension may be fastest. How do you make an array in Python? From webhook-mailer at python.org Mon Oct 5 12:10:20 2020 From: webhook-mailer at python.org (=?utf-8?q?=C5=81ukasz?= Langa) Date: Mon, 05 Oct 2020 16:10:20 -0000 Subject: [Python-checkins] [3.9] bpo-27032, bpo-37328: Document removing HTMLParser.unescape() (GH-22288) Message-ID: https://github.com/python/cpython/commit/8dd430265ccd4d34b74c841d6bcc8f58aa93ad94 commit: 8dd430265ccd4d34b74c841d6bcc8f58aa93ad94 branch: 3.9 author: Terry Jan Reedy committer: ?ukasz Langa date: 2020-10-04T17:30:47+02:00 summary: [3.9] bpo-27032, bpo-37328: Document removing HTMLParser.unescape() (GH-22288) (cherry picked from commit a33f2c2bae759fc9d06e1c032fd2026135f2df45) Co-authored-by: Serhiy Storchaka files: M Doc/whatsnew/3.9.rst diff --git a/Doc/whatsnew/3.9.rst b/Doc/whatsnew/3.9.rst index 2b7cbaa48531a..40c1592af4d5c 100644 --- a/Doc/whatsnew/3.9.rst +++ b/Doc/whatsnew/3.9.rst @@ -892,6 +892,11 @@ Removed :func:`asyncio.current_task` and :func:`asyncio.all_tasks` instead. (Contributed by R?mi Lapeyre in :issue:`40967`) +* The ``unescape()`` method in the :class:`html.parser.HTMLParser` class + has been removed (it was deprecated since Python 3.4). :func:`html.unescape` + should be used for converting character references to the corresponding + unicode characters. + Porting to Python 3.9 ===================== From webhook-mailer at python.org Mon Oct 5 12:24:16 2020 From: webhook-mailer at python.org (Victor Stinner) Date: Mon, 05 Oct 2020 16:24:16 -0000 Subject: [Python-checkins] bpo-41939: Fix test_site.test_license_exists_at_url() (#22559) Message-ID: https://github.com/python/cpython/commit/1fce240d6c4b2b2cc17a86e88c65169e15b9feeb commit: 1fce240d6c4b2b2cc17a86e88c65169e15b9feeb branch: master author: Victor Stinner committer: GitHub date: 2020-10-05T18:24:00+02:00 summary: bpo-41939: Fix test_site.test_license_exists_at_url() (#22559) Call urllib.request.urlcleanup() to reset the global urllib.request._opener. files: A Misc/NEWS.d/next/Tests/2020-10-05-09-37-43.bpo-41939.P4OlbA.rst M Lib/test/test_site.py diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py index d3ee68facdbc3..a475ed1ab4c46 100644 --- a/Lib/test/test_site.py +++ b/Lib/test/test_site.py @@ -525,6 +525,8 @@ def test_license_exists_at_url(self): # string displayed by license in the absence of a LICENSE file. url = license._Printer__data.split()[1] req = urllib.request.Request(url, method='HEAD') + # Reset global urllib.request._opener + self.addCleanup(urllib.request.urlcleanup) try: with socket_helper.transient_internet(url): with urllib.request.urlopen(req) as data: diff --git a/Misc/NEWS.d/next/Tests/2020-10-05-09-37-43.bpo-41939.P4OlbA.rst b/Misc/NEWS.d/next/Tests/2020-10-05-09-37-43.bpo-41939.P4OlbA.rst new file mode 100644 index 0000000000000..e58ad2616da1b --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2020-10-05-09-37-43.bpo-41939.P4OlbA.rst @@ -0,0 +1,3 @@ +Fix test_site.test_license_exists_at_url(): call +``urllib.request.urlcleanup()`` to reset the global +``urllib.request._opener``. Patch by Victor Stinner. From webhook-mailer at python.org Mon Oct 5 12:42:40 2020 From: webhook-mailer at python.org (Brett Cannon) Date: Mon, 05 Oct 2020 16:42:40 -0000 Subject: [Python-checkins] bpo-41584: clarify when the reflected method of a binary arithemtic operator is called (#22505) Message-ID: https://github.com/python/cpython/commit/d02d824e05e2cb86f4df381be18832e76e2c475f commit: d02d824e05e2cb86f4df381be18832e76e2c475f branch: master author: Brett Cannon committer: GitHub date: 2020-10-05T09:42:21-07:00 summary: bpo-41584: clarify when the reflected method of a binary arithemtic operator is called (#22505) files: M Doc/reference/datamodel.rst diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index a817408c3b1ef..4396f1b9b7299 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -2376,10 +2376,11 @@ left undefined. .. note:: - If the right operand's type is a subclass of the left operand's type and that - subclass provides the reflected method for the operation, this method will be - called before the left operand's non-reflected method. This behavior allows - subclasses to override their ancestors' operations. + If the right operand's type is a subclass of the left operand's type and + that subclass provides a different implementation of the reflected method + for the operation, this method will be called before the left operand's + non-reflected method. This behavior allows subclasses to override their + ancestors' operations. .. method:: object.__iadd__(self, other) @@ -2771,6 +2772,6 @@ An example of an asynchronous context manager class:: method?that will instead have the opposite effect of explicitly *blocking* such fallback. -.. [#] For operands of the same type, it is assumed that if the non-reflected method - (such as :meth:`__add__`) fails the operation is not supported, which is why the - reflected method is not called. +.. [#] For operands of the same type, it is assumed that if the non-reflected + method -- such as :meth:`__add__` -- fails then the overall operation is not + supported, which is why the reflected method is not called. From webhook-mailer at python.org Mon Oct 5 12:49:50 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Mon, 05 Oct 2020 16:49:50 -0000 Subject: [Python-checkins] bpo-41939: Fix test_site.test_license_exists_at_url() (GH-22559) (#22567) Message-ID: https://github.com/python/cpython/commit/10b4136bfa5878c058753e1d1bd091e0f7e1ba40 commit: 10b4136bfa5878c058753e1d1bd091e0f7e1ba40 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-05T18:49:41+02:00 summary: bpo-41939: Fix test_site.test_license_exists_at_url() (GH-22559) (#22567) Call urllib.request.urlcleanup() to reset the global urllib.request._opener. (cherry picked from commit 1fce240d6c4b2b2cc17a86e88c65169e15b9feeb) Co-authored-by: Victor Stinner files: A Misc/NEWS.d/next/Tests/2020-10-05-09-37-43.bpo-41939.P4OlbA.rst M Lib/test/test_site.py diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py index 923f35efe2ede..ece6a07280634 100644 --- a/Lib/test/test_site.py +++ b/Lib/test/test_site.py @@ -509,6 +509,8 @@ def test_license_exists_at_url(self): # string displayed by license in the absence of a LICENSE file. url = license._Printer__data.split()[1] req = urllib.request.Request(url, method='HEAD') + # Reset global urllib.request._opener + self.addCleanup(urllib.request.urlcleanup) try: with socket_helper.transient_internet(url): with urllib.request.urlopen(req) as data: diff --git a/Misc/NEWS.d/next/Tests/2020-10-05-09-37-43.bpo-41939.P4OlbA.rst b/Misc/NEWS.d/next/Tests/2020-10-05-09-37-43.bpo-41939.P4OlbA.rst new file mode 100644 index 0000000000000..e58ad2616da1b --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2020-10-05-09-37-43.bpo-41939.P4OlbA.rst @@ -0,0 +1,3 @@ +Fix test_site.test_license_exists_at_url(): call +``urllib.request.urlcleanup()`` to reset the global +``urllib.request._opener``. Patch by Victor Stinner. From webhook-mailer at python.org Mon Oct 5 12:52:11 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Mon, 05 Oct 2020 16:52:11 -0000 Subject: [Python-checkins] bpo-41584: clarify when the reflected method of a binary arithemtic operator is called (GH-22505) Message-ID: https://github.com/python/cpython/commit/31ceccb2c77854893f3a754aca04bedd74bedb10 commit: 31ceccb2c77854893f3a754aca04bedd74bedb10 branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-05T09:52:02-07:00 summary: bpo-41584: clarify when the reflected method of a binary arithemtic operator is called (GH-22505) (cherry picked from commit d02d824e05e2cb86f4df381be18832e76e2c475f) Co-authored-by: Brett Cannon files: M Doc/reference/datamodel.rst diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 3f05385862416..924f6d9e2d7e0 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -2371,10 +2371,11 @@ left undefined. .. note:: - If the right operand's type is a subclass of the left operand's type and that - subclass provides the reflected method for the operation, this method will be - called before the left operand's non-reflected method. This behavior allows - subclasses to override their ancestors' operations. + If the right operand's type is a subclass of the left operand's type and + that subclass provides a different implementation of the reflected method + for the operation, this method will be called before the left operand's + non-reflected method. This behavior allows subclasses to override their + ancestors' operations. .. method:: object.__iadd__(self, other) @@ -2773,6 +2774,6 @@ An example of an asynchronous context manager class:: method?that will instead have the opposite effect of explicitly *blocking* such fallback. -.. [#] For operands of the same type, it is assumed that if the non-reflected method - (such as :meth:`__add__`) fails the operation is not supported, which is why the - reflected method is not called. +.. [#] For operands of the same type, it is assumed that if the non-reflected + method -- such as :meth:`__add__` -- fails then the overall operation is not + supported, which is why the reflected method is not called. From webhook-mailer at python.org Mon Oct 5 13:06:00 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Mon, 05 Oct 2020 17:06:00 -0000 Subject: [Python-checkins] bpo-41584: clarify when the reflected method of a binary arithemtic operator is called (GH-22505) Message-ID: https://github.com/python/cpython/commit/4aad1e5770fab72908f922a7876075d3d5a7c0d0 commit: 4aad1e5770fab72908f922a7876075d3d5a7c0d0 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-05T10:05:50-07:00 summary: bpo-41584: clarify when the reflected method of a binary arithemtic operator is called (GH-22505) (cherry picked from commit d02d824e05e2cb86f4df381be18832e76e2c475f) Co-authored-by: Brett Cannon files: M Doc/reference/datamodel.rst diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index fc304a191a314..354c6ce53f76f 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -2376,10 +2376,11 @@ left undefined. .. note:: - If the right operand's type is a subclass of the left operand's type and that - subclass provides the reflected method for the operation, this method will be - called before the left operand's non-reflected method. This behavior allows - subclasses to override their ancestors' operations. + If the right operand's type is a subclass of the left operand's type and + that subclass provides a different implementation of the reflected method + for the operation, this method will be called before the left operand's + non-reflected method. This behavior allows subclasses to override their + ancestors' operations. .. method:: object.__iadd__(self, other) @@ -2778,6 +2779,6 @@ An example of an asynchronous context manager class:: method?that will instead have the opposite effect of explicitly *blocking* such fallback. -.. [#] For operands of the same type, it is assumed that if the non-reflected method - (such as :meth:`__add__`) fails the operation is not supported, which is why the - reflected method is not called. +.. [#] For operands of the same type, it is assumed that if the non-reflected + method -- such as :meth:`__add__` -- fails then the overall operation is not + supported, which is why the reflected method is not called. From webhook-mailer at python.org Mon Oct 5 16:18:16 2020 From: webhook-mailer at python.org (Pablo Galindo) Date: Mon, 05 Oct 2020 20:18:16 -0000 Subject: [Python-checkins] Python 3.10.0a1 Message-ID: https://github.com/python/cpython/commit/8e9afaf8226b62a18080d69533a41159c70193cb commit: 8e9afaf8226b62a18080d69533a41159c70193cb branch: master author: Pablo Galindo committer: Pablo Galindo date: 2020-10-05T18:30:18+01:00 summary: Python 3.10.0a1 files: A Misc/NEWS.d/3.10.0a1.rst D Misc/NEWS.d/next/Build/2020-05-19-10-54-08.bpo-40683.W8JHrr.rst D Misc/NEWS.d/next/Build/2020-06-08-19-57-05.bpo-40684.WIY2-i.rst D Misc/NEWS.d/next/Build/2020-06-15-22-14-25.bpo-36020.wbiv0P.rst D Misc/NEWS.d/next/Build/2020-06-25-06-59-13.bpo-40204.GpD04D.rst D Misc/NEWS.d/next/Build/2020-08-24-18-34-01.bpo-41617.sKKXz7.rst D Misc/NEWS.d/next/Build/2020-09-28-21-56-51.bpo-38249.uzMCaZ.rst D Misc/NEWS.d/next/C API/2020-02-08-08-01-35.bpo-39583.qURKSl.rst D Misc/NEWS.d/next/C API/2020-05-20-19-11-12.bpo-40703.qQXfW8.rst D Misc/NEWS.d/next/C API/2020-05-26-16-21-47.bpo-39573.depAgq.rst D Misc/NEWS.d/next/C API/2020-05-27-11-02-15.bpo-40792.pBw2Bb.rst D Misc/NEWS.d/next/C API/2020-06-01-16-12-37.bpo-40826.zQzFoK.rst D Misc/NEWS.d/next/C API/2020-06-01-20-47-49.bpo-40839.bAi52Z.rst D Misc/NEWS.d/next/C API/2020-06-03-17-48-13.bpo-40679.3sgWma.rst D Misc/NEWS.d/next/C API/2020-06-04-08-01-23.bpo-40724.qIIdSi.rst D Misc/NEWS.d/next/C API/2020-06-08-15-59-06.bpo-40910.L56oI0.rst D Misc/NEWS.d/next/C API/2020-06-10-18-37-26.bpo-40943.i4q7rK.rst D Misc/NEWS.d/next/C API/2020-06-15-16-46-01.bpo-36020.djI6jw.rst D Misc/NEWS.d/next/C API/2020-06-15-23-17-51.bpo-40989.tlzG3r.rst D Misc/NEWS.d/next/C API/2020-06-17-11-24-00.bpo-36346.fTMr3S.rst D Misc/NEWS.d/next/C API/2020-06-17-20-31-12.bpo-36346.mwIyxi.rst D Misc/NEWS.d/next/C API/2020-06-24-22-57-07.bpo-41103.doojgE.rst D Misc/NEWS.d/next/C API/2020-06-26-13-29-25.bpo-41123.bRa1oy.rst D Misc/NEWS.d/next/C API/2020-06-28-11-39-22.bpo-41123.sjJWjQ.rst D Misc/NEWS.d/next/C API/2020-06-29-11-33-49.bpo-41123.qFevek.rst D Misc/NEWS.d/next/C API/2020-06-29-15-49-36.bpo-41123.wYY4E1.rst D Misc/NEWS.d/next/C API/2020-07-08-10-14-52.bpo-40170.N6Qx1i.rst D Misc/NEWS.d/next/C API/2020-07-26-19-39-45.bpo-30155.rHZRJ_.rst D Misc/NEWS.d/next/C API/2020-08-10-16-05-08.bpo-41324.waZD35.rst D Misc/NEWS.d/next/C API/2020-08-12-17-09-06.bpo-41524.u6Xfr2.rst D Misc/NEWS.d/next/C API/2020-09-01-23-39-45.bpo-41689.zxHbLB.rst D Misc/NEWS.d/next/C API/2020-09-22-14-47-12.bpo-41834.nrOrDU.rst D Misc/NEWS.d/next/C API/2020-09-27-20-43-16.bpo-41842.bCakAj.rst D Misc/NEWS.d/next/C API/2020-10-02-00-57-34.bpo-41692.fDScsF.rst D Misc/NEWS.d/next/C API/2020-10-05-01-25-23.bpo-41936.1gb5ra.rst D Misc/NEWS.d/next/Core and Builtins/2018-03-15-11-51-36.bpo-26680.wOWYps.rst D Misc/NEWS.d/next/Core and Builtins/2018-08-29-15-57-07.bpo-19569.RGu2Kb.rst D Misc/NEWS.d/next/Core and Builtins/2019-05-25-05-27-39.bpo-36982.0UHgfB.rst D Misc/NEWS.d/next/Core and Builtins/2019-06-02-11-29-15.bpo-29882.AkRzjb.rst D Misc/NEWS.d/next/Core and Builtins/2019-09-01-14-26-02.bpo-37999.XPl6dn.rst D Misc/NEWS.d/next/Core and Builtins/2020-04-05-02-35-08.bpo-1635741.Kfe9fT.rst D Misc/NEWS.d/next/Core and Builtins/2020-04-10-23-54-57.bpo-1635741.ZURqoN.rst D Misc/NEWS.d/next/Core and Builtins/2020-04-11-13-07-49.bpo-4022.Ctpn_F.rst D Misc/NEWS.d/next/Core and Builtins/2020-05-03-22-26-00.bpo-29590.aRz3l7.rst D Misc/NEWS.d/next/Core and Builtins/2020-05-19-19-39-49.bpo-40679.SVzz9p.rst D Misc/NEWS.d/next/Core and Builtins/2020-05-20-01-17-34.bpo-40521.wvAehI.rst D Misc/NEWS.d/next/Core and Builtins/2020-05-21-01-54-00.bpo-40696.u3n8Wx.rst D Misc/NEWS.d/next/Core and Builtins/2020-05-22-00-34-34.bpo-39573.QO2QHj.rst D Misc/NEWS.d/next/Core and Builtins/2020-05-23-01-15-51.bpo-40217.jZsHTc.rst D Misc/NEWS.d/next/Core and Builtins/2020-05-24-02-42-26.bpo-40750.ZmO9Ev.rst D Misc/NEWS.d/next/Core and Builtins/2020-05-25-21-49-11.bpo-38964.lrml90.rst D Misc/NEWS.d/next/Core and Builtins/2020-05-26-17-43-58.bpo-40780.3Ckdgm.rst D Misc/NEWS.d/next/Core and Builtins/2020-05-27-22-37-58.bpo-40792.WEDqqU.rst D Misc/NEWS.d/next/Core and Builtins/2020-05-30-14-37-18.bpo-40824.XR3V5s.rst D Misc/NEWS.d/next/Core and Builtins/2020-05-30-23-18-35.bpo-19468.S-TA7p.rst D Misc/NEWS.d/next/Core and Builtins/2020-05-30-23-23-35.bpo-1635741.0D-laM.rst D Misc/NEWS.d/next/Core and Builtins/2020-06-01-20-31-07.bpo-40826.XCI4M2.rst D Misc/NEWS.d/next/Core and Builtins/2020-06-03-13-53-24.bpo-40854.O6vfQU.rst D Misc/NEWS.d/next/Core and Builtins/2020-06-05-12-48-28.bpo-40870.9cd2sk.rst D Misc/NEWS.d/next/Core and Builtins/2020-06-05-23-25-00.bpo-40883.M6sQ-Q.rst D Misc/NEWS.d/next/Core and Builtins/2020-06-06-00-23-19.bpo-40880.fjdzSh.rst D Misc/NEWS.d/next/Core and Builtins/2020-06-07-22-50-10.bpo-40903.7dWejS.rst D Misc/NEWS.d/next/Core and Builtins/2020-06-08-01-08-57.bpo-40904.76qQzo.rst D Misc/NEWS.d/next/Core and Builtins/2020-06-08-22-46-33.bpo-40889.vIBl-W.rst D Misc/NEWS.d/next/Core and Builtins/2020-06-09-00-20-13.bpo-40890.LoRV-g.rst D Misc/NEWS.d/next/Core and Builtins/2020-06-09-23-52-32.bpo-40847.4XAACw.rst D Misc/NEWS.d/next/Core and Builtins/2020-06-10-11-27-15.bpo-40939.DO-wAI.rst D Misc/NEWS.d/next/Core and Builtins/2020-06-11-16-06-49.bpo-40947.72cZcR.rst D Misc/NEWS.d/next/Core and Builtins/2020-06-12-00-12-28.bpo-40950.tzMy7m.rst D Misc/NEWS.d/next/Core and Builtins/2020-06-12-12-21-54.bpo-40957.Z8n6I6.rst D Misc/NEWS.d/next/Core and Builtins/2020-06-12-22-56-17.bpo-1635741.mmlp3Q.rst D Misc/NEWS.d/next/Core and Builtins/2020-06-15-01-20-44.bpo-40958.7O2Wh1.rst D Misc/NEWS.d/next/Core and Builtins/2020-06-15-16-29-55.bpo-40985.IIN_xX.rst D Misc/NEWS.d/next/Core and Builtins/2020-06-17-00-52-21.bpo-1635741.61iyYh.rst D Misc/NEWS.d/next/Core and Builtins/2020-06-17-10-27-17.bpo-40636.MYaCIe.rst D Misc/NEWS.d/next/Core and Builtins/2020-06-18-00-07-09.bpo-41006.H-wN-d.rst D Misc/NEWS.d/next/Core and Builtins/2020-06-18-19-04-30.bpo-40077._yI-ax.rst D Misc/NEWS.d/next/Core and Builtins/2020-06-20-16-59-02.bpo-40939.6810Ak.rst D Misc/NEWS.d/next/Core and Builtins/2020-06-20-17-00-44.bpo-35975.UDHCHp.rst D Misc/NEWS.d/next/Core and Builtins/2020-06-20-19-27-47.bpo-40939.jxJ4yn.rst D Misc/NEWS.d/next/Core and Builtins/2020-06-20-22-46-18.bpo-41052.46MPeF.rst D Misc/NEWS.d/next/Core and Builtins/2020-06-21-10-54-02.bpo-41061.AHf9MU.rst D Misc/NEWS.d/next/Core and Builtins/2020-06-21-19-53-33.bpo-41056.IDu_EK.rst D Misc/NEWS.d/next/Core and Builtins/2020-06-22-13-22-30.bpo-41076.eWYw2N.rst D Misc/NEWS.d/next/Core and Builtins/2020-06-23-07-35-11.bpo-40521.dMNA6k.rst D Misc/NEWS.d/next/Core and Builtins/2020-06-23-15-10-19.bpo-41084.pt3y7F.rst D Misc/NEWS.d/next/Core and Builtins/2020-06-23-18-32-41.bpo-39960.Kez3fP.rst D Misc/NEWS.d/next/Core and Builtins/2020-06-23-23-26-42.bpo-41094.zEIJse.rst D Misc/NEWS.d/next/Core and Builtins/2020-06-30-04-44-29.bpo-41100.PJwA6F.rst D Misc/NEWS.d/next/Core and Builtins/2020-06-30-20-17-31.bpo-41175.acJoXB.rst D Misc/NEWS.d/next/Core and Builtins/2020-07-01-20-17-38.bpo-1635741.-AtPYu.rst D Misc/NEWS.d/next/Core and Builtins/2020-07-03-23-10-02.bpo-1635741.F5coWe.rst D Misc/NEWS.d/next/Core and Builtins/2020-07-06-13-35-17.bpo-41218.oKnSr2.rst D Misc/NEWS.d/next/Core and Builtins/2020-07-06-18-36-33.bpo-41215.vFGFIz.rst D Misc/NEWS.d/next/Core and Builtins/2020-07-06-20-43-19.bpo-1635741.LYhsni.rst D Misc/NEWS.d/next/Core and Builtins/2020-07-07-16-10-52.bpo-1635741.zU-H_n.rst D Misc/NEWS.d/next/Core and Builtins/2020-07-08-21-55-23.bpo-41252.nBWL-Y.rst D Misc/NEWS.d/next/Core and Builtins/2020-07-08-22-03-54.bpo-41247.PndYIk.rst D Misc/NEWS.d/next/Core and Builtins/2020-07-17-11-31-54.bpo-41323.ChbZHh.rst D Misc/NEWS.d/next/Core and Builtins/2020-07-18-08-15-32.bpo-41295.pu8Ezo.rst D Misc/NEWS.d/next/Core and Builtins/2020-07-18-18-01-10.bpo-41334.t5xMGp.rst D Misc/NEWS.d/next/Core and Builtins/2020-07-19-15-40-52.bpo-41342.RRk_m_.rst D Misc/NEWS.d/next/Core and Builtins/2020-07-20-17-01-17.bpo-38156.ptcdRy.rst D Misc/NEWS.d/next/Core and Builtins/2020-07-27-01-50-06.bpo-41340.pZXfcF.rst D Misc/NEWS.d/next/Core and Builtins/2020-07-28-22-43-27.bpo-41428.FM6xsI.rst D Misc/NEWS.d/next/Core and Builtins/2020-08-02-15-53-12.bpo-41431.TblUBT.rst D Misc/NEWS.d/next/Core and Builtins/2020-08-10-16-11-32.bpo-1635741.O0d3ym.rst D Misc/NEWS.d/next/Core and Builtins/2020-08-12-07-35-07.bpo-41525.d9q3XL.rst D Misc/NEWS.d/next/Core and Builtins/2020-08-12-19-32-15.bpo-41531.WgPzjT.rst D Misc/NEWS.d/next/Core and Builtins/2020-08-12-20-29-57.bpo-41533.4pcVAc.rst D Misc/NEWS.d/next/Core and Builtins/2020-08-13-07-18-05.bpo-1635741.FC13e7.rst D Misc/NEWS.d/next/Core and Builtins/2020-08-13-07-19-21.bpo-1653741.fubBkb.rst D Misc/NEWS.d/next/Core and Builtins/2020-08-25-22-43-33.bpo-40077.vcxSUa.rst D Misc/NEWS.d/next/Core and Builtins/2020-08-26-11-23-31.bpo-41631.3jZcd9.rst D Misc/NEWS.d/next/Core and Builtins/2020-08-28-20-54-04.bpo-1635741.7ijlcI.rst D Misc/NEWS.d/next/Core and Builtins/2020-08-30-20-38-33.bpo-41654.HtnhAM.rst D Misc/NEWS.d/next/Core and Builtins/2020-08-31-11-37-59.bpo-41670.vmRJRx.rst D Misc/NEWS.d/next/Core and Builtins/2020-08-31-14-53-17.bpo-41675.VSoqWU.rst D Misc/NEWS.d/next/Core and Builtins/2020-08-31-17-49-02.bpo-41681.3-VJiH.rst D Misc/NEWS.d/next/Core and Builtins/2020-09-01-17-06-02.bpo-1635741.5jZymK.rst D Misc/NEWS.d/next/Core and Builtins/2020-09-01-17-08-07.bpo-1635741.X9CZgo.rst D Misc/NEWS.d/next/Core and Builtins/2020-09-01-17-22-35.bpo-1635741.CnRME3.rst D Misc/NEWS.d/next/Core and Builtins/2020-09-02-12-00-57.bpo-41690.Ny-Sfy.rst D Misc/NEWS.d/next/Core and Builtins/2020-09-07-09-45-47.bpo-1635741.QuDIut.rst D Misc/NEWS.d/next/Core and Builtins/2020-09-07-11-35-02.bpo-1635741.rvIexb.rst D Misc/NEWS.d/next/Core and Builtins/2020-09-08-20-39-43.bpo-1635741.jiXmyT.rst D Misc/NEWS.d/next/Core and Builtins/2020-09-08-21-58-47.bpo-1635741.vdjSLH.rst D Misc/NEWS.d/next/Core and Builtins/2020-09-12-12-55-45.bpo-41756.1h0tbV.rst D Misc/NEWS.d/next/Core and Builtins/2020-09-12-18-34-34.bpo-1635741.lh335O.rst D Misc/NEWS.d/next/Core and Builtins/2020-09-15-23-29-49.bpo-41780.bOBUIH.rst D Misc/NEWS.d/next/Core and Builtins/2020-09-24-12-15-45.bpo-39934.YVHTCF.rst D Misc/NEWS.d/next/Core and Builtins/2020-09-26-14-43-30.bpo-1635741.aJS9B3.rst D Misc/NEWS.d/next/Core and Builtins/2020-09-27-22-23-14.bpo-41870.2v6_v4.rst D Misc/NEWS.d/next/Core and Builtins/2020-09-28-08-58-28.bpo-41873.VzEDhA.rst D Misc/NEWS.d/next/Core and Builtins/2020-10-04-01-02-58.bpo-41922.kHGT8I.rst D Misc/NEWS.d/next/Core and Builtins/2020-10-04-10-55-12.bpo-41909.BqHPcm.rst D Misc/NEWS.d/next/Documentation/2019-08-16-20-25-42.bpo-37703.Qm_l_H.rst D Misc/NEWS.d/next/Documentation/2020-03-07-03-53-39.bpo-39883.1tnb4-.rst D Misc/NEWS.d/next/Documentation/2020-05-09-12-10-31.bpo-40552._0uB73.rst D Misc/NEWS.d/next/Documentation/2020-07-21-15-23-30.bpo-40979.pLA8rO.rst D Misc/NEWS.d/next/Documentation/2020-07-25-14-20-00.bpo-41314.yrjko0.rst D Misc/NEWS.d/next/Documentation/2020-07-27-20-46-17.bpo-41045.GFF6Ul.rst D Misc/NEWS.d/next/Documentation/2020-08-12-18-35-40.bpo-40204.C8A_pe.rst D Misc/NEWS.d/next/Documentation/2020-08-25-15-11-23.bpo-41624.ddjJlN.rst D Misc/NEWS.d/next/Documentation/2020-09-08-16-57-09.bpo-41726.g0UXrn.rst D Misc/NEWS.d/next/Documentation/2020-09-10-07-48-02.bpo-37149.VD0rCv.rst D Misc/NEWS.d/next/Documentation/2020-09-12-17-37-13.bpo-35293._cOwPD.rst D Misc/NEWS.d/next/Documentation/2020-09-24-15-35-13.bpo-41774.5IqdGP.rst D Misc/NEWS.d/next/Documentation/2020-10-03-18-20-46.bpo-41428._ju1NE.rst D Misc/NEWS.d/next/IDLE/2020-05-24-06-19-43.bpo-40723.AJLd4U.rst D Misc/NEWS.d/next/IDLE/2020-05-29-18-21-58.bpo-39885.zB_-bN.rst D Misc/NEWS.d/next/IDLE/2020-06-27-17-02-00.bpo-41144.JoFGIX.rst D Misc/NEWS.d/next/IDLE/2020-06-29-14-51-15.bpo-41152.d6mV0C.rst D Misc/NEWS.d/next/IDLE/2020-07-07-18-44-30.bpo-37765.umc1o8.rst D Misc/NEWS.d/next/IDLE/2020-07-16-17-39-06.bpo-41300.wRixNb.rst D Misc/NEWS.d/next/IDLE/2020-07-24-17-49-58.bpo-41373.YQIPu_.rst D Misc/NEWS.d/next/IDLE/2020-08-09-13-42-55.bpo-41468.zkP0_Y.rst D Misc/NEWS.d/next/IDLE/2020-09-22-00-45-40.bpo-40181.hhQi3z.rst D Misc/NEWS.d/next/IDLE/2020-09-22-11-13-45.bpo-35764.VoNa8y.rst D Misc/NEWS.d/next/IDLE/2020-09-24-14-31-16.bpo-41775.sB8Vre.rst D Misc/NEWS.d/next/Library/2018-03-15-11-55-04.bpo-26680.eKAi85.rst D Misc/NEWS.d/next/Library/2018-03-15-11-56-48.bpo-26680.Udkhn4.rst D Misc/NEWS.d/next/Library/2018-06-07-22-04-01.bpo-28557.ViNJnK.rst D Misc/NEWS.d/next/Library/2018-06-12-23-30-41.bpo-33660.AdDn5Z.rst D Misc/NEWS.d/next/Library/2018-07-29-12-14-54.bpo-34226.BE7zbu.rst D Misc/NEWS.d/next/Library/2018-07-30-12-48-17.bpo-31844.0_GKsD.rst D Misc/NEWS.d/next/Library/2018-08-21-16-20-33.bpo-29620.xxx666.rst D Misc/NEWS.d/next/Library/2018-10-27-09-37-03.bpo-35078.kweA3R.rst D Misc/NEWS.d/next/Library/2019-03-01-01-56-23.bpo-33944.-82Pkt.rst D Misc/NEWS.d/next/Library/2019-03-17-19-01-53.bpo-36290.7VXo_K.rst D Misc/NEWS.d/next/Library/2019-05-31-23-54-28.bpo-12178.N6FLCZ.rst D Misc/NEWS.d/next/Library/2019-08-11-16-28-03.bpo-26543.X-TJZO.rst D Misc/NEWS.d/next/Library/2019-09-12-21-34-03.bpo-38144.8uQCdd.rst D Misc/NEWS.d/next/Library/2019-10-25-23-45-49.bpo-35714.fw3xb7.rst D Misc/NEWS.d/next/Library/2019-11-13-07-37-11.bpo-38731.9qmcSx.rst D Misc/NEWS.d/next/Library/2019-12-15-18-47-20.bpo-39040.tKa0Qs.rst D Misc/NEWS.d/next/Library/2020-02-23-15-09-47.bpo-39244.aBK5IM.rst D Misc/NEWS.d/next/Library/2020-02-24-10-58-34.bpo-39728.kOOaHn.rst D Misc/NEWS.d/next/Library/2020-03-11-07-44-06.bpo-31122.zIQ80l.rst D Misc/NEWS.d/next/Library/2020-03-29-21-32-00.bpo-40084.MCYwcv.rst D Misc/NEWS.d/next/Library/2020-04-03-16-13-59.bpo-40105.hfM2c0.rst D Misc/NEWS.d/next/Library/2020-04-18-14-16-02.bpo-40318.K2UdRx.rst D Misc/NEWS.d/next/Library/2020-04-20-22-08-36.bpo-23082.iX90Id.rst D Misc/NEWS.d/next/Library/2020-04-23-18-21-19.bpo-39385.MIAyS7.rst D Misc/NEWS.d/next/Library/2020-05-06-02-01-25.bpo-13097.Wh5xSK.rst D Misc/NEWS.d/next/Library/2020-05-07-22-00-12.bpo-39881.E1xsNv.rst D Misc/NEWS.d/next/Library/2020-05-13-16-28-33.bpo-40611.ZCk0_c.rst D Misc/NEWS.d/next/Library/2020-05-15-21-14-45.bpo-36543.Jt-eSX.rst D Misc/NEWS.d/next/Library/2020-05-17-02-03-09.bpo-32309.KM9psl.rst D Misc/NEWS.d/next/Library/2020-05-18-15-26-31.bpo-40671.NeZ9Cy.rst D Misc/NEWS.d/next/Library/2020-05-18-15-38-25.bpo-25920.PxrLY8.rst D Misc/NEWS.d/next/Library/2020-05-18-17-29-30.bpo-40626.NeZufF.rst D Misc/NEWS.d/next/Library/2020-05-18-22-41-02.bpo-40614.8j3kmq.rst D Misc/NEWS.d/next/Library/2020-05-20-12-53-20.bpo-9216.ps7Yf1.rst D Misc/NEWS.d/next/Library/2020-05-20-13-03-28.bpo-40695.lr4aIS.rst D Misc/NEWS.d/next/Library/2020-05-20-14-38-04.bpo-40698.zwl5Hc.rst D Misc/NEWS.d/next/Library/2020-05-22-12-45-58.bpo-40726.7oBdMw.rst D Misc/NEWS.d/next/Library/2020-05-23-00-22-11.bpo-40737.iph-CM.rst D Misc/NEWS.d/next/Library/2020-05-23-04-18-00.bpo-37129.YoYoYo.rst D Misc/NEWS.d/next/Library/2020-05-24-11-06-37.bpo-40756.7ZH83z.rst D Misc/NEWS.d/next/Library/2020-05-24-23-52-35.bpo-40759.DdZdaw.rst D Misc/NEWS.d/next/Library/2020-05-25-11-52-23.bpo-30064.6CICsH.rst D Misc/NEWS.d/next/Library/2020-05-25-22-18-38.bpo-30008.CKC3td.rst D Misc/NEWS.d/next/Library/2020-05-27-00-09-52.bpo-16995.4niOT7.rst D Misc/NEWS.d/next/Library/2020-05-27-17-00-18.bpo-40795.eZSnHA.rst D Misc/NEWS.d/next/Library/2020-05-27-18-04-52.bpo-40791.IzpNor.rst D Misc/NEWS.d/next/Library/2020-05-27-21-27-01.bpo-40767.L5MnVV.rst D Misc/NEWS.d/next/Library/2020-05-27-22-19-42.bpo-40792.87Yx01.rst D Misc/NEWS.d/next/Library/2020-05-28-16-51-00.bpo-38488.hFQNgA.rst D Misc/NEWS.d/next/Library/2020-05-28-17-32-29.bpo-40777.1kJU6N.rst D Misc/NEWS.d/next/Library/2020-05-30-08-10-23.bpo-40744.jKURVV.rst D Misc/NEWS.d/next/Library/2020-05-30-12-44-29.bpo-39384.Iqxy3q.rst D Misc/NEWS.d/next/Library/2020-05-30-14-19-47.bpo-26407.MjWLO1.rst D Misc/NEWS.d/next/Library/2020-05-30-18-48-58.bpo-40755.IyOe2J.rst D Misc/NEWS.d/next/Library/2020-05-31-15-52-18.bpo-40834.MO9_hb.rst D Misc/NEWS.d/next/Library/2020-05-31-23-32-36.bpo-17005.JlRUGB.rst D Misc/NEWS.d/next/Library/2020-06-01-02-16-29.bpo-39314.0T9hlA.rst D Misc/NEWS.d/next/Library/2020-06-02-02-16-02.bpo-39791.StCJlA.rst D Misc/NEWS.d/next/Library/2020-06-02-23-49-07.bpo-32604.ZN4V4l.rst D Misc/NEWS.d/next/Library/2020-06-04-16-25-15.bpo-40807.yYyLWx.rst D Misc/NEWS.d/next/Library/2020-06-05-19-29-10.bpo-39791._CcO3d.rst D Misc/NEWS.d/next/Library/2020-06-05-20-00-18.bpo-40876.zDhiZj.rst D Misc/NEWS.d/next/Library/2020-06-06-02-42-26.bpo-40884.n7fOwS.rst D Misc/NEWS.d/next/Library/2020-06-06-14-09-55.bpo-33689.EFUDH7.rst D Misc/NEWS.d/next/Library/2020-06-08-18-59-16.bpo-23427.ilg1Cz.rst D Misc/NEWS.d/next/Library/2020-06-11-11-07-10.bpo-40939.-D5Asl.rst D Misc/NEWS.d/next/Library/2020-06-12-10-44-15.bpo-40855.jSot83.rst D Misc/NEWS.d/next/Library/2020-06-12-11-55-30.bpo-40955.huixCg.rst D Misc/NEWS.d/next/Library/2020-06-13-12-04-50.bpo-40924.SM_luS.rst D Misc/NEWS.d/next/Library/2020-06-15-00-13-57.bpo-40967._dx3OO.rst D Misc/NEWS.d/next/Library/2020-06-15-12-22-53.bpo-40448.1dk8Bu.rst D Misc/NEWS.d/next/Library/2020-06-17-17-26-24.bpo-41002.NPBItE.rst D Misc/NEWS.d/next/Library/2020-06-17-23-49-45.bpo-35018.NP5_Qk.rst D Misc/NEWS.d/next/Library/2020-06-18-10-34-59.bpo-41025.elf_nz.rst D Misc/NEWS.d/next/Library/2020-06-20-00-19-30.bpo-41043.p-Pk-H.rst D Misc/NEWS.d/next/Library/2020-06-20-10-16-57.bpo-41048.hEXB-B.rst D Misc/NEWS.d/next/Library/2020-06-20-18-33-03.bpo-41056.gTH4Bq.rst D Misc/NEWS.d/next/Library/2020-06-20-18-35-43.bpo-41056.Garcle.rst D Misc/NEWS.d/next/Library/2020-06-20-18-37-29.bpo-41056.d9v_uL.rst D Misc/NEWS.d/next/Library/2020-06-20-21-03-55.bpo-41058.gztdZy.rst D Misc/NEWS.d/next/Library/2020-06-22-10-25-39.bpo-41068._bX2BW.rst D Misc/NEWS.d/next/Library/2020-06-22-20-08-40.bpo-31938.EVuko9.rst D Misc/NEWS.d/next/Library/2020-06-23-06-09-59.bpo-40521.HUfxP7.rst D Misc/NEWS.d/next/Library/2020-06-25-10-11-47.bpo-31082.HsgDkx.rst D Misc/NEWS.d/next/Library/2020-06-27-13-51-36.bpo-41138.bIpf7g.rst D Misc/NEWS.d/next/Library/2020-06-28-21-16-51.bpo-40874.YImvzA.rst D Misc/NEWS.d/next/Library/2020-06-30-20-50-51.bpo-41161.QTdJjz.rst D Misc/NEWS.d/next/Library/2020-07-01-17-33-50.bpo-41182.FPFI0N.rst D Misc/NEWS.d/next/Library/2020-07-02-11-53-45.bpo-41193.8-Tnql.rst D Misc/NEWS.d/next/Library/2020-07-02-15-03-04.bpo-41195.cEnpO3.rst D Misc/NEWS.d/next/Library/2020-07-03-13-15-08.bpo-41194.djrKjs.rst D Misc/NEWS.d/next/Library/2020-07-04-21-56-46.bpo-39168.DQWsXj.rst D Misc/NEWS.d/next/Library/2020-07-05-19-16-02.bpo-29727.Q6Z2rg.rst D Misc/NEWS.d/next/Library/2020-07-06-16-58-53.bpo-41207.Emw7Nk.rst D Misc/NEWS.d/next/Library/2020-07-07-21-56-26.bpo-41235.H2csMU.rst D Misc/NEWS.d/next/Library/2020-07-11-00-15-01.bpo-41273.SVrsJh.rst D Misc/NEWS.d/next/Library/2020-07-12-22-16-58.bpo-39017.x3Cg-9.rst D Misc/NEWS.d/next/Library/2020-07-13-15-06-35.bpo-41288.8mn5P-.rst D Misc/NEWS.d/next/Library/2020-07-18-18-07-40.bpo-41333.upkHIm.rst D Misc/NEWS.d/next/Library/2020-07-20-13-27-48.bpo-41344.iKipNd.rst D Misc/NEWS.d/next/Library/2020-07-20-19-13-17.bpo-41341.wqrj8C.rst D Misc/NEWS.d/next/Library/2020-07-21-16-20-55.bpo-35328.jXovHb.rst D Misc/NEWS.d/next/Library/2020-07-21-21-45-55.bpo-41364.5O-k7A.rst D Misc/NEWS.d/next/Library/2020-07-23-01-18-34.bpo-41317.O17Z6x.rst D Misc/NEWS.d/next/Library/2020-07-26-21-18-43.bpo-41384.MlzIgV.rst D Misc/NEWS.d/next/Library/2020-07-28-12-08-58.bpo-41316.bSCbK4.rst D Misc/NEWS.d/next/Library/2020-07-30-14-56-58.bpo-41440.rju34k.rst D Misc/NEWS.d/next/Library/2020-08-01-00-51-15.bpo-41421.dHKRVB.rst D Misc/NEWS.d/next/Library/2020-08-03-01-59-48.bpo-41425.KJo6zF.rst D Misc/NEWS.d/next/Library/2020-08-04-00-20-30.bpo-41467.Z8DgTL.rst D Misc/NEWS.d/next/Library/2020-08-07-06-06-29.bpo-41497.aBtsWz.rst D Misc/NEWS.d/next/Library/2020-08-07-15-18-16.bpo-41503.IYftcu.rst D Misc/NEWS.d/next/Library/2020-08-09-18-16-05.bpo-41513.e6K6EK.rst D Misc/NEWS.d/next/Library/2020-08-12-07-43-31.bpo-41528.bu83oD.rst D Misc/NEWS.d/next/Library/2020-08-12-13-25-16.bpo-41520.BEUWa4.rst D Misc/NEWS.d/next/Library/2020-08-13-08-07-25.bpo-40782.aGZqmB.rst D Misc/NEWS.d/next/Library/2020-08-15-15-21-40.bpo-37658.f9nivB.rst D Misc/NEWS.d/next/Library/2020-08-15-15-50-12.bpo-32751.85je5X.rst D Misc/NEWS.d/next/Library/2020-08-15-18-17-21.bpo-39994.dOgPOh.rst D Misc/NEWS.d/next/Library/2020-08-21-15-51-15.bpo-41609.JmiUKG.rst D Misc/NEWS.d/next/Library/2020-08-23-14-23-18.bpo-41513.DGqc_I.rst D Misc/NEWS.d/next/Library/2020-08-29-16-07-36.bpo-41662.Mn79zh.rst D Misc/NEWS.d/next/Library/2020-08-29-16-45-12.bpo-41638.iZfW5N.rst D Misc/NEWS.d/next/Library/2020-08-30-10-24-26.bpo-39010._mzXJW.rst D Misc/NEWS.d/next/Library/2020-08-30-21-38-57.bpo-41662.6e9iZn.rst D Misc/NEWS.d/next/Library/2020-09-01-15-57-51.bpo-41687.m1b1KA.rst D Misc/NEWS.d/next/Library/2020-09-03-01-35-32.bpo-41696.zkYGre.rst D Misc/NEWS.d/next/Library/2020-09-04-20-45-38.bpo-41720.PW9MzZ.rst D Misc/NEWS.d/next/Library/2020-09-06-20-27-10.bpo-41732.1SKv26.rst D Misc/NEWS.d/next/Library/2020-09-08-13-51-16.bpo-1635741.wkPeoT.rst D Misc/NEWS.d/next/Library/2020-09-08-13-55-34.bpo-1635741.56MLP-.rst D Misc/NEWS.d/next/Library/2020-09-11-12-38-55.bpo-39651.JMp9l2.rst D Misc/NEWS.d/next/Library/2020-09-12-16-18-42.bpo-32218.IpYkEe.rst D Misc/NEWS.d/next/Library/2020-09-14-19-27-46.bpo-41789.pI_uZQ.rst D Misc/NEWS.d/next/Library/2020-09-15-07-55-35.bpo-41792.qMpSlU.rst D Misc/NEWS.d/next/Library/2020-09-15-14-56-13.bpo-39587.69xzuh.rst D Misc/NEWS.d/next/Library/2020-09-15-22-43-30.bpo-41517.sLBH7g.rst D Misc/NEWS.d/next/Library/2020-09-19-12-22-08.bpo-41816.ynynXJ.rst D Misc/NEWS.d/next/Library/2020-09-19-23-14-54.bpo-41815.RNpuX3.rst D Misc/NEWS.d/next/Library/2020-09-20-15-14-05.bpo-41810.7l8lyV.rst D Misc/NEWS.d/next/Library/2020-09-22-00-23-30.bpo-41817.bnh-VG.rst D Misc/NEWS.d/next/Library/2020-09-22-13-51-14.bpo-41833.6HVDjT.rst D Misc/NEWS.d/next/Library/2020-09-22-14-55-34.bpo-40670.R5sm68.rst D Misc/NEWS.d/next/Library/2020-09-23-03-33-37.bpo-40564.iXQqMq.rst D Misc/NEWS.d/next/Library/2020-09-23-22-52-24.bpo-41842.lIuhC9.rst D Misc/NEWS.d/next/Library/2020-09-23-23-17-59.bpo-41840.QRFr4L.rst D Misc/NEWS.d/next/Library/2020-09-28-23-22-25.bpo-41773.oKkus0.rst D Misc/NEWS.d/next/Library/2020-09-30-23-49-42.bpo-41887.-ee2S-.rst D Misc/NEWS.d/next/Library/2020-10-01-10-50-12.bpo-41900.Cho7oh.rst D Misc/NEWS.d/next/Security/2020-02-12-14-17-39.bpo-39603.Gt3RSg.rst D Misc/NEWS.d/next/Security/2020-06-29-16-02-29.bpo-41004.ovF0KZ.rst D Misc/NEWS.d/next/Security/2020-07-03-17-21-37.bpo-29778.cR_fGS.rst D Misc/NEWS.d/next/Security/2020-07-03-20-41-29.bpo-41162.tb8pVj.rst D Misc/NEWS.d/next/Security/2020-07-15-20-15-08.bpo-41304.vNEeYA.rst D Misc/NEWS.d/next/Tests/2018-08-20-09-38-52.bpo-34401.eGxMPm.rst D Misc/NEWS.d/next/Tests/2019-09-14-13-20-27.bpo-38169.hurq4B.rst D Misc/NEWS.d/next/Tests/2020-04-09-15-40-03.bpo-31904.TJ4k3d.rst D Misc/NEWS.d/next/Tests/2020-05-26-07-53-31.bpo-17258.X_IKTQ.rst D Misc/NEWS.d/next/Tests/2020-06-09-18-48-18.bpo-40927.67ylLg.rst D Misc/NEWS.d/next/Tests/2020-06-12-20-46-23.bpo-40964.OBzf2c.rst D Misc/NEWS.d/next/Tests/2020-06-17-15-07-14.bpo-41003.tiH_Fy.rst D Misc/NEWS.d/next/Tests/2020-06-17-17-27-07.bpo-41009.Rvn6OQ.rst D Misc/NEWS.d/next/Tests/2020-06-17-18-00-21.bpo-38377.jfg4TH.rst D Misc/NEWS.d/next/Tests/2020-06-22-00-21-12.bpo-41069.bLZkX-.rst D Misc/NEWS.d/next/Tests/2020-06-23-12-02-45.bpo-41085.JZKsyz.rst D Misc/NEWS.d/next/Tests/2020-08-07-17-28-49.bpo-41477.GrFexU.rst D Misc/NEWS.d/next/Tests/2020-08-11-14-59-13.bpo-41521.w2UYK7.rst D Misc/NEWS.d/next/Tests/2020-08-25-19-25-36.bpo-41602.Z64s0I.rst D Misc/NEWS.d/next/Tests/2020-09-11-19-12-31.bpo-41731.Ivxh4U.rst D Misc/NEWS.d/next/Tests/2020-10-05-09-37-43.bpo-41939.P4OlbA.rst D Misc/NEWS.d/next/Windows/2019-07-11-06-11-09.bpo-37556.sygMUU.rst D Misc/NEWS.d/next/Windows/2020-05-19-04-11-12.bpo-40677.qQbLW8.rst D Misc/NEWS.d/next/Windows/2020-05-19-14-43-33.bpo-39631.Z5yXam.rst D Misc/NEWS.d/next/Windows/2020-06-12-13-13-44.bpo-40164.SPrSn5.rst D Misc/NEWS.d/next/Windows/2020-06-23-03-12-57.bpo-41039.0hgd0s.rst D Misc/NEWS.d/next/Windows/2020-06-24-21-30-42.bpo-41074.gaQc3C.rst D Misc/NEWS.d/next/Windows/2020-06-28-12-40-41.bpo-41142.jpZzzh.rst D Misc/NEWS.d/next/Windows/2020-07-20-23-26-26.bpo-40741.C9sc_d.rst D Misc/NEWS.d/next/Windows/2020-07-28-11-55-43.bpo-41412.ME20KB.rst D Misc/NEWS.d/next/Windows/2020-07-28-12-39-32.bpo-40948.ISUFO6.rst D Misc/NEWS.d/next/Windows/2020-08-06-16-59-10.bpo-41492.2FQ9cM.rst D Misc/NEWS.d/next/Windows/2020-08-13-22-40-58.bpo-41526.-i2bwb.rst D Misc/NEWS.d/next/Windows/2020-09-04-21-35-28.bpo-41627.sx2KN1.rst D Misc/NEWS.d/next/Windows/2020-09-11-17-59-33.bpo-41744.e_ugDQ.rst D Misc/NEWS.d/next/macOS/2020-06-07-20-10-56.bpo-40741.80A2BW.rst D Misc/NEWS.d/next/macOS/2020-06-17-13-45-15.bpo-41005.zZegdV.rst D Misc/NEWS.d/next/macOS/2020-06-19-14-19-08.bpo-40741.L7yTbm.rst D Misc/NEWS.d/next/macOS/2020-06-24-13-51-57.bpo-41100.mcHdc5.rst D Misc/NEWS.d/next/macOS/2020-06-25-06-09-00.bpo-39580.N_vJ9h.rst D Misc/NEWS.d/next/macOS/2020-08-26-09-31-37.bpo-41557.mcQ75z.rst M Include/patchlevel.h M Lib/pydoc_data/topics.py M README.rst diff --git a/Include/patchlevel.h b/Include/patchlevel.h index 8578b6597f602..c4468ad3f2223 100644 --- a/Include/patchlevel.h +++ b/Include/patchlevel.h @@ -20,10 +20,10 @@ #define PY_MINOR_VERSION 10 #define PY_MICRO_VERSION 0 #define PY_RELEASE_LEVEL PY_RELEASE_LEVEL_ALPHA -#define PY_RELEASE_SERIAL 0 +#define PY_RELEASE_SERIAL 1 /* Version as a string */ -#define PY_VERSION "3.10.0a0" +#define PY_VERSION "3.10.0a1" /*--end constants--*/ /* Version as a single 4-byte hex number, e.g. 0x010502B2 == 1.5.2b2. diff --git a/Lib/pydoc_data/topics.py b/Lib/pydoc_data/topics.py index 8aca5c0cb88e3..1fdb1ae859e3e 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 Apr 27 22:35:16 2020 +# Autogenerated by Sphinx on Mon Oct 5 18:27:28 2020 topics = {'assert': 'The "assert" statement\n' '**********************\n' '\n' @@ -99,27 +99,26 @@ 'assigned,\n' ' from left to right, to the corresponding targets.\n' '\n' - ' * If the target list contains one target prefixed with an\n' - ' asterisk, called a ?starred? target: The object must be ' - 'an\n' - ' iterable with at least as many items as there are targets ' - 'in the\n' - ' target list, minus one. The first items of the iterable ' - 'are\n' - ' assigned, from left to right, to the targets before the ' + ' * If the target list contains one target prefixed with an ' + 'asterisk,\n' + ' called a ?starred? target: The object must be an iterable ' + 'with at\n' + ' least as many items as there are targets in the target ' + 'list, minus\n' + ' one. The first items of the iterable are assigned, from ' + 'left to\n' + ' right, to the targets before the starred target. The ' + 'final items\n' + ' of the iterable are assigned to the targets after the ' 'starred\n' - ' target. The final items of the iterable are assigned to ' - 'the\n' - ' targets after the starred target. A list of the remaining ' - 'items\n' - ' in the iterable is then assigned to the starred target ' - '(the list\n' - ' can be empty).\n' + ' target. A list of the remaining items in the iterable is ' + 'then\n' + ' assigned to the starred target (the list can be empty).\n' '\n' ' * Else: The object must be an iterable with the same number ' - 'of\n' - ' items as there are targets in the target list, and the ' - 'items are\n' + 'of items\n' + ' as there are targets in the target list, and the items ' + 'are\n' ' assigned, from left to right, to the corresponding ' 'targets.\n' '\n' @@ -135,10 +134,10 @@ 'in the\n' ' current local namespace.\n' '\n' - ' * Otherwise: the name is bound to the object in the global\n' - ' namespace or the outer namespace determined by ' - '"nonlocal",\n' - ' respectively.\n' + ' * Otherwise: the name is bound to the object in the global ' + 'namespace\n' + ' or the outer namespace determined by "nonlocal", ' + 'respectively.\n' '\n' ' The name is rebound if it was already bound. This may cause ' 'the\n' @@ -225,26 +224,27 @@ 'called with\n' ' appropriate arguments.\n' '\n' - '* If the target is a slicing: The primary expression in the\n' - ' reference is evaluated. It should yield a mutable sequence ' - 'object\n' - ' (such as a list). The assigned object should be a sequence ' - 'object\n' - ' of the same type. Next, the lower and upper bound ' - 'expressions are\n' - ' evaluated, insofar they are present; defaults are zero and ' - 'the\n' - ' sequence?s length. The bounds should evaluate to integers. ' - 'If\n' - ' either bound is negative, the sequence?s length is added to ' - 'it. The\n' - ' resulting bounds are clipped to lie between zero and the ' + '* If the target is a slicing: The primary expression in the ' + 'reference\n' + ' is evaluated. It should yield a mutable sequence object ' + '(such as a\n' + ' list). The assigned object should be a sequence object of ' + 'the same\n' + ' type. Next, the lower and upper bound expressions are ' + 'evaluated,\n' + ' insofar they are present; defaults are zero and the ' 'sequence?s\n' - ' length, inclusive. Finally, the sequence object is asked to ' - 'replace\n' - ' the slice with the items of the assigned sequence. The ' - 'length of\n' - ' the slice may be different from the length of the assigned ' + ' length. The bounds should evaluate to integers. If either ' + 'bound is\n' + ' negative, the sequence?s length is added to it. The ' + 'resulting\n' + ' bounds are clipped to lie between zero and the sequence?s ' + 'length,\n' + ' inclusive. Finally, the sequence object is asked to replace ' + 'the\n' + ' slice with the items of the assigned sequence. The length ' + 'of the\n' + ' slice may be different from the length of the assigned ' 'sequence,\n' ' thus changing the length of the target sequence, if the ' 'target\n' @@ -544,13 +544,17 @@ '\n' '-[ Footnotes ]-\n' '\n' - '[1] The exception is propagated to the invocation stack unless\n' - ' there is a "finally" clause which happens to raise another\n' - ' exception. That new exception causes the old one to be lost.\n' + '[1] The exception is propagated to the invocation stack unless ' + 'there\n' + ' is a "finally" clause which happens to raise another ' + 'exception.\n' + ' That new exception causes the old one to be lost.\n' '\n' - '[2] A string literal appearing as the first statement in the\n' - ' function body is transformed into the function?s "__doc__"\n' - ' attribute and therefore the function?s *docstring*.\n' + '[2] A string literal appearing as the first statement in the ' + 'function\n' + ' body is transformed into the function?s "__doc__" attribute ' + 'and\n' + ' therefore the function?s *docstring*.\n' '\n' '[3] A string literal appearing as the first statement in the class\n' ' body is transformed into the namespace?s "__doc__" item and\n' @@ -688,11 +692,13 @@ 'needs, for\n' ' example, "object.__getattribute__(self, name)".\n' '\n' - ' Note: This method may still be bypassed when looking ' - 'up special\n' - ' methods as the result of implicit invocation via ' - 'language syntax\n' - ' or built-in functions. See Special method lookup.\n' + ' Note:\n' + '\n' + ' This method may still be bypassed when looking up ' + 'special methods\n' + ' as the result of implicit invocation via language ' + 'syntax or\n' + ' built-in functions. See Special method lookup.\n' '\n' 'object.__setattr__(self, name, value)\n' '\n' @@ -776,15 +782,16 @@ '\n' ' sys.modules[__name__].__class__ = VerboseModule\n' '\n' - 'Note: Defining module "__getattr__" and setting module ' - '"__class__"\n' - ' only affect lookups made using the attribute access ' - 'syntax ?\n' - ' directly accessing the module globals (whether by code ' - 'within the\n' - ' module, or via a reference to the module?s globals ' - 'dictionary) is\n' - ' unaffected.\n' + 'Note:\n' + '\n' + ' Defining module "__getattr__" and setting module ' + '"__class__" only\n' + ' affect lookups made using the attribute access syntax ' + '? directly\n' + ' accessing the module globals (whether by code within ' + 'the module, or\n' + ' via a reference to the module?s globals dictionary) is ' + 'unaffected.\n' '\n' 'Changed in version 3.5: "__class__" module attribute is ' 'now writable.\n' @@ -867,12 +874,14 @@ 'created. The\n' ' descriptor has been assigned to *name*.\n' '\n' - ' Note: "__set_name__()" is only called implicitly as ' - 'part of the\n' - ' "type" constructor, so it will need to be called ' - 'explicitly with\n' - ' the appropriate parameters when a descriptor is ' - 'added to a class\n' + ' Note:\n' + '\n' + ' "__set_name__()" is only called implicitly as part ' + 'of the "type"\n' + ' constructor, so it will need to be called ' + 'explicitly with the\n' + ' appropriate parameters when a descriptor is added ' + 'to a class\n' ' after initial creation:\n' '\n' ' class A:\n' @@ -1033,10 +1042,9 @@ '--------------------------\n' '\n' '* When inheriting from a class without *__slots__*, the ' - '*__dict__*\n' - ' and *__weakref__* attribute of the instances will ' - 'always be\n' - ' accessible.\n' + '*__dict__* and\n' + ' *__weakref__* attribute of the instances will always ' + 'be accessible.\n' '\n' '* Without a *__dict__* variable, instances cannot be ' 'assigned new\n' @@ -1051,14 +1059,12 @@ ' declaration.\n' '\n' '* Without a *__weakref__* variable for each instance, ' - 'classes\n' - ' defining *__slots__* do not support weak references to ' - 'its\n' - ' instances. If weak reference support is needed, then ' - 'add\n' - ' "\'__weakref__\'" to the sequence of strings in the ' - '*__slots__*\n' - ' declaration.\n' + 'classes defining\n' + ' *__slots__* do not support weak references to its ' + 'instances. If weak\n' + ' reference support is needed, then add ' + '"\'__weakref__\'" to the\n' + ' sequence of strings in the *__slots__* declaration.\n' '\n' '* *__slots__* are implemented at the class level by ' 'creating\n' @@ -1071,24 +1077,23 @@ ' attribute would overwrite the descriptor assignment.\n' '\n' '* The action of a *__slots__* declaration is not limited ' - 'to the\n' - ' class where it is defined. *__slots__* declared in ' - 'parents are\n' - ' available in child classes. However, child subclasses ' - 'will get a\n' - ' *__dict__* and *__weakref__* unless they also define ' - '*__slots__*\n' - ' (which should only contain names of any *additional* ' - 'slots).\n' + 'to the class\n' + ' where it is defined. *__slots__* declared in parents ' + 'are available\n' + ' in child classes. However, child subclasses will get a ' + '*__dict__*\n' + ' and *__weakref__* unless they also define *__slots__* ' + '(which should\n' + ' only contain names of any *additional* slots).\n' '\n' '* If a class defines a slot also defined in a base ' - 'class, the\n' - ' instance variable defined by the base class slot is ' - 'inaccessible\n' - ' (except by retrieving its descriptor directly from the ' - 'base class).\n' - ' This renders the meaning of the program undefined. In ' - 'the future, a\n' + 'class, the instance\n' + ' variable defined by the base class slot is ' + 'inaccessible (except by\n' + ' retrieving its descriptor directly from the base ' + 'class). This\n' + ' renders the meaning of the program undefined. In the ' + 'future, a\n' ' check may be added to prevent this.\n' '\n' '* Nonempty *__slots__* does not work for classes derived ' @@ -1097,9 +1102,9 @@ '"bytes" and "tuple".\n' '\n' '* Any non-string iterable may be assigned to ' - '*__slots__*. Mappings\n' - ' may also be used; however, in the future, special ' - 'meaning may be\n' + '*__slots__*. Mappings may\n' + ' also be used; however, in the future, special meaning ' + 'may be\n' ' assigned to the values corresponding to each key.\n' '\n' '* *__class__* assignment works only if both classes have ' @@ -1115,9 +1120,9 @@ ' raise "TypeError".\n' '\n' '* If an iterator is used for *__slots__* then a ' - 'descriptor is\n' - ' created for each of the iterator?s values. However, ' - 'the *__slots__*\n' + 'descriptor is created\n' + ' for each of the iterator?s values. However, the ' + '*__slots__*\n' ' attribute will be an empty iterator.\n', 'attribute-references': 'Attribute references\n' '********************\n' @@ -1882,10 +1887,10 @@ ' != x" is true. This behavior is compliant with IEEE 754.\n' '\n' '* "None" and "NotImplemented" are singletons. **PEP 8** ' - 'advises\n' - ' that comparisons for singletons should always be done with ' - '"is" or\n' - ' "is not", never the equality operators.\n' + 'advises that\n' + ' comparisons for singletons should always be done with "is" ' + 'or "is\n' + ' not", never the equality operators.\n' '\n' '* Binary sequences (instances of "bytes" or "bytearray") can ' 'be\n' @@ -1901,15 +1906,15 @@ '\n' ' Strings and binary sequences cannot be directly compared.\n' '\n' - '* Sequences (instances of "tuple", "list", or "range") can ' - 'be\n' - ' compared only within each of their types, with the ' - 'restriction that\n' - ' ranges do not support order comparison. Equality ' - 'comparison across\n' - ' these types results in inequality, and ordering comparison ' - 'across\n' - ' these types raises "TypeError".\n' + '* Sequences (instances of "tuple", "list", or "range") can be ' + 'compared\n' + ' only within each of their types, with the restriction that ' + 'ranges do\n' + ' not support order comparison. Equality comparison across ' + 'these\n' + ' types results in inequality, and ordering comparison across ' + 'these\n' + ' types raises "TypeError".\n' '\n' ' Sequences compare lexicographically using comparison of\n' ' corresponding elements. The built-in containers typically ' @@ -1933,8 +1938,8 @@ ' false because the type is not the same).\n' '\n' ' * Collections that support order comparison are ordered the ' - 'same\n' - ' as their first unequal elements (for example, "[1,2,x] <= ' + 'same as\n' + ' their first unequal elements (for example, "[1,2,x] <= ' '[1,2,y]"\n' ' has the same value as "x <= y"). If a corresponding ' 'element does\n' @@ -1952,8 +1957,8 @@ '"TypeError".\n' '\n' '* Sets (instances of "set" or "frozenset") can be compared ' - 'within\n' - ' and across their types.\n' + 'within and\n' + ' across their types.\n' '\n' ' They define order comparison operators to mean subset and ' 'superset\n' @@ -1972,8 +1977,8 @@ ' Comparison of sets enforces reflexivity of its elements.\n' '\n' '* Most other built-in types have no comparison methods ' - 'implemented,\n' - ' so they inherit the default comparison behavior.\n' + 'implemented, so\n' + ' they inherit the default comparison behavior.\n' '\n' 'User-defined classes that customize their comparison behavior ' 'should\n' @@ -2022,10 +2027,10 @@ ' "total_ordering()" decorator.\n' '\n' '* The "hash()" result should be consistent with equality. ' - 'Objects\n' - ' that are equal should either have the same hash value, or ' - 'be marked\n' - ' as unhashable.\n' + 'Objects that\n' + ' are equal should either have the same hash value, or be ' + 'marked as\n' + ' unhashable.\n' '\n' 'Python does not enforce these consistency rules. In fact, ' 'the\n' @@ -2299,10 +2304,11 @@ ':= a to b do"; e.g., "list(range(3))" returns the list "[0, 1, ' '2]".\n' '\n' - 'Note: There is a subtlety when the sequence is being modified by ' - 'the\n' - ' loop (this can only occur for mutable sequences, e.g. lists). ' - 'An\n' + 'Note:\n' + '\n' + ' There is a subtlety when the sequence is being modified by the ' + 'loop\n' + ' (this can only occur for mutable sequences, e.g. lists). An\n' ' internal counter is used to keep track of which item is used ' 'next,\n' ' and this is incremented on each iteration. When this counter ' @@ -2525,8 +2531,8 @@ 'follows:\n' '\n' '1. The context expression (the expression given in the ' - '"with_item")\n' - ' is evaluated to obtain a context manager.\n' + '"with_item") is\n' + ' evaluated to obtain a context manager.\n' '\n' '2. The context manager?s "__enter__()" is loaded for later use.\n' '\n' @@ -2534,13 +2540,15 @@ '\n' '4. The context manager?s "__enter__()" method is invoked.\n' '\n' - '5. If a target was included in the "with" statement, the return\n' - ' value from "__enter__()" is assigned to it.\n' + '5. If a target was included in the "with" statement, the return ' + 'value\n' + ' from "__enter__()" is assigned to it.\n' + '\n' + ' Note:\n' '\n' - ' Note: The "with" statement guarantees that if the ' - '"__enter__()"\n' - ' method returns without an error, then "__exit__()" will ' - 'always be\n' + ' The "with" statement guarantees that if the "__enter__()" ' + 'method\n' + ' returns without an error, then "__exit__()" will always be\n' ' called. Thus, if an error occurs during the assignment to ' 'the\n' ' target list, it will be treated the same as an error ' @@ -2710,17 +2718,17 @@ '?pre-\n' 'computed? value is used for each call. This is especially ' 'important\n' - 'to understand when a default parameter is a mutable object, such ' - 'as a\n' - 'list or a dictionary: if the function modifies the object (e.g. ' - 'by\n' - 'appending an item to a list), the default value is in effect ' - 'modified.\n' - 'This is generally not what was intended. A way around this is ' - 'to use\n' - '"None" as the default, and explicitly test for it in the body of ' - 'the\n' - 'function, e.g.:\n' + 'to understand when a default parameter value is a mutable ' + 'object, such\n' + 'as a list or a dictionary: if the function modifies the object ' + '(e.g.\n' + 'by appending an item to a list), the default parameter value is ' + 'in\n' + 'effect modified. This is generally not what was intended. A ' + 'way\n' + 'around this is to use "None" as the default, and explicitly test ' + 'for\n' + 'it in the body of the function, e.g.:\n' '\n' ' def whats_on_the_telly(penguin=None):\n' ' if penguin is None:\n' @@ -3054,14 +3062,17 @@ '\n' '-[ Footnotes ]-\n' '\n' - '[1] The exception is propagated to the invocation stack unless\n' - ' there is a "finally" clause which happens to raise another\n' - ' exception. That new exception causes the old one to be ' - 'lost.\n' + '[1] The exception is propagated to the invocation stack unless ' + 'there\n' + ' is a "finally" clause which happens to raise another ' + 'exception.\n' + ' That new exception causes the old one to be lost.\n' '\n' - '[2] A string literal appearing as the first statement in the\n' - ' function body is transformed into the function?s "__doc__"\n' - ' attribute and therefore the function?s *docstring*.\n' + '[2] A string literal appearing as the first statement in the ' + 'function\n' + ' body is transformed into the function?s "__doc__" attribute ' + 'and\n' + ' therefore the function?s *docstring*.\n' '\n' '[3] A string literal appearing as the first statement in the ' 'class\n' @@ -3160,8 +3171,8 @@ ' complex;\n' '\n' '* otherwise, if either argument is a floating point number, ' - 'the\n' - ' other is converted to floating point;\n' + 'the other\n' + ' is converted to floating point;\n' '\n' '* otherwise, both must be integers and no conversion is ' 'necessary.\n' @@ -3271,7 +3282,9 @@ 'for\n' ' objects that still exist when the interpreter exits.\n' '\n' - ' Note: "del x" doesn?t directly call "x.__del__()" ? the ' + ' Note:\n' + '\n' + ' "del x" doesn?t directly call "x.__del__()" ? the ' 'former\n' ' decrements the reference count for "x" by one, and the ' 'latter is\n' @@ -3295,13 +3308,15 @@ '\n' ' See also: Documentation for the "gc" module.\n' '\n' - ' Warning: Due to the precarious circumstances under ' - 'which\n' - ' "__del__()" methods are invoked, exceptions that occur ' - 'during\n' - ' their execution are ignored, and a warning is printed ' - 'to\n' - ' "sys.stderr" instead. In particular:\n' + ' Warning:\n' + '\n' + ' Due to the precarious circumstances under which ' + '"__del__()"\n' + ' methods are invoked, exceptions that occur during ' + 'their execution\n' + ' are ignored, and a warning is printed to "sys.stderr" ' + 'instead.\n' + ' In particular:\n' '\n' ' * "__del__()" can be invoked when arbitrary code is ' 'being\n' @@ -3314,22 +3329,20 @@ ' that gets interrupted to execute "__del__()".\n' '\n' ' * "__del__()" can be executed during interpreter ' - 'shutdown. As\n' - ' a consequence, the global variables it needs to ' - 'access\n' - ' (including other modules) may already have been ' - 'deleted or set\n' - ' to "None". Python guarantees that globals whose name ' - 'begins\n' - ' with a single underscore are deleted from their ' - 'module before\n' - ' other globals are deleted; if no other references to ' - 'such\n' - ' globals exist, this may help in assuring that ' - 'imported modules\n' - ' are still available at the time when the "__del__()" ' - 'method is\n' - ' called.\n' + 'shutdown. As a\n' + ' consequence, the global variables it needs to access ' + '(including\n' + ' other modules) may already have been deleted or set ' + 'to "None".\n' + ' Python guarantees that globals whose name begins ' + 'with a single\n' + ' underscore are deleted from their module before ' + 'other globals\n' + ' are deleted; if no other references to such globals ' + 'exist, this\n' + ' may help in assuring that imported modules are still ' + 'available\n' + ' at the time when the "__del__()" method is called.\n' '\n' 'object.__repr__(self)\n' '\n' @@ -3505,19 +3518,21 @@ ' def __hash__(self):\n' ' return hash((self.name, self.nick, self.color))\n' '\n' - ' Note: "hash()" truncates the value returned from an ' - 'object?s\n' - ' custom "__hash__()" method to the size of a ' - '"Py_ssize_t". This\n' - ' is typically 8 bytes on 64-bit builds and 4 bytes on ' - '32-bit\n' - ' builds. If an object?s "__hash__()" must ' - 'interoperate on builds\n' - ' of different bit sizes, be sure to check the width on ' - 'all\n' - ' supported builds. An easy way to do this is with ' - '"python -c\n' - ' "import sys; print(sys.hash_info.width)"".\n' + ' Note:\n' + '\n' + ' "hash()" truncates the value returned from an object?s ' + 'custom\n' + ' "__hash__()" method to the size of a "Py_ssize_t". ' + 'This is\n' + ' typically 8 bytes on 64-bit builds and 4 bytes on ' + '32-bit builds.\n' + ' If an object?s "__hash__()" must interoperate on ' + 'builds of\n' + ' different bit sizes, be sure to check the width on all ' + 'supported\n' + ' builds. An easy way to do this is with "python -c ' + '"import sys;\n' + ' print(sys.hash_info.width)"".\n' '\n' ' If a class does not define an "__eq__()" method it ' 'should not\n' @@ -3575,22 +3590,24 @@ ' hashable by an "isinstance(obj, ' 'collections.abc.Hashable)" call.\n' '\n' - ' Note: By default, the "__hash__()" values of str and ' - 'bytes\n' - ' objects are ?salted? with an unpredictable random ' - 'value.\n' - ' Although they remain constant within an individual ' - 'Python\n' - ' process, they are not predictable between repeated ' - 'invocations of\n' - ' Python.This is intended to provide protection against ' - 'a denial-\n' - ' of-service caused by carefully-chosen inputs that ' - 'exploit the\n' - ' worst case performance of a dict insertion, O(n^2) ' - 'complexity.\n' - ' See ' - 'http://www.ocert.org/advisories/ocert-2011-003.html for\n' + ' Note:\n' + '\n' + ' By default, the "__hash__()" values of str and bytes ' + 'objects are\n' + ' ?salted? with an unpredictable random value. Although ' + 'they\n' + ' remain constant within an individual Python process, ' + 'they are not\n' + ' predictable between repeated invocations of ' + 'Python.This is\n' + ' intended to provide protection against a ' + 'denial-of-service caused\n' + ' by carefully-chosen inputs that exploit the worst ' + 'case\n' + ' performance of a dict insertion, O(n^2) complexity. ' + 'See\n' + ' http://www.ocert.org/advisories/ocert-2011-003.html ' + 'for\n' ' details.Changing hash values affects the iteration ' 'order of sets.\n' ' Python has never made guarantees about this ordering ' @@ -4170,9 +4187,11 @@ 'its\n' ' value.\n' '\n' - ' Note: "print()" can also be used, but is not a debugger ' - 'command ?\n' - ' this executes the Python "print()" function.\n' + ' Note:\n' + '\n' + ' "print()" can also be used, but is not a debugger command ? ' + 'this\n' + ' executes the Python "print()" function.\n' '\n' 'pp expression\n' '\n' @@ -4298,13 +4317,14 @@ ' the current environment).\n' '\n' 'retval\n' - 'Print the return value for the last return of a function.\n' + '\n' + ' Print the return value for the last return of a function.\n' '\n' '-[ Footnotes ]-\n' '\n' '[1] Whether a frame is considered to originate in a certain ' - 'module\n' - ' is determined by the "__name__" in the frame globals.\n', + 'module is\n' + ' determined by the "__name__" in the frame globals.\n', 'del': 'The "del" statement\n' '*******************\n' '\n' @@ -4484,13 +4504,15 @@ 'about the\n' 'exceptional condition.\n' '\n' - 'Note: Exception messages are not part of the Python API. ' - 'Their\n' - ' contents may change from one version of Python to the next ' - 'without\n' - ' warning and should not be relied on by code which will run ' - 'under\n' - ' multiple versions of the interpreter.\n' + 'Note:\n' + '\n' + ' Exception messages are not part of the Python API. Their ' + 'contents\n' + ' may change from one version of Python to the next without ' + 'warning\n' + ' and should not be relied on by code which will run under ' + 'multiple\n' + ' versions of the interpreter.\n' '\n' 'See also the description of the "try" statement in section The ' 'try\n' @@ -4500,10 +4522,9 @@ '-[ Footnotes ]-\n' '\n' '[1] This limitation occurs because the code that is executed ' - 'by\n' - ' these operations is not available at the time the module ' - 'is\n' - ' compiled.\n', + 'by these\n' + ' operations is not available at the time the module is ' + 'compiled.\n', 'execmodel': 'Execution model\n' '***************\n' '\n' @@ -4809,13 +4830,15 @@ 'about the\n' 'exceptional condition.\n' '\n' - 'Note: Exception messages are not part of the Python API. ' - 'Their\n' - ' contents may change from one version of Python to the next ' - 'without\n' - ' warning and should not be relied on by code which will run ' - 'under\n' - ' multiple versions of the interpreter.\n' + 'Note:\n' + '\n' + ' Exception messages are not part of the Python API. Their ' + 'contents\n' + ' may change from one version of Python to the next without ' + 'warning\n' + ' and should not be relied on by code which will run under ' + 'multiple\n' + ' versions of the interpreter.\n' '\n' 'See also the description of the "try" statement in section The ' 'try\n' @@ -4824,11 +4847,10 @@ '\n' '-[ Footnotes ]-\n' '\n' - '[1] This limitation occurs because the code that is executed ' - 'by\n' - ' these operations is not available at the time the module ' - 'is\n' - ' compiled.\n', + '[1] This limitation occurs because the code that is executed by ' + 'these\n' + ' operations is not available at the time the module is ' + 'compiled.\n', 'exprlists': 'Expression lists\n' '****************\n' '\n' @@ -4947,8 +4969,11 @@ 'i\n' ':= a to b do"; e.g., "list(range(3))" returns the list "[0, 1, 2]".\n' '\n' - 'Note: There is a subtlety when the sequence is being modified by the\n' - ' loop (this can only occur for mutable sequences, e.g. lists). An\n' + 'Note:\n' + '\n' + ' There is a subtlety when the sequence is being modified by the ' + 'loop\n' + ' (this can only occur for mutable sequences, e.g. lists). An\n' ' internal counter is used to keep track of which item is used next,\n' ' and this is incremented on each iteration. When this counter has\n' ' reached the length of the sequence the loop terminates. This ' @@ -5786,17 +5811,17 @@ '?pre-\n' 'computed? value is used for each call. This is especially ' 'important\n' - 'to understand when a default parameter is a mutable object, such ' - 'as a\n' - 'list or a dictionary: if the function modifies the object (e.g. ' - 'by\n' - 'appending an item to a list), the default value is in effect ' - 'modified.\n' - 'This is generally not what was intended. A way around this is ' - 'to use\n' - '"None" as the default, and explicitly test for it in the body of ' - 'the\n' - 'function, e.g.:\n' + 'to understand when a default parameter value is a mutable ' + 'object, such\n' + 'as a list or a dictionary: if the function modifies the object ' + '(e.g.\n' + 'by appending an item to a list), the default parameter value is ' + 'in\n' + 'effect modified. This is generally not what was intended. A ' + 'way\n' + 'around this is to use "None" as the default, and explicitly test ' + 'for\n' + 'it in the body of the function, e.g.:\n' '\n' ' def whats_on_the_telly(penguin=None):\n' ' if penguin is None:\n' @@ -5956,7 +5981,9 @@ 'defined.\n' ' See section The import statement.\n' '\n' - ' Note: The name "_" is often used in conjunction with\n' + ' Note:\n' + '\n' + ' The name "_" is often used in conjunction with\n' ' internationalization; refer to the documentation for the\n' ' "gettext" module for more information on this ' 'convention.\n' @@ -6060,8 +6087,8 @@ '\n' 'A non-normative HTML file listing all valid identifier ' 'characters for\n' - 'Unicode 4.1 can be found at https://www.dcl.hpi.uni-\n' - 'potsdam.de/home/loewis/table-3131.html.\n' + 'Unicode 4.1 can be found at\n' + 'https://www.unicode.org/Public/13.0.0/ucd/DerivedCoreProperties.txt\n' '\n' '\n' 'Keywords\n' @@ -6102,7 +6129,9 @@ 'defined.\n' ' See section The import statement.\n' '\n' - ' Note: The name "_" is often used in conjunction with\n' + ' Note:\n' + '\n' + ' The name "_" is often used in conjunction with\n' ' internationalization; refer to the documentation for ' 'the\n' ' "gettext" module for more information on this ' @@ -6187,8 +6216,9 @@ '\n' '1. find a module, loading and initializing it if necessary\n' '\n' - '2. define a name or names in the local namespace for the scope\n' - ' where the "import" statement occurs.\n' + '2. define a name or names in the local namespace for the scope ' + 'where\n' + ' the "import" statement occurs.\n' '\n' 'When the statement contains multiple clauses (separated by commas) ' 'the\n' @@ -6214,8 +6244,9 @@ 'made\n' 'available in the local namespace in one of three ways:\n' '\n' - '* If the module name is followed by "as", then the name following\n' - ' "as" is bound directly to the imported module.\n' + '* If the module name is followed by "as", then the name following ' + '"as"\n' + ' is bound directly to the imported module.\n' '\n' '* If no other name is specified, and the module being imported is ' 'a\n' @@ -6893,15 +6924,18 @@ '"__rpow__()" (the\n' ' coercion rules would become too complicated).\n' '\n' - ' Note: If the right operand?s type is a subclass of the ' - 'left\n' - ' operand?s type and that subclass provides the ' - 'reflected method\n' - ' for the operation, this method will be called before ' - 'the left\n' - ' operand?s non-reflected method. This behavior allows ' - 'subclasses\n' - ' to override their ancestors? operations.\n' + ' Note:\n' + '\n' + ' If the right operand?s type is a subclass of the left ' + 'operand?s\n' + ' type and that subclass provides a different ' + 'implementation of the\n' + ' reflected method for the operation, this method will ' + 'be called\n' + ' before the left operand?s non-reflected method. This ' + 'behavior\n' + ' allows subclasses to override their ancestors? ' + 'operations.\n' '\n' 'object.__iadd__(self, other)\n' 'object.__isub__(self, other)\n' @@ -7221,8 +7255,8 @@ '-[ Footnotes ]-\n' '\n' '[1] While "abs(x%y) < abs(y)" is true mathematically, ' - 'for floats\n' - ' it may not be true numerically due to roundoff. For ' + 'for floats it\n' + ' may not be true numerically due to roundoff. For ' 'example, and\n' ' assuming a platform on which a Python float is an ' 'IEEE 754 double-\n' @@ -7287,22 +7321,22 @@ '"unicodedata.normalize()".\n' '\n' '[4] Due to automatic garbage-collection, free lists, and ' - 'the\n' - ' dynamic nature of descriptors, you may notice ' - 'seemingly unusual\n' - ' behaviour in certain uses of the "is" operator, like ' - 'those\n' - ' involving comparisons between instance methods, or ' - 'constants.\n' - ' Check their documentation for more info.\n' + 'the dynamic\n' + ' nature of descriptors, you may notice seemingly ' + 'unusual behaviour\n' + ' in certain uses of the "is" operator, like those ' + 'involving\n' + ' comparisons between instance methods, or constants. ' + 'Check their\n' + ' documentation for more info.\n' '\n' '[5] The "%" operator is also used for string formatting; ' 'the same\n' ' precedence applies.\n' '\n' '[6] The power operator "**" binds less tightly than an ' - 'arithmetic\n' - ' or bitwise unary operator on its right, that is, ' + 'arithmetic or\n' + ' bitwise unary operator on its right, that is, ' '"2**-1" is "0.5".\n', 'pass': 'The "pass" statement\n' '********************\n' @@ -7592,9 +7626,11 @@ '\n' ' New in version 3.4.\n' '\n' - 'Note: Slicing is done exclusively with the following three ' - 'methods.\n' - ' A call like\n' + 'Note:\n' + '\n' + ' Slicing is done exclusively with the following three ' + 'methods. A\n' + ' call like\n' '\n' ' a[1:2] = b\n' '\n' @@ -7625,7 +7661,9 @@ 'the\n' ' container), "KeyError" should be raised.\n' '\n' - ' Note: "for" loops expect that an "IndexError" will be ' + ' Note:\n' + '\n' + ' "for" loops expect that an "IndexError" will be ' 'raised for\n' ' illegal indexes to allow proper detection of the end ' 'of the\n' @@ -7861,26 +7899,26 @@ '-[ Footnotes ]-\n' '\n' '[1] Additional information on these special methods may be ' - 'found\n' - ' in the Python Reference Manual (Basic customization).\n' + 'found in\n' + ' the Python Reference Manual (Basic customization).\n' '\n' '[2] As a consequence, the list "[1, 2]" is considered equal ' - 'to\n' - ' "[1.0, 2.0]", and similarly for tuples.\n' + 'to "[1.0,\n' + ' 2.0]", and similarly for tuples.\n' '\n' '[3] They must have since the parser can?t tell the type of ' 'the\n' ' operands.\n' '\n' '[4] Cased characters are those with general category ' - 'property\n' - ' being one of ?Lu? (Letter, uppercase), ?Ll? (Letter, ' - 'lowercase),\n' - ' or ?Lt? (Letter, titlecase).\n' - '\n' - '[5] To format only a tuple you should therefore provide a\n' - ' singleton tuple whose only element is the tuple to be ' - 'formatted.\n', + 'property being\n' + ' one of ?Lu? (Letter, uppercase), ?Ll? (Letter, ' + 'lowercase), or ?Lt?\n' + ' (Letter, titlecase).\n' + '\n' + '[5] To format only a tuple you should therefore provide a ' + 'singleton\n' + ' tuple whose only element is the tuple to be formatted.\n', 'specialnames': 'Special method names\n' '********************\n' '\n' @@ -8025,7 +8063,9 @@ 'for\n' ' objects that still exist when the interpreter exits.\n' '\n' - ' Note: "del x" doesn?t directly call "x.__del__()" ? the ' + ' Note:\n' + '\n' + ' "del x" doesn?t directly call "x.__del__()" ? the ' 'former\n' ' decrements the reference count for "x" by one, and the ' 'latter is\n' @@ -8049,12 +8089,15 @@ '\n' ' See also: Documentation for the "gc" module.\n' '\n' - ' Warning: Due to the precarious circumstances under which\n' - ' "__del__()" methods are invoked, exceptions that occur ' - 'during\n' - ' their execution are ignored, and a warning is printed ' - 'to\n' - ' "sys.stderr" instead. In particular:\n' + ' Warning:\n' + '\n' + ' Due to the precarious circumstances under which ' + '"__del__()"\n' + ' methods are invoked, exceptions that occur during their ' + 'execution\n' + ' are ignored, and a warning is printed to "sys.stderr" ' + 'instead.\n' + ' In particular:\n' '\n' ' * "__del__()" can be invoked when arbitrary code is ' 'being\n' @@ -8067,22 +8110,20 @@ ' that gets interrupted to execute "__del__()".\n' '\n' ' * "__del__()" can be executed during interpreter ' - 'shutdown. As\n' - ' a consequence, the global variables it needs to ' - 'access\n' - ' (including other modules) may already have been ' - 'deleted or set\n' - ' to "None". Python guarantees that globals whose name ' - 'begins\n' - ' with a single underscore are deleted from their ' - 'module before\n' - ' other globals are deleted; if no other references to ' - 'such\n' - ' globals exist, this may help in assuring that ' - 'imported modules\n' - ' are still available at the time when the "__del__()" ' - 'method is\n' - ' called.\n' + 'shutdown. As a\n' + ' consequence, the global variables it needs to access ' + '(including\n' + ' other modules) may already have been deleted or set ' + 'to "None".\n' + ' Python guarantees that globals whose name begins with ' + 'a single\n' + ' underscore are deleted from their module before other ' + 'globals\n' + ' are deleted; if no other references to such globals ' + 'exist, this\n' + ' may help in assuring that imported modules are still ' + 'available\n' + ' at the time when the "__del__()" method is called.\n' '\n' 'object.__repr__(self)\n' '\n' @@ -8258,19 +8299,21 @@ ' def __hash__(self):\n' ' return hash((self.name, self.nick, self.color))\n' '\n' - ' Note: "hash()" truncates the value returned from an ' - 'object?s\n' - ' custom "__hash__()" method to the size of a ' - '"Py_ssize_t". This\n' - ' is typically 8 bytes on 64-bit builds and 4 bytes on ' - '32-bit\n' - ' builds. If an object?s "__hash__()" must interoperate ' - 'on builds\n' - ' of different bit sizes, be sure to check the width on ' - 'all\n' - ' supported builds. An easy way to do this is with ' - '"python -c\n' - ' "import sys; print(sys.hash_info.width)"".\n' + ' Note:\n' + '\n' + ' "hash()" truncates the value returned from an object?s ' + 'custom\n' + ' "__hash__()" method to the size of a "Py_ssize_t". ' + 'This is\n' + ' typically 8 bytes on 64-bit builds and 4 bytes on ' + '32-bit builds.\n' + ' If an object?s "__hash__()" must interoperate on ' + 'builds of\n' + ' different bit sizes, be sure to check the width on all ' + 'supported\n' + ' builds. An easy way to do this is with "python -c ' + '"import sys;\n' + ' print(sys.hash_info.width)"".\n' '\n' ' If a class does not define an "__eq__()" method it should ' 'not\n' @@ -8326,21 +8369,22 @@ ' hashable by an "isinstance(obj, ' 'collections.abc.Hashable)" call.\n' '\n' - ' Note: By default, the "__hash__()" values of str and ' - 'bytes\n' - ' objects are ?salted? with an unpredictable random ' - 'value.\n' - ' Although they remain constant within an individual ' - 'Python\n' - ' process, they are not predictable between repeated ' - 'invocations of\n' - ' Python.This is intended to provide protection against a ' - 'denial-\n' - ' of-service caused by carefully-chosen inputs that ' - 'exploit the\n' - ' worst case performance of a dict insertion, O(n^2) ' - 'complexity.\n' - ' See http://www.ocert.org/advisories/ocert-2011-003.html ' + ' Note:\n' + '\n' + ' By default, the "__hash__()" values of str and bytes ' + 'objects are\n' + ' ?salted? with an unpredictable random value. Although ' + 'they\n' + ' remain constant within an individual Python process, ' + 'they are not\n' + ' predictable between repeated invocations of Python.This ' + 'is\n' + ' intended to provide protection against a ' + 'denial-of-service caused\n' + ' by carefully-chosen inputs that exploit the worst case\n' + ' performance of a dict insertion, O(n^2) complexity. ' + 'See\n' + ' http://www.ocert.org/advisories/ocert-2011-003.html ' 'for\n' ' details.Changing hash values affects the iteration ' 'order of sets.\n' @@ -8429,11 +8473,13 @@ 'needs, for\n' ' example, "object.__getattribute__(self, name)".\n' '\n' - ' Note: This method may still be bypassed when looking up ' - 'special\n' - ' methods as the result of implicit invocation via ' - 'language syntax\n' - ' or built-in functions. See Special method lookup.\n' + ' Note:\n' + '\n' + ' This method may still be bypassed when looking up ' + 'special methods\n' + ' as the result of implicit invocation via language ' + 'syntax or\n' + ' built-in functions. See Special method lookup.\n' '\n' 'object.__setattr__(self, name, value)\n' '\n' @@ -8517,15 +8563,16 @@ '\n' ' sys.modules[__name__].__class__ = VerboseModule\n' '\n' - 'Note: Defining module "__getattr__" and setting module ' - '"__class__"\n' - ' only affect lookups made using the attribute access syntax ' - '?\n' - ' directly accessing the module globals (whether by code ' - 'within the\n' - ' module, or via a reference to the module?s globals ' - 'dictionary) is\n' - ' unaffected.\n' + 'Note:\n' + '\n' + ' Defining module "__getattr__" and setting module ' + '"__class__" only\n' + ' affect lookups made using the attribute access syntax ? ' + 'directly\n' + ' accessing the module globals (whether by code within the ' + 'module, or\n' + ' via a reference to the module?s globals dictionary) is ' + 'unaffected.\n' '\n' 'Changed in version 3.5: "__class__" module attribute is now ' 'writable.\n' @@ -8608,12 +8655,14 @@ 'The\n' ' descriptor has been assigned to *name*.\n' '\n' - ' Note: "__set_name__()" is only called implicitly as part ' - 'of the\n' - ' "type" constructor, so it will need to be called ' - 'explicitly with\n' - ' the appropriate parameters when a descriptor is added ' - 'to a class\n' + ' Note:\n' + '\n' + ' "__set_name__()" is only called implicitly as part of ' + 'the "type"\n' + ' constructor, so it will need to be called explicitly ' + 'with the\n' + ' appropriate parameters when a descriptor is added to a ' + 'class\n' ' after initial creation:\n' '\n' ' class A:\n' @@ -8772,10 +8821,9 @@ '~~~~~~~~~~~~~~~~~~~~~~~~~~\n' '\n' '* When inheriting from a class without *__slots__*, the ' - '*__dict__*\n' - ' and *__weakref__* attribute of the instances will always ' - 'be\n' - ' accessible.\n' + '*__dict__* and\n' + ' *__weakref__* attribute of the instances will always be ' + 'accessible.\n' '\n' '* Without a *__dict__* variable, instances cannot be ' 'assigned new\n' @@ -8789,13 +8837,12 @@ ' declaration.\n' '\n' '* Without a *__weakref__* variable for each instance, ' - 'classes\n' - ' defining *__slots__* do not support weak references to ' - 'its\n' - ' instances. If weak reference support is needed, then add\n' - ' "\'__weakref__\'" to the sequence of strings in the ' - '*__slots__*\n' - ' declaration.\n' + 'classes defining\n' + ' *__slots__* do not support weak references to its ' + 'instances. If weak\n' + ' reference support is needed, then add "\'__weakref__\'" to ' + 'the\n' + ' sequence of strings in the *__slots__* declaration.\n' '\n' '* *__slots__* are implemented at the class level by ' 'creating\n' @@ -8808,23 +8855,22 @@ ' attribute would overwrite the descriptor assignment.\n' '\n' '* The action of a *__slots__* declaration is not limited to ' - 'the\n' - ' class where it is defined. *__slots__* declared in ' - 'parents are\n' - ' available in child classes. However, child subclasses will ' - 'get a\n' - ' *__dict__* and *__weakref__* unless they also define ' - '*__slots__*\n' - ' (which should only contain names of any *additional* ' - 'slots).\n' + 'the class\n' + ' where it is defined. *__slots__* declared in parents are ' + 'available\n' + ' in child classes. However, child subclasses will get a ' + '*__dict__*\n' + ' and *__weakref__* unless they also define *__slots__* ' + '(which should\n' + ' only contain names of any *additional* slots).\n' '\n' '* If a class defines a slot also defined in a base class, ' - 'the\n' - ' instance variable defined by the base class slot is ' - 'inaccessible\n' - ' (except by retrieving its descriptor directly from the ' - 'base class).\n' - ' This renders the meaning of the program undefined. In the ' + 'the instance\n' + ' variable defined by the base class slot is inaccessible ' + '(except by\n' + ' retrieving its descriptor directly from the base class). ' + 'This\n' + ' renders the meaning of the program undefined. In the ' 'future, a\n' ' check may be added to prevent this.\n' '\n' @@ -8834,9 +8880,9 @@ 'and "tuple".\n' '\n' '* Any non-string iterable may be assigned to *__slots__*. ' - 'Mappings\n' - ' may also be used; however, in the future, special meaning ' - 'may be\n' + 'Mappings may\n' + ' also be used; however, in the future, special meaning may ' + 'be\n' ' assigned to the values corresponding to each key.\n' '\n' '* *__class__* assignment works only if both classes have the ' @@ -8852,8 +8898,8 @@ ' raise "TypeError".\n' '\n' '* If an iterator is used for *__slots__* then a descriptor ' - 'is\n' - ' created for each of the iterator?s values. However, the ' + 'is created\n' + ' for each of the iterator?s values. However, the ' '*__slots__*\n' ' attribute will be an empty iterator.\n' '\n' @@ -8906,9 +8952,11 @@ 'does nothing,\n' ' but raises an error if it is called with any arguments.\n' '\n' - ' Note: The metaclass hint "metaclass" is consumed by the ' - 'rest of\n' - ' the type machinery, and is never passed to ' + ' Note:\n' + '\n' + ' The metaclass hint "metaclass" is consumed by the rest ' + 'of the\n' + ' type machinery, and is never passed to ' '"__init_subclass__"\n' ' implementations. The actual metaclass (rather than the ' 'explicit\n' @@ -8976,9 +9024,10 @@ 'tuple may\n' 'be empty, in such case the original base is ignored.\n' '\n' - 'See also: **PEP 560** - Core support for typing module and ' - 'generic\n' - ' types\n' + 'See also:\n' + '\n' + ' **PEP 560** - Core support for typing module and generic ' + 'types\n' '\n' '\n' 'Determining the appropriate metaclass\n' @@ -9236,9 +9285,10 @@ 'type hints,\n' 'other usage is discouraged.\n' '\n' - 'See also: **PEP 560** - Core support for typing module and ' - 'generic\n' - ' types\n' + 'See also:\n' + '\n' + ' **PEP 560** - Core support for typing module and generic ' + 'types\n' '\n' '\n' 'Emulating callable objects\n' @@ -9350,9 +9400,11 @@ '\n' ' New in version 3.4.\n' '\n' - 'Note: Slicing is done exclusively with the following three ' - 'methods.\n' - ' A call like\n' + 'Note:\n' + '\n' + ' Slicing is done exclusively with the following three ' + 'methods. A\n' + ' call like\n' '\n' ' a[1:2] = b\n' '\n' @@ -9383,8 +9435,10 @@ 'the\n' ' container), "KeyError" should be raised.\n' '\n' - ' Note: "for" loops expect that an "IndexError" will be ' - 'raised for\n' + ' Note:\n' + '\n' + ' "for" loops expect that an "IndexError" will be raised ' + 'for\n' ' illegal indexes to allow proper detection of the end of ' 'the\n' ' sequence.\n' @@ -9574,15 +9628,18 @@ '"__rpow__()" (the\n' ' coercion rules would become too complicated).\n' '\n' - ' Note: If the right operand?s type is a subclass of the ' - 'left\n' - ' operand?s type and that subclass provides the reflected ' - 'method\n' - ' for the operation, this method will be called before ' - 'the left\n' - ' operand?s non-reflected method. This behavior allows ' - 'subclasses\n' - ' to override their ancestors? operations.\n' + ' Note:\n' + '\n' + ' If the right operand?s type is a subclass of the left ' + 'operand?s\n' + ' type and that subclass provides a different ' + 'implementation of the\n' + ' reflected method for the operation, this method will be ' + 'called\n' + ' before the left operand?s non-reflected method. This ' + 'behavior\n' + ' allows subclasses to override their ancestors? ' + 'operations.\n' '\n' 'object.__iadd__(self, other)\n' 'object.__isub__(self, other)\n' @@ -9914,36 +9971,7 @@ '*start* and\n' ' *end* are interpreted as in slice notation.\n' '\n' - 'str.removeprefix(prefix, /)\n' - '\n' - ' If the string starts with the *prefix* string, return\n' - ' "string[len(prefix):]". Otherwise, return a copy of the ' - 'original\n' - ' string:\n' - '\n' - " >>> 'TestHook'.removeprefix('Test')\n" - " 'Hook'\n" - " >>> 'BaseTestCase'.removeprefix('Test')\n" - " 'BaseTestCase'\n" - '\n' - ' New in version 3.9.\n' - '\n' - 'str.removesuffix(suffix, /)\n' - '\n' - ' If the string ends with the *suffix* string and that ' - '*suffix* is\n' - ' not empty, return "string[:-len(suffix)]". Otherwise, ' - 'return a copy\n' - ' of the original string:\n' - '\n' - " >>> 'MiscTests'.removesuffix('Tests')\n" - " 'Misc'\n" - " >>> 'TmpDirMixin'.removesuffix('Tests')\n" - " 'TmpDirMixin'\n" - '\n' - ' New in version 3.9.\n' - '\n' - 'str.encode(encoding="utf-8", errors="strict")\n' + "str.encode(encoding='utf-8', errors='strict')\n" '\n' ' Return an encoded version of the string as a bytes ' 'object. Default\n' @@ -10029,11 +10057,13 @@ '"-1" if\n' ' *sub* is not found.\n' '\n' - ' Note: The "find()" method should be used only if you ' - 'need to know\n' - ' the position of *sub*. To check if *sub* is a ' - 'substring or not,\n' - ' use the "in" operator:\n' + ' Note:\n' + '\n' + ' The "find()" method should be used only if you need ' + 'to know the\n' + ' position of *sub*. To check if *sub* is a substring ' + 'or not, use\n' + ' the "in" operator:\n' '\n' " >>> 'Py' in 'Python'\n" ' True\n' @@ -10062,8 +10092,9 @@ ' formatting options that can be specified in format ' 'strings.\n' '\n' - ' Note: When formatting a number ("int", "float", ' - '"complex",\n' + ' Note:\n' + '\n' + ' When formatting a number ("int", "float", "complex",\n' ' "decimal.Decimal" and subclasses) with the "n" type ' '(ex:\n' ' "\'{:n}\'.format(1234)"), the function temporarily ' @@ -10371,6 +10402,35 @@ 'followed by\n' ' two empty strings.\n' '\n' + 'str.removeprefix(prefix, /)\n' + '\n' + ' If the string starts with the *prefix* string, return\n' + ' "string[len(prefix):]". Otherwise, return a copy of the ' + 'original\n' + ' string:\n' + '\n' + " >>> 'TestHook'.removeprefix('Test')\n" + " 'Hook'\n" + " >>> 'BaseTestCase'.removeprefix('Test')\n" + " 'BaseTestCase'\n" + '\n' + ' New in version 3.9.\n' + '\n' + 'str.removesuffix(suffix, /)\n' + '\n' + ' If the string ends with the *suffix* string and that ' + '*suffix* is\n' + ' not empty, return "string[:-len(suffix)]". Otherwise, ' + 'return a copy\n' + ' of the original string:\n' + '\n' + " >>> 'MiscTests'.removesuffix('Tests')\n" + " 'Misc'\n" + " >>> 'TmpDirMixin'.removesuffix('Tests')\n" + " 'TmpDirMixin'\n" + '\n' + ' New in version 3.9.\n' + '\n' 'str.replace(old, new[, count])\n' '\n' ' Return a copy of the string with all occurrences of ' @@ -10417,7 +10477,7 @@ 'followed by\n' ' the string itself.\n' '\n' - 'str.rsplit(sep=None, maxsplit=-1)\n' + 'str.rsplit(sep=None, maxsplit=- 1)\n' '\n' ' Return a list of the words in the string, using *sep* ' 'as the\n' @@ -10458,7 +10518,7 @@ " >>> 'Monty Python'.removesuffix(' Python')\n" " 'Monty'\n" '\n' - 'str.split(sep=None, maxsplit=-1)\n' + 'str.split(sep=None, maxsplit=- 1)\n' '\n' ' Return a list of the words in the string, using *sep* ' 'as the\n' @@ -10939,17 +10999,20 @@ '\n' '2. Unlike in Standard C, exactly two hex digits are required.\n' '\n' - '3. In a bytes literal, hexadecimal and octal escapes denote the\n' - ' byte with the given value. In a string literal, these escapes\n' - ' denote a Unicode character with the given value.\n' + '3. In a bytes literal, hexadecimal and octal escapes denote the ' + 'byte\n' + ' with the given value. In a string literal, these escapes ' + 'denote a\n' + ' Unicode character with the given value.\n' '\n' '4. Changed in version 3.3: Support for name aliases [1] has been\n' ' added.\n' '\n' '5. Exactly four hex digits are required.\n' '\n' - '6. Any Unicode character can be encoded this way. Exactly eight\n' - ' hex digits are required.\n' + '6. Any Unicode character can be encoded this way. Exactly eight ' + 'hex\n' + ' digits are required.\n' '\n' 'Unlike Standard C, all unrecognized escape sequences are left in ' 'the\n' @@ -11399,7 +11462,7 @@ ' points. All the code points in the range "U+0000 - ' 'U+10FFFF"\n' ' can be represented in a string. Python doesn?t have a ' - '"char"\n' + '*char*\n' ' type; instead, every code point in the string is ' 'represented\n' ' as a string object with length "1". The built-in ' @@ -12647,9 +12710,11 @@ '\n' ' Changed in version 3.8: Dictionaries are now reversible.\n' '\n' - 'See also: "types.MappingProxyType" can be used to create a ' - 'read-only\n' - ' view of a "dict".\n' + 'See also:\n' + '\n' + ' "types.MappingProxyType" can be used to create a read-only ' + 'view of a\n' + ' "dict".\n' '\n' '\n' 'Dictionary view objects\n' @@ -12712,6 +12777,14 @@ ' Changed in version 3.8: Dictionary views are now ' 'reversible.\n' '\n' + 'dictview.mapping\n' + '\n' + ' Return a "types.MappingProxyType" that wraps the ' + 'original\n' + ' dictionary to which the view refers.\n' + '\n' + ' New in version 3.10.\n' + '\n' 'Keys views are set-like since their entries are unique and ' 'hashable.\n' 'If all values are hashable, so that "(key, value)" pairs are ' @@ -12757,7 +12830,15 @@ " >>> keys & {'eggs', 'bacon', 'salad'}\n" " {'bacon'}\n" " >>> keys ^ {'sausage', 'juice'}\n" - " {'juice', 'sausage', 'bacon', 'spam'}\n", + " {'juice', 'sausage', 'bacon', 'spam'}\n" + '\n' + ' >>> # get back a read-only proxy for the original ' + 'dictionary\n' + ' >>> values.mapping\n' + " mappingproxy({'eggs': 2, 'sausage': 1, 'bacon': 1, " + "'spam': 500})\n" + " >>> values.mapping['spam']\n" + ' 500\n', 'typesmethods': 'Methods\n' '*******\n' '\n' @@ -13033,13 +13114,14 @@ '"None", it\n' ' is treated like "1".\n' '\n' - '6. Concatenating immutable sequences always results in a new\n' - ' object. This means that building up a sequence by repeated\n' - ' concatenation will have a quadratic runtime cost in the ' - 'total\n' - ' sequence length. To get a linear runtime cost, you must ' - 'switch to\n' - ' one of the alternatives below:\n' + '6. Concatenating immutable sequences always results in a new ' + 'object.\n' + ' This means that building up a sequence by repeated ' + 'concatenation\n' + ' will have a quadratic runtime cost in the total sequence ' + 'length.\n' + ' To get a linear runtime cost, you must switch to one of the\n' + ' alternatives below:\n' '\n' ' * if concatenating "str" objects, you can build a list and ' 'use\n' @@ -13057,24 +13139,25 @@ ' * for other types, investigate the relevant class ' 'documentation\n' '\n' - '7. Some sequence types (such as "range") only support item\n' - ' sequences that follow specific patterns, and hence don?t ' - 'support\n' - ' sequence concatenation or repetition.\n' - '\n' - '8. "index" raises "ValueError" when *x* is not found in *s*. ' - 'Not\n' - ' all implementations support passing the additional arguments ' - '*i*\n' - ' and *j*. These arguments allow efficient searching of ' - 'subsections\n' - ' of the sequence. Passing the extra arguments is roughly ' - 'equivalent\n' - ' to using "s[i:j].index(x)", only without copying any data and ' - 'with\n' - ' the returned index being relative to the start of the ' + '7. Some sequence types (such as "range") only support item ' + 'sequences\n' + ' that follow specific patterns, and hence don?t support ' 'sequence\n' - ' rather than the start of the slice.\n' + ' concatenation or repetition.\n' + '\n' + '8. "index" raises "ValueError" when *x* is not found in *s*. Not ' + 'all\n' + ' implementations support passing the additional arguments *i* ' + 'and\n' + ' *j*. These arguments allow efficient searching of subsections ' + 'of\n' + ' the sequence. Passing the extra arguments is roughly ' + 'equivalent to\n' + ' using "s[i:j].index(x)", only without copying any data and ' + 'with the\n' + ' returned index being relative to the start of the sequence ' + 'rather\n' + ' than the start of the slice.\n' '\n' '\n' 'Immutable Sequence Types\n' @@ -13202,17 +13285,17 @@ '1. *t* must have the same length as the slice it is replacing.\n' '\n' '2. The optional argument *i* defaults to "-1", so that by ' - 'default\n' - ' the last item is removed and returned.\n' + 'default the\n' + ' last item is removed and returned.\n' '\n' '3. "remove()" raises "ValueError" when *x* is not found in *s*.\n' '\n' - '4. The "reverse()" method modifies the sequence in place for\n' - ' economy of space when reversing a large sequence. To remind ' - 'users\n' - ' that it operates by side effect, it does not return the ' - 'reversed\n' - ' sequence.\n' + '4. The "reverse()" method modifies the sequence in place for ' + 'economy\n' + ' of space when reversing a large sequence. To remind users ' + 'that it\n' + ' operates by side effect, it does not return the reversed ' + 'sequence.\n' '\n' '5. "clear()" and "copy()" are included for consistency with the\n' ' interfaces of mutable containers that don?t support slicing\n' @@ -13249,9 +13332,9 @@ ' * Using a pair of square brackets to denote the empty list: ' '"[]"\n' '\n' - ' * Using square brackets, separating items with commas: ' - '"[a]",\n' - ' "[a, b, c]"\n' + ' * Using square brackets, separating items with commas: "[a]", ' + '"[a,\n' + ' b, c]"\n' '\n' ' * Using a list comprehension: "[x for x in iterable]"\n' '\n' @@ -13554,9 +13637,9 @@ '\n' 'See also:\n' '\n' - ' * The linspace recipe shows how to implement a lazy version ' - 'of\n' - ' range suitable for floating point applications.\n', + ' * The linspace recipe shows how to implement a lazy version of ' + 'range\n' + ' suitable for floating point applications.\n', 'typesseq-mutable': 'Mutable Sequence Types\n' '**********************\n' '\n' @@ -13667,19 +13750,18 @@ 'replacing.\n' '\n' '2. The optional argument *i* defaults to "-1", so that ' - 'by default\n' - ' the last item is removed and returned.\n' + 'by default the\n' + ' last item is removed and returned.\n' '\n' '3. "remove()" raises "ValueError" when *x* is not found ' 'in *s*.\n' '\n' '4. The "reverse()" method modifies the sequence in place ' - 'for\n' - ' economy of space when reversing a large sequence. To ' - 'remind users\n' - ' that it operates by side effect, it does not return ' - 'the reversed\n' - ' sequence.\n' + 'for economy\n' + ' of space when reversing a large sequence. To remind ' + 'users that it\n' + ' operates by side effect, it does not return the ' + 'reversed sequence.\n' '\n' '5. "clear()" and "copy()" are included for consistency ' 'with the\n' @@ -13762,8 +13844,9 @@ 'The execution of the "with" statement with one ?item? proceeds as\n' 'follows:\n' '\n' - '1. The context expression (the expression given in the "with_item")\n' - ' is evaluated to obtain a context manager.\n' + '1. The context expression (the expression given in the "with_item") ' + 'is\n' + ' evaluated to obtain a context manager.\n' '\n' '2. The context manager?s "__enter__()" is loaded for later use.\n' '\n' @@ -13771,12 +13854,15 @@ '\n' '4. The context manager?s "__enter__()" method is invoked.\n' '\n' - '5. If a target was included in the "with" statement, the return\n' - ' value from "__enter__()" is assigned to it.\n' + '5. If a target was included in the "with" statement, the return ' + 'value\n' + ' from "__enter__()" is assigned to it.\n' + '\n' + ' Note:\n' '\n' - ' Note: The "with" statement guarantees that if the "__enter__()"\n' - ' method returns without an error, then "__exit__()" will always ' - 'be\n' + ' The "with" statement guarantees that if the "__enter__()" ' + 'method\n' + ' returns without an error, then "__exit__()" will always be\n' ' called. Thus, if an error occurs during the assignment to the\n' ' target list, it will be treated the same as an error occurring\n' ' within the suite would be. See step 6 below.\n' diff --git a/Misc/NEWS.d/3.10.0a1.rst b/Misc/NEWS.d/3.10.0a1.rst new file mode 100644 index 0000000000000..725dfd16b180a --- /dev/null +++ b/Misc/NEWS.d/3.10.0a1.rst @@ -0,0 +1,3519 @@ +.. bpo: 41304 +.. date: 2020-07-15-20-15-08 +.. nonce: vNEeYA +.. release date: 2020-10-05 +.. section: Security + +Fixes `python3x._pth` being ignored on Windows, caused by the fix for +:issue:`29778` (CVE-2020-15801). + +.. + +.. bpo: 41162 +.. date: 2020-07-03-20-41-29 +.. nonce: tb8pVj +.. section: Security + +Audit hooks are now cleared later during finalization to avoid missing +events. + +.. + +.. bpo: 29778 +.. date: 2020-07-03-17-21-37 +.. nonce: cR_fGS +.. section: Security + +Ensure :file:`python3.dll` is loaded from correct locations when Python is +embedded (CVE-2020-15523). + +.. + +.. bpo: 41004 +.. date: 2020-06-29-16-02-29 +.. nonce: ovF0KZ +.. section: Security + +The __hash__() methods of ipaddress.IPv4Interface and +ipaddress.IPv6Interface incorrectly generated constant hash values of 32 and +128 respectively. This resulted in always causing hash collisions. The fix +uses hash() to generate hash values for the tuple of (address, mask length, +network address). + +.. + +.. bpo: 39603 +.. date: 2020-02-12-14-17-39 +.. nonce: Gt3RSg +.. section: Security + +Prevent http header injection by rejecting control characters in +http.client.putrequest(...). + +.. + +.. bpo: 41909 +.. date: 2020-10-04-10-55-12 +.. nonce: BqHPcm +.. section: Core and Builtins + +Fixed stack overflow in :func:`issubclass` and :func:`isinstance` when +getting the ``__bases__`` attribute leads to infinite recursion. + +.. + +.. bpo: 41922 +.. date: 2020-10-04-01-02-58 +.. nonce: kHGT8I +.. section: Core and Builtins + +Speed up calls to ``reversed()`` by using the :pep:`590` ``vectorcall`` +calling convention. Patch by Dong-hee Na. + +.. + +.. bpo: 41873 +.. date: 2020-09-28-08-58-28 +.. nonce: VzEDhA +.. section: Core and Builtins + +Calls to ``float()`` are now faster due to the ``vectorcall`` calling +convention. Patch by Dennis Sweeney. + +.. + +.. bpo: 41870 +.. date: 2020-09-27-22-23-14 +.. nonce: 2v6_v4 +.. section: Core and Builtins + +Speed up calls to ``bool()`` by using the :pep:`590` ``vectorcall`` calling +convention. Patch by Dong-hee Na. + +.. + +.. bpo: 1635741 +.. date: 2020-09-26-14-43-30 +.. nonce: aJS9B3 +.. section: Core and Builtins + +Port the :mod:`_bisect` module to the multi-phase initialization API +(:pep:`489`). + +.. + +.. bpo: 39934 +.. date: 2020-09-24-12-15-45 +.. nonce: YVHTCF +.. section: Core and Builtins + +Correctly count control blocks in 'except' in compiler. Ensures that a +syntax error, rather a fatal error, occurs for deeply nested, named +exception handlers. + +.. + +.. bpo: 41780 +.. date: 2020-09-15-23-29-49 +.. nonce: bOBUIH +.. section: Core and Builtins + +Fix :meth:`__dir__` of :class:`types.GenericAlias`. Patch by Batuhan +Taskaya. + +.. + +.. bpo: 1635741 +.. date: 2020-09-12-18-34-34 +.. nonce: lh335O +.. section: Core and Builtins + +Port the :mod:`_lsprof` extension module to multi-phase initialization +(:pep:`489`). + +.. + +.. bpo: 41756 +.. date: 2020-09-12-12-55-45 +.. nonce: 1h0tbV +.. section: Core and Builtins + +Add PyGen_Send function to allow sending value into generator/coroutine +without raising StopIteration exception to signal return + +.. + +.. bpo: 1635741 +.. date: 2020-09-08-21-58-47 +.. nonce: vdjSLH +.. section: Core and Builtins + +Port the :mod:`cmath` extension module to multi-phase initialization +(:pep:`489`). + +.. + +.. bpo: 1635741 +.. date: 2020-09-08-20-39-43 +.. nonce: jiXmyT +.. section: Core and Builtins + +Port the :mod:`_scproxy` extension module to multi-phase initialization +(:pep:`489`). + +.. + +.. bpo: 1635741 +.. date: 2020-09-07-11-35-02 +.. nonce: rvIexb +.. section: Core and Builtins + +Port the :mod:`termios` extension module to multi-phase initialization +(:pep:`489`). + +.. + +.. bpo: 1635741 +.. date: 2020-09-07-09-45-47 +.. nonce: QuDIut +.. section: Core and Builtins + +Convert the :mod:`_sha256` extension module types to heap types. + +.. + +.. bpo: 41690 +.. date: 2020-09-02-12-00-57 +.. nonce: Ny-Sfy +.. section: Core and Builtins + +Fix a possible stack overflow in the parser when parsing functions and +classes with a huge ammount of arguments. Patch by Pablo Galindo. + +.. + +.. bpo: 1635741 +.. date: 2020-09-01-17-22-35 +.. nonce: CnRME3 +.. section: Core and Builtins + +Port the :mod:`_overlapped` extension module to multi-phase initialization +(:pep:`489`). + +.. + +.. bpo: 1635741 +.. date: 2020-09-01-17-08-07 +.. nonce: X9CZgo +.. section: Core and Builtins + +Port the :mod:`_curses_panel` extension module to multi-phase initialization +(:pep:`489`). + +.. + +.. bpo: 1635741 +.. date: 2020-09-01-17-06-02 +.. nonce: 5jZymK +.. section: Core and Builtins + +Port the :mod:`_opcode` extension module to multi-phase initialization +(:pep:`489`). + +.. + +.. bpo: 41681 +.. date: 2020-08-31-17-49-02 +.. nonce: 3-VJiH +.. section: Core and Builtins + +Fixes the wrong error description in the error raised by using 2 `,` in +format string in f-string and :meth:`str.format`. + +.. + +.. bpo: 41675 +.. date: 2020-08-31-14-53-17 +.. nonce: VSoqWU +.. section: Core and Builtins + +The implementation of :func:`signal.siginterrupt` now uses +:c:func:`sigaction` (if it is available in the system) instead of the +deprecated :c:func:`siginterrupt`. Patch by Pablo Galindo. + +.. + +.. bpo: 41670 +.. date: 2020-08-31-11-37-59 +.. nonce: vmRJRx +.. section: Core and Builtins + +Prevent line trace being skipped on platforms not compiled with +``USE_COMPUTED_GOTOS``. Fixes issue where some lines nested within a +try-except block were not being traced on Windows. + +.. + +.. bpo: 41654 +.. date: 2020-08-30-20-38-33 +.. nonce: HtnhAM +.. section: Core and Builtins + +Fix a crash that occurred when destroying subclasses of +:class:`MemoryError`. Patch by Pablo Galindo. + +.. + +.. bpo: 1635741 +.. date: 2020-08-28-20-54-04 +.. nonce: 7ijlcI +.. section: Core and Builtins + +Port the :mod:`zlib` extension module to multi-phase initialization +(:pep:`489`). + +.. + +.. bpo: 41631 +.. date: 2020-08-26-11-23-31 +.. nonce: 3jZcd9 +.. section: Core and Builtins + +The ``_ast`` module uses again a global state. Using a module state per +module instance is causing subtle practical problems. For example, the +Mercurial project replaces the ``__import__()`` function to implement lazy +import, whereas Python expected that ``import _ast`` always return a fully +initialized ``_ast`` module. + +.. + +.. bpo: 40077 +.. date: 2020-08-25-22-43-33 +.. nonce: vcxSUa +.. section: Core and Builtins + +Convert :mod:`_operator` to use :c:func:`PyType_FromSpec`. + +.. + +.. bpo: 1653741 +.. date: 2020-08-13-07-19-21 +.. nonce: fubBkb +.. section: Core and Builtins + +Port :mod:`_sha3` to multi-phase init. Convert static types to heap types. + +.. + +.. bpo: 1635741 +.. date: 2020-08-13-07-18-05 +.. nonce: FC13e7 +.. section: Core and Builtins + +Port the :mod:`_blake2` extension module to the multi-phase initialization +API (:pep:`489`). + +.. + +.. bpo: 41533 +.. date: 2020-08-12-20-29-57 +.. nonce: 4pcVAc +.. section: Core and Builtins + +Free the stack allocated in ``va_build_stack`` if ``do_mkstack`` fails and +the stack is not a ``small_stack``. + +.. + +.. bpo: 41531 +.. date: 2020-08-12-19-32-15 +.. nonce: WgPzjT +.. section: Core and Builtins + +Fix a bug that was dropping keys when compiling dict literals with more than +0xFFFF elements. Patch by Pablo Galindo. + +.. + +.. bpo: 41525 +.. date: 2020-08-12-07-35-07 +.. nonce: d9q3XL +.. section: Core and Builtins + +The output of ``python --help`` contains now only ASCII characters. + +.. + +.. bpo: 1635741 +.. date: 2020-08-10-16-11-32 +.. nonce: O0d3ym +.. section: Core and Builtins + +Port the :mod:`_sha1`, :mod:`_sha512`, and :mod:`_md5` extension modules to +multi-phase initialization API (:pep:`489`). + +.. + +.. bpo: 41431 +.. date: 2020-08-02-15-53-12 +.. nonce: TblUBT +.. section: Core and Builtins + +Optimize ``dict_merge()`` for copying dict (e.g. ``dict(d)`` and +``{}.update(d)``). + +.. + +.. bpo: 41428 +.. date: 2020-07-28-22-43-27 +.. nonce: FM6xsI +.. section: Core and Builtins + +Implement PEP 604. This supports (int | str) etc. in place of Union[str, +int]. + +.. + +.. bpo: 41340 +.. date: 2020-07-27-01-50-06 +.. nonce: pZXfcF +.. section: Core and Builtins + +Removed fallback implementation for ``strdup``. + +.. + +.. bpo: 38156 +.. date: 2020-07-20-17-01-17 +.. nonce: ptcdRy +.. section: Core and Builtins + +Handle interrupts that come after EOF correctly in ``PyOS_StdioReadline``. + +.. + +.. bpo: 41342 +.. date: 2020-07-19-15-40-52 +.. nonce: RRk_m_ +.. section: Core and Builtins + +:func:`round` with integer argument is now faster (9--60%). + +.. + +.. bpo: 41334 +.. date: 2020-07-18-18-01-10 +.. nonce: t5xMGp +.. section: Core and Builtins + +Constructors :func:`str`, :func:`bytes` and :func:`bytearray` are now faster +(around 30--40% for small objects). + +.. + +.. bpo: 41295 +.. date: 2020-07-18-08-15-32 +.. nonce: pu8Ezo +.. section: Core and Builtins + +Resolve a regression in CPython 3.8.4 where defining "__setattr__" in a +multi-inheritance setup and calling up the hierarchy chain could fail if +builtins/extension types were involved in the base types. + +.. + +.. bpo: 41323 +.. date: 2020-07-17-11-31-54 +.. nonce: ChbZHh +.. section: Core and Builtins + +Bytecode optimizations are performed directly on the control flow graph. +This will result in slightly more compact code objects in some +circumstances. + +.. + +.. bpo: 41247 +.. date: 2020-07-08-22-03-54 +.. nonce: PndYIk +.. section: Core and Builtins + +Always cache the running loop holder when running +``asyncio.set_running_loop``. + +.. + +.. bpo: 41252 +.. date: 2020-07-08-21-55-23 +.. nonce: nBWL-Y +.. section: Core and Builtins + +Fix incorrect refcounting in _ssl.c's ``_servername_callback()``. + +.. + +.. bpo: 1635741 +.. date: 2020-07-07-16-10-52 +.. nonce: zU-H_n +.. section: Core and Builtins + +Port :mod:`multiprocessing` to multi-phase initialization + +.. + +.. bpo: 1635741 +.. date: 2020-07-06-20-43-19 +.. nonce: LYhsni +.. section: Core and Builtins + +Port :mod:`winapi` to multiphase initialization + +.. + +.. bpo: 41215 +.. date: 2020-07-06-18-36-33 +.. nonce: vFGFIz +.. section: Core and Builtins + +Use non-NULL default values in the PEG parser keyword list to overcome a bug +that was preventing Python from being properly compiled when using the XLC +compiler. Patch by Pablo Galindo. + +.. + +.. bpo: 41218 +.. date: 2020-07-06-13-35-17 +.. nonce: oKnSr2 +.. section: Core and Builtins + +Python 3.8.3 had a regression where compiling with +ast.PyCF_ALLOW_TOP_LEVEL_AWAIT would aggressively mark list comprehension +with CO_COROUTINE. Now only list comprehension making use of async/await +will tagged as so. + +.. + +.. bpo: 1635741 +.. date: 2020-07-03-23-10-02 +.. nonce: F5coWe +.. section: Core and Builtins + +Port :mod:`faulthandler` to multiphase initialization. + +.. + +.. bpo: 1635741 +.. date: 2020-07-01-20-17-38 +.. nonce: -AtPYu +.. section: Core and Builtins + +Port :mod:`sha256` to multiphase initialization + +.. + +.. bpo: 41175 +.. date: 2020-06-30-20-17-31 +.. nonce: acJoXB +.. section: Core and Builtins + +Guard against a NULL pointer dereference within bytearrayobject triggered by +the ``bytearray() + bytearray()`` operation. + +.. + +.. bpo: 41100 +.. date: 2020-06-30-04-44-29 +.. nonce: PJwA6F +.. section: Core and Builtins + +add arm64 to the allowable Mac OS arches in mpdecimal.h + +.. + +.. bpo: 41094 +.. date: 2020-06-23-23-26-42 +.. nonce: zEIJse +.. section: Core and Builtins + +Fix decoding errors with audit when open files with non-ASCII names on +non-UTF-8 locale. + +.. + +.. bpo: 39960 +.. date: 2020-06-23-18-32-41 +.. nonce: Kez3fP +.. section: Core and Builtins + +The "hackcheck" that prevents sneaking around a type's __setattr__() by +calling the superclass method was rewritten to allow C implemented heap +types. + +.. + +.. bpo: 41084 +.. date: 2020-06-23-15-10-19 +.. nonce: pt3y7F +.. section: Core and Builtins + +Prefix the error message with 'f-string: ', when parsing an f-string +expression which throws a :exc:`SyntaxError`. + +.. + +.. bpo: 40521 +.. date: 2020-06-23-07-35-11 +.. nonce: dMNA6k +.. section: Core and Builtins + +Empty frozensets are no longer singletons. + +.. + +.. bpo: 41076 +.. date: 2020-06-22-13-22-30 +.. nonce: eWYw2N +.. section: Core and Builtins + +Pre-feed the parser with the location of the f-string expression, not the +f-string itself, which allows us to skip the shifting of the AST node +locations after the parsing is completed. + +.. + +.. bpo: 41056 +.. date: 2020-06-21-19-53-33 +.. nonce: IDu_EK +.. section: Core and Builtins + +Fixes a reference to deallocated stack space during startup when +constructing sys.path involving a relative symlink when code was supplied +via -c. (discovered via Coverity) + +.. + +.. bpo: 41061 +.. date: 2020-06-21-10-54-02 +.. nonce: AHf9MU +.. section: Core and Builtins + +Fix incorrect expressions and asserts in hashtable code and tests. + +.. + +.. bpo: 41052 +.. date: 2020-06-20-22-46-18 +.. nonce: 46MPeF +.. section: Core and Builtins + +Opt out serialization/deserialization for _random.Random + +.. + +.. bpo: 40939 +.. date: 2020-06-20-19-27-47 +.. nonce: jxJ4yn +.. section: Core and Builtins + +Rename `PyPegen*` functions to `PyParser*`, so that we can remove the old +set of `PyParser*` functions that were using the old parser, but keep +everything backwards-compatible. + +.. + +.. bpo: 35975 +.. date: 2020-06-20-17-00-44 +.. nonce: UDHCHp +.. section: Core and Builtins + +Stefan Behnel reported that cf_feature_version is used even when +PyCF_ONLY_AST is not set. This is against the intention and against the +documented behavior, so it's been fixed. + +.. + +.. bpo: 40939 +.. date: 2020-06-20-16-59-02 +.. nonce: 6810Ak +.. section: Core and Builtins + +Remove the remaining files from the old parser and the :mod:`symbol` module. + +.. + +.. bpo: 40077 +.. date: 2020-06-18-19-04-30 +.. nonce: _yI-ax +.. section: Core and Builtins + +Convert :mod:`_bz2` to use :c:func:`PyType_FromSpec`. + +.. + +.. bpo: 41006 +.. date: 2020-06-18-00-07-09 +.. nonce: H-wN-d +.. section: Core and Builtins + +The ``encodings.latin_1`` module is no longer imported at startup. Now it is +only imported when it is the filesystem encoding or the stdio encoding. + +.. + +.. bpo: 40636 +.. date: 2020-06-17-10-27-17 +.. nonce: MYaCIe +.. section: Core and Builtins + +:func:`zip` now supports :pep:`618`'s ``strict`` parameter, which raises a +:exc:`ValueError` if the arguments are exhausted at different lengths. Patch +by Brandt Bucher. + +.. + +.. bpo: 1635741 +.. date: 2020-06-17-00-52-21 +.. nonce: 61iyYh +.. section: Core and Builtins + +Port :mod:`_gdbm` to multiphase initialization. + +.. + +.. bpo: 40985 +.. date: 2020-06-15-16-29-55 +.. nonce: IIN_xX +.. section: Core and Builtins + +Fix a bug that caused the :exc:`SyntaxError` text to be empty when a file +ends with a line ending in a line continuation character (i.e. backslash). +The error text should contain the text of the last line. + +.. + +.. bpo: 40958 +.. date: 2020-06-15-01-20-44 +.. nonce: 7O2Wh1 +.. section: Core and Builtins + +Fix a possible buffer overflow in the PEG parser when gathering information +for emitting syntax errors. Patch by Pablo Galindo. + +.. + +.. bpo: 1635741 +.. date: 2020-06-12-22-56-17 +.. nonce: mmlp3Q +.. section: Core and Builtins + +Port :mod:`_dbm` to multiphase initialization. + +.. + +.. bpo: 40957 +.. date: 2020-06-12-12-21-54 +.. nonce: Z8n6I6 +.. section: Core and Builtins + +Fix refleak in _Py_fopen_obj() when PySys_Audit() fails + +.. + +.. bpo: 40950 +.. date: 2020-06-12-00-12-28 +.. nonce: tzMy7m +.. section: Core and Builtins + +Add a state to the :mod:`nis` module (:pep:`3121`) and apply the multiphase +initialization. Patch by Dong-hee Na. + +.. + +.. bpo: 40947 +.. date: 2020-06-11-16-06-49 +.. nonce: 72cZcR +.. section: Core and Builtins + +The Python :ref:`Path Configuration ` now takes +:c:member:`PyConfig.platlibdir` in account. + +.. + +.. bpo: 40939 +.. date: 2020-06-10-11-27-15 +.. nonce: DO-wAI +.. section: Core and Builtins + +Remove the old parser, the :mod:`parser` module and all associated support +code, command-line options and environment variables. Patch by Pablo +Galindo. + +.. + +.. bpo: 40847 +.. date: 2020-06-09-23-52-32 +.. nonce: 4XAACw +.. section: Core and Builtins + +Fix a bug where a line with only a line continuation character is not +considered a blank line at tokenizer level. In such cases, more than a +single `NEWLINE` token was emitted. The old parser was working around the +issue, but the new parser threw a :exc:`SyntaxError` for valid input due to +this. For example, an empty line following a line continuation character was +interpreted as a :exc:`SyntaxError`. + +.. + +.. bpo: 40890 +.. date: 2020-06-09-00-20-13 +.. nonce: LoRV-g +.. section: Core and Builtins + +Each dictionary view now has a ``mapping`` attribute that provides a +:class:`types.MappingProxyType` wrapping the original dictionary. Patch +contributed by Dennis Sweeney. + +.. + +.. bpo: 40889 +.. date: 2020-06-08-22-46-33 +.. nonce: vIBl-W +.. section: Core and Builtins + +Improved the performance of symmetric difference operations on dictionary +item views. Patch by Dennis Sweeney. + +.. + +.. bpo: 40904 +.. date: 2020-06-08-01-08-57 +.. nonce: 76qQzo +.. section: Core and Builtins + +Fix possible segfault in the new PEG parser when parsing f-string containing +yield statements with no value (:code:`f"{yield}"`). Patch by Pablo Galindo + +.. + +.. bpo: 40903 +.. date: 2020-06-07-22-50-10 +.. nonce: 7dWejS +.. section: Core and Builtins + +Fixed a possible segfault in the new PEG parser when producing error +messages for invalid assignments of the form :code:`p=p=`. Patch by Pablo +Galindo + +.. + +.. bpo: 40880 +.. date: 2020-06-06-00-23-19 +.. nonce: fjdzSh +.. section: Core and Builtins + +Fix invalid memory read in the new parser when checking newlines in string +literals. Patch by Pablo Galindo. + +.. + +.. bpo: 40883 +.. date: 2020-06-05-23-25-00 +.. nonce: M6sQ-Q +.. section: Core and Builtins + +Fix memory leak in when parsing f-strings in the new parser. Patch by Pablo +Galindo + +.. + +.. bpo: 40870 +.. date: 2020-06-05-12-48-28 +.. nonce: 9cd2sk +.. section: Core and Builtins + +Raise :exc:`ValueError` when validating custom AST's where the constants +``True``, ``False`` and ``None`` are used within a :class:`ast.Name` node. + +.. + +.. bpo: 40854 +.. date: 2020-06-03-13-53-24 +.. nonce: O6vfQU +.. section: Core and Builtins + +Allow overriding :data:`sys.platlibdir` via a new :envvar:`PYTHONPLATLIBDIR` +environment variable. + +.. + +.. bpo: 40826 +.. date: 2020-06-01-20-31-07 +.. nonce: XCI4M2 +.. section: Core and Builtins + +Fix GIL usage in :c:func:`PyOS_Readline`: lock the GIL to set an exception +and pass the Python thread state when checking if there is a pending signal. + +.. + +.. bpo: 1635741 +.. date: 2020-05-30-23-23-35 +.. nonce: 0D-laM +.. section: Core and Builtins + +Port :mod:`fcntl` to multiphase initialization. + +.. + +.. bpo: 19468 +.. date: 2020-05-30-23-18-35 +.. nonce: S-TA7p +.. section: Core and Builtins + +Delete unnecessary instance check in importlib.reload(). Patch by Furkan +?nder. + +.. + +.. bpo: 40824 +.. date: 2020-05-30-14-37-18 +.. nonce: XR3V5s +.. section: Core and Builtins + +Unexpected errors in calling the ``__iter__`` method are no longer masked by +``TypeError`` in the :keyword:`in` operator and functions +:func:`~operator.contains`, :func:`~operator.indexOf` and +:func:`~operator.countOf` of the :mod:`operator` module. + +.. + +.. bpo: 40792 +.. date: 2020-05-27-22-37-58 +.. nonce: WEDqqU +.. section: Core and Builtins + +Attributes ``start``, ``stop`` and ``step`` of the :class:`range` object now +always has exact type :class:`int`. Previously, they could have been an +instance of a subclass of ``int``. + +.. + +.. bpo: 40780 +.. date: 2020-05-26-17-43-58 +.. nonce: 3Ckdgm +.. section: Core and Builtins + +Fix a corner case where g-style string formatting of a float failed to +remove trailing zeros. + +.. + +.. bpo: 38964 +.. date: 2020-05-25-21-49-11 +.. nonce: lrml90 +.. section: Core and Builtins + +When there's a :exc:`SyntaxError` in the expression part of an fstring, the +filename attribute of the :exc:`SyntaxError` gets correctly set to the name +of the file the fstring resides in. + +.. + +.. bpo: 40750 +.. date: 2020-05-24-02-42-26 +.. nonce: ZmO9Ev +.. section: Core and Builtins + +Support the "-d" debug flag in the new PEG parser. Patch by Pablo Galindo + +.. + +.. bpo: 40217 +.. date: 2020-05-23-01-15-51 +.. nonce: jZsHTc +.. section: Core and Builtins + +Instances of types created with :c:func:`PyType_FromSpecWithBases` will no +longer automatically visit their class object when traversing references in +the garbage collector. The user is expected to manually visit the object's +class. Patch by Pablo Galindo. + +.. + +.. bpo: 39573 +.. date: 2020-05-22-00-34-34 +.. nonce: QO2QHj +.. section: Core and Builtins + +:c:func:`Py_TYPE()` is changed to the inline static function. Patch by +Dong-hee Na. + +.. + +.. bpo: 40696 +.. date: 2020-05-21-01-54-00 +.. nonce: u3n8Wx +.. section: Core and Builtins + +Fix a hang that can arise after :meth:`generator.throw` due to a cycle in +the exception context chain. + +.. + +.. bpo: 40521 +.. date: 2020-05-20-01-17-34 +.. nonce: wvAehI +.. section: Core and Builtins + +Each interpreter now its has own free lists, singletons and caches: + +* Free lists: float, tuple, list, dict, frame, context, + asynchronous generator, MemoryError. +* Singletons: empty tuple, empty bytes string, empty Unicode string, + single byte character, single Unicode (latin1) character. +* Slice cache. + +They are no longer shared by all interpreters. + +.. + +.. bpo: 40679 +.. date: 2020-05-19-19-39-49 +.. nonce: SVzz9p +.. section: Core and Builtins + +Certain :exc:`TypeError` messages about missing or extra arguments now +include the function's :term:`qualified name`. Patch by Dennis Sweeney. + +.. + +.. bpo: 29590 +.. date: 2020-05-03-22-26-00 +.. nonce: aRz3l7 +.. section: Core and Builtins + +Make the stack trace correct after calling :meth:`generator.throw` on a +generator that has yielded from a ``yield from``. + +.. + +.. bpo: 4022 +.. date: 2020-04-11-13-07-49 +.. nonce: Ctpn_F +.. section: Core and Builtins + +Improve performance of generators by not raising internal StopIteration. + +.. + +.. bpo: 1635741 +.. date: 2020-04-10-23-54-57 +.. nonce: ZURqoN +.. section: Core and Builtins + +Port :mod:`mmap` to multiphase initialization. + +.. + +.. bpo: 1635741 +.. date: 2020-04-05-02-35-08 +.. nonce: Kfe9fT +.. section: Core and Builtins + +Port :mod:`_lzma` to multiphase initialization. + +.. + +.. bpo: 37999 +.. date: 2019-09-01-14-26-02 +.. nonce: XPl6dn +.. section: Core and Builtins + +Builtin and extension functions that take integer arguments no longer accept +:class:`~decimal.Decimal`\ s, :class:`~fractions.Fraction`\ s and other +objects that can be converted to integers only with a loss (e.g. that have +the :meth:`~object.__int__` method but do not have the +:meth:`~object.__index__` method). + +.. + +.. bpo: 29882 +.. date: 2019-06-02-11-29-15 +.. nonce: AkRzjb +.. section: Core and Builtins + +Add :meth:`int.bit_count()`, counting the number of ones in the binary +representation of an integer. Patch by Niklas Fiekas. + +.. + +.. bpo: 36982 +.. date: 2019-05-25-05-27-39 +.. nonce: 0UHgfB +.. section: Core and Builtins + +Use ncurses extended color functions when available to support terminals +with 256 colors, and add the new function +:func:`curses.has_extended_color_support` to indicate whether extended color +support is provided by the underlying ncurses library. + +.. + +.. bpo: 19569 +.. date: 2018-08-29-15-57-07 +.. nonce: RGu2Kb +.. section: Core and Builtins + +Add the private macros ``_Py_COMP_DIAG_PUSH``, +``_Py_COMP_DIAG_IGNORE_DEPR_DECLS``, and ``_Py_COMP_DIAG_POP``. + +.. + +.. bpo: 26680 +.. date: 2018-03-15-11-51-36 +.. nonce: wOWYps +.. section: Core and Builtins + +The int type now supports the x.is_integer() method for compatibility with +float. + +.. + +.. bpo: 41900 +.. date: 2020-10-01-10-50-12 +.. nonce: Cho7oh +.. section: Library + +C14N 2.0 serialisation in xml.etree.ElementTree failed for unprefixed +attributes when a default namespace was defined. + +.. + +.. bpo: 41887 +.. date: 2020-09-30-23-49-42 +.. nonce: -ee2S- +.. section: Library + +Strip leading spaces and tabs on :func:`ast.literal_eval`. Also document +stripping of spaces and tabs for :func:`eval`. + +.. + +.. bpo: 41773 +.. date: 2020-09-28-23-22-25 +.. nonce: oKkus0 +.. section: Library + +Note in documentation that :func:`random.choices` doesn't support non-finite +weights, raise :exc:`ValueError` when given non-finite weights. + +.. + +.. bpo: 41840 +.. date: 2020-09-23-23-17-59 +.. nonce: QRFr4L +.. section: Library + +Fix a bug in the :mod:`symtable` module that was causing module-scope global +variables to not be reported as both local and global. Patch by Pablo +Galindo. + +.. + +.. bpo: 41842 +.. date: 2020-09-23-22-52-24 +.. nonce: lIuhC9 +.. section: Library + +Add :func:`codecs.unregister` function to unregister a codec search +function. + +.. + +.. bpo: 40564 +.. date: 2020-09-23-03-33-37 +.. nonce: iXQqMq +.. section: Library + +In ``zipfile.Path``, mutate the passed ZipFile object type instead of making +a copy. Prevents issues when both the local copy and the caller?s copy +attempt to close the same file handle. + +.. + +.. bpo: 40670 +.. date: 2020-09-22-14-55-34 +.. nonce: R5sm68 +.. section: Library + +More reliable validation of statements in :class:`timeit.Timer`. It now +accepts "empty" statements (only whitespaces and comments) and rejects +misindentent statements. + +.. + +.. bpo: 41833 +.. date: 2020-09-22-13-51-14 +.. nonce: 6HVDjT +.. section: Library + +The :class:`threading.Thread` constructor now uses the target name if the +*target* argument is specified but the *name* argument is omitted. + +.. + +.. bpo: 41817 +.. date: 2020-09-22-00-23-30 +.. nonce: bnh-VG +.. section: Library + +fix `tkinter.EventType` Enum so all members are strings, and none are tuples + +.. + +.. bpo: 41810 +.. date: 2020-09-20-15-14-05 +.. nonce: 7l8lyV +.. section: Library + +:data:`types.EllipsisType`, :data:`types.NotImplementedType` and +:data:`types.NoneType` have been reintroduced, providing a new set of types +readily interpretable by static type checkers. + +.. + +.. bpo: 41815 +.. date: 2020-09-19-23-14-54 +.. nonce: RNpuX3 +.. section: Library + +Fix SQLite3 segfault when backing up closed database. Patch contributed by +Peter David McCormick. + +.. + +.. bpo: 41816 +.. date: 2020-09-19-12-22-08 +.. nonce: ynynXJ +.. section: Library + +StrEnum added: it ensures that all members are already strings or string +candidates + +.. + +.. bpo: 41517 +.. date: 2020-09-15-22-43-30 +.. nonce: sLBH7g +.. section: Library + +fix bug allowing Enums to be extended via multiple inheritance + +.. + +.. bpo: 39587 +.. date: 2020-09-15-14-56-13 +.. nonce: 69xzuh +.. section: Library + +use the correct mix-in data type when constructing Enums + +.. + +.. bpo: 41792 +.. date: 2020-09-15-07-55-35 +.. nonce: qMpSlU +.. section: Library + +Add is_typeddict function to typing.py to check if a type is a TypedDict +class + +Previously there was no way to check that without using private API. See the +`relevant issue in python/typing +` + +.. + +.. bpo: 41789 +.. date: 2020-09-14-19-27-46 +.. nonce: pI_uZQ +.. section: Library + +Honor `object` overrides in `Enum` class creation (specifically, `__str__`, +`__repr__`, `__format__`, and `__reduce_ex__`). + +.. + +.. bpo: 32218 +.. date: 2020-09-12-16-18-42 +.. nonce: IpYkEe +.. section: Library + +`enum.Flag` and `enum.IntFlag` members are now iterable + +.. + +.. bpo: 39651 +.. date: 2020-09-11-12-38-55 +.. nonce: JMp9l2 +.. section: Library + +Fix a race condition in the ``call_soon_threadsafe()`` method of +``asyncio.ProactorEventLoop``: do nothing if the self-pipe socket has been +closed. + +.. + +.. bpo: 1635741 +.. date: 2020-09-08-13-55-34 +.. nonce: 56MLP- +.. section: Library + +Port the ``mashal`` extension module to the multi-phase initialization API +(:pep:`489`). + +.. + +.. bpo: 1635741 +.. date: 2020-09-08-13-51-16 +.. nonce: wkPeoT +.. section: Library + +Port the ``_string`` extension module to the multi-phase initialization API +(:pep:`489`). + +.. + +.. bpo: 41732 +.. date: 2020-09-06-20-27-10 +.. nonce: 1SKv26 +.. section: Library + +Added an :term:`iterator` to :class:`memoryview`. + +.. + +.. bpo: 41720 +.. date: 2020-09-04-20-45-38 +.. nonce: PW9MzZ +.. section: Library + +Fixed :meth:`turtle.Vec2D.__rmul__` for arguments which are not int or +float. + +.. + +.. bpo: 41696 +.. date: 2020-09-03-01-35-32 +.. nonce: zkYGre +.. section: Library + +Fix handling of debug mode in :func:`asyncio.run`. This allows setting +``PYTHONASYNCIODEBUG`` or ``-X dev`` to enable asyncio debug mode when using +:func:`asyncio.run`. + +.. + +.. bpo: 41687 +.. date: 2020-09-01-15-57-51 +.. nonce: m1b1KA +.. section: Library + +Fix implementation of sendfile to be compatible with Solaris. + +.. + +.. bpo: 41662 +.. date: 2020-08-30-21-38-57 +.. nonce: 6e9iZn +.. section: Library + +No longer override exceptions raised in ``__len__()`` of a sequence of +parameters in :mod:`sqlite3` with :exc:`~sqlite3.ProgrammingError`. + +.. + +.. bpo: 39010 +.. date: 2020-08-30-10-24-26 +.. nonce: _mzXJW +.. section: Library + +Restarting a ``ProactorEventLoop`` on Windows no longer logs spurious +``ConnectionResetErrors``. + +.. + +.. bpo: 41638 +.. date: 2020-08-29-16-45-12 +.. nonce: iZfW5N +.. section: Library + +:exc:`~sqlite3.ProgrammingError` message for absent parameter in +:mod:`sqlite3` contains now the name of the parameter instead of its index +when parameters are supplied as a dict. + +.. + +.. bpo: 41662 +.. date: 2020-08-29-16-07-36 +.. nonce: Mn79zh +.. section: Library + +Fixed crash when mutate list of parameters during iteration in +:mod:`sqlite3`. + +.. + +.. bpo: 41513 +.. date: 2020-08-23-14-23-18 +.. nonce: DGqc_I +.. section: Library + +Improved the accuracy of math.hypot(). Internally, each step is computed +with extra precision so that the result is now almost always correctly +rounded. + +.. + +.. bpo: 41609 +.. date: 2020-08-21-15-51-15 +.. nonce: JmiUKG +.. section: Library + +The pdb whatis command correctly reports instance methods as 'Method' rather +than 'Function'. + +.. + +.. bpo: 39994 +.. date: 2020-08-15-18-17-21 +.. nonce: dOgPOh +.. section: Library + +Fixed pprint's handling of dict subclasses that override __repr__. + +.. + +.. bpo: 32751 +.. date: 2020-08-15-15-50-12 +.. nonce: 85je5X +.. section: Library + +When cancelling the task due to a timeout, :meth:`asyncio.wait_for` will now +wait until the cancellation is complete also in the case when *timeout* is +<= 0, like it does with positive timeouts. + +.. + +.. bpo: 37658 +.. date: 2020-08-15-15-21-40 +.. nonce: f9nivB +.. section: Library + +:meth:`asyncio.wait_for` now properly handles races between cancellation of +itself and the completion of the wrapped awaitable. + +.. + +.. bpo: 40782 +.. date: 2020-08-13-08-07-25 +.. nonce: aGZqmB +.. section: Library + +Change the method asyncio.AbstractEventLoop.run_in_executor to not be a +coroutine. + +.. + +.. bpo: 41520 +.. date: 2020-08-12-13-25-16 +.. nonce: BEUWa4 +.. section: Library + +Fix :mod:`codeop` regression that prevented turning compile warnings into +errors. + +.. + +.. bpo: 41528 +.. date: 2020-08-12-07-43-31 +.. nonce: bu83oD +.. section: Library + +turtle uses math module functions to convert degrees to radians and vice +versa and to calculate vector norm + +.. + +.. bpo: 41513 +.. date: 2020-08-09-18-16-05 +.. nonce: e6K6EK +.. section: Library + +Minor algorithmic improvement to math.hypot() and math.dist() giving small +gains in speed and accuracy. + +.. + +.. bpo: 41503 +.. date: 2020-08-07-15-18-16 +.. nonce: IYftcu +.. section: Library + +Fixed a race between setTarget and flush in logging.handlers.MemoryHandler. + +.. + +.. bpo: 41497 +.. date: 2020-08-07-06-06-29 +.. nonce: aBtsWz +.. section: Library + +Fix potential UnicodeDecodeError in dis module. + +.. + +.. bpo: 41467 +.. date: 2020-08-04-00-20-30 +.. nonce: Z8DgTL +.. section: Library + +On Windows, fix asyncio ``recv_into()`` return value when the socket/pipe is +closed (:exc:`BrokenPipeError`): return ``0`` rather than an empty byte +string (``b''``). + +.. + +.. bpo: 41425 +.. date: 2020-08-03-01-59-48 +.. nonce: KJo6zF +.. section: Library + +Make tkinter doc example runnable. + +.. + +.. bpo: 41421 +.. date: 2020-08-01-00-51-15 +.. nonce: dHKRVB +.. section: Library + +Make an algebraic simplification to random.paretovariate(). It now is +slightly less subject to round-off error and is slightly faster. Inputs that +used to cause ZeroDivisionError now cause an OverflowError instead. + +.. + +.. bpo: 41440 +.. date: 2020-07-30-14-56-58 +.. nonce: rju34k +.. section: Library + +Add :func:`os.cpu_count()` support for VxWorks RTOS. + +.. + +.. bpo: 41316 +.. date: 2020-07-28-12-08-58 +.. nonce: bSCbK4 +.. section: Library + +Fix the :mod:`tarfile` module to write only basename of TAR file to GZIP +compression header. + +.. + +.. bpo: 41384 +.. date: 2020-07-26-21-18-43 +.. nonce: MlzIgV +.. section: Library + +Raise TclError instead of TypeError when an unknown option is passed to +tkinter.OptionMenu. + +.. + +.. bpo: 41317 +.. date: 2020-07-23-01-18-34 +.. nonce: O17Z6x +.. section: Library + +Use add_done_callback() in asyncio.loop.sock_accept() to unsubscribe reader +early on cancellation. + +.. + +.. bpo: 41364 +.. date: 2020-07-21-21-45-55 +.. nonce: 5O-k7A +.. section: Library + +Reduce import overhead of :mod:`uuid`. + +.. + +.. bpo: 35328 +.. date: 2020-07-21-16-20-55 +.. nonce: jXovHb +.. section: Library + +Set the environment variable ``VIRTUAL_ENV_PROMPT`` at :mod:`venv` +activation. + +.. + +.. bpo: 41341 +.. date: 2020-07-20-19-13-17 +.. nonce: wqrj8C +.. section: Library + +Recursive evaluation of `typing.ForwardRef` in `get_type_hints`. + +.. + +.. bpo: 41344 +.. date: 2020-07-20-13-27-48 +.. nonce: iKipNd +.. section: Library + +Prevent creating :class:`shared_memory.SharedMemory` objects with +:code:`size=0`. + +.. + +.. bpo: 41333 +.. date: 2020-07-18-18-07-40 +.. nonce: upkHIm +.. section: Library + +:meth:`collections.OrderedDict.pop` is now 2 times faster. + +.. + +.. bpo: 41288 +.. date: 2020-07-13-15-06-35 +.. nonce: 8mn5P- +.. section: Library + +Unpickling invalid NEWOBJ_EX opcode with the C implementation raises now +UnpicklingError instead of crashing. + +.. + +.. bpo: 39017 +.. date: 2020-07-12-22-16-58 +.. nonce: x3Cg-9 +.. section: Library + +Avoid infinite loop when reading specially crafted TAR files using the +tarfile module (CVE-2019-20907). + +.. + +.. bpo: 41273 +.. date: 2020-07-11-00-15-01 +.. nonce: SVrsJh +.. section: Library + +Speed up any transport using ``_ProactorReadPipeTransport`` by calling +``recv_into`` instead of ``recv``, thus not creating a new buffer for each +``recv`` call in the transport's read loop. + +.. + +.. bpo: 41235 +.. date: 2020-07-07-21-56-26 +.. nonce: H2csMU +.. section: Library + +Fix the error handling in :meth:`ssl.SSLContext.load_dh_params`. + +.. + +.. bpo: 41207 +.. date: 2020-07-06-16-58-53 +.. nonce: Emw7Nk +.. section: Library + +In distutils.spawn, restore expectation that DistutilsExecError is raised +when the command is not found. + +.. + +.. bpo: 29727 +.. date: 2020-07-05-19-16-02 +.. nonce: Q6Z2rg +.. section: Library + +Register :class:`array.array` as a +:class:`~collections.abc.MutableSequence`. Patch by Pablo Galindo. + +.. + +.. bpo: 39168 +.. date: 2020-07-04-21-56-46 +.. nonce: DQWsXj +.. section: Library + +Remove the ``__new__`` method of :class:`typing.Generic`. + +.. + +.. bpo: 41194 +.. date: 2020-07-03-13-15-08 +.. nonce: djrKjs +.. section: Library + +Fix a crash in the ``_ast`` module: it can no longer be loaded more than +once. It now uses a global state rather than a module state. + +.. + +.. bpo: 41195 +.. date: 2020-07-02-15-03-04 +.. nonce: cEnpO3 +.. section: Library + +Add read-only ssl.SSLContext.security_level attribute to retrieve the +context's security level. + +.. + +.. bpo: 41193 +.. date: 2020-07-02-11-53-45 +.. nonce: 8-Tnql +.. section: Library + +The ``write_history()`` atexit function of the readline completer now +ignores any :exc:`OSError` to ignore error if the filesystem is read-only, +instead of only ignoring :exc:`FileNotFoundError` and +:exc:`PermissionError`. + +.. + +.. bpo: 41182 +.. date: 2020-07-01-17-33-50 +.. nonce: FPFI0N +.. section: Library + +selector: use DefaultSelector based upon implementation + +.. + +.. bpo: 41161 +.. date: 2020-06-30-20-50-51 +.. nonce: QTdJjz +.. section: Library + +The decimal module now requires libmpdec-2.5.0. Users of +--with-system-libmpdec should update their system library. + +.. + +.. bpo: 40874 +.. date: 2020-06-28-21-16-51 +.. nonce: YImvzA +.. section: Library + +The decimal module now requires libmpdec-2.5.0. + +.. + +.. bpo: 41138 +.. date: 2020-06-27-13-51-36 +.. nonce: bIpf7g +.. section: Library + +Fixed the :mod:`trace` module CLI for Python source files with non-UTF-8 +encoding. + +.. + +.. bpo: 31082 +.. date: 2020-06-25-10-11-47 +.. nonce: HsgDkx +.. section: Library + +Use the term "iterable" in the docstring for :func:`functools.reduce`. + +.. + +.. bpo: 40521 +.. date: 2020-06-23-06-09-59 +.. nonce: HUfxP7 +.. section: Library + +Remove freelist from collections.deque(). + +.. + +.. bpo: 31938 +.. date: 2020-06-22-20-08-40 +.. nonce: EVuko9 +.. section: Library + +Fix default-value signatures of several functions in the :mod:`select` +module - by Anthony Sottile. + +.. + +.. bpo: 41068 +.. date: 2020-06-22-10-25-39 +.. nonce: _bX2BW +.. section: Library + +Fixed reading files with non-ASCII names from ZIP archive directly after +writing them. + +.. + +.. bpo: 41058 +.. date: 2020-06-20-21-03-55 +.. nonce: gztdZy +.. section: Library + +:func:`pdb.find_function` now correctly determines the source file encoding. + +.. + +.. bpo: 41056 +.. date: 2020-06-20-18-37-29 +.. nonce: d9v_uL +.. section: Library + +Invalid file descriptor values are now prevented from being passed to +os.fpathconf. (discovered by Coverity) + +.. + +.. bpo: 41056 +.. date: 2020-06-20-18-35-43 +.. nonce: Garcle +.. section: Library + +Fix a NULL pointer dereference within the ssl module during a MemoryError in +the keylog callback. (discovered by Coverity) + +.. + +.. bpo: 41056 +.. date: 2020-06-20-18-33-03 +.. nonce: gTH4Bq +.. section: Library + +Fixed an instance where a MemoryError within the zoneinfo module might not +be reported or not reported at its source. (found by Coverity) + +.. + +.. bpo: 41048 +.. date: 2020-06-20-10-16-57 +.. nonce: hEXB-B +.. section: Library + +:func:`mimetypes.read_mime_types` function reads the rule file using UTF-8 +encoding, not the locale encoding. Patch by Srinivas Reddy Thatiparthy. + +.. + +.. bpo: 41043 +.. date: 2020-06-20-00-19-30 +.. nonce: p-Pk-H +.. section: Library + +Fixed the use of :func:`~glob.glob` in the stdlib: literal part of the path +is now always correctly escaped. + +.. + +.. bpo: 41025 +.. date: 2020-06-18-10-34-59 +.. nonce: elf_nz +.. section: Library + +Fixed an issue preventing the C implementation of :class:`zoneinfo.ZoneInfo` +from being subclassed. + +.. + +.. bpo: 35018 +.. date: 2020-06-17-23-49-45 +.. nonce: NP5_Qk +.. section: Library + +Add the :class:`xml.sax.handler.LexicalHandler` class that is present in +other SAX XML implementations. + +.. + +.. bpo: 41002 +.. date: 2020-06-17-17-26-24 +.. nonce: NPBItE +.. section: Library + +Improve performance of HTTPResponse.read with a given amount. Patch by Bruce +Merry. + +.. + +.. bpo: 40448 +.. date: 2020-06-15-12-22-53 +.. nonce: 1dk8Bu +.. section: Library + +:mod:`ensurepip` now disables the use of `pip` cache when installing the +bundled versions of `pip` and `setuptools`. Patch by Krzysztof Konopko. + +.. + +.. bpo: 40967 +.. date: 2020-06-15-00-13-57 +.. nonce: _dx3OO +.. section: Library + +Removed :meth:`asyncio.Task.current_task` and +:meth:`asyncio.Task.all_tasks`. Patch contributed by R?mi Lapeyre. + +.. + +.. bpo: 40924 +.. date: 2020-06-13-12-04-50 +.. nonce: SM_luS +.. section: Library + +Ensure ``importlib.resources.path`` returns an extant path for the +SourceFileLoader's resource reader. Avoids the regression identified in +master while a long-term solution is devised. + +.. + +.. bpo: 40955 +.. date: 2020-06-12-11-55-30 +.. nonce: huixCg +.. section: Library + +Fix a minor memory leak in :mod:`subprocess` module when extra_groups was +specified. + +.. + +.. bpo: 40855 +.. date: 2020-06-12-10-44-15 +.. nonce: jSot83 +.. section: Library + +The standard deviation and variance functions in the statistics module were +ignoring their mu and xbar arguments. + +.. + +.. bpo: 40939 +.. date: 2020-06-11-11-07-10 +.. nonce: -D5Asl +.. section: Library + +Use the new PEG parser when generating the stdlib :mod:`keyword` module. + +.. + +.. bpo: 23427 +.. date: 2020-06-08-18-59-16 +.. nonce: ilg1Cz +.. section: Library + +Add :data:`sys.orig_argv` attribute: the list of the original command line +arguments passed to the Python executable. + +.. + +.. bpo: 33689 +.. date: 2020-06-06-14-09-55 +.. nonce: EFUDH7 +.. section: Library + +Ignore empty or whitespace-only lines in .pth files. This matches the +documentated behavior. Before, empty lines caused the site-packages dir to +appear multiple times in sys.path. By Ido Michael, contributors Malcolm +Smith and Tal Einat. + +.. + +.. bpo: 40884 +.. date: 2020-06-06-02-42-26 +.. nonce: n7fOwS +.. section: Library + +Added a `defaults` parameter to :class:`logging.Formatter`, to allow +specifying default values for custom fields. Patch by Asaf Alon and Bar +Harel. + +.. + +.. bpo: 40876 +.. date: 2020-06-05-20-00-18 +.. nonce: zDhiZj +.. section: Library + +Clarify error message in the :mod:`csv` module. + +.. + +.. bpo: 39791 +.. date: 2020-06-05-19-29-10 +.. nonce: _CcO3d +.. section: Library + +Refresh importlib.metadata from importlib_metadata 1.6.1. + +.. + +.. bpo: 40807 +.. date: 2020-06-04-16-25-15 +.. nonce: yYyLWx +.. section: Library + +Stop codeop._maybe_compile, used by code.InteractiveInterpreter (and IDLE). +from emitting each warning three times. + +.. + +.. bpo: 32604 +.. date: 2020-06-02-23-49-07 +.. nonce: ZN4V4l +.. section: Library + +Fix reference leak in the :mod:`select` module when the module is imported +in a subinterpreter. + +.. + +.. bpo: 39791 +.. date: 2020-06-02-02-16-02 +.. nonce: StCJlA +.. section: Library + +Built-in loaders (SourceFileLoader and ZipImporter) now supply +``TraversableResources`` implementations for ``ResourceReader``, and the +fallback function has been removed. + +.. + +.. bpo: 39314 +.. date: 2020-06-01-02-16-29 +.. nonce: 0T9hlA +.. section: Library + +:class:`rlcompleter.Completer` and the standard Python shell now close the +parenthesis for functions that take no arguments. Patch contributed by R?mi +Lapeyre. + +.. + +.. bpo: 17005 +.. date: 2020-05-31-23-32-36 +.. nonce: JlRUGB +.. section: Library + +The topological sort functionality that was introduced initially in the +:mod:`functools` module has been moved to a new :mod:`graphlib` module to +better accommodate the new tools and keep the original scope of the +:mod:`functools` module. Patch by Pablo Galindo + +.. + +.. bpo: 40834 +.. date: 2020-05-31-15-52-18 +.. nonce: MO9_hb +.. section: Library + +Fix truncate when sending str object with_xxsubinterpreters.channel_send. + +.. + +.. bpo: 40755 +.. date: 2020-05-30-18-48-58 +.. nonce: IyOe2J +.. section: Library + +Add rich comparisons to collections.Counter(). + +.. + +.. bpo: 26407 +.. date: 2020-05-30-14-19-47 +.. nonce: MjWLO1 +.. section: Library + +Unexpected errors in calling the ``__iter__`` method are no longer masked by +``TypeError`` in :func:`csv.reader`, :func:`csv.writer.writerow` and +:meth:`csv.writer.writerows`. + +.. + +.. bpo: 39384 +.. date: 2020-05-30-12-44-29 +.. nonce: Iqxy3q +.. section: Library + +Fixed email.contentmanager to allow set_content() to set a null string. + +.. + +.. bpo: 40744 +.. date: 2020-05-30-08-10-23 +.. nonce: jKURVV +.. section: Library + +The :mod:`sqlite3` module uses SQLite API functions that require SQLite +v3.7.3 or higher. This patch removes support for older SQLite versions, and +explicitly requires SQLite 3.7.3 both at build, compile and runtime. Patch +by Sergey Fedoseev and Erlend E. Aasland. + +.. + +.. bpo: 40777 +.. date: 2020-05-28-17-32-29 +.. nonce: 1kJU6N +.. section: Library + +Initialize PyDateTime_IsoCalendarDateType.tp_base at run-time to avoid +errors on some compilers. + +.. + +.. bpo: 38488 +.. date: 2020-05-28-16-51-00 +.. nonce: hFQNgA +.. section: Library + +Update ensurepip to install pip 20.1.1 and setuptools 47.1.0. + +.. + +.. bpo: 40792 +.. date: 2020-05-27-22-19-42 +.. nonce: 87Yx01 +.. section: Library + +The result of :func:`operator.index` now always has exact type :class:`int`. +Previously, the result could have been an instance of a subclass of ``int``. + +.. + +.. bpo: 40767 +.. date: 2020-05-27-21-27-01 +.. nonce: L5MnVV +.. section: Library + +:mod:`webbrowser` now properly finds the default browser in pure Wayland +systems by checking the WAYLAND_DISPLAY environment variable. Patch +contributed by J?r?my Attali. + +.. + +.. bpo: 40791 +.. date: 2020-05-27-18-04-52 +.. nonce: IzpNor +.. section: Library + +:func:`hashlib.compare_digest` uses OpenSSL's ``CRYPTO_memcmp()`` function +when OpenSSL is available. + +.. + +.. bpo: 40795 +.. date: 2020-05-27-17-00-18 +.. nonce: eZSnHA +.. section: Library + +:mod:`ctypes` module: If ctypes fails to convert the result of a callback or +if a ctypes callback function raises an exception, sys.unraisablehook is now +called with an exception set. Previously, the error was logged into stderr +by :c:func:`PyErr_Print`. + +.. + +.. bpo: 16995 +.. date: 2020-05-27-00-09-52 +.. nonce: 4niOT7 +.. section: Library + +Add :func:`base64.b32hexencode` and :func:`base64.b32hexdecode` to support +the Base32 Encoding with Extended Hex Alphabet. + +.. + +.. bpo: 30008 +.. date: 2020-05-25-22-18-38 +.. nonce: CKC3td +.. section: Library + +Fix :mod:`ssl` code to be compatible with OpenSSL 1.1.x builds that use +``no-deprecated`` and ``--api=1.1.0``. + +.. + +.. bpo: 30064 +.. date: 2020-05-25-11-52-23 +.. nonce: 6CICsH +.. section: Library + +Fix asyncio ``loop.sock_*`` race condition issue + +.. + +.. bpo: 40759 +.. date: 2020-05-24-23-52-35 +.. nonce: DdZdaw +.. section: Library + +Deprecate the :mod:`symbol` module. + +.. + +.. bpo: 40756 +.. date: 2020-05-24-11-06-37 +.. nonce: 7ZH83z +.. section: Library + +The second argument (extra) of ``LoggerAdapter.__init__`` now defaults to +None. + +.. + +.. bpo: 37129 +.. date: 2020-05-23-04-18-00 +.. nonce: YoYoYo +.. section: Library + +Add a new :data:`os.RWF_APPEND` flag for :func:`os.pwritev`. + +.. + +.. bpo: 40737 +.. date: 2020-05-23-00-22-11 +.. nonce: iph-CM +.. section: Library + +Fix possible reference leak for :mod:`sqlite3` initialization. + +.. + +.. bpo: 40726 +.. date: 2020-05-22-12-45-58 +.. nonce: 7oBdMw +.. section: Library + +Handle cases where the ``end_lineno`` is ``None`` on +:func:`ast.increment_lineno`. + +.. + +.. bpo: 40698 +.. date: 2020-05-20-14-38-04 +.. nonce: zwl5Hc +.. section: Library + +:mod:`distutils` upload creates SHA2-256 and Blake2b-256 digests. MD5 +digests is skipped if platform blocks MD5. + +.. + +.. bpo: 40695 +.. date: 2020-05-20-13-03-28 +.. nonce: lr4aIS +.. section: Library + +:mod:`hashlib` no longer falls back to builtin hash implementations when +OpenSSL provides a hash digest and the algorithm is blocked by security +policy. + +.. + +.. bpo: 9216 +.. date: 2020-05-20-12-53-20 +.. nonce: ps7Yf1 +.. section: Library + +func:`hashlib.new` passed ``usedforsecurity`` to OpenSSL EVP constructor +``_hashlib.new()``. test_hashlib and test_smtplib handle strict security +policy better. + +.. + +.. bpo: 40614 +.. date: 2020-05-18-22-41-02 +.. nonce: 8j3kmq +.. section: Library + +:func:`ast.parse` will not parse self documenting expressions in f-strings +when passed ``feature_version`` is less than ``(3, 8)``. + +.. + +.. bpo: 40626 +.. date: 2020-05-18-17-29-30 +.. nonce: NeZufF +.. section: Library + +Add h5 file extension as MIME Type application/x-hdf5, as per HDF Group +recommendation for HDF5 formatted data files. Patch contributed by Mark +Schwab. + +.. + +.. bpo: 25920 +.. date: 2020-05-18-15-38-25 +.. nonce: PxrLY8 +.. section: Library + +On macOS, when building Python for macOS 10.4 and older, which wasn't the +case for python.org macOS installer, :func:`socket.getaddrinfo` no longer +uses an internal lock to prevent race conditions when calling +``getaddrinfo()`` which is thread-safe since macOS 10.5. Python 3.9 requires +macOS 10.6 or newer. The internal lock caused random hang on fork when +another thread was calling :func:`socket.getaddrinfo`. The lock was also +used on FreeBSD older than 5.3, OpenBSD older than 201311 and NetBSD older +than 4. + +.. + +.. bpo: 40671 +.. date: 2020-05-18-15-26-31 +.. nonce: NeZ9Cy +.. section: Library + +Prepare ``_hashlib`` for :pep:`489` and use :c:func:`PyModule_AddType`. + +.. + +.. bpo: 32309 +.. date: 2020-05-17-02-03-09 +.. nonce: KM9psl +.. section: Library + +Added a new :term:`coroutine` :func:`asyncio.to_thread`. It is mainly used +for running IO-bound functions in a separate thread to avoid blocking the +event loop, and essentially works as a high-level version of +:meth:`~asyncio.loop.run_in_executor` that can directly take keyword +arguments. + +.. + +.. bpo: 36543 +.. date: 2020-05-15-21-14-45 +.. nonce: Jt-eSX +.. section: Library + +Restored the deprecated :mod:`xml.etree.cElementTree` module. + +.. + +.. bpo: 40611 +.. date: 2020-05-13-16-28-33 +.. nonce: ZCk0_c +.. section: Library + +:data:`~mmap.MAP_POPULATE` constant has now been added to the list of +exported :mod:`mmap` module flags. + +.. + +.. bpo: 39881 +.. date: 2020-05-07-22-00-12 +.. nonce: E1xsNv +.. section: Library + +PEP 554 for use in the test suite. (Patch By Joannah Nanjekye) + +.. + +.. bpo: 13097 +.. date: 2020-05-06-02-01-25 +.. nonce: Wh5xSK +.. section: Library + +``ctypes`` now raises an ``ArgumentError`` when a callback is invoked with +more than 1024 arguments. + +.. + +.. bpo: 39385 +.. date: 2020-04-23-18-21-19 +.. nonce: MIAyS7 +.. section: Library + +A new test assertion context-manager, :func:`unittest.assertNoLogs` will +ensure a given block of code emits no log messages using the logging module. +Contributed by Kit Yan Choi. + +.. + +.. bpo: 23082 +.. date: 2020-04-20-22-08-36 +.. nonce: iX90Id +.. section: Library + +Updated the error message and docs of PurePath.relative_to() to better +reflect the function behaviour. + +.. + +.. bpo: 40318 +.. date: 2020-04-18-14-16-02 +.. nonce: K2UdRx +.. section: Library + +Use SQLite3 trace v2 API, if it is available. + +.. + +.. bpo: 40105 +.. date: 2020-04-03-16-13-59 +.. nonce: hfM2c0 +.. section: Library + +ZipFile truncates files to avoid corruption when a shorter comment is +provided in append ("a") mode. Patch by Jan Mazur. + +.. + +.. bpo: 40084 +.. date: 2020-03-29-21-32-00 +.. nonce: MCYwcv +.. section: Library + +Fix ``Enum.__dir__``: dir(Enum.member) now includes attributes as well as +methods. + +.. + +.. bpo: 31122 +.. date: 2020-03-11-07-44-06 +.. nonce: zIQ80l +.. section: Library + +ssl.wrap_socket() now raises ssl.SSLEOFError rather than OSError when peer +closes connection during TLS negotiation + +.. + +.. bpo: 39728 +.. date: 2020-02-24-10-58-34 +.. nonce: kOOaHn +.. section: Library + +fix default `_missing_` so a duplicate `ValueError` is not set as the +`__context__` of the original `ValueError` + +.. + +.. bpo: 39244 +.. date: 2020-02-23-15-09-47 +.. nonce: aBK5IM +.. section: Library + +Fixed :class:`multiprocessing.context.get_all_start_methods` to properly +return the default method first on macOS. + +.. + +.. bpo: 39040 +.. date: 2019-12-15-18-47-20 +.. nonce: tKa0Qs +.. section: Library + +Fix parsing of invalid mime headers parameters by collapsing whitespace +between encoded words in a bare-quote-string. + +.. + +.. bpo: 38731 +.. date: 2019-11-13-07-37-11 +.. nonce: 9qmcSx +.. section: Library + +Add ``--quiet`` option to command-line interface of :mod:`py_compile`. Patch +by Gregory Schevchenko. + +.. + +.. bpo: 35714 +.. date: 2019-10-25-23-45-49 +.. nonce: fw3xb7 +.. section: Library + +:exc:`struct.error` is now raised if there is a null character in a +:mod:`struct` format string. + +.. + +.. bpo: 38144 +.. date: 2019-09-12-21-34-03 +.. nonce: 8uQCdd +.. section: Library + +Added the *root_dir* and *dir_fd* parameters in :func:`glob.glob`. + +.. + +.. bpo: 26543 +.. date: 2019-08-11-16-28-03 +.. nonce: X-TJZO +.. section: Library + +Fix :meth:`IMAP4.noop()` when debug mode is enabled (ex: ``imaplib.Debug = +3``). + +.. + +.. bpo: 12178 +.. date: 2019-05-31-23-54-28 +.. nonce: N6FLCZ +.. section: Library + +:func:`csv.writer` now correctly escapes *escapechar* when input contains +*escapechar*. Patch by Catalin Iacob, Berker Peksag, and Itay Elbirt. + +.. + +.. bpo: 36290 +.. date: 2019-03-17-19-01-53 +.. nonce: 7VXo_K +.. section: Library + +AST nodes are now raising :exc:`TypeError` on conflicting keyword arguments. +Patch contributed by R?mi Lapeyre. + +.. + +.. bpo: 33944 +.. date: 2019-03-01-01-56-23 +.. nonce: -82Pkt +.. section: Library + +Added site.py site-packages tracing in verbose mode. + +.. + +.. bpo: 35078 +.. date: 2018-10-27-09-37-03 +.. nonce: kweA3R +.. section: Library + +Refactor formatweekday, formatmonthname methods in LocaleHTMLCalendar and +LocaleTextCalendar classes in calendar module to call the base class +methods.This enables customizable CSS classes for LocaleHTMLCalendar. Patch +by Srinivas Reddy Thatiparthy + +.. + +.. bpo: 29620 +.. date: 2018-08-21-16-20-33 +.. nonce: xxx666 +.. section: Library + +:func:`~unittest.TestCase.assertWarns` no longer raises a +``RuntimeException`` when accessing a module's ``__warningregistry__`` +causes importation of a new module, or when a new module is imported in +another thread. Patch by Kernc. + +.. + +.. bpo: 31844 +.. date: 2018-07-30-12-48-17 +.. nonce: 0_GKsD +.. section: Library + +Remove ``ParserBase.error()`` method from the private and undocumented +``_markupbase`` module. :class:`html.parser.HTMLParser` is the only +subclass of ``ParserBase`` and its ``error()`` implementation was deprecated +in Python 3.4 and removed in Python 3.5. + +.. + +.. bpo: 34226 +.. date: 2018-07-29-12-14-54 +.. nonce: BE7zbu +.. section: Library + +Fix `cgi.parse_multipart` without content_length. Patch by Roger Duran + +.. + +.. bpo: 33660 +.. date: 2018-06-12-23-30-41 +.. nonce: AdDn5Z +.. section: Library + +Fix pathlib.PosixPath to resolve a relative path located on the root +directory properly. + +.. + +.. bpo: 28557 +.. date: 2018-06-07-22-04-01 +.. nonce: ViNJnK +.. section: Library + +Improve the error message for a misbehaving ``rawio.readinto`` + +.. + +.. bpo: 26680 +.. date: 2018-03-15-11-56-48 +.. nonce: Udkhn4 +.. section: Library + +The d.is_integer() method is added to the Decimal type, for compatibility +with other number types. + +.. + +.. bpo: 26680 +.. date: 2018-03-15-11-55-04 +.. nonce: eKAi85 +.. section: Library + +The x.is_integer() method is incorporated into the abstract types of the +numeric tower, Real, Rational and Integral, with appropriate default +implementations. + +.. + +.. bpo: 41428 +.. date: 2020-10-03-18-20-46 +.. nonce: _ju1NE +.. section: Documentation + +Add documentation for :pep:`604` (Allow writing union types as ``X | Y``). + +.. + +.. bpo: 41774 +.. date: 2020-09-24-15-35-13 +.. nonce: 5IqdGP +.. section: Documentation + +In Programming FAQ "Sequences (Tuples/Lists)" section, add "How do you +remove multiple items from a list". + +.. + +.. bpo: 35293 +.. date: 2020-09-12-17-37-13 +.. nonce: _cOwPD +.. section: Documentation + +Fix RemovedInSphinx40Warning when building the documentation. Patch by +Dong-hee Na. + +.. + +.. bpo: 37149 +.. date: 2020-09-10-07-48-02 +.. nonce: VD0rCv +.. section: Documentation + +Change Shipman tkinter doc link from archive.org to TkDocs. (The doc has +been removed from the NMT server.) The new link responds much faster and +includes a short explanatory note. + +.. + +.. bpo: 41726 +.. date: 2020-09-08-16-57-09 +.. nonce: g0UXrn +.. section: Documentation + +Update the refcounts info of ``PyType_FromModuleAndSpec``. + +.. + +.. bpo: 41624 +.. date: 2020-08-25-15-11-23 +.. nonce: ddjJlN +.. section: Documentation + +Fix the signature of :class:`typing.Coroutine`. + +.. + +.. bpo: 40204 +.. date: 2020-08-12-18-35-40 +.. nonce: C8A_pe +.. section: Documentation + +Enable Sphinx 3.2 ``c_allow_pre_v3`` option and disable +``c_warn_on_allowed_pre_v3`` option to make the documentation compatible +with Sphinx 2 and Sphinx 3. + +.. + +.. bpo: 41045 +.. date: 2020-07-27-20-46-17 +.. nonce: GFF6Ul +.. section: Documentation + +Add documentation for debug feature of f-strings. + +.. + +.. bpo: 41314 +.. date: 2020-07-25-14-20-00 +.. nonce: yrjko0 +.. section: Documentation + +Changed the release when ``from __future__ import annotations`` becomes the +default from ``4.0`` to ``3.10`` (following a change in PEP 563). + +.. + +.. bpo: 40979 +.. date: 2020-07-21-15-23-30 +.. nonce: pLA8rO +.. section: Documentation + +Refactored typing.rst, arranging more than 70 classes, functions, and +decorators into new sub-sections. + +.. + +.. bpo: 40552 +.. date: 2020-05-09-12-10-31 +.. nonce: _0uB73 +.. section: Documentation + +Fix in tutorial section 4.2. Code snippet is now correct. + +.. + +.. bpo: 39883 +.. date: 2020-03-07-03-53-39 +.. nonce: 1tnb4- +.. section: Documentation + +Make code, examples, and recipes in the Python documentation be licensed +under the more permissive BSD0 license in addition to the existing Python +2.0 license. + +.. + +.. bpo: 37703 +.. date: 2019-08-16-20-25-42 +.. nonce: Qm_l_H +.. section: Documentation + +Updated Documentation to comprehensively elaborate on the behaviour of +gather.cancel() + +.. + +.. bpo: 41939 +.. date: 2020-10-05-09-37-43 +.. nonce: P4OlbA +.. section: Tests + +Fix test_site.test_license_exists_at_url(): call +``urllib.request.urlcleanup()`` to reset the global +``urllib.request._opener``. Patch by Victor Stinner. + +.. + +.. bpo: 41731 +.. date: 2020-09-11-19-12-31 +.. nonce: Ivxh4U +.. section: Tests + +Make test_cmd_line_script pass with option '-vv'. + +.. + +.. bpo: 41602 +.. date: 2020-08-25-19-25-36 +.. nonce: Z64s0I +.. section: Tests + +Add tests for SIGINT handling in the runpy module. + +.. + +.. bpo: 41521 +.. date: 2020-08-11-14-59-13 +.. nonce: w2UYK7 +.. section: Tests + +:mod:`test.support`: Rename ``blacklist`` parameter of +:func:`~test.support.check__all__` to ``not_exported``. + +.. + +.. bpo: 41477 +.. date: 2020-08-07-17-28-49 +.. nonce: GrFexU +.. section: Tests + +Make ctypes optional in test_genericalias. + +.. + +.. bpo: 41085 +.. date: 2020-06-23-12-02-45 +.. nonce: JZKsyz +.. section: Tests + +Fix integer overflow in the :meth:`array.array.index` method on 64-bit +Windows for index larger than ``2**31``. + +.. + +.. bpo: 41069 +.. date: 2020-06-22-00-21-12 +.. nonce: bLZkX- +.. section: Tests + +:data:`test.support.TESTFN` and the current directory for tests when run via +``test.regrtest`` contain now non-ascii characters if possible. + +.. + +.. bpo: 38377 +.. date: 2020-06-17-18-00-21 +.. nonce: jfg4TH +.. section: Tests + +On Linux, skip tests using multiprocessing if the current user cannot create +a file in ``/dev/shm/`` directory. Add the +:func:`~test.support.skip_if_broken_multiprocessing_synchronize` function to +the :mod:`test.support` module. + +.. + +.. bpo: 41009 +.. date: 2020-06-17-17-27-07 +.. nonce: Rvn6OQ +.. section: Tests + +Fix use of ``support.require_{linux|mac|freebsd}_version()`` decorators as +class decorator. + +.. + +.. bpo: 41003 +.. date: 2020-06-17-15-07-14 +.. nonce: tiH_Fy +.. section: Tests + +Fix ``test_copyreg`` when ``numpy`` is installed: ``test.pickletester`` now +saves/restores warnings filters when importing ``numpy``, to ignore filters +installed by ``numpy``. + +.. + +.. bpo: 40964 +.. date: 2020-06-12-20-46-23 +.. nonce: OBzf2c +.. section: Tests + +Disable remote :mod:`imaplib` tests, host cyrus.andrew.cmu.edu is blocking +incoming connections. + +.. + +.. bpo: 40927 +.. date: 2020-06-09-18-48-18 +.. nonce: 67ylLg +.. section: Tests + +Fix test_binhex when run twice: it now uses import_fresh_module() to ensure +that it raises DeprecationWarning each time. + +.. + +.. bpo: 17258 +.. date: 2020-05-26-07-53-31 +.. nonce: X_IKTQ +.. section: Tests + +Skip some :mod:`multiprocessing` tests when MD5 hash digest is blocked. + +.. + +.. bpo: 31904 +.. date: 2020-04-09-15-40-03 +.. nonce: TJ4k3d +.. section: Tests + +Increase LOOPBACK_TIMEOUT to 10 for VxWorks RTOS. + +.. + +.. bpo: 38169 +.. date: 2019-09-14-13-20-27 +.. nonce: hurq4B +.. section: Tests + +Increase code coverage for SharedMemory and ShareableList + +.. + +.. bpo: 34401 +.. date: 2018-08-20-09-38-52 +.. nonce: eGxMPm +.. section: Tests + +Make test_gdb properly run on HP-UX. Patch by Michael Osipov. + +.. + +.. bpo: 38249 +.. date: 2020-09-28-21-56-51 +.. nonce: uzMCaZ +.. section: Build + +Update :c:macro:`Py_UNREACHABLE` to use __builtin_unreachable() if only the +compiler is able to use it. Patch by Dong-hee Na. + +.. + +.. bpo: 41617 +.. date: 2020-08-24-18-34-01 +.. nonce: sKKXz7 +.. section: Build + +Fix ``pycore_bitutils.h`` header file to support old clang versions: +``__builtin_bswap16()`` is not available in LLVM clang 3.0. + +.. + +.. bpo: 40204 +.. date: 2020-06-25-06-59-13 +.. nonce: GpD04D +.. section: Build + +Pin Sphinx version to 2.3.1 in ``Doc/Makefile``. + +.. + +.. bpo: 36020 +.. date: 2020-06-15-22-14-25 +.. nonce: wbiv0P +.. section: Build + +The C99 functions :c:func:`snprintf` and :c:func:`vsnprintf` are now +required to build Python. + +.. + +.. bpo: 40684 +.. date: 2020-06-08-19-57-05 +.. nonce: WIY2-i +.. section: Build + +``make install`` now uses the ``PLATLIBDIR`` variable for the destination +``lib-dynload/`` directory when ``./configure --with-platlibdir`` is used. + +.. + +.. bpo: 40683 +.. date: 2020-05-19-10-54-08 +.. nonce: W8JHrr +.. section: Build + +Fixed an issue where the :mod:`zoneinfo` module and its tests were not +included when Python is installed with ``make``. + +.. + +.. bpo: 41744 +.. date: 2020-09-11-17-59-33 +.. nonce: e_ugDQ +.. section: Windows + +Fixes automatic import of props file when using the Nuget package. + +.. + +.. bpo: 41627 +.. date: 2020-09-04-21-35-28 +.. nonce: sx2KN1 +.. section: Windows + +The user site directory for 32-bit now includes a ``-32`` suffix to +distinguish it from the 64-bit interpreter's directory. + +.. + +.. bpo: 41526 +.. date: 2020-08-13-22-40-58 +.. nonce: -i2bwb +.. section: Windows + +Fixed layout of final page of the installer by removing the special thanks +to Mark Hammond (with his permission). + +.. + +.. bpo: 41492 +.. date: 2020-08-06-16-59-10 +.. nonce: 2FQ9cM +.. section: Windows + +Fixes the description that appears in UAC prompts. + +.. + +.. bpo: 40948 +.. date: 2020-07-28-12-39-32 +.. nonce: ISUFO6 +.. section: Windows + +Improve post-install message to direct people to the "py" command. + +.. + +.. bpo: 41412 +.. date: 2020-07-28-11-55-43 +.. nonce: ME20KB +.. section: Windows + +The installer will now fail to install on Windows 7 and Windows 8. Further, +the UCRT dependency is now always downloaded on demand. + +.. + +.. bpo: 40741 +.. date: 2020-07-20-23-26-26 +.. nonce: C9sc_d +.. section: Windows + +Update Windows release to include SQLite 3.32.3. + +.. + +.. bpo: 41142 +.. date: 2020-06-28-12-40-41 +.. nonce: jpZzzh +.. section: Windows + +:mod:`msilib` now supports creating CAB files with non-ASCII file path and +adding files with non-ASCII file path to them. + +.. + +.. bpo: 41074 +.. date: 2020-06-24-21-30-42 +.. nonce: gaQc3C +.. section: Windows + +Fixed support of non-ASCII names in functions :func:`msilib.OpenDatabase` +and :func:`msilib.init_database` and non-ASCII SQL in method +:meth:`msilib.Database.OpenView`. + +.. + +.. bpo: 41039 +.. date: 2020-06-23-03-12-57 +.. nonce: 0hgd0s +.. section: Windows + +Stable ABI redirection DLL (python3.dll) now uses ``#pragma +comment(linker)`` for re-exporting. + +.. + +.. bpo: 40164 +.. date: 2020-06-12-13-13-44 +.. nonce: SPrSn5 +.. section: Windows + +Updates Windows OpenSSL to 1.1.1g + +.. + +.. bpo: 39631 +.. date: 2020-05-19-14-43-33 +.. nonce: Z5yXam +.. section: Windows + +Changes the registered MIME type for ``.py`` files on Windows to +``text/x-python`` instead of ``text/plain``. + +.. + +.. bpo: 40677 +.. date: 2020-05-19-04-11-12 +.. nonce: qQbLW8 +.. section: Windows + +Manually define IO_REPARSE_TAG_APPEXECLINK in case some old Windows SDK +doesn't have it. + +.. + +.. bpo: 37556 +.. date: 2019-07-11-06-11-09 +.. nonce: sygMUU +.. section: Windows + +Extend py.exe help to mention overrides via venv, shebang, environmental +variables & ini files. + +.. + +.. bpo: 41557 +.. date: 2020-08-26-09-31-37 +.. nonce: mcQ75z +.. section: macOS + +Update macOS installer to use SQLite 3.33.0. + +.. + +.. bpo: 39580 +.. date: 2020-06-25-06-09-00 +.. nonce: N_vJ9h +.. section: macOS + +Avoid opening Finder window if running installer from the command line. +Patch contributed by Rick Heil. + +.. + +.. bpo: 41100 +.. date: 2020-06-24-13-51-57 +.. nonce: mcHdc5 +.. section: macOS + +Fix configure error when building on macOS 11. Note that the current Python +release was released shortly after the first developer preview of macOS 11 +(Big Sur); there are other known issues with building and running on the +developer preview. Big Sur is expected to be fully supported in a future +bugfix release of Python 3.8.x and with 3.9.0. + +.. + +.. bpo: 40741 +.. date: 2020-06-19-14-19-08 +.. nonce: L7yTbm +.. section: macOS + +Update macOS installer to use SQLite 3.32.3. + +.. + +.. bpo: 41005 +.. date: 2020-06-17-13-45-15 +.. nonce: zZegdV +.. section: macOS + +fixed an XDG settings issue not allowing macos to open browser in +webbrowser.py + +.. + +.. bpo: 40741 +.. date: 2020-06-07-20-10-56 +.. nonce: 80A2BW +.. section: macOS + +Update macOS installer to use SQLite 3.32.2. + +.. + +.. bpo: 41775 +.. date: 2020-09-24-14-31-16 +.. nonce: sB8Vre +.. section: IDLE + +Use 'IDLE Shell' as shell title + +.. + +.. bpo: 35764 +.. date: 2020-09-22-11-13-45 +.. nonce: VoNa8y +.. section: IDLE + +Rewrite the Calltips doc section. + +.. + +.. bpo: 40181 +.. date: 2020-09-22-00-45-40 +.. nonce: hhQi3z +.. section: IDLE + +In calltips, stop reminding that '/' marks the end of positional-only +arguments. + +.. + +.. bpo: 41468 +.. date: 2020-08-09-13-42-55 +.. nonce: zkP0_Y +.. section: IDLE + +Improve IDLE run crash error message (which users should never see). + +.. + +.. bpo: 41373 +.. date: 2020-07-24-17-49-58 +.. nonce: YQIPu_ +.. section: IDLE + +Save files loaded with no line ending, as when blank, or different line +endings, by setting its line ending to the system default. Fix regression in +3.8.4 and 3.9.0b4. + +.. + +.. bpo: 41300 +.. date: 2020-07-16-17-39-06 +.. nonce: wRixNb +.. section: IDLE + +Save files with non-ascii chars. Fix regression released in 3.9.0b4 and +3.8.4. + +.. + +.. bpo: 37765 +.. date: 2020-07-07-18-44-30 +.. nonce: umc1o8 +.. section: IDLE + +Add keywords to module name completion list. Rewrite Completions section of +IDLE doc. + +.. + +.. bpo: 41152 +.. date: 2020-06-29-14-51-15 +.. nonce: d6mV0C +.. section: IDLE + +The encoding of ``stdin``, ``stdout`` and ``stderr`` in IDLE is now always +UTF-8. + +.. + +.. bpo: 41144 +.. date: 2020-06-27-17-02-00 +.. nonce: JoFGIX +.. section: IDLE + +Make Open Module open a special module such as os.path. + +.. + +.. bpo: 39885 +.. date: 2020-05-29-18-21-58 +.. nonce: zB_-bN +.. section: IDLE + +Make context menu Cut and Copy work again when right-clicking within a +selection. + +.. + +.. bpo: 40723 +.. date: 2020-05-24-06-19-43 +.. nonce: AJLd4U +.. section: IDLE + +Make test_idle pass when run after import. + +.. + +.. bpo: 41936 +.. date: 2020-10-05-01-25-23 +.. nonce: 1gb5ra +.. section: C API + +Removed undocumented macros ``Py_ALLOW_RECURSION`` and +``Py_END_ALLOW_RECURSION`` and the ``recursion_critical`` field of the +:c:type:`PyInterpreterState` structure. + +.. + +.. bpo: 41692 +.. date: 2020-10-02-00-57-34 +.. nonce: fDScsF +.. section: C API + +The ``PyUnicode_InternImmortal()`` function is now deprecated and will be +removed in Python 3.12: use :c:func:`PyUnicode_InternInPlace` instead. Patch +by Victor Stinner. + +.. + +.. bpo: 41842 +.. date: 2020-09-27-20-43-16 +.. nonce: bCakAj +.. section: C API + +Add :c:func:`PyCodec_Unregister` function to unregister a codec search +function. + +.. + +.. bpo: 41834 +.. date: 2020-09-22-14-47-12 +.. nonce: nrOrDU +.. section: C API + +Remove the ``_Py_CheckRecursionLimit`` variable: it has been replaced by +``ceval.recursion_limit`` of the :c:type:`PyInterpreterState` structure. +Patch by Victor Stinner. + +.. + +.. bpo: 41689 +.. date: 2020-09-01-23-39-45 +.. nonce: zxHbLB +.. section: C API + +Types created with :c:func:`PyType_FromSpec` now make any signature in their +``tp_doc`` slot accessible from ``__text_signature__``. + +.. + +.. bpo: 41524 +.. date: 2020-08-12-17-09-06 +.. nonce: u6Xfr2 +.. section: C API + +Fix bug in PyOS_mystrnicmp and PyOS_mystricmp that incremented pointers +beyond the end of a string. + +.. + +.. bpo: 41324 +.. date: 2020-08-10-16-05-08 +.. nonce: waZD35 +.. section: C API + +Add a minimal decimal capsule API. The API supports fast conversions +between Decimals up to 38 digits and their triple representation as a C +struct. + +.. + +.. bpo: 30155 +.. date: 2020-07-26-19-39-45 +.. nonce: rHZRJ_ +.. section: C API + +Add :c:func:`PyDateTime_DATE_GET_TZINFO` and +:c:func:`PyDateTime_TIME_GET_TZINFO` macros for accessing the ``tzinfo`` +attributes of :class:`datetime.datetime` and :class:`datetime.time` objects. + +.. + +.. bpo: 40170 +.. date: 2020-07-08-10-14-52 +.. nonce: N6Qx1i +.. section: C API + +Revert :c:func:`PyType_HasFeature` change: it reads again directly the +:c:member:`PyTypeObject.tp_flags` member when the limited C API is not used, +rather than always calling :c:func:`PyType_GetFlags` which hides +implementation details. + +.. + +.. bpo: 41123 +.. date: 2020-06-29-15-49-36 +.. nonce: wYY4E1 +.. section: C API + +Remove ``PyUnicode_AsUnicodeCopy``. + +.. + +.. bpo: 41123 +.. date: 2020-06-29-11-33-49 +.. nonce: qFevek +.. section: C API + +Removed ``PyLong_FromUnicode()``. + +.. + +.. bpo: 41123 +.. date: 2020-06-28-11-39-22 +.. nonce: sjJWjQ +.. section: C API + +Removed ``PyUnicode_GetMax()``. + +.. + +.. bpo: 41123 +.. date: 2020-06-26-13-29-25 +.. nonce: bRa1oy +.. section: C API + +Removed ``Py_UNICODE_str*`` functions manipulating ``Py_UNICODE*`` strings. + +.. + +.. bpo: 41103 +.. date: 2020-06-24-22-57-07 +.. nonce: doojgE +.. section: C API + +``PyObject_AsCharBuffer()``, ``PyObject_AsReadBuffer()``, +``PyObject_CheckReadBuffer()``, and ``PyObject_AsWriteBuffer()`` are +removed. Please migrate to new buffer protocol; :c:func:`PyObject_GetBuffer` +and :c:func:`PyBuffer_Release`. + +.. + +.. bpo: 36346 +.. date: 2020-06-17-20-31-12 +.. nonce: mwIyxi +.. section: C API + +Raises DeprecationWarning for ``PyUnicode_FromUnicode(NULL, size)`` and +``PyUnicode_FromStringAndSize(NULL, size)`` with ``size > 0``. + +.. + +.. bpo: 36346 +.. date: 2020-06-17-11-24-00 +.. nonce: fTMr3S +.. section: C API + +Mark ``Py_UNICODE_COPY``, ``Py_UNICODE_FILL``, ``PyUnicode_WSTR_LENGTH``, +``PyUnicode_FromUnicode``, ``PyUnicode_AsUnicode``, and +``PyUnicode_AsUnicodeAndSize`` as deprecated in C. Remove +``Py_UNICODE_MATCH`` which was deprecated and broken since Python 3.3. + +.. + +.. bpo: 40989 +.. date: 2020-06-15-23-17-51 +.. nonce: tlzG3r +.. section: C API + +The :c:func:`PyObject_INIT` and :c:func:`PyObject_INIT_VAR` macros become +aliases to, respectively, :c:func:`PyObject_Init` and +:c:func:`PyObject_InitVar` functions. + +.. + +.. bpo: 36020 +.. date: 2020-06-15-16-46-01 +.. nonce: djI6jw +.. section: C API + +On Windows, ``#include "pyerrors.h"`` no longer defines ``snprintf`` and +``vsnprintf`` macros. + +.. + +.. bpo: 40943 +.. date: 2020-06-10-18-37-26 +.. nonce: i4q7rK +.. section: C API + +The ``PY_SSIZE_T_CLEAN`` macro must now be defined to use +:c:func:`PyArg_ParseTuple` and :c:func:`Py_BuildValue` formats which use +``#``: ``es#``, ``et#``, ``s#``, ``u#``, ``y#``, ``z#``, ``U#`` and ``Z#``. +See :ref:`Parsing arguments and building values ` and the +:pep:`353`. + +.. + +.. bpo: 40910 +.. date: 2020-06-08-15-59-06 +.. nonce: L56oI0 +.. section: C API + +Export explicitly the :c:func:`Py_GetArgcArgv` function to the C API and +document the function. Previously, it was exported implicitly which no +longer works since Python is built with ``-fvisibility=hidden``. + +.. + +.. bpo: 40724 +.. date: 2020-06-04-08-01-23 +.. nonce: qIIdSi +.. section: C API + +Allow defining buffer slots in type specs. + +.. + +.. bpo: 40679 +.. date: 2020-06-03-17-48-13 +.. nonce: 3sgWma +.. section: C API + +Fix a ``_PyEval_EvalCode()`` crash if *qualname* argument is NULL. + +.. + +.. bpo: 40839 +.. date: 2020-06-01-20-47-49 +.. nonce: bAi52Z +.. section: C API + +Calling :c:func:`PyDict_GetItem` without :term:`GIL` held had been allowed +for historical reason. It is no longer allowed. + +.. + +.. bpo: 40826 +.. date: 2020-06-01-16-12-37 +.. nonce: zQzFoK +.. section: C API + +:c:func:`PyOS_InterruptOccurred` now fails with a fatal error if it is +called with the GIL released. + +.. + +.. bpo: 40792 +.. date: 2020-05-27-11-02-15 +.. nonce: pBw2Bb +.. section: C API + +The result of :c:func:`PyNumber_Index` now always has exact type +:class:`int`. Previously, the result could have been an instance of a +subclass of ``int``. + +.. + +.. bpo: 39573 +.. date: 2020-05-26-16-21-47 +.. nonce: depAgq +.. section: C API + +Convert :c:func:`Py_REFCNT` and :c:func:`Py_SIZE` macros to static inline +functions. They cannot be used as l-value anymore: use +:c:func:`Py_SET_REFCNT` and :c:func:`Py_SET_SIZE` to set an object reference +count and size. This change is backward incompatible on purpose, to prepare +the C API for an opaque :c:type:`PyObject` structure. + +.. + +.. bpo: 40703 +.. date: 2020-05-20-19-11-12 +.. nonce: qQXfW8 +.. section: C API + +The PyType_FromSpec*() functions no longer overwrite the type's "__module__" +attribute if it is set via "Py_tp_members" or "Py_tp_getset". + +.. + +.. bpo: 39583 +.. date: 2020-02-08-08-01-35 +.. nonce: qURKSl +.. section: C API + +Remove superfluous "extern C" declarations from ``Include/cpython/*.h``. diff --git a/Misc/NEWS.d/next/Build/2020-05-19-10-54-08.bpo-40683.W8JHrr.rst b/Misc/NEWS.d/next/Build/2020-05-19-10-54-08.bpo-40683.W8JHrr.rst deleted file mode 100644 index d57e064c03d61..0000000000000 --- a/Misc/NEWS.d/next/Build/2020-05-19-10-54-08.bpo-40683.W8JHrr.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fixed an issue where the :mod:`zoneinfo` module and its tests were not -included when Python is installed with ``make``. diff --git a/Misc/NEWS.d/next/Build/2020-06-08-19-57-05.bpo-40684.WIY2-i.rst b/Misc/NEWS.d/next/Build/2020-06-08-19-57-05.bpo-40684.WIY2-i.rst deleted file mode 100644 index 0495e5e413622..0000000000000 --- a/Misc/NEWS.d/next/Build/2020-06-08-19-57-05.bpo-40684.WIY2-i.rst +++ /dev/null @@ -1,2 +0,0 @@ -``make install`` now uses the ``PLATLIBDIR`` variable for the destination -``lib-dynload/`` directory when ``./configure --with-platlibdir`` is used. diff --git a/Misc/NEWS.d/next/Build/2020-06-15-22-14-25.bpo-36020.wbiv0P.rst b/Misc/NEWS.d/next/Build/2020-06-15-22-14-25.bpo-36020.wbiv0P.rst deleted file mode 100644 index de50dff3b1d27..0000000000000 --- a/Misc/NEWS.d/next/Build/2020-06-15-22-14-25.bpo-36020.wbiv0P.rst +++ /dev/null @@ -1,2 +0,0 @@ -The C99 functions :c:func:`snprintf` and :c:func:`vsnprintf` are now required -to build Python. diff --git a/Misc/NEWS.d/next/Build/2020-06-25-06-59-13.bpo-40204.GpD04D.rst b/Misc/NEWS.d/next/Build/2020-06-25-06-59-13.bpo-40204.GpD04D.rst deleted file mode 100644 index 25a6d751e5f45..0000000000000 --- a/Misc/NEWS.d/next/Build/2020-06-25-06-59-13.bpo-40204.GpD04D.rst +++ /dev/null @@ -1 +0,0 @@ -Pin Sphinx version to 2.3.1 in ``Doc/Makefile``. diff --git a/Misc/NEWS.d/next/Build/2020-08-24-18-34-01.bpo-41617.sKKXz7.rst b/Misc/NEWS.d/next/Build/2020-08-24-18-34-01.bpo-41617.sKKXz7.rst deleted file mode 100644 index 715eadbee896f..0000000000000 --- a/Misc/NEWS.d/next/Build/2020-08-24-18-34-01.bpo-41617.sKKXz7.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix ``pycore_bitutils.h`` header file to support old clang versions: -``__builtin_bswap16()`` is not available in LLVM clang 3.0. diff --git a/Misc/NEWS.d/next/Build/2020-09-28-21-56-51.bpo-38249.uzMCaZ.rst b/Misc/NEWS.d/next/Build/2020-09-28-21-56-51.bpo-38249.uzMCaZ.rst deleted file mode 100644 index 3e409ec2e7c20..0000000000000 --- a/Misc/NEWS.d/next/Build/2020-09-28-21-56-51.bpo-38249.uzMCaZ.rst +++ /dev/null @@ -1,2 +0,0 @@ -Update :c:macro:`Py_UNREACHABLE` to use __builtin_unreachable() if only the -compiler is able to use it. Patch by Dong-hee Na. diff --git a/Misc/NEWS.d/next/C API/2020-02-08-08-01-35.bpo-39583.qURKSl.rst b/Misc/NEWS.d/next/C API/2020-02-08-08-01-35.bpo-39583.qURKSl.rst deleted file mode 100644 index 1c9f44f7443c1..0000000000000 --- a/Misc/NEWS.d/next/C API/2020-02-08-08-01-35.bpo-39583.qURKSl.rst +++ /dev/null @@ -1 +0,0 @@ -Remove superfluous "extern C" declarations from ``Include/cpython/*.h``. diff --git a/Misc/NEWS.d/next/C API/2020-05-20-19-11-12.bpo-40703.qQXfW8.rst b/Misc/NEWS.d/next/C API/2020-05-20-19-11-12.bpo-40703.qQXfW8.rst deleted file mode 100644 index 5385a2d8dce45..0000000000000 --- a/Misc/NEWS.d/next/C API/2020-05-20-19-11-12.bpo-40703.qQXfW8.rst +++ /dev/null @@ -1,2 +0,0 @@ -The PyType_FromSpec*() functions no longer overwrite the type's "__module__" attribute -if it is set via "Py_tp_members" or "Py_tp_getset". diff --git a/Misc/NEWS.d/next/C API/2020-05-26-16-21-47.bpo-39573.depAgq.rst b/Misc/NEWS.d/next/C API/2020-05-26-16-21-47.bpo-39573.depAgq.rst deleted file mode 100644 index f8f675cebcac7..0000000000000 --- a/Misc/NEWS.d/next/C API/2020-05-26-16-21-47.bpo-39573.depAgq.rst +++ /dev/null @@ -1,5 +0,0 @@ -Convert :c:func:`Py_REFCNT` and :c:func:`Py_SIZE` macros to static inline -functions. They cannot be used as l-value anymore: use -:c:func:`Py_SET_REFCNT` and :c:func:`Py_SET_SIZE` to set an object reference -count and size. This change is backward incompatible on purpose, to prepare -the C API for an opaque :c:type:`PyObject` structure. diff --git a/Misc/NEWS.d/next/C API/2020-05-27-11-02-15.bpo-40792.pBw2Bb.rst b/Misc/NEWS.d/next/C API/2020-05-27-11-02-15.bpo-40792.pBw2Bb.rst deleted file mode 100644 index 4cfe09bc774af..0000000000000 --- a/Misc/NEWS.d/next/C API/2020-05-27-11-02-15.bpo-40792.pBw2Bb.rst +++ /dev/null @@ -1,2 +0,0 @@ -The result of :c:func:`PyNumber_Index` now always has exact type :class:`int`. -Previously, the result could have been an instance of a subclass of ``int``. diff --git a/Misc/NEWS.d/next/C API/2020-06-01-16-12-37.bpo-40826.zQzFoK.rst b/Misc/NEWS.d/next/C API/2020-06-01-16-12-37.bpo-40826.zQzFoK.rst deleted file mode 100644 index 0d7a36c3eb401..0000000000000 --- a/Misc/NEWS.d/next/C API/2020-06-01-16-12-37.bpo-40826.zQzFoK.rst +++ /dev/null @@ -1,2 +0,0 @@ -:c:func:`PyOS_InterruptOccurred` now fails with a fatal error if it is -called with the GIL released. diff --git a/Misc/NEWS.d/next/C API/2020-06-01-20-47-49.bpo-40839.bAi52Z.rst b/Misc/NEWS.d/next/C API/2020-06-01-20-47-49.bpo-40839.bAi52Z.rst deleted file mode 100644 index 5de2f40c14eca..0000000000000 --- a/Misc/NEWS.d/next/C API/2020-06-01-20-47-49.bpo-40839.bAi52Z.rst +++ /dev/null @@ -1,2 +0,0 @@ -Calling :c:func:`PyDict_GetItem` without :term:`GIL` held had been allowed for -historical reason. It is no longer allowed. diff --git a/Misc/NEWS.d/next/C API/2020-06-03-17-48-13.bpo-40679.3sgWma.rst b/Misc/NEWS.d/next/C API/2020-06-03-17-48-13.bpo-40679.3sgWma.rst deleted file mode 100644 index ccf908cef1914..0000000000000 --- a/Misc/NEWS.d/next/C API/2020-06-03-17-48-13.bpo-40679.3sgWma.rst +++ /dev/null @@ -1 +0,0 @@ -Fix a ``_PyEval_EvalCode()`` crash if *qualname* argument is NULL. diff --git a/Misc/NEWS.d/next/C API/2020-06-04-08-01-23.bpo-40724.qIIdSi.rst b/Misc/NEWS.d/next/C API/2020-06-04-08-01-23.bpo-40724.qIIdSi.rst deleted file mode 100644 index 82793dbf7ad5f..0000000000000 --- a/Misc/NEWS.d/next/C API/2020-06-04-08-01-23.bpo-40724.qIIdSi.rst +++ /dev/null @@ -1 +0,0 @@ -Allow defining buffer slots in type specs. diff --git a/Misc/NEWS.d/next/C API/2020-06-08-15-59-06.bpo-40910.L56oI0.rst b/Misc/NEWS.d/next/C API/2020-06-08-15-59-06.bpo-40910.L56oI0.rst deleted file mode 100644 index 1d0cb0b0235bf..0000000000000 --- a/Misc/NEWS.d/next/C API/2020-06-08-15-59-06.bpo-40910.L56oI0.rst +++ /dev/null @@ -1,3 +0,0 @@ -Export explicitly the :c:func:`Py_GetArgcArgv` function to the C API and -document the function. Previously, it was exported implicitly which no -longer works since Python is built with ``-fvisibility=hidden``. diff --git a/Misc/NEWS.d/next/C API/2020-06-10-18-37-26.bpo-40943.i4q7rK.rst b/Misc/NEWS.d/next/C API/2020-06-10-18-37-26.bpo-40943.i4q7rK.rst deleted file mode 100644 index 360ddae34cb96..0000000000000 --- a/Misc/NEWS.d/next/C API/2020-06-10-18-37-26.bpo-40943.i4q7rK.rst +++ /dev/null @@ -1,5 +0,0 @@ -The ``PY_SSIZE_T_CLEAN`` macro must now be defined to use -:c:func:`PyArg_ParseTuple` and :c:func:`Py_BuildValue` formats which use ``#``: -``es#``, ``et#``, ``s#``, ``u#``, ``y#``, ``z#``, ``U#`` and ``Z#``. -See :ref:`Parsing arguments and building values ` and the -:pep:`353`. diff --git a/Misc/NEWS.d/next/C API/2020-06-15-16-46-01.bpo-36020.djI6jw.rst b/Misc/NEWS.d/next/C API/2020-06-15-16-46-01.bpo-36020.djI6jw.rst deleted file mode 100644 index 1f91dce4608d3..0000000000000 --- a/Misc/NEWS.d/next/C API/2020-06-15-16-46-01.bpo-36020.djI6jw.rst +++ /dev/null @@ -1,2 +0,0 @@ -On Windows, ``#include "pyerrors.h"`` no longer defines ``snprintf`` and -``vsnprintf`` macros. diff --git a/Misc/NEWS.d/next/C API/2020-06-15-23-17-51.bpo-40989.tlzG3r.rst b/Misc/NEWS.d/next/C API/2020-06-15-23-17-51.bpo-40989.tlzG3r.rst deleted file mode 100644 index 1be473d142760..0000000000000 --- a/Misc/NEWS.d/next/C API/2020-06-15-23-17-51.bpo-40989.tlzG3r.rst +++ /dev/null @@ -1,3 +0,0 @@ -The :c:func:`PyObject_INIT` and :c:func:`PyObject_INIT_VAR` macros become -aliases to, respectively, :c:func:`PyObject_Init` and -:c:func:`PyObject_InitVar` functions. diff --git a/Misc/NEWS.d/next/C API/2020-06-17-11-24-00.bpo-36346.fTMr3S.rst b/Misc/NEWS.d/next/C API/2020-06-17-11-24-00.bpo-36346.fTMr3S.rst deleted file mode 100644 index 1e448303a853c..0000000000000 --- a/Misc/NEWS.d/next/C API/2020-06-17-11-24-00.bpo-36346.fTMr3S.rst +++ /dev/null @@ -1,4 +0,0 @@ -Mark ``Py_UNICODE_COPY``, ``Py_UNICODE_FILL``, ``PyUnicode_WSTR_LENGTH``, -``PyUnicode_FromUnicode``, ``PyUnicode_AsUnicode``, -and ``PyUnicode_AsUnicodeAndSize`` as deprecated in C. Remove ``Py_UNICODE_MATCH`` -which was deprecated and broken since Python 3.3. diff --git a/Misc/NEWS.d/next/C API/2020-06-17-20-31-12.bpo-36346.mwIyxi.rst b/Misc/NEWS.d/next/C API/2020-06-17-20-31-12.bpo-36346.mwIyxi.rst deleted file mode 100644 index 9b0400399beb9..0000000000000 --- a/Misc/NEWS.d/next/C API/2020-06-17-20-31-12.bpo-36346.mwIyxi.rst +++ /dev/null @@ -1,2 +0,0 @@ -Raises DeprecationWarning for ``PyUnicode_FromUnicode(NULL, size)`` and -``PyUnicode_FromStringAndSize(NULL, size)`` with ``size > 0``. diff --git a/Misc/NEWS.d/next/C API/2020-06-24-22-57-07.bpo-41103.doojgE.rst b/Misc/NEWS.d/next/C API/2020-06-24-22-57-07.bpo-41103.doojgE.rst deleted file mode 100644 index 082b77b9035cb..0000000000000 --- a/Misc/NEWS.d/next/C API/2020-06-24-22-57-07.bpo-41103.doojgE.rst +++ /dev/null @@ -1,4 +0,0 @@ -``PyObject_AsCharBuffer()``, ``PyObject_AsReadBuffer()``, -``PyObject_CheckReadBuffer()``, and ``PyObject_AsWriteBuffer()`` are -removed. Please migrate to new buffer protocol; :c:func:`PyObject_GetBuffer` -and :c:func:`PyBuffer_Release`. diff --git a/Misc/NEWS.d/next/C API/2020-06-26-13-29-25.bpo-41123.bRa1oy.rst b/Misc/NEWS.d/next/C API/2020-06-26-13-29-25.bpo-41123.bRa1oy.rst deleted file mode 100644 index 1261a8708d6c9..0000000000000 --- a/Misc/NEWS.d/next/C API/2020-06-26-13-29-25.bpo-41123.bRa1oy.rst +++ /dev/null @@ -1 +0,0 @@ -Removed ``Py_UNICODE_str*`` functions manipulating ``Py_UNICODE*`` strings. diff --git a/Misc/NEWS.d/next/C API/2020-06-28-11-39-22.bpo-41123.sjJWjQ.rst b/Misc/NEWS.d/next/C API/2020-06-28-11-39-22.bpo-41123.sjJWjQ.rst deleted file mode 100644 index 97331458c6ab9..0000000000000 --- a/Misc/NEWS.d/next/C API/2020-06-28-11-39-22.bpo-41123.sjJWjQ.rst +++ /dev/null @@ -1 +0,0 @@ -Removed ``PyUnicode_GetMax()``. diff --git a/Misc/NEWS.d/next/C API/2020-06-29-11-33-49.bpo-41123.qFevek.rst b/Misc/NEWS.d/next/C API/2020-06-29-11-33-49.bpo-41123.qFevek.rst deleted file mode 100644 index 1f5813594b0ec..0000000000000 --- a/Misc/NEWS.d/next/C API/2020-06-29-11-33-49.bpo-41123.qFevek.rst +++ /dev/null @@ -1 +0,0 @@ -Removed ``PyLong_FromUnicode()``. diff --git a/Misc/NEWS.d/next/C API/2020-06-29-15-49-36.bpo-41123.wYY4E1.rst b/Misc/NEWS.d/next/C API/2020-06-29-15-49-36.bpo-41123.wYY4E1.rst deleted file mode 100644 index 74ac45462773e..0000000000000 --- a/Misc/NEWS.d/next/C API/2020-06-29-15-49-36.bpo-41123.wYY4E1.rst +++ /dev/null @@ -1 +0,0 @@ -Remove ``PyUnicode_AsUnicodeCopy``. diff --git a/Misc/NEWS.d/next/C API/2020-07-08-10-14-52.bpo-40170.N6Qx1i.rst b/Misc/NEWS.d/next/C API/2020-07-08-10-14-52.bpo-40170.N6Qx1i.rst deleted file mode 100644 index 760a3ff4d17b4..0000000000000 --- a/Misc/NEWS.d/next/C API/2020-07-08-10-14-52.bpo-40170.N6Qx1i.rst +++ /dev/null @@ -1,4 +0,0 @@ -Revert :c:func:`PyType_HasFeature` change: it reads again directly the -:c:member:`PyTypeObject.tp_flags` member when the limited C API is not used, -rather than always calling :c:func:`PyType_GetFlags` which hides implementation -details. diff --git a/Misc/NEWS.d/next/C API/2020-07-26-19-39-45.bpo-30155.rHZRJ_.rst b/Misc/NEWS.d/next/C API/2020-07-26-19-39-45.bpo-30155.rHZRJ_.rst deleted file mode 100644 index a276759da7933..0000000000000 --- a/Misc/NEWS.d/next/C API/2020-07-26-19-39-45.bpo-30155.rHZRJ_.rst +++ /dev/null @@ -1,3 +0,0 @@ -Add :c:func:`PyDateTime_DATE_GET_TZINFO` and -:c:func:`PyDateTime_TIME_GET_TZINFO` macros for accessing the ``tzinfo`` -attributes of :class:`datetime.datetime` and :class:`datetime.time` objects. diff --git a/Misc/NEWS.d/next/C API/2020-08-10-16-05-08.bpo-41324.waZD35.rst b/Misc/NEWS.d/next/C API/2020-08-10-16-05-08.bpo-41324.waZD35.rst deleted file mode 100644 index e09332ab11e1d..0000000000000 --- a/Misc/NEWS.d/next/C API/2020-08-10-16-05-08.bpo-41324.waZD35.rst +++ /dev/null @@ -1,3 +0,0 @@ -Add a minimal decimal capsule API. The API supports fast conversions -between Decimals up to 38 digits and their triple representation as a C -struct. diff --git a/Misc/NEWS.d/next/C API/2020-08-12-17-09-06.bpo-41524.u6Xfr2.rst b/Misc/NEWS.d/next/C API/2020-08-12-17-09-06.bpo-41524.u6Xfr2.rst deleted file mode 100644 index 4704e29be29bb..0000000000000 --- a/Misc/NEWS.d/next/C API/2020-08-12-17-09-06.bpo-41524.u6Xfr2.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix bug in PyOS_mystrnicmp and PyOS_mystricmp that incremented -pointers beyond the end of a string. \ No newline at end of file diff --git a/Misc/NEWS.d/next/C API/2020-09-01-23-39-45.bpo-41689.zxHbLB.rst b/Misc/NEWS.d/next/C API/2020-09-01-23-39-45.bpo-41689.zxHbLB.rst deleted file mode 100644 index 44cf58a4b0638..0000000000000 --- a/Misc/NEWS.d/next/C API/2020-09-01-23-39-45.bpo-41689.zxHbLB.rst +++ /dev/null @@ -1,2 +0,0 @@ -Types created with :c:func:`PyType_FromSpec` now make any signature in their -``tp_doc`` slot accessible from ``__text_signature__``. diff --git a/Misc/NEWS.d/next/C API/2020-09-22-14-47-12.bpo-41834.nrOrDU.rst b/Misc/NEWS.d/next/C API/2020-09-22-14-47-12.bpo-41834.nrOrDU.rst deleted file mode 100644 index 07043dce5a627..0000000000000 --- a/Misc/NEWS.d/next/C API/2020-09-22-14-47-12.bpo-41834.nrOrDU.rst +++ /dev/null @@ -1,3 +0,0 @@ -Remove the ``_Py_CheckRecursionLimit`` variable: it has been replaced by -``ceval.recursion_limit`` of the :c:type:`PyInterpreterState` -structure. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/C API/2020-09-27-20-43-16.bpo-41842.bCakAj.rst b/Misc/NEWS.d/next/C API/2020-09-27-20-43-16.bpo-41842.bCakAj.rst deleted file mode 100644 index 116d08f490859..0000000000000 --- a/Misc/NEWS.d/next/C API/2020-09-27-20-43-16.bpo-41842.bCakAj.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add :c:func:`PyCodec_Unregister` function to unregister a codec search -function. diff --git a/Misc/NEWS.d/next/C API/2020-10-02-00-57-34.bpo-41692.fDScsF.rst b/Misc/NEWS.d/next/C API/2020-10-02-00-57-34.bpo-41692.fDScsF.rst deleted file mode 100644 index 1be37c6572271..0000000000000 --- a/Misc/NEWS.d/next/C API/2020-10-02-00-57-34.bpo-41692.fDScsF.rst +++ /dev/null @@ -1,3 +0,0 @@ -The ``PyUnicode_InternImmortal()`` function is now deprecated and will be -removed in Python 3.12: use :c:func:`PyUnicode_InternInPlace` instead. -Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/C API/2020-10-05-01-25-23.bpo-41936.1gb5ra.rst b/Misc/NEWS.d/next/C API/2020-10-05-01-25-23.bpo-41936.1gb5ra.rst deleted file mode 100644 index 6461353308616..0000000000000 --- a/Misc/NEWS.d/next/C API/2020-10-05-01-25-23.bpo-41936.1gb5ra.rst +++ /dev/null @@ -1,3 +0,0 @@ -Removed undocumented macros ``Py_ALLOW_RECURSION`` and -``Py_END_ALLOW_RECURSION`` and the ``recursion_critical`` field of the -:c:type:`PyInterpreterState` structure. diff --git a/Misc/NEWS.d/next/Core and Builtins/2018-03-15-11-51-36.bpo-26680.wOWYps.rst b/Misc/NEWS.d/next/Core and Builtins/2018-03-15-11-51-36.bpo-26680.wOWYps.rst deleted file mode 100644 index 93325ffffcbfc..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2018-03-15-11-51-36.bpo-26680.wOWYps.rst +++ /dev/null @@ -1,2 +0,0 @@ -The int type now supports the x.is_integer() method for compatibility with -float. diff --git a/Misc/NEWS.d/next/Core and Builtins/2018-08-29-15-57-07.bpo-19569.RGu2Kb.rst b/Misc/NEWS.d/next/Core and Builtins/2018-08-29-15-57-07.bpo-19569.RGu2Kb.rst deleted file mode 100644 index 1b76bd8e247fc..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2018-08-29-15-57-07.bpo-19569.RGu2Kb.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add the private macros ``_Py_COMP_DIAG_PUSH``, -``_Py_COMP_DIAG_IGNORE_DEPR_DECLS``, and ``_Py_COMP_DIAG_POP``. diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-05-25-05-27-39.bpo-36982.0UHgfB.rst b/Misc/NEWS.d/next/Core and Builtins/2019-05-25-05-27-39.bpo-36982.0UHgfB.rst deleted file mode 100644 index f105f1857d487..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2019-05-25-05-27-39.bpo-36982.0UHgfB.rst +++ /dev/null @@ -1 +0,0 @@ -Use ncurses extended color functions when available to support terminals with 256 colors, and add the new function :func:`curses.has_extended_color_support` to indicate whether extended color support is provided by the underlying ncurses library. \ No newline at end of file diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-06-02-11-29-15.bpo-29882.AkRzjb.rst b/Misc/NEWS.d/next/Core and Builtins/2019-06-02-11-29-15.bpo-29882.AkRzjb.rst deleted file mode 100644 index 240b5680b36a2..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2019-06-02-11-29-15.bpo-29882.AkRzjb.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add :meth:`int.bit_count()`, counting the number of ones in the binary -representation of an integer. Patch by Niklas Fiekas. diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-09-01-14-26-02.bpo-37999.XPl6dn.rst b/Misc/NEWS.d/next/Core and Builtins/2019-09-01-14-26-02.bpo-37999.XPl6dn.rst deleted file mode 100644 index 8d7e9369af4c6..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2019-09-01-14-26-02.bpo-37999.XPl6dn.rst +++ /dev/null @@ -1,5 +0,0 @@ -Builtin and extension functions that take integer arguments no longer accept -:class:`~decimal.Decimal`\ s, :class:`~fractions.Fraction`\ s and other -objects that can be converted to integers only with a loss (e.g. that have -the :meth:`~object.__int__` method but do not have the -:meth:`~object.__index__` method). diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-04-05-02-35-08.bpo-1635741.Kfe9fT.rst b/Misc/NEWS.d/next/Core and Builtins/2020-04-05-02-35-08.bpo-1635741.Kfe9fT.rst deleted file mode 100644 index 956d0b68a8dfb..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-04-05-02-35-08.bpo-1635741.Kfe9fT.rst +++ /dev/null @@ -1 +0,0 @@ -Port :mod:`_lzma` to multiphase initialization. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-04-10-23-54-57.bpo-1635741.ZURqoN.rst b/Misc/NEWS.d/next/Core and Builtins/2020-04-10-23-54-57.bpo-1635741.ZURqoN.rst deleted file mode 100644 index cb849fb9b4430..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-04-10-23-54-57.bpo-1635741.ZURqoN.rst +++ /dev/null @@ -1 +0,0 @@ -Port :mod:`mmap` to multiphase initialization. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-04-11-13-07-49.bpo-4022.Ctpn_F.rst b/Misc/NEWS.d/next/Core and Builtins/2020-04-11-13-07-49.bpo-4022.Ctpn_F.rst deleted file mode 100644 index a13a8e8822683..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-04-11-13-07-49.bpo-4022.Ctpn_F.rst +++ /dev/null @@ -1 +0,0 @@ -Improve performance of generators by not raising internal StopIteration. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-05-03-22-26-00.bpo-29590.aRz3l7.rst b/Misc/NEWS.d/next/Core and Builtins/2020-05-03-22-26-00.bpo-29590.aRz3l7.rst deleted file mode 100644 index 2570c4f2c7c0f..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-05-03-22-26-00.bpo-29590.aRz3l7.rst +++ /dev/null @@ -1,2 +0,0 @@ -Make the stack trace correct after calling :meth:`generator.throw` -on a generator that has yielded from a ``yield from``. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-05-19-19-39-49.bpo-40679.SVzz9p.rst b/Misc/NEWS.d/next/Core and Builtins/2020-05-19-19-39-49.bpo-40679.SVzz9p.rst deleted file mode 100644 index 2d0a432b6fa69..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-05-19-19-39-49.bpo-40679.SVzz9p.rst +++ /dev/null @@ -1,2 +0,0 @@ -Certain :exc:`TypeError` messages about missing or extra arguments now include the function's -:term:`qualified name`. Patch by Dennis Sweeney. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-05-20-01-17-34.bpo-40521.wvAehI.rst b/Misc/NEWS.d/next/Core and Builtins/2020-05-20-01-17-34.bpo-40521.wvAehI.rst deleted file mode 100644 index 43226931ccc88..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-05-20-01-17-34.bpo-40521.wvAehI.rst +++ /dev/null @@ -1,9 +0,0 @@ -Each interpreter now its has own free lists, singletons and caches: - -* Free lists: float, tuple, list, dict, frame, context, - asynchronous generator, MemoryError. -* Singletons: empty tuple, empty bytes string, empty Unicode string, - single byte character, single Unicode (latin1) character. -* Slice cache. - -They are no longer shared by all interpreters. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-05-21-01-54-00.bpo-40696.u3n8Wx.rst b/Misc/NEWS.d/next/Core and Builtins/2020-05-21-01-54-00.bpo-40696.u3n8Wx.rst deleted file mode 100644 index f99bdea2e3177..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-05-21-01-54-00.bpo-40696.u3n8Wx.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix a hang that can arise after :meth:`generator.throw` due to a cycle -in the exception context chain. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-05-22-00-34-34.bpo-39573.QO2QHj.rst b/Misc/NEWS.d/next/Core and Builtins/2020-05-22-00-34-34.bpo-39573.QO2QHj.rst deleted file mode 100644 index 2430030304267..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-05-22-00-34-34.bpo-39573.QO2QHj.rst +++ /dev/null @@ -1,2 +0,0 @@ -:c:func:`Py_TYPE()` is changed to the inline static function. Patch by -Dong-hee Na. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-05-23-01-15-51.bpo-40217.jZsHTc.rst b/Misc/NEWS.d/next/Core and Builtins/2020-05-23-01-15-51.bpo-40217.jZsHTc.rst deleted file mode 100644 index b13e8eeb0634f..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-05-23-01-15-51.bpo-40217.jZsHTc.rst +++ /dev/null @@ -1,4 +0,0 @@ -Instances of types created with :c:func:`PyType_FromSpecWithBases` will no -longer automatically visit their class object when traversing references in -the garbage collector. The user is expected to manually visit the object's -class. Patch by Pablo Galindo. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-05-24-02-42-26.bpo-40750.ZmO9Ev.rst b/Misc/NEWS.d/next/Core and Builtins/2020-05-24-02-42-26.bpo-40750.ZmO9Ev.rst deleted file mode 100644 index 4032b80169035..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-05-24-02-42-26.bpo-40750.ZmO9Ev.rst +++ /dev/null @@ -1 +0,0 @@ -Support the "-d" debug flag in the new PEG parser. Patch by Pablo Galindo diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-05-25-21-49-11.bpo-38964.lrml90.rst b/Misc/NEWS.d/next/Core and Builtins/2020-05-25-21-49-11.bpo-38964.lrml90.rst deleted file mode 100644 index 1200764306946..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-05-25-21-49-11.bpo-38964.lrml90.rst +++ /dev/null @@ -1 +0,0 @@ -When there's a :exc:`SyntaxError` in the expression part of an fstring, the filename attribute of the :exc:`SyntaxError` gets correctly set to the name of the file the fstring resides in. \ No newline at end of file diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-05-26-17-43-58.bpo-40780.3Ckdgm.rst b/Misc/NEWS.d/next/Core and Builtins/2020-05-26-17-43-58.bpo-40780.3Ckdgm.rst deleted file mode 100644 index ed6020c2e2355..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-05-26-17-43-58.bpo-40780.3Ckdgm.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix a corner case where g-style string formatting of a float failed to -remove trailing zeros. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-05-27-22-37-58.bpo-40792.WEDqqU.rst b/Misc/NEWS.d/next/Core and Builtins/2020-05-27-22-37-58.bpo-40792.WEDqqU.rst deleted file mode 100644 index 5986a221f5b37..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-05-27-22-37-58.bpo-40792.WEDqqU.rst +++ /dev/null @@ -1,3 +0,0 @@ -Attributes ``start``, ``stop`` and ``step`` of the :class:`range` object now -always has exact type :class:`int`. Previously, they could have been an -instance of a subclass of ``int``. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-05-30-14-37-18.bpo-40824.XR3V5s.rst b/Misc/NEWS.d/next/Core and Builtins/2020-05-30-14-37-18.bpo-40824.XR3V5s.rst deleted file mode 100644 index 73c593c04a0da..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-05-30-14-37-18.bpo-40824.XR3V5s.rst +++ /dev/null @@ -1,4 +0,0 @@ -Unexpected errors in calling the ``__iter__`` method are no longer masked by -``TypeError`` in the :keyword:`in` operator and functions -:func:`~operator.contains`, :func:`~operator.indexOf` and -:func:`~operator.countOf` of the :mod:`operator` module. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-05-30-23-18-35.bpo-19468.S-TA7p.rst b/Misc/NEWS.d/next/Core and Builtins/2020-05-30-23-18-35.bpo-19468.S-TA7p.rst deleted file mode 100644 index e35750e37f4da..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-05-30-23-18-35.bpo-19468.S-TA7p.rst +++ /dev/null @@ -1,2 +0,0 @@ -Delete unnecessary instance check in importlib.reload(). -Patch by Furkan ?nder. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-05-30-23-23-35.bpo-1635741.0D-laM.rst b/Misc/NEWS.d/next/Core and Builtins/2020-05-30-23-23-35.bpo-1635741.0D-laM.rst deleted file mode 100644 index cd2bcb6e60877..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-05-30-23-23-35.bpo-1635741.0D-laM.rst +++ /dev/null @@ -1 +0,0 @@ -Port :mod:`fcntl` to multiphase initialization. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-06-01-20-31-07.bpo-40826.XCI4M2.rst b/Misc/NEWS.d/next/Core and Builtins/2020-06-01-20-31-07.bpo-40826.XCI4M2.rst deleted file mode 100644 index a03ed180eb952..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-06-01-20-31-07.bpo-40826.XCI4M2.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix GIL usage in :c:func:`PyOS_Readline`: lock the GIL to set an exception -and pass the Python thread state when checking if there is a pending signal. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-06-03-13-53-24.bpo-40854.O6vfQU.rst b/Misc/NEWS.d/next/Core and Builtins/2020-06-03-13-53-24.bpo-40854.O6vfQU.rst deleted file mode 100644 index 6ef4ed5af7318..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-06-03-13-53-24.bpo-40854.O6vfQU.rst +++ /dev/null @@ -1 +0,0 @@ -Allow overriding :data:`sys.platlibdir` via a new :envvar:`PYTHONPLATLIBDIR` environment variable. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-06-05-12-48-28.bpo-40870.9cd2sk.rst b/Misc/NEWS.d/next/Core and Builtins/2020-06-05-12-48-28.bpo-40870.9cd2sk.rst deleted file mode 100644 index 8e943a29f337f..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-06-05-12-48-28.bpo-40870.9cd2sk.rst +++ /dev/null @@ -1,2 +0,0 @@ -Raise :exc:`ValueError` when validating custom AST's where the constants -``True``, ``False`` and ``None`` are used within a :class:`ast.Name` node. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-06-05-23-25-00.bpo-40883.M6sQ-Q.rst b/Misc/NEWS.d/next/Core and Builtins/2020-06-05-23-25-00.bpo-40883.M6sQ-Q.rst deleted file mode 100644 index ebeb0cc60d16b..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-06-05-23-25-00.bpo-40883.M6sQ-Q.rst +++ /dev/null @@ -1 +0,0 @@ -Fix memory leak in when parsing f-strings in the new parser. Patch by Pablo Galindo \ No newline at end of file diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-06-06-00-23-19.bpo-40880.fjdzSh.rst b/Misc/NEWS.d/next/Core and Builtins/2020-06-06-00-23-19.bpo-40880.fjdzSh.rst deleted file mode 100644 index ab42f5c205f81..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-06-06-00-23-19.bpo-40880.fjdzSh.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix invalid memory read in the new parser when checking newlines in string -literals. Patch by Pablo Galindo. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-06-07-22-50-10.bpo-40903.7dWejS.rst b/Misc/NEWS.d/next/Core and Builtins/2020-06-07-22-50-10.bpo-40903.7dWejS.rst deleted file mode 100644 index 5ee72c14ad352..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-06-07-22-50-10.bpo-40903.7dWejS.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed a possible segfault in the new PEG parser when producing error messages for invalid assignments of the form :code:`p=p=`. Patch by Pablo Galindo \ No newline at end of file diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-06-08-01-08-57.bpo-40904.76qQzo.rst b/Misc/NEWS.d/next/Core and Builtins/2020-06-08-01-08-57.bpo-40904.76qQzo.rst deleted file mode 100644 index 09009b18c63a3..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-06-08-01-08-57.bpo-40904.76qQzo.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix possible segfault in the new PEG parser when parsing f-string containing -yield statements with no value (:code:`f"{yield}"`). Patch by Pablo Galindo diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-06-08-22-46-33.bpo-40889.vIBl-W.rst b/Misc/NEWS.d/next/Core and Builtins/2020-06-08-22-46-33.bpo-40889.vIBl-W.rst deleted file mode 100644 index 0ab1a261e3e6e..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-06-08-22-46-33.bpo-40889.vIBl-W.rst +++ /dev/null @@ -1 +0,0 @@ -Improved the performance of symmetric difference operations on dictionary item views. Patch by Dennis Sweeney. \ No newline at end of file diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-06-09-00-20-13.bpo-40890.LoRV-g.rst b/Misc/NEWS.d/next/Core and Builtins/2020-06-09-00-20-13.bpo-40890.LoRV-g.rst deleted file mode 100644 index eaefc894a13a5..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-06-09-00-20-13.bpo-40890.LoRV-g.rst +++ /dev/null @@ -1 +0,0 @@ -Each dictionary view now has a ``mapping`` attribute that provides a :class:`types.MappingProxyType` wrapping the original dictionary. Patch contributed by Dennis Sweeney. \ No newline at end of file diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-06-09-23-52-32.bpo-40847.4XAACw.rst b/Misc/NEWS.d/next/Core and Builtins/2020-06-09-23-52-32.bpo-40847.4XAACw.rst deleted file mode 100644 index 0b489f2483215..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-06-09-23-52-32.bpo-40847.4XAACw.rst +++ /dev/null @@ -1,4 +0,0 @@ -Fix a bug where a line with only a line continuation character is not considered a blank line at tokenizer level. -In such cases, more than a single `NEWLINE` token was emitted. The old parser was working around the issue, -but the new parser threw a :exc:`SyntaxError` for valid input due to this. For example, an empty line following -a line continuation character was interpreted as a :exc:`SyntaxError`. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-06-10-11-27-15.bpo-40939.DO-wAI.rst b/Misc/NEWS.d/next/Core and Builtins/2020-06-10-11-27-15.bpo-40939.DO-wAI.rst deleted file mode 100644 index b12985d081604..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-06-10-11-27-15.bpo-40939.DO-wAI.rst +++ /dev/null @@ -1 +0,0 @@ -Remove the old parser, the :mod:`parser` module and all associated support code, command-line options and environment variables. Patch by Pablo Galindo. \ No newline at end of file diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-06-11-16-06-49.bpo-40947.72cZcR.rst b/Misc/NEWS.d/next/Core and Builtins/2020-06-11-16-06-49.bpo-40947.72cZcR.rst deleted file mode 100644 index e7dfe06531a4d..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-06-11-16-06-49.bpo-40947.72cZcR.rst +++ /dev/null @@ -1,2 +0,0 @@ -The Python :ref:`Path Configuration ` now takes -:c:member:`PyConfig.platlibdir` in account. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-06-12-00-12-28.bpo-40950.tzMy7m.rst b/Misc/NEWS.d/next/Core and Builtins/2020-06-12-00-12-28.bpo-40950.tzMy7m.rst deleted file mode 100644 index 925b5790f73f3..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-06-12-00-12-28.bpo-40950.tzMy7m.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add a state to the :mod:`nis` module (:pep:`3121`) and apply -the multiphase initialization. Patch by Dong-hee Na. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-06-12-12-21-54.bpo-40957.Z8n6I6.rst b/Misc/NEWS.d/next/Core and Builtins/2020-06-12-12-21-54.bpo-40957.Z8n6I6.rst deleted file mode 100644 index f99c374f94aac..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-06-12-12-21-54.bpo-40957.Z8n6I6.rst +++ /dev/null @@ -1 +0,0 @@ -Fix refleak in _Py_fopen_obj() when PySys_Audit() fails diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-06-12-22-56-17.bpo-1635741.mmlp3Q.rst b/Misc/NEWS.d/next/Core and Builtins/2020-06-12-22-56-17.bpo-1635741.mmlp3Q.rst deleted file mode 100644 index ae12d25baa3ad..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-06-12-22-56-17.bpo-1635741.mmlp3Q.rst +++ /dev/null @@ -1 +0,0 @@ -Port :mod:`_dbm` to multiphase initialization. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-06-15-01-20-44.bpo-40958.7O2Wh1.rst b/Misc/NEWS.d/next/Core and Builtins/2020-06-15-01-20-44.bpo-40958.7O2Wh1.rst deleted file mode 100644 index 8e36897948f9b..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-06-15-01-20-44.bpo-40958.7O2Wh1.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix a possible buffer overflow in the PEG parser when gathering information -for emitting syntax errors. Patch by Pablo Galindo. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-06-15-16-29-55.bpo-40985.IIN_xX.rst b/Misc/NEWS.d/next/Core and Builtins/2020-06-15-16-29-55.bpo-40985.IIN_xX.rst deleted file mode 100644 index e07134c7166ad..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-06-15-16-29-55.bpo-40985.IIN_xX.rst +++ /dev/null @@ -1 +0,0 @@ -Fix a bug that caused the :exc:`SyntaxError` text to be empty when a file ends with a line ending in a line continuation character (i.e. backslash). The error text should contain the text of the last line. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-06-17-00-52-21.bpo-1635741.61iyYh.rst b/Misc/NEWS.d/next/Core and Builtins/2020-06-17-00-52-21.bpo-1635741.61iyYh.rst deleted file mode 100644 index cffe70dd71eaf..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-06-17-00-52-21.bpo-1635741.61iyYh.rst +++ /dev/null @@ -1 +0,0 @@ -Port :mod:`_gdbm` to multiphase initialization. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-06-17-10-27-17.bpo-40636.MYaCIe.rst b/Misc/NEWS.d/next/Core and Builtins/2020-06-17-10-27-17.bpo-40636.MYaCIe.rst deleted file mode 100644 index ba26ad9373ce3..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-06-17-10-27-17.bpo-40636.MYaCIe.rst +++ /dev/null @@ -1,3 +0,0 @@ -:func:`zip` now supports :pep:`618`'s ``strict`` parameter, which raises a -:exc:`ValueError` if the arguments are exhausted at different lengths. -Patch by Brandt Bucher. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-06-18-00-07-09.bpo-41006.H-wN-d.rst b/Misc/NEWS.d/next/Core and Builtins/2020-06-18-00-07-09.bpo-41006.H-wN-d.rst deleted file mode 100644 index 4593e6bb89a9e..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-06-18-00-07-09.bpo-41006.H-wN-d.rst +++ /dev/null @@ -1,2 +0,0 @@ -The ``encodings.latin_1`` module is no longer imported at startup. Now it is -only imported when it is the filesystem encoding or the stdio encoding. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-06-18-19-04-30.bpo-40077._yI-ax.rst b/Misc/NEWS.d/next/Core and Builtins/2020-06-18-19-04-30.bpo-40077._yI-ax.rst deleted file mode 100644 index 2e0258a7b369d..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-06-18-19-04-30.bpo-40077._yI-ax.rst +++ /dev/null @@ -1 +0,0 @@ -Convert :mod:`_bz2` to use :c:func:`PyType_FromSpec`. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-06-20-16-59-02.bpo-40939.6810Ak.rst b/Misc/NEWS.d/next/Core and Builtins/2020-06-20-16-59-02.bpo-40939.6810Ak.rst deleted file mode 100644 index 8a626d479a91c..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-06-20-16-59-02.bpo-40939.6810Ak.rst +++ /dev/null @@ -1 +0,0 @@ -Remove the remaining files from the old parser and the :mod:`symbol` module. \ No newline at end of file diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-06-20-17-00-44.bpo-35975.UDHCHp.rst b/Misc/NEWS.d/next/Core and Builtins/2020-06-20-17-00-44.bpo-35975.UDHCHp.rst deleted file mode 100644 index 73f4a6da2e5c0..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-06-20-17-00-44.bpo-35975.UDHCHp.rst +++ /dev/null @@ -1,3 +0,0 @@ -Stefan Behnel reported that cf_feature_version is used even when -PyCF_ONLY_AST is not set. This is against the intention and against the -documented behavior, so it's been fixed. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-06-20-19-27-47.bpo-40939.jxJ4yn.rst b/Misc/NEWS.d/next/Core and Builtins/2020-06-20-19-27-47.bpo-40939.jxJ4yn.rst deleted file mode 100644 index 7024dfe47ae1c..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-06-20-19-27-47.bpo-40939.jxJ4yn.rst +++ /dev/null @@ -1 +0,0 @@ -Rename `PyPegen*` functions to `PyParser*`, so that we can remove the old set of `PyParser*` functions that were using the old parser, but keep everything backwards-compatible. \ No newline at end of file diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-06-20-22-46-18.bpo-41052.46MPeF.rst b/Misc/NEWS.d/next/Core and Builtins/2020-06-20-22-46-18.bpo-41052.46MPeF.rst deleted file mode 100644 index 82969bf4a7894..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-06-20-22-46-18.bpo-41052.46MPeF.rst +++ /dev/null @@ -1 +0,0 @@ -Opt out serialization/deserialization for _random.Random diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-06-21-10-54-02.bpo-41061.AHf9MU.rst b/Misc/NEWS.d/next/Core and Builtins/2020-06-21-10-54-02.bpo-41061.AHf9MU.rst deleted file mode 100644 index b5bb81621b7f2..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-06-21-10-54-02.bpo-41061.AHf9MU.rst +++ /dev/null @@ -1 +0,0 @@ -Fix incorrect expressions and asserts in hashtable code and tests. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-06-21-19-53-33.bpo-41056.IDu_EK.rst b/Misc/NEWS.d/next/Core and Builtins/2020-06-21-19-53-33.bpo-41056.IDu_EK.rst deleted file mode 100644 index 25f93c9da3105..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-06-21-19-53-33.bpo-41056.IDu_EK.rst +++ /dev/null @@ -1 +0,0 @@ -Fixes a reference to deallocated stack space during startup when constructing sys.path involving a relative symlink when code was supplied via -c. (discovered via Coverity) \ No newline at end of file diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-06-22-13-22-30.bpo-41076.eWYw2N.rst b/Misc/NEWS.d/next/Core and Builtins/2020-06-22-13-22-30.bpo-41076.eWYw2N.rst deleted file mode 100644 index f13560ad9d269..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-06-22-13-22-30.bpo-41076.eWYw2N.rst +++ /dev/null @@ -1 +0,0 @@ -Pre-feed the parser with the location of the f-string expression, not the f-string itself, which allows us to skip the shifting of the AST node locations after the parsing is completed. \ No newline at end of file diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-06-23-07-35-11.bpo-40521.dMNA6k.rst b/Misc/NEWS.d/next/Core and Builtins/2020-06-23-07-35-11.bpo-40521.dMNA6k.rst deleted file mode 100644 index 25f146e35ef43..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-06-23-07-35-11.bpo-40521.dMNA6k.rst +++ /dev/null @@ -1 +0,0 @@ -Empty frozensets are no longer singletons. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-06-23-15-10-19.bpo-41084.pt3y7F.rst b/Misc/NEWS.d/next/Core and Builtins/2020-06-23-15-10-19.bpo-41084.pt3y7F.rst deleted file mode 100644 index cd349af770bd0..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-06-23-15-10-19.bpo-41084.pt3y7F.rst +++ /dev/null @@ -1 +0,0 @@ -Prefix the error message with 'f-string: ', when parsing an f-string expression which throws a :exc:`SyntaxError`. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-06-23-18-32-41.bpo-39960.Kez3fP.rst b/Misc/NEWS.d/next/Core and Builtins/2020-06-23-18-32-41.bpo-39960.Kez3fP.rst deleted file mode 100644 index f69fccfa4db69..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-06-23-18-32-41.bpo-39960.Kez3fP.rst +++ /dev/null @@ -1,2 +0,0 @@ -The "hackcheck" that prevents sneaking around a type's __setattr__() by calling the -superclass method was rewritten to allow C implemented heap types. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-06-23-23-26-42.bpo-41094.zEIJse.rst b/Misc/NEWS.d/next/Core and Builtins/2020-06-23-23-26-42.bpo-41094.zEIJse.rst deleted file mode 100644 index 6dd45e21d1758..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-06-23-23-26-42.bpo-41094.zEIJse.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix decoding errors with audit when open files with non-ASCII names on non-UTF-8 -locale. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-06-30-04-44-29.bpo-41100.PJwA6F.rst b/Misc/NEWS.d/next/Core and Builtins/2020-06-30-04-44-29.bpo-41100.PJwA6F.rst deleted file mode 100644 index d6176d69f0eb0..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-06-30-04-44-29.bpo-41100.PJwA6F.rst +++ /dev/null @@ -1 +0,0 @@ -add arm64 to the allowable Mac OS arches in mpdecimal.h \ No newline at end of file diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-06-30-20-17-31.bpo-41175.acJoXB.rst b/Misc/NEWS.d/next/Core and Builtins/2020-06-30-20-17-31.bpo-41175.acJoXB.rst deleted file mode 100644 index 844fb804c0c8d..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-06-30-20-17-31.bpo-41175.acJoXB.rst +++ /dev/null @@ -1,2 +0,0 @@ -Guard against a NULL pointer dereference within bytearrayobject triggered by -the ``bytearray() + bytearray()`` operation. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-07-01-20-17-38.bpo-1635741.-AtPYu.rst b/Misc/NEWS.d/next/Core and Builtins/2020-07-01-20-17-38.bpo-1635741.-AtPYu.rst deleted file mode 100644 index c529923779fa8..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-07-01-20-17-38.bpo-1635741.-AtPYu.rst +++ /dev/null @@ -1 +0,0 @@ -Port :mod:`sha256` to multiphase initialization diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-07-03-23-10-02.bpo-1635741.F5coWe.rst b/Misc/NEWS.d/next/Core and Builtins/2020-07-03-23-10-02.bpo-1635741.F5coWe.rst deleted file mode 100644 index 927c8e5b7083f..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-07-03-23-10-02.bpo-1635741.F5coWe.rst +++ /dev/null @@ -1 +0,0 @@ -Port :mod:`faulthandler` to multiphase initialization. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-07-06-13-35-17.bpo-41218.oKnSr2.rst b/Misc/NEWS.d/next/Core and Builtins/2020-07-06-13-35-17.bpo-41218.oKnSr2.rst deleted file mode 100644 index d98b3433ef05f..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-07-06-13-35-17.bpo-41218.oKnSr2.rst +++ /dev/null @@ -1,4 +0,0 @@ -Python 3.8.3 had a regression where compiling with -ast.PyCF_ALLOW_TOP_LEVEL_AWAIT would aggressively mark list comprehension -with CO_COROUTINE. Now only list comprehension making use of async/await -will tagged as so. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-07-06-18-36-33.bpo-41215.vFGFIz.rst b/Misc/NEWS.d/next/Core and Builtins/2020-07-06-18-36-33.bpo-41215.vFGFIz.rst deleted file mode 100644 index 7343da31e94f7..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-07-06-18-36-33.bpo-41215.vFGFIz.rst +++ /dev/null @@ -1,2 +0,0 @@ -Use non-NULL default values in the PEG parser keyword list to overcome a bug that was preventing -Python from being properly compiled when using the XLC compiler. Patch by Pablo Galindo. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-07-06-20-43-19.bpo-1635741.LYhsni.rst b/Misc/NEWS.d/next/Core and Builtins/2020-07-06-20-43-19.bpo-1635741.LYhsni.rst deleted file mode 100644 index 956fcd5d1ee29..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-07-06-20-43-19.bpo-1635741.LYhsni.rst +++ /dev/null @@ -1 +0,0 @@ -Port :mod:`winapi` to multiphase initialization diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-07-07-16-10-52.bpo-1635741.zU-H_n.rst b/Misc/NEWS.d/next/Core and Builtins/2020-07-07-16-10-52.bpo-1635741.zU-H_n.rst deleted file mode 100644 index 52e184dc31707..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-07-07-16-10-52.bpo-1635741.zU-H_n.rst +++ /dev/null @@ -1 +0,0 @@ -Port :mod:`multiprocessing` to multi-phase initialization diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-07-08-21-55-23.bpo-41252.nBWL-Y.rst b/Misc/NEWS.d/next/Core and Builtins/2020-07-08-21-55-23.bpo-41252.nBWL-Y.rst deleted file mode 100644 index 65f3189c83ec6..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-07-08-21-55-23.bpo-41252.nBWL-Y.rst +++ /dev/null @@ -1 +0,0 @@ -Fix incorrect refcounting in _ssl.c's ``_servername_callback()``. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-07-08-22-03-54.bpo-41247.PndYIk.rst b/Misc/NEWS.d/next/Core and Builtins/2020-07-08-22-03-54.bpo-41247.PndYIk.rst deleted file mode 100644 index 08699b6e4a1f0..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-07-08-22-03-54.bpo-41247.PndYIk.rst +++ /dev/null @@ -1,2 +0,0 @@ -Always cache the running loop holder when running -``asyncio.set_running_loop``. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-07-17-11-31-54.bpo-41323.ChbZHh.rst b/Misc/NEWS.d/next/Core and Builtins/2020-07-17-11-31-54.bpo-41323.ChbZHh.rst deleted file mode 100644 index 671d874b535af..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-07-17-11-31-54.bpo-41323.ChbZHh.rst +++ /dev/null @@ -1,3 +0,0 @@ -Bytecode optimizations are performed directly on the control flow graph. -This will result in slightly more compact code objects in some -circumstances. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-07-18-08-15-32.bpo-41295.pu8Ezo.rst b/Misc/NEWS.d/next/Core and Builtins/2020-07-18-08-15-32.bpo-41295.pu8Ezo.rst deleted file mode 100644 index d61fd8f0a2968..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-07-18-08-15-32.bpo-41295.pu8Ezo.rst +++ /dev/null @@ -1,3 +0,0 @@ -Resolve a regression in CPython 3.8.4 where defining "__setattr__" in a -multi-inheritance setup and calling up the hierarchy chain could fail -if builtins/extension types were involved in the base types. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-07-18-18-01-10.bpo-41334.t5xMGp.rst b/Misc/NEWS.d/next/Core and Builtins/2020-07-18-18-01-10.bpo-41334.t5xMGp.rst deleted file mode 100644 index 5d44527a561a1..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-07-18-18-01-10.bpo-41334.t5xMGp.rst +++ /dev/null @@ -1,2 +0,0 @@ -Constructors :func:`str`, :func:`bytes` and :func:`bytearray` are now faster -(around 30--40% for small objects). diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-07-19-15-40-52.bpo-41342.RRk_m_.rst b/Misc/NEWS.d/next/Core and Builtins/2020-07-19-15-40-52.bpo-41342.RRk_m_.rst deleted file mode 100644 index 38851a7f7fc0f..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-07-19-15-40-52.bpo-41342.RRk_m_.rst +++ /dev/null @@ -1 +0,0 @@ -:func:`round` with integer argument is now faster (9--60%). diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-07-20-17-01-17.bpo-38156.ptcdRy.rst b/Misc/NEWS.d/next/Core and Builtins/2020-07-20-17-01-17.bpo-38156.ptcdRy.rst deleted file mode 100644 index 254d13cf3ed3a..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-07-20-17-01-17.bpo-38156.ptcdRy.rst +++ /dev/null @@ -1 +0,0 @@ -Handle interrupts that come after EOF correctly in ``PyOS_StdioReadline``. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-07-27-01-50-06.bpo-41340.pZXfcF.rst b/Misc/NEWS.d/next/Core and Builtins/2020-07-27-01-50-06.bpo-41340.pZXfcF.rst deleted file mode 100644 index 3a93a57693107..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-07-27-01-50-06.bpo-41340.pZXfcF.rst +++ /dev/null @@ -1 +0,0 @@ -Removed fallback implementation for ``strdup``. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-07-28-22-43-27.bpo-41428.FM6xsI.rst b/Misc/NEWS.d/next/Core and Builtins/2020-07-28-22-43-27.bpo-41428.FM6xsI.rst deleted file mode 100644 index a6652de927511..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-07-28-22-43-27.bpo-41428.FM6xsI.rst +++ /dev/null @@ -1 +0,0 @@ -Implement PEP 604. This supports (int | str) etc. in place of Union[str, int]. \ No newline at end of file diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-08-02-15-53-12.bpo-41431.TblUBT.rst b/Misc/NEWS.d/next/Core and Builtins/2020-08-02-15-53-12.bpo-41431.TblUBT.rst deleted file mode 100644 index fa9d047edc394..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-08-02-15-53-12.bpo-41431.TblUBT.rst +++ /dev/null @@ -1,2 +0,0 @@ -Optimize ``dict_merge()`` for copying dict (e.g. ``dict(d)`` and -``{}.update(d)``). diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-08-10-16-11-32.bpo-1635741.O0d3ym.rst b/Misc/NEWS.d/next/Core and Builtins/2020-08-10-16-11-32.bpo-1635741.O0d3ym.rst deleted file mode 100644 index 12af3d01ed8ef..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-08-10-16-11-32.bpo-1635741.O0d3ym.rst +++ /dev/null @@ -1,2 +0,0 @@ -Port the :mod:`_sha1`, :mod:`_sha512`, and :mod:`_md5` extension modules -to multi-phase initialization API (:pep:`489`). diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-08-12-07-35-07.bpo-41525.d9q3XL.rst b/Misc/NEWS.d/next/Core and Builtins/2020-08-12-07-35-07.bpo-41525.d9q3XL.rst deleted file mode 100644 index acc00f8b992c9..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-08-12-07-35-07.bpo-41525.d9q3XL.rst +++ /dev/null @@ -1 +0,0 @@ -The output of ``python --help`` contains now only ASCII characters. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-08-12-19-32-15.bpo-41531.WgPzjT.rst b/Misc/NEWS.d/next/Core and Builtins/2020-08-12-19-32-15.bpo-41531.WgPzjT.rst deleted file mode 100644 index 8544664f39335..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-08-12-19-32-15.bpo-41531.WgPzjT.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix a bug that was dropping keys when compiling dict literals with more than -0xFFFF elements. Patch by Pablo Galindo. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-08-12-20-29-57.bpo-41533.4pcVAc.rst b/Misc/NEWS.d/next/Core and Builtins/2020-08-12-20-29-57.bpo-41533.4pcVAc.rst deleted file mode 100644 index e166f0c0b621a..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-08-12-20-29-57.bpo-41533.4pcVAc.rst +++ /dev/null @@ -1,2 +0,0 @@ -Free the stack allocated in ``va_build_stack`` if ``do_mkstack`` fails and -the stack is not a ``small_stack``. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-08-13-07-18-05.bpo-1635741.FC13e7.rst b/Misc/NEWS.d/next/Core and Builtins/2020-08-13-07-18-05.bpo-1635741.FC13e7.rst deleted file mode 100644 index cdfee874095fe..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-08-13-07-18-05.bpo-1635741.FC13e7.rst +++ /dev/null @@ -1 +0,0 @@ -Port the :mod:`_blake2` extension module to the multi-phase initialization API (:pep:`489`). diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-08-13-07-19-21.bpo-1653741.fubBkb.rst b/Misc/NEWS.d/next/Core and Builtins/2020-08-13-07-19-21.bpo-1653741.fubBkb.rst deleted file mode 100644 index 73a4fdbac48a2..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-08-13-07-19-21.bpo-1653741.fubBkb.rst +++ /dev/null @@ -1 +0,0 @@ -Port :mod:`_sha3` to multi-phase init. Convert static types to heap types. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-08-25-22-43-33.bpo-40077.vcxSUa.rst b/Misc/NEWS.d/next/Core and Builtins/2020-08-25-22-43-33.bpo-40077.vcxSUa.rst deleted file mode 100644 index ee950010e6d13..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-08-25-22-43-33.bpo-40077.vcxSUa.rst +++ /dev/null @@ -1 +0,0 @@ -Convert :mod:`_operator` to use :c:func:`PyType_FromSpec`. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-08-26-11-23-31.bpo-41631.3jZcd9.rst b/Misc/NEWS.d/next/Core and Builtins/2020-08-26-11-23-31.bpo-41631.3jZcd9.rst deleted file mode 100644 index 68bb51024d9e7..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-08-26-11-23-31.bpo-41631.3jZcd9.rst +++ /dev/null @@ -1,5 +0,0 @@ -The ``_ast`` module uses again a global state. Using a module state per module -instance is causing subtle practical problems. For example, the Mercurial -project replaces the ``__import__()`` function to implement lazy import, -whereas Python expected that ``import _ast`` always return a fully initialized -``_ast`` module. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-08-28-20-54-04.bpo-1635741.7ijlcI.rst b/Misc/NEWS.d/next/Core and Builtins/2020-08-28-20-54-04.bpo-1635741.7ijlcI.rst deleted file mode 100644 index 4d6ce1185ed93..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-08-28-20-54-04.bpo-1635741.7ijlcI.rst +++ /dev/null @@ -1 +0,0 @@ -Port the :mod:`zlib` extension module to multi-phase initialization (:pep:`489`). diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-08-30-20-38-33.bpo-41654.HtnhAM.rst b/Misc/NEWS.d/next/Core and Builtins/2020-08-30-20-38-33.bpo-41654.HtnhAM.rst deleted file mode 100644 index e05c3133e1262..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-08-30-20-38-33.bpo-41654.HtnhAM.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix a crash that occurred when destroying subclasses of -:class:`MemoryError`. Patch by Pablo Galindo. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-08-31-11-37-59.bpo-41670.vmRJRx.rst b/Misc/NEWS.d/next/Core and Builtins/2020-08-31-11-37-59.bpo-41670.vmRJRx.rst deleted file mode 100644 index 6ad5fb6dc9bb4..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-08-31-11-37-59.bpo-41670.vmRJRx.rst +++ /dev/null @@ -1,4 +0,0 @@ -Prevent line trace being skipped on platforms not compiled -with ``USE_COMPUTED_GOTOS``. -Fixes issue where some lines nested within a try-except block -were not being traced on Windows. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-08-31-14-53-17.bpo-41675.VSoqWU.rst b/Misc/NEWS.d/next/Core and Builtins/2020-08-31-14-53-17.bpo-41675.VSoqWU.rst deleted file mode 100644 index aa102f8fe4384..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-08-31-14-53-17.bpo-41675.VSoqWU.rst +++ /dev/null @@ -1,3 +0,0 @@ -The implementation of :func:`signal.siginterrupt` now uses :c:func:`sigaction` -(if it is available in the system) instead of the deprecated :c:func:`siginterrupt`. -Patch by Pablo Galindo. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-08-31-17-49-02.bpo-41681.3-VJiH.rst b/Misc/NEWS.d/next/Core and Builtins/2020-08-31-17-49-02.bpo-41681.3-VJiH.rst deleted file mode 100644 index ed557f92d85ca..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-08-31-17-49-02.bpo-41681.3-VJiH.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fixes the wrong error description in the error raised by using 2 `,` in -format string in f-string and :meth:`str.format`. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-09-01-17-06-02.bpo-1635741.5jZymK.rst b/Misc/NEWS.d/next/Core and Builtins/2020-09-01-17-06-02.bpo-1635741.5jZymK.rst deleted file mode 100644 index c3bc9a78a2e05..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-09-01-17-06-02.bpo-1635741.5jZymK.rst +++ /dev/null @@ -1,2 +0,0 @@ -Port the :mod:`_opcode` extension module to multi-phase initialization -(:pep:`489`). diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-09-01-17-08-07.bpo-1635741.X9CZgo.rst b/Misc/NEWS.d/next/Core and Builtins/2020-09-01-17-08-07.bpo-1635741.X9CZgo.rst deleted file mode 100644 index a39673a26307a..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-09-01-17-08-07.bpo-1635741.X9CZgo.rst +++ /dev/null @@ -1,2 +0,0 @@ -Port the :mod:`_curses_panel` extension module to multi-phase initialization -(:pep:`489`). diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-09-01-17-22-35.bpo-1635741.CnRME3.rst b/Misc/NEWS.d/next/Core and Builtins/2020-09-01-17-22-35.bpo-1635741.CnRME3.rst deleted file mode 100644 index 76f985bb87b4e..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-09-01-17-22-35.bpo-1635741.CnRME3.rst +++ /dev/null @@ -1,2 +0,0 @@ -Port the :mod:`_overlapped` extension module to multi-phase initialization -(:pep:`489`). diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-09-02-12-00-57.bpo-41690.Ny-Sfy.rst b/Misc/NEWS.d/next/Core and Builtins/2020-09-02-12-00-57.bpo-41690.Ny-Sfy.rst deleted file mode 100644 index 5711aa5a55f07..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-09-02-12-00-57.bpo-41690.Ny-Sfy.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix a possible stack overflow in the parser when parsing functions and -classes with a huge ammount of arguments. Patch by Pablo Galindo. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-09-07-09-45-47.bpo-1635741.QuDIut.rst b/Misc/NEWS.d/next/Core and Builtins/2020-09-07-09-45-47.bpo-1635741.QuDIut.rst deleted file mode 100644 index 90e56542d1e97..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-09-07-09-45-47.bpo-1635741.QuDIut.rst +++ /dev/null @@ -1 +0,0 @@ -Convert the :mod:`_sha256` extension module types to heap types. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-09-07-11-35-02.bpo-1635741.rvIexb.rst b/Misc/NEWS.d/next/Core and Builtins/2020-09-07-11-35-02.bpo-1635741.rvIexb.rst deleted file mode 100644 index 1e19b34b372d8..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-09-07-11-35-02.bpo-1635741.rvIexb.rst +++ /dev/null @@ -1,2 +0,0 @@ -Port the :mod:`termios` extension module to multi-phase initialization -(:pep:`489`). diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-09-08-20-39-43.bpo-1635741.jiXmyT.rst b/Misc/NEWS.d/next/Core and Builtins/2020-09-08-20-39-43.bpo-1635741.jiXmyT.rst deleted file mode 100644 index 17752b2ccd3fa..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-09-08-20-39-43.bpo-1635741.jiXmyT.rst +++ /dev/null @@ -1,2 +0,0 @@ -Port the :mod:`_scproxy` extension module to multi-phase initialization -(:pep:`489`). diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-09-08-21-58-47.bpo-1635741.vdjSLH.rst b/Misc/NEWS.d/next/Core and Builtins/2020-09-08-21-58-47.bpo-1635741.vdjSLH.rst deleted file mode 100644 index bc1a6c888e33b..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-09-08-21-58-47.bpo-1635741.vdjSLH.rst +++ /dev/null @@ -1,2 +0,0 @@ -Port the :mod:`cmath` extension module to multi-phase initialization -(:pep:`489`). diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-09-12-12-55-45.bpo-41756.1h0tbV.rst b/Misc/NEWS.d/next/Core and Builtins/2020-09-12-12-55-45.bpo-41756.1h0tbV.rst deleted file mode 100644 index b387cfd94033c..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-09-12-12-55-45.bpo-41756.1h0tbV.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add PyGen_Send function to allow sending value into generator/coroutine -without raising StopIteration exception to signal return diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-09-12-18-34-34.bpo-1635741.lh335O.rst b/Misc/NEWS.d/next/Core and Builtins/2020-09-12-18-34-34.bpo-1635741.lh335O.rst deleted file mode 100644 index ba61819df9e08..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-09-12-18-34-34.bpo-1635741.lh335O.rst +++ /dev/null @@ -1,2 +0,0 @@ -Port the :mod:`_lsprof` extension module to multi-phase initialization -(:pep:`489`). diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-09-15-23-29-49.bpo-41780.bOBUIH.rst b/Misc/NEWS.d/next/Core and Builtins/2020-09-15-23-29-49.bpo-41780.bOBUIH.rst deleted file mode 100644 index 9a7594fc45338..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-09-15-23-29-49.bpo-41780.bOBUIH.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix :meth:`__dir__` of :class:`types.GenericAlias`. Patch by Batuhan -Taskaya. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-09-24-12-15-45.bpo-39934.YVHTCF.rst b/Misc/NEWS.d/next/Core and Builtins/2020-09-24-12-15-45.bpo-39934.YVHTCF.rst deleted file mode 100644 index 92cd1ba234d21..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-09-24-12-15-45.bpo-39934.YVHTCF.rst +++ /dev/null @@ -1,3 +0,0 @@ -Correctly count control blocks in 'except' in compiler. Ensures that a -syntax error, rather a fatal error, occurs for deeply nested, named -exception handlers. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-09-26-14-43-30.bpo-1635741.aJS9B3.rst b/Misc/NEWS.d/next/Core and Builtins/2020-09-26-14-43-30.bpo-1635741.aJS9B3.rst deleted file mode 100644 index 252dab35a1368..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-09-26-14-43-30.bpo-1635741.aJS9B3.rst +++ /dev/null @@ -1 +0,0 @@ -Port the :mod:`_bisect` module to the multi-phase initialization API (:pep:`489`). diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-09-27-22-23-14.bpo-41870.2v6_v4.rst b/Misc/NEWS.d/next/Core and Builtins/2020-09-27-22-23-14.bpo-41870.2v6_v4.rst deleted file mode 100644 index 13a6bb04a28fd..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-09-27-22-23-14.bpo-41870.2v6_v4.rst +++ /dev/null @@ -1,2 +0,0 @@ -Speed up calls to ``bool()`` by using the :pep:`590` ``vectorcall`` calling -convention. Patch by Dong-hee Na. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-09-28-08-58-28.bpo-41873.VzEDhA.rst b/Misc/NEWS.d/next/Core and Builtins/2020-09-28-08-58-28.bpo-41873.VzEDhA.rst deleted file mode 100644 index ee2636704c299..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-09-28-08-58-28.bpo-41873.VzEDhA.rst +++ /dev/null @@ -1 +0,0 @@ -Calls to ``float()`` are now faster due to the ``vectorcall`` calling convention. Patch by Dennis Sweeney. \ No newline at end of file diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-10-04-01-02-58.bpo-41922.kHGT8I.rst b/Misc/NEWS.d/next/Core and Builtins/2020-10-04-01-02-58.bpo-41922.kHGT8I.rst deleted file mode 100644 index 3c4de2c93555f..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-10-04-01-02-58.bpo-41922.kHGT8I.rst +++ /dev/null @@ -1,2 +0,0 @@ -Speed up calls to ``reversed()`` by using the :pep:`590` ``vectorcall`` -calling convention. Patch by Dong-hee Na. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-10-04-10-55-12.bpo-41909.BqHPcm.rst b/Misc/NEWS.d/next/Core and Builtins/2020-10-04-10-55-12.bpo-41909.BqHPcm.rst deleted file mode 100644 index 388cfea065eed..0000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2020-10-04-10-55-12.bpo-41909.BqHPcm.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fixed stack overflow in :func:`issubclass` and :func:`isinstance` when -getting the ``__bases__`` attribute leads to infinite recursion. diff --git a/Misc/NEWS.d/next/Documentation/2019-08-16-20-25-42.bpo-37703.Qm_l_H.rst b/Misc/NEWS.d/next/Documentation/2019-08-16-20-25-42.bpo-37703.Qm_l_H.rst deleted file mode 100644 index a1a1c354b1688..0000000000000 --- a/Misc/NEWS.d/next/Documentation/2019-08-16-20-25-42.bpo-37703.Qm_l_H.rst +++ /dev/null @@ -1,2 +0,0 @@ -Updated Documentation to comprehensively elaborate on the behaviour of -gather.cancel() diff --git a/Misc/NEWS.d/next/Documentation/2020-03-07-03-53-39.bpo-39883.1tnb4-.rst b/Misc/NEWS.d/next/Documentation/2020-03-07-03-53-39.bpo-39883.1tnb4-.rst deleted file mode 100644 index 4941d50a560e2..0000000000000 --- a/Misc/NEWS.d/next/Documentation/2020-03-07-03-53-39.bpo-39883.1tnb4-.rst +++ /dev/null @@ -1 +0,0 @@ -Make code, examples, and recipes in the Python documentation be licensed under the more permissive BSD0 license in addition to the existing Python 2.0 license. \ No newline at end of file diff --git a/Misc/NEWS.d/next/Documentation/2020-05-09-12-10-31.bpo-40552._0uB73.rst b/Misc/NEWS.d/next/Documentation/2020-05-09-12-10-31.bpo-40552._0uB73.rst deleted file mode 100644 index 5ed9c31834ac2..0000000000000 --- a/Misc/NEWS.d/next/Documentation/2020-05-09-12-10-31.bpo-40552._0uB73.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix in tutorial section 4.2. -Code snippet is now correct. \ No newline at end of file diff --git a/Misc/NEWS.d/next/Documentation/2020-07-21-15-23-30.bpo-40979.pLA8rO.rst b/Misc/NEWS.d/next/Documentation/2020-07-21-15-23-30.bpo-40979.pLA8rO.rst deleted file mode 100644 index b0ca4327ad61a..0000000000000 --- a/Misc/NEWS.d/next/Documentation/2020-07-21-15-23-30.bpo-40979.pLA8rO.rst +++ /dev/null @@ -1 +0,0 @@ -Refactored typing.rst, arranging more than 70 classes, functions, and decorators into new sub-sections. \ No newline at end of file diff --git a/Misc/NEWS.d/next/Documentation/2020-07-25-14-20-00.bpo-41314.yrjko0.rst b/Misc/NEWS.d/next/Documentation/2020-07-25-14-20-00.bpo-41314.yrjko0.rst deleted file mode 100644 index 48f9c933828b0..0000000000000 --- a/Misc/NEWS.d/next/Documentation/2020-07-25-14-20-00.bpo-41314.yrjko0.rst +++ /dev/null @@ -1 +0,0 @@ -Changed the release when ``from __future__ import annotations`` becomes the default from ``4.0`` to ``3.10`` (following a change in PEP 563). diff --git a/Misc/NEWS.d/next/Documentation/2020-07-27-20-46-17.bpo-41045.GFF6Ul.rst b/Misc/NEWS.d/next/Documentation/2020-07-27-20-46-17.bpo-41045.GFF6Ul.rst deleted file mode 100644 index dfc9891bb89f2..0000000000000 --- a/Misc/NEWS.d/next/Documentation/2020-07-27-20-46-17.bpo-41045.GFF6Ul.rst +++ /dev/null @@ -1 +0,0 @@ -Add documentation for debug feature of f-strings. \ No newline at end of file diff --git a/Misc/NEWS.d/next/Documentation/2020-08-12-18-35-40.bpo-40204.C8A_pe.rst b/Misc/NEWS.d/next/Documentation/2020-08-12-18-35-40.bpo-40204.C8A_pe.rst deleted file mode 100644 index 152f6c98b9004..0000000000000 --- a/Misc/NEWS.d/next/Documentation/2020-08-12-18-35-40.bpo-40204.C8A_pe.rst +++ /dev/null @@ -1,3 +0,0 @@ -Enable Sphinx 3.2 ``c_allow_pre_v3`` option and disable -``c_warn_on_allowed_pre_v3`` option to make the documentation compatible -with Sphinx 2 and Sphinx 3. diff --git a/Misc/NEWS.d/next/Documentation/2020-08-25-15-11-23.bpo-41624.ddjJlN.rst b/Misc/NEWS.d/next/Documentation/2020-08-25-15-11-23.bpo-41624.ddjJlN.rst deleted file mode 100644 index bdbc5a445f339..0000000000000 --- a/Misc/NEWS.d/next/Documentation/2020-08-25-15-11-23.bpo-41624.ddjJlN.rst +++ /dev/null @@ -1 +0,0 @@ -Fix the signature of :class:`typing.Coroutine`. diff --git a/Misc/NEWS.d/next/Documentation/2020-09-08-16-57-09.bpo-41726.g0UXrn.rst b/Misc/NEWS.d/next/Documentation/2020-09-08-16-57-09.bpo-41726.g0UXrn.rst deleted file mode 100644 index 1079a757c054a..0000000000000 --- a/Misc/NEWS.d/next/Documentation/2020-09-08-16-57-09.bpo-41726.g0UXrn.rst +++ /dev/null @@ -1 +0,0 @@ -Update the refcounts info of ``PyType_FromModuleAndSpec``. diff --git a/Misc/NEWS.d/next/Documentation/2020-09-10-07-48-02.bpo-37149.VD0rCv.rst b/Misc/NEWS.d/next/Documentation/2020-09-10-07-48-02.bpo-37149.VD0rCv.rst deleted file mode 100644 index aeca652b4ed97..0000000000000 --- a/Misc/NEWS.d/next/Documentation/2020-09-10-07-48-02.bpo-37149.VD0rCv.rst +++ /dev/null @@ -1 +0,0 @@ -Change Shipman tkinter doc link from archive.org to TkDocs. (The doc has been removed from the NMT server.) The new link responds much faster and includes a short explanatory note. diff --git a/Misc/NEWS.d/next/Documentation/2020-09-12-17-37-13.bpo-35293._cOwPD.rst b/Misc/NEWS.d/next/Documentation/2020-09-12-17-37-13.bpo-35293._cOwPD.rst deleted file mode 100644 index 089d44e35d2ba..0000000000000 --- a/Misc/NEWS.d/next/Documentation/2020-09-12-17-37-13.bpo-35293._cOwPD.rst +++ /dev/null @@ -1 +0,0 @@ -Fix RemovedInSphinx40Warning when building the documentation. Patch by Dong-hee Na. diff --git a/Misc/NEWS.d/next/Documentation/2020-09-24-15-35-13.bpo-41774.5IqdGP.rst b/Misc/NEWS.d/next/Documentation/2020-09-24-15-35-13.bpo-41774.5IqdGP.rst deleted file mode 100644 index af8e02437cb2b..0000000000000 --- a/Misc/NEWS.d/next/Documentation/2020-09-24-15-35-13.bpo-41774.5IqdGP.rst +++ /dev/null @@ -1,2 +0,0 @@ -In Programming FAQ "Sequences (Tuples/Lists)" section, add "How do you -remove multiple items from a list". diff --git a/Misc/NEWS.d/next/Documentation/2020-10-03-18-20-46.bpo-41428._ju1NE.rst b/Misc/NEWS.d/next/Documentation/2020-10-03-18-20-46.bpo-41428._ju1NE.rst deleted file mode 100644 index 2c33393456019..0000000000000 --- a/Misc/NEWS.d/next/Documentation/2020-10-03-18-20-46.bpo-41428._ju1NE.rst +++ /dev/null @@ -1 +0,0 @@ -Add documentation for :pep:`604` (Allow writing union types as ``X | Y``). diff --git a/Misc/NEWS.d/next/IDLE/2020-05-24-06-19-43.bpo-40723.AJLd4U.rst b/Misc/NEWS.d/next/IDLE/2020-05-24-06-19-43.bpo-40723.AJLd4U.rst deleted file mode 100644 index e0de2f9d83668..0000000000000 --- a/Misc/NEWS.d/next/IDLE/2020-05-24-06-19-43.bpo-40723.AJLd4U.rst +++ /dev/null @@ -1 +0,0 @@ -Make test_idle pass when run after import. diff --git a/Misc/NEWS.d/next/IDLE/2020-05-29-18-21-58.bpo-39885.zB_-bN.rst b/Misc/NEWS.d/next/IDLE/2020-05-29-18-21-58.bpo-39885.zB_-bN.rst deleted file mode 100644 index a847b75997117..0000000000000 --- a/Misc/NEWS.d/next/IDLE/2020-05-29-18-21-58.bpo-39885.zB_-bN.rst +++ /dev/null @@ -1,2 +0,0 @@ -Make context menu Cut and Copy work again when right-clicking within a -selection. diff --git a/Misc/NEWS.d/next/IDLE/2020-06-27-17-02-00.bpo-41144.JoFGIX.rst b/Misc/NEWS.d/next/IDLE/2020-06-27-17-02-00.bpo-41144.JoFGIX.rst deleted file mode 100644 index ed558d3e7ded1..0000000000000 --- a/Misc/NEWS.d/next/IDLE/2020-06-27-17-02-00.bpo-41144.JoFGIX.rst +++ /dev/null @@ -1 +0,0 @@ -Make Open Module open a special module such as os.path. diff --git a/Misc/NEWS.d/next/IDLE/2020-06-29-14-51-15.bpo-41152.d6mV0C.rst b/Misc/NEWS.d/next/IDLE/2020-06-29-14-51-15.bpo-41152.d6mV0C.rst deleted file mode 100644 index 434be10b5309c..0000000000000 --- a/Misc/NEWS.d/next/IDLE/2020-06-29-14-51-15.bpo-41152.d6mV0C.rst +++ /dev/null @@ -1,2 +0,0 @@ -The encoding of ``stdin``, ``stdout`` and ``stderr`` in IDLE is now always -UTF-8. diff --git a/Misc/NEWS.d/next/IDLE/2020-07-07-18-44-30.bpo-37765.umc1o8.rst b/Misc/NEWS.d/next/IDLE/2020-07-07-18-44-30.bpo-37765.umc1o8.rst deleted file mode 100644 index f8b53ca482a21..0000000000000 --- a/Misc/NEWS.d/next/IDLE/2020-07-07-18-44-30.bpo-37765.umc1o8.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add keywords to module name completion list. Rewrite Completions -section of IDLE doc. diff --git a/Misc/NEWS.d/next/IDLE/2020-07-16-17-39-06.bpo-41300.wRixNb.rst b/Misc/NEWS.d/next/IDLE/2020-07-16-17-39-06.bpo-41300.wRixNb.rst deleted file mode 100644 index 080775f7d7ab4..0000000000000 --- a/Misc/NEWS.d/next/IDLE/2020-07-16-17-39-06.bpo-41300.wRixNb.rst +++ /dev/null @@ -1,2 +0,0 @@ -Save files with non-ascii chars. Fix regression released in 3.9.0b4 and -3.8.4. diff --git a/Misc/NEWS.d/next/IDLE/2020-07-24-17-49-58.bpo-41373.YQIPu_.rst b/Misc/NEWS.d/next/IDLE/2020-07-24-17-49-58.bpo-41373.YQIPu_.rst deleted file mode 100644 index b50a72fe676a8..0000000000000 --- a/Misc/NEWS.d/next/IDLE/2020-07-24-17-49-58.bpo-41373.YQIPu_.rst +++ /dev/null @@ -1,3 +0,0 @@ -Save files loaded with no line ending, as when blank, or different line -endings, by setting its line ending to the system default. Fix regression in -3.8.4 and 3.9.0b4. diff --git a/Misc/NEWS.d/next/IDLE/2020-08-09-13-42-55.bpo-41468.zkP0_Y.rst b/Misc/NEWS.d/next/IDLE/2020-08-09-13-42-55.bpo-41468.zkP0_Y.rst deleted file mode 100644 index e41c7d574905c..0000000000000 --- a/Misc/NEWS.d/next/IDLE/2020-08-09-13-42-55.bpo-41468.zkP0_Y.rst +++ /dev/null @@ -1 +0,0 @@ -Improve IDLE run crash error message (which users should never see). diff --git a/Misc/NEWS.d/next/IDLE/2020-09-22-00-45-40.bpo-40181.hhQi3z.rst b/Misc/NEWS.d/next/IDLE/2020-09-22-00-45-40.bpo-40181.hhQi3z.rst deleted file mode 100644 index b6866e19c4d41..0000000000000 --- a/Misc/NEWS.d/next/IDLE/2020-09-22-00-45-40.bpo-40181.hhQi3z.rst +++ /dev/null @@ -1,2 +0,0 @@ -In calltips, stop reminding that '/' marks the end of positional-only -arguments. diff --git a/Misc/NEWS.d/next/IDLE/2020-09-22-11-13-45.bpo-35764.VoNa8y.rst b/Misc/NEWS.d/next/IDLE/2020-09-22-11-13-45.bpo-35764.VoNa8y.rst deleted file mode 100644 index eb62d3699d5fe..0000000000000 --- a/Misc/NEWS.d/next/IDLE/2020-09-22-11-13-45.bpo-35764.VoNa8y.rst +++ /dev/null @@ -1 +0,0 @@ -Rewrite the Calltips doc section. diff --git a/Misc/NEWS.d/next/IDLE/2020-09-24-14-31-16.bpo-41775.sB8Vre.rst b/Misc/NEWS.d/next/IDLE/2020-09-24-14-31-16.bpo-41775.sB8Vre.rst deleted file mode 100644 index 59605fa40235f..0000000000000 --- a/Misc/NEWS.d/next/IDLE/2020-09-24-14-31-16.bpo-41775.sB8Vre.rst +++ /dev/null @@ -1 +0,0 @@ -Use 'IDLE Shell' as shell title diff --git a/Misc/NEWS.d/next/Library/2018-03-15-11-55-04.bpo-26680.eKAi85.rst b/Misc/NEWS.d/next/Library/2018-03-15-11-55-04.bpo-26680.eKAi85.rst deleted file mode 100644 index 8b2e818383041..0000000000000 --- a/Misc/NEWS.d/next/Library/2018-03-15-11-55-04.bpo-26680.eKAi85.rst +++ /dev/null @@ -1,3 +0,0 @@ -The x.is_integer() method is incorporated into the abstract types of the -numeric tower, Real, Rational and Integral, with appropriate default -implementations. diff --git a/Misc/NEWS.d/next/Library/2018-03-15-11-56-48.bpo-26680.Udkhn4.rst b/Misc/NEWS.d/next/Library/2018-03-15-11-56-48.bpo-26680.Udkhn4.rst deleted file mode 100644 index df75e080fa6ee..0000000000000 --- a/Misc/NEWS.d/next/Library/2018-03-15-11-56-48.bpo-26680.Udkhn4.rst +++ /dev/null @@ -1,2 +0,0 @@ -The d.is_integer() method is added to the Decimal type, for compatibility -with other number types. diff --git a/Misc/NEWS.d/next/Library/2018-06-07-22-04-01.bpo-28557.ViNJnK.rst b/Misc/NEWS.d/next/Library/2018-06-07-22-04-01.bpo-28557.ViNJnK.rst deleted file mode 100644 index 4137e2ff89beb..0000000000000 --- a/Misc/NEWS.d/next/Library/2018-06-07-22-04-01.bpo-28557.ViNJnK.rst +++ /dev/null @@ -1 +0,0 @@ -Improve the error message for a misbehaving ``rawio.readinto`` diff --git a/Misc/NEWS.d/next/Library/2018-06-12-23-30-41.bpo-33660.AdDn5Z.rst b/Misc/NEWS.d/next/Library/2018-06-12-23-30-41.bpo-33660.AdDn5Z.rst deleted file mode 100644 index cce3dbb1c6ea5..0000000000000 --- a/Misc/NEWS.d/next/Library/2018-06-12-23-30-41.bpo-33660.AdDn5Z.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix pathlib.PosixPath to resolve a relative path located on the root -directory properly. diff --git a/Misc/NEWS.d/next/Library/2018-07-29-12-14-54.bpo-34226.BE7zbu.rst b/Misc/NEWS.d/next/Library/2018-07-29-12-14-54.bpo-34226.BE7zbu.rst deleted file mode 100644 index 2656b4bf22ae4..0000000000000 --- a/Misc/NEWS.d/next/Library/2018-07-29-12-14-54.bpo-34226.BE7zbu.rst +++ /dev/null @@ -1 +0,0 @@ -Fix `cgi.parse_multipart` without content_length. Patch by Roger Duran diff --git a/Misc/NEWS.d/next/Library/2018-07-30-12-48-17.bpo-31844.0_GKsD.rst b/Misc/NEWS.d/next/Library/2018-07-30-12-48-17.bpo-31844.0_GKsD.rst deleted file mode 100644 index 9034afd3284c4..0000000000000 --- a/Misc/NEWS.d/next/Library/2018-07-30-12-48-17.bpo-31844.0_GKsD.rst +++ /dev/null @@ -1,4 +0,0 @@ -Remove ``ParserBase.error()`` method from the private and undocumented -``_markupbase`` module. :class:`html.parser.HTMLParser` is the only -subclass of ``ParserBase`` and its ``error()`` implementation was deprecated -in Python 3.4 and removed in Python 3.5. diff --git a/Misc/NEWS.d/next/Library/2018-08-21-16-20-33.bpo-29620.xxx666.rst b/Misc/NEWS.d/next/Library/2018-08-21-16-20-33.bpo-29620.xxx666.rst deleted file mode 100644 index d781919504e68..0000000000000 --- a/Misc/NEWS.d/next/Library/2018-08-21-16-20-33.bpo-29620.xxx666.rst +++ /dev/null @@ -1,3 +0,0 @@ -:func:`~unittest.TestCase.assertWarns` no longer raises a ``RuntimeException`` -when accessing a module's ``__warningregistry__`` causes importation of a new -module, or when a new module is imported in another thread. Patch by Kernc. diff --git a/Misc/NEWS.d/next/Library/2018-10-27-09-37-03.bpo-35078.kweA3R.rst b/Misc/NEWS.d/next/Library/2018-10-27-09-37-03.bpo-35078.kweA3R.rst deleted file mode 100644 index 123f9dabde913..0000000000000 --- a/Misc/NEWS.d/next/Library/2018-10-27-09-37-03.bpo-35078.kweA3R.rst +++ /dev/null @@ -1,3 +0,0 @@ -Refactor formatweekday, formatmonthname methods in LocaleHTMLCalendar and LocaleTextCalendar classes in calendar module to call the base class methods.This enables customizable CSS classes for LocaleHTMLCalendar. -Patch by Srinivas Reddy Thatiparthy - diff --git a/Misc/NEWS.d/next/Library/2019-03-01-01-56-23.bpo-33944.-82Pkt.rst b/Misc/NEWS.d/next/Library/2019-03-01-01-56-23.bpo-33944.-82Pkt.rst deleted file mode 100644 index b0c953dd6752e..0000000000000 --- a/Misc/NEWS.d/next/Library/2019-03-01-01-56-23.bpo-33944.-82Pkt.rst +++ /dev/null @@ -1 +0,0 @@ -Added site.py site-packages tracing in verbose mode. diff --git a/Misc/NEWS.d/next/Library/2019-03-17-19-01-53.bpo-36290.7VXo_K.rst b/Misc/NEWS.d/next/Library/2019-03-17-19-01-53.bpo-36290.7VXo_K.rst deleted file mode 100644 index a9afe62b0c46e..0000000000000 --- a/Misc/NEWS.d/next/Library/2019-03-17-19-01-53.bpo-36290.7VXo_K.rst +++ /dev/null @@ -1,2 +0,0 @@ -AST nodes are now raising :exc:`TypeError` on conflicting keyword arguments. -Patch contributed by R?mi Lapeyre. diff --git a/Misc/NEWS.d/next/Library/2019-05-31-23-54-28.bpo-12178.N6FLCZ.rst b/Misc/NEWS.d/next/Library/2019-05-31-23-54-28.bpo-12178.N6FLCZ.rst deleted file mode 100644 index 80e2a7b5fbb2c..0000000000000 --- a/Misc/NEWS.d/next/Library/2019-05-31-23-54-28.bpo-12178.N6FLCZ.rst +++ /dev/null @@ -1,3 +0,0 @@ -:func:`csv.writer` now correctly escapes *escapechar* when input -contains *escapechar*. Patch by Catalin Iacob, Berker Peksag, -and Itay Elbirt. diff --git a/Misc/NEWS.d/next/Library/2019-08-11-16-28-03.bpo-26543.X-TJZO.rst b/Misc/NEWS.d/next/Library/2019-08-11-16-28-03.bpo-26543.X-TJZO.rst deleted file mode 100644 index 8715b8d79cace..0000000000000 --- a/Misc/NEWS.d/next/Library/2019-08-11-16-28-03.bpo-26543.X-TJZO.rst +++ /dev/null @@ -1 +0,0 @@ -Fix :meth:`IMAP4.noop()` when debug mode is enabled (ex: ``imaplib.Debug = 3``). diff --git a/Misc/NEWS.d/next/Library/2019-09-12-21-34-03.bpo-38144.8uQCdd.rst b/Misc/NEWS.d/next/Library/2019-09-12-21-34-03.bpo-38144.8uQCdd.rst deleted file mode 100644 index 2c335bf29cfb3..0000000000000 --- a/Misc/NEWS.d/next/Library/2019-09-12-21-34-03.bpo-38144.8uQCdd.rst +++ /dev/null @@ -1 +0,0 @@ -Added the *root_dir* and *dir_fd* parameters in :func:`glob.glob`. diff --git a/Misc/NEWS.d/next/Library/2019-10-25-23-45-49.bpo-35714.fw3xb7.rst b/Misc/NEWS.d/next/Library/2019-10-25-23-45-49.bpo-35714.fw3xb7.rst deleted file mode 100644 index 39102065ca7b5..0000000000000 --- a/Misc/NEWS.d/next/Library/2019-10-25-23-45-49.bpo-35714.fw3xb7.rst +++ /dev/null @@ -1,2 +0,0 @@ -:exc:`struct.error` is now raised if there is a null character in a -:mod:`struct` format string. diff --git a/Misc/NEWS.d/next/Library/2019-11-13-07-37-11.bpo-38731.9qmcSx.rst b/Misc/NEWS.d/next/Library/2019-11-13-07-37-11.bpo-38731.9qmcSx.rst deleted file mode 100644 index ba9e522ecfcbf..0000000000000 --- a/Misc/NEWS.d/next/Library/2019-11-13-07-37-11.bpo-38731.9qmcSx.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add ``--quiet`` option to command-line interface of :mod:`py_compile`. -Patch by Gregory Schevchenko. diff --git a/Misc/NEWS.d/next/Library/2019-12-15-18-47-20.bpo-39040.tKa0Qs.rst b/Misc/NEWS.d/next/Library/2019-12-15-18-47-20.bpo-39040.tKa0Qs.rst deleted file mode 100644 index 078bce22be30f..0000000000000 --- a/Misc/NEWS.d/next/Library/2019-12-15-18-47-20.bpo-39040.tKa0Qs.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix parsing of invalid mime headers parameters by collapsing whitespace between -encoded words in a bare-quote-string. diff --git a/Misc/NEWS.d/next/Library/2020-02-23-15-09-47.bpo-39244.aBK5IM.rst b/Misc/NEWS.d/next/Library/2020-02-23-15-09-47.bpo-39244.aBK5IM.rst deleted file mode 100644 index c7d8e0de676b5..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-02-23-15-09-47.bpo-39244.aBK5IM.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fixed :class:`multiprocessing.context.get_all_start_methods` -to properly return the default method first on macOS. diff --git a/Misc/NEWS.d/next/Library/2020-02-24-10-58-34.bpo-39728.kOOaHn.rst b/Misc/NEWS.d/next/Library/2020-02-24-10-58-34.bpo-39728.kOOaHn.rst deleted file mode 100644 index beb2016a85ba6..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-02-24-10-58-34.bpo-39728.kOOaHn.rst +++ /dev/null @@ -1 +0,0 @@ -fix default `_missing_` so a duplicate `ValueError` is not set as the `__context__` of the original `ValueError` diff --git a/Misc/NEWS.d/next/Library/2020-03-11-07-44-06.bpo-31122.zIQ80l.rst b/Misc/NEWS.d/next/Library/2020-03-11-07-44-06.bpo-31122.zIQ80l.rst deleted file mode 100644 index 2e70f7aee65c8..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-03-11-07-44-06.bpo-31122.zIQ80l.rst +++ /dev/null @@ -1 +0,0 @@ -ssl.wrap_socket() now raises ssl.SSLEOFError rather than OSError when peer closes connection during TLS negotiation \ No newline at end of file diff --git a/Misc/NEWS.d/next/Library/2020-03-29-21-32-00.bpo-40084.MCYwcv.rst b/Misc/NEWS.d/next/Library/2020-03-29-21-32-00.bpo-40084.MCYwcv.rst deleted file mode 100644 index 65ff4ce36e82e..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-03-29-21-32-00.bpo-40084.MCYwcv.rst +++ /dev/null @@ -1 +0,0 @@ -Fix ``Enum.__dir__``: dir(Enum.member) now includes attributes as well as methods. diff --git a/Misc/NEWS.d/next/Library/2020-04-03-16-13-59.bpo-40105.hfM2c0.rst b/Misc/NEWS.d/next/Library/2020-04-03-16-13-59.bpo-40105.hfM2c0.rst deleted file mode 100644 index f71a7a1e697a4..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-04-03-16-13-59.bpo-40105.hfM2c0.rst +++ /dev/null @@ -1,2 +0,0 @@ -ZipFile truncates files to avoid corruption when a shorter comment is provided -in append ("a") mode. Patch by Jan Mazur. diff --git a/Misc/NEWS.d/next/Library/2020-04-18-14-16-02.bpo-40318.K2UdRx.rst b/Misc/NEWS.d/next/Library/2020-04-18-14-16-02.bpo-40318.K2UdRx.rst deleted file mode 100644 index 3d5fcfb74a0fe..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-04-18-14-16-02.bpo-40318.K2UdRx.rst +++ /dev/null @@ -1 +0,0 @@ -Use SQLite3 trace v2 API, if it is available. diff --git a/Misc/NEWS.d/next/Library/2020-04-20-22-08-36.bpo-23082.iX90Id.rst b/Misc/NEWS.d/next/Library/2020-04-20-22-08-36.bpo-23082.iX90Id.rst deleted file mode 100644 index 13ed0defe529c..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-04-20-22-08-36.bpo-23082.iX90Id.rst +++ /dev/null @@ -1 +0,0 @@ -Updated the error message and docs of PurePath.relative_to() to better reflect the function behaviour. \ No newline at end of file diff --git a/Misc/NEWS.d/next/Library/2020-04-23-18-21-19.bpo-39385.MIAyS7.rst b/Misc/NEWS.d/next/Library/2020-04-23-18-21-19.bpo-39385.MIAyS7.rst deleted file mode 100644 index e6c5c0dd4380b..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-04-23-18-21-19.bpo-39385.MIAyS7.rst +++ /dev/null @@ -1,3 +0,0 @@ -A new test assertion context-manager, :func:`unittest.assertNoLogs` will -ensure a given block of code emits no log messages using the logging module. -Contributed by Kit Yan Choi. diff --git a/Misc/NEWS.d/next/Library/2020-05-06-02-01-25.bpo-13097.Wh5xSK.rst b/Misc/NEWS.d/next/Library/2020-05-06-02-01-25.bpo-13097.Wh5xSK.rst deleted file mode 100644 index a7f5f58828917..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-05-06-02-01-25.bpo-13097.Wh5xSK.rst +++ /dev/null @@ -1 +0,0 @@ -``ctypes`` now raises an ``ArgumentError`` when a callback is invoked with more than 1024 arguments. \ No newline at end of file diff --git a/Misc/NEWS.d/next/Library/2020-05-07-22-00-12.bpo-39881.E1xsNv.rst b/Misc/NEWS.d/next/Library/2020-05-07-22-00-12.bpo-39881.E1xsNv.rst deleted file mode 100644 index 1129cd7649b96..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-05-07-22-00-12.bpo-39881.E1xsNv.rst +++ /dev/null @@ -1,2 +0,0 @@ -PEP 554 for use in the test suite. -(Patch By Joannah Nanjekye) \ No newline at end of file diff --git a/Misc/NEWS.d/next/Library/2020-05-13-16-28-33.bpo-40611.ZCk0_c.rst b/Misc/NEWS.d/next/Library/2020-05-13-16-28-33.bpo-40611.ZCk0_c.rst deleted file mode 100644 index 50ef3ad200a5e..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-05-13-16-28-33.bpo-40611.ZCk0_c.rst +++ /dev/null @@ -1 +0,0 @@ -:data:`~mmap.MAP_POPULATE` constant has now been added to the list of exported :mod:`mmap` module flags. diff --git a/Misc/NEWS.d/next/Library/2020-05-15-21-14-45.bpo-36543.Jt-eSX.rst b/Misc/NEWS.d/next/Library/2020-05-15-21-14-45.bpo-36543.Jt-eSX.rst deleted file mode 100644 index 468c1ac9eee17..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-05-15-21-14-45.bpo-36543.Jt-eSX.rst +++ /dev/null @@ -1 +0,0 @@ -Restored the deprecated :mod:`xml.etree.cElementTree` module. diff --git a/Misc/NEWS.d/next/Library/2020-05-17-02-03-09.bpo-32309.KM9psl.rst b/Misc/NEWS.d/next/Library/2020-05-17-02-03-09.bpo-32309.KM9psl.rst deleted file mode 100644 index 6272c35edf4d5..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-05-17-02-03-09.bpo-32309.KM9psl.rst +++ /dev/null @@ -1,4 +0,0 @@ -Added a new :term:`coroutine` :func:`asyncio.to_thread`. It is mainly used for -running IO-bound functions in a separate thread to avoid blocking the event -loop, and essentially works as a high-level version of -:meth:`~asyncio.loop.run_in_executor` that can directly take keyword arguments. \ No newline at end of file diff --git a/Misc/NEWS.d/next/Library/2020-05-18-15-26-31.bpo-40671.NeZ9Cy.rst b/Misc/NEWS.d/next/Library/2020-05-18-15-26-31.bpo-40671.NeZ9Cy.rst deleted file mode 100644 index d38b88dbf356d..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-05-18-15-26-31.bpo-40671.NeZ9Cy.rst +++ /dev/null @@ -1 +0,0 @@ -Prepare ``_hashlib`` for :pep:`489` and use :c:func:`PyModule_AddType`. diff --git a/Misc/NEWS.d/next/Library/2020-05-18-15-38-25.bpo-25920.PxrLY8.rst b/Misc/NEWS.d/next/Library/2020-05-18-15-38-25.bpo-25920.PxrLY8.rst deleted file mode 100644 index cc60e976286c3..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-05-18-15-38-25.bpo-25920.PxrLY8.rst +++ /dev/null @@ -1,7 +0,0 @@ -On macOS, when building Python for macOS 10.4 and older, which wasn't the case -for python.org macOS installer, :func:`socket.getaddrinfo` no longer uses an -internal lock to prevent race conditions when calling ``getaddrinfo()`` which -is thread-safe since macOS 10.5. Python 3.9 requires macOS 10.6 or newer. The -internal lock caused random hang on fork when another thread was calling -:func:`socket.getaddrinfo`. The lock was also used on FreeBSD older than 5.3, -OpenBSD older than 201311 and NetBSD older than 4. diff --git a/Misc/NEWS.d/next/Library/2020-05-18-17-29-30.bpo-40626.NeZufF.rst b/Misc/NEWS.d/next/Library/2020-05-18-17-29-30.bpo-40626.NeZufF.rst deleted file mode 100644 index fe652cd7ee39d..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-05-18-17-29-30.bpo-40626.NeZufF.rst +++ /dev/null @@ -1 +0,0 @@ -Add h5 file extension as MIME Type application/x-hdf5, as per HDF Group recommendation for HDF5 formatted data files. Patch contributed by Mark Schwab. diff --git a/Misc/NEWS.d/next/Library/2020-05-18-22-41-02.bpo-40614.8j3kmq.rst b/Misc/NEWS.d/next/Library/2020-05-18-22-41-02.bpo-40614.8j3kmq.rst deleted file mode 100644 index 238b98c14a326..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-05-18-22-41-02.bpo-40614.8j3kmq.rst +++ /dev/null @@ -1 +0,0 @@ -:func:`ast.parse` will not parse self documenting expressions in f-strings when passed ``feature_version`` is less than ``(3, 8)``. diff --git a/Misc/NEWS.d/next/Library/2020-05-20-12-53-20.bpo-9216.ps7Yf1.rst b/Misc/NEWS.d/next/Library/2020-05-20-12-53-20.bpo-9216.ps7Yf1.rst deleted file mode 100644 index 37542e8caffd4..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-05-20-12-53-20.bpo-9216.ps7Yf1.rst +++ /dev/null @@ -1,3 +0,0 @@ -func:`hashlib.new` passed ``usedforsecurity`` to OpenSSL EVP constructor -``_hashlib.new()``. test_hashlib and test_smtplib handle strict security -policy better. diff --git a/Misc/NEWS.d/next/Library/2020-05-20-13-03-28.bpo-40695.lr4aIS.rst b/Misc/NEWS.d/next/Library/2020-05-20-13-03-28.bpo-40695.lr4aIS.rst deleted file mode 100644 index 643779bab4948..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-05-20-13-03-28.bpo-40695.lr4aIS.rst +++ /dev/null @@ -1,3 +0,0 @@ -:mod:`hashlib` no longer falls back to builtin hash implementations when -OpenSSL provides a hash digest and the algorithm is blocked by security -policy. diff --git a/Misc/NEWS.d/next/Library/2020-05-20-14-38-04.bpo-40698.zwl5Hc.rst b/Misc/NEWS.d/next/Library/2020-05-20-14-38-04.bpo-40698.zwl5Hc.rst deleted file mode 100644 index e57624819d54a..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-05-20-14-38-04.bpo-40698.zwl5Hc.rst +++ /dev/null @@ -1,2 +0,0 @@ -:mod:`distutils` upload creates SHA2-256 and Blake2b-256 digests. MD5 -digests is skipped if platform blocks MD5. diff --git a/Misc/NEWS.d/next/Library/2020-05-22-12-45-58.bpo-40726.7oBdMw.rst b/Misc/NEWS.d/next/Library/2020-05-22-12-45-58.bpo-40726.7oBdMw.rst deleted file mode 100644 index 7409eb3d80df6..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-05-22-12-45-58.bpo-40726.7oBdMw.rst +++ /dev/null @@ -1,2 +0,0 @@ -Handle cases where the ``end_lineno`` is ``None`` on -:func:`ast.increment_lineno`. diff --git a/Misc/NEWS.d/next/Library/2020-05-23-00-22-11.bpo-40737.iph-CM.rst b/Misc/NEWS.d/next/Library/2020-05-23-00-22-11.bpo-40737.iph-CM.rst deleted file mode 100644 index f068d3a091a03..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-05-23-00-22-11.bpo-40737.iph-CM.rst +++ /dev/null @@ -1 +0,0 @@ -Fix possible reference leak for :mod:`sqlite3` initialization. diff --git a/Misc/NEWS.d/next/Library/2020-05-23-04-18-00.bpo-37129.YoYoYo.rst b/Misc/NEWS.d/next/Library/2020-05-23-04-18-00.bpo-37129.YoYoYo.rst deleted file mode 100644 index e025e96f4f1c0..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-05-23-04-18-00.bpo-37129.YoYoYo.rst +++ /dev/null @@ -1 +0,0 @@ -Add a new :data:`os.RWF_APPEND` flag for :func:`os.pwritev`. diff --git a/Misc/NEWS.d/next/Library/2020-05-24-11-06-37.bpo-40756.7ZH83z.rst b/Misc/NEWS.d/next/Library/2020-05-24-11-06-37.bpo-40756.7ZH83z.rst deleted file mode 100644 index a970f5be156f5..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-05-24-11-06-37.bpo-40756.7ZH83z.rst +++ /dev/null @@ -1,2 +0,0 @@ -The second argument (extra) of ``LoggerAdapter.__init__`` now defaults to -None. diff --git a/Misc/NEWS.d/next/Library/2020-05-24-23-52-35.bpo-40759.DdZdaw.rst b/Misc/NEWS.d/next/Library/2020-05-24-23-52-35.bpo-40759.DdZdaw.rst deleted file mode 100644 index e77da3ac3dfa9..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-05-24-23-52-35.bpo-40759.DdZdaw.rst +++ /dev/null @@ -1 +0,0 @@ -Deprecate the :mod:`symbol` module. diff --git a/Misc/NEWS.d/next/Library/2020-05-25-11-52-23.bpo-30064.6CICsH.rst b/Misc/NEWS.d/next/Library/2020-05-25-11-52-23.bpo-30064.6CICsH.rst deleted file mode 100644 index 904991dca16d8..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-05-25-11-52-23.bpo-30064.6CICsH.rst +++ /dev/null @@ -1 +0,0 @@ -Fix asyncio ``loop.sock_*`` race condition issue diff --git a/Misc/NEWS.d/next/Library/2020-05-25-22-18-38.bpo-30008.CKC3td.rst b/Misc/NEWS.d/next/Library/2020-05-25-22-18-38.bpo-30008.CKC3td.rst deleted file mode 100644 index c4cfa56ce02c5..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-05-25-22-18-38.bpo-30008.CKC3td.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix :mod:`ssl` code to be compatible with OpenSSL 1.1.x builds that use -``no-deprecated`` and ``--api=1.1.0``. diff --git a/Misc/NEWS.d/next/Library/2020-05-27-00-09-52.bpo-16995.4niOT7.rst b/Misc/NEWS.d/next/Library/2020-05-27-00-09-52.bpo-16995.4niOT7.rst deleted file mode 100644 index 88b95998d085f..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-05-27-00-09-52.bpo-16995.4niOT7.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add :func:`base64.b32hexencode` and :func:`base64.b32hexdecode` to support the -Base32 Encoding with Extended Hex Alphabet. diff --git a/Misc/NEWS.d/next/Library/2020-05-27-17-00-18.bpo-40795.eZSnHA.rst b/Misc/NEWS.d/next/Library/2020-05-27-17-00-18.bpo-40795.eZSnHA.rst deleted file mode 100644 index dd02fb05cab5e..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-05-27-17-00-18.bpo-40795.eZSnHA.rst +++ /dev/null @@ -1,4 +0,0 @@ -:mod:`ctypes` module: If ctypes fails to convert the result of a callback or -if a ctypes callback function raises an exception, sys.unraisablehook is now -called with an exception set. Previously, the error was logged into stderr -by :c:func:`PyErr_Print`. diff --git a/Misc/NEWS.d/next/Library/2020-05-27-18-04-52.bpo-40791.IzpNor.rst b/Misc/NEWS.d/next/Library/2020-05-27-18-04-52.bpo-40791.IzpNor.rst deleted file mode 100644 index b88f308ec3b52..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-05-27-18-04-52.bpo-40791.IzpNor.rst +++ /dev/null @@ -1,2 +0,0 @@ -:func:`hashlib.compare_digest` uses OpenSSL's ``CRYPTO_memcmp()`` function -when OpenSSL is available. diff --git a/Misc/NEWS.d/next/Library/2020-05-27-21-27-01.bpo-40767.L5MnVV.rst b/Misc/NEWS.d/next/Library/2020-05-27-21-27-01.bpo-40767.L5MnVV.rst deleted file mode 100644 index 4bebb311b4d54..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-05-27-21-27-01.bpo-40767.L5MnVV.rst +++ /dev/null @@ -1,3 +0,0 @@ -:mod:`webbrowser` now properly finds the default browser in pure Wayland -systems by checking the WAYLAND_DISPLAY environment variable. Patch -contributed by J?r?my Attali. diff --git a/Misc/NEWS.d/next/Library/2020-05-27-22-19-42.bpo-40792.87Yx01.rst b/Misc/NEWS.d/next/Library/2020-05-27-22-19-42.bpo-40792.87Yx01.rst deleted file mode 100644 index 032a96c6a5cb6..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-05-27-22-19-42.bpo-40792.87Yx01.rst +++ /dev/null @@ -1,2 +0,0 @@ -The result of :func:`operator.index` now always has exact type :class:`int`. -Previously, the result could have been an instance of a subclass of ``int``. diff --git a/Misc/NEWS.d/next/Library/2020-05-28-16-51-00.bpo-38488.hFQNgA.rst b/Misc/NEWS.d/next/Library/2020-05-28-16-51-00.bpo-38488.hFQNgA.rst deleted file mode 100644 index c44da9fecb605..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-05-28-16-51-00.bpo-38488.hFQNgA.rst +++ /dev/null @@ -1 +0,0 @@ -Update ensurepip to install pip 20.1.1 and setuptools 47.1.0. diff --git a/Misc/NEWS.d/next/Library/2020-05-28-17-32-29.bpo-40777.1kJU6N.rst b/Misc/NEWS.d/next/Library/2020-05-28-17-32-29.bpo-40777.1kJU6N.rst deleted file mode 100644 index 761bc83562c34..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-05-28-17-32-29.bpo-40777.1kJU6N.rst +++ /dev/null @@ -1,2 +0,0 @@ -Initialize PyDateTime_IsoCalendarDateType.tp_base at run-time to avoid -errors on some compilers. diff --git a/Misc/NEWS.d/next/Library/2020-05-30-08-10-23.bpo-40744.jKURVV.rst b/Misc/NEWS.d/next/Library/2020-05-30-08-10-23.bpo-40744.jKURVV.rst deleted file mode 100644 index 2d1d1f9a20e32..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-05-30-08-10-23.bpo-40744.jKURVV.rst +++ /dev/null @@ -1,4 +0,0 @@ -The :mod:`sqlite3` module uses SQLite API functions that require SQLite -v3.7.3 or higher. This patch removes support for older SQLite versions, and -explicitly requires SQLite 3.7.3 both at build, compile and runtime. Patch by -Sergey Fedoseev and Erlend E. Aasland. diff --git a/Misc/NEWS.d/next/Library/2020-05-30-12-44-29.bpo-39384.Iqxy3q.rst b/Misc/NEWS.d/next/Library/2020-05-30-12-44-29.bpo-39384.Iqxy3q.rst deleted file mode 100644 index 482ae624da079..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-05-30-12-44-29.bpo-39384.Iqxy3q.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed email.contentmanager to allow set_content() to set a null string. diff --git a/Misc/NEWS.d/next/Library/2020-05-30-14-19-47.bpo-26407.MjWLO1.rst b/Misc/NEWS.d/next/Library/2020-05-30-14-19-47.bpo-26407.MjWLO1.rst deleted file mode 100644 index d0e45cf1b1f2f..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-05-30-14-19-47.bpo-26407.MjWLO1.rst +++ /dev/null @@ -1,3 +0,0 @@ -Unexpected errors in calling the ``__iter__`` method are no longer masked -by ``TypeError`` in :func:`csv.reader`, :func:`csv.writer.writerow` and -:meth:`csv.writer.writerows`. diff --git a/Misc/NEWS.d/next/Library/2020-05-30-18-48-58.bpo-40755.IyOe2J.rst b/Misc/NEWS.d/next/Library/2020-05-30-18-48-58.bpo-40755.IyOe2J.rst deleted file mode 100644 index be5653ea58f27..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-05-30-18-48-58.bpo-40755.IyOe2J.rst +++ /dev/null @@ -1 +0,0 @@ -Add rich comparisons to collections.Counter(). diff --git a/Misc/NEWS.d/next/Library/2020-05-31-15-52-18.bpo-40834.MO9_hb.rst b/Misc/NEWS.d/next/Library/2020-05-31-15-52-18.bpo-40834.MO9_hb.rst deleted file mode 100644 index 272783773ff94..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-05-31-15-52-18.bpo-40834.MO9_hb.rst +++ /dev/null @@ -1 +0,0 @@ -Fix truncate when sending str object with_xxsubinterpreters.channel_send. \ No newline at end of file diff --git a/Misc/NEWS.d/next/Library/2020-05-31-23-32-36.bpo-17005.JlRUGB.rst b/Misc/NEWS.d/next/Library/2020-05-31-23-32-36.bpo-17005.JlRUGB.rst deleted file mode 100644 index 0fd01fb623093..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-05-31-23-32-36.bpo-17005.JlRUGB.rst +++ /dev/null @@ -1,4 +0,0 @@ -The topological sort functionality that was introduced initially in the -:mod:`functools` module has been moved to a new :mod:`graphlib` module to -better accommodate the new tools and keep the original scope of the -:mod:`functools` module. Patch by Pablo Galindo diff --git a/Misc/NEWS.d/next/Library/2020-06-01-02-16-29.bpo-39314.0T9hlA.rst b/Misc/NEWS.d/next/Library/2020-06-01-02-16-29.bpo-39314.0T9hlA.rst deleted file mode 100644 index e805332efb626..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-06-01-02-16-29.bpo-39314.0T9hlA.rst +++ /dev/null @@ -1,3 +0,0 @@ -:class:`rlcompleter.Completer` and the standard Python shell now close the -parenthesis for functions that take no arguments. Patch contributed by R?mi -Lapeyre. diff --git a/Misc/NEWS.d/next/Library/2020-06-02-02-16-02.bpo-39791.StCJlA.rst b/Misc/NEWS.d/next/Library/2020-06-02-02-16-02.bpo-39791.StCJlA.rst deleted file mode 100644 index 61753a57ca8b7..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-06-02-02-16-02.bpo-39791.StCJlA.rst +++ /dev/null @@ -1 +0,0 @@ -Built-in loaders (SourceFileLoader and ZipImporter) now supply ``TraversableResources`` implementations for ``ResourceReader``, and the fallback function has been removed. diff --git a/Misc/NEWS.d/next/Library/2020-06-02-23-49-07.bpo-32604.ZN4V4l.rst b/Misc/NEWS.d/next/Library/2020-06-02-23-49-07.bpo-32604.ZN4V4l.rst deleted file mode 100644 index af284b06eaed6..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-06-02-23-49-07.bpo-32604.ZN4V4l.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix reference leak in the :mod:`select` module when the module is -imported in a subinterpreter. diff --git a/Misc/NEWS.d/next/Library/2020-06-04-16-25-15.bpo-40807.yYyLWx.rst b/Misc/NEWS.d/next/Library/2020-06-04-16-25-15.bpo-40807.yYyLWx.rst deleted file mode 100644 index c64a86295d770..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-06-04-16-25-15.bpo-40807.yYyLWx.rst +++ /dev/null @@ -1,2 +0,0 @@ -Stop codeop._maybe_compile, used by code.InteractiveInterpreter (and IDLE). -from emitting each warning three times. diff --git a/Misc/NEWS.d/next/Library/2020-06-05-19-29-10.bpo-39791._CcO3d.rst b/Misc/NEWS.d/next/Library/2020-06-05-19-29-10.bpo-39791._CcO3d.rst deleted file mode 100644 index 73e0cbb013f84..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-06-05-19-29-10.bpo-39791._CcO3d.rst +++ /dev/null @@ -1 +0,0 @@ -Refresh importlib.metadata from importlib_metadata 1.6.1. \ No newline at end of file diff --git a/Misc/NEWS.d/next/Library/2020-06-05-20-00-18.bpo-40876.zDhiZj.rst b/Misc/NEWS.d/next/Library/2020-06-05-20-00-18.bpo-40876.zDhiZj.rst deleted file mode 100644 index 75f62addbabbc..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-06-05-20-00-18.bpo-40876.zDhiZj.rst +++ /dev/null @@ -1 +0,0 @@ -Clarify error message in the :mod:`csv` module. diff --git a/Misc/NEWS.d/next/Library/2020-06-06-02-42-26.bpo-40884.n7fOwS.rst b/Misc/NEWS.d/next/Library/2020-06-06-02-42-26.bpo-40884.n7fOwS.rst deleted file mode 100644 index 64990e8023fba..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-06-06-02-42-26.bpo-40884.n7fOwS.rst +++ /dev/null @@ -1,3 +0,0 @@ -Added a `defaults` parameter to :class:`logging.Formatter`, to allow -specifying default values for custom fields. Patch by Asaf Alon and Bar -Harel. diff --git a/Misc/NEWS.d/next/Library/2020-06-06-14-09-55.bpo-33689.EFUDH7.rst b/Misc/NEWS.d/next/Library/2020-06-06-14-09-55.bpo-33689.EFUDH7.rst deleted file mode 100644 index bc0756d02ddc9..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-06-06-14-09-55.bpo-33689.EFUDH7.rst +++ /dev/null @@ -1,4 +0,0 @@ -Ignore empty or whitespace-only lines in .pth files. This matches the -documentated behavior. Before, empty lines caused the site-packages -dir to appear multiple times in sys.path. -By Ido Michael, contributors Malcolm Smith and Tal Einat. diff --git a/Misc/NEWS.d/next/Library/2020-06-08-18-59-16.bpo-23427.ilg1Cz.rst b/Misc/NEWS.d/next/Library/2020-06-08-18-59-16.bpo-23427.ilg1Cz.rst deleted file mode 100644 index 37382975bb4fc..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-06-08-18-59-16.bpo-23427.ilg1Cz.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add :data:`sys.orig_argv` attribute: the list of the original command line -arguments passed to the Python executable. diff --git a/Misc/NEWS.d/next/Library/2020-06-11-11-07-10.bpo-40939.-D5Asl.rst b/Misc/NEWS.d/next/Library/2020-06-11-11-07-10.bpo-40939.-D5Asl.rst deleted file mode 100644 index 0e831129dd87e..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-06-11-11-07-10.bpo-40939.-D5Asl.rst +++ /dev/null @@ -1 +0,0 @@ -Use the new PEG parser when generating the stdlib :mod:`keyword` module. \ No newline at end of file diff --git a/Misc/NEWS.d/next/Library/2020-06-12-10-44-15.bpo-40855.jSot83.rst b/Misc/NEWS.d/next/Library/2020-06-12-10-44-15.bpo-40855.jSot83.rst deleted file mode 100644 index 201d510327a47..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-06-12-10-44-15.bpo-40855.jSot83.rst +++ /dev/null @@ -1,2 +0,0 @@ -The standard deviation and variance functions in the statistics module were -ignoring their mu and xbar arguments. diff --git a/Misc/NEWS.d/next/Library/2020-06-12-11-55-30.bpo-40955.huixCg.rst b/Misc/NEWS.d/next/Library/2020-06-12-11-55-30.bpo-40955.huixCg.rst deleted file mode 100644 index 9a9803044ec96..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-06-12-11-55-30.bpo-40955.huixCg.rst +++ /dev/null @@ -1 +0,0 @@ -Fix a minor memory leak in :mod:`subprocess` module when extra_groups was specified. diff --git a/Misc/NEWS.d/next/Library/2020-06-13-12-04-50.bpo-40924.SM_luS.rst b/Misc/NEWS.d/next/Library/2020-06-13-12-04-50.bpo-40924.SM_luS.rst deleted file mode 100644 index 4e4c6e88ac572..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-06-13-12-04-50.bpo-40924.SM_luS.rst +++ /dev/null @@ -1,3 +0,0 @@ -Ensure ``importlib.resources.path`` returns an extant path for the -SourceFileLoader's resource reader. Avoids the regression identified in -master while a long-term solution is devised. diff --git a/Misc/NEWS.d/next/Library/2020-06-15-00-13-57.bpo-40967._dx3OO.rst b/Misc/NEWS.d/next/Library/2020-06-15-00-13-57.bpo-40967._dx3OO.rst deleted file mode 100644 index 4694d991babd7..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-06-15-00-13-57.bpo-40967._dx3OO.rst +++ /dev/null @@ -1,2 +0,0 @@ -Removed :meth:`asyncio.Task.current_task` and -:meth:`asyncio.Task.all_tasks`. Patch contributed by R?mi Lapeyre. diff --git a/Misc/NEWS.d/next/Library/2020-06-15-12-22-53.bpo-40448.1dk8Bu.rst b/Misc/NEWS.d/next/Library/2020-06-15-12-22-53.bpo-40448.1dk8Bu.rst deleted file mode 100644 index a755c5faa671c..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-06-15-12-22-53.bpo-40448.1dk8Bu.rst +++ /dev/null @@ -1,2 +0,0 @@ -:mod:`ensurepip` now disables the use of `pip` cache when installing the -bundled versions of `pip` and `setuptools`. Patch by Krzysztof Konopko. diff --git a/Misc/NEWS.d/next/Library/2020-06-17-17-26-24.bpo-41002.NPBItE.rst b/Misc/NEWS.d/next/Library/2020-06-17-17-26-24.bpo-41002.NPBItE.rst deleted file mode 100644 index c3eebb7b9aed7..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-06-17-17-26-24.bpo-41002.NPBItE.rst +++ /dev/null @@ -1 +0,0 @@ -Improve performance of HTTPResponse.read with a given amount. Patch by Bruce Merry. diff --git a/Misc/NEWS.d/next/Library/2020-06-17-23-49-45.bpo-35018.NP5_Qk.rst b/Misc/NEWS.d/next/Library/2020-06-17-23-49-45.bpo-35018.NP5_Qk.rst deleted file mode 100644 index f764323ae631c..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-06-17-23-49-45.bpo-35018.NP5_Qk.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add the :class:`xml.sax.handler.LexicalHandler` class that is present in -other SAX XML implementations. diff --git a/Misc/NEWS.d/next/Library/2020-06-18-10-34-59.bpo-41025.elf_nz.rst b/Misc/NEWS.d/next/Library/2020-06-18-10-34-59.bpo-41025.elf_nz.rst deleted file mode 100644 index 21e184d0a4063..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-06-18-10-34-59.bpo-41025.elf_nz.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fixed an issue preventing the C implementation of :class:`zoneinfo.ZoneInfo` -from being subclassed. diff --git a/Misc/NEWS.d/next/Library/2020-06-20-00-19-30.bpo-41043.p-Pk-H.rst b/Misc/NEWS.d/next/Library/2020-06-20-00-19-30.bpo-41043.p-Pk-H.rst deleted file mode 100644 index 9c6020eb8d738..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-06-20-00-19-30.bpo-41043.p-Pk-H.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fixed the use of :func:`~glob.glob` in the stdlib: literal part of the path -is now always correctly escaped. diff --git a/Misc/NEWS.d/next/Library/2020-06-20-10-16-57.bpo-41048.hEXB-B.rst b/Misc/NEWS.d/next/Library/2020-06-20-10-16-57.bpo-41048.hEXB-B.rst deleted file mode 100644 index 2595900137d69..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-06-20-10-16-57.bpo-41048.hEXB-B.rst +++ /dev/null @@ -1,2 +0,0 @@ -:func:`mimetypes.read_mime_types` function reads the rule file using UTF-8 encoding, not the locale encoding. -Patch by Srinivas Reddy Thatiparthy. \ No newline at end of file diff --git a/Misc/NEWS.d/next/Library/2020-06-20-18-33-03.bpo-41056.gTH4Bq.rst b/Misc/NEWS.d/next/Library/2020-06-20-18-33-03.bpo-41056.gTH4Bq.rst deleted file mode 100644 index 0439d82a50ad1..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-06-20-18-33-03.bpo-41056.gTH4Bq.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed an instance where a MemoryError within the zoneinfo module might not be reported or not reported at its source. (found by Coverity) \ No newline at end of file diff --git a/Misc/NEWS.d/next/Library/2020-06-20-18-35-43.bpo-41056.Garcle.rst b/Misc/NEWS.d/next/Library/2020-06-20-18-35-43.bpo-41056.Garcle.rst deleted file mode 100644 index 1776f0d1cf8a3..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-06-20-18-35-43.bpo-41056.Garcle.rst +++ /dev/null @@ -1 +0,0 @@ -Fix a NULL pointer dereference within the ssl module during a MemoryError in the keylog callback. (discovered by Coverity) \ No newline at end of file diff --git a/Misc/NEWS.d/next/Library/2020-06-20-18-37-29.bpo-41056.d9v_uL.rst b/Misc/NEWS.d/next/Library/2020-06-20-18-37-29.bpo-41056.d9v_uL.rst deleted file mode 100644 index ddcc1102d5ed7..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-06-20-18-37-29.bpo-41056.d9v_uL.rst +++ /dev/null @@ -1 +0,0 @@ -Invalid file descriptor values are now prevented from being passed to os.fpathconf. (discovered by Coverity) \ No newline at end of file diff --git a/Misc/NEWS.d/next/Library/2020-06-20-21-03-55.bpo-41058.gztdZy.rst b/Misc/NEWS.d/next/Library/2020-06-20-21-03-55.bpo-41058.gztdZy.rst deleted file mode 100644 index 6ac90098aa52b..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-06-20-21-03-55.bpo-41058.gztdZy.rst +++ /dev/null @@ -1 +0,0 @@ -:func:`pdb.find_function` now correctly determines the source file encoding. diff --git a/Misc/NEWS.d/next/Library/2020-06-22-10-25-39.bpo-41068._bX2BW.rst b/Misc/NEWS.d/next/Library/2020-06-22-10-25-39.bpo-41068._bX2BW.rst deleted file mode 100644 index 20580c7886fac..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-06-22-10-25-39.bpo-41068._bX2BW.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fixed reading files with non-ASCII names from ZIP archive directly after -writing them. diff --git a/Misc/NEWS.d/next/Library/2020-06-22-20-08-40.bpo-31938.EVuko9.rst b/Misc/NEWS.d/next/Library/2020-06-22-20-08-40.bpo-31938.EVuko9.rst deleted file mode 100644 index 0488e94d42e8c..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-06-22-20-08-40.bpo-31938.EVuko9.rst +++ /dev/null @@ -1 +0,0 @@ -Fix default-value signatures of several functions in the :mod:`select` module - by Anthony Sottile. diff --git a/Misc/NEWS.d/next/Library/2020-06-23-06-09-59.bpo-40521.HUfxP7.rst b/Misc/NEWS.d/next/Library/2020-06-23-06-09-59.bpo-40521.HUfxP7.rst deleted file mode 100644 index 7689a1470b034..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-06-23-06-09-59.bpo-40521.HUfxP7.rst +++ /dev/null @@ -1 +0,0 @@ -Remove freelist from collections.deque(). diff --git a/Misc/NEWS.d/next/Library/2020-06-25-10-11-47.bpo-31082.HsgDkx.rst b/Misc/NEWS.d/next/Library/2020-06-25-10-11-47.bpo-31082.HsgDkx.rst deleted file mode 100644 index 9746d33a49638..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-06-25-10-11-47.bpo-31082.HsgDkx.rst +++ /dev/null @@ -1 +0,0 @@ -Use the term "iterable" in the docstring for :func:`functools.reduce`. diff --git a/Misc/NEWS.d/next/Library/2020-06-27-13-51-36.bpo-41138.bIpf7g.rst b/Misc/NEWS.d/next/Library/2020-06-27-13-51-36.bpo-41138.bIpf7g.rst deleted file mode 100644 index 839d430e89b66..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-06-27-13-51-36.bpo-41138.bIpf7g.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fixed the :mod:`trace` module CLI for Python source files with non-UTF-8 -encoding. diff --git a/Misc/NEWS.d/next/Library/2020-06-28-21-16-51.bpo-40874.YImvzA.rst b/Misc/NEWS.d/next/Library/2020-06-28-21-16-51.bpo-40874.YImvzA.rst deleted file mode 100644 index a43eab8f4dcdd..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-06-28-21-16-51.bpo-40874.YImvzA.rst +++ /dev/null @@ -1 +0,0 @@ -The decimal module now requires libmpdec-2.5.0. diff --git a/Misc/NEWS.d/next/Library/2020-06-30-20-50-51.bpo-41161.QTdJjz.rst b/Misc/NEWS.d/next/Library/2020-06-30-20-50-51.bpo-41161.QTdJjz.rst deleted file mode 100644 index 0d8fb521bad50..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-06-30-20-50-51.bpo-41161.QTdJjz.rst +++ /dev/null @@ -1,2 +0,0 @@ -The decimal module now requires libmpdec-2.5.0. Users of ---with-system-libmpdec should update their system library. diff --git a/Misc/NEWS.d/next/Library/2020-07-01-17-33-50.bpo-41182.FPFI0N.rst b/Misc/NEWS.d/next/Library/2020-07-01-17-33-50.bpo-41182.FPFI0N.rst deleted file mode 100644 index ae31db512abdc..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-07-01-17-33-50.bpo-41182.FPFI0N.rst +++ /dev/null @@ -1 +0,0 @@ -selector: use DefaultSelector based upon implementation diff --git a/Misc/NEWS.d/next/Library/2020-07-02-11-53-45.bpo-41193.8-Tnql.rst b/Misc/NEWS.d/next/Library/2020-07-02-11-53-45.bpo-41193.8-Tnql.rst deleted file mode 100644 index 8807d9c21febd..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-07-02-11-53-45.bpo-41193.8-Tnql.rst +++ /dev/null @@ -1,4 +0,0 @@ -The ``write_history()`` atexit function of the readline completer now -ignores any :exc:`OSError` to ignore error if the filesystem is read-only, -instead of only ignoring :exc:`FileNotFoundError` and -:exc:`PermissionError`. diff --git a/Misc/NEWS.d/next/Library/2020-07-02-15-03-04.bpo-41195.cEnpO3.rst b/Misc/NEWS.d/next/Library/2020-07-02-15-03-04.bpo-41195.cEnpO3.rst deleted file mode 100644 index f96d5fadbdc7b..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-07-02-15-03-04.bpo-41195.cEnpO3.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add read-only ssl.SSLContext.security_level attribute to retrieve the -context's security level. diff --git a/Misc/NEWS.d/next/Library/2020-07-03-13-15-08.bpo-41194.djrKjs.rst b/Misc/NEWS.d/next/Library/2020-07-03-13-15-08.bpo-41194.djrKjs.rst deleted file mode 100644 index d63a0e5222ba9..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-07-03-13-15-08.bpo-41194.djrKjs.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix a crash in the ``_ast`` module: it can no longer be loaded more than once. -It now uses a global state rather than a module state. diff --git a/Misc/NEWS.d/next/Library/2020-07-04-21-56-46.bpo-39168.DQWsXj.rst b/Misc/NEWS.d/next/Library/2020-07-04-21-56-46.bpo-39168.DQWsXj.rst deleted file mode 100644 index 667885eccd9c7..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-07-04-21-56-46.bpo-39168.DQWsXj.rst +++ /dev/null @@ -1 +0,0 @@ -Remove the ``__new__`` method of :class:`typing.Generic`. diff --git a/Misc/NEWS.d/next/Library/2020-07-05-19-16-02.bpo-29727.Q6Z2rg.rst b/Misc/NEWS.d/next/Library/2020-07-05-19-16-02.bpo-29727.Q6Z2rg.rst deleted file mode 100644 index 85cfa4f893811..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-07-05-19-16-02.bpo-29727.Q6Z2rg.rst +++ /dev/null @@ -1,2 +0,0 @@ -Register :class:`array.array` as a -:class:`~collections.abc.MutableSequence`. Patch by Pablo Galindo. diff --git a/Misc/NEWS.d/next/Library/2020-07-06-16-58-53.bpo-41207.Emw7Nk.rst b/Misc/NEWS.d/next/Library/2020-07-06-16-58-53.bpo-41207.Emw7Nk.rst deleted file mode 100644 index db99c63f94883..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-07-06-16-58-53.bpo-41207.Emw7Nk.rst +++ /dev/null @@ -1 +0,0 @@ -In distutils.spawn, restore expectation that DistutilsExecError is raised when the command is not found. diff --git a/Misc/NEWS.d/next/Library/2020-07-07-21-56-26.bpo-41235.H2csMU.rst b/Misc/NEWS.d/next/Library/2020-07-07-21-56-26.bpo-41235.H2csMU.rst deleted file mode 100644 index c55275bb1c622..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-07-07-21-56-26.bpo-41235.H2csMU.rst +++ /dev/null @@ -1 +0,0 @@ -Fix the error handling in :meth:`ssl.SSLContext.load_dh_params`. diff --git a/Misc/NEWS.d/next/Library/2020-07-11-00-15-01.bpo-41273.SVrsJh.rst b/Misc/NEWS.d/next/Library/2020-07-11-00-15-01.bpo-41273.SVrsJh.rst deleted file mode 100644 index c08204b9908c6..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-07-11-00-15-01.bpo-41273.SVrsJh.rst +++ /dev/null @@ -1,3 +0,0 @@ -Speed up any transport using ``_ProactorReadPipeTransport`` by calling -``recv_into`` instead of ``recv``, thus not creating a new buffer for each -``recv`` call in the transport's read loop. diff --git a/Misc/NEWS.d/next/Library/2020-07-12-22-16-58.bpo-39017.x3Cg-9.rst b/Misc/NEWS.d/next/Library/2020-07-12-22-16-58.bpo-39017.x3Cg-9.rst deleted file mode 100644 index ad26676f8b856..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-07-12-22-16-58.bpo-39017.x3Cg-9.rst +++ /dev/null @@ -1 +0,0 @@ -Avoid infinite loop when reading specially crafted TAR files using the tarfile module (CVE-2019-20907). diff --git a/Misc/NEWS.d/next/Library/2020-07-13-15-06-35.bpo-41288.8mn5P-.rst b/Misc/NEWS.d/next/Library/2020-07-13-15-06-35.bpo-41288.8mn5P-.rst deleted file mode 100644 index 3c3adbabf16ff..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-07-13-15-06-35.bpo-41288.8mn5P-.rst +++ /dev/null @@ -1,2 +0,0 @@ -Unpickling invalid NEWOBJ_EX opcode with the C implementation raises now -UnpicklingError instead of crashing. diff --git a/Misc/NEWS.d/next/Library/2020-07-18-18-07-40.bpo-41333.upkHIm.rst b/Misc/NEWS.d/next/Library/2020-07-18-18-07-40.bpo-41333.upkHIm.rst deleted file mode 100644 index 73e8b1199772d..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-07-18-18-07-40.bpo-41333.upkHIm.rst +++ /dev/null @@ -1 +0,0 @@ -:meth:`collections.OrderedDict.pop` is now 2 times faster. diff --git a/Misc/NEWS.d/next/Library/2020-07-20-13-27-48.bpo-41344.iKipNd.rst b/Misc/NEWS.d/next/Library/2020-07-20-13-27-48.bpo-41344.iKipNd.rst deleted file mode 100644 index 475bc9bddb0d5..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-07-20-13-27-48.bpo-41344.iKipNd.rst +++ /dev/null @@ -1 +0,0 @@ -Prevent creating :class:`shared_memory.SharedMemory` objects with :code:`size=0`. diff --git a/Misc/NEWS.d/next/Library/2020-07-20-19-13-17.bpo-41341.wqrj8C.rst b/Misc/NEWS.d/next/Library/2020-07-20-19-13-17.bpo-41341.wqrj8C.rst deleted file mode 100644 index c78b24d2faae7..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-07-20-19-13-17.bpo-41341.wqrj8C.rst +++ /dev/null @@ -1 +0,0 @@ -Recursive evaluation of `typing.ForwardRef` in `get_type_hints`. \ No newline at end of file diff --git a/Misc/NEWS.d/next/Library/2020-07-21-16-20-55.bpo-35328.jXovHb.rst b/Misc/NEWS.d/next/Library/2020-07-21-16-20-55.bpo-35328.jXovHb.rst deleted file mode 100644 index f4d1c6511d0d1..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-07-21-16-20-55.bpo-35328.jXovHb.rst +++ /dev/null @@ -1,2 +0,0 @@ -Set the environment variable ``VIRTUAL_ENV_PROMPT`` at :mod:`venv` -activation. diff --git a/Misc/NEWS.d/next/Library/2020-07-21-21-45-55.bpo-41364.5O-k7A.rst b/Misc/NEWS.d/next/Library/2020-07-21-21-45-55.bpo-41364.5O-k7A.rst deleted file mode 100644 index f136e892ae5fe..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-07-21-21-45-55.bpo-41364.5O-k7A.rst +++ /dev/null @@ -1 +0,0 @@ -Reduce import overhead of :mod:`uuid`. diff --git a/Misc/NEWS.d/next/Library/2020-07-23-01-18-34.bpo-41317.O17Z6x.rst b/Misc/NEWS.d/next/Library/2020-07-23-01-18-34.bpo-41317.O17Z6x.rst deleted file mode 100644 index 1af985e90e3e9..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-07-23-01-18-34.bpo-41317.O17Z6x.rst +++ /dev/null @@ -1,2 +0,0 @@ -Use add_done_callback() in asyncio.loop.sock_accept() to unsubscribe reader -early on cancellation. diff --git a/Misc/NEWS.d/next/Library/2020-07-26-21-18-43.bpo-41384.MlzIgV.rst b/Misc/NEWS.d/next/Library/2020-07-26-21-18-43.bpo-41384.MlzIgV.rst deleted file mode 100644 index d797374a09e6f..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-07-26-21-18-43.bpo-41384.MlzIgV.rst +++ /dev/null @@ -1,2 +0,0 @@ -Raise TclError instead of TypeError when an unknown option is passed to -tkinter.OptionMenu. diff --git a/Misc/NEWS.d/next/Library/2020-07-28-12-08-58.bpo-41316.bSCbK4.rst b/Misc/NEWS.d/next/Library/2020-07-28-12-08-58.bpo-41316.bSCbK4.rst deleted file mode 100644 index 139a170866ed4..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-07-28-12-08-58.bpo-41316.bSCbK4.rst +++ /dev/null @@ -1 +0,0 @@ -Fix the :mod:`tarfile` module to write only basename of TAR file to GZIP compression header. \ No newline at end of file diff --git a/Misc/NEWS.d/next/Library/2020-07-30-14-56-58.bpo-41440.rju34k.rst b/Misc/NEWS.d/next/Library/2020-07-30-14-56-58.bpo-41440.rju34k.rst deleted file mode 100644 index 3ee1f656d1870..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-07-30-14-56-58.bpo-41440.rju34k.rst +++ /dev/null @@ -1 +0,0 @@ -Add :func:`os.cpu_count()` support for VxWorks RTOS. diff --git a/Misc/NEWS.d/next/Library/2020-08-01-00-51-15.bpo-41421.dHKRVB.rst b/Misc/NEWS.d/next/Library/2020-08-01-00-51-15.bpo-41421.dHKRVB.rst deleted file mode 100644 index cf291c60d8ad5..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-08-01-00-51-15.bpo-41421.dHKRVB.rst +++ /dev/null @@ -1,3 +0,0 @@ -Make an algebraic simplification to random.paretovariate(). It now is -slightly less subject to round-off error and is slightly faster. Inputs that -used to cause ZeroDivisionError now cause an OverflowError instead. diff --git a/Misc/NEWS.d/next/Library/2020-08-03-01-59-48.bpo-41425.KJo6zF.rst b/Misc/NEWS.d/next/Library/2020-08-03-01-59-48.bpo-41425.KJo6zF.rst deleted file mode 100644 index 617df72faeb37..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-08-03-01-59-48.bpo-41425.KJo6zF.rst +++ /dev/null @@ -1 +0,0 @@ -Make tkinter doc example runnable. diff --git a/Misc/NEWS.d/next/Library/2020-08-04-00-20-30.bpo-41467.Z8DgTL.rst b/Misc/NEWS.d/next/Library/2020-08-04-00-20-30.bpo-41467.Z8DgTL.rst deleted file mode 100644 index f12693e117631..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-08-04-00-20-30.bpo-41467.Z8DgTL.rst +++ /dev/null @@ -1,3 +0,0 @@ -On Windows, fix asyncio ``recv_into()`` return value when the socket/pipe is -closed (:exc:`BrokenPipeError`): return ``0`` rather than an empty byte -string (``b''``). diff --git a/Misc/NEWS.d/next/Library/2020-08-07-06-06-29.bpo-41497.aBtsWz.rst b/Misc/NEWS.d/next/Library/2020-08-07-06-06-29.bpo-41497.aBtsWz.rst deleted file mode 100644 index 2c863ed7ffa3f..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-08-07-06-06-29.bpo-41497.aBtsWz.rst +++ /dev/null @@ -1 +0,0 @@ -Fix potential UnicodeDecodeError in dis module. \ No newline at end of file diff --git a/Misc/NEWS.d/next/Library/2020-08-07-15-18-16.bpo-41503.IYftcu.rst b/Misc/NEWS.d/next/Library/2020-08-07-15-18-16.bpo-41503.IYftcu.rst deleted file mode 100644 index c34996d881937..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-08-07-15-18-16.bpo-41503.IYftcu.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed a race between setTarget and flush in logging.handlers.MemoryHandler. \ No newline at end of file diff --git a/Misc/NEWS.d/next/Library/2020-08-09-18-16-05.bpo-41513.e6K6EK.rst b/Misc/NEWS.d/next/Library/2020-08-09-18-16-05.bpo-41513.e6K6EK.rst deleted file mode 100644 index cfb9f98c376a0..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-08-09-18-16-05.bpo-41513.e6K6EK.rst +++ /dev/null @@ -1,2 +0,0 @@ -Minor algorithmic improvement to math.hypot() and math.dist() giving small -gains in speed and accuracy. diff --git a/Misc/NEWS.d/next/Library/2020-08-12-07-43-31.bpo-41528.bu83oD.rst b/Misc/NEWS.d/next/Library/2020-08-12-07-43-31.bpo-41528.bu83oD.rst deleted file mode 100644 index a4ba57c2438ed..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-08-12-07-43-31.bpo-41528.bu83oD.rst +++ /dev/null @@ -1 +0,0 @@ -turtle uses math module functions to convert degrees to radians and vice versa and to calculate vector norm \ No newline at end of file diff --git a/Misc/NEWS.d/next/Library/2020-08-12-13-25-16.bpo-41520.BEUWa4.rst b/Misc/NEWS.d/next/Library/2020-08-12-13-25-16.bpo-41520.BEUWa4.rst deleted file mode 100644 index 0e140d91bb4b6..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-08-12-13-25-16.bpo-41520.BEUWa4.rst +++ /dev/null @@ -1 +0,0 @@ -Fix :mod:`codeop` regression that prevented turning compile warnings into errors. diff --git a/Misc/NEWS.d/next/Library/2020-08-13-08-07-25.bpo-40782.aGZqmB.rst b/Misc/NEWS.d/next/Library/2020-08-13-08-07-25.bpo-40782.aGZqmB.rst deleted file mode 100644 index d4c7e0e2419df..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-08-13-08-07-25.bpo-40782.aGZqmB.rst +++ /dev/null @@ -1 +0,0 @@ -Change the method asyncio.AbstractEventLoop.run_in_executor to not be a coroutine. \ No newline at end of file diff --git a/Misc/NEWS.d/next/Library/2020-08-15-15-21-40.bpo-37658.f9nivB.rst b/Misc/NEWS.d/next/Library/2020-08-15-15-21-40.bpo-37658.f9nivB.rst deleted file mode 100644 index 694fbbbf346dc..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-08-15-15-21-40.bpo-37658.f9nivB.rst +++ /dev/null @@ -1,2 +0,0 @@ -:meth:`asyncio.wait_for` now properly handles races between cancellation of -itself and the completion of the wrapped awaitable. diff --git a/Misc/NEWS.d/next/Library/2020-08-15-15-50-12.bpo-32751.85je5X.rst b/Misc/NEWS.d/next/Library/2020-08-15-15-50-12.bpo-32751.85je5X.rst deleted file mode 100644 index c172ce5d9e948..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-08-15-15-50-12.bpo-32751.85je5X.rst +++ /dev/null @@ -1,3 +0,0 @@ -When cancelling the task due to a timeout, :meth:`asyncio.wait_for` will now -wait until the cancellation is complete also in the case when *timeout* is -<= 0, like it does with positive timeouts. diff --git a/Misc/NEWS.d/next/Library/2020-08-15-18-17-21.bpo-39994.dOgPOh.rst b/Misc/NEWS.d/next/Library/2020-08-15-18-17-21.bpo-39994.dOgPOh.rst deleted file mode 100644 index 46876c15ea199..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-08-15-18-17-21.bpo-39994.dOgPOh.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed pprint's handling of dict subclasses that override __repr__. diff --git a/Misc/NEWS.d/next/Library/2020-08-21-15-51-15.bpo-41609.JmiUKG.rst b/Misc/NEWS.d/next/Library/2020-08-21-15-51-15.bpo-41609.JmiUKG.rst deleted file mode 100644 index ecaf40eee7bab..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-08-21-15-51-15.bpo-41609.JmiUKG.rst +++ /dev/null @@ -1 +0,0 @@ -The pdb whatis command correctly reports instance methods as 'Method' rather than 'Function'. \ No newline at end of file diff --git a/Misc/NEWS.d/next/Library/2020-08-23-14-23-18.bpo-41513.DGqc_I.rst b/Misc/NEWS.d/next/Library/2020-08-23-14-23-18.bpo-41513.DGqc_I.rst deleted file mode 100644 index b4d0d9b63cf87..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-08-23-14-23-18.bpo-41513.DGqc_I.rst +++ /dev/null @@ -1,3 +0,0 @@ -Improved the accuracy of math.hypot(). Internally, each step is computed -with extra precision so that the result is now almost always correctly -rounded. diff --git a/Misc/NEWS.d/next/Library/2020-08-29-16-07-36.bpo-41662.Mn79zh.rst b/Misc/NEWS.d/next/Library/2020-08-29-16-07-36.bpo-41662.Mn79zh.rst deleted file mode 100644 index 0571c2d110bee..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-08-29-16-07-36.bpo-41662.Mn79zh.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed crash when mutate list of parameters during iteration in :mod:`sqlite3`. diff --git a/Misc/NEWS.d/next/Library/2020-08-29-16-45-12.bpo-41638.iZfW5N.rst b/Misc/NEWS.d/next/Library/2020-08-29-16-45-12.bpo-41638.iZfW5N.rst deleted file mode 100644 index 8ab7b5e9903dc..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-08-29-16-45-12.bpo-41638.iZfW5N.rst +++ /dev/null @@ -1,3 +0,0 @@ -:exc:`~sqlite3.ProgrammingError` message for absent parameter in :mod:`sqlite3` -contains now the name of the parameter instead of its index when parameters -are supplied as a dict. diff --git a/Misc/NEWS.d/next/Library/2020-08-30-10-24-26.bpo-39010._mzXJW.rst b/Misc/NEWS.d/next/Library/2020-08-30-10-24-26.bpo-39010._mzXJW.rst deleted file mode 100644 index 0d9015b490e4c..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-08-30-10-24-26.bpo-39010._mzXJW.rst +++ /dev/null @@ -1,2 +0,0 @@ -Restarting a ``ProactorEventLoop`` on Windows no longer logs spurious -``ConnectionResetErrors``. diff --git a/Misc/NEWS.d/next/Library/2020-08-30-21-38-57.bpo-41662.6e9iZn.rst b/Misc/NEWS.d/next/Library/2020-08-30-21-38-57.bpo-41662.6e9iZn.rst deleted file mode 100644 index aecb0a1ea4d08..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-08-30-21-38-57.bpo-41662.6e9iZn.rst +++ /dev/null @@ -1,2 +0,0 @@ -No longer override exceptions raised in ``__len__()`` of a sequence of -parameters in :mod:`sqlite3` with :exc:`~sqlite3.ProgrammingError`. diff --git a/Misc/NEWS.d/next/Library/2020-09-01-15-57-51.bpo-41687.m1b1KA.rst b/Misc/NEWS.d/next/Library/2020-09-01-15-57-51.bpo-41687.m1b1KA.rst deleted file mode 100644 index 284f500735701..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-09-01-15-57-51.bpo-41687.m1b1KA.rst +++ /dev/null @@ -1 +0,0 @@ -Fix implementation of sendfile to be compatible with Solaris. diff --git a/Misc/NEWS.d/next/Library/2020-09-03-01-35-32.bpo-41696.zkYGre.rst b/Misc/NEWS.d/next/Library/2020-09-03-01-35-32.bpo-41696.zkYGre.rst deleted file mode 100644 index 67bbbb857f18c..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-09-03-01-35-32.bpo-41696.zkYGre.rst +++ /dev/null @@ -1 +0,0 @@ -Fix handling of debug mode in :func:`asyncio.run`. This allows setting ``PYTHONASYNCIODEBUG`` or ``-X dev`` to enable asyncio debug mode when using :func:`asyncio.run`. \ No newline at end of file diff --git a/Misc/NEWS.d/next/Library/2020-09-04-20-45-38.bpo-41720.PW9MzZ.rst b/Misc/NEWS.d/next/Library/2020-09-04-20-45-38.bpo-41720.PW9MzZ.rst deleted file mode 100644 index 5d2a5094ddeaa..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-09-04-20-45-38.bpo-41720.PW9MzZ.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fixed :meth:`turtle.Vec2D.__rmul__` for arguments which are not int or -float. diff --git a/Misc/NEWS.d/next/Library/2020-09-06-20-27-10.bpo-41732.1SKv26.rst b/Misc/NEWS.d/next/Library/2020-09-06-20-27-10.bpo-41732.1SKv26.rst deleted file mode 100644 index caf237f37f4de..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-09-06-20-27-10.bpo-41732.1SKv26.rst +++ /dev/null @@ -1 +0,0 @@ -Added an :term:`iterator` to :class:`memoryview`. diff --git a/Misc/NEWS.d/next/Library/2020-09-08-13-51-16.bpo-1635741.wkPeoT.rst b/Misc/NEWS.d/next/Library/2020-09-08-13-51-16.bpo-1635741.wkPeoT.rst deleted file mode 100644 index 972d69b94b6ba..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-09-08-13-51-16.bpo-1635741.wkPeoT.rst +++ /dev/null @@ -1,2 +0,0 @@ -Port the ``_string`` extension module to the multi-phase initialization API -(:pep:`489`). diff --git a/Misc/NEWS.d/next/Library/2020-09-08-13-55-34.bpo-1635741.56MLP-.rst b/Misc/NEWS.d/next/Library/2020-09-08-13-55-34.bpo-1635741.56MLP-.rst deleted file mode 100644 index 8b5bd5efdc2c0..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-09-08-13-55-34.bpo-1635741.56MLP-.rst +++ /dev/null @@ -1,2 +0,0 @@ -Port the ``mashal`` extension module to the multi-phase initialization API -(:pep:`489`). diff --git a/Misc/NEWS.d/next/Library/2020-09-11-12-38-55.bpo-39651.JMp9l2.rst b/Misc/NEWS.d/next/Library/2020-09-11-12-38-55.bpo-39651.JMp9l2.rst deleted file mode 100644 index 78dcff1370029..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-09-11-12-38-55.bpo-39651.JMp9l2.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fix a race condition in the ``call_soon_threadsafe()`` method of -``asyncio.ProactorEventLoop``: do nothing if the self-pipe socket has been -closed. diff --git a/Misc/NEWS.d/next/Library/2020-09-12-16-18-42.bpo-32218.IpYkEe.rst b/Misc/NEWS.d/next/Library/2020-09-12-16-18-42.bpo-32218.IpYkEe.rst deleted file mode 100644 index d5832b9767b70..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-09-12-16-18-42.bpo-32218.IpYkEe.rst +++ /dev/null @@ -1 +0,0 @@ -`enum.Flag` and `enum.IntFlag` members are now iterable diff --git a/Misc/NEWS.d/next/Library/2020-09-14-19-27-46.bpo-41789.pI_uZQ.rst b/Misc/NEWS.d/next/Library/2020-09-14-19-27-46.bpo-41789.pI_uZQ.rst deleted file mode 100644 index 5ce7a3ca67b72..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-09-14-19-27-46.bpo-41789.pI_uZQ.rst +++ /dev/null @@ -1,2 +0,0 @@ -Honor `object` overrides in `Enum` class creation (specifically, `__str__`, -`__repr__`, `__format__`, and `__reduce_ex__`). diff --git a/Misc/NEWS.d/next/Library/2020-09-15-07-55-35.bpo-41792.qMpSlU.rst b/Misc/NEWS.d/next/Library/2020-09-15-07-55-35.bpo-41792.qMpSlU.rst deleted file mode 100644 index fbbc6724ba51e..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-09-15-07-55-35.bpo-41792.qMpSlU.rst +++ /dev/null @@ -1,6 +0,0 @@ -Add is_typeddict function to typing.py to check if a type is a TypedDict -class - -Previously there was no way to check that without using private API. See the -`relevant issue in python/typing -` diff --git a/Misc/NEWS.d/next/Library/2020-09-15-14-56-13.bpo-39587.69xzuh.rst b/Misc/NEWS.d/next/Library/2020-09-15-14-56-13.bpo-39587.69xzuh.rst deleted file mode 100644 index e2f2b64867bed..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-09-15-14-56-13.bpo-39587.69xzuh.rst +++ /dev/null @@ -1 +0,0 @@ -use the correct mix-in data type when constructing Enums diff --git a/Misc/NEWS.d/next/Library/2020-09-15-22-43-30.bpo-41517.sLBH7g.rst b/Misc/NEWS.d/next/Library/2020-09-15-22-43-30.bpo-41517.sLBH7g.rst deleted file mode 100644 index e7654711062ce..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-09-15-22-43-30.bpo-41517.sLBH7g.rst +++ /dev/null @@ -1 +0,0 @@ -fix bug allowing Enums to be extended via multiple inheritance diff --git a/Misc/NEWS.d/next/Library/2020-09-19-12-22-08.bpo-41816.ynynXJ.rst b/Misc/NEWS.d/next/Library/2020-09-19-12-22-08.bpo-41816.ynynXJ.rst deleted file mode 100644 index 605c346f37a81..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-09-19-12-22-08.bpo-41816.ynynXJ.rst +++ /dev/null @@ -1,2 +0,0 @@ -StrEnum added: it ensures that all members are already strings or string -candidates diff --git a/Misc/NEWS.d/next/Library/2020-09-19-23-14-54.bpo-41815.RNpuX3.rst b/Misc/NEWS.d/next/Library/2020-09-19-23-14-54.bpo-41815.RNpuX3.rst deleted file mode 100644 index 3560db9bc5d35..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-09-19-23-14-54.bpo-41815.RNpuX3.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix SQLite3 segfault when backing up closed database. Patch contributed by -Peter David McCormick. diff --git a/Misc/NEWS.d/next/Library/2020-09-20-15-14-05.bpo-41810.7l8lyV.rst b/Misc/NEWS.d/next/Library/2020-09-20-15-14-05.bpo-41810.7l8lyV.rst deleted file mode 100644 index 515aea9e36ce9..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-09-20-15-14-05.bpo-41810.7l8lyV.rst +++ /dev/null @@ -1,3 +0,0 @@ -:data:`types.EllipsisType`, :data:`types.NotImplementedType` and -:data:`types.NoneType` have been reintroduced, providing a new set -of types readily interpretable by static type checkers. diff --git a/Misc/NEWS.d/next/Library/2020-09-22-00-23-30.bpo-41817.bnh-VG.rst b/Misc/NEWS.d/next/Library/2020-09-22-00-23-30.bpo-41817.bnh-VG.rst deleted file mode 100644 index 6a634bb613260..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-09-22-00-23-30.bpo-41817.bnh-VG.rst +++ /dev/null @@ -1 +0,0 @@ -fix `tkinter.EventType` Enum so all members are strings, and none are tuples diff --git a/Misc/NEWS.d/next/Library/2020-09-22-13-51-14.bpo-41833.6HVDjT.rst b/Misc/NEWS.d/next/Library/2020-09-22-13-51-14.bpo-41833.6HVDjT.rst deleted file mode 100644 index abb3a077d91b8..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-09-22-13-51-14.bpo-41833.6HVDjT.rst +++ /dev/null @@ -1,2 +0,0 @@ -The :class:`threading.Thread` constructor now uses the target name if the -*target* argument is specified but the *name* argument is omitted. diff --git a/Misc/NEWS.d/next/Library/2020-09-22-14-55-34.bpo-40670.R5sm68.rst b/Misc/NEWS.d/next/Library/2020-09-22-14-55-34.bpo-40670.R5sm68.rst deleted file mode 100644 index 0436194d736ab..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-09-22-14-55-34.bpo-40670.R5sm68.rst +++ /dev/null @@ -1,3 +0,0 @@ -More reliable validation of statements in :class:`timeit.Timer`. It now -accepts "empty" statements (only whitespaces and comments) and rejects -misindentent statements. diff --git a/Misc/NEWS.d/next/Library/2020-09-23-03-33-37.bpo-40564.iXQqMq.rst b/Misc/NEWS.d/next/Library/2020-09-23-03-33-37.bpo-40564.iXQqMq.rst deleted file mode 100644 index 085534734ec94..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-09-23-03-33-37.bpo-40564.iXQqMq.rst +++ /dev/null @@ -1 +0,0 @@ -In ``zipfile.Path``, mutate the passed ZipFile object type instead of making a copy. Prevents issues when both the local copy and the caller?s copy attempt to close the same file handle. \ No newline at end of file diff --git a/Misc/NEWS.d/next/Library/2020-09-23-22-52-24.bpo-41842.lIuhC9.rst b/Misc/NEWS.d/next/Library/2020-09-23-22-52-24.bpo-41842.lIuhC9.rst deleted file mode 100644 index 306b02d76fffe..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-09-23-22-52-24.bpo-41842.lIuhC9.rst +++ /dev/null @@ -1 +0,0 @@ -Add :func:`codecs.unregister` function to unregister a codec search function. diff --git a/Misc/NEWS.d/next/Library/2020-09-23-23-17-59.bpo-41840.QRFr4L.rst b/Misc/NEWS.d/next/Library/2020-09-23-23-17-59.bpo-41840.QRFr4L.rst deleted file mode 100644 index e96942d8ebd07..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-09-23-23-17-59.bpo-41840.QRFr4L.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fix a bug in the :mod:`symtable` module that was causing module-scope global -variables to not be reported as both local and global. Patch by Pablo -Galindo. diff --git a/Misc/NEWS.d/next/Library/2020-09-28-23-22-25.bpo-41773.oKkus0.rst b/Misc/NEWS.d/next/Library/2020-09-28-23-22-25.bpo-41773.oKkus0.rst deleted file mode 100644 index cef7ff0188354..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-09-28-23-22-25.bpo-41773.oKkus0.rst +++ /dev/null @@ -1,2 +0,0 @@ -Note in documentation that :func:`random.choices` doesn't support non-finite -weights, raise :exc:`ValueError` when given non-finite weights. diff --git a/Misc/NEWS.d/next/Library/2020-09-30-23-49-42.bpo-41887.-ee2S-.rst b/Misc/NEWS.d/next/Library/2020-09-30-23-49-42.bpo-41887.-ee2S-.rst deleted file mode 100644 index 2a43ab3f2890c..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-09-30-23-49-42.bpo-41887.-ee2S-.rst +++ /dev/null @@ -1,2 +0,0 @@ -Strip leading spaces and tabs on :func:`ast.literal_eval`. Also document -stripping of spaces and tabs for :func:`eval`. diff --git a/Misc/NEWS.d/next/Library/2020-10-01-10-50-12.bpo-41900.Cho7oh.rst b/Misc/NEWS.d/next/Library/2020-10-01-10-50-12.bpo-41900.Cho7oh.rst deleted file mode 100644 index 6586c09ec985d..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-10-01-10-50-12.bpo-41900.Cho7oh.rst +++ /dev/null @@ -1,2 +0,0 @@ -C14N 2.0 serialisation in xml.etree.ElementTree failed for unprefixed attributes -when a default namespace was defined. diff --git a/Misc/NEWS.d/next/Security/2020-02-12-14-17-39.bpo-39603.Gt3RSg.rst b/Misc/NEWS.d/next/Security/2020-02-12-14-17-39.bpo-39603.Gt3RSg.rst deleted file mode 100644 index 990affc3edd9d..0000000000000 --- a/Misc/NEWS.d/next/Security/2020-02-12-14-17-39.bpo-39603.Gt3RSg.rst +++ /dev/null @@ -1,2 +0,0 @@ -Prevent http header injection by rejecting control characters in -http.client.putrequest(...). diff --git a/Misc/NEWS.d/next/Security/2020-06-29-16-02-29.bpo-41004.ovF0KZ.rst b/Misc/NEWS.d/next/Security/2020-06-29-16-02-29.bpo-41004.ovF0KZ.rst deleted file mode 100644 index 1380b31fbe9f4..0000000000000 --- a/Misc/NEWS.d/next/Security/2020-06-29-16-02-29.bpo-41004.ovF0KZ.rst +++ /dev/null @@ -1 +0,0 @@ -The __hash__() methods of ipaddress.IPv4Interface and ipaddress.IPv6Interface incorrectly generated constant hash values of 32 and 128 respectively. This resulted in always causing hash collisions. The fix uses hash() to generate hash values for the tuple of (address, mask length, network address). diff --git a/Misc/NEWS.d/next/Security/2020-07-03-17-21-37.bpo-29778.cR_fGS.rst b/Misc/NEWS.d/next/Security/2020-07-03-17-21-37.bpo-29778.cR_fGS.rst deleted file mode 100644 index 998ffb1ee6667..0000000000000 --- a/Misc/NEWS.d/next/Security/2020-07-03-17-21-37.bpo-29778.cR_fGS.rst +++ /dev/null @@ -1,2 +0,0 @@ -Ensure :file:`python3.dll` is loaded from correct locations when Python is -embedded (CVE-2020-15523). diff --git a/Misc/NEWS.d/next/Security/2020-07-03-20-41-29.bpo-41162.tb8pVj.rst b/Misc/NEWS.d/next/Security/2020-07-03-20-41-29.bpo-41162.tb8pVj.rst deleted file mode 100644 index f0333ac4a7bb3..0000000000000 --- a/Misc/NEWS.d/next/Security/2020-07-03-20-41-29.bpo-41162.tb8pVj.rst +++ /dev/null @@ -1 +0,0 @@ -Audit hooks are now cleared later during finalization to avoid missing events. \ No newline at end of file diff --git a/Misc/NEWS.d/next/Security/2020-07-15-20-15-08.bpo-41304.vNEeYA.rst b/Misc/NEWS.d/next/Security/2020-07-15-20-15-08.bpo-41304.vNEeYA.rst deleted file mode 100644 index 8cc4bb8d280a7..0000000000000 --- a/Misc/NEWS.d/next/Security/2020-07-15-20-15-08.bpo-41304.vNEeYA.rst +++ /dev/null @@ -1 +0,0 @@ -Fixes `python3x._pth` being ignored on Windows, caused by the fix for :issue:`29778` (CVE-2020-15801). diff --git a/Misc/NEWS.d/next/Tests/2018-08-20-09-38-52.bpo-34401.eGxMPm.rst b/Misc/NEWS.d/next/Tests/2018-08-20-09-38-52.bpo-34401.eGxMPm.rst deleted file mode 100644 index 1b28d94c056d4..0000000000000 --- a/Misc/NEWS.d/next/Tests/2018-08-20-09-38-52.bpo-34401.eGxMPm.rst +++ /dev/null @@ -1 +0,0 @@ -Make test_gdb properly run on HP-UX. Patch by Michael Osipov. diff --git a/Misc/NEWS.d/next/Tests/2019-09-14-13-20-27.bpo-38169.hurq4B.rst b/Misc/NEWS.d/next/Tests/2019-09-14-13-20-27.bpo-38169.hurq4B.rst deleted file mode 100644 index 3972b9d440a87..0000000000000 --- a/Misc/NEWS.d/next/Tests/2019-09-14-13-20-27.bpo-38169.hurq4B.rst +++ /dev/null @@ -1 +0,0 @@ -Increase code coverage for SharedMemory and ShareableList diff --git a/Misc/NEWS.d/next/Tests/2020-04-09-15-40-03.bpo-31904.TJ4k3d.rst b/Misc/NEWS.d/next/Tests/2020-04-09-15-40-03.bpo-31904.TJ4k3d.rst deleted file mode 100644 index 40d232ec2f1e4..0000000000000 --- a/Misc/NEWS.d/next/Tests/2020-04-09-15-40-03.bpo-31904.TJ4k3d.rst +++ /dev/null @@ -1 +0,0 @@ -Increase LOOPBACK_TIMEOUT to 10 for VxWorks RTOS. diff --git a/Misc/NEWS.d/next/Tests/2020-05-26-07-53-31.bpo-17258.X_IKTQ.rst b/Misc/NEWS.d/next/Tests/2020-05-26-07-53-31.bpo-17258.X_IKTQ.rst deleted file mode 100644 index 0a4b329b802e3..0000000000000 --- a/Misc/NEWS.d/next/Tests/2020-05-26-07-53-31.bpo-17258.X_IKTQ.rst +++ /dev/null @@ -1 +0,0 @@ -Skip some :mod:`multiprocessing` tests when MD5 hash digest is blocked. diff --git a/Misc/NEWS.d/next/Tests/2020-06-09-18-48-18.bpo-40927.67ylLg.rst b/Misc/NEWS.d/next/Tests/2020-06-09-18-48-18.bpo-40927.67ylLg.rst deleted file mode 100644 index 66209b84c94d6..0000000000000 --- a/Misc/NEWS.d/next/Tests/2020-06-09-18-48-18.bpo-40927.67ylLg.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix test_binhex when run twice: it now uses import_fresh_module() to ensure -that it raises DeprecationWarning each time. diff --git a/Misc/NEWS.d/next/Tests/2020-06-12-20-46-23.bpo-40964.OBzf2c.rst b/Misc/NEWS.d/next/Tests/2020-06-12-20-46-23.bpo-40964.OBzf2c.rst deleted file mode 100644 index abfe4f0da4351..0000000000000 --- a/Misc/NEWS.d/next/Tests/2020-06-12-20-46-23.bpo-40964.OBzf2c.rst +++ /dev/null @@ -1,2 +0,0 @@ -Disable remote :mod:`imaplib` tests, host cyrus.andrew.cmu.edu is blocking -incoming connections. diff --git a/Misc/NEWS.d/next/Tests/2020-06-17-15-07-14.bpo-41003.tiH_Fy.rst b/Misc/NEWS.d/next/Tests/2020-06-17-15-07-14.bpo-41003.tiH_Fy.rst deleted file mode 100644 index 6f908d99feaf7..0000000000000 --- a/Misc/NEWS.d/next/Tests/2020-06-17-15-07-14.bpo-41003.tiH_Fy.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fix ``test_copyreg`` when ``numpy`` is installed: ``test.pickletester`` now -saves/restores warnings filters when importing ``numpy``, to ignore filters -installed by ``numpy``. diff --git a/Misc/NEWS.d/next/Tests/2020-06-17-17-27-07.bpo-41009.Rvn6OQ.rst b/Misc/NEWS.d/next/Tests/2020-06-17-17-27-07.bpo-41009.Rvn6OQ.rst deleted file mode 100644 index 1208c119a3556..0000000000000 --- a/Misc/NEWS.d/next/Tests/2020-06-17-17-27-07.bpo-41009.Rvn6OQ.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix use of ``support.require_{linux|mac|freebsd}_version()`` decorators as -class decorator. diff --git a/Misc/NEWS.d/next/Tests/2020-06-17-18-00-21.bpo-38377.jfg4TH.rst b/Misc/NEWS.d/next/Tests/2020-06-17-18-00-21.bpo-38377.jfg4TH.rst deleted file mode 100644 index 11a30761d36c9..0000000000000 --- a/Misc/NEWS.d/next/Tests/2020-06-17-18-00-21.bpo-38377.jfg4TH.rst +++ /dev/null @@ -1,4 +0,0 @@ -On Linux, skip tests using multiprocessing if the current user cannot create -a file in ``/dev/shm/`` directory. Add the -:func:`~test.support.skip_if_broken_multiprocessing_synchronize` function to -the :mod:`test.support` module. diff --git a/Misc/NEWS.d/next/Tests/2020-06-22-00-21-12.bpo-41069.bLZkX-.rst b/Misc/NEWS.d/next/Tests/2020-06-22-00-21-12.bpo-41069.bLZkX-.rst deleted file mode 100644 index 14bbd1a39a4cd..0000000000000 --- a/Misc/NEWS.d/next/Tests/2020-06-22-00-21-12.bpo-41069.bLZkX-.rst +++ /dev/null @@ -1,2 +0,0 @@ -:data:`test.support.TESTFN` and the current directory for tests when run via -``test.regrtest`` contain now non-ascii characters if possible. diff --git a/Misc/NEWS.d/next/Tests/2020-06-23-12-02-45.bpo-41085.JZKsyz.rst b/Misc/NEWS.d/next/Tests/2020-06-23-12-02-45.bpo-41085.JZKsyz.rst deleted file mode 100644 index 463dffdd653ee..0000000000000 --- a/Misc/NEWS.d/next/Tests/2020-06-23-12-02-45.bpo-41085.JZKsyz.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix integer overflow in the :meth:`array.array.index` method on 64-bit Windows -for index larger than ``2**31``. diff --git a/Misc/NEWS.d/next/Tests/2020-08-07-17-28-49.bpo-41477.GrFexU.rst b/Misc/NEWS.d/next/Tests/2020-08-07-17-28-49.bpo-41477.GrFexU.rst deleted file mode 100644 index bf0f54abecd85..0000000000000 --- a/Misc/NEWS.d/next/Tests/2020-08-07-17-28-49.bpo-41477.GrFexU.rst +++ /dev/null @@ -1 +0,0 @@ -Make ctypes optional in test_genericalias. diff --git a/Misc/NEWS.d/next/Tests/2020-08-11-14-59-13.bpo-41521.w2UYK7.rst b/Misc/NEWS.d/next/Tests/2020-08-11-14-59-13.bpo-41521.w2UYK7.rst deleted file mode 100644 index 658372b1a7f22..0000000000000 --- a/Misc/NEWS.d/next/Tests/2020-08-11-14-59-13.bpo-41521.w2UYK7.rst +++ /dev/null @@ -1,2 +0,0 @@ -:mod:`test.support`: Rename ``blacklist`` parameter of -:func:`~test.support.check__all__` to ``not_exported``. diff --git a/Misc/NEWS.d/next/Tests/2020-08-25-19-25-36.bpo-41602.Z64s0I.rst b/Misc/NEWS.d/next/Tests/2020-08-25-19-25-36.bpo-41602.Z64s0I.rst deleted file mode 100644 index fa3d2f1aa374e..0000000000000 --- a/Misc/NEWS.d/next/Tests/2020-08-25-19-25-36.bpo-41602.Z64s0I.rst +++ /dev/null @@ -1 +0,0 @@ -Add tests for SIGINT handling in the runpy module. diff --git a/Misc/NEWS.d/next/Tests/2020-09-11-19-12-31.bpo-41731.Ivxh4U.rst b/Misc/NEWS.d/next/Tests/2020-09-11-19-12-31.bpo-41731.Ivxh4U.rst deleted file mode 100644 index e368a60f77b1e..0000000000000 --- a/Misc/NEWS.d/next/Tests/2020-09-11-19-12-31.bpo-41731.Ivxh4U.rst +++ /dev/null @@ -1 +0,0 @@ -Make test_cmd_line_script pass with option '-vv'. diff --git a/Misc/NEWS.d/next/Tests/2020-10-05-09-37-43.bpo-41939.P4OlbA.rst b/Misc/NEWS.d/next/Tests/2020-10-05-09-37-43.bpo-41939.P4OlbA.rst deleted file mode 100644 index e58ad2616da1b..0000000000000 --- a/Misc/NEWS.d/next/Tests/2020-10-05-09-37-43.bpo-41939.P4OlbA.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fix test_site.test_license_exists_at_url(): call -``urllib.request.urlcleanup()`` to reset the global -``urllib.request._opener``. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/Windows/2019-07-11-06-11-09.bpo-37556.sygMUU.rst b/Misc/NEWS.d/next/Windows/2019-07-11-06-11-09.bpo-37556.sygMUU.rst deleted file mode 100644 index e8af96421b845..0000000000000 --- a/Misc/NEWS.d/next/Windows/2019-07-11-06-11-09.bpo-37556.sygMUU.rst +++ /dev/null @@ -1 +0,0 @@ -Extend py.exe help to mention overrides via venv, shebang, environmental variables & ini files. \ No newline at end of file diff --git a/Misc/NEWS.d/next/Windows/2020-05-19-04-11-12.bpo-40677.qQbLW8.rst b/Misc/NEWS.d/next/Windows/2020-05-19-04-11-12.bpo-40677.qQbLW8.rst deleted file mode 100644 index a09cb243aba31..0000000000000 --- a/Misc/NEWS.d/next/Windows/2020-05-19-04-11-12.bpo-40677.qQbLW8.rst +++ /dev/null @@ -1 +0,0 @@ -Manually define IO_REPARSE_TAG_APPEXECLINK in case some old Windows SDK doesn't have it. \ No newline at end of file diff --git a/Misc/NEWS.d/next/Windows/2020-05-19-14-43-33.bpo-39631.Z5yXam.rst b/Misc/NEWS.d/next/Windows/2020-05-19-14-43-33.bpo-39631.Z5yXam.rst deleted file mode 100644 index 38db4b431b6af..0000000000000 --- a/Misc/NEWS.d/next/Windows/2020-05-19-14-43-33.bpo-39631.Z5yXam.rst +++ /dev/null @@ -1,2 +0,0 @@ -Changes the registered MIME type for ``.py`` files on Windows to -``text/x-python`` instead of ``text/plain``. diff --git a/Misc/NEWS.d/next/Windows/2020-06-12-13-13-44.bpo-40164.SPrSn5.rst b/Misc/NEWS.d/next/Windows/2020-06-12-13-13-44.bpo-40164.SPrSn5.rst deleted file mode 100644 index 6390de717d71f..0000000000000 --- a/Misc/NEWS.d/next/Windows/2020-06-12-13-13-44.bpo-40164.SPrSn5.rst +++ /dev/null @@ -1 +0,0 @@ -Updates Windows OpenSSL to 1.1.1g \ No newline at end of file diff --git a/Misc/NEWS.d/next/Windows/2020-06-23-03-12-57.bpo-41039.0hgd0s.rst b/Misc/NEWS.d/next/Windows/2020-06-23-03-12-57.bpo-41039.0hgd0s.rst deleted file mode 100644 index acc3f7441f1b1..0000000000000 --- a/Misc/NEWS.d/next/Windows/2020-06-23-03-12-57.bpo-41039.0hgd0s.rst +++ /dev/null @@ -1,2 +0,0 @@ -Stable ABI redirection DLL (python3.dll) now uses ``#pragma -comment(linker)`` for re-exporting. diff --git a/Misc/NEWS.d/next/Windows/2020-06-24-21-30-42.bpo-41074.gaQc3C.rst b/Misc/NEWS.d/next/Windows/2020-06-24-21-30-42.bpo-41074.gaQc3C.rst deleted file mode 100644 index ec91fd361c3de..0000000000000 --- a/Misc/NEWS.d/next/Windows/2020-06-24-21-30-42.bpo-41074.gaQc3C.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fixed support of non-ASCII names in functions :func:`msilib.OpenDatabase` -and :func:`msilib.init_database` and non-ASCII SQL in method -:meth:`msilib.Database.OpenView`. diff --git a/Misc/NEWS.d/next/Windows/2020-06-28-12-40-41.bpo-41142.jpZzzh.rst b/Misc/NEWS.d/next/Windows/2020-06-28-12-40-41.bpo-41142.jpZzzh.rst deleted file mode 100644 index 91406da7a2544..0000000000000 --- a/Misc/NEWS.d/next/Windows/2020-06-28-12-40-41.bpo-41142.jpZzzh.rst +++ /dev/null @@ -1,2 +0,0 @@ -:mod:`msilib` now supports creating CAB files with non-ASCII file path and -adding files with non-ASCII file path to them. diff --git a/Misc/NEWS.d/next/Windows/2020-07-20-23-26-26.bpo-40741.C9sc_d.rst b/Misc/NEWS.d/next/Windows/2020-07-20-23-26-26.bpo-40741.C9sc_d.rst deleted file mode 100644 index 69b7cce43803c..0000000000000 --- a/Misc/NEWS.d/next/Windows/2020-07-20-23-26-26.bpo-40741.C9sc_d.rst +++ /dev/null @@ -1 +0,0 @@ -Update Windows release to include SQLite 3.32.3. diff --git a/Misc/NEWS.d/next/Windows/2020-07-28-11-55-43.bpo-41412.ME20KB.rst b/Misc/NEWS.d/next/Windows/2020-07-28-11-55-43.bpo-41412.ME20KB.rst deleted file mode 100644 index 274264ad876d3..0000000000000 --- a/Misc/NEWS.d/next/Windows/2020-07-28-11-55-43.bpo-41412.ME20KB.rst +++ /dev/null @@ -1,2 +0,0 @@ -The installer will now fail to install on Windows 7 and Windows 8. Further, -the UCRT dependency is now always downloaded on demand. diff --git a/Misc/NEWS.d/next/Windows/2020-07-28-12-39-32.bpo-40948.ISUFO6.rst b/Misc/NEWS.d/next/Windows/2020-07-28-12-39-32.bpo-40948.ISUFO6.rst deleted file mode 100644 index f8831d8c13701..0000000000000 --- a/Misc/NEWS.d/next/Windows/2020-07-28-12-39-32.bpo-40948.ISUFO6.rst +++ /dev/null @@ -1 +0,0 @@ -Improve post-install message to direct people to the "py" command. diff --git a/Misc/NEWS.d/next/Windows/2020-08-06-16-59-10.bpo-41492.2FQ9cM.rst b/Misc/NEWS.d/next/Windows/2020-08-06-16-59-10.bpo-41492.2FQ9cM.rst deleted file mode 100644 index 065803e2c2075..0000000000000 --- a/Misc/NEWS.d/next/Windows/2020-08-06-16-59-10.bpo-41492.2FQ9cM.rst +++ /dev/null @@ -1 +0,0 @@ -Fixes the description that appears in UAC prompts. diff --git a/Misc/NEWS.d/next/Windows/2020-08-13-22-40-58.bpo-41526.-i2bwb.rst b/Misc/NEWS.d/next/Windows/2020-08-13-22-40-58.bpo-41526.-i2bwb.rst deleted file mode 100644 index 756c8270599f2..0000000000000 --- a/Misc/NEWS.d/next/Windows/2020-08-13-22-40-58.bpo-41526.-i2bwb.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fixed layout of final page of the installer by removing the special thanks -to Mark Hammond (with his permission). diff --git a/Misc/NEWS.d/next/Windows/2020-09-04-21-35-28.bpo-41627.sx2KN1.rst b/Misc/NEWS.d/next/Windows/2020-09-04-21-35-28.bpo-41627.sx2KN1.rst deleted file mode 100644 index 043bd5e9341c3..0000000000000 --- a/Misc/NEWS.d/next/Windows/2020-09-04-21-35-28.bpo-41627.sx2KN1.rst +++ /dev/null @@ -1,2 +0,0 @@ -The user site directory for 32-bit now includes a ``-32`` suffix to -distinguish it from the 64-bit interpreter's directory. diff --git a/Misc/NEWS.d/next/Windows/2020-09-11-17-59-33.bpo-41744.e_ugDQ.rst b/Misc/NEWS.d/next/Windows/2020-09-11-17-59-33.bpo-41744.e_ugDQ.rst deleted file mode 100644 index 6106d6604c7dd..0000000000000 --- a/Misc/NEWS.d/next/Windows/2020-09-11-17-59-33.bpo-41744.e_ugDQ.rst +++ /dev/null @@ -1 +0,0 @@ -Fixes automatic import of props file when using the Nuget package. \ No newline at end of file diff --git a/Misc/NEWS.d/next/macOS/2020-06-07-20-10-56.bpo-40741.80A2BW.rst b/Misc/NEWS.d/next/macOS/2020-06-07-20-10-56.bpo-40741.80A2BW.rst deleted file mode 100644 index 6ff7b9a805b95..0000000000000 --- a/Misc/NEWS.d/next/macOS/2020-06-07-20-10-56.bpo-40741.80A2BW.rst +++ /dev/null @@ -1 +0,0 @@ -Update macOS installer to use SQLite 3.32.2. diff --git a/Misc/NEWS.d/next/macOS/2020-06-17-13-45-15.bpo-41005.zZegdV.rst b/Misc/NEWS.d/next/macOS/2020-06-17-13-45-15.bpo-41005.zZegdV.rst deleted file mode 100644 index 3b5f3f23a12f5..0000000000000 --- a/Misc/NEWS.d/next/macOS/2020-06-17-13-45-15.bpo-41005.zZegdV.rst +++ /dev/null @@ -1 +0,0 @@ -fixed an XDG settings issue not allowing macos to open browser in webbrowser.py \ No newline at end of file diff --git a/Misc/NEWS.d/next/macOS/2020-06-19-14-19-08.bpo-40741.L7yTbm.rst b/Misc/NEWS.d/next/macOS/2020-06-19-14-19-08.bpo-40741.L7yTbm.rst deleted file mode 100644 index 78a21b76c2fe4..0000000000000 --- a/Misc/NEWS.d/next/macOS/2020-06-19-14-19-08.bpo-40741.L7yTbm.rst +++ /dev/null @@ -1 +0,0 @@ -Update macOS installer to use SQLite 3.32.3. diff --git a/Misc/NEWS.d/next/macOS/2020-06-24-13-51-57.bpo-41100.mcHdc5.rst b/Misc/NEWS.d/next/macOS/2020-06-24-13-51-57.bpo-41100.mcHdc5.rst deleted file mode 100644 index d6bb616136690..0000000000000 --- a/Misc/NEWS.d/next/macOS/2020-06-24-13-51-57.bpo-41100.mcHdc5.rst +++ /dev/null @@ -1,7 +0,0 @@ -Fix configure error when building on macOS 11. -Note that the current Python release was released -shortly after the first developer preview of macOS -11 (Big Sur); there are other known issues with -building and running on the developer preview. -Big Sur is expected to be fully supported in a -future bugfix release of Python 3.8.x and with 3.9.0. \ No newline at end of file diff --git a/Misc/NEWS.d/next/macOS/2020-06-25-06-09-00.bpo-39580.N_vJ9h.rst b/Misc/NEWS.d/next/macOS/2020-06-25-06-09-00.bpo-39580.N_vJ9h.rst deleted file mode 100644 index 95d65359804d0..0000000000000 --- a/Misc/NEWS.d/next/macOS/2020-06-25-06-09-00.bpo-39580.N_vJ9h.rst +++ /dev/null @@ -1,2 +0,0 @@ -Avoid opening Finder window if running installer from the command line. -Patch contributed by Rick Heil. diff --git a/Misc/NEWS.d/next/macOS/2020-08-26-09-31-37.bpo-41557.mcQ75z.rst b/Misc/NEWS.d/next/macOS/2020-08-26-09-31-37.bpo-41557.mcQ75z.rst deleted file mode 100644 index 5f2d9937c0606..0000000000000 --- a/Misc/NEWS.d/next/macOS/2020-08-26-09-31-37.bpo-41557.mcQ75z.rst +++ /dev/null @@ -1 +0,0 @@ -Update macOS installer to use SQLite 3.33.0. diff --git a/README.rst b/README.rst index 14f4f32bca796..176562cae308b 100644 --- a/README.rst +++ b/README.rst @@ -1,4 +1,4 @@ -This is Python version 3.10.0 alpha 0 +This is Python version 3.10.0 alpha 1 ===================================== .. image:: https://travis-ci.com/python/cpython.svg?branch=master From webhook-mailer at python.org Tue Oct 6 03:03:36 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Tue, 06 Oct 2020 07:03:36 -0000 Subject: [Python-checkins] bpo-41939: Fix test_site.test_license_exists_at_url() (GH-22559) Message-ID: https://github.com/python/cpython/commit/78bddc7794a629614dc84e195b9117416ae97ed3 commit: 78bddc7794a629614dc84e195b9117416ae97ed3 branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-06T00:03:27-07:00 summary: bpo-41939: Fix test_site.test_license_exists_at_url() (GH-22559) Call urllib.request.urlcleanup() to reset the global urllib.request._opener. (cherry picked from commit 1fce240d6c4b2b2cc17a86e88c65169e15b9feeb) Co-authored-by: Victor Stinner files: A Misc/NEWS.d/next/Tests/2020-10-05-09-37-43.bpo-41939.P4OlbA.rst M Lib/test/test_site.py diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py index 01008656bdd75..b95c108771d8c 100644 --- a/Lib/test/test_site.py +++ b/Lib/test/test_site.py @@ -501,6 +501,8 @@ def test_license_exists_at_url(self): # string displayed by license in the absence of a LICENSE file. url = license._Printer__data.split()[1] req = urllib.request.Request(url, method='HEAD') + # Reset global urllib.request._opener + self.addCleanup(urllib.request.urlcleanup) try: with test.support.transient_internet(url): with urllib.request.urlopen(req) as data: diff --git a/Misc/NEWS.d/next/Tests/2020-10-05-09-37-43.bpo-41939.P4OlbA.rst b/Misc/NEWS.d/next/Tests/2020-10-05-09-37-43.bpo-41939.P4OlbA.rst new file mode 100644 index 0000000000000..e58ad2616da1b --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2020-10-05-09-37-43.bpo-41939.P4OlbA.rst @@ -0,0 +1,3 @@ +Fix test_site.test_license_exists_at_url(): call +``urllib.request.urlcleanup()`` to reset the global +``urllib.request._opener``. Patch by Victor Stinner. From webhook-mailer at python.org Tue Oct 6 08:15:00 2020 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Tue, 06 Oct 2020 12:15:00 -0000 Subject: [Python-checkins] bpo-41944: No longer call eval() on content received via HTTP in the CJK codec tests (GH-22566) Message-ID: https://github.com/python/cpython/commit/2ef5caa58febc8968e670e39e3d37cf8eef3cab8 commit: 2ef5caa58febc8968e670e39e3d37cf8eef3cab8 branch: master author: Serhiy Storchaka committer: GitHub date: 2020-10-06T15:14:51+03:00 summary: bpo-41944: No longer call eval() on content received via HTTP in the CJK codec tests (GH-22566) files: A Misc/NEWS.d/next/Tests/2020-10-05-17-43-46.bpo-41944.rf1dYb.rst M Lib/test/multibytecodec_support.py diff --git a/Lib/test/multibytecodec_support.py b/Lib/test/multibytecodec_support.py index cca8af67d6d1d..f76c0153f5ecf 100644 --- a/Lib/test/multibytecodec_support.py +++ b/Lib/test/multibytecodec_support.py @@ -305,29 +305,23 @@ def test_mapping_file(self): self._test_mapping_file_plain() def _test_mapping_file_plain(self): - unichrs = lambda s: ''.join(map(chr, map(eval, s.split('+')))) + def unichrs(s): + return ''.join(chr(int(x, 16)) for x in s.split('+')) + urt_wa = {} with self.open_mapping_file() as f: for line in f: if not line: break - data = line.split('#')[0].strip().split() + data = line.split('#')[0].split() if len(data) != 2: continue - csetval = eval(data[0]) - if csetval <= 0x7F: - csetch = bytes([csetval & 0xff]) - elif csetval >= 0x1000000: - csetch = bytes([(csetval >> 24), ((csetval >> 16) & 0xff), - ((csetval >> 8) & 0xff), (csetval & 0xff)]) - elif csetval >= 0x10000: - csetch = bytes([(csetval >> 16), ((csetval >> 8) & 0xff), - (csetval & 0xff)]) - elif csetval >= 0x100: - csetch = bytes([(csetval >> 8), (csetval & 0xff)]) - else: + if data[0][:2] != '0x': + self.fail(f"Invalid line: {line!r}") + csetch = bytes.fromhex(data[0][2:]) + if len(csetch) == 1 and 0x80 <= csetch[0]: continue unich = unichrs(data[1]) diff --git a/Misc/NEWS.d/next/Tests/2020-10-05-17-43-46.bpo-41944.rf1dYb.rst b/Misc/NEWS.d/next/Tests/2020-10-05-17-43-46.bpo-41944.rf1dYb.rst new file mode 100644 index 0000000000000..4f9782f1c85af --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2020-10-05-17-43-46.bpo-41944.rf1dYb.rst @@ -0,0 +1 @@ +Tests for CJK codecs no longer call ``eval()`` on content received via HTTP. From webhook-mailer at python.org Tue Oct 6 08:37:45 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Tue, 06 Oct 2020 12:37:45 -0000 Subject: [Python-checkins] bpo-41944: No longer call eval() on content received via HTTP in the CJK codec tests (GH-22566) Message-ID: https://github.com/python/cpython/commit/b664a1df4ee71d3760ab937653b10997081b1794 commit: b664a1df4ee71d3760ab937653b10997081b1794 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-06T05:37:36-07:00 summary: bpo-41944: No longer call eval() on content received via HTTP in the CJK codec tests (GH-22566) (cherry picked from commit 2ef5caa58febc8968e670e39e3d37cf8eef3cab8) Co-authored-by: Serhiy Storchaka files: A Misc/NEWS.d/next/Tests/2020-10-05-17-43-46.bpo-41944.rf1dYb.rst M Lib/test/multibytecodec_support.py diff --git a/Lib/test/multibytecodec_support.py b/Lib/test/multibytecodec_support.py index cca8af67d6d1d..f76c0153f5ecf 100644 --- a/Lib/test/multibytecodec_support.py +++ b/Lib/test/multibytecodec_support.py @@ -305,29 +305,23 @@ def test_mapping_file(self): self._test_mapping_file_plain() def _test_mapping_file_plain(self): - unichrs = lambda s: ''.join(map(chr, map(eval, s.split('+')))) + def unichrs(s): + return ''.join(chr(int(x, 16)) for x in s.split('+')) + urt_wa = {} with self.open_mapping_file() as f: for line in f: if not line: break - data = line.split('#')[0].strip().split() + data = line.split('#')[0].split() if len(data) != 2: continue - csetval = eval(data[0]) - if csetval <= 0x7F: - csetch = bytes([csetval & 0xff]) - elif csetval >= 0x1000000: - csetch = bytes([(csetval >> 24), ((csetval >> 16) & 0xff), - ((csetval >> 8) & 0xff), (csetval & 0xff)]) - elif csetval >= 0x10000: - csetch = bytes([(csetval >> 16), ((csetval >> 8) & 0xff), - (csetval & 0xff)]) - elif csetval >= 0x100: - csetch = bytes([(csetval >> 8), (csetval & 0xff)]) - else: + if data[0][:2] != '0x': + self.fail(f"Invalid line: {line!r}") + csetch = bytes.fromhex(data[0][2:]) + if len(csetch) == 1 and 0x80 <= csetch[0]: continue unich = unichrs(data[1]) diff --git a/Misc/NEWS.d/next/Tests/2020-10-05-17-43-46.bpo-41944.rf1dYb.rst b/Misc/NEWS.d/next/Tests/2020-10-05-17-43-46.bpo-41944.rf1dYb.rst new file mode 100644 index 0000000000000..4f9782f1c85af --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2020-10-05-17-43-46.bpo-41944.rf1dYb.rst @@ -0,0 +1 @@ +Tests for CJK codecs no longer call ``eval()`` on content received via HTTP. From webhook-mailer at python.org Tue Oct 6 08:38:58 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Tue, 06 Oct 2020 12:38:58 -0000 Subject: [Python-checkins] bpo-41944: No longer call eval() on content received via HTTP in the CJK codec tests (GH-22566) (GH-22577) Message-ID: https://github.com/python/cpython/commit/6c6c256df3636ff6f6136820afaefa5a10a3ac33 commit: 6c6c256df3636ff6f6136820afaefa5a10a3ac33 branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-06T14:38:54+02:00 summary: bpo-41944: No longer call eval() on content received via HTTP in the CJK codec tests (GH-22566) (GH-22577) (cherry picked from commit 2ef5caa58febc8968e670e39e3d37cf8eef3cab8) Co-authored-by: Serhiy Storchaka Co-authored-by: Serhiy Storchaka files: A Misc/NEWS.d/next/Tests/2020-10-05-17-43-46.bpo-41944.rf1dYb.rst M Lib/test/multibytecodec_support.py diff --git a/Lib/test/multibytecodec_support.py b/Lib/test/multibytecodec_support.py index cca8af67d6d1d..f76c0153f5ecf 100644 --- a/Lib/test/multibytecodec_support.py +++ b/Lib/test/multibytecodec_support.py @@ -305,29 +305,23 @@ def test_mapping_file(self): self._test_mapping_file_plain() def _test_mapping_file_plain(self): - unichrs = lambda s: ''.join(map(chr, map(eval, s.split('+')))) + def unichrs(s): + return ''.join(chr(int(x, 16)) for x in s.split('+')) + urt_wa = {} with self.open_mapping_file() as f: for line in f: if not line: break - data = line.split('#')[0].strip().split() + data = line.split('#')[0].split() if len(data) != 2: continue - csetval = eval(data[0]) - if csetval <= 0x7F: - csetch = bytes([csetval & 0xff]) - elif csetval >= 0x1000000: - csetch = bytes([(csetval >> 24), ((csetval >> 16) & 0xff), - ((csetval >> 8) & 0xff), (csetval & 0xff)]) - elif csetval >= 0x10000: - csetch = bytes([(csetval >> 16), ((csetval >> 8) & 0xff), - (csetval & 0xff)]) - elif csetval >= 0x100: - csetch = bytes([(csetval >> 8), (csetval & 0xff)]) - else: + if data[0][:2] != '0x': + self.fail(f"Invalid line: {line!r}") + csetch = bytes.fromhex(data[0][2:]) + if len(csetch) == 1 and 0x80 <= csetch[0]: continue unich = unichrs(data[1]) diff --git a/Misc/NEWS.d/next/Tests/2020-10-05-17-43-46.bpo-41944.rf1dYb.rst b/Misc/NEWS.d/next/Tests/2020-10-05-17-43-46.bpo-41944.rf1dYb.rst new file mode 100644 index 0000000000000..4f9782f1c85af --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2020-10-05-17-43-46.bpo-41944.rf1dYb.rst @@ -0,0 +1 @@ +Tests for CJK codecs no longer call ``eval()`` on content received via HTTP. From webhook-mailer at python.org Tue Oct 6 10:22:07 2020 From: webhook-mailer at python.org (Florian Bruhin) Date: Tue, 06 Oct 2020 14:22:07 -0000 Subject: [Python-checkins] bpo-41944: No longer call eval() on content received via HTTP in the UnicodeNames tests (GH-22575) Message-ID: https://github.com/python/cpython/commit/a8bf44d04915f7366d9f8dfbf84822ac37a4bab3 commit: a8bf44d04915f7366d9f8dfbf84822ac37a4bab3 branch: master author: Florian Bruhin committer: GitHub date: 2020-10-06T16:21:56+02:00 summary: bpo-41944: No longer call eval() on content received via HTTP in the UnicodeNames tests (GH-22575) Similarly to GH-22566, those tests called eval() on content received via HTTP in test_named_sequences_full. This likely isn't exploitable because unicodedata.lookup(seqname) is called before self.checkletter(seqname, None) - thus any string which isn't a valid unicode character name wouldn't ever reach the checkletter method. Still, it's probably better to be safe than sorry. files: M Lib/test/test_ucn.py diff --git a/Lib/test/test_ucn.py b/Lib/test/test_ucn.py index e95f911d8eedd..cbfd5af2bb751 100644 --- a/Lib/test/test_ucn.py +++ b/Lib/test/test_ucn.py @@ -7,6 +7,7 @@ """#" +import ast import unittest import unicodedata @@ -24,7 +25,7 @@ def checkletter(self, name, code): # Helper that put all \N escapes inside eval'd raw strings, # to make sure this script runs even if the compiler # chokes on \N escapes - res = eval(r'"\N{%s}"' % name) + res = ast.literal_eval(r'"\N{%s}"' % name) self.assertEqual(res, code) return res From webhook-mailer at python.org Tue Oct 6 13:41:19 2020 From: webhook-mailer at python.org (Ben Avrahami) Date: Tue, 06 Oct 2020 17:41:19 -0000 Subject: [Python-checkins] bpo-41905: Add abc.update_abstractmethods() (GH-22485) Message-ID: https://github.com/python/cpython/commit/bef7d299eb911086ea5a7ccf7a9da337e38a8491 commit: bef7d299eb911086ea5a7ccf7a9da337e38a8491 branch: master author: Ben Avrahami committer: GitHub date: 2020-10-06T10:40:50-07:00 summary: bpo-41905: Add abc.update_abstractmethods() (GH-22485) This function recomputes `cls.__abstractmethods__`. Also update `@dataclass` to use it. files: A Misc/NEWS.d/next/Library/2020-10-01-21-11-03.bpo-41905._JpjR4.rst M Doc/library/abc.rst M Doc/library/functools.rst M Lib/abc.py M Lib/dataclasses.py M Lib/test/test_abc.py M Lib/test/test_dataclasses.py diff --git a/Doc/library/abc.rst b/Doc/library/abc.rst index 424ae547d829a..3a7414d7358e7 100644 --- a/Doc/library/abc.rst +++ b/Doc/library/abc.rst @@ -174,10 +174,11 @@ The :mod:`abc` module also provides the following decorator: to declare abstract methods for properties and descriptors. Dynamically adding abstract methods to a class, or attempting to modify the - abstraction status of a method or class once it is created, are not - supported. The :func:`abstractmethod` only affects subclasses derived using - regular inheritance; "virtual subclasses" registered with the ABC's - :meth:`register` method are not affected. + abstraction status of a method or class once it is created, are only + supported using the :func:`update_abstractmethods` function. The + :func:`abstractmethod` only affects subclasses derived using regular + inheritance; "virtual subclasses" registered with the ABC's :meth:`register` + method are not affected. When :func:`abstractmethod` is applied in combination with other method descriptors, it should be applied as the innermost decorator, as shown in @@ -235,7 +236,6 @@ The :mod:`abc` module also provides the following decorator: super-call in a framework that uses cooperative multiple-inheritance. - The :mod:`abc` module also supports the following legacy decorators: .. decorator:: abstractclassmethod @@ -335,6 +335,22 @@ The :mod:`abc` module also provides the following functions: .. versionadded:: 3.4 +.. function:: update_abstractmethods(cls) + A function to recalculate an abstract class's abstraction status. This + function should be called if a class's abstract methods have been + implemented or changed after it was created. Usually, this function should + be called from within a class decorator. + + Returns *cls*, to allow usage as a class decorator. + + If *cls* is not an instance of ABCMeta, does nothing. + + .. note:: + + This function assumes that *cls*'s superclasses are already updated. + It does not update any subclasses. + + .. versionadded:: 3.10 .. rubric:: Footnotes diff --git a/Doc/library/functools.rst b/Doc/library/functools.rst index 14aa184e2cd14..186cb4c381dee 100644 --- a/Doc/library/functools.rst +++ b/Doc/library/functools.rst @@ -254,6 +254,13 @@ The :mod:`functools` module defines the following functions: application, implementing all six rich comparison methods instead is likely to provide an easy speed boost. + .. note:: + + This decorator makes no attempt to override methods that have been + declared in the class *or its superclasses*. Meaning that if a + superclass defines a comparison operator, *total_ordering* will not + implement it again, even if the original method is abstract. + .. versionadded:: 3.2 .. versionchanged:: 3.4 diff --git a/Lib/abc.py b/Lib/abc.py index 431b64040a66e..276ef9a2cd485 100644 --- a/Lib/abc.py +++ b/Lib/abc.py @@ -122,6 +122,44 @@ def _abc_caches_clear(cls): _reset_caches(cls) +def update_abstractmethods(cls): + """Recalculate the set of abstract methods of an abstract class. + + If a class has had one of its abstract methods implemented after the + class was created, the method will not be considered implemented until + this function is called. Alternatively, if a new abstract method has been + added to the class, it will only be considered an abstract method of the + class after this function is called. + + This function should be called before any use is made of the class, + usually in class decorators that add methods to the subject class. + + Returns cls, to allow usage as a class decorator. + + If cls is not an instance of ABCMeta, does nothing. + """ + if not hasattr(cls, '__abstractmethods__'): + # We check for __abstractmethods__ here because cls might by a C + # implementation or a python implementation (especially during + # testing), and we want to handle both cases. + return cls + + abstracts = set() + # Check the existing abstract methods of the parents, keep only the ones + # that are not implemented. + for scls in cls.__bases__: + for name in getattr(scls, '__abstractmethods__', ()): + value = getattr(cls, name, None) + if getattr(value, "__isabstractmethod__", False): + abstracts.add(name) + # Also add any other newly added abstract methods. + for name, value in cls.__dict__.items(): + if getattr(value, "__isabstractmethod__", False): + abstracts.add(name) + cls.__abstractmethods__ = frozenset(abstracts) + return cls + + class ABC(metaclass=ABCMeta): """Helper class that provides a standard way to create an ABC using inheritance. diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py index 530d3e99574e8..65091021f3716 100644 --- a/Lib/dataclasses.py +++ b/Lib/dataclasses.py @@ -6,6 +6,7 @@ import keyword import builtins import functools +import abc import _thread from types import GenericAlias @@ -992,6 +993,8 @@ def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen): cls.__doc__ = (cls.__name__ + str(inspect.signature(cls)).replace(' -> None', '')) + abc.update_abstractmethods(cls) + return cls diff --git a/Lib/test/test_abc.py b/Lib/test/test_abc.py index 7e9c47b3cacb9..3d603e7734d87 100644 --- a/Lib/test/test_abc.py +++ b/Lib/test/test_abc.py @@ -488,6 +488,155 @@ class C(with_metaclass(abc_ABCMeta, A, B)): pass self.assertEqual(C.__class__, abc_ABCMeta) + def test_update_del(self): + class A(metaclass=abc_ABCMeta): + @abc.abstractmethod + def foo(self): + pass + + del A.foo + self.assertEqual(A.__abstractmethods__, {'foo'}) + self.assertFalse(hasattr(A, 'foo')) + + abc.update_abstractmethods(A) + + self.assertEqual(A.__abstractmethods__, set()) + A() + + + def test_update_new_abstractmethods(self): + class A(metaclass=abc_ABCMeta): + @abc.abstractmethod + def bar(self): + pass + + @abc.abstractmethod + def updated_foo(self): + pass + + A.foo = updated_foo + abc.update_abstractmethods(A) + self.assertEqual(A.__abstractmethods__, {'foo', 'bar'}) + msg = "class A with abstract methods bar, foo" + self.assertRaisesRegex(TypeError, msg, A) + + def test_update_implementation(self): + class A(metaclass=abc_ABCMeta): + @abc.abstractmethod + def foo(self): + pass + + class B(A): + pass + + msg = "class B with abstract method foo" + self.assertRaisesRegex(TypeError, msg, B) + self.assertEqual(B.__abstractmethods__, {'foo'}) + + B.foo = lambda self: None + + abc.update_abstractmethods(B) + + B() + self.assertEqual(B.__abstractmethods__, set()) + + def test_update_as_decorator(self): + class A(metaclass=abc_ABCMeta): + @abc.abstractmethod + def foo(self): + pass + + def class_decorator(cls): + cls.foo = lambda self: None + return cls + + @abc.update_abstractmethods + @class_decorator + class B(A): + pass + + B() + self.assertEqual(B.__abstractmethods__, set()) + + def test_update_non_abc(self): + class A: + pass + + @abc.abstractmethod + def updated_foo(self): + pass + + A.foo = updated_foo + abc.update_abstractmethods(A) + A() + self.assertFalse(hasattr(A, '__abstractmethods__')) + + def test_update_del_implementation(self): + class A(metaclass=abc_ABCMeta): + @abc.abstractmethod + def foo(self): + pass + + class B(A): + def foo(self): + pass + + B() + + del B.foo + + abc.update_abstractmethods(B) + + msg = "class B with abstract method foo" + self.assertRaisesRegex(TypeError, msg, B) + + def test_update_layered_implementation(self): + class A(metaclass=abc_ABCMeta): + @abc.abstractmethod + def foo(self): + pass + + class B(A): + pass + + class C(B): + def foo(self): + pass + + C() + + del C.foo + + abc.update_abstractmethods(C) + + msg = "class C with abstract method foo" + self.assertRaisesRegex(TypeError, msg, C) + + def test_update_multi_inheritance(self): + class A(metaclass=abc_ABCMeta): + @abc.abstractmethod + def foo(self): + pass + + class B(metaclass=abc_ABCMeta): + def foo(self): + pass + + class C(B, A): + @abc.abstractmethod + def foo(self): + pass + + self.assertEqual(C.__abstractmethods__, {'foo'}) + + del C.foo + + abc.update_abstractmethods(C) + + self.assertEqual(C.__abstractmethods__, set()) + + C() + class TestABCWithInitSubclass(unittest.TestCase): def test_works_with_init_subclass(self): diff --git a/Lib/test/test_dataclasses.py b/Lib/test/test_dataclasses.py index b20103bdce51c..b31a469ec7922 100644 --- a/Lib/test/test_dataclasses.py +++ b/Lib/test/test_dataclasses.py @@ -4,6 +4,7 @@ from dataclasses import * +import abc import pickle import inspect import builtins @@ -3332,6 +3333,42 @@ class C: ## replace(c, x=5) +class TestAbstract(unittest.TestCase): + def test_abc_implementation(self): + class Ordered(abc.ABC): + @abc.abstractmethod + def __lt__(self, other): + pass + + @abc.abstractmethod + def __le__(self, other): + pass + + @dataclass(order=True) + class Date(Ordered): + year: int + month: 'Month' + day: 'int' + + self.assertFalse(inspect.isabstract(Date)) + self.assertGreater(Date(2020,12,25), Date(2020,8,31)) + + def test_maintain_abc(self): + class A(abc.ABC): + @abc.abstractmethod + def foo(self): + pass + + @dataclass + class Date(A): + year: int + month: 'Month' + day: 'int' + + self.assertTrue(inspect.isabstract(Date)) + msg = 'class Date with abstract method foo' + self.assertRaisesRegex(TypeError, msg, Date) + if __name__ == '__main__': unittest.main() diff --git a/Misc/NEWS.d/next/Library/2020-10-01-21-11-03.bpo-41905._JpjR4.rst b/Misc/NEWS.d/next/Library/2020-10-01-21-11-03.bpo-41905._JpjR4.rst new file mode 100644 index 0000000000000..0d8c0ba6a66bd --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-10-01-21-11-03.bpo-41905._JpjR4.rst @@ -0,0 +1 @@ +A new function in abc: *update_abstractmethods* to re-calculate an abstract class's abstract status. In addition, *dataclass* has been changed to call this function. \ No newline at end of file From webhook-mailer at python.org Tue Oct 6 16:03:31 2020 From: webhook-mailer at python.org (Batuhan Taskaya) Date: Tue, 06 Oct 2020 20:03:31 -0000 Subject: [Python-checkins] bpo-38605: Make 'from __future__ import annotations' the default (GH-20434) Message-ID: https://github.com/python/cpython/commit/044a1048ca93d466965afc027b91a5a9eb9ce23c commit: 044a1048ca93d466965afc027b91a5a9eb9ce23c branch: master author: Batuhan Taskaya committer: GitHub date: 2020-10-06T13:03:02-07:00 summary: bpo-38605: Make 'from __future__ import annotations' the default (GH-20434) The hard part was making all the tests pass; there are some subtle issues here, because apparently the future import wasn't tested very thoroughly in previous Python versions. For example, `inspect.signature()` returned type objects normally (except for forward references), but strings with the future import. We changed it to try and return type objects by calling `typing.get_type_hints()`, but fall back on returning strings if that function fails (which it may do if there are future references in the annotations that require passing in a specific namespace to resolve). files: A Lib/test/test_annotations.py A Misc/NEWS.d/next/Core and Builtins/2020-05-27-16-08-16.bpo-38605.rcs2uK.rst D Lib/test/dataclass_module_1_str.py D Lib/test/dataclass_module_2_str.py M Doc/reference/compound_stmts.rst M Doc/whatsnew/3.10.rst M Lib/dataclasses.py M Lib/inspect.py M Lib/test/dataclass_module_1.py M Lib/test/dataclass_module_2.py M Lib/test/dataclass_textanno.py M Lib/test/test_coroutines.py M Lib/test/test_dataclasses.py M Lib/test/test_dis.py M Lib/test/test_functools.py M Lib/test/test_grammar.py M Lib/test/test_inspect.py M Lib/test/test_opcodes.py M Lib/test/test_positional_only_arg.py M Lib/test/test_pydoc.py M Lib/test/test_syntax.py M Lib/test/test_types.py M Lib/test/test_typing.py M Lib/typing.py M Python/ast_opt.c M Python/compile.c M Python/future.c diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst index 158d6a8f164e2..04a3948d0c9dc 100644 --- a/Doc/reference/compound_stmts.rst +++ b/Doc/reference/compound_stmts.rst @@ -610,13 +610,9 @@ following the parameter name. Any parameter may have an annotation, even those ``*identifier`` or ``**identifier``. Functions may have "return" annotation of the form "``-> expression``" after the parameter list. These annotations can be any valid Python expression. The presence of annotations does not change the -semantics of a function. The annotation values are available as values of -a dictionary keyed by the parameters' names in the :attr:`__annotations__` -attribute of the function object. If the ``annotations`` import from -:mod:`__future__` is used, annotations are preserved as strings at runtime which -enables postponed evaluation. Otherwise, they are evaluated when the function -definition is executed. In this case annotations may be evaluated in -a different order than they appear in the source code. +semantics of a function. The annotation values are available as string values +in a dictionary keyed by the parameters' names in the :attr:`__annotations__` +attribute of the function object. .. index:: pair: lambda; expression diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index 1ea5aeac8a3c6..2bcdba69957b6 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -70,6 +70,23 @@ Summary -- Release highlights New Features ============ +.. _whatsnew310-pep563: + +PEP 563: Postponed Evaluation of Annotations Becomes Default +------------------------------------------------------------ + +In Python 3.7, postponed evaluation of annotations was added, +to be enabled with a ``from __future__ import annotations`` +directive. In 3.10 this became the default behavior, even +without that future directive. With this being default, all +annotations stored in :attr:`__annotations__` will be strings. +If needed, annotations can be resolved at runtime using +:func:`typing.get_type_hints`. See :pep:`563` for a full +description. Also, the :func:`inspect.signature` will try to +resolve types from now on, and when it fails it will fall back to +showing the string annotations. (Contributed by Batuhan Taskaya +in :issue:`38605`.) + * The :class:`int` type has a new method :meth:`int.bit_count`, returning the number of ones in the binary expansion of a given integer, also known as the population count. (Contributed by Niklas Fiekas in :issue:`29882`.) diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py index 65091021f3716..adfb9b7240b9f 100644 --- a/Lib/dataclasses.py +++ b/Lib/dataclasses.py @@ -399,8 +399,10 @@ def _create_fn(name, args, body, *, globals=None, locals=None, ns = {} exec(txt, globals, ns) - return ns['__create_fn__'](**locals) - + func = ns['__create_fn__'](**locals) + for arg, annotation in func.__annotations__.copy().items(): + func.__annotations__[arg] = locals[annotation] + return func def _field_assign(frozen, name, value, self_name): # If we're a frozen class, then assign to our fields in __init__ @@ -651,6 +653,11 @@ def _is_type(annotation, cls, a_module, a_type, is_type_predicate): # a eval() penalty for every single field of every dataclass # that's defined. It was judged not worth it. + # Strip away the extra quotes as a result of double-stringifying when the + # 'annotations' feature became default. + if annotation.startswith(("'", '"')) and annotation.endswith(("'", '"')): + annotation = annotation[1:-1] + match = _MODULE_IDENTIFIER_RE.match(annotation) if match: ns = None @@ -991,7 +998,7 @@ def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen): if not getattr(cls, '__doc__'): # Create a class doc-string. cls.__doc__ = (cls.__name__ + - str(inspect.signature(cls)).replace(' -> None', '')) + str(inspect.signature(cls)).replace(' -> NoneType', '')) abc.update_abstractmethods(cls) diff --git a/Lib/inspect.py b/Lib/inspect.py index 887a3424057b6..ac127cbd725b9 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -45,6 +45,7 @@ import tokenize import token import types +import typing import warnings import functools import builtins @@ -1877,7 +1878,10 @@ def _signature_is_functionlike(obj): code = getattr(obj, '__code__', None) defaults = getattr(obj, '__defaults__', _void) # Important to use _void ... kwdefaults = getattr(obj, '__kwdefaults__', _void) # ... and not None here - annotations = getattr(obj, '__annotations__', None) + try: + annotations = _get_type_hints(obj) + except AttributeError: + annotations = None return (isinstance(code, types.CodeType) and isinstance(name, str) and @@ -2118,6 +2122,16 @@ def p(name_node, default_node, default=empty): return cls(parameters, return_annotation=cls.empty) +def _get_type_hints(func): + try: + return typing.get_type_hints(func) + except Exception: + # First, try to use the get_type_hints to resolve + # annotations. But for keeping the behavior intact + # if there was a problem with that (like the namespace + # can't resolve some annotation) continue to use + # string annotations + return func.__annotations__ def _signature_from_builtin(cls, func, skip_bound_arg=True): """Private helper function to get signature for @@ -2161,7 +2175,8 @@ def _signature_from_function(cls, func, skip_bound_arg=True): positional = arg_names[:pos_count] keyword_only_count = func_code.co_kwonlyargcount keyword_only = arg_names[pos_count:pos_count + keyword_only_count] - annotations = func.__annotations__ + annotations = _get_type_hints(func) + defaults = func.__defaults__ kwdefaults = func.__kwdefaults__ diff --git a/Lib/test/dataclass_module_1.py b/Lib/test/dataclass_module_1.py index 87a33f8191d3d..9f0aeda67f9ab 100644 --- a/Lib/test/dataclass_module_1.py +++ b/Lib/test/dataclass_module_1.py @@ -1,9 +1,3 @@ -#from __future__ import annotations -USING_STRINGS = False - -# dataclass_module_1.py and dataclass_module_1_str.py are identical -# except only the latter uses string annotations. - import dataclasses import typing diff --git a/Lib/test/dataclass_module_1_str.py b/Lib/test/dataclass_module_1_str.py deleted file mode 100644 index 6de490b7ad784..0000000000000 --- a/Lib/test/dataclass_module_1_str.py +++ /dev/null @@ -1,32 +0,0 @@ -from __future__ import annotations -USING_STRINGS = True - -# dataclass_module_1.py and dataclass_module_1_str.py are identical -# except only the latter uses string annotations. - -import dataclasses -import typing - -T_CV2 = typing.ClassVar[int] -T_CV3 = typing.ClassVar - -T_IV2 = dataclasses.InitVar[int] -T_IV3 = dataclasses.InitVar - - at dataclasses.dataclass -class CV: - T_CV4 = typing.ClassVar - cv0: typing.ClassVar[int] = 20 - cv1: typing.ClassVar = 30 - cv2: T_CV2 - cv3: T_CV3 - not_cv4: T_CV4 # When using string annotations, this field is not recognized as a ClassVar. - - at dataclasses.dataclass -class IV: - T_IV4 = dataclasses.InitVar - iv0: dataclasses.InitVar[int] - iv1: dataclasses.InitVar - iv2: T_IV2 - iv3: T_IV3 - not_iv4: T_IV4 # When using string annotations, this field is not recognized as an InitVar. diff --git a/Lib/test/dataclass_module_2.py b/Lib/test/dataclass_module_2.py index 68fb733e29925..8d120d181bd3d 100644 --- a/Lib/test/dataclass_module_2.py +++ b/Lib/test/dataclass_module_2.py @@ -1,9 +1,3 @@ -#from __future__ import annotations -USING_STRINGS = False - -# dataclass_module_2.py and dataclass_module_2_str.py are identical -# except only the latter uses string annotations. - from dataclasses import dataclass, InitVar from typing import ClassVar diff --git a/Lib/test/dataclass_module_2_str.py b/Lib/test/dataclass_module_2_str.py deleted file mode 100644 index b363d17c176c2..0000000000000 --- a/Lib/test/dataclass_module_2_str.py +++ /dev/null @@ -1,32 +0,0 @@ -from __future__ import annotations -USING_STRINGS = True - -# dataclass_module_2.py and dataclass_module_2_str.py are identical -# except only the latter uses string annotations. - -from dataclasses import dataclass, InitVar -from typing import ClassVar - -T_CV2 = ClassVar[int] -T_CV3 = ClassVar - -T_IV2 = InitVar[int] -T_IV3 = InitVar - - at dataclass -class CV: - T_CV4 = ClassVar - cv0: ClassVar[int] = 20 - cv1: ClassVar = 30 - cv2: T_CV2 - cv3: T_CV3 - not_cv4: T_CV4 # When using string annotations, this field is not recognized as a ClassVar. - - at dataclass -class IV: - T_IV4 = InitVar - iv0: InitVar[int] - iv1: InitVar - iv2: T_IV2 - iv3: T_IV3 - not_iv4: T_IV4 # When using string annotations, this field is not recognized as an InitVar. diff --git a/Lib/test/dataclass_textanno.py b/Lib/test/dataclass_textanno.py index 3eb6c943d4c43..589b60f0cd61d 100644 --- a/Lib/test/dataclass_textanno.py +++ b/Lib/test/dataclass_textanno.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import dataclasses diff --git a/Lib/test/test_annotations.py b/Lib/test/test_annotations.py new file mode 100644 index 0000000000000..3e6b709fb4f1e --- /dev/null +++ b/Lib/test/test_annotations.py @@ -0,0 +1,228 @@ +import unittest +import sys +from textwrap import dedent + +class PostponedAnnotationsTestCase(unittest.TestCase): + template = dedent( + """ + def f() -> {ann}: + ... + def g(arg: {ann}) -> None: + ... + async def f2() -> {ann}: + ... + async def g2(arg: {ann}) -> None: + ... + var: {ann} + var2: {ann} = None + """ + ) + + def getActual(self, annotation): + scope = {} + exec(self.template.format(ann=annotation), {}, scope) + func_ret_ann = scope['f'].__annotations__['return'] + func_arg_ann = scope['g'].__annotations__['arg'] + async_func_ret_ann = scope['f2'].__annotations__['return'] + async_func_arg_ann = scope['g2'].__annotations__['arg'] + var_ann1 = scope['__annotations__']['var'] + var_ann2 = scope['__annotations__']['var2'] + self.assertEqual(func_ret_ann, func_arg_ann) + self.assertEqual(func_ret_ann, async_func_ret_ann) + self.assertEqual(func_ret_ann, async_func_arg_ann) + self.assertEqual(func_ret_ann, var_ann1) + self.assertEqual(func_ret_ann, var_ann2) + return func_ret_ann + + def assertAnnotationEqual( + self, annotation, expected=None, drop_parens=False, is_tuple=False, + ): + actual = self.getActual(annotation) + if expected is None: + expected = annotation if not is_tuple else annotation[1:-1] + if drop_parens: + self.assertNotEqual(actual, expected) + actual = actual.replace("(", "").replace(")", "") + + self.assertEqual(actual, expected) + + def test_annotations(self): + eq = self.assertAnnotationEqual + eq('...') + eq("'some_string'") + eq("u'some_string'") + eq("b'\\xa3'") + eq('Name') + eq('None') + eq('True') + eq('False') + eq('1') + eq('1.0') + eq('1j') + eq('True or False') + eq('True or False or None') + eq('True and False') + eq('True and False and None') + eq('Name1 and Name2 or Name3') + eq('Name1 and (Name2 or Name3)') + eq('Name1 or Name2 and Name3') + eq('(Name1 or Name2) and Name3') + eq('Name1 and Name2 or Name3 and Name4') + eq('Name1 or Name2 and Name3 or Name4') + eq('a + b + (c + d)') + eq('a * b * (c * d)') + eq('(a ** b) ** c ** d') + eq('v1 << 2') + eq('1 >> v2') + eq('1 % finished') + eq('1 + v2 - v3 * 4 ^ 5 ** v6 / 7 // 8') + eq('not great') + eq('not not great') + eq('~great') + eq('+value') + eq('++value') + eq('-1') + eq('~int and not v1 ^ 123 + v2 | True') + eq('a + (not b)') + eq('lambda: None') + eq('lambda arg: None') + eq('lambda a=True: a') + eq('lambda a, b, c=True: a') + eq("lambda a, b, c=True, *, d=1 << v2, e='str': a") + eq("lambda a, b, c=True, *vararg, d, e='str', **kwargs: a + b") + eq("lambda a, /, b, c=True, *vararg, d, e='str', **kwargs: a + b") + eq('lambda x, /: x') + eq('lambda x=1, /: x') + eq('lambda x, /, y: x + y') + eq('lambda x=1, /, y=2: x + y') + eq('lambda x, /, y=1: x + y') + eq('lambda x, /, y=1, *, z=3: x + y + z') + eq('lambda x=1, /, y=2, *, z=3: x + y + z') + eq('lambda x=1, /, y=2, *, z: x + y + z') + eq('lambda x=1, y=2, z=3, /, w=4, *, l, l2: x + y + z + w + l + l2') + eq('lambda x=1, y=2, z=3, /, w=4, *, l, l2, **kwargs: x + y + z + w + l + l2') + eq('lambda x, /, y=1, *, z: x + y + z') + eq('lambda x: lambda y: x + y') + eq('1 if True else 2') + eq('str or None if int or True else str or bytes or None') + eq('str or None if (1 if True else 2) else str or bytes or None') + eq("0 if not x else 1 if x > 0 else -1") + eq("(1 if x > 0 else -1) if x else 0") + eq("{'2.7': dead, '3.7': long_live or die_hard}") + eq("{'2.7': dead, '3.7': long_live or die_hard, **{'3.6': verygood}}") + eq("{**a, **b, **c}") + eq("{'2.7', '3.6', '3.7', '3.8', '3.9', '4.0' if gilectomy else '3.10'}") + eq("{*a, *b, *c}") + eq("({'a': 'b'}, True or False, +value, 'string', b'bytes') or None") + eq("()") + eq("(a,)") + eq("(a, b)") + eq("(a, b, c)") + eq("(*a, *b, *c)") + eq("[]") + eq("[1, 2, 3, 4, 5, 6, 7, 8, 9, 10 or A, 11 or B, 12 or C]") + eq("[*a, *b, *c]") + eq("{i for i in (1, 2, 3)}") + eq("{i ** 2 for i in (1, 2, 3)}") + eq("{i ** 2 for i, _ in ((1, 'a'), (2, 'b'), (3, 'c'))}") + eq("{i ** 2 + j for i in (1, 2, 3) for j in (1, 2, 3)}") + eq("[i for i in (1, 2, 3)]") + eq("[i ** 2 for i in (1, 2, 3)]") + eq("[i ** 2 for i, _ in ((1, 'a'), (2, 'b'), (3, 'c'))]") + eq("[i ** 2 + j for i in (1, 2, 3) for j in (1, 2, 3)]") + eq("(i for i in (1, 2, 3))") + eq("(i ** 2 for i in (1, 2, 3))") + eq("(i ** 2 for i, _ in ((1, 'a'), (2, 'b'), (3, 'c')))") + eq("(i ** 2 + j for i in (1, 2, 3) for j in (1, 2, 3))") + eq("{i: 0 for i in (1, 2, 3)}") + eq("{i: j for i, j in ((1, 'a'), (2, 'b'), (3, 'c'))}") + eq("[(x, y) for x, y in (a, b)]") + eq("[(x,) for x, in (a,)]") + eq("Python3 > Python2 > COBOL") + eq("Life is Life") + eq("call()") + eq("call(arg)") + eq("call(kwarg='hey')") + eq("call(arg, kwarg='hey')") + eq("call(arg, *args, another, kwarg='hey')") + eq("call(arg, another, kwarg='hey', **kwargs, kwarg2='ho')") + eq("lukasz.langa.pl") + eq("call.me(maybe)") + eq("1 .real") + eq("1.0.real") + eq("....__class__") + eq("list[str]") + eq("dict[str, int]") + eq("set[str,]") + eq("tuple[str, ...]") + eq("tuple[(str, *types)]") + eq("tuple[str, int, (str, int)]") + eq("tuple[(*int, str, str, (str, int))]") + eq("tuple[str, int, float, dict[str, int]]") + eq("slice[0]") + eq("slice[0:1]") + eq("slice[0:1:2]") + eq("slice[:]") + eq("slice[:-1]") + eq("slice[1:]") + eq("slice[::-1]") + eq("slice[:,]") + eq("slice[1:2,]") + eq("slice[1:2:3,]") + eq("slice[1:2, 1]") + eq("slice[1:2, 2, 3]") + eq("slice[()]") + eq("slice[a, b:c, d:e:f]") + eq("slice[(x for x in a)]") + eq('str or None if sys.version_info[0] > (3,) else str or bytes or None') + eq("f'f-string without formatted values is just a string'") + eq("f'{{NOT a formatted value}}'") + eq("f'some f-string with {a} {few():.2f} {formatted.values!r}'") + eq('''f"{f'{nested} inner'} outer"''') + eq("f'space between opening braces: { {a for a in (1, 2, 3)}}'") + eq("f'{(lambda x: x)}'") + eq("f'{(None if a else lambda x: x)}'") + eq("f'{x}'") + eq("f'{x!r}'") + eq("f'{x!a}'") + eq('(yield from outside_of_generator)') + eq('(yield)') + eq('(yield a + b)') + eq('await some.complicated[0].call(with_args=True or 1 is not 1)') + eq('[x for x in (a if b else c)]') + eq('[x for x in a if (b if c else d)]') + eq('f(x for x in a)') + eq('f(1, (x for x in a))') + eq('f((x for x in a), 2)') + eq('(((a)))', 'a') + eq('(((a, b)))', '(a, b)') + eq("(x := 10)") + eq("f'{(x := 10):=10}'") + eq("1 + 2") + eq("1 + 2 + 3") + + def test_fstring_debug_annotations(self): + # f-strings with '=' don't round trip very well, so set the expected + # result explicitely. + self.assertAnnotationEqual("f'{x=!r}'", expected="f'x={x!r}'") + self.assertAnnotationEqual("f'{x=:}'", expected="f'x={x:}'") + self.assertAnnotationEqual("f'{x=:.2f}'", expected="f'x={x:.2f}'") + self.assertAnnotationEqual("f'{x=!r}'", expected="f'x={x!r}'") + self.assertAnnotationEqual("f'{x=!a}'", expected="f'x={x!a}'") + self.assertAnnotationEqual("f'{x=!s:*^20}'", expected="f'x={x!s:*^20}'") + + def test_infinity_numbers(self): + inf = "1e" + repr(sys.float_info.max_10_exp + 1) + infj = f"{inf}j" + self.assertAnnotationEqual("1e1000", expected=inf) + self.assertAnnotationEqual("1e1000j", expected=infj) + self.assertAnnotationEqual("-1e1000", expected=f"-{inf}") + self.assertAnnotationEqual("3+1e1000j", expected=f"3 + {infj}") + self.assertAnnotationEqual("(1e1000, 1e1000j)", expected=f"({inf}, {infj})") + self.assertAnnotationEqual("'inf'") + self.assertAnnotationEqual("('inf', 1e1000, 'infxxx', 1e1000j)", expected=f"('inf', {inf}, 'infxxx', {infj})") + self.assertAnnotationEqual("(1e1000, (1e1000j,))", expected=f"({inf}, ({infj},))") + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_coroutines.py b/Lib/test/test_coroutines.py index 145adb6778170..40c2eb8d232dd 100644 --- a/Lib/test/test_coroutines.py +++ b/Lib/test/test_coroutines.py @@ -91,10 +91,6 @@ def test_badsyntax_1(self): pass """, - """async def foo(a:await something()): - pass - """, - """async def foo(): def bar(): [i async for i in els] @@ -299,10 +295,6 @@ def bar(): pass """, - """async def foo(a:await b): - pass - """, - """def baz(): async def foo(a=await b): pass diff --git a/Lib/test/test_dataclasses.py b/Lib/test/test_dataclasses.py index b31a469ec7922..7c1d9c568f4ef 100644 --- a/Lib/test/test_dataclasses.py +++ b/Lib/test/test_dataclasses.py @@ -9,6 +9,7 @@ import inspect import builtins import unittest +from textwrap import dedent from unittest.mock import Mock from typing import ClassVar, Any, List, Union, Tuple, Dict, Generic, TypeVar, Optional from typing import get_type_hints @@ -562,17 +563,17 @@ class C: self.assertEqual(len(the_fields), 3) self.assertEqual(the_fields[0].name, 'x') - self.assertEqual(the_fields[0].type, int) + self.assertEqual(the_fields[0].type, 'int') self.assertFalse(hasattr(C, 'x')) self.assertTrue (the_fields[0].init) self.assertTrue (the_fields[0].repr) self.assertEqual(the_fields[1].name, 'y') - self.assertEqual(the_fields[1].type, str) + self.assertEqual(the_fields[1].type, 'str') self.assertIsNone(getattr(C, 'y')) self.assertFalse(the_fields[1].init) self.assertTrue (the_fields[1].repr) self.assertEqual(the_fields[2].name, 'z') - self.assertEqual(the_fields[2].type, str) + self.assertEqual(the_fields[2].type, 'str') self.assertFalse(hasattr(C, 'z')) self.assertTrue (the_fields[2].init) self.assertFalse(the_fields[2].repr) @@ -758,11 +759,11 @@ class F: def validate_class(cls): # First, check __annotations__, even though they're not # function annotations. - self.assertEqual(cls.__annotations__['i'], int) - self.assertEqual(cls.__annotations__['j'], str) - self.assertEqual(cls.__annotations__['k'], F) - self.assertEqual(cls.__annotations__['l'], float) - self.assertEqual(cls.__annotations__['z'], complex) + self.assertEqual(cls.__annotations__['i'], 'int') + self.assertEqual(cls.__annotations__['j'], 'str') + self.assertEqual(cls.__annotations__['k'], 'F') + self.assertEqual(cls.__annotations__['l'], 'float') + self.assertEqual(cls.__annotations__['z'], 'complex') # Verify __init__. @@ -777,22 +778,22 @@ def validate_class(cls): self.assertEqual(param.name, 'self') param = next(params) self.assertEqual(param.name, 'i') - self.assertIs (param.annotation, int) + self.assertIs (param.annotation, 'int') self.assertEqual(param.default, inspect.Parameter.empty) self.assertEqual(param.kind, inspect.Parameter.POSITIONAL_OR_KEYWORD) param = next(params) self.assertEqual(param.name, 'j') - self.assertIs (param.annotation, str) + self.assertIs (param.annotation, 'str') self.assertEqual(param.default, inspect.Parameter.empty) self.assertEqual(param.kind, inspect.Parameter.POSITIONAL_OR_KEYWORD) param = next(params) self.assertEqual(param.name, 'k') - self.assertIs (param.annotation, F) + self.assertIs (param.annotation, 'F') # Don't test for the default, since it's set to MISSING. self.assertEqual(param.kind, inspect.Parameter.POSITIONAL_OR_KEYWORD) param = next(params) self.assertEqual(param.name, 'l') - self.assertIs (param.annotation, float) + self.assertIs (param.annotation, 'float') # Don't test for the default, since it's set to MISSING. self.assertEqual(param.kind, inspect.Parameter.POSITIONAL_OR_KEYWORD) self.assertRaises(StopIteration, next, params) @@ -2806,13 +2807,10 @@ class C: class TestStringAnnotations(unittest.TestCase): def test_classvar(self): - # Some expressions recognized as ClassVar really aren't. But - # if you're using string annotations, it's not an exact - # science. # These tests assume that both "import typing" and "from # typing import *" have been run in this file. for typestr in ('ClassVar[int]', - 'ClassVar [int]' + 'ClassVar [int]', ' ClassVar [int]', 'ClassVar', ' ClassVar ', @@ -2823,17 +2821,15 @@ def test_classvar(self): 'typing. ClassVar[str]', 'typing.ClassVar [str]', 'typing.ClassVar [ str]', - + # Double stringified + '"typing.ClassVar[int]"', # Not syntactically valid, but these will - # be treated as ClassVars. + # be treated as ClassVars. 'typing.ClassVar.[int]', 'typing.ClassVar+', ): with self.subTest(typestr=typestr): - @dataclass - class C: - x: typestr - + C = dataclass(type("C", (), {"__annotations__": {"x": typestr}})) # x is a ClassVar, so C() takes no args. C() @@ -2854,9 +2850,7 @@ def test_isnt_classvar(self): 'typingxClassVar[str]', ): with self.subTest(typestr=typestr): - @dataclass - class C: - x: typestr + C = dataclass(type("C", (), {"__annotations__": {"x": typestr}})) # x is not a ClassVar, so C() takes one arg. self.assertEqual(C(10).x, 10) @@ -2876,16 +2870,16 @@ def test_initvar(self): 'dataclasses. InitVar[str]', 'dataclasses.InitVar [str]', 'dataclasses.InitVar [ str]', - + # Double stringified + '"dataclasses.InitVar[int]"', # Not syntactically valid, but these will # be treated as InitVars. 'dataclasses.InitVar.[int]', 'dataclasses.InitVar+', ): with self.subTest(typestr=typestr): - @dataclass - class C: - x: typestr + C = dataclass(type("C", (), {"__annotations__": {"x": typestr}})) + # x is an InitVar, so doesn't create a member. with self.assertRaisesRegex(AttributeError, @@ -2899,30 +2893,22 @@ def test_isnt_initvar(self): 'typing.xInitVar[int]', ): with self.subTest(typestr=typestr): - @dataclass - class C: - x: typestr + C = dataclass(type("C", (), {"__annotations__": {"x": typestr}})) # x is not an InitVar, so there will be a member x. self.assertEqual(C(10).x, 10) def test_classvar_module_level_import(self): from test import dataclass_module_1 - from test import dataclass_module_1_str from test import dataclass_module_2 - from test import dataclass_module_2_str - for m in (dataclass_module_1, dataclass_module_1_str, - dataclass_module_2, dataclass_module_2_str, - ): + for m in (dataclass_module_1, + dataclass_module_2): with self.subTest(m=m): # There's a difference in how the ClassVars are # interpreted when using string annotations or # not. See the imported modules for details. - if m.USING_STRINGS: - c = m.CV(10) - else: - c = m.CV() + c = m.CV(10) self.assertEqual(c.cv0, 20) @@ -2938,14 +2924,9 @@ def test_classvar_module_level_import(self): # not an instance field. getattr(c, field_name) - if m.USING_STRINGS: - # iv4 is interpreted as a normal field. - self.assertIn('not_iv4', c.__dict__) - self.assertEqual(c.not_iv4, 4) - else: - # iv4 is interpreted as an InitVar, so it - # won't exist on the instance. - self.assertNotIn('not_iv4', c.__dict__) + # iv4 is interpreted as a normal field. + self.assertIn('not_iv4', c.__dict__) + self.assertEqual(c.not_iv4, 4) def test_text_annotations(self): from test import dataclass_textanno diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index 4533a016a2fab..bbaddd57d2918 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -227,28 +227,26 @@ def bug1333982(x=[]): 2 0 SETUP_ANNOTATIONS 2 LOAD_CONST 0 (1) 4 STORE_NAME 0 (x) - 6 LOAD_NAME 1 (int) - 8 LOAD_NAME 2 (__annotations__) - 10 LOAD_CONST 1 ('x') + 6 LOAD_CONST 1 ('int') + 8 LOAD_NAME 1 (__annotations__) + 10 LOAD_CONST 2 ('x') 12 STORE_SUBSCR - 3 14 LOAD_NAME 3 (fun) - 16 LOAD_CONST 0 (1) - 18 CALL_FUNCTION 1 - 20 LOAD_NAME 2 (__annotations__) - 22 LOAD_CONST 2 ('y') - 24 STORE_SUBSCR - - 4 26 LOAD_CONST 0 (1) - 28 LOAD_NAME 4 (lst) - 30 LOAD_NAME 3 (fun) - 32 LOAD_CONST 3 (0) - 34 CALL_FUNCTION 1 - 36 STORE_SUBSCR - 38 LOAD_NAME 1 (int) - 40 POP_TOP - 42 LOAD_CONST 4 (None) - 44 RETURN_VALUE + 3 14 LOAD_CONST 3 ('fun(1)') + 16 LOAD_NAME 1 (__annotations__) + 18 LOAD_CONST 4 ('y') + 20 STORE_SUBSCR + + 4 22 LOAD_CONST 0 (1) + 24 LOAD_NAME 2 (lst) + 26 LOAD_NAME 3 (fun) + 28 LOAD_CONST 5 (0) + 30 CALL_FUNCTION 1 + 32 STORE_SUBSCR + 34 LOAD_NAME 4 (int) + 36 POP_TOP + 38 LOAD_CONST 6 (None) + 40 RETURN_VALUE """ compound_stmt_str = """\ diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py index edd5773e13d54..bee9f9112bf18 100644 --- a/Lib/test/test_functools.py +++ b/Lib/test/test_functools.py @@ -618,7 +618,7 @@ def check_wrapper(self, wrapper, wrapped, def _default_update(self): - def f(a:'This is a new annotation'): + def f(a: int): """This is a test""" pass f.attr = 'This is also a test' @@ -635,7 +635,7 @@ def test_default_update(self): self.assertEqual(wrapper.__name__, 'f') self.assertEqual(wrapper.__qualname__, f.__qualname__) self.assertEqual(wrapper.attr, 'This is also a test') - self.assertEqual(wrapper.__annotations__['a'], 'This is a new annotation') + self.assertEqual(wrapper.__annotations__['a'], 'int') self.assertNotIn('b', wrapper.__annotations__) @unittest.skipIf(sys.flags.optimize >= 2, diff --git a/Lib/test/test_grammar.py b/Lib/test/test_grammar.py index 5235fa2c783f0..2f6716dfc9a13 100644 --- a/Lib/test/test_grammar.py +++ b/Lib/test/test_grammar.py @@ -362,7 +362,7 @@ class C: z = 2 def __init__(self, x): self.x: int = x - self.assertEqual(C.__annotations__, {'_C__foo': int, 's': str}) + self.assertEqual(C.__annotations__, {'_C__foo': 'int', 's': 'str'}) with self.assertRaises(NameError): class CBad: no_such_name_defined.attr: int = 0 @@ -378,15 +378,15 @@ def __prepare__(metacls, name, bases, **kwds): return {'__annotations__': CNS()} class CC(metaclass=CMeta): XX: 'ANNOT' - self.assertEqual(CC.__annotations__['xx'], 'ANNOT') + self.assertEqual(CC.__annotations__['xx'], repr('ANNOT')) def test_var_annot_module_semantics(self): with self.assertRaises(AttributeError): print(test.__annotations__) self.assertEqual(ann_module.__annotations__, - {1: 2, 'x': int, 'y': str, 'f': typing.Tuple[int, int]}) + {1: 2, 'x': 'int', 'y': 'str', 'f': 'Tuple[int, int]'}) self.assertEqual(ann_module.M.__annotations__, - {'123': 123, 'o': type}) + {'123': 123, 'o': 'type'}) self.assertEqual(ann_module2.__annotations__, {}) def test_var_annot_in_module(self): @@ -405,7 +405,7 @@ def test_var_annot_simple_exec(self): exec("'docstring'\n" "__annotations__[1] = 2\n" "x: int = 5\n", gns, lns) - self.assertEqual(lns["__annotations__"], {1: 2, 'x': int}) + self.assertEqual(lns["__annotations__"], {1: 2, 'x': 'int'}) with self.assertRaises(KeyError): gns['__annotations__'] @@ -413,8 +413,8 @@ def test_var_annot_custom_maps(self): # tests with custom locals() and __annotations__ ns = {'__annotations__': CNS()} exec('X: int; Z: str = "Z"; (w): complex = 1j', ns) - self.assertEqual(ns['__annotations__']['x'], int) - self.assertEqual(ns['__annotations__']['z'], str) + self.assertEqual(ns['__annotations__']['x'], 'int') + self.assertEqual(ns['__annotations__']['z'], 'str') with self.assertRaises(KeyError): ns['__annotations__']['w'] nonloc_ns = {} @@ -428,7 +428,7 @@ def __setitem__(self, item, value): def __getitem__(self, item): return self._dct[item] exec('x: int = 1', {}, CNS2()) - self.assertEqual(nonloc_ns['__annotations__']['x'], int) + self.assertEqual(nonloc_ns['__annotations__']['x'], 'int') def test_var_annot_refleak(self): # complex case: custom locals plus custom __annotations__ @@ -445,7 +445,7 @@ def __setitem__(self, item, value): def __getitem__(self, item): return self._dct[item] exec('X: str', {}, CNS2()) - self.assertEqual(nonloc_ns['__annotations__']['x'], str) + self.assertEqual(nonloc_ns['__annotations__']['x'], 'str') def test_var_annot_rhs(self): ns = {} @@ -625,50 +625,46 @@ def f(*args, **kwargs): # argument annotation tests def f(x) -> list: pass - self.assertEqual(f.__annotations__, {'return': list}) + self.assertEqual(f.__annotations__, {'return': 'list'}) def f(x: int): pass - self.assertEqual(f.__annotations__, {'x': int}) + self.assertEqual(f.__annotations__, {'x': 'int'}) def f(x: int, /): pass - self.assertEqual(f.__annotations__, {'x': int}) + self.assertEqual(f.__annotations__, {'x': 'int'}) def f(x: int = 34, /): pass - self.assertEqual(f.__annotations__, {'x': int}) + self.assertEqual(f.__annotations__, {'x': 'int'}) def f(*x: str): pass - self.assertEqual(f.__annotations__, {'x': str}) + self.assertEqual(f.__annotations__, {'x': 'str'}) def f(**x: float): pass - self.assertEqual(f.__annotations__, {'x': float}) - def f(x, y: 1+2): pass - self.assertEqual(f.__annotations__, {'y': 3}) - def f(x, y: 1+2, /): pass - self.assertEqual(f.__annotations__, {'y': 3}) + self.assertEqual(f.__annotations__, {'x': 'float'}) def f(a, b: 1, c: 2, d): pass - self.assertEqual(f.__annotations__, {'b': 1, 'c': 2}) + self.assertEqual(f.__annotations__, {'b': '1', 'c': '2'}) def f(a, b: 1, /, c: 2, d): pass - self.assertEqual(f.__annotations__, {'b': 1, 'c': 2}) + self.assertEqual(f.__annotations__, {'b': '1', 'c': '2'}) def f(a, b: 1, c: 2, d, e: 3 = 4, f=5, *g: 6): pass self.assertEqual(f.__annotations__, - {'b': 1, 'c': 2, 'e': 3, 'g': 6}) + {'b': '1', 'c': '2', 'e': '3', 'g': '6'}) def f(a, b: 1, c: 2, d, e: 3 = 4, f=5, *g: 6, h: 7, i=8, j: 9 = 10, **k: 11) -> 12: pass self.assertEqual(f.__annotations__, - {'b': 1, 'c': 2, 'e': 3, 'g': 6, 'h': 7, 'j': 9, - 'k': 11, 'return': 12}) + {'b': '1', 'c': '2', 'e': '3', 'g': '6', 'h': '7', 'j': '9', + 'k': '11', 'return': '12'}) def f(a, b: 1, c: 2, d, e: 3 = 4, f: int = 5, /, *g: 6, h: 7, i=8, j: 9 = 10, **k: 11) -> 12: pass self.assertEqual(f.__annotations__, - {'b': 1, 'c': 2, 'e': 3, 'f': int, 'g': 6, 'h': 7, 'j': 9, - 'k': 11, 'return': 12}) + {'b': '1', 'c': '2', 'e': '3', 'f': 'int', 'g': '6', 'h': '7', 'j': '9', + 'k': '11', 'return': '12'}) # Check for issue #20625 -- annotations mangling class Spam: def f(self, *, __kw: 1): pass class Ham(Spam): pass - self.assertEqual(Spam.f.__annotations__, {'_Spam__kw': 1}) - self.assertEqual(Ham.f.__annotations__, {'_Spam__kw': 1}) + self.assertEqual(Spam.f.__annotations__, {'_Spam__kw': '1'}) + self.assertEqual(Ham.f.__annotations__, {'_Spam__kw': '1'}) # Check for SF Bug #1697248 - mixing decorators and a return annotation def null(x): return x @null def f(x) -> list: pass - self.assertEqual(f.__annotations__, {'return': list}) + self.assertEqual(f.__annotations__, {'return': 'list'}) # Test expressions as decorators (PEP 614): @False or null @@ -1116,8 +1112,6 @@ def g(): rest = 4, 5, 6; yield 1, 2, 3, *rest # Not allowed at class scope check_syntax_error(self, "class foo:yield 1") check_syntax_error(self, "class foo:yield from ()") - # Check annotation refleak on SyntaxError - check_syntax_error(self, "def g(a:(yield)): pass") def test_yield_in_comprehensions(self): # Check yield in comprehensions diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py index 6667dc91edbce..71c4f27d27b98 100644 --- a/Lib/test/test_inspect.py +++ b/Lib/test/test_inspect.py @@ -862,7 +862,7 @@ def test_getfullargspec(self): self.assertFullArgSpecEquals(mod2.annotated, ['arg1'], ann_e={'arg1' : list}, - formatted='(arg1: list)') + formatted="(arg1: list)") self.assertFullArgSpecEquals(mod2.keyword_only_arg, [], kwonlyargs_e=['arg'], formatted='(*, arg)') @@ -2211,8 +2211,8 @@ def test(a, b:'foo') -> 123: pass self.assertEqual(self.signature(test), ((('a', ..., ..., "positional_or_keyword"), - ('b', ..., 'foo', "positional_or_keyword")), - 123)) + ('b', ..., repr('foo'), "positional_or_keyword")), + '123')) def test_signature_on_wkwonly(self): def test(*, a:float, b:str) -> int: @@ -2227,11 +2227,11 @@ def test(a, b:'foo'=10, *args:'bar', spam:'baz', ham=123, **kwargs:int): pass self.assertEqual(self.signature(test), ((('a', ..., ..., "positional_or_keyword"), - ('b', 10, 'foo', "positional_or_keyword"), - ('args', ..., 'bar', "var_positional"), - ('spam', ..., 'baz', "keyword_only"), + ('b', 10, repr('foo'), "positional_or_keyword"), + ('args', ..., repr('bar'), "var_positional"), + ('spam', ..., repr('baz'), "keyword_only"), ('ham', 123, ..., "keyword_only"), - ('kwargs', ..., int, "var_keyword")), + ('kwargs', ..., 'int', "var_keyword")), ...)) def test_signature_without_self(self): @@ -2640,12 +2640,12 @@ def test(a, b, c:int) -> 42: self.assertEqual(self.signature(partial(partial(test, 1))), ((('b', ..., ..., "positional_or_keyword"), - ('c', ..., int, "positional_or_keyword")), - 42)) + ('c', ..., 'int', "positional_or_keyword")), + '42')) self.assertEqual(self.signature(partial(partial(test, 1), 2)), - ((('c', ..., int, "positional_or_keyword"),), - 42)) + ((('c', ..., 'int', "positional_or_keyword"),), + '42')) psig = inspect.signature(partial(partial(test, 1), 2)) @@ -2764,12 +2764,12 @@ def test(it, a, *, c) -> 'spam': ((('it', ..., ..., 'positional_or_keyword'), ('a', ..., ..., 'positional_or_keyword'), ('c', 1, ..., 'keyword_only')), - 'spam')) + repr('spam'))) self.assertEqual(self.signature(Spam().ham), ((('a', ..., ..., 'positional_or_keyword'), ('c', 1, ..., 'keyword_only')), - 'spam')) + repr('spam'))) class Spam: def test(self: 'anno', x): @@ -2778,7 +2778,7 @@ def test(self: 'anno', x): g = partialmethod(test, 1) self.assertEqual(self.signature(Spam.g), - ((('self', ..., 'anno', 'positional_or_keyword'),), + ((('self', ..., repr('anno'), 'positional_or_keyword'),), ...)) def test_signature_on_fake_partialmethod(self): @@ -3116,20 +3116,16 @@ def foo(a={}): pass with self.assertRaisesRegex(TypeError, 'unhashable type'): hash(inspect.signature(foo)) - def foo(a) -> {}: pass - with self.assertRaisesRegex(TypeError, 'unhashable type'): - hash(inspect.signature(foo)) - def test_signature_str(self): def foo(a:int=1, *, b, c=None, **kwargs) -> 42: pass self.assertEqual(str(inspect.signature(foo)), - '(a: int = 1, *, b, c=None, **kwargs) -> 42') + '(a: \'int\' = 1, *, b, c=None, **kwargs) -> \'42\'') def foo(a:int=1, *args, b, c=None, **kwargs) -> 42: pass self.assertEqual(str(inspect.signature(foo)), - '(a: int = 1, *args, b, c=None, **kwargs) -> 42') + '(a: \'int\' = 1, *args, b, c=None, **kwargs) -> \'42\'') def foo(): pass @@ -3172,8 +3168,8 @@ def test() -> 42: self.assertIs(sig.return_annotation, None) sig = sig.replace(return_annotation=sig.empty) self.assertIs(sig.return_annotation, sig.empty) - sig = sig.replace(return_annotation=42) - self.assertEqual(sig.return_annotation, 42) + sig = sig.replace(return_annotation='42') + self.assertEqual(sig.return_annotation, '42') self.assertEqual(sig, inspect.signature(test)) def test_signature_on_mangled_parameters(self): @@ -3185,8 +3181,8 @@ class Ham(Spam): self.assertEqual(self.signature(Spam.foo), ((('self', ..., ..., "positional_or_keyword"), - ('_Spam__p1', 2, 1, "positional_or_keyword"), - ('_Spam__p2', 3, 2, "keyword_only")), + ('_Spam__p1', 2, '1', "positional_or_keyword"), + ('_Spam__p2', 3, '2', "keyword_only")), ...)) self.assertEqual(self.signature(Spam.foo), diff --git a/Lib/test/test_opcodes.py b/Lib/test/test_opcodes.py index 527aca664d38e..1152eb65bb2c3 100644 --- a/Lib/test/test_opcodes.py +++ b/Lib/test/test_opcodes.py @@ -39,7 +39,7 @@ class C: pass def test_use_existing_annotations(self): ns = {'__annotations__': {1: 2}} exec('x: int', ns) - self.assertEqual(ns['__annotations__'], {'x': int, 1: 2}) + self.assertEqual(ns['__annotations__'], {'x': 'int', 1: 2}) def test_do_not_recreate_annotations(self): # Don't rely on the existence of the '__annotations__' global. diff --git a/Lib/test/test_positional_only_arg.py b/Lib/test/test_positional_only_arg.py index 0a9503e2025d6..1fe8256d46ea4 100644 --- a/Lib/test/test_positional_only_arg.py +++ b/Lib/test/test_positional_only_arg.py @@ -302,14 +302,14 @@ def inner_has_pos_only(): def f(x: int, /): ... return f - assert inner_has_pos_only().__annotations__ == {'x': int} + assert inner_has_pos_only().__annotations__ == {'x': 'int'} class Something: def method(self): def f(x: int, /): ... return f - assert Something().method().__annotations__ == {'x': int} + assert Something().method().__annotations__ == {'x': 'int'} def multiple_levels(): def inner_has_pos_only(): @@ -317,7 +317,7 @@ def f(x: int, /): ... return f return inner_has_pos_only() - assert multiple_levels().__annotations__ == {'x': int} + assert multiple_levels().__annotations__ == {'x': 'int'} def test_same_keyword_as_positional_with_kwargs(self): def f(something,/,**kwargs): @@ -429,17 +429,6 @@ def method(self, /): self.assertEqual(C().method(), sentinel) - def test_annotations_constant_fold(self): - def g(): - def f(x: not (int is int), /): ... - - # without constant folding we end up with - # COMPARE_OP(is), IS_OP (0) - # with constant folding we should expect a IS_OP (1) - codes = [(i.opname, i.argval) for i in dis.get_instructions(g)] - self.assertNotIn(('UNARY_NOT', None), codes) - self.assertIn(('IS_OP', 1), codes) - if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_pydoc.py b/Lib/test/test_pydoc.py index 76d2af8e461ed..2f502627f4d0a 100644 --- a/Lib/test/test_pydoc.py +++ b/Lib/test/test_pydoc.py @@ -81,7 +81,7 @@ class B(builtins.object) |\x20\x20 | NO_MEANING = 'eggs' |\x20\x20 - | __annotations__ = {'NO_MEANING': } + | __annotations__ = {'NO_MEANING': 'str'} \x20\x20\x20\x20 class C(builtins.object) | Methods defined here: @@ -194,7 +194,7 @@ class C(builtins.object) Data and other attributes defined here:
NO_MEANING = 'eggs'
-
__annotations__ = {'NO_MEANING': <class 'str'>}
+
__annotations__ = {'NO_MEANING': 'str'}

diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index 09c6eb3375409..7c3302c1d46ae 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -752,14 +752,6 @@ Traceback (most recent call last): SyntaxError: cannot assign to __debug__ - >>> def f(*args:(lambda __debug__:0)): pass - Traceback (most recent call last): - SyntaxError: cannot assign to __debug__ - - >>> def f(**kwargs:(lambda __debug__:0)): pass - Traceback (most recent call last): - SyntaxError: cannot assign to __debug__ - >>> with (lambda *:0): pass Traceback (most recent call last): SyntaxError: named arguments must follow bare * diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py index 52a59d54f044d..75c5eee42dc54 100644 --- a/Lib/test/test_types.py +++ b/Lib/test/test_types.py @@ -671,8 +671,8 @@ def test_or_type_operator_with_forward(self): ForwardBefore = 'Forward' | T def forward_after(x: ForwardAfter[int]) -> None: ... def forward_before(x: ForwardBefore[int]) -> None: ... - assert typing.get_args(typing.get_type_hints(forward_after)['x']) == (int, Forward) - assert typing.get_args(typing.get_type_hints(forward_before)['x']) == (int, Forward) + assert typing.get_args(typing.get_type_hints(forward_after, localns=locals())['x']) == (int, Forward) + assert typing.get_args(typing.get_type_hints(forward_before, localns=locals())['x']) == (int, Forward) def test_or_type_operator_with_Protocol(self): class Proto(typing.Protocol): diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 42aa430c5e107..4bef42f4f32fc 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -349,7 +349,7 @@ def test_empty(self): def test_no_eval_union(self): u = Union[int, str] def f(x: u): ... - self.assertIs(get_type_hints(f)['x'], u) + self.assertIs(get_type_hints(f, globals(), locals())['x'], u) def test_function_repr_union(self): def fun() -> int: ... @@ -2849,11 +2849,11 @@ def test_get_type_hints_classes(self): self.assertEqual(gth(HasForeignBaseClass), {'some_xrepr': XRepr, 'other_a': mod_generics_cache.A, 'some_b': mod_generics_cache.B}) - self.assertEqual(gth(XRepr.__new__), + self.assertEqual(gth(XRepr), {'x': int, 'y': int}) self.assertEqual(gth(mod_generics_cache.B), {'my_inner_a1': mod_generics_cache.B.A, - 'my_inner_a2': mod_generics_cache.B.A, + 'my_inner_a2': mod_generics_cache.A, 'my_outer_a': mod_generics_cache.A}) def test_respect_no_type_check(self): @@ -3641,7 +3641,7 @@ def test_annotation_usage(self): self.assertEqual(tim.cool, 9000) self.assertEqual(CoolEmployee.__name__, 'CoolEmployee') self.assertEqual(CoolEmployee._fields, ('name', 'cool')) - self.assertEqual(CoolEmployee.__annotations__, + self.assertEqual(gth(CoolEmployee), collections.OrderedDict(name=str, cool=int)) def test_annotation_usage_with_default(self): @@ -3655,7 +3655,7 @@ def test_annotation_usage_with_default(self): self.assertEqual(CoolEmployeeWithDefault.__name__, 'CoolEmployeeWithDefault') self.assertEqual(CoolEmployeeWithDefault._fields, ('name', 'cool')) - self.assertEqual(CoolEmployeeWithDefault.__annotations__, + self.assertEqual(gth(CoolEmployeeWithDefault), dict(name=str, cool=int)) self.assertEqual(CoolEmployeeWithDefault._field_defaults, dict(cool=0)) @@ -3823,7 +3823,7 @@ def test_typeddict_errors(self): def test_py36_class_syntax_usage(self): self.assertEqual(LabelPoint2D.__name__, 'LabelPoint2D') self.assertEqual(LabelPoint2D.__module__, __name__) - self.assertEqual(LabelPoint2D.__annotations__, {'x': int, 'y': int, 'label': str}) + self.assertEqual(gth(LabelPoint2D), {'x': int, 'y': int, 'label': str}) self.assertEqual(LabelPoint2D.__bases__, (dict,)) self.assertEqual(LabelPoint2D.__total__, True) self.assertNotIsSubclass(LabelPoint2D, typing.Sequence) @@ -3882,11 +3882,11 @@ class Cat(Animal): assert BaseAnimal.__required_keys__ == frozenset(['name']) assert BaseAnimal.__optional_keys__ == frozenset([]) - assert BaseAnimal.__annotations__ == {'name': str} + assert gth(BaseAnimal) == {'name': str} assert Animal.__required_keys__ == frozenset(['name']) assert Animal.__optional_keys__ == frozenset(['tail', 'voice']) - assert Animal.__annotations__ == { + assert gth(Animal) == { 'name': str, 'tail': bool, 'voice': str, @@ -3894,7 +3894,7 @@ class Cat(Animal): assert Cat.__required_keys__ == frozenset(['name', 'fur_color']) assert Cat.__optional_keys__ == frozenset(['tail', 'voice']) - assert Cat.__annotations__ == { + assert gth(Cat) == { 'fur_color': str, 'name': str, 'tail': bool, @@ -3915,7 +3915,7 @@ def test_io(self): def stuff(a: IO) -> AnyStr: return a.readline() - a = stuff.__annotations__['a'] + a = gth(stuff)['a'] self.assertEqual(a.__parameters__, (AnyStr,)) def test_textio(self): @@ -3923,7 +3923,7 @@ def test_textio(self): def stuff(a: TextIO) -> str: return a.readline() - a = stuff.__annotations__['a'] + a = gth(stuff)['a'] self.assertEqual(a.__parameters__, ()) def test_binaryio(self): @@ -3931,7 +3931,7 @@ def test_binaryio(self): def stuff(a: BinaryIO) -> bytes: return a.readline() - a = stuff.__annotations__['a'] + a = gth(stuff)['a'] self.assertEqual(a.__parameters__, ()) def test_io_submodule(self): diff --git a/Lib/typing.py b/Lib/typing.py index 8c61bd8e084a8..4cf33c1ae9265 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -18,6 +18,7 @@ """ from abc import abstractmethod, ABCMeta +import ast import collections import collections.abc import contextlib @@ -469,6 +470,13 @@ class ForwardRef(_Final, _root=True): def __init__(self, arg, is_argument=True): if not isinstance(arg, str): raise TypeError(f"Forward reference must be a string -- got {arg!r}") + + # Double-stringified forward references is a result of activating + # the 'annotations' future by default. This way, we eliminate them in + # the runtime. + if arg.startswith(("'", '\"')) and arg.endswith(("'", '"')): + arg = arg[1:-1] + try: code = compile(arg, '', 'eval') except SyntaxError: diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-05-27-16-08-16.bpo-38605.rcs2uK.rst b/Misc/NEWS.d/next/Core and Builtins/2020-05-27-16-08-16.bpo-38605.rcs2uK.rst new file mode 100644 index 0000000000000..cbfe6e23523bb --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2020-05-27-16-08-16.bpo-38605.rcs2uK.rst @@ -0,0 +1,3 @@ +Enable ``from __future__ import annotations`` (:pep:`563`) by default. +The values found in :attr:`__annotations__` dicts are now strings, e.g. +``{"x": "int"}`` instead of ``{"x": int}``. diff --git a/Python/ast_opt.c b/Python/ast_opt.c index 5efaac4c8925a..22ca6f23aefa3 100644 --- a/Python/ast_opt.c +++ b/Python/ast_opt.c @@ -392,7 +392,6 @@ static int astfold_expr(expr_ty node_, PyArena *ctx_, _PyASTOptimizeState *state static int astfold_arguments(arguments_ty node_, PyArena *ctx_, _PyASTOptimizeState *state); static int astfold_comprehension(comprehension_ty node_, PyArena *ctx_, _PyASTOptimizeState *state); static int astfold_keyword(keyword_ty node_, PyArena *ctx_, _PyASTOptimizeState *state); -static int astfold_arg(arg_ty node_, PyArena *ctx_, _PyASTOptimizeState *state); static int astfold_withitem(withitem_ty node_, PyArena *ctx_, _PyASTOptimizeState *state); static int astfold_excepthandler(excepthandler_ty node_, PyArena *ctx_, _PyASTOptimizeState *state); #define CALL(FUNC, TYPE, ARG) \ @@ -595,25 +594,11 @@ astfold_comprehension(comprehension_ty node_, PyArena *ctx_, _PyASTOptimizeState static int astfold_arguments(arguments_ty node_, PyArena *ctx_, _PyASTOptimizeState *state) { - CALL_SEQ(astfold_arg, arg, node_->posonlyargs); - CALL_SEQ(astfold_arg, arg, node_->args); - CALL_OPT(astfold_arg, arg_ty, node_->vararg); - CALL_SEQ(astfold_arg, arg, node_->kwonlyargs); CALL_SEQ(astfold_expr, expr, node_->kw_defaults); - CALL_OPT(astfold_arg, arg_ty, node_->kwarg); CALL_SEQ(astfold_expr, expr, node_->defaults); return 1; } -static int -astfold_arg(arg_ty node_, PyArena *ctx_, _PyASTOptimizeState *state) -{ - if (!(state->ff_features & CO_FUTURE_ANNOTATIONS)) { - CALL_OPT(astfold_expr, expr_ty, node_->annotation); - } - return 1; -} - static int astfold_stmt(stmt_ty node_, PyArena *ctx_, _PyASTOptimizeState *state) { @@ -622,17 +607,11 @@ astfold_stmt(stmt_ty node_, PyArena *ctx_, _PyASTOptimizeState *state) CALL(astfold_arguments, arguments_ty, node_->v.FunctionDef.args); CALL(astfold_body, asdl_seq, node_->v.FunctionDef.body); CALL_SEQ(astfold_expr, expr, node_->v.FunctionDef.decorator_list); - if (!(state->ff_features & CO_FUTURE_ANNOTATIONS)) { - CALL_OPT(astfold_expr, expr_ty, node_->v.FunctionDef.returns); - } break; case AsyncFunctionDef_kind: CALL(astfold_arguments, arguments_ty, node_->v.AsyncFunctionDef.args); CALL(astfold_body, asdl_seq, node_->v.AsyncFunctionDef.body); CALL_SEQ(astfold_expr, expr, node_->v.AsyncFunctionDef.decorator_list); - if (!(state->ff_features & CO_FUTURE_ANNOTATIONS)) { - CALL_OPT(astfold_expr, expr_ty, node_->v.AsyncFunctionDef.returns); - } break; case ClassDef_kind: CALL_SEQ(astfold_expr, expr, node_->v.ClassDef.bases); @@ -656,9 +635,6 @@ astfold_stmt(stmt_ty node_, PyArena *ctx_, _PyASTOptimizeState *state) break; case AnnAssign_kind: CALL(astfold_expr, expr_ty, node_->v.AnnAssign.target); - if (!(state->ff_features & CO_FUTURE_ANNOTATIONS)) { - CALL(astfold_expr, expr_ty, node_->v.AnnAssign.annotation); - } CALL_OPT(astfold_expr, expr_ty, node_->v.AnnAssign.value); break; case For_kind: diff --git a/Python/compile.c b/Python/compile.c index f2563d7f7a411..ddd2a049629c1 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -2026,12 +2026,7 @@ compiler_visit_argannotation(struct compiler *c, identifier id, { if (annotation) { PyObject *mangled; - if (c->c_future->ff_features & CO_FUTURE_ANNOTATIONS) { - VISIT(c, annexpr, annotation) - } - else { - VISIT(c, expr, annotation); - } + VISIT(c, annexpr, annotation); mangled = _Py_Mangle(c->u->u_private, id); if (!mangled) return 0; @@ -5261,12 +5256,7 @@ compiler_annassign(struct compiler *c, stmt_ty s) if (s->v.AnnAssign.simple && (c->u->u_scope_type == COMPILER_SCOPE_MODULE || c->u->u_scope_type == COMPILER_SCOPE_CLASS)) { - if (c->c_future->ff_features & CO_FUTURE_ANNOTATIONS) { - VISIT(c, annexpr, s->v.AnnAssign.annotation) - } - else { - VISIT(c, expr, s->v.AnnAssign.annotation); - } + VISIT(c, annexpr, s->v.AnnAssign.annotation); ADDOP_NAME(c, LOAD_NAME, __annotations__, names); mangled = _Py_Mangle(c->u->u_private, targ->v.Name.id); ADDOP_LOAD_CONST_NEW(c, mangled); diff --git a/Python/future.c b/Python/future.c index 3cea4fee78085..4b73eb6412905 100644 --- a/Python/future.c +++ b/Python/future.c @@ -41,7 +41,7 @@ future_check_features(PyFutureFeatures *ff, stmt_ty s, PyObject *filename) } else if (strcmp(feature, FUTURE_GENERATOR_STOP) == 0) { continue; } else if (strcmp(feature, FUTURE_ANNOTATIONS) == 0) { - ff->ff_features |= CO_FUTURE_ANNOTATIONS; + continue; } else if (strcmp(feature, "braces") == 0) { PyErr_SetString(PyExc_SyntaxError, "not a chance"); From webhook-mailer at python.org Wed Oct 7 10:13:13 2020 From: webhook-mailer at python.org (Stefan Pochmann) Date: Wed, 07 Oct 2020 14:13:13 -0000 Subject: [Python-checkins] Fix comment about PyObject_IsTrue. (GH-22343) Message-ID: https://github.com/python/cpython/commit/f90dc36c15d7fee0efaf6d39e97be0bdf2683e93 commit: f90dc36c15d7fee0efaf6d39e97be0bdf2683e93 branch: master author: Stefan Pochmann <609905+pochmann at users.noreply.github.com> committer: GitHub date: 2020-10-07T09:12:52-05:00 summary: Fix comment about PyObject_IsTrue. (GH-22343) The `for` statement doesn't use a condition and this function, the `while` statement does. files: M Objects/object.c diff --git a/Objects/object.c b/Objects/object.c index fe3734404f5cf..9889503cfd893 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -1387,7 +1387,7 @@ PyObject_GenericSetDict(PyObject *obj, PyObject *value, void *context) } -/* Test a value used as condition, e.g., in a for or if statement. +/* Test a value used as condition, e.g., in a while or if statement. Return -1 if an error occurred */ int From webhook-mailer at python.org Wed Oct 7 17:44:50 2020 From: webhook-mailer at python.org (Mikhail Golubev) Date: Wed, 07 Oct 2020 21:44:50 -0000 Subject: [Python-checkins] bpo-41923: PEP 613: Add TypeAlias to typing module (#22532) Message-ID: https://github.com/python/cpython/commit/4f3c25043d651a13c41cffcee703f7d5cb677cc7 commit: 4f3c25043d651a13c41cffcee703f7d5cb677cc7 branch: master author: Mikhail Golubev committer: GitHub date: 2020-10-07T14:44:31-07:00 summary: bpo-41923: PEP 613: Add TypeAlias to typing module (#22532) This special marker annotation is intended to help in distinguishing proper PEP 484-compliant type aliases from regular top-level variable assignments. files: A Misc/NEWS.d/next/Library/2020-10-03-23-14-50.bpo-41923.Buonw9.rst M Doc/library/typing.rst M Doc/whatsnew/3.10.rst M Lib/test/test_typing.py M Lib/typing.py M Misc/ACKS diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index a72632e61b073..f4b2718cdc2f8 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -34,6 +34,8 @@ In the function ``greeting``, the argument ``name`` is expected to be of type :class:`str` and the return type :class:`str`. Subtypes are accepted as arguments. +.. _type-aliases: + Type aliases ============ @@ -489,6 +491,17 @@ These can be used as types in annotations and do not support ``[]``. .. versionadded:: 3.5.4 .. versionadded:: 3.6.2 +.. data:: TypeAlias + + Special annotation for explicitly declaring a :ref:`type alias `. + For example:: + + from typing import TypeAlias + + Factors: TypeAlias = list[int] + + .. versionadded:: 3.10 + Special forms """"""""""""" diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index 2bcdba69957b6..4ada4be3b6671 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -99,8 +99,29 @@ in :issue:`38605`.) * :pep:`618`: The :func:`zip` function now has an optional ``strict`` flag, used to require that all the iterables have an equal length. -PEP604: New Type Operator -------------------------- +PEP 613: TypeAlias Annotation +----------------------------- + +:pep:`484` introduced the concept of type aliases, only requiring them to be +top-level unannotated assignments. This simplicity sometimes made it difficult +for type checkers to distinguish between type aliases and ordinary assignments, +especially when forward references or invalid types were involved. Compare:: + + StrCache = 'Cache[str]' # a type alias + LOG_PREFIX = 'LOG[DEBUG]' # a module constant + +Now the :mod:`typing` module has a special annotation :data:`TypeAlias` to +declare type aliases more explicitly:: + + StrCache: TypeAlias = 'Cache[str]' # a type alias + LOG_PREFIX = 'LOG[DEBUG]' # a module constant + +See :pep:`613` for more details. + +(Contributed by Mikhail Golubev in :issue:`41923`.) + +PEP604: New Type Union Operator +------------------------------- A new type union operator was introduced which enables the syntax ``X | Y``. This provides a cleaner way of expressing 'either type X or type Y' instead of diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 4bef42f4f32fc..57dd73c529da5 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -24,6 +24,7 @@ from typing import IO, TextIO, BinaryIO from typing import Pattern, Match from typing import Annotated, ForwardRef +from typing import TypeAlias import abc import typing import weakref @@ -4176,6 +4177,45 @@ def test_annotated_in_other_types(self): self.assertEqual(X[int], List[Annotated[int, 5]]) +class TypeAliasTests(BaseTestCase): + def test_canonical_usage_with_variable_annotation(self): + Alias: TypeAlias = Employee + + def test_canonical_usage_with_type_comment(self): + Alias = Employee # type: TypeAlias + + def test_cannot_instantiate(self): + with self.assertRaises(TypeError): + TypeAlias() + + def test_no_isinstance(self): + with self.assertRaises(TypeError): + isinstance(42, TypeAlias) + + def test_no_issubclass(self): + with self.assertRaises(TypeError): + issubclass(Employee, TypeAlias) + + with self.assertRaises(TypeError): + issubclass(TypeAlias, Employee) + + def test_cannot_subclass(self): + with self.assertRaises(TypeError): + class C(TypeAlias): + pass + + with self.assertRaises(TypeError): + class C(type(TypeAlias)): + pass + + def test_repr(self): + self.assertEqual(repr(TypeAlias), 'typing.TypeAlias') + + def test_cannot_subscript(self): + with self.assertRaises(TypeError): + TypeAlias[int] + + class AllTests(BaseTestCase): """Tests for __all__.""" diff --git a/Lib/typing.py b/Lib/typing.py index 4cf33c1ae9265..0f457ab1f56df 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -113,6 +113,7 @@ 'runtime_checkable', 'Text', 'TYPE_CHECKING', + 'TypeAlias', ] # The pseudo-submodules 're' and 'io' are part of the public @@ -460,6 +461,21 @@ def open_helper(file: str, mode: MODE) -> str: return _GenericAlias(self, parameters) + at _SpecialForm +def TypeAlias(self, parameters): + """Special marker indicating that an assignment should + be recognized as a proper type alias definition by type + checkers. + + For example:: + + Predicate: TypeAlias = Callable[..., bool] + + It's invalid when used anywhere except as in the example above. + """ + raise TypeError(f"{self} is not subscriptable") + + class ForwardRef(_Final, _root=True): """Internal wrapper to hold a forward reference.""" diff --git a/Misc/ACKS b/Misc/ACKS index 08449fe08269b..7d445c5721455 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -611,6 +611,7 @@ Christoph Gohlke Tim Golden Yonatan Goldschmidt Mark Gollahon +Mikhail Golubev Guilherme Gon?alves Tiago Gon?alves Chris Gonnerman diff --git a/Misc/NEWS.d/next/Library/2020-10-03-23-14-50.bpo-41923.Buonw9.rst b/Misc/NEWS.d/next/Library/2020-10-03-23-14-50.bpo-41923.Buonw9.rst new file mode 100644 index 0000000000000..dd9a1f709f33f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-10-03-23-14-50.bpo-41923.Buonw9.rst @@ -0,0 +1 @@ +Implement :pep:`613`, introducing :data:`typing.TypeAlias` annotation. From webhook-mailer at python.org Wed Oct 7 19:43:53 2020 From: webhook-mailer at python.org (Raymond Hettinger) Date: Wed, 07 Oct 2020 23:43:53 -0000 Subject: [Python-checkins] Revert "bpo-26680: Incorporate is_integer in all built-in and standard library numeric types (GH-6121)" (GH-22584) Message-ID: https://github.com/python/cpython/commit/4e0ce820586e93cfcefb16c2a3df8eaefc54cbca commit: 4e0ce820586e93cfcefb16c2a3df8eaefc54cbca branch: master author: Raymond Hettinger committer: GitHub date: 2020-10-07T16:43:44-07:00 summary: Revert "bpo-26680: Incorporate is_integer in all built-in and standard library numeric types (GH-6121)" (GH-22584) This reverts commit 58a7da9e125422323f79c4ee95ac5549989d8162. files: M Doc/library/decimal.rst M Doc/library/numbers.rst M Doc/library/stdtypes.rst M Lib/_pydecimal.py M Lib/numbers.py M Lib/test/decimaltestdata/extra.decTest M Lib/test/test_bool.py M Lib/test/test_decimal.py M Lib/test/test_fractions.py M Lib/test/test_long.py M Lib/test/test_numeric_tower.py M Misc/ACKS M Modules/_decimal/_decimal.c M Modules/_decimal/docstrings.h M Objects/clinic/longobject.c.h M Objects/longobject.c diff --git a/Doc/library/decimal.rst b/Doc/library/decimal.rst index 7a6497305952f..e194649e30d85 100644 --- a/Doc/library/decimal.rst +++ b/Doc/library/decimal.rst @@ -621,13 +621,6 @@ Decimal objects Return :const:`True` if the argument is either positive or negative infinity and :const:`False` otherwise. - .. method:: is_integer() - - Return :const:`True` if the argument is a finite integral value and - :const:`False` otherwise. - - .. versionadded:: 3.10 - .. method:: is_nan() Return :const:`True` if the argument is a (quiet or signaling) NaN and @@ -1222,13 +1215,6 @@ In addition to the three supplied contexts, new contexts can be created with the Returns ``True`` if *x* is infinite; otherwise returns ``False``. - .. method:: is_integer(x) - - Returns ``True`` if *x* is finite and integral; otherwise - returns ``False``. - - .. versionadded:: 3.10 - .. method:: is_nan(x) Returns ``True`` if *x* is a qNaN or sNaN; otherwise returns ``False``. diff --git a/Doc/library/numbers.rst b/Doc/library/numbers.rst index 5d49f5eb96b7a..1b594952ead72 100644 --- a/Doc/library/numbers.rst +++ b/Doc/library/numbers.rst @@ -49,30 +49,19 @@ The numeric tower numbers. In short, those are: a conversion to :class:`float`, :func:`math.trunc`, - :func:`round`, :func:`math.floor`, :func:`math.ceil`, :func:`divmod`, - :func:`~Real.is_integer`, ``//``, ``%``, ``<``, ``<=``, ``>``, and ``>=``. + :func:`round`, :func:`math.floor`, :func:`math.ceil`, :func:`divmod`, ``//``, + ``%``, ``<``, ``<=``, ``>``, and ``>=``. Real also provides defaults for :func:`complex`, :attr:`~Complex.real`, :attr:`~Complex.imag`, and :meth:`~Complex.conjugate`. - .. method:: is_integer() - - Returns :const:`True` if this number has a finite and integral value, - otherwise :const:`False`. This is a default implementation which - relies on successful conversion to :class:`int`. It may be overridden - in subclasses (such as it is in :class:`float`) for better performance, - or to handle special values such as NaN which are not - convertible to :class:`int`. - - .. versionadded:: 3.10 - .. class:: Rational Subtypes :class:`Real` and adds :attr:`~Rational.numerator` and :attr:`~Rational.denominator` properties, which - should be in lowest terms. With these, it provides defaults for - :func:`float` and :func:`~Real.is_integer`. + should be in lowest terms. With these, it provides a default for + :func:`float`. .. attribute:: numerator @@ -86,10 +75,9 @@ The numeric tower .. class:: Integral Subtypes :class:`Rational` and adds a conversion to :class:`int`. Provides - defaults for :func:`float`, :attr:`~Rational.numerator`, - :attr:`~Rational.denominator`, and :func:`~Real.is_integer`. Adds abstract - methods for ``**`` and bit-string operations: ``<<``, ``>>``, ``&``, ``^``, - ``|``, ``~``. + defaults for :func:`float`, :attr:`~Rational.numerator`, and + :attr:`~Rational.denominator`. Adds abstract methods for ``**`` and + bit-string operations: ``<<``, ``>>``, ``&``, ``^``, ``|``, ``~``. Notes for type implementors diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 04dfea276d2b1..5c6acc66bb4cc 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -310,10 +310,6 @@ the operations, see :ref:`operator-summary`): +---------------------+---------------------------------+---------+--------------------+ | ``x ** y`` | *x* to the power *y* | \(5) | | +---------------------+---------------------------------+---------+--------------------+ -| ``x.is_integer()`` | ``True`` if *x* has a finite | | :func:`~numbers\ | -| | and integral value, otherwise | | .Real.is_integer` | -| | ``False``. | | | -+---------------------+---------------------------------+---------+--------------------+ .. index:: triple: operations on; numeric; types @@ -587,6 +583,16 @@ class`. float also has the following additional methods. :exc:`OverflowError` on infinities and a :exc:`ValueError` on NaNs. +.. method:: float.is_integer() + + Return ``True`` if the float instance is finite with integral + value, and ``False`` otherwise:: + + >>> (-2.0).is_integer() + True + >>> (3.2).is_integer() + False + Two methods support conversion to and from hexadecimal strings. Since Python's floats are stored internally as binary numbers, converting a float to or from a diff --git a/Lib/_pydecimal.py b/Lib/_pydecimal.py index 8c0ef57092219..ab989e5206a9e 100644 --- a/Lib/_pydecimal.py +++ b/Lib/_pydecimal.py @@ -3164,12 +3164,6 @@ def is_zero(self): """Return True if self is a zero; otherwise return False.""" return not self._is_special and self._int == '0' - def is_integer(self): - """Return True is self is finite and integral; otherwise False.""" - if self._is_special: - return False - return self.to_integral_value(rounding=ROUND_FLOOR) == self - def _ln_exp_bound(self): """Compute a lower bound for the adjusted exponent of self.ln(). In other words, compute r such that self.ln() >= 10**r. Assumes @@ -4665,25 +4659,6 @@ def is_zero(self, a): a = _convert_other(a, raiseit=True) return a.is_zero() - def is_integer(self, a): - """Return True if the operand is integral; otherwise return False. - - >>> ExtendedContext.is_integer(Decimal('0')) - True - >>> ExtendedContext.is_integer(Decimal('2.50')) - False - >>> ExtendedContext.is_integer(Decimal('-0E+2')) - True - >>> ExtendedContext.is_integer(Decimal('-0.5')) - False - >>> ExtendedContext.is_integer(Decimal('NaN')) - False - >>> ExtendedContext.is_integer(10) - True - """ - a = _convert_other(a, raiseit=True) - return a.is_integer() - def ln(self, a): """Returns the natural (base e) logarithm of the operand. diff --git a/Lib/numbers.py b/Lib/numbers.py index 0634f62ff123c..ed815ef41ebe1 100644 --- a/Lib/numbers.py +++ b/Lib/numbers.py @@ -148,7 +148,7 @@ class Real(Complex): """To Complex, Real adds the operations that work on real numbers. In short, those are: a conversion to float, trunc(), divmod, - is_integer, %, <, <=, >, and >=. + %, <, <=, >, and >=. Real also provides defaults for the derived operations. """ @@ -242,17 +242,6 @@ def __le__(self, other): """self <= other""" raise NotImplementedError - def is_integer(self): - """Return True if the Real is integral; otherwise return False. - - This default implementation can be overridden in subclasses - for performance reasons or to deal with values such as NaN, - which would otherwise cause an exception to be raised. - """ - # Although __int__ is not defined at this level, the int - # constructor falls back to __trunc__, which we do have. - return self == int(self) - # Concrete implementations of Complex abstract methods. def __complex__(self): """complex(self) == complex(float(self), 0)""" @@ -301,10 +290,6 @@ def __float__(self): """ return self.numerator / self.denominator - def is_integer(self): - """Return True if the Rational is integral; otherwise return False.""" - return self.denominator == 1 - class Integral(Rational): """Integral adds a conversion to int and the bit-string operations.""" @@ -401,8 +386,4 @@ def denominator(self): """Integers have a denominator of 1.""" return 1 - def is_integer(self): - """Return True; all Integrals represent an integral value.""" - return True - Integral.register(int) diff --git a/Lib/test/decimaltestdata/extra.decTest b/Lib/test/decimaltestdata/extra.decTest index 2f0719ed22342..b630d8e3f9d45 100644 --- a/Lib/test/decimaltestdata/extra.decTest +++ b/Lib/test/decimaltestdata/extra.decTest @@ -2346,24 +2346,6 @@ bool2096 iszero sNaN -> 0 bool2097 iszero -sNaN -> 0 bool2098 iszero sNaN123 -> 0 bool2099 iszero -sNaN123 -> 0 -bool2100 is_integer -1.0 -> 1 -bool2101 is_integer 0.0 -> 1 -bool2102 is_integer 1.0 -> 1 -bool2103 is_integer 42 -> 1 -bool2104 is_integer 1e2 -> 1 -bool2105 is_integer 1.5 -> 0 -bool2106 is_integer 1e-2 -> 0 -bool2107 is_integer NaN -> 0 -bool2109 is_integer -NaN -> 0 -bool2110 is_integer NaN123 -> 0 -bool2111 is_integer -NaN123 -> 0 -bool2112 is_integer sNaN -> 0 -bool2113 is_integer -sNaN -> 0 -bool2114 is_integer sNaN123 -> 0 -bool2115 is_integer -sNaN123 -> 0 -bool2116 is_integer Infinity -> 0 -bool2117 is_integer -Infinity -> 0 - ------------------------------------------------------------------------ -- The following tests (pwmx0 through pwmx440) are for the -- diff --git a/Lib/test/test_bool.py b/Lib/test/test_bool.py index bc201e10ff267..7b3a3859e0893 100644 --- a/Lib/test/test_bool.py +++ b/Lib/test/test_bool.py @@ -354,11 +354,6 @@ def test_real_and_imag(self): self.assertIs(type(False.real), int) self.assertIs(type(False.imag), int) - def test_always_is_integer(self): - # Issue #26680: Incorporating number.is_integer into bool - self.assertTrue(all(b.is_integer() for b in (False, True))) - - def test_main(): support.run_unittest(BoolTest) diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py index efb41fd465056..dbd58e8a6519b 100644 --- a/Lib/test/test_decimal.py +++ b/Lib/test/test_decimal.py @@ -276,7 +276,6 @@ def setUp(self): 'is_snan', 'is_subnormal', 'is_zero', - 'is_integer', 'same_quantum') def read_unlimited(self, v, context): @@ -2727,7 +2726,6 @@ def test_named_parameters(self): self.assertRaises(TypeError, D(1).is_snan, context=xc) self.assertRaises(TypeError, D(1).is_signed, context=xc) self.assertRaises(TypeError, D(1).is_zero, context=xc) - self.assertRaises(TypeError, D(1).is_integer, context=xc) self.assertFalse(D("0.01").is_normal(context=xc)) self.assertTrue(D("0.01").is_subnormal(context=xc)) @@ -3199,15 +3197,6 @@ def test_is_zero(self): self.assertEqual(c.is_zero(10), d) self.assertRaises(TypeError, c.is_zero, '10') - def test_is_integer(self): - Decimal = self.decimal.Decimal - Context = self.decimal.Context - - c = Context() - b = c.is_integer(Decimal(10)) - self.assertEqual(c.is_integer(10), b) - self.assertRaises(TypeError, c.is_integer, '10') - def test_ln(self): Decimal = self.decimal.Decimal Context = self.decimal.Context @@ -4371,19 +4360,6 @@ def test_implicit_context(self): self.assertTrue(Decimal("-1").is_signed()) self.assertTrue(Decimal("0").is_zero()) self.assertTrue(Decimal("0").is_zero()) - self.assertTrue(Decimal("-1").is_integer()) - self.assertTrue(Decimal("0").is_integer()) - self.assertTrue(Decimal("1").is_integer()) - self.assertTrue(Decimal("42").is_integer()) - self.assertTrue(Decimal("1e2").is_integer()) - self.assertFalse(Decimal("1.5").is_integer()) - self.assertFalse(Decimal("1e-2").is_integer()) - self.assertFalse(Decimal("NaN").is_integer()) - self.assertFalse(Decimal("-NaN").is_integer()) - self.assertFalse(Decimal("sNaN").is_integer()) - self.assertFalse(Decimal("-sNaN").is_integer()) - self.assertFalse(Decimal("Inf").is_integer()) - self.assertFalse(Decimal("-Inf").is_integer()) # Copy with localcontext() as c: diff --git a/Lib/test/test_fractions.py b/Lib/test/test_fractions.py index 811b58fd8f56a..0845f7921c39e 100644 --- a/Lib/test/test_fractions.py +++ b/Lib/test/test_fractions.py @@ -724,17 +724,6 @@ def denominator(self): self.assertEqual(type(f.numerator), myint) self.assertEqual(type(f.denominator), myint) - def test_is_integer(self): - # Issue #26680: Incorporating number.is_integer into Fraction - self.assertTrue(F(-1, 1).is_integer()) - self.assertTrue(F(0, 1).is_integer()) - self.assertTrue(F(1, 1).is_integer()) - self.assertTrue(F(42, 1).is_integer()) - self.assertTrue(F(2, 2).is_integer()) - self.assertTrue(F(8, 4).is_integer()) - self.assertFalse(F(1, 2).is_integer()) - self.assertFalse(F(1, 3).is_integer()) - self.assertFalse(F(2, 3).is_integer()) if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_long.py b/Lib/test/test_long.py index 669826c0fa3c1..c97842b5bfd23 100644 --- a/Lib/test/test_long.py +++ b/Lib/test/test_long.py @@ -1381,10 +1381,6 @@ class myint(int): self.assertEqual(type(numerator), int) self.assertEqual(type(denominator), int) - def test_int_always_is_integer(self): - # Issue #26680: Incorporating number.is_integer into int - self.assertTrue(all(x.is_integer() for x in (-1, 0, 1, 42))) - if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_numeric_tower.py b/Lib/test/test_numeric_tower.py index 4e46aacad82b6..c54dedb8b793a 100644 --- a/Lib/test/test_numeric_tower.py +++ b/Lib/test/test_numeric_tower.py @@ -6,7 +6,6 @@ import sys import operator -from numbers import Real, Rational, Integral from decimal import Decimal as D from fractions import Fraction as F @@ -199,35 +198,5 @@ def test_complex(self): self.assertRaises(TypeError, op, v, z) -class IsIntegerTest(unittest.TestCase): - - def test_real_is_integer(self): - self.assertTrue(Real.is_integer(-1.0)) - self.assertTrue(Real.is_integer(0.0)) - self.assertTrue(Real.is_integer(1.0)) - self.assertTrue(Real.is_integer(42.0)) - - self.assertFalse(Real.is_integer(-0.5)) - self.assertFalse(Real.is_integer(4.2)) - - def test_rational_is_integer(self): - self.assertTrue(Rational.is_integer(F(-1, 1))) - self.assertTrue(Rational.is_integer(F(0, 1))) - self.assertTrue(Rational.is_integer(F(1, 1))) - self.assertTrue(Rational.is_integer(F(42, 1))) - self.assertTrue(Rational.is_integer(F(2, 2))) - self.assertTrue(Rational.is_integer(F(8, 4))) - - self.assertFalse(Rational.is_integer(F(1, 2))) - self.assertFalse(Rational.is_integer(F(1, 3))) - self.assertFalse(Rational.is_integer(F(2, 3))) - - def test_integral_is_integer(self): - self.assertTrue(Integral.is_integer(-1)) - self.assertTrue(Integral.is_integer(0)) - self.assertTrue(Integral.is_integer(1)) - self.assertTrue(Integral.is_integer(1729)) - - if __name__ == '__main__': unittest.main() diff --git a/Misc/ACKS b/Misc/ACKS index 7d445c5721455..660b8ef7504ee 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1613,7 +1613,6 @@ Roman Skurikhin Ville Skytt? Michael Sloan Nick Sloan -Robert Smallshire V?clav ?milauer Allen W. Smith Christopher Smith diff --git a/Modules/_decimal/_decimal.c b/Modules/_decimal/_decimal.c index 5200b1a48e4bf..e7c44acba02fc 100644 --- a/Modules/_decimal/_decimal.c +++ b/Modules/_decimal/_decimal.c @@ -4138,7 +4138,6 @@ Dec_BoolFunc(mpd_isqnan) Dec_BoolFunc(mpd_issnan) Dec_BoolFunc(mpd_issigned) Dec_BoolFunc(mpd_iszero) -Dec_BoolFunc(mpd_isinteger) /* Boolean functions, optional context arg */ Dec_BoolFuncVA(mpd_isnormal) @@ -4773,7 +4772,6 @@ static PyMethodDef dec_methods [] = { "is_snan", dec_mpd_issnan, METH_NOARGS, doc_is_snan }, { "is_signed", dec_mpd_issigned, METH_NOARGS, doc_is_signed }, { "is_zero", dec_mpd_iszero, METH_NOARGS, doc_is_zero }, - { "is_integer", dec_mpd_isinteger, METH_NOARGS, doc_is_integer}, /* Boolean functions, optional context arg */ { "is_normal", (PyCFunction)(void(*)(void))dec_mpd_isnormal, METH_VARARGS|METH_KEYWORDS, doc_is_normal }, @@ -5185,7 +5183,6 @@ DecCtx_BoolFunc_NO_CTX(mpd_isqnan) DecCtx_BoolFunc_NO_CTX(mpd_issigned) DecCtx_BoolFunc_NO_CTX(mpd_issnan) DecCtx_BoolFunc_NO_CTX(mpd_iszero) -DecCtx_BoolFunc_NO_CTX(mpd_isinteger) static PyObject * ctx_iscanonical(PyObject *context UNUSED, PyObject *v) @@ -5467,7 +5464,6 @@ static PyMethodDef context_methods [] = { "is_snan", ctx_mpd_issnan, METH_O, doc_ctx_is_snan }, { "is_subnormal", ctx_mpd_issubnormal, METH_O, doc_ctx_is_subnormal }, { "is_zero", ctx_mpd_iszero, METH_O, doc_ctx_is_zero }, - { "is_integer", ctx_mpd_isinteger, METH_O, doc_ctx_is_integer }, /* Functions with a single decimal argument */ { "_apply", PyDecContext_Apply, METH_O, NULL }, /* alias for apply */ @@ -6101,3 +6097,5 @@ PyInit__decimal(void) return NULL; /* GCOV_NOT_REACHED */ } + + diff --git a/Modules/_decimal/docstrings.h b/Modules/_decimal/docstrings.h index bd602ab278e0e..f7fd6e7952998 100644 --- a/Modules/_decimal/docstrings.h +++ b/Modules/_decimal/docstrings.h @@ -260,11 +260,6 @@ Return True if the argument is a (positive or negative) zero and False\n\ otherwise.\n\ \n"); -PyDoc_STRVAR(doc_is_integer, -"is_integer($self, /)\n--\n\n\ -Return True if the argument is finite and integral, otherwise False.\n\ -\n"); - PyDoc_STRVAR(doc_ln, "ln($self, /, context=None)\n--\n\n\ Return the natural (base e) logarithm of the operand. The function always\n\ @@ -690,11 +685,6 @@ PyDoc_STRVAR(doc_ctx_is_zero, Return True if x is a zero, False otherwise.\n\ \n"); -PyDoc_STRVAR(doc_ctx_is_integer, -"is_integer($self, x, /)\n--\n\n\ -+Return True if x is finite and integral, False otherwise.\n\ -+\n"); - PyDoc_STRVAR(doc_ctx_ln, "ln($self, x, /)\n--\n\n\ Return the natural (base e) logarithm of x.\n\ @@ -889,3 +879,6 @@ Convert a number to a string using scientific notation.\n\ #endif /* DOCSTRINGS_H */ + + + diff --git a/Objects/clinic/longobject.c.h b/Objects/clinic/longobject.c.h index 16e6f7e619e87..4bd47b116f883 100644 --- a/Objects/clinic/longobject.c.h +++ b/Objects/clinic/longobject.c.h @@ -121,24 +121,6 @@ int___round__(PyObject *self, PyObject *const *args, Py_ssize_t nargs) return return_value; } -PyDoc_STRVAR(int_is_integer__doc__, -"is_integer($self, /)\n" -"--\n" -"\n" -"Returns True for all integers."); - -#define INT_IS_INTEGER_METHODDEF \ - {"is_integer", (PyCFunction)int_is_integer, METH_NOARGS, int_is_integer__doc__}, - -static PyObject * -int_is_integer_impl(PyObject *self); - -static PyObject * -int_is_integer(PyObject *self, PyObject *Py_UNUSED(ignored)) -{ - return int_is_integer_impl(self); -} - PyDoc_STRVAR(int___sizeof____doc__, "__sizeof__($self, /)\n" "--\n" @@ -385,4 +367,4 @@ int_from_bytes(PyTypeObject *type, PyObject *const *args, Py_ssize_t nargs, PyOb exit: return return_value; } -/*[clinic end generated code: output=022614978e2fcdf3 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=ea18e51af5b53591 input=a9049054013a1b77]*/ diff --git a/Objects/longobject.c b/Objects/longobject.c index bc5b49dcf8b56..92514d4154e2c 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -5233,19 +5233,6 @@ int___round___impl(PyObject *self, PyObject *o_ndigits) return result; } -/*[clinic input] -int.is_integer - -Returns True for all integers. -[clinic start generated code]*/ - -static PyObject * -int_is_integer_impl(PyObject *self) -/*[clinic end generated code: output=90f8e794ce5430ef input=1c1a86957301d26d]*/ -{ - Py_RETURN_TRUE; -} - /*[clinic input] int.__sizeof__ -> Py_ssize_t @@ -5560,7 +5547,6 @@ static PyMethodDef long_methods[] = { {"__ceil__", long_long_meth, METH_NOARGS, "Ceiling of an Integral returns itself."}, INT___ROUND___METHODDEF - INT_IS_INTEGER_METHODDEF INT___GETNEWARGS___METHODDEF INT___FORMAT___METHODDEF INT___SIZEOF___METHODDEF From webhook-mailer at python.org Thu Oct 8 02:38:06 2020 From: webhook-mailer at python.org (Phil Elson) Date: Thu, 08 Oct 2020 06:38:06 -0000 Subject: [Python-checkins] bpo-41376: Fix the documentation of `site.getusersitepackages()` (GH-21602) Message-ID: https://github.com/python/cpython/commit/35f041dd0171f575fc3adce1709b31fdf45a5ff6 commit: 35f041dd0171f575fc3adce1709b31fdf45a5ff6 branch: master author: Phil Elson committer: GitHub date: 2020-10-08T15:37:46+09:00 summary: bpo-41376: Fix the documentation of `site.getusersitepackages()` (GH-21602) `site.getusersitepackages()` returns the location of the user-specific site-packages directory even when the user-specific site-packages is disabled. ``` $ python -s -m site sys.path = [ '/home/user/conda/lib/python37.zip', '/home/user/conda/lib/python3.7', '/home/user/conda/lib/python3.7/lib-dynload', '/home/user/conda/lib/python3.7/site-packages', ] USER_BASE: '/home/user/.local' (exists) USER_SITE: '/home/user/.local/lib/python3.7/site-packages' (doesn't exist) ENABLE_USER_SITE: False ``` It was not practical to prevent the function from returning None if user-specific site-packages are disabled, since there are other uses of the function which are relying on this behaviour (e.g. `python -m site`). files: M Doc/library/site.rst diff --git a/Doc/library/site.rst b/Doc/library/site.rst index b424e1ba348d8..2e3646f6a74f8 100644 --- a/Doc/library/site.rst +++ b/Doc/library/site.rst @@ -231,7 +231,9 @@ Module contents Return the path of the user-specific site-packages directory, :data:`USER_SITE`. If it is not initialized yet, this function will also set - it, respecting :envvar:`PYTHONNOUSERSITE` and :data:`USER_BASE`. + it, respecting :data:`USER_BASE`. To determine if the user-specific + site-packages was added to ``sys.path`` :data:`ENABLE_USER_SITE` should be + used. .. versionadded:: 3.2 From webhook-mailer at python.org Thu Oct 8 03:00:39 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Thu, 08 Oct 2020 07:00:39 -0000 Subject: [Python-checkins] bpo-41376: Fix the documentation of `site.getusersitepackages()` (GH-21602) Message-ID: https://github.com/python/cpython/commit/5b1fdcacfc36dfe36693f4c350c958df6600de14 commit: 5b1fdcacfc36dfe36693f4c350c958df6600de14 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-08T00:00:28-07:00 summary: bpo-41376: Fix the documentation of `site.getusersitepackages()` (GH-21602) `site.getusersitepackages()` returns the location of the user-specific site-packages directory even when the user-specific site-packages is disabled. ``` $ python -s -m site sys.path = [ '/home/user/conda/lib/python37.zip', '/home/user/conda/lib/python3.7', '/home/user/conda/lib/python3.7/lib-dynload', '/home/user/conda/lib/python3.7/site-packages', ] USER_BASE: '/home/user/.local' (exists) USER_SITE: '/home/user/.local/lib/python3.7/site-packages' (doesn't exist) ENABLE_USER_SITE: False ``` It was not practical to prevent the function from returning None if user-specific site-packages are disabled, since there are other uses of the function which are relying on this behaviour (e.g. `python -m site`). (cherry picked from commit 35f041dd0171f575fc3adce1709b31fdf45a5ff6) Co-authored-by: Phil Elson files: M Doc/library/site.rst diff --git a/Doc/library/site.rst b/Doc/library/site.rst index b424e1ba348d8..2e3646f6a74f8 100644 --- a/Doc/library/site.rst +++ b/Doc/library/site.rst @@ -231,7 +231,9 @@ Module contents Return the path of the user-specific site-packages directory, :data:`USER_SITE`. If it is not initialized yet, this function will also set - it, respecting :envvar:`PYTHONNOUSERSITE` and :data:`USER_BASE`. + it, respecting :data:`USER_BASE`. To determine if the user-specific + site-packages was added to ``sys.path`` :data:`ENABLE_USER_SITE` should be + used. .. versionadded:: 3.2 From webhook-mailer at python.org Thu Oct 8 09:24:38 2020 From: webhook-mailer at python.org (Pablo Galindo) Date: Thu, 08 Oct 2020 13:24:38 -0000 Subject: [Python-checkins] bpo-41970: Avoid test failure in test_lib2to3 if the module is already imported (GH-22595) Message-ID: https://github.com/python/cpython/commit/4a9f82f50d957b6cf3fd207de8b583d9137316b8 commit: 4a9f82f50d957b6cf3fd207de8b583d9137316b8 branch: master author: Pablo Galindo committer: GitHub date: 2020-10-08T06:24:28-07:00 summary: bpo-41970: Avoid test failure in test_lib2to3 if the module is already imported (GH-22595) ? Automerge-Triggered-By: @pablogsal files: A Misc/NEWS.d/next/Tests/2020-10-08-14-00-17.bpo-41970.aZ8QFf.rst M Lib/test/test_lib2to3.py diff --git a/Lib/test/test_lib2to3.py b/Lib/test/test_lib2to3.py index 159a8387e4e97..fd12a7e7acbb4 100644 --- a/Lib/test/test_lib2to3.py +++ b/Lib/test/test_lib2to3.py @@ -1,8 +1,9 @@ import unittest +from test.support.import_helper import import_fresh_module from test.support.warnings_helper import check_warnings with check_warnings(("", PendingDeprecationWarning)): - from lib2to3.tests import load_tests + load_tests = import_fresh_module('lib2to3.tests', fresh=['lib2to3']).load_tests if __name__ == '__main__': unittest.main() diff --git a/Misc/NEWS.d/next/Tests/2020-10-08-14-00-17.bpo-41970.aZ8QFf.rst b/Misc/NEWS.d/next/Tests/2020-10-08-14-00-17.bpo-41970.aZ8QFf.rst new file mode 100644 index 0000000000000..4cdca197fbfc6 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2020-10-08-14-00-17.bpo-41970.aZ8QFf.rst @@ -0,0 +1,2 @@ +Avoid a test failure in ``test_lib2to3`` if the module has already imported +at the time the test executes. Patch by Pablo Galindo. From webhook-mailer at python.org Thu Oct 8 09:30:25 2020 From: webhook-mailer at python.org (E-Paine) Date: Thu, 08 Oct 2020 13:30:25 -0000 Subject: [Python-checkins] bpo-41306: Allow scale value to not be rounded (GH-21715) Message-ID: https://github.com/python/cpython/commit/aecf036738a404371303e770f4ce4fd9f7d43de7 commit: aecf036738a404371303e770f4ce4fd9f7d43de7 branch: master author: E-Paine <63801254+E-Paine at users.noreply.github.com> committer: GitHub date: 2020-10-08T06:30:13-07:00 summary: bpo-41306: Allow scale value to not be rounded (GH-21715) This fixes the test failure with Tk 6.8.10 which is caused by changes to how Tk rounds the `from`, `to` and `tickinterval` arguments. This PR uses `noconv` if the patchlevel is greater than or equal to 8.6.10 (credit to Serhiy for this idea as it is much simpler than what I previously proposed). Going into more detail for those who want it, the Tk change was made in [commit 591f68c](https://github.com/tcltk/tk/commit/591f68cb382525b72664c6fecaab87742b6cc87a) and means that the arguments listed above are rounded relative to the value of `from`. However, when rounding the `from` argument ([line 623](https://github.com/tcltk/tk/blob/591f68cb382525b72664c6fecaab87742b6cc87a/generic/tkScale.c#L623)), it is rounded relative to itself (i.e. rounding `0`) and therefore the assigned value for `from` is always what is given (no matter what values of `from` and `resolution`). Automerge-Triggered-By: @pablogsal files: A Misc/NEWS.d/next/Tests/2020-08-03-13-44-37.bpo-41306.VDoWXI.rst M Lib/tkinter/test/test_tkinter/test_widgets.py diff --git a/Lib/tkinter/test/test_tkinter/test_widgets.py b/Lib/tkinter/test/test_tkinter/test_widgets.py index 721e81369a8d5..b6f792d6c2cf8 100644 --- a/Lib/tkinter/test/test_tkinter/test_widgets.py +++ b/Lib/tkinter/test/test_tkinter/test_widgets.py @@ -940,7 +940,8 @@ def test_digits(self): def test_from(self): widget = self.create() - self.checkFloatParam(widget, 'from', 100, 14.9, 15.1, conv=float_round) + 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): widget = self.create() diff --git a/Misc/NEWS.d/next/Tests/2020-08-03-13-44-37.bpo-41306.VDoWXI.rst b/Misc/NEWS.d/next/Tests/2020-08-03-13-44-37.bpo-41306.VDoWXI.rst new file mode 100644 index 0000000000000..5e9ba2d8a2741 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2020-08-03-13-44-37.bpo-41306.VDoWXI.rst @@ -0,0 +1 @@ +Fixed a failure in ``test_tk.test_widgets.ScaleTest`` happening when executing the test with Tk 8.6.10. From webhook-mailer at python.org Thu Oct 8 12:15:03 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Thu, 08 Oct 2020 16:15:03 -0000 Subject: [Python-checkins] bpo-41306: Allow scale value to not be rounded (GH-21715) Message-ID: https://github.com/python/cpython/commit/3d103e06951324680c587d4fe1135f06367c9d3d commit: 3d103e06951324680c587d4fe1135f06367c9d3d branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-08T09:14:33-07:00 summary: bpo-41306: Allow scale value to not be rounded (GH-21715) This fixes the test failure with Tk 6.8.10 which is caused by changes to how Tk rounds the `from`, `to` and `tickinterval` arguments. This PR uses `noconv` if the patchlevel is greater than or equal to 8.6.10 (credit to Serhiy for this idea as it is much simpler than what I previously proposed). Going into more detail for those who want it, the Tk change was made in [commit 591f68c](https://github.com/tcltk/tk/commit/591f68cb382525b72664c6fecaab87742b6cc87a) and means that the arguments listed above are rounded relative to the value of `from`. However, when rounding the `from` argument ([line 623](https://github.com/tcltk/tk/blob/591f68cb382525b72664c6fecaab87742b6cc87a/generic/tkScale.cGH-L623)), it is rounded relative to itself (i.e. rounding `0`) and therefore the assigned value for `from` is always what is given (no matter what values of `from` and `resolution`). Automerge-Triggered-By: @pablogsal (cherry picked from commit aecf036738a404371303e770f4ce4fd9f7d43de7) Co-authored-by: E-Paine <63801254+E-Paine at users.noreply.github.com> files: A Misc/NEWS.d/next/Tests/2020-08-03-13-44-37.bpo-41306.VDoWXI.rst M Lib/tkinter/test/test_tkinter/test_widgets.py diff --git a/Lib/tkinter/test/test_tkinter/test_widgets.py b/Lib/tkinter/test/test_tkinter/test_widgets.py index 721e81369a8d5..b6f792d6c2cf8 100644 --- a/Lib/tkinter/test/test_tkinter/test_widgets.py +++ b/Lib/tkinter/test/test_tkinter/test_widgets.py @@ -940,7 +940,8 @@ def test_digits(self): def test_from(self): widget = self.create() - self.checkFloatParam(widget, 'from', 100, 14.9, 15.1, conv=float_round) + 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): widget = self.create() diff --git a/Misc/NEWS.d/next/Tests/2020-08-03-13-44-37.bpo-41306.VDoWXI.rst b/Misc/NEWS.d/next/Tests/2020-08-03-13-44-37.bpo-41306.VDoWXI.rst new file mode 100644 index 0000000000000..5e9ba2d8a2741 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2020-08-03-13-44-37.bpo-41306.VDoWXI.rst @@ -0,0 +1 @@ +Fixed a failure in ``test_tk.test_widgets.ScaleTest`` happening when executing the test with Tk 8.6.10. From webhook-mailer at python.org Thu Oct 8 12:51:02 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Thu, 08 Oct 2020 16:51:02 -0000 Subject: [Python-checkins] bpo-41306: Allow scale value to not be rounded (GH-21715) Message-ID: https://github.com/python/cpython/commit/15e091f63f12b659ad17563a4ce7f5188aceae0a commit: 15e091f63f12b659ad17563a4ce7f5188aceae0a branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-08T09:50:36-07:00 summary: bpo-41306: Allow scale value to not be rounded (GH-21715) This fixes the test failure with Tk 6.8.10 which is caused by changes to how Tk rounds the `from`, `to` and `tickinterval` arguments. This PR uses `noconv` if the patchlevel is greater than or equal to 8.6.10 (credit to Serhiy for this idea as it is much simpler than what I previously proposed). Going into more detail for those who want it, the Tk change was made in [commit 591f68c](https://github.com/tcltk/tk/commit/591f68cb382525b72664c6fecaab87742b6cc87a) and means that the arguments listed above are rounded relative to the value of `from`. However, when rounding the `from` argument ([line 623](https://github.com/tcltk/tk/blob/591f68cb382525b72664c6fecaab87742b6cc87a/generic/tkScale.cGH-L623)), it is rounded relative to itself (i.e. rounding `0`) and therefore the assigned value for `from` is always what is given (no matter what values of `from` and `resolution`). Automerge-Triggered-By: @pablogsal (cherry picked from commit aecf036738a404371303e770f4ce4fd9f7d43de7) Co-authored-by: E-Paine <63801254+E-Paine at users.noreply.github.com> files: A Misc/NEWS.d/next/Tests/2020-08-03-13-44-37.bpo-41306.VDoWXI.rst M Lib/tkinter/test/test_tkinter/test_widgets.py diff --git a/Lib/tkinter/test/test_tkinter/test_widgets.py b/Lib/tkinter/test/test_tkinter/test_widgets.py index 721e81369a8d5..b6f792d6c2cf8 100644 --- a/Lib/tkinter/test/test_tkinter/test_widgets.py +++ b/Lib/tkinter/test/test_tkinter/test_widgets.py @@ -940,7 +940,8 @@ def test_digits(self): def test_from(self): widget = self.create() - self.checkFloatParam(widget, 'from', 100, 14.9, 15.1, conv=float_round) + 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): widget = self.create() diff --git a/Misc/NEWS.d/next/Tests/2020-08-03-13-44-37.bpo-41306.VDoWXI.rst b/Misc/NEWS.d/next/Tests/2020-08-03-13-44-37.bpo-41306.VDoWXI.rst new file mode 100644 index 0000000000000..5e9ba2d8a2741 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2020-08-03-13-44-37.bpo-41306.VDoWXI.rst @@ -0,0 +1 @@ +Fixed a failure in ``test_tk.test_widgets.ScaleTest`` happening when executing the test with Tk 8.6.10. From webhook-mailer at python.org Thu Oct 8 14:31:26 2020 From: webhook-mailer at python.org (Pablo Galindo) Date: Thu, 08 Oct 2020 18:31:26 -0000 Subject: [Python-checkins] bpo-41976: Fix the fallback to gcc of ctypes.util.find_library when using gcc>9 (GH-22598) Message-ID: https://github.com/python/cpython/commit/27ac19cca2c639caaf6fedf3632fe6beb265f24f commit: 27ac19cca2c639caaf6fedf3632fe6beb265f24f branch: master author: Pablo Galindo committer: GitHub date: 2020-10-08T19:31:19+01:00 summary: bpo-41976: Fix the fallback to gcc of ctypes.util.find_library when using gcc>9 (GH-22598) files: A Misc/NEWS.d/next/Library/2020-10-08-18-22-28.bpo-41976.Svm0wb.rst M Lib/ctypes/test/test_find.py M Lib/ctypes/util.py diff --git a/Lib/ctypes/test/test_find.py b/Lib/ctypes/test/test_find.py index bfb6b42cbb227..4a8a3820f3fe1 100644 --- a/Lib/ctypes/test/test_find.py +++ b/Lib/ctypes/test/test_find.py @@ -1,4 +1,5 @@ import unittest +import unittest.mock import os.path import sys import test.support @@ -73,7 +74,7 @@ def test_shell_injection(self): @unittest.skipUnless(sys.platform.startswith('linux'), 'Test only valid for Linux') -class LibPathFindTest(unittest.TestCase): +class FindLibraryLinux(unittest.TestCase): def test_find_on_libpath(self): import subprocess import tempfile @@ -112,6 +113,15 @@ def test_find_on_libpath(self): # LD_LIBRARY_PATH) self.assertEqual(find_library(libname), 'lib%s.so' % libname) + def test_find_library_with_gcc(self): + with unittest.mock.patch("ctypes.util._findSoname_ldconfig", lambda *args: None): + self.assertNotEqual(find_library('c'), None) + + def test_find_library_with_ld(self): + with unittest.mock.patch("ctypes.util._findSoname_ldconfig", lambda *args: None), \ + unittest.mock.patch("ctypes.util._findLib_gcc", lambda *args: None): + self.assertNotEqual(find_library('c'), None) + if __name__ == "__main__": unittest.main() diff --git a/Lib/ctypes/util.py b/Lib/ctypes/util.py index 01176bf969657..0c2510e1619c8 100644 --- a/Lib/ctypes/util.py +++ b/Lib/ctypes/util.py @@ -93,6 +93,12 @@ def find_library(name): # Andreas Degert's find functions, using gcc, /sbin/ldconfig, objdump import re, tempfile + def _is_elf(filename): + "Return True if the given file is an ELF file" + elf_header = b'\x7fELF' + with open(filename, 'br') as thefile: + return thefile.read(4) == elf_header + def _findLib_gcc(name): # Run GCC's linker with the -t (aka --trace) option and examine the # library name it prints out. The GCC command will fail because we @@ -130,10 +136,17 @@ def _findLib_gcc(name): # Raised if the file was already removed, which is the normal # behaviour of GCC if linking fails pass - res = re.search(expr, trace) + res = re.findall(expr, trace) if not res: return None - return os.fsdecode(res.group(0)) + + for file in res: + # Check if the given file is an elf file: gcc can report + # some files that are linker scripts and not actual + # shared objects. See bpo-41976 for more details + if not _is_elf(file): + continue + return os.fsdecode(file) if sys.platform == "sunos5": @@ -299,9 +312,14 @@ def _findLib_ld(name): stderr=subprocess.PIPE, universal_newlines=True) out, _ = p.communicate() - res = re.search(expr, os.fsdecode(out)) - if res: - result = res.group(0) + res = re.findall(expr, os.fsdecode(out)) + for file in res: + # Check if the given file is an elf file: gcc can report + # some files that are linker scripts and not actual + # shared objects. See bpo-41976 for more details + if not _is_elf(file): + continue + return os.fsdecode(file) except Exception: pass # result will be None return result @@ -309,7 +327,7 @@ def _findLib_ld(name): def find_library(name): # See issue #9998 return _findSoname_ldconfig(name) or \ - _get_soname(_findLib_gcc(name) or _findLib_ld(name)) + _get_soname(_findLib_gcc(name)) or _get_soname(_findLib_ld(name)) ################################################################ # test code diff --git a/Misc/NEWS.d/next/Library/2020-10-08-18-22-28.bpo-41976.Svm0wb.rst b/Misc/NEWS.d/next/Library/2020-10-08-18-22-28.bpo-41976.Svm0wb.rst new file mode 100644 index 0000000000000..c8b3fc771845e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-10-08-18-22-28.bpo-41976.Svm0wb.rst @@ -0,0 +1,3 @@ +Fixed a bug that was causing :func:`ctypes.util.find_library` to return +``None`` when triying to locate a library in an environment when gcc>=9 is +available and ``ldconfig`` is not. Patch by Pablo Galindo From webhook-mailer at python.org Thu Oct 8 14:40:34 2020 From: webhook-mailer at python.org (Erlend Egeberg Aasland) Date: Thu, 08 Oct 2020 18:40:34 -0000 Subject: [Python-checkins] bpo-41557: Update Windows installer to use SQLite 3.33.0 (GH-21960) Message-ID: https://github.com/python/cpython/commit/bfe6e03cd6931813dd61b50f5fdf7d8a8848f4cd commit: bfe6e03cd6931813dd61b50f5fdf7d8a8848f4cd branch: master author: Erlend Egeberg Aasland committer: GitHub date: 2020-10-08T19:40:27+01:00 summary: bpo-41557: Update Windows installer to use SQLite 3.33.0 (GH-21960) files: A Misc/NEWS.d/next/Windows/2020-08-26-09-35-06.bpo-41557.vt00cQ.rst M PCbuild/get_externals.bat M PCbuild/python.props M PCbuild/readme.txt diff --git a/Misc/NEWS.d/next/Windows/2020-08-26-09-35-06.bpo-41557.vt00cQ.rst b/Misc/NEWS.d/next/Windows/2020-08-26-09-35-06.bpo-41557.vt00cQ.rst new file mode 100644 index 0000000000000..9d85461f00923 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2020-08-26-09-35-06.bpo-41557.vt00cQ.rst @@ -0,0 +1 @@ +Update Windows installer to use SQLite 3.33.0. diff --git a/PCbuild/get_externals.bat b/PCbuild/get_externals.bat index a48b59cad2ed1..3bb281904f6a6 100644 --- a/PCbuild/get_externals.bat +++ b/PCbuild/get_externals.bat @@ -54,7 +54,7 @@ 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.32.3.0 +set libraries=%libraries% sqlite-3.33.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 diff --git a/PCbuild/python.props b/PCbuild/python.props index 5f4926efa2517..acc41a2c01768 100644 --- a/PCbuild/python.props +++ b/PCbuild/python.props @@ -56,7 +56,7 @@ $(EXTERNALS_DIR) $([System.IO.Path]::GetFullPath(`$(PySourcePath)externals`)) $(ExternalsDir)\ - $(ExternalsDir)sqlite-3.32.3.0\ + $(ExternalsDir)sqlite-3.33.0.0\ $(ExternalsDir)bzip2-1.0.6\ $(ExternalsDir)xz-5.2.2\ $(ExternalsDir)libffi\ diff --git a/PCbuild/readme.txt b/PCbuild/readme.txt index b8849757e893a..73833d54637d5 100644 --- a/PCbuild/readme.txt +++ b/PCbuild/readme.txt @@ -185,7 +185,7 @@ _ssl again when building. _sqlite3 - Wraps SQLite 3.32.3.0, which is itself built by sqlite3.vcxproj + Wraps SQLite 3.33.0, which is itself built by sqlite3.vcxproj Homepage: http://www.sqlite.org/ _tkinter From webhook-mailer at python.org Thu Oct 8 14:50:38 2020 From: webhook-mailer at python.org (Pablo Galindo) Date: Thu, 08 Oct 2020 18:50:38 -0000 Subject: [Python-checkins] [3.8] bpo-41976: Fix the fallback to gcc of ctypes.util.find_library when using gcc>9 (GH-22598). (GH-22600) Message-ID: https://github.com/python/cpython/commit/6ceb5232aeb1943dacbbdb87e7dddfaa716678bc commit: 6ceb5232aeb1943dacbbdb87e7dddfaa716678bc branch: 3.8 author: Pablo Galindo committer: GitHub date: 2020-10-08T19:50:28+01:00 summary: [3.8] bpo-41976: Fix the fallback to gcc of ctypes.util.find_library when using gcc>9 (GH-22598). (GH-22600) (cherry picked from commit 27ac19cca2c639caaf6fedf3632fe6beb265f24f) Co-authored-by: Pablo Galindo files: A Misc/NEWS.d/next/Library/2020-10-08-18-22-28.bpo-41976.Svm0wb.rst M Lib/ctypes/test/test_find.py M Lib/ctypes/util.py diff --git a/Lib/ctypes/test/test_find.py b/Lib/ctypes/test/test_find.py index b99fdcba7b28f..92ac1840ad7d4 100644 --- a/Lib/ctypes/test/test_find.py +++ b/Lib/ctypes/test/test_find.py @@ -1,4 +1,5 @@ import unittest +import unittest.mock import os.path import sys import test.support @@ -72,7 +73,7 @@ def test_shell_injection(self): @unittest.skipUnless(sys.platform.startswith('linux'), 'Test only valid for Linux') -class LibPathFindTest(unittest.TestCase): +class FindLibraryLinux(unittest.TestCase): def test_find_on_libpath(self): import subprocess import tempfile @@ -111,6 +112,15 @@ def test_find_on_libpath(self): # LD_LIBRARY_PATH) self.assertEqual(find_library(libname), 'lib%s.so' % libname) + def test_find_library_with_gcc(self): + with unittest.mock.patch("ctypes.util._findSoname_ldconfig", lambda *args: None): + self.assertNotEqual(find_library('c'), None) + + def test_find_library_with_ld(self): + with unittest.mock.patch("ctypes.util._findSoname_ldconfig", lambda *args: None), \ + unittest.mock.patch("ctypes.util._findLib_gcc", lambda *args: None): + self.assertNotEqual(find_library('c'), None) + if __name__ == "__main__": unittest.main() diff --git a/Lib/ctypes/util.py b/Lib/ctypes/util.py index 97973bce001d9..0c2510e1619c8 100644 --- a/Lib/ctypes/util.py +++ b/Lib/ctypes/util.py @@ -93,6 +93,12 @@ def find_library(name): # Andreas Degert's find functions, using gcc, /sbin/ldconfig, objdump import re, tempfile + def _is_elf(filename): + "Return True if the given file is an ELF file" + elf_header = b'\x7fELF' + with open(filename, 'br') as thefile: + return thefile.read(4) == elf_header + def _findLib_gcc(name): # Run GCC's linker with the -t (aka --trace) option and examine the # library name it prints out. The GCC command will fail because we @@ -130,10 +136,17 @@ def _findLib_gcc(name): # Raised if the file was already removed, which is the normal # behaviour of GCC if linking fails pass - res = re.search(expr, trace) + res = re.findall(expr, trace) if not res: return None - return os.fsdecode(res.group(0)) + + for file in res: + # Check if the given file is an elf file: gcc can report + # some files that are linker scripts and not actual + # shared objects. See bpo-41976 for more details + if not _is_elf(file): + continue + return os.fsdecode(file) if sys.platform == "sunos5": @@ -299,17 +312,22 @@ def _findLib_ld(name): stderr=subprocess.PIPE, universal_newlines=True) out, _ = p.communicate() - res = re.search(expr, os.fsdecode(out)) - if res: - result = res.group(0) - except Exception as e: + res = re.findall(expr, os.fsdecode(out)) + for file in res: + # Check if the given file is an elf file: gcc can report + # some files that are linker scripts and not actual + # shared objects. See bpo-41976 for more details + if not _is_elf(file): + continue + return os.fsdecode(file) + except Exception: pass # result will be None return result def find_library(name): # See issue #9998 return _findSoname_ldconfig(name) or \ - _get_soname(_findLib_gcc(name) or _findLib_ld(name)) + _get_soname(_findLib_gcc(name)) or _get_soname(_findLib_ld(name)) ################################################################ # test code diff --git a/Misc/NEWS.d/next/Library/2020-10-08-18-22-28.bpo-41976.Svm0wb.rst b/Misc/NEWS.d/next/Library/2020-10-08-18-22-28.bpo-41976.Svm0wb.rst new file mode 100644 index 0000000000000..c8b3fc771845e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-10-08-18-22-28.bpo-41976.Svm0wb.rst @@ -0,0 +1,3 @@ +Fixed a bug that was causing :func:`ctypes.util.find_library` to return +``None`` when triying to locate a library in an environment when gcc>=9 is +available and ``ldconfig`` is not. Patch by Pablo Galindo From webhook-mailer at python.org Thu Oct 8 14:50:42 2020 From: webhook-mailer at python.org (Pablo Galindo) Date: Thu, 08 Oct 2020 18:50:42 -0000 Subject: [Python-checkins] [3.7] bpo-41976: Fix the fallback to gcc of ctypes.util.find_library when using gcc>9 (GH-22598). (GH-22601) Message-ID: https://github.com/python/cpython/commit/9b5a023a5dc3127da15253f7acad71019395ebe1 commit: 9b5a023a5dc3127da15253f7acad71019395ebe1 branch: 3.7 author: Pablo Galindo committer: GitHub date: 2020-10-08T19:50:37+01:00 summary: [3.7] bpo-41976: Fix the fallback to gcc of ctypes.util.find_library when using gcc>9 (GH-22598). (GH-22601) (cherry picked from commit 27ac19cca2c639caaf6fedf3632fe6beb265f24f) Co-authored-by: Pablo Galindo files: A Misc/NEWS.d/next/Library/2020-10-08-18-22-28.bpo-41976.Svm0wb.rst M Lib/ctypes/test/test_find.py M Lib/ctypes/util.py diff --git a/Lib/ctypes/test/test_find.py b/Lib/ctypes/test/test_find.py index b99fdcba7b28f..92ac1840ad7d4 100644 --- a/Lib/ctypes/test/test_find.py +++ b/Lib/ctypes/test/test_find.py @@ -1,4 +1,5 @@ import unittest +import unittest.mock import os.path import sys import test.support @@ -72,7 +73,7 @@ def test_shell_injection(self): @unittest.skipUnless(sys.platform.startswith('linux'), 'Test only valid for Linux') -class LibPathFindTest(unittest.TestCase): +class FindLibraryLinux(unittest.TestCase): def test_find_on_libpath(self): import subprocess import tempfile @@ -111,6 +112,15 @@ def test_find_on_libpath(self): # LD_LIBRARY_PATH) self.assertEqual(find_library(libname), 'lib%s.so' % libname) + def test_find_library_with_gcc(self): + with unittest.mock.patch("ctypes.util._findSoname_ldconfig", lambda *args: None): + self.assertNotEqual(find_library('c'), None) + + def test_find_library_with_ld(self): + with unittest.mock.patch("ctypes.util._findSoname_ldconfig", lambda *args: None), \ + unittest.mock.patch("ctypes.util._findLib_gcc", lambda *args: None): + self.assertNotEqual(find_library('c'), None) + if __name__ == "__main__": unittest.main() diff --git a/Lib/ctypes/util.py b/Lib/ctypes/util.py index 97973bce001d9..0c2510e1619c8 100644 --- a/Lib/ctypes/util.py +++ b/Lib/ctypes/util.py @@ -93,6 +93,12 @@ def find_library(name): # Andreas Degert's find functions, using gcc, /sbin/ldconfig, objdump import re, tempfile + def _is_elf(filename): + "Return True if the given file is an ELF file" + elf_header = b'\x7fELF' + with open(filename, 'br') as thefile: + return thefile.read(4) == elf_header + def _findLib_gcc(name): # Run GCC's linker with the -t (aka --trace) option and examine the # library name it prints out. The GCC command will fail because we @@ -130,10 +136,17 @@ def _findLib_gcc(name): # Raised if the file was already removed, which is the normal # behaviour of GCC if linking fails pass - res = re.search(expr, trace) + res = re.findall(expr, trace) if not res: return None - return os.fsdecode(res.group(0)) + + for file in res: + # Check if the given file is an elf file: gcc can report + # some files that are linker scripts and not actual + # shared objects. See bpo-41976 for more details + if not _is_elf(file): + continue + return os.fsdecode(file) if sys.platform == "sunos5": @@ -299,17 +312,22 @@ def _findLib_ld(name): stderr=subprocess.PIPE, universal_newlines=True) out, _ = p.communicate() - res = re.search(expr, os.fsdecode(out)) - if res: - result = res.group(0) - except Exception as e: + res = re.findall(expr, os.fsdecode(out)) + for file in res: + # Check if the given file is an elf file: gcc can report + # some files that are linker scripts and not actual + # shared objects. See bpo-41976 for more details + if not _is_elf(file): + continue + return os.fsdecode(file) + except Exception: pass # result will be None return result def find_library(name): # See issue #9998 return _findSoname_ldconfig(name) or \ - _get_soname(_findLib_gcc(name) or _findLib_ld(name)) + _get_soname(_findLib_gcc(name)) or _get_soname(_findLib_ld(name)) ################################################################ # test code diff --git a/Misc/NEWS.d/next/Library/2020-10-08-18-22-28.bpo-41976.Svm0wb.rst b/Misc/NEWS.d/next/Library/2020-10-08-18-22-28.bpo-41976.Svm0wb.rst new file mode 100644 index 0000000000000..c8b3fc771845e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-10-08-18-22-28.bpo-41976.Svm0wb.rst @@ -0,0 +1,3 @@ +Fixed a bug that was causing :func:`ctypes.util.find_library` to return +``None`` when triying to locate a library in an environment when gcc>=9 is +available and ``ldconfig`` is not. Patch by Pablo Galindo From webhook-mailer at python.org Thu Oct 8 14:55:33 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Thu, 08 Oct 2020 18:55:33 -0000 Subject: [Python-checkins] bpo-41976: Fix the fallback to gcc of ctypes.util.find_library when using gcc>9 (GH-22598) Message-ID: https://github.com/python/cpython/commit/a4ac5fadf55a9743c6812dea8d31ca4f05f33829 commit: a4ac5fadf55a9743c6812dea8d31ca4f05f33829 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-08T11:55:24-07:00 summary: bpo-41976: Fix the fallback to gcc of ctypes.util.find_library when using gcc>9 (GH-22598) (cherry picked from commit 27ac19cca2c639caaf6fedf3632fe6beb265f24f) Co-authored-by: Pablo Galindo files: A Misc/NEWS.d/next/Library/2020-10-08-18-22-28.bpo-41976.Svm0wb.rst M Lib/ctypes/test/test_find.py M Lib/ctypes/util.py diff --git a/Lib/ctypes/test/test_find.py b/Lib/ctypes/test/test_find.py index b99fdcba7b28f..92ac1840ad7d4 100644 --- a/Lib/ctypes/test/test_find.py +++ b/Lib/ctypes/test/test_find.py @@ -1,4 +1,5 @@ import unittest +import unittest.mock import os.path import sys import test.support @@ -72,7 +73,7 @@ def test_shell_injection(self): @unittest.skipUnless(sys.platform.startswith('linux'), 'Test only valid for Linux') -class LibPathFindTest(unittest.TestCase): +class FindLibraryLinux(unittest.TestCase): def test_find_on_libpath(self): import subprocess import tempfile @@ -111,6 +112,15 @@ def test_find_on_libpath(self): # LD_LIBRARY_PATH) self.assertEqual(find_library(libname), 'lib%s.so' % libname) + def test_find_library_with_gcc(self): + with unittest.mock.patch("ctypes.util._findSoname_ldconfig", lambda *args: None): + self.assertNotEqual(find_library('c'), None) + + def test_find_library_with_ld(self): + with unittest.mock.patch("ctypes.util._findSoname_ldconfig", lambda *args: None), \ + unittest.mock.patch("ctypes.util._findLib_gcc", lambda *args: None): + self.assertNotEqual(find_library('c'), None) + if __name__ == "__main__": unittest.main() diff --git a/Lib/ctypes/util.py b/Lib/ctypes/util.py index 01176bf969657..0c2510e1619c8 100644 --- a/Lib/ctypes/util.py +++ b/Lib/ctypes/util.py @@ -93,6 +93,12 @@ def find_library(name): # Andreas Degert's find functions, using gcc, /sbin/ldconfig, objdump import re, tempfile + def _is_elf(filename): + "Return True if the given file is an ELF file" + elf_header = b'\x7fELF' + with open(filename, 'br') as thefile: + return thefile.read(4) == elf_header + def _findLib_gcc(name): # Run GCC's linker with the -t (aka --trace) option and examine the # library name it prints out. The GCC command will fail because we @@ -130,10 +136,17 @@ def _findLib_gcc(name): # Raised if the file was already removed, which is the normal # behaviour of GCC if linking fails pass - res = re.search(expr, trace) + res = re.findall(expr, trace) if not res: return None - return os.fsdecode(res.group(0)) + + for file in res: + # Check if the given file is an elf file: gcc can report + # some files that are linker scripts and not actual + # shared objects. See bpo-41976 for more details + if not _is_elf(file): + continue + return os.fsdecode(file) if sys.platform == "sunos5": @@ -299,9 +312,14 @@ def _findLib_ld(name): stderr=subprocess.PIPE, universal_newlines=True) out, _ = p.communicate() - res = re.search(expr, os.fsdecode(out)) - if res: - result = res.group(0) + res = re.findall(expr, os.fsdecode(out)) + for file in res: + # Check if the given file is an elf file: gcc can report + # some files that are linker scripts and not actual + # shared objects. See bpo-41976 for more details + if not _is_elf(file): + continue + return os.fsdecode(file) except Exception: pass # result will be None return result @@ -309,7 +327,7 @@ def _findLib_ld(name): def find_library(name): # See issue #9998 return _findSoname_ldconfig(name) or \ - _get_soname(_findLib_gcc(name) or _findLib_ld(name)) + _get_soname(_findLib_gcc(name)) or _get_soname(_findLib_ld(name)) ################################################################ # test code diff --git a/Misc/NEWS.d/next/Library/2020-10-08-18-22-28.bpo-41976.Svm0wb.rst b/Misc/NEWS.d/next/Library/2020-10-08-18-22-28.bpo-41976.Svm0wb.rst new file mode 100644 index 0000000000000..c8b3fc771845e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-10-08-18-22-28.bpo-41976.Svm0wb.rst @@ -0,0 +1,3 @@ +Fixed a bug that was causing :func:`ctypes.util.find_library` to return +``None`` when triying to locate a library in an environment when gcc>=9 is +available and ``ldconfig`` is not. Patch by Pablo Galindo From webhook-mailer at python.org Thu Oct 8 14:57:41 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Thu, 08 Oct 2020 18:57:41 -0000 Subject: [Python-checkins] bpo-41557: Update Windows installer to use SQLite 3.33.0 (GH-21960) Message-ID: https://github.com/python/cpython/commit/30a788990af55b950db424b9cc5408b51e0b24ed commit: 30a788990af55b950db424b9cc5408b51e0b24ed branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-08T11:57:33-07:00 summary: bpo-41557: Update Windows installer to use SQLite 3.33.0 (GH-21960) (cherry picked from commit bfe6e03cd6931813dd61b50f5fdf7d8a8848f4cd) Co-authored-by: Erlend Egeberg Aasland files: A Misc/NEWS.d/next/Windows/2020-08-26-09-35-06.bpo-41557.vt00cQ.rst M PCbuild/get_externals.bat M PCbuild/python.props M PCbuild/readme.txt diff --git a/Misc/NEWS.d/next/Windows/2020-08-26-09-35-06.bpo-41557.vt00cQ.rst b/Misc/NEWS.d/next/Windows/2020-08-26-09-35-06.bpo-41557.vt00cQ.rst new file mode 100644 index 0000000000000..9d85461f00923 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2020-08-26-09-35-06.bpo-41557.vt00cQ.rst @@ -0,0 +1 @@ +Update Windows installer to use SQLite 3.33.0. diff --git a/PCbuild/get_externals.bat b/PCbuild/get_externals.bat index 181587e602f82..24d430a004d3a 100644 --- a/PCbuild/get_externals.bat +++ b/PCbuild/get_externals.bat @@ -54,7 +54,7 @@ set libraries= set libraries=%libraries% bzip2-1.0.6 if NOT "%IncludeLibffiSrc%"=="false" set libraries=%libraries% libffi-3.3.0-rc0-r1 if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-1.1.1g -set libraries=%libraries% sqlite-3.32.3.0 +set libraries=%libraries% sqlite-3.33.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 diff --git a/PCbuild/python.props b/PCbuild/python.props index 5f4926efa2517..acc41a2c01768 100644 --- a/PCbuild/python.props +++ b/PCbuild/python.props @@ -56,7 +56,7 @@ $(EXTERNALS_DIR) $([System.IO.Path]::GetFullPath(`$(PySourcePath)externals`)) $(ExternalsDir)\ - $(ExternalsDir)sqlite-3.32.3.0\ + $(ExternalsDir)sqlite-3.33.0.0\ $(ExternalsDir)bzip2-1.0.6\ $(ExternalsDir)xz-5.2.2\ $(ExternalsDir)libffi\ diff --git a/PCbuild/readme.txt b/PCbuild/readme.txt index 8a4e8dc0c3e64..6e304eae553cd 100644 --- a/PCbuild/readme.txt +++ b/PCbuild/readme.txt @@ -184,7 +184,7 @@ _ssl again when building. _sqlite3 - Wraps SQLite 3.32.3.0, which is itself built by sqlite3.vcxproj + Wraps SQLite 3.33.0, which is itself built by sqlite3.vcxproj Homepage: http://www.sqlite.org/ _tkinter From webhook-mailer at python.org Thu Oct 8 15:02:41 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Thu, 08 Oct 2020 19:02:41 -0000 Subject: [Python-checkins] bpo-41557: Update Windows installer to use SQLite 3.33.0 (GH-21960) Message-ID: https://github.com/python/cpython/commit/089c38677d2269ab52924e4eb117394a74dfd2d9 commit: 089c38677d2269ab52924e4eb117394a74dfd2d9 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-08T12:02:33-07:00 summary: bpo-41557: Update Windows installer to use SQLite 3.33.0 (GH-21960) (cherry picked from commit bfe6e03cd6931813dd61b50f5fdf7d8a8848f4cd) Co-authored-by: Erlend Egeberg Aasland files: A Misc/NEWS.d/next/Windows/2020-08-26-09-35-06.bpo-41557.vt00cQ.rst M PCbuild/get_externals.bat M PCbuild/python.props M PCbuild/readme.txt diff --git a/Misc/NEWS.d/next/Windows/2020-08-26-09-35-06.bpo-41557.vt00cQ.rst b/Misc/NEWS.d/next/Windows/2020-08-26-09-35-06.bpo-41557.vt00cQ.rst new file mode 100644 index 0000000000000..9d85461f00923 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2020-08-26-09-35-06.bpo-41557.vt00cQ.rst @@ -0,0 +1 @@ +Update Windows installer to use SQLite 3.33.0. diff --git a/PCbuild/get_externals.bat b/PCbuild/get_externals.bat index a48b59cad2ed1..3bb281904f6a6 100644 --- a/PCbuild/get_externals.bat +++ b/PCbuild/get_externals.bat @@ -54,7 +54,7 @@ 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.32.3.0 +set libraries=%libraries% sqlite-3.33.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 diff --git a/PCbuild/python.props b/PCbuild/python.props index 5f4926efa2517..acc41a2c01768 100644 --- a/PCbuild/python.props +++ b/PCbuild/python.props @@ -56,7 +56,7 @@ $(EXTERNALS_DIR) $([System.IO.Path]::GetFullPath(`$(PySourcePath)externals`)) $(ExternalsDir)\ - $(ExternalsDir)sqlite-3.32.3.0\ + $(ExternalsDir)sqlite-3.33.0.0\ $(ExternalsDir)bzip2-1.0.6\ $(ExternalsDir)xz-5.2.2\ $(ExternalsDir)libffi\ diff --git a/PCbuild/readme.txt b/PCbuild/readme.txt index 27af74e53debd..bafd91248a587 100644 --- a/PCbuild/readme.txt +++ b/PCbuild/readme.txt @@ -185,7 +185,7 @@ _ssl again when building. _sqlite3 - Wraps SQLite 3.32.3.0, which is itself built by sqlite3.vcxproj + Wraps SQLite 3.33.0, which is itself built by sqlite3.vcxproj Homepage: http://www.sqlite.org/ _tkinter From webhook-mailer at python.org Thu Oct 8 15:21:17 2020 From: webhook-mailer at python.org (Hai Shi) Date: Thu, 08 Oct 2020 19:21:17 -0000 Subject: [Python-checkins] bpo-39337: Add a test case for normalizing of codec names (GH-19069) Message-ID: https://github.com/python/cpython/commit/3f342376ab0da3b4c8a38a27edfafba8e8cdf52d commit: 3f342376ab0da3b4c8a38a27edfafba8e8cdf52d branch: master author: Hai Shi committer: GitHub date: 2020-10-08T21:20:57+02:00 summary: bpo-39337: Add a test case for normalizing of codec names (GH-19069) files: M Lib/test/test_codecs.py diff --git a/Lib/test/test_codecs.py b/Lib/test/test_codecs.py index ed508f36ad423..ddf4e08af6247 100644 --- a/Lib/test/test_codecs.py +++ b/Lib/test/test_codecs.py @@ -3415,5 +3415,30 @@ def test_rot13_func(self): 'To be, or not to be, that is the question') +class CodecNameNormalizationTest(unittest.TestCase): + """Test codec name normalization""" + def test_normalized_encoding(self): + FOUND = (1, 2, 3, 4) + NOT_FOUND = (None, None, None, None) + def search_function(encoding): + if encoding == "aaa_8": + return FOUND + else: + return NOT_FOUND + + codecs.register(search_function) + self.addCleanup(codecs.unregister, search_function) + self.assertEqual(FOUND, codecs.lookup('aaa_8')) + self.assertEqual(FOUND, codecs.lookup('AAA-8')) + self.assertEqual(FOUND, codecs.lookup('AAA---8')) + self.assertEqual(FOUND, codecs.lookup('AAA 8')) + self.assertEqual(FOUND, codecs.lookup('aaa\xe9\u20ac-8')) + self.assertEqual(NOT_FOUND, codecs.lookup('AAA.8')) + self.assertEqual(NOT_FOUND, codecs.lookup('AAA...8')) + self.assertEqual(NOT_FOUND, codecs.lookup('BBB-8')) + self.assertEqual(NOT_FOUND, codecs.lookup('BBB.8')) + self.assertEqual(NOT_FOUND, codecs.lookup('a\xe9\u20ac-8')) + + if __name__ == "__main__": unittest.main() From webhook-mailer at python.org Thu Oct 8 17:38:49 2020 From: webhook-mailer at python.org (Mikhail Golubev) Date: Thu, 08 Oct 2020 21:38:49 -0000 Subject: [Python-checkins] Fix the attribute names in the docstring of GenericAlias (GH-22594) Message-ID: https://github.com/python/cpython/commit/77f0a23e7a9fb247101b9b14a060c4ba1c4b87a5 commit: 77f0a23e7a9fb247101b9b14a060c4ba1c4b87a5 branch: master author: Mikhail Golubev committer: GitHub date: 2020-10-08T14:38:36-07:00 summary: Fix the attribute names in the docstring of GenericAlias (GH-22594) files: M Objects/genericaliasobject.c diff --git a/Objects/genericaliasobject.c b/Objects/genericaliasobject.c index ab56e1c4bf1a8..6508c69cbf7e3 100644 --- a/Objects/genericaliasobject.c +++ b/Objects/genericaliasobject.c @@ -582,7 +582,7 @@ PyTypeObject Py_GenericAliasType = { .tp_name = "types.GenericAlias", .tp_doc = "Represent a PEP 585 generic type\n" "\n" - "E.g. for t = list[int], t.origin is list and t.args is (int,).", + "E.g. for t = list[int], t.__origin__ is list and t.__args__ is (int,).", .tp_basicsize = sizeof(gaobject), .tp_dealloc = ga_dealloc, .tp_repr = ga_repr, From webhook-mailer at python.org Thu Oct 8 18:02:30 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Thu, 08 Oct 2020 22:02:30 -0000 Subject: [Python-checkins] Fix the attribute names in the docstring of GenericAlias (GH-22594) Message-ID: https://github.com/python/cpython/commit/3da210b69f76bdd7f4e7fa2e3a12eab4dd478ed2 commit: 3da210b69f76bdd7f4e7fa2e3a12eab4dd478ed2 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-08T15:02:26-07:00 summary: Fix the attribute names in the docstring of GenericAlias (GH-22594) (cherry picked from commit 77f0a23e7a9fb247101b9b14a060c4ba1c4b87a5) Co-authored-by: Mikhail Golubev files: M Objects/genericaliasobject.c diff --git a/Objects/genericaliasobject.c b/Objects/genericaliasobject.c index 4f952162ec930..3e850b51092cc 100644 --- a/Objects/genericaliasobject.c +++ b/Objects/genericaliasobject.c @@ -582,7 +582,7 @@ PyTypeObject Py_GenericAliasType = { .tp_name = "types.GenericAlias", .tp_doc = "Represent a PEP 585 generic type\n" "\n" - "E.g. for t = list[int], t.origin is list and t.args is (int,).", + "E.g. for t = list[int], t.__origin__ is list and t.__args__ is (int,).", .tp_basicsize = sizeof(gaobject), .tp_dealloc = ga_dealloc, .tp_repr = ga_repr, From webhook-mailer at python.org Fri Oct 9 05:32:33 2020 From: webhook-mailer at python.org (Nishit) Date: Fri, 09 Oct 2020 09:32:33 -0000 Subject: [Python-checkins] Updated README for python 3.10 (GH-22605) Message-ID: https://github.com/python/cpython/commit/666f583e9e47d04c557a20555379d97e810779a6 commit: 666f583e9e47d04c557a20555379d97e810779a6 branch: master author: Nishit committer: GitHub date: 2020-10-09T10:32:15+01:00 summary: Updated README for python 3.10 (GH-22605) Updated python version and link to the release schedule files: M README.rst diff --git a/README.rst b/README.rst index 176562cae308b..33ccfc01d9c29 100644 --- a/README.rst +++ b/README.rst @@ -244,7 +244,7 @@ All current PEPs, as well as guidelines for submitting a new PEP, are listed at Release Schedule ---------------- -See :pep:`596` for Python 3.9 release details. +See :pep:`619` for Python 3.10 release details. Copyright and License Information From webhook-mailer at python.org Fri Oct 9 05:56:58 2020 From: webhook-mailer at python.org (Batuhan Taskaya) Date: Fri, 09 Oct 2020 09:56:58 -0000 Subject: [Python-checkins] bpo-41979: Accept star-unpacking on with-item targets (GH-22611) Message-ID: https://github.com/python/cpython/commit/48f305fd122080a9181cbda33bdb42ea36a0136f commit: 48f305fd122080a9181cbda33bdb42ea36a0136f branch: master author: Batuhan Taskaya committer: GitHub date: 2020-10-09T10:56:48+01:00 summary: bpo-41979: Accept star-unpacking on with-item targets (GH-22611) Co-authored-by: Pablo Galindo files: A Misc/NEWS.d/next/Core and Builtins/2020-10-09-10-55-50.bpo-41979.ImXIk2.rst M Grammar/python.gram M Lib/test/test_with.py M Parser/parser.c diff --git a/Grammar/python.gram b/Grammar/python.gram index e4533b1a1b879..2f52bd7f2f6a4 100644 --- a/Grammar/python.gram +++ b/Grammar/python.gram @@ -182,7 +182,7 @@ with_stmt[stmt_ty]: | ASYNC 'with' a[asdl_withitem_seq*]=','.with_item+ ':' tc=[TYPE_COMMENT] b=block { CHECK_VERSION(5, "Async with statements are", _Py_AsyncWith(a, b, NEW_TYPE_COMMENT(p, tc), EXTRA)) } with_item[withitem_ty]: - | e=expression 'as' t=target &(',' | ')' | ':') { _Py_withitem(e, t, p->arena) } + | e=expression 'as' t=star_target &(',' | ')' | ':') { _Py_withitem(e, t, p->arena) } | invalid_with_item | e=expression { _Py_withitem(e, NULL, p->arena) } diff --git a/Lib/test/test_with.py b/Lib/test/test_with.py index b1d7a15b5e4ee..f21bf65fed849 100644 --- a/Lib/test/test_with.py +++ b/Lib/test/test_with.py @@ -7,7 +7,7 @@ import sys import unittest from collections import deque -from contextlib import _GeneratorContextManager, contextmanager +from contextlib import _GeneratorContextManager, contextmanager, nullcontext class MockContextManager(_GeneratorContextManager): @@ -641,6 +641,12 @@ class B: pass self.assertEqual(blah.two, 2) self.assertEqual(blah.three, 3) + def testWithExtendedTargets(self): + with nullcontext(range(1, 5)) as (a, *b, c): + self.assertEqual(a, 1) + self.assertEqual(b, [2, 3]) + self.assertEqual(c, 4) + class ExitSwallowsExceptionTestCase(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-10-09-10-55-50.bpo-41979.ImXIk2.rst b/Misc/NEWS.d/next/Core and Builtins/2020-10-09-10-55-50.bpo-41979.ImXIk2.rst new file mode 100644 index 0000000000000..3250309ca22cd --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2020-10-09-10-55-50.bpo-41979.ImXIk2.rst @@ -0,0 +1 @@ +Star-unpacking is now allowed for with item's targets in the PEG parser. diff --git a/Parser/parser.c b/Parser/parser.c index 1bd74a38fbc2b..0d92256a3ebe2 100644 --- a/Parser/parser.c +++ b/Parser/parser.c @@ -4290,7 +4290,10 @@ with_stmt_rule(Parser *p) return _res; } -// with_item: expression 'as' target &(',' | ')' | ':') | invalid_with_item | expression +// with_item: +// | expression 'as' star_target &(',' | ')' | ':') +// | invalid_with_item +// | expression static withitem_ty with_item_rule(Parser *p) { @@ -4301,12 +4304,12 @@ with_item_rule(Parser *p) } withitem_ty _res = NULL; int _mark = p->mark; - { // expression 'as' target &(',' | ')' | ':') + { // expression 'as' star_target &(',' | ')' | ':') if (p->error_indicator) { D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> with_item[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression 'as' target &(',' | ')' | ':')")); + D(fprintf(stderr, "%*c> with_item[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression 'as' star_target &(',' | ')' | ':')")); Token * _keyword; expr_ty e; expr_ty t; @@ -4315,12 +4318,12 @@ with_item_rule(Parser *p) && (_keyword = _PyPegen_expect_token(p, 520)) // token='as' && - (t = target_rule(p)) // target + (t = star_target_rule(p)) // star_target && _PyPegen_lookahead(1, _tmp_47_rule, p) ) { - D(fprintf(stderr, "%*c+ with_item[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression 'as' target &(',' | ')' | ':')")); + D(fprintf(stderr, "%*c+ with_item[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression 'as' star_target &(',' | ')' | ':')")); _res = _Py_withitem ( e , t , p -> arena ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -4331,7 +4334,7 @@ with_item_rule(Parser *p) } p->mark = _mark; D(fprintf(stderr, "%*c%s with_item[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression 'as' target &(',' | ')' | ':')")); + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression 'as' star_target &(',' | ')' | ':')")); } { // invalid_with_item if (p->error_indicator) { From webhook-mailer at python.org Fri Oct 9 06:08:50 2020 From: webhook-mailer at python.org (Karthikeyan Singaravelan) Date: Fri, 09 Oct 2020 10:08:50 -0000 Subject: [Python-checkins] [3.9] bpo-41970: Avoid test failure in test_lib2to3 if the module is already imported (GH-22595) (GH-22609) Message-ID: https://github.com/python/cpython/commit/c6f41e62f55933f9735e9b9c103c6860685b028a commit: c6f41e62f55933f9735e9b9c103c6860685b028a branch: 3.9 author: Karthikeyan Singaravelan committer: GitHub date: 2020-10-09T11:08:42+01:00 summary: [3.9] bpo-41970: Avoid test failure in test_lib2to3 if the module is already imported (GH-22595) (GH-22609) (cherry picked from commit 4a9f82f50d957b6cf3fd207de8b583d9137316b8) Co-authored-by: Pablo Galindo files: A Misc/NEWS.d/next/Tests/2020-10-08-14-00-17.bpo-41970.aZ8QFf.rst M Lib/test/test_lib2to3.py diff --git a/Lib/test/test_lib2to3.py b/Lib/test/test_lib2to3.py index 15c317e957bc0..e939d47336e36 100644 --- a/Lib/test/test_lib2to3.py +++ b/Lib/test/test_lib2to3.py @@ -1,8 +1,8 @@ import unittest -from test.support import check_warnings +from test.support import check_warnings, import_fresh_module with check_warnings(("", PendingDeprecationWarning)): - from lib2to3.tests import load_tests + load_tests = import_fresh_module('lib2to3.tests', fresh=['lib2to3']).load_tests if __name__ == '__main__': unittest.main() diff --git a/Misc/NEWS.d/next/Tests/2020-10-08-14-00-17.bpo-41970.aZ8QFf.rst b/Misc/NEWS.d/next/Tests/2020-10-08-14-00-17.bpo-41970.aZ8QFf.rst new file mode 100644 index 0000000000000..4cdca197fbfc6 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2020-10-08-14-00-17.bpo-41970.aZ8QFf.rst @@ -0,0 +1,2 @@ +Avoid a test failure in ``test_lib2to3`` if the module has already imported +at the time the test executes. Patch by Pablo Galindo. From webhook-mailer at python.org Fri Oct 9 06:31:16 2020 From: webhook-mailer at python.org (Batuhan Taskaya) Date: Fri, 09 Oct 2020 10:31:16 -0000 Subject: [Python-checkins] [3.9] bpo-41979: Accept star-unpacking on with-item targets (GH-22611) (GH-22612) Message-ID: https://github.com/python/cpython/commit/42157b9eaa7f4de69a5f5f65d4403d70efa6a5c2 commit: 42157b9eaa7f4de69a5f5f65d4403d70efa6a5c2 branch: 3.9 author: Batuhan Taskaya committer: GitHub date: 2020-10-09T03:31:07-07:00 summary: [3.9] bpo-41979: Accept star-unpacking on with-item targets (GH-22611) (GH-22612) Co-authored-by: Batuhan Taskaya Automerge-Triggered-By: @pablogsal files: A Misc/NEWS.d/next/Core and Builtins/2020-10-09-10-55-50.bpo-41979.ImXIk2.rst M Grammar/python.gram M Lib/test/test_with.py M Parser/pegen/parse.c diff --git a/Grammar/python.gram b/Grammar/python.gram index 334a7f56f0107..8f4d250dc3ae4 100644 --- a/Grammar/python.gram +++ b/Grammar/python.gram @@ -182,7 +182,7 @@ with_stmt[stmt_ty]: | ASYNC 'with' a=','.with_item+ ':' tc=[TYPE_COMMENT] b=block { CHECK_VERSION(5, "Async with statements are", _Py_AsyncWith(a, b, NEW_TYPE_COMMENT(p, tc), EXTRA)) } with_item[withitem_ty]: - | e=expression 'as' t=target &(',' | ')' | ':') { _Py_withitem(e, t, p->arena) } + | e=expression 'as' t=star_target &(',' | ')' | ':') { _Py_withitem(e, t, p->arena) } | invalid_with_item | e=expression { _Py_withitem(e, NULL, p->arena) } diff --git a/Lib/test/test_with.py b/Lib/test/test_with.py index b1d7a15b5e4ee..f21bf65fed849 100644 --- a/Lib/test/test_with.py +++ b/Lib/test/test_with.py @@ -7,7 +7,7 @@ import sys import unittest from collections import deque -from contextlib import _GeneratorContextManager, contextmanager +from contextlib import _GeneratorContextManager, contextmanager, nullcontext class MockContextManager(_GeneratorContextManager): @@ -641,6 +641,12 @@ class B: pass self.assertEqual(blah.two, 2) self.assertEqual(blah.three, 3) + def testWithExtendedTargets(self): + with nullcontext(range(1, 5)) as (a, *b, c): + self.assertEqual(a, 1) + self.assertEqual(b, [2, 3]) + self.assertEqual(c, 4) + class ExitSwallowsExceptionTestCase(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-10-09-10-55-50.bpo-41979.ImXIk2.rst b/Misc/NEWS.d/next/Core and Builtins/2020-10-09-10-55-50.bpo-41979.ImXIk2.rst new file mode 100644 index 0000000000000..3250309ca22cd --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2020-10-09-10-55-50.bpo-41979.ImXIk2.rst @@ -0,0 +1 @@ +Star-unpacking is now allowed for with item's targets in the PEG parser. diff --git a/Parser/pegen/parse.c b/Parser/pegen/parse.c index f74b6f297b040..1a4df757eae33 100644 --- a/Parser/pegen/parse.c +++ b/Parser/pegen/parse.c @@ -4289,7 +4289,10 @@ with_stmt_rule(Parser *p) return _res; } -// with_item: expression 'as' target &(',' | ')' | ':') | invalid_with_item | expression +// with_item: +// | expression 'as' star_target &(',' | ')' | ':') +// | invalid_with_item +// | expression static withitem_ty with_item_rule(Parser *p) { @@ -4300,12 +4303,12 @@ with_item_rule(Parser *p) } withitem_ty _res = NULL; int _mark = p->mark; - { // expression 'as' target &(',' | ')' | ':') + { // expression 'as' star_target &(',' | ')' | ':') if (p->error_indicator) { D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> with_item[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression 'as' target &(',' | ')' | ':')")); + D(fprintf(stderr, "%*c> with_item[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression 'as' star_target &(',' | ')' | ':')")); Token * _keyword; expr_ty e; expr_ty t; @@ -4314,12 +4317,12 @@ with_item_rule(Parser *p) && (_keyword = _PyPegen_expect_token(p, 520)) // token='as' && - (t = target_rule(p)) // target + (t = star_target_rule(p)) // star_target && _PyPegen_lookahead(1, _tmp_47_rule, p) ) { - D(fprintf(stderr, "%*c+ with_item[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression 'as' target &(',' | ')' | ':')")); + D(fprintf(stderr, "%*c+ with_item[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression 'as' star_target &(',' | ')' | ':')")); _res = _Py_withitem ( e , t , p -> arena ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -4330,7 +4333,7 @@ with_item_rule(Parser *p) } p->mark = _mark; D(fprintf(stderr, "%*c%s with_item[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression 'as' target &(',' | ')' | ':')")); + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression 'as' star_target &(',' | ')' | ':')")); } { // invalid_with_item if (p->error_indicator) { From webhook-mailer at python.org Fri Oct 9 07:14:50 2020 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Fri, 09 Oct 2020 11:14:50 -0000 Subject: [Python-checkins] bpo-41974: Remove complex.__float__, complex.__floordiv__, etc (GH-22593) Message-ID: https://github.com/python/cpython/commit/e2ec0b27c02a158d0007c11dcc1f2d7a95948712 commit: e2ec0b27c02a158d0007c11dcc1f2d7a95948712 branch: master author: Serhiy Storchaka committer: GitHub date: 2020-10-09T14:14:37+03:00 summary: bpo-41974: Remove complex.__float__, complex.__floordiv__, etc (GH-22593) Remove complex special methods __int__, __float__, __floordiv__, __mod__, __divmod__, __rfloordiv__, __rmod__ and __rdivmod__ which always raised a TypeError. files: A Misc/NEWS.d/next/Core and Builtins/2020-10-08-09-58-19.bpo-41974.8B-q8O.rst M Doc/whatsnew/3.10.rst M Lib/test/test_complex.py M Objects/abstract.c M Objects/bytesobject.c M Objects/complexobject.c M Objects/floatobject.c M Objects/unicodeobject.c diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index 4ada4be3b6671..7401ba722fb4f 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -255,6 +255,12 @@ Deprecated Removed ======= +* Removed special methods ``__int__``, ``__float__``, ``__floordiv__``, + ``__mod__``, ``__divmod__``, ``__rfloordiv__``, ``__rmod__`` and + ``__rdivmod__`` of the :class:`complex` class. They always raised + a :exc:`TypeError`. + (Contributed by Serhiy Storchaka in :issue:`41974`.) + * The ``ParserBase.error()`` method from the private and undocumented ``_markupbase`` module has been removed. :class:`html.parser.HTMLParser` is the only subclass of ``ParserBase`` and its ``error()`` implementation has already been removed in diff --git a/Lib/test/test_complex.py b/Lib/test/test_complex.py index d1f241f7a60c9..af39ee878dc91 100644 --- a/Lib/test/test_complex.py +++ b/Lib/test/test_complex.py @@ -11,6 +11,14 @@ NAN = float("nan") # These tests ensure that complex math does the right thing +ZERO_DIVISION = ( + (1+1j, 0+0j), + (1+1j, 0.0), + (1+1j, 0), + (1.0, 0+0j), + (1, 0+0j), +) + class ComplexTest(unittest.TestCase): def assertAlmostEqual(self, a, b): @@ -99,20 +107,34 @@ def test_truediv(self): self.check_div(complex(random(), random()), complex(random(), random())) - self.assertRaises(ZeroDivisionError, complex.__truediv__, 1+1j, 0+0j) - self.assertRaises(OverflowError, pow, 1e200+1j, 1e200+1j) - self.assertAlmostEqual(complex.__truediv__(2+0j, 1+1j), 1-1j) - self.assertRaises(ZeroDivisionError, complex.__truediv__, 1+1j, 0+0j) for denom_real, denom_imag in [(0, NAN), (NAN, 0), (NAN, NAN)]: z = complex(0, 0) / complex(denom_real, denom_imag) self.assertTrue(isnan(z.real)) self.assertTrue(isnan(z.imag)) + def test_truediv_zero_division(self): + for a, b in ZERO_DIVISION: + with self.assertRaises(ZeroDivisionError): + a / b + def test_floordiv(self): - self.assertRaises(TypeError, complex.__floordiv__, 3+0j, 1.5+0j) - self.assertRaises(TypeError, complex.__floordiv__, 3+0j, 0+0j) + with self.assertRaises(TypeError): + (1+1j) // (1+0j) + with self.assertRaises(TypeError): + (1+1j) // 1.0 + with self.assertRaises(TypeError): + (1+1j) // 1 + with self.assertRaises(TypeError): + 1.0 // (1+0j) + with self.assertRaises(TypeError): + 1 // (1+0j) + + def test_floordiv_zero_division(self): + for a, b in ZERO_DIVISION: + with self.assertRaises(TypeError): + a // b def test_richcompare(self): self.assertIs(complex.__eq__(1+1j, 1<<10000), False) @@ -159,13 +181,32 @@ def check(n, deltas, is_equal, imag = 0.0): def test_mod(self): # % is no longer supported on complex numbers - self.assertRaises(TypeError, (1+1j).__mod__, 0+0j) - self.assertRaises(TypeError, lambda: (3.33+4.43j) % 0) - self.assertRaises(TypeError, (1+1j).__mod__, 4.3j) + with self.assertRaises(TypeError): + (1+1j) % (1+0j) + with self.assertRaises(TypeError): + (1+1j) % 1.0 + with self.assertRaises(TypeError): + (1+1j) % 1 + with self.assertRaises(TypeError): + 1.0 % (1+0j) + with self.assertRaises(TypeError): + 1 % (1+0j) + + def test_mod_zero_division(self): + for a, b in ZERO_DIVISION: + with self.assertRaises(TypeError): + a % b def test_divmod(self): self.assertRaises(TypeError, divmod, 1+1j, 1+0j) - self.assertRaises(TypeError, divmod, 1+1j, 0+0j) + self.assertRaises(TypeError, divmod, 1+1j, 1.0) + self.assertRaises(TypeError, divmod, 1+1j, 1) + self.assertRaises(TypeError, divmod, 1.0, 1+0j) + self.assertRaises(TypeError, divmod, 1, 1+0j) + + def test_divmod_zero_division(self): + for a, b in ZERO_DIVISION: + self.assertRaises(TypeError, divmod, a, b) def test_pow(self): self.assertAlmostEqual(pow(1+1j, 0+0j), 1.0) @@ -174,6 +215,7 @@ def test_pow(self): self.assertAlmostEqual(pow(1j, -1), 1/1j) self.assertAlmostEqual(pow(1j, 200), 1) self.assertRaises(ValueError, pow, 1+1j, 1+1j, 1+1j) + self.assertRaises(OverflowError, pow, 1e200+1j, 1e200+1j) a = 3.33+4.43j self.assertEqual(a ** 0j, 1) diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-10-08-09-58-19.bpo-41974.8B-q8O.rst b/Misc/NEWS.d/next/Core and Builtins/2020-10-08-09-58-19.bpo-41974.8B-q8O.rst new file mode 100644 index 0000000000000..034cfede84b8a --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2020-10-08-09-58-19.bpo-41974.8B-q8O.rst @@ -0,0 +1,4 @@ +Removed special methods ``__int__``, ``__float__``, ``__floordiv__``, +``__mod__``, ``__divmod__``, ``__rfloordiv__``, ``__rmod__`` and +``__rdivmod__`` of the :class:`complex` class. They always raised +a :exc:`TypeError`. diff --git a/Objects/abstract.c b/Objects/abstract.c index c30fb4eb6a604..2ab3371a3f3cb 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -747,10 +747,10 @@ PyObject_Format(PyObject *obj, PyObject *format_spec) int PyNumber_Check(PyObject *o) { - return o && Py_TYPE(o)->tp_as_number && - (Py_TYPE(o)->tp_as_number->nb_index || - Py_TYPE(o)->tp_as_number->nb_int || - Py_TYPE(o)->tp_as_number->nb_float); + if (o == NULL) + return 0; + PyNumberMethods *nb = Py_TYPE(o)->tp_as_number; + return nb && (nb->nb_index || nb->nb_int || nb->nb_float || PyComplex_Check(o)); } /* Binary operators */ @@ -1461,7 +1461,7 @@ PyNumber_Long(PyObject *o) } return type_error("int() argument must be a string, a bytes-like object " - "or a number, not '%.200s'", o); + "or a real number, not '%.200s'", o); } PyObject * diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c index 836a736037ba4..990730cd8cdc1 100644 --- a/Objects/bytesobject.c +++ b/Objects/bytesobject.c @@ -522,7 +522,7 @@ formatlong(PyObject *v, int flags, int prec, int type) PyErr_Format(PyExc_TypeError, "%%%c format: %s is required, not %.200s", type, (type == 'o' || type == 'x' || type == 'X') ? "an integer" - : "a number", + : "a real number", Py_TYPE(v)->tp_name); return NULL; } diff --git a/Objects/complexobject.c b/Objects/complexobject.c index 69f6c17b4a49c..5ab839a9e9423 100644 --- a/Objects/complexobject.c +++ b/Objects/complexobject.c @@ -509,23 +509,6 @@ complex_div(PyObject *v, PyObject *w) return PyComplex_FromCComplex(quot); } -static PyObject * -complex_remainder(PyObject *v, PyObject *w) -{ - PyErr_SetString(PyExc_TypeError, - "can't mod complex numbers."); - return NULL; -} - - -static PyObject * -complex_divmod(PyObject *v, PyObject *w) -{ - PyErr_SetString(PyExc_TypeError, - "can't take floor or mod of complex number."); - return NULL; -} - static PyObject * complex_pow(PyObject *v, PyObject *w, PyObject *z) { @@ -562,14 +545,6 @@ complex_pow(PyObject *v, PyObject *w, PyObject *z) return PyComplex_FromCComplex(p); } -static PyObject * -complex_int_div(PyObject *v, PyObject *w) -{ - PyErr_SetString(PyExc_TypeError, - "can't take floor of complex number."); - return NULL; -} - static PyObject * complex_neg(PyComplexObject *v) { @@ -668,22 +643,6 @@ complex_richcompare(PyObject *v, PyObject *w, int op) Py_RETURN_NOTIMPLEMENTED; } -static PyObject * -complex_int(PyObject *v) -{ - PyErr_SetString(PyExc_TypeError, - "can't convert complex to int"); - return NULL; -} - -static PyObject * -complex_float(PyObject *v) -{ - PyErr_SetString(PyExc_TypeError, - "can't convert complex to float"); - return NULL; -} - /*[clinic input] complex.conjugate @@ -966,7 +925,9 @@ complex_new_impl(PyTypeObject *type, PyObject *r, PyObject *i) } nbr = Py_TYPE(r)->tp_as_number; - if (nbr == NULL || (nbr->nb_float == NULL && nbr->nb_index == NULL)) { + if (nbr == NULL || + (nbr->nb_float == NULL && nbr->nb_index == NULL && !PyComplex_Check(r))) + { PyErr_Format(PyExc_TypeError, "complex() first argument must be a string or a number, " "not '%.200s'", @@ -978,7 +939,9 @@ complex_new_impl(PyTypeObject *type, PyObject *r, PyObject *i) } if (i != NULL) { nbi = Py_TYPE(i)->tp_as_number; - if (nbi == NULL || (nbi->nb_float == NULL && nbi->nb_index == NULL)) { + if (nbi == NULL || + (nbi->nb_float == NULL && nbi->nb_index == NULL && !PyComplex_Check(i))) + { PyErr_Format(PyExc_TypeError, "complex() second argument must be a number, " "not '%.200s'", @@ -1057,8 +1020,8 @@ static PyNumberMethods complex_as_number = { (binaryfunc)complex_add, /* nb_add */ (binaryfunc)complex_sub, /* nb_subtract */ (binaryfunc)complex_mul, /* nb_multiply */ - (binaryfunc)complex_remainder, /* nb_remainder */ - (binaryfunc)complex_divmod, /* nb_divmod */ + 0, /* nb_remainder */ + 0, /* nb_divmod */ (ternaryfunc)complex_pow, /* nb_power */ (unaryfunc)complex_neg, /* nb_negative */ (unaryfunc)complex_pos, /* nb_positive */ @@ -1070,9 +1033,9 @@ static PyNumberMethods complex_as_number = { 0, /* nb_and */ 0, /* nb_xor */ 0, /* nb_or */ - complex_int, /* nb_int */ + 0, /* nb_int */ 0, /* nb_reserved */ - complex_float, /* nb_float */ + 0, /* nb_float */ 0, /* nb_inplace_add */ 0, /* nb_inplace_subtract */ 0, /* nb_inplace_multiply*/ @@ -1083,7 +1046,7 @@ static PyNumberMethods complex_as_number = { 0, /* nb_inplace_and */ 0, /* nb_inplace_xor */ 0, /* nb_inplace_or */ - (binaryfunc)complex_int_div, /* nb_floor_divide */ + 0, /* nb_floor_divide */ (binaryfunc)complex_div, /* nb_true_divide */ 0, /* nb_inplace_floor_divide */ 0, /* nb_inplace_true_divide */ diff --git a/Objects/floatobject.c b/Objects/floatobject.c index d0af0ea1a9825..828bde18df70c 100644 --- a/Objects/floatobject.c +++ b/Objects/floatobject.c @@ -215,7 +215,7 @@ PyFloat_FromString(PyObject *v) } else { PyErr_Format(PyExc_TypeError, - "float() argument must be a string or a number, not '%.200s'", + "float() argument must be a string or a real number, not '%.200s'", Py_TYPE(v)->tp_name); return NULL; } diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 6ae06a508c614..01e5c728b383f 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -14839,7 +14839,7 @@ mainformatlong(PyObject *v, break; default: PyErr_Format(PyExc_TypeError, - "%%%c format: a number is required, " + "%%%c format: a real number is required, " "not %.200s", type, Py_TYPE(v)->tp_name); break; From webhook-mailer at python.org Fri Oct 9 14:45:56 2020 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Fri, 09 Oct 2020 18:45:56 -0000 Subject: [Python-checkins] bpo-41831: Add tests for tkinter.Event.__repr__ (GH-22354) Message-ID: https://github.com/python/cpython/commit/f25323a307a72c40862c87c2df822f83be6645da commit: f25323a307a72c40862c87c2df822f83be6645da branch: master author: Serhiy Storchaka committer: GitHub date: 2020-10-09T21:45:46+03:00 summary: bpo-41831: Add tests for tkinter.Event.__repr__ (GH-22354) files: M Lib/tkinter/test/test_tkinter/test_misc.py diff --git a/Lib/tkinter/test/test_tkinter/test_misc.py b/Lib/tkinter/test/test_tkinter/test_misc.py index 1e089747a91ee..b8eea2544f522 100644 --- a/Lib/tkinter/test/test_tkinter/test_misc.py +++ b/Lib/tkinter/test/test_tkinter/test_misc.py @@ -192,6 +192,54 @@ def test_clipboard_astral(self): with self.assertRaises(tkinter.TclError): root.clipboard_get() + def test_event_repr_defaults(self): + e = tkinter.Event() + e.serial = 12345 + e.num = '??' + e.height = '??' + e.keycode = '??' + e.state = 0 + e.time = 123456789 + e.width = '??' + e.x = '??' + e.y = '??' + e.char = '' + e.keysym = '??' + e.keysym_num = '??' + e.type = '100' + e.widget = '??' + e.x_root = '??' + e.y_root = '??' + e.delta = 0 + self.assertEqual(repr(e), '<100 event>') + + def test_event_repr(self): + e = tkinter.Event() + e.serial = 12345 + e.num = 3 + e.focus = True + e.height = 200 + e.keycode = 65 + e.state = 0x30405 + e.time = 123456789 + e.width = 300 + e.x = 10 + e.y = 20 + e.char = 'A' + e.send_event = True + e.keysym = 'Key-A' + e.keysym_num = ord('A') + e.type = tkinter.EventType.Configure + e.widget = '.text' + e.x_root = 1010 + e.y_root = 1020 + e.delta = -1 + self.assertEqual(repr(e), + "") tests_gui = (MiscTest, ) From webhook-mailer at python.org Fri Oct 9 15:05:30 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Fri, 09 Oct 2020 19:05:30 -0000 Subject: [Python-checkins] bpo-41831: Add tests for tkinter.Event.__repr__ (GH-22354) Message-ID: https://github.com/python/cpython/commit/86938548d5829400065d2705e3d25604c14b23b7 commit: 86938548d5829400065d2705e3d25604c14b23b7 branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-09T12:05:26-07:00 summary: bpo-41831: Add tests for tkinter.Event.__repr__ (GH-22354) (cherry picked from commit f25323a307a72c40862c87c2df822f83be6645da) Co-authored-by: Serhiy Storchaka files: M Lib/tkinter/test/test_tkinter/test_misc.py diff --git a/Lib/tkinter/test/test_tkinter/test_misc.py b/Lib/tkinter/test/test_tkinter/test_misc.py index 236cae0e9fac4..350f561bab406 100644 --- a/Lib/tkinter/test/test_tkinter/test_misc.py +++ b/Lib/tkinter/test/test_tkinter/test_misc.py @@ -178,6 +178,54 @@ def test_clipboard_astral(self): with self.assertRaises(tkinter.TclError): root.clipboard_get() + def test_event_repr_defaults(self): + e = tkinter.Event() + e.serial = 12345 + e.num = '??' + e.height = '??' + e.keycode = '??' + e.state = 0 + e.time = 123456789 + e.width = '??' + e.x = '??' + e.y = '??' + e.char = '' + e.keysym = '??' + e.keysym_num = '??' + e.type = '100' + e.widget = '??' + e.x_root = '??' + e.y_root = '??' + e.delta = 0 + self.assertEqual(repr(e), '<100 event>') + + def test_event_repr(self): + e = tkinter.Event() + e.serial = 12345 + e.num = 3 + e.focus = True + e.height = 200 + e.keycode = 65 + e.state = 0x30405 + e.time = 123456789 + e.width = 300 + e.x = 10 + e.y = 20 + e.char = 'A' + e.send_event = True + e.keysym = 'Key-A' + e.keysym_num = ord('A') + e.type = tkinter.EventType.Configure + e.widget = '.text' + e.x_root = 1010 + e.y_root = 1020 + e.delta = -1 + self.assertEqual(repr(e), + "") tests_gui = (MiscTest, ) From webhook-mailer at python.org Fri Oct 9 15:56:28 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Fri, 09 Oct 2020 19:56:28 -0000 Subject: [Python-checkins] bpo-41831: Add tests for tkinter.Event.__repr__ (GH-22354) (GH-22617) Message-ID: https://github.com/python/cpython/commit/43c3eafa1bcdc522870e112d3e2d67ce2451c34b commit: 43c3eafa1bcdc522870e112d3e2d67ce2451c34b branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-09T22:56:19+03:00 summary: bpo-41831: Add tests for tkinter.Event.__repr__ (GH-22354) (GH-22617) (cherry picked from commit f25323a307a72c40862c87c2df822f83be6645da) Co-authored-by: Serhiy Storchaka files: M Lib/tkinter/test/test_tkinter/test_misc.py diff --git a/Lib/tkinter/test/test_tkinter/test_misc.py b/Lib/tkinter/test/test_tkinter/test_misc.py index 1e089747a91ee..b8eea2544f522 100644 --- a/Lib/tkinter/test/test_tkinter/test_misc.py +++ b/Lib/tkinter/test/test_tkinter/test_misc.py @@ -192,6 +192,54 @@ def test_clipboard_astral(self): with self.assertRaises(tkinter.TclError): root.clipboard_get() + def test_event_repr_defaults(self): + e = tkinter.Event() + e.serial = 12345 + e.num = '??' + e.height = '??' + e.keycode = '??' + e.state = 0 + e.time = 123456789 + e.width = '??' + e.x = '??' + e.y = '??' + e.char = '' + e.keysym = '??' + e.keysym_num = '??' + e.type = '100' + e.widget = '??' + e.x_root = '??' + e.y_root = '??' + e.delta = 0 + self.assertEqual(repr(e), '<100 event>') + + def test_event_repr(self): + e = tkinter.Event() + e.serial = 12345 + e.num = 3 + e.focus = True + e.height = 200 + e.keycode = 65 + e.state = 0x30405 + e.time = 123456789 + e.width = 300 + e.x = 10 + e.y = 20 + e.char = 'A' + e.send_event = True + e.keysym = 'Key-A' + e.keysym_num = ord('A') + e.type = tkinter.EventType.Configure + e.widget = '.text' + e.x_root = 1010 + e.y_root = 1020 + e.delta = -1 + self.assertEqual(repr(e), + "") tests_gui = (MiscTest, ) From webhook-mailer at python.org Fri Oct 9 15:57:43 2020 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Fri, 09 Oct 2020 19:57:43 -0000 Subject: [Python-checkins] bpo-41831: Restore str implementation of __str__ in tkinter.EventType (GH-22355) Message-ID: https://github.com/python/cpython/commit/eb38c6b7aa35d3003ec0958533421902d19ce408 commit: eb38c6b7aa35d3003ec0958533421902d19ce408 branch: master author: Serhiy Storchaka committer: GitHub date: 2020-10-09T22:57:34+03:00 summary: bpo-41831: Restore str implementation of __str__ in tkinter.EventType (GH-22355) files: A Misc/NEWS.d/next/Library/2020-09-22-11-07-50.bpo-41831.k-Eop_.rst M Lib/tkinter/__init__.py diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py index 3919397d3cead..3bfeb7a017903 100644 --- a/Lib/tkinter/__init__.py +++ b/Lib/tkinter/__init__.py @@ -185,8 +185,7 @@ class EventType(enum.StrEnum): Deactivate = '37' MouseWheel = '38' - def __str__(self): - return self.name + __str__ = str.__str__ class Event: @@ -266,7 +265,7 @@ def __repr__(self): 'num', 'delta', 'focus', 'x', 'y', 'width', 'height') return '<%s event%s>' % ( - self.type, + getattr(self.type, 'name', self.type), ''.join(' %s=%s' % (k, attrs[k]) for k in keys if k in attrs) ) diff --git a/Misc/NEWS.d/next/Library/2020-09-22-11-07-50.bpo-41831.k-Eop_.rst b/Misc/NEWS.d/next/Library/2020-09-22-11-07-50.bpo-41831.k-Eop_.rst new file mode 100644 index 0000000000000..84a3f5253a060 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-09-22-11-07-50.bpo-41831.k-Eop_.rst @@ -0,0 +1,3 @@ +``str()`` for the ``type`` attribute of the ``tkinter.Event`` object always +returns now the numeric code returned by Tk instead of the name of the event +type. From webhook-mailer at python.org Fri Oct 9 16:00:29 2020 From: webhook-mailer at python.org (Saiyang Gou) Date: Fri, 09 Oct 2020 20:00:29 -0000 Subject: [Python-checkins] bpo-39481: Fix duplicate SimpleQueue type in test_genericalias.py (GH-22619) Message-ID: https://github.com/python/cpython/commit/b2c0a43699bd9023a69e3fa554f5488a2e17e278 commit: b2c0a43699bd9023a69e3fa554f5488a2e17e278 branch: master author: Saiyang Gou committer: GitHub date: 2020-10-09T13:00:15-07:00 summary: bpo-39481: Fix duplicate SimpleQueue type in test_genericalias.py (GH-22619) There are two different `SimpleQueue` types imported (from `multiprocessing.queues` and `queue`) in `Lib/test/test_genericalias.py`, the second one shadowing the first one, making the first one not actually tested. Fix by using different names. Automerge-Triggered-By: @gvanrossum files: M Lib/test/test_genericalias.py diff --git a/Lib/test/test_genericalias.py b/Lib/test/test_genericalias.py index 643fffc073e82..2979cfb55083c 100644 --- a/Lib/test/test_genericalias.py +++ b/Lib/test/test_genericalias.py @@ -29,7 +29,7 @@ except ImportError: # multiprocessing.shared_memory is not available on e.g. Android ShareableList = None -from multiprocessing.queues import SimpleQueue +from multiprocessing.queues import SimpleQueue as MPSimpleQueue from os import DirEntry from re import Pattern, Match from types import GenericAlias, MappingProxyType, AsyncGeneratorType @@ -81,7 +81,7 @@ def test_subscriptable(self): SplitResult, ParseResult, ValueProxy, ApplyResult, WeakSet, ReferenceType, ref, - ShareableList, SimpleQueue, + ShareableList, MPSimpleQueue, Future, _WorkItem, Morsel] if ctypes is not None: From webhook-mailer at python.org Fri Oct 9 16:00:53 2020 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Fri, 09 Oct 2020 20:00:53 -0000 Subject: [Python-checkins] bpo-41985: Add _PyLong_FileDescriptor_Converter and AC converter for "fildes". (GH-22620) Message-ID: https://github.com/python/cpython/commit/9975cc5008c795e069ce11e2dbed2110cc12e74e commit: 9975cc5008c795e069ce11e2dbed2110cc12e74e branch: master author: Serhiy Storchaka committer: GitHub date: 2020-10-09T23:00:45+03:00 summary: bpo-41985: Add _PyLong_FileDescriptor_Converter and AC converter for "fildes". (GH-22620) files: M Include/cpython/fileobject.h M Modules/clinic/fcntlmodule.c.h M Modules/clinic/posixmodule.c.h M Modules/clinic/selectmodule.c.h M Modules/fcntlmodule.c M Modules/posixmodule.c M Modules/selectmodule.c M Modules/termios.c M Objects/fileobject.c M Tools/clinic/clinic.py diff --git a/Include/cpython/fileobject.h b/Include/cpython/fileobject.h index 4f2408c7e8760..fb54cabac75f8 100644 --- a/Include/cpython/fileobject.h +++ b/Include/cpython/fileobject.h @@ -22,3 +22,5 @@ typedef PyObject * (*Py_OpenCodeHookFunction)(PyObject *, void *); PyAPI_FUNC(PyObject *) PyFile_OpenCode(const char *utf8path); PyAPI_FUNC(PyObject *) PyFile_OpenCodeObject(PyObject *path); PyAPI_FUNC(int) PyFile_SetOpenCodeHook(Py_OpenCodeHookFunction hook, void *userData); + +PyAPI_FUNC(int) _PyLong_FileDescriptor_Converter(PyObject *, void *); diff --git a/Modules/clinic/fcntlmodule.c.h b/Modules/clinic/fcntlmodule.c.h index c6bf45fa494f0..adf527fd44311 100644 --- a/Modules/clinic/fcntlmodule.c.h +++ b/Modules/clinic/fcntlmodule.c.h @@ -35,7 +35,7 @@ fcntl_fcntl(PyObject *module, PyObject *const *args, Py_ssize_t nargs) if (!_PyArg_CheckPositional("fcntl", nargs, 2, 3)) { goto exit; } - if (!conv_descriptor(args[0], &fd)) { + if (!_PyLong_FileDescriptor_Converter(args[0], &fd)) { goto exit; } code = _PyLong_AsInt(args[1]); @@ -105,7 +105,7 @@ fcntl_ioctl(PyObject *module, PyObject *const *args, Py_ssize_t nargs) if (!_PyArg_CheckPositional("ioctl", nargs, 2, 4)) { goto exit; } - if (!conv_descriptor(args[0], &fd)) { + if (!_PyLong_FileDescriptor_Converter(args[0], &fd)) { goto exit; } code = (unsigned int)PyLong_AsUnsignedLongMask(args[1]); @@ -155,7 +155,7 @@ fcntl_flock(PyObject *module, PyObject *const *args, Py_ssize_t nargs) if (!_PyArg_CheckPositional("flock", nargs, 2, 2)) { goto exit; } - if (!conv_descriptor(args[0], &fd)) { + if (!_PyLong_FileDescriptor_Converter(args[0], &fd)) { goto exit; } code = _PyLong_AsInt(args[1]); @@ -215,7 +215,7 @@ fcntl_lockf(PyObject *module, PyObject *const *args, Py_ssize_t nargs) if (!_PyArg_CheckPositional("lockf", nargs, 2, 5)) { goto exit; } - if (!conv_descriptor(args[0], &fd)) { + if (!_PyLong_FileDescriptor_Converter(args[0], &fd)) { goto exit; } code = _PyLong_AsInt(args[1]); @@ -243,4 +243,4 @@ fcntl_lockf(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=91c2295402509595 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=8ea34bd0f7cf25ec input=a9049054013a1b77]*/ diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h index c15def0a0f2b8..df680d5738c8e 100644 --- a/Modules/clinic/posixmodule.c.h +++ b/Modules/clinic/posixmodule.c.h @@ -357,7 +357,7 @@ os_fchdir(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *k if (!args) { goto exit; } - if (!fildes_converter(args[0], &fd)) { + if (!_PyLong_FileDescriptor_Converter(args[0], &fd)) { goto exit; } return_value = os_fchdir_impl(module, fd); @@ -727,7 +727,7 @@ os_fsync(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kw if (!args) { goto exit; } - if (!fildes_converter(args[0], &fd)) { + if (!_PyLong_FileDescriptor_Converter(args[0], &fd)) { goto exit; } return_value = os_fsync_impl(module, fd); @@ -787,7 +787,7 @@ os_fdatasync(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject if (!args) { goto exit; } - if (!fildes_converter(args[0], &fd)) { + if (!_PyLong_FileDescriptor_Converter(args[0], &fd)) { goto exit; } return_value = os_fdatasync_impl(module, fd); @@ -6821,7 +6821,7 @@ os_fpathconf(PyObject *module, PyObject *const *args, Py_ssize_t nargs) if (!_PyArg_CheckPositional("fpathconf", nargs, 2, 2)) { goto exit; } - if (!fildes_converter(args[0], &fd)) { + if (!_PyLong_FileDescriptor_Converter(args[0], &fd)) { goto exit; } if (!conv_path_confname(args[1], &name)) { @@ -8919,4 +8919,4 @@ os_waitstatus_to_exitcode(PyObject *module, PyObject *const *args, Py_ssize_t na #ifndef OS_WAITSTATUS_TO_EXITCODE_METHODDEF #define OS_WAITSTATUS_TO_EXITCODE_METHODDEF #endif /* !defined(OS_WAITSTATUS_TO_EXITCODE_METHODDEF) */ -/*[clinic end generated code: output=a0fbdea47249ee0c input=a9049054013a1b77]*/ +/*[clinic end generated code: output=936f33448cd66ccb input=a9049054013a1b77]*/ diff --git a/Modules/clinic/selectmodule.c.h b/Modules/clinic/selectmodule.c.h index 3a06d6d0ec900..00a78c48477b4 100644 --- a/Modules/clinic/selectmodule.c.h +++ b/Modules/clinic/selectmodule.c.h @@ -92,7 +92,7 @@ select_poll_register(pollObject *self, PyObject *const *args, Py_ssize_t nargs) if (!_PyArg_CheckPositional("register", nargs, 1, 2)) { goto exit; } - if (!fildes_converter(args[0], &fd)) { + if (!_PyLong_FileDescriptor_Converter(args[0], &fd)) { goto exit; } if (nargs < 2) { @@ -140,7 +140,7 @@ select_poll_modify(pollObject *self, PyObject *const *args, Py_ssize_t nargs) if (!_PyArg_CheckPositional("modify", nargs, 2, 2)) { goto exit; } - if (!fildes_converter(args[0], &fd)) { + if (!_PyLong_FileDescriptor_Converter(args[0], &fd)) { goto exit; } if (!_PyLong_UnsignedShort_Converter(args[1], &eventmask)) { @@ -174,7 +174,7 @@ select_poll_unregister(pollObject *self, PyObject *arg) PyObject *return_value = NULL; int fd; - if (!fildes_converter(arg, &fd)) { + if (!_PyLong_FileDescriptor_Converter(arg, &fd)) { goto exit; } return_value = select_poll_unregister_impl(self, fd); @@ -256,7 +256,7 @@ select_devpoll_register(devpollObject *self, PyObject *const *args, Py_ssize_t n if (!_PyArg_CheckPositional("register", nargs, 1, 2)) { goto exit; } - if (!fildes_converter(args[0], &fd)) { + if (!_PyLong_FileDescriptor_Converter(args[0], &fd)) { goto exit; } if (nargs < 2) { @@ -306,7 +306,7 @@ select_devpoll_modify(devpollObject *self, PyObject *const *args, Py_ssize_t nar if (!_PyArg_CheckPositional("modify", nargs, 1, 2)) { goto exit; } - if (!fildes_converter(args[0], &fd)) { + if (!_PyLong_FileDescriptor_Converter(args[0], &fd)) { goto exit; } if (nargs < 2) { @@ -344,7 +344,7 @@ select_devpoll_unregister(devpollObject *self, PyObject *arg) PyObject *return_value = NULL; int fd; - if (!fildes_converter(arg, &fd)) { + if (!_PyLong_FileDescriptor_Converter(arg, &fd)) { goto exit; } return_value = select_devpoll_unregister_impl(self, fd); @@ -668,7 +668,7 @@ select_epoll_register(pyEpoll_Object *self, PyObject *const *args, Py_ssize_t na if (!args) { goto exit; } - if (!fildes_converter(args[0], &fd)) { + if (!_PyLong_FileDescriptor_Converter(args[0], &fd)) { goto exit; } if (!noptargs) { @@ -721,7 +721,7 @@ select_epoll_modify(pyEpoll_Object *self, PyObject *const *args, Py_ssize_t narg if (!args) { goto exit; } - if (!fildes_converter(args[0], &fd)) { + if (!_PyLong_FileDescriptor_Converter(args[0], &fd)) { goto exit; } eventmask = (unsigned int)PyLong_AsUnsignedLongMask(args[1]); @@ -766,7 +766,7 @@ select_epoll_unregister(pyEpoll_Object *self, PyObject *const *args, Py_ssize_t if (!args) { goto exit; } - if (!fildes_converter(args[0], &fd)) { + if (!_PyLong_FileDescriptor_Converter(args[0], &fd)) { goto exit; } return_value = select_epoll_unregister_impl(self, fd); @@ -1179,4 +1179,4 @@ select_kqueue_control(kqueue_queue_Object *self, PyObject *const *args, Py_ssize #ifndef SELECT_KQUEUE_CONTROL_METHODDEF #define SELECT_KQUEUE_CONTROL_METHODDEF #endif /* !defined(SELECT_KQUEUE_CONTROL_METHODDEF) */ -/*[clinic end generated code: output=7144233c42e18279 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=162f4f4efa850416 input=a9049054013a1b77]*/ diff --git a/Modules/fcntlmodule.c b/Modules/fcntlmodule.c index 39baea01ec84e..afd28106faf4b 100644 --- a/Modules/fcntlmodule.c +++ b/Modules/fcntlmodule.c @@ -20,24 +20,12 @@ module fcntl [clinic start generated code]*/ /*[clinic end generated code: output=da39a3ee5e6b4b0d input=124b58387c158179]*/ -static int -conv_descriptor(PyObject *object, int *target) -{ - int fd = PyObject_AsFileDescriptor(object); - - if (fd < 0) - return 0; - *target = fd; - return 1; -} - -/* Must come after conv_descriptor definition. */ #include "clinic/fcntlmodule.c.h" /*[clinic input] fcntl.fcntl - fd: object(type='int', converter='conv_descriptor') + fd: fildes cmd as code: int arg: object(c_default='NULL') = 0 / @@ -57,7 +45,7 @@ corresponding to the return value of the fcntl call in the C code. static PyObject * fcntl_fcntl_impl(PyObject *module, int fd, int code, PyObject *arg) -/*[clinic end generated code: output=888fc93b51c295bd input=8cefbe59b29efbe2]*/ +/*[clinic end generated code: output=888fc93b51c295bd input=7955340198e5f334]*/ { unsigned int int_arg = 0; int ret; @@ -116,7 +104,7 @@ fcntl_fcntl_impl(PyObject *module, int fd, int code, PyObject *arg) /*[clinic input] fcntl.ioctl - fd: object(type='int', converter='conv_descriptor') + fd: fildes request as code: unsigned_int(bitwise=True) arg as ob_arg: object(c_default='NULL') = 0 mutate_flag as mutate_arg: bool = True @@ -155,7 +143,7 @@ code. static PyObject * fcntl_ioctl_impl(PyObject *module, int fd, unsigned int code, PyObject *ob_arg, int mutate_arg) -/*[clinic end generated code: output=7f7f5840c65991be input=ede70c433cccbbb2]*/ +/*[clinic end generated code: output=7f7f5840c65991be input=967b4a4cbeceb0a8]*/ { #define IOCTL_BUFSZ 1024 /* We use the unsigned non-checked 'I' format for the 'code' parameter @@ -280,7 +268,7 @@ fcntl_ioctl_impl(PyObject *module, int fd, unsigned int code, /*[clinic input] fcntl.flock - fd: object(type='int', converter='conv_descriptor') + fd: fildes operation as code: int / @@ -292,7 +280,7 @@ function is emulated using fcntl()). static PyObject * fcntl_flock_impl(PyObject *module, int fd, int code) -/*[clinic end generated code: output=84059e2b37d2fc64 input=b70a0a41ca22a8a0]*/ +/*[clinic end generated code: output=84059e2b37d2fc64 input=0bfc00f795953452]*/ { int ret; int async_err = 0; @@ -346,7 +334,7 @@ fcntl_flock_impl(PyObject *module, int fd, int code) /*[clinic input] fcntl.lockf - fd: object(type='int', converter='conv_descriptor') + fd: fildes cmd as code: int len as lenobj: object(c_default='NULL') = 0 start as startobj: object(c_default='NULL') = 0 @@ -380,7 +368,7 @@ starts. `whence` is as with fileobj.seek(), specifically: static PyObject * fcntl_lockf_impl(PyObject *module, int fd, int code, PyObject *lenobj, PyObject *startobj, int whence) -/*[clinic end generated code: output=4985e7a172e7461a input=3a5dc01b04371f1a]*/ +/*[clinic end generated code: output=4985e7a172e7461a input=5480479fc63a04b8]*/ { int ret; int async_err = 0; diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 7c496938ed4c5..165625c9a670a 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -1634,18 +1634,6 @@ path_error2(path_t *path, path_t *path2) /* POSIX generic methods */ -static int -fildes_converter(PyObject *o, void *p) -{ - int fd; - int *pointer = (int *)p; - fd = PyObject_AsFileDescriptor(o); - if (fd < 0) - return 0; - *pointer = fd; - return 1; -} - static PyObject * posix_fildes_fd(int fd, int (*func)(int)) { @@ -2642,10 +2630,6 @@ class dir_fd_converter(CConverter): else: self.converter = 'dir_fd_converter' -class fildes_converter(CConverter): - type = 'int' - converter = 'fildes_converter' - class uid_t_converter(CConverter): type = "uid_t" converter = '_Py_Uid_Converter' @@ -2708,7 +2692,7 @@ class sysconf_confname_converter(path_confname_converter): converter="conv_sysconf_confname" [python start generated code]*/ -/*[python end generated code: output=da39a3ee5e6b4b0d input=f1c8ae8d744f6c8b]*/ +/*[python end generated code: output=da39a3ee5e6b4b0d input=3338733161aa7879]*/ /*[clinic input] diff --git a/Modules/selectmodule.c b/Modules/selectmodule.c index 13ffe09c6d4f8..fe852f93c37d1 100644 --- a/Modules/selectmodule.c +++ b/Modules/selectmodule.c @@ -88,25 +88,6 @@ class select.kqueue "kqueue_queue_Object *" "_selectstate_global->kqueue_queue_T [clinic start generated code]*/ /*[clinic end generated code: output=da39a3ee5e6b4b0d input=41071028e0ede093]*/ -static int -fildes_converter(PyObject *o, void *p) -{ - int fd; - int *pointer = (int *)p; - fd = PyObject_AsFileDescriptor(o); - if (fd == -1) - return 0; - *pointer = fd; - return 1; -} - -/*[python input] -class fildes_converter(CConverter): - type = 'int' - converter = 'fildes_converter' -[python start generated code]*/ -/*[python end generated code: output=da39a3ee5e6b4b0d input=ca54eb5aa476e20a]*/ - /* list of Python objects and their file descriptor */ typedef struct { PyObject *obj; /* owned reference */ diff --git a/Modules/termios.c b/Modules/termios.c index cc0d5853f85e3..79b60ffaaba4a 100644 --- a/Modules/termios.c +++ b/Modules/termios.c @@ -51,18 +51,6 @@ get_termios_state(PyObject *module) return (termiosmodulestate *)state; } -static int fdconv(PyObject* obj, void* p) -{ - int fd; - - fd = PyObject_AsFileDescriptor(obj); - if (fd >= 0) { - *(int*)p = fd; - return 1; - } - return 0; -} - static struct PyModuleDef termiosmodule; PyDoc_STRVAR(termios_tcgetattr__doc__, @@ -81,7 +69,7 @@ termios_tcgetattr(PyObject *module, PyObject *args) { int fd; if (!PyArg_ParseTuple(args, "O&:tcgetattr", - fdconv, (void*)&fd)) { + _PyLong_FileDescriptor_Converter, (void*)&fd)) { return NULL; } @@ -160,7 +148,7 @@ termios_tcsetattr(PyObject *module, PyObject *args) int fd, when; PyObject *term; if (!PyArg_ParseTuple(args, "O&iO:tcsetattr", - fdconv, &fd, &when, &term)) { + _PyLong_FileDescriptor_Converter, &fd, &when, &term)) { return NULL; } @@ -233,7 +221,7 @@ termios_tcsendbreak(PyObject *module, PyObject *args) { int fd, duration; if (!PyArg_ParseTuple(args, "O&i:tcsendbreak", - fdconv, &fd, &duration)) { + _PyLong_FileDescriptor_Converter, &fd, &duration)) { return NULL; } @@ -255,7 +243,7 @@ termios_tcdrain(PyObject *module, PyObject *args) { int fd; if (!PyArg_ParseTuple(args, "O&:tcdrain", - fdconv, &fd)) { + _PyLong_FileDescriptor_Converter, &fd)) { return NULL; } @@ -280,7 +268,7 @@ termios_tcflush(PyObject *module, PyObject *args) { int fd, queue; if (!PyArg_ParseTuple(args, "O&i:tcflush", - fdconv, &fd, &queue)) { + _PyLong_FileDescriptor_Converter, &fd, &queue)) { return NULL; } @@ -305,7 +293,7 @@ termios_tcflow(PyObject *module, PyObject *args) { int fd, action; if (!PyArg_ParseTuple(args, "O&i:tcflow", - fdconv, &fd, &action)) { + _PyLong_FileDescriptor_Converter, &fd, &action)) { return NULL; } diff --git a/Objects/fileobject.c b/Objects/fileobject.c index 1c6ecaf82c24e..9b89448006e84 100644 --- a/Objects/fileobject.c +++ b/Objects/fileobject.c @@ -223,6 +223,17 @@ PyObject_AsFileDescriptor(PyObject *o) return fd; } +int +_PyLong_FileDescriptor_Converter(PyObject *o, void *ptr) +{ + int fd = PyObject_AsFileDescriptor(o); + if (fd == -1) { + return 0; + } + *(int *)ptr = fd; + return 1; +} + /* ** Py_UniversalNewlineFgets is an fgets variation that understands ** all of \r, \n and \r\n conventions. diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index 1bbbd4f9fb193..5f2eb53e6a092 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -3103,6 +3103,19 @@ def parse_arg(self, argname, displayname): return super().parse_arg(argname, displayname) +class fildes_converter(CConverter): + type = 'int' + converter = '_PyLong_FileDescriptor_Converter' + + def _parse_arg(self, argname, displayname): + return """ + {paramname} = PyObject_AsFileDescriptor({argname}); + if ({paramname} == -1) {{{{ + goto exit; + }}}} + """.format(argname=argname, paramname=self.name) + + class float_converter(CConverter): type = 'float' default_type = float From webhook-mailer at python.org Fri Oct 9 16:21:04 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Fri, 09 Oct 2020 20:21:04 -0000 Subject: [Python-checkins] bpo-41831: Restore str implementation of __str__ in tkinter.EventType (GH-22355) Message-ID: https://github.com/python/cpython/commit/1f75fc7a9d9efbced2e0b0001ac148268a626d63 commit: 1f75fc7a9d9efbced2e0b0001ac148268a626d63 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-09T13:20:48-07:00 summary: bpo-41831: Restore str implementation of __str__ in tkinter.EventType (GH-22355) (cherry picked from commit eb38c6b7aa35d3003ec0958533421902d19ce408) Co-authored-by: Serhiy Storchaka files: A Misc/NEWS.d/next/Library/2020-09-22-11-07-50.bpo-41831.k-Eop_.rst M Lib/tkinter/__init__.py diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py index 11ced6a3aff81..2175afcffa435 100644 --- a/Lib/tkinter/__init__.py +++ b/Lib/tkinter/__init__.py @@ -185,8 +185,7 @@ class EventType(str, enum.Enum): Deactivate = '37' MouseWheel = '38' - def __str__(self): - return self.name + __str__ = str.__str__ class Event: @@ -266,7 +265,7 @@ def __repr__(self): 'num', 'delta', 'focus', 'x', 'y', 'width', 'height') return '<%s event%s>' % ( - self.type, + getattr(self.type, 'name', self.type), ''.join(' %s=%s' % (k, attrs[k]) for k in keys if k in attrs) ) diff --git a/Misc/NEWS.d/next/Library/2020-09-22-11-07-50.bpo-41831.k-Eop_.rst b/Misc/NEWS.d/next/Library/2020-09-22-11-07-50.bpo-41831.k-Eop_.rst new file mode 100644 index 0000000000000..84a3f5253a060 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-09-22-11-07-50.bpo-41831.k-Eop_.rst @@ -0,0 +1,3 @@ +``str()`` for the ``type`` attribute of the ``tkinter.Event`` object always +returns now the numeric code returned by Tk instead of the name of the event +type. From webhook-mailer at python.org Fri Oct 9 16:21:19 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Fri, 09 Oct 2020 20:21:19 -0000 Subject: [Python-checkins] bpo-41831: Restore str implementation of __str__ in tkinter.EventType (GH-22355) Message-ID: https://github.com/python/cpython/commit/f8ebb7b62ea266df5c6e0f910b7a6b0eb73deee4 commit: f8ebb7b62ea266df5c6e0f910b7a6b0eb73deee4 branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-09T13:21:15-07:00 summary: bpo-41831: Restore str implementation of __str__ in tkinter.EventType (GH-22355) (cherry picked from commit eb38c6b7aa35d3003ec0958533421902d19ce408) Co-authored-by: Serhiy Storchaka files: A Misc/NEWS.d/next/Library/2020-09-22-11-07-50.bpo-41831.k-Eop_.rst M Lib/tkinter/__init__.py diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py index 479eb016da7f0..b685901d4df83 100644 --- a/Lib/tkinter/__init__.py +++ b/Lib/tkinter/__init__.py @@ -185,8 +185,7 @@ class EventType(str, enum.Enum): Deactivate = '37' MouseWheel = '38' - def __str__(self): - return self.name + __str__ = str.__str__ class Event: @@ -266,7 +265,7 @@ def __repr__(self): 'num', 'delta', 'focus', 'x', 'y', 'width', 'height') return '<%s event%s>' % ( - self.type, + getattr(self.type, 'name', self.type), ''.join(' %s=%s' % (k, attrs[k]) for k in keys if k in attrs) ) diff --git a/Misc/NEWS.d/next/Library/2020-09-22-11-07-50.bpo-41831.k-Eop_.rst b/Misc/NEWS.d/next/Library/2020-09-22-11-07-50.bpo-41831.k-Eop_.rst new file mode 100644 index 0000000000000..84a3f5253a060 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-09-22-11-07-50.bpo-41831.k-Eop_.rst @@ -0,0 +1,3 @@ +``str()`` for the ``type`` attribute of the ``tkinter.Event`` object always +returns now the numeric code returned by Tk instead of the name of the event +type. From webhook-mailer at python.org Fri Oct 9 20:15:24 2020 From: webhook-mailer at python.org (Vladimir Matveev) Date: Sat, 10 Oct 2020 00:15:24 -0000 Subject: [Python-checkins] bpo-41756: Add PyIter_Send function (#22443) Message-ID: https://github.com/python/cpython/commit/037245c5ac46c3436f617a1f5d965929754be239 commit: 037245c5ac46c3436f617a1f5d965929754be239 branch: master author: Vladimir Matveev committer: GitHub date: 2020-10-09T17:15:15-07:00 summary: bpo-41756: Add PyIter_Send function (#22443) files: A Misc/NEWS.d/next/C API/2020-09-28-14-31-07.bpo-41756.ZZ5wJG.rst M Doc/c-api/gen.rst M Doc/c-api/iter.rst M Doc/data/refcounts.dat M Doc/whatsnew/3.10.rst M Include/abstract.h M Include/genobject.h M Modules/_asynciomodule.c M Modules/_testcapimodule.c M Objects/abstract.c M Objects/genobject.c M Python/ceval.c diff --git a/Doc/c-api/gen.rst b/Doc/c-api/gen.rst index e098425e6364d..600f53486f79d 100644 --- a/Doc/c-api/gen.rst +++ b/Doc/c-api/gen.rst @@ -15,11 +15,6 @@ than explicitly calling :c:func:`PyGen_New` or :c:func:`PyGen_NewWithQualName`. The C structure used for generator objects. -.. c:type:: PySendResult - - The enum value used to represent different results of :c:func:`PyGen_Send`. - - .. c:var:: PyTypeObject PyGen_Type The type object corresponding to generator objects. diff --git a/Doc/c-api/iter.rst b/Doc/c-api/iter.rst index a2992b3452f91..a068a43c86b6c 100644 --- a/Doc/c-api/iter.rst +++ b/Doc/c-api/iter.rst @@ -44,3 +44,17 @@ something like this:: else { /* continue doing useful work */ } + + +.. c:type:: PySendResult + + The enum value used to represent different results of :c:func:`PyIter_Send`. + + +.. c:function:: PySendResult PyIter_Send(PyObject *iter, PyObject *arg, PyObject **presult) + + Sends the *arg* value into the iterator *iter*. Returns: + + - ``PYGEN_RETURN`` if iterator returns. Return value is returned via *presult*. + - ``PYGEN_NEXT`` if iterator yields. Yielded value is returned via *presult*. + - ``PYGEN_ERROR`` if iterator has raised and exception. *presult* is set to ``NULL``. diff --git a/Doc/data/refcounts.dat b/Doc/data/refcounts.dat index 6b1bde37967ae..87ce5d03d0064 100644 --- a/Doc/data/refcounts.dat +++ b/Doc/data/refcounts.dat @@ -1081,6 +1081,11 @@ PyIter_Check:PyObject*:o:0: PyIter_Next:PyObject*::+1: PyIter_Next:PyObject*:o:0: +PyIter_Send:int::: +PyIter_Send:PyObject*:iter:0: +PyIter_Send:PyObject*:arg:0: +PyIter_Send:PyObject**:presult:+1: + PyList_Append:int::: PyList_Append:PyObject*:list:0: PyList_Append:PyObject*:item:+1: diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index 7401ba722fb4f..1c50978a8b750 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -314,6 +314,10 @@ New Features search function. (Contributed by Hai Shi in :issue:`41842`.) +* The :c:func:`PyIter_Send` and :c:func:`PyGen_Send` functions were added to allow + sending value into iterator without raising ``StopIteration`` exception. + (Contributed by Vladimir Matveev in :issue:`41756`.) + Porting to Python 3.10 ---------------------- diff --git a/Include/abstract.h b/Include/abstract.h index a23b7dc78f480..716cd4b5ebbba 100644 --- a/Include/abstract.h +++ b/Include/abstract.h @@ -338,6 +338,22 @@ PyAPI_FUNC(int) PyIter_Check(PyObject *); NULL with an exception means an error occurred. */ PyAPI_FUNC(PyObject *) PyIter_Next(PyObject *); +typedef enum { + PYGEN_RETURN = 0, + PYGEN_ERROR = -1, + PYGEN_NEXT = 1, +} PySendResult; + +/* Takes generator, coroutine or iterator object and sends the value into it. + Returns: + - PYGEN_RETURN (0) if generator has returned. + 'result' parameter is filled with return value + - PYGEN_ERROR (-1) if exception was raised. + 'result' parameter is NULL + - PYGEN_NEXT (1) if generator has yielded. + 'result' parameter is filled with yielded value. */ +PyAPI_FUNC(PySendResult) PyIter_Send(PyObject *, PyObject *, PyObject **); + /* === Number Protocol ================================================== */ diff --git a/Include/genobject.h b/Include/genobject.h index 7488054c68fcd..e719b25a80072 100644 --- a/Include/genobject.h +++ b/Include/genobject.h @@ -9,6 +9,7 @@ extern "C" { #endif #include "pystate.h" /* _PyErr_StackItem */ +#include "abstract.h" /* PySendResult */ /* _PyGenObject_HEAD defines the initial segment of generator and coroutine objects. */ @@ -41,16 +42,9 @@ PyAPI_FUNC(PyObject *) PyGen_NewWithQualName(PyFrameObject *, PyObject *name, PyObject *qualname); PyAPI_FUNC(int) _PyGen_SetStopIterationValue(PyObject *); PyAPI_FUNC(int) _PyGen_FetchStopIterationValue(PyObject **); -PyAPI_FUNC(PyObject *) _PyGen_Send(PyGenObject *, PyObject *); PyObject *_PyGen_yf(PyGenObject *); PyAPI_FUNC(void) _PyGen_Finalize(PyObject *self); -typedef enum { - PYGEN_RETURN = 0, - PYGEN_ERROR = -1, - PYGEN_NEXT = 1, -} PySendResult; - /* Sends the value into the generator or the coroutine. Returns: - PYGEN_RETURN (0) if generator has returned. 'result' parameter is filled with return value diff --git a/Misc/NEWS.d/next/C API/2020-09-28-14-31-07.bpo-41756.ZZ5wJG.rst b/Misc/NEWS.d/next/C API/2020-09-28-14-31-07.bpo-41756.ZZ5wJG.rst new file mode 100644 index 0000000000000..f7e27b4401529 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2020-09-28-14-31-07.bpo-41756.ZZ5wJG.rst @@ -0,0 +1,3 @@ +Add `PyIter_Send` function to allow sending value into +generator/coroutine/iterator without raising StopIteration exception to +signal return. diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 2151f20281a31..f01e5884c6fe2 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -16,7 +16,6 @@ _Py_IDENTIFIER(add_done_callback); _Py_IDENTIFIER(call_soon); _Py_IDENTIFIER(cancel); _Py_IDENTIFIER(get_event_loop); -_Py_IDENTIFIER(send); _Py_IDENTIFIER(throw); @@ -2695,13 +2694,7 @@ task_step_impl(TaskObj *task, PyObject *exc) int gen_status = PYGEN_ERROR; if (exc == NULL) { - if (PyGen_CheckExact(coro) || PyCoro_CheckExact(coro)) { - gen_status = PyGen_Send((PyGenObject*)coro, Py_None, &result); - } - else { - result = _PyObject_CallMethodIdOneArg(coro, &PyId_send, Py_None); - gen_status = gen_status_from_result(&result); - } + gen_status = PyIter_Send(coro, Py_None, &result); } else { result = _PyObject_CallMethodIdOneArg(coro, &PyId_throw, exc); diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 0e098779696b7..8c7544fa90e28 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -5028,6 +5028,7 @@ dict_get_version(PyObject *self, PyObject *args) static PyObject * raise_SIGINT_then_send_None(PyObject *self, PyObject *args) { + _Py_IDENTIFIER(send); PyGenObject *gen; if (!PyArg_ParseTuple(args, "O!", &PyGen_Type, &gen)) @@ -5044,7 +5045,7 @@ raise_SIGINT_then_send_None(PyObject *self, PyObject *args) because we check for signals before every bytecode operation. */ raise(SIGINT); - return _PyGen_Send(gen, Py_None); + return _PyObject_CallMethodIdOneArg((PyObject *)gen, &PyId_send, Py_None); } diff --git a/Objects/abstract.c b/Objects/abstract.c index 2ab3371a3f3cb..502a2d64e25e1 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -2669,6 +2669,30 @@ PyIter_Next(PyObject *iter) return result; } +PySendResult +PyIter_Send(PyObject *iter, PyObject *arg, PyObject **result) +{ + _Py_IDENTIFIER(send); + assert(result != NULL); + + if (PyGen_CheckExact(iter) || PyCoro_CheckExact(iter)) { + return PyGen_Send((PyGenObject *)iter, arg, result); + } + + if (arg == Py_None && PyIter_Check(iter)) { + *result = Py_TYPE(iter)->tp_iternext(iter); + } + else { + *result = _PyObject_CallMethodIdOneArg(iter, &PyId_send, arg); + } + if (*result != NULL) { + return PYGEN_NEXT; + } + if (_PyGen_FetchStopIterationValue(result) == 0) { + return PYGEN_RETURN; + } + return PYGEN_ERROR; +} /* * Flatten a sequence of bytes() objects into a C array of diff --git a/Objects/genobject.c b/Objects/genobject.c index f0943ae847c54..eb134ebf4bc87 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -308,12 +308,6 @@ gen_send(PyGenObject *gen, PyObject *arg) return gen_send_ex(gen, arg, 0, 0); } -PyObject * -_PyGen_Send(PyGenObject *gen, PyObject *arg) -{ - return gen_send(gen, arg); -} - PyDoc_STRVAR(close_doc, "close() -> raise GeneratorExit inside generator."); @@ -1012,7 +1006,7 @@ PyDoc_STRVAR(coro_close_doc, "close() -> raise GeneratorExit inside coroutine."); static PyMethodDef coro_methods[] = { - {"send",(PyCFunction)_PyGen_Send, METH_O, coro_send_doc}, + {"send",(PyCFunction)gen_send, METH_O, coro_send_doc}, {"throw",(PyCFunction)gen_throw, METH_VARARGS, coro_throw_doc}, {"close",(PyCFunction)gen_close, METH_NOARGS, coro_close_doc}, {NULL, NULL} /* Sentinel */ diff --git a/Python/ceval.c b/Python/ceval.c index 500c588e3c2af..762de577e6b55 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2210,24 +2210,17 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) case TARGET(YIELD_FROM): { PyObject *v = POP(); PyObject *receiver = TOP(); - int is_gen_or_coro = PyGen_CheckExact(receiver) || PyCoro_CheckExact(receiver); - int gen_status; - if (tstate->c_tracefunc == NULL && is_gen_or_coro) { - gen_status = PyGen_Send((PyGenObject *)receiver, v, &retval); + PySendResult gen_status; + if (tstate->c_tracefunc == NULL) { + gen_status = PyIter_Send(receiver, v, &retval); } else { - if (is_gen_or_coro) { - retval = _PyGen_Send((PyGenObject *)receiver, v); + _Py_IDENTIFIER(send); + if (v == Py_None && PyIter_Check(receiver)) { + retval = Py_TYPE(receiver)->tp_iternext(receiver); } else { - _Py_IDENTIFIER(send); - if (v == Py_None) { - retval = Py_TYPE(receiver)->tp_iternext(receiver); - } - else { - retval = _PyObject_CallMethodIdOneArg(receiver, &PyId_send, v); - } + retval = _PyObject_CallMethodIdOneArg(receiver, &PyId_send, v); } - if (retval == NULL) { if (tstate->c_tracefunc != NULL && _PyErr_ExceptionMatches(tstate, PyExc_StopIteration)) From webhook-mailer at python.org Fri Oct 9 22:38:50 2020 From: webhook-mailer at python.org (Xie Yanbo) Date: Sat, 10 Oct 2020 02:38:50 -0000 Subject: [Python-checkins] Fix incorrect parameter name (GH-22613) Message-ID: https://github.com/python/cpython/commit/a42759351bff7b07fa8bf2cece0088f8539721d1 commit: a42759351bff7b07fa8bf2cece0088f8539721d1 branch: master author: Xie Yanbo committer: GitHub date: 2020-10-09T19:38:43-07:00 summary: Fix incorrect parameter name (GH-22613) Automerge-Triggered-By: @Mariatta files: M Doc/library/shutil.rst diff --git a/Doc/library/shutil.rst b/Doc/library/shutil.rst index ecc3309ed5cc0..3f5122760ee16 100644 --- a/Doc/library/shutil.rst +++ b/Doc/library/shutil.rst @@ -349,7 +349,7 @@ Directory and files operations will be created in or as *dst* and *src* will be removed. If *copy_function* is given, it must be a callable that takes two arguments - *src* and *dst*, and will be used to copy *src* to *dest* if + *src* and *dst*, and will be used to copy *src* to *dst* if :func:`os.rename` cannot be used. If the source is a directory, :func:`copytree` is called, passing it the :func:`copy_function`. The default *copy_function* is :func:`copy2`. Using :func:`~shutil.copy` as the From webhook-mailer at python.org Sat Oct 10 00:39:13 2020 From: webhook-mailer at python.org (Karthikeyan Singaravelan) Date: Sat, 10 Oct 2020 04:39:13 -0000 Subject: [Python-checkins] [3.9] bpo-41371: Handle lzma lib import error in test_zoneinfo.py (GH-21734) (GH-22039) Message-ID: https://github.com/python/cpython/commit/20bdeedfb4ebd250dad9834c96cb858d83c896cb commit: 20bdeedfb4ebd250dad9834c96cb858d83c896cb branch: 3.9 author: Karthikeyan Singaravelan committer: GitHub date: 2020-10-10T10:09:09+05:30 summary: [3.9] bpo-41371: Handle lzma lib import error in test_zoneinfo.py (GH-21734) (GH-22039) (cherry picked from commit 5f0769a) Co-authored-by: Nathan M files: M Lib/test/test_zoneinfo/test_zoneinfo.py diff --git a/Lib/test/test_zoneinfo/test_zoneinfo.py b/Lib/test/test_zoneinfo/test_zoneinfo.py index 85703269a1336..d4704b75d389d 100644 --- a/Lib/test/test_zoneinfo/test_zoneinfo.py +++ b/Lib/test/test_zoneinfo/test_zoneinfo.py @@ -6,7 +6,6 @@ import importlib.metadata import io import json -import lzma import os import pathlib import pickle @@ -20,7 +19,9 @@ from . import _support as test_support from ._support import OS_ENV_LOCK, TZPATH_TEST_LOCK, ZoneInfoTestBase +from test.support import import_module +lzma = import_module('lzma') py_zoneinfo, c_zoneinfo = test_support.get_modules() try: From webhook-mailer at python.org Sat Oct 10 00:50:31 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Sat, 10 Oct 2020 04:50:31 -0000 Subject: [Python-checkins] Fix incorrect parameter name (GH-22613) Message-ID: https://github.com/python/cpython/commit/ae485f5240daee76650935d78733fa558d1638f0 commit: ae485f5240daee76650935d78733fa558d1638f0 branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-09T21:50:23-07:00 summary: Fix incorrect parameter name (GH-22613) Automerge-Triggered-By: @Mariatta (cherry picked from commit a42759351bff7b07fa8bf2cece0088f8539721d1) Co-authored-by: Xie Yanbo files: M Doc/library/shutil.rst diff --git a/Doc/library/shutil.rst b/Doc/library/shutil.rst index 25b749e57b27d..00c7d1049e6a0 100644 --- a/Doc/library/shutil.rst +++ b/Doc/library/shutil.rst @@ -349,7 +349,7 @@ Directory and files operations will be created in or as *dst* and *src* will be removed. If *copy_function* is given, it must be a callable that takes two arguments - *src* and *dst*, and will be used to copy *src* to *dest* if + *src* and *dst*, and will be used to copy *src* to *dst* if :func:`os.rename` cannot be used. If the source is a directory, :func:`copytree` is called, passing it the :func:`copy_function`. The default *copy_function* is :func:`copy2`. Using :func:`~shutil.copy` as the From webhook-mailer at python.org Sat Oct 10 10:09:53 2020 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Sat, 10 Oct 2020 14:09:53 -0000 Subject: [Python-checkins] bpo-41986: Add Py_FileSystemDefaultEncodeErrors and Py_UTF8Mode back to limited API (GH-22621) Message-ID: https://github.com/python/cpython/commit/637a09b0d6e3ad4e34e0b5e0fc82f5afeae6f74b commit: 637a09b0d6e3ad4e34e0b5e0fc82f5afeae6f74b branch: master author: Serhiy Storchaka committer: GitHub date: 2020-10-10T17:09:45+03:00 summary: bpo-41986: Add Py_FileSystemDefaultEncodeErrors and Py_UTF8Mode back to limited API (GH-22621) files: A Misc/NEWS.d/next/C API/2020-10-09-22-50-46.bpo-41986.JUPE59.rst M Include/cpython/fileobject.h M Include/fileobject.h diff --git a/Include/cpython/fileobject.h b/Include/cpython/fileobject.h index fb54cabac75f8..cff2243d625e7 100644 --- a/Include/cpython/fileobject.h +++ b/Include/cpython/fileobject.h @@ -4,14 +4,6 @@ PyAPI_FUNC(char *) Py_UniversalNewlineFgets(char *, int, FILE*, PyObject *); -#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03060000 -PyAPI_DATA(const char *) Py_FileSystemDefaultEncodeErrors; -#endif - -#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03070000 -PyAPI_DATA(int) Py_UTF8Mode; -#endif - /* The std printer acts as a preliminary sys.stderr until the new io infrastructure is in place. */ PyAPI_FUNC(PyObject *) PyFile_NewStdPrinter(int); diff --git a/Include/fileobject.h b/Include/fileobject.h index 456887ef9d045..6ec2994aa859b 100644 --- a/Include/fileobject.h +++ b/Include/fileobject.h @@ -20,8 +20,15 @@ PyAPI_FUNC(int) PyObject_AsFileDescriptor(PyObject *); If non-NULL, this is different than the default encoding for strings */ PyAPI_DATA(const char *) Py_FileSystemDefaultEncoding; +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03060000 +PyAPI_DATA(const char *) Py_FileSystemDefaultEncodeErrors; +#endif PyAPI_DATA(int) Py_HasFileSystemDefaultEncoding; +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03070000 +PyAPI_DATA(int) Py_UTF8Mode; +#endif + /* A routine to check if a file descriptor can be select()-ed. */ #ifdef _MSC_VER /* On Windows, any socket fd can be select()-ed, no matter how high */ diff --git a/Misc/NEWS.d/next/C API/2020-10-09-22-50-46.bpo-41986.JUPE59.rst b/Misc/NEWS.d/next/C API/2020-10-09-22-50-46.bpo-41986.JUPE59.rst new file mode 100644 index 0000000000000..d456ba66bafd6 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2020-10-09-22-50-46.bpo-41986.JUPE59.rst @@ -0,0 +1,2 @@ +:c:data:`Py_FileSystemDefaultEncodeErrors` and :c:data:`Py_UTF8Mode` are +available again in limited API. From webhook-mailer at python.org Sat Oct 10 10:28:54 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Sat, 10 Oct 2020 14:28:54 -0000 Subject: [Python-checkins] bpo-41986: Add Py_FileSystemDefaultEncodeErrors and Py_UTF8Mode back to limited API (GH-22621) Message-ID: https://github.com/python/cpython/commit/ff6870f199511f09304e9d3ee7e7d8ed3902ffd1 commit: ff6870f199511f09304e9d3ee7e7d8ed3902ffd1 branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-10T07:28:49-07:00 summary: bpo-41986: Add Py_FileSystemDefaultEncodeErrors and Py_UTF8Mode back to limited API (GH-22621) (cherry picked from commit 637a09b0d6e3ad4e34e0b5e0fc82f5afeae6f74b) Co-authored-by: Serhiy Storchaka files: A Misc/NEWS.d/next/C API/2020-10-09-22-50-46.bpo-41986.JUPE59.rst M Include/cpython/fileobject.h M Include/fileobject.h diff --git a/Include/cpython/fileobject.h b/Include/cpython/fileobject.h index 57eac13c064c2..3005ce1f00f9d 100644 --- a/Include/cpython/fileobject.h +++ b/Include/cpython/fileobject.h @@ -8,14 +8,6 @@ extern "C" { PyAPI_FUNC(char *) Py_UniversalNewlineFgets(char *, int, FILE*, PyObject *); -#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03060000 -PyAPI_DATA(const char *) Py_FileSystemDefaultEncodeErrors; -#endif - -#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03070000 -PyAPI_DATA(int) Py_UTF8Mode; -#endif - /* The std printer acts as a preliminary sys.stderr until the new io infrastructure is in place. */ PyAPI_FUNC(PyObject *) PyFile_NewStdPrinter(int); diff --git a/Include/fileobject.h b/Include/fileobject.h index 456887ef9d045..6ec2994aa859b 100644 --- a/Include/fileobject.h +++ b/Include/fileobject.h @@ -20,8 +20,15 @@ PyAPI_FUNC(int) PyObject_AsFileDescriptor(PyObject *); If non-NULL, this is different than the default encoding for strings */ PyAPI_DATA(const char *) Py_FileSystemDefaultEncoding; +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03060000 +PyAPI_DATA(const char *) Py_FileSystemDefaultEncodeErrors; +#endif PyAPI_DATA(int) Py_HasFileSystemDefaultEncoding; +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03070000 +PyAPI_DATA(int) Py_UTF8Mode; +#endif + /* A routine to check if a file descriptor can be select()-ed. */ #ifdef _MSC_VER /* On Windows, any socket fd can be select()-ed, no matter how high */ diff --git a/Misc/NEWS.d/next/C API/2020-10-09-22-50-46.bpo-41986.JUPE59.rst b/Misc/NEWS.d/next/C API/2020-10-09-22-50-46.bpo-41986.JUPE59.rst new file mode 100644 index 0000000000000..d456ba66bafd6 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2020-10-09-22-50-46.bpo-41986.JUPE59.rst @@ -0,0 +1,2 @@ +:c:data:`Py_FileSystemDefaultEncodeErrors` and :c:data:`Py_UTF8Mode` are +available again in limited API. From webhook-mailer at python.org Sat Oct 10 10:32:14 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Sat, 10 Oct 2020 14:32:14 -0000 Subject: [Python-checkins] bpo-41986: Add Py_FileSystemDefaultEncodeErrors and Py_UTF8Mode back to limited API (GH-22621) Message-ID: https://github.com/python/cpython/commit/ebc5a6b59ece48b490987bdaa2af842c29f5b2f8 commit: ebc5a6b59ece48b490987bdaa2af842c29f5b2f8 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-10T07:32:09-07:00 summary: bpo-41986: Add Py_FileSystemDefaultEncodeErrors and Py_UTF8Mode back to limited API (GH-22621) (cherry picked from commit 637a09b0d6e3ad4e34e0b5e0fc82f5afeae6f74b) Co-authored-by: Serhiy Storchaka files: A Misc/NEWS.d/next/C API/2020-10-09-22-50-46.bpo-41986.JUPE59.rst M Include/cpython/fileobject.h M Include/fileobject.h diff --git a/Include/cpython/fileobject.h b/Include/cpython/fileobject.h index 57eac13c064c2..3005ce1f00f9d 100644 --- a/Include/cpython/fileobject.h +++ b/Include/cpython/fileobject.h @@ -8,14 +8,6 @@ extern "C" { PyAPI_FUNC(char *) Py_UniversalNewlineFgets(char *, int, FILE*, PyObject *); -#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03060000 -PyAPI_DATA(const char *) Py_FileSystemDefaultEncodeErrors; -#endif - -#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03070000 -PyAPI_DATA(int) Py_UTF8Mode; -#endif - /* The std printer acts as a preliminary sys.stderr until the new io infrastructure is in place. */ PyAPI_FUNC(PyObject *) PyFile_NewStdPrinter(int); diff --git a/Include/fileobject.h b/Include/fileobject.h index 456887ef9d045..6ec2994aa859b 100644 --- a/Include/fileobject.h +++ b/Include/fileobject.h @@ -20,8 +20,15 @@ PyAPI_FUNC(int) PyObject_AsFileDescriptor(PyObject *); If non-NULL, this is different than the default encoding for strings */ PyAPI_DATA(const char *) Py_FileSystemDefaultEncoding; +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03060000 +PyAPI_DATA(const char *) Py_FileSystemDefaultEncodeErrors; +#endif PyAPI_DATA(int) Py_HasFileSystemDefaultEncoding; +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03070000 +PyAPI_DATA(int) Py_UTF8Mode; +#endif + /* A routine to check if a file descriptor can be select()-ed. */ #ifdef _MSC_VER /* On Windows, any socket fd can be select()-ed, no matter how high */ diff --git a/Misc/NEWS.d/next/C API/2020-10-09-22-50-46.bpo-41986.JUPE59.rst b/Misc/NEWS.d/next/C API/2020-10-09-22-50-46.bpo-41986.JUPE59.rst new file mode 100644 index 0000000000000..d456ba66bafd6 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2020-10-09-22-50-46.bpo-41986.JUPE59.rst @@ -0,0 +1,2 @@ +:c:data:`Py_FileSystemDefaultEncodeErrors` and :c:data:`Py_UTF8Mode` are +available again in limited API. From webhook-mailer at python.org Sat Oct 10 13:15:06 2020 From: webhook-mailer at python.org (Batuhan Taskaya) Date: Sat, 10 Oct 2020 17:15:06 -0000 Subject: [Python-checkins] bpo-42000: Cleanup the AST related C-code (GH-22641) Message-ID: https://github.com/python/cpython/commit/02a1603f918b9862e164e4fd050c505b16bc9f57 commit: 02a1603f918b9862e164e4fd050c505b16bc9f57 branch: master author: Batuhan Taskaya committer: GitHub date: 2020-10-10T10:14:59-07:00 summary: bpo-42000: Cleanup the AST related C-code (GH-22641) - Use the proper asdl sequence when creating empty arguments - Remove reduntant casts (thanks to new typed asdl_sequences) - Remove MarshalPrototypeVisitor and some utilities from asdl generator - Fix the header of `Python/ast.c` (kept from pgen times) Automerge-Triggered-By: @pablogsal files: M Parser/asdl_c.py M Parser/pegen.c M Python/ast.c diff --git a/Parser/asdl_c.py b/Parser/asdl_c.py index 242eccf3d37d7..481261cd85359 100755 --- a/Parser/asdl_c.py +++ b/Parser/asdl_c.py @@ -618,16 +618,6 @@ def visitField(self, field, name, sum=None, prod=None, depth=0): self.emit("}", depth) -class MarshalPrototypeVisitor(PickleVisitor): - - def prototype(self, sum, name): - ctype = get_c_type(name) - self.emit("static int marshal_write_%s(PyObject **, int *, %s);" - % (name, ctype), 0) - - visitProduct = visitSum = prototype - - class SequenceConstructorVisitor(EmitVisitor): def visitModule(self, mod): for dfn in mod.dfns: @@ -1167,25 +1157,6 @@ def addObj(self, name): self.emit("Py_INCREF(state->%s_type);" % name, 1) -_SPECIALIZED_SEQUENCES = ('stmt', 'expr') - -def find_sequence(fields, doing_specialization): - """Return True if any field uses a sequence.""" - for f in fields: - if f.seq: - if not doing_specialization: - return True - if str(f.type) not in _SPECIALIZED_SEQUENCES: - return True - return False - -def has_sequence(types, doing_specialization): - for t in types: - if find_sequence(t.fields, doing_specialization): - return True - return False - - class StaticVisitor(PickleVisitor): CODE = '''Very simple, always emit this static code. Override CODE''' @@ -1283,18 +1254,6 @@ def emit(s, d): emit("goto failed;", 1) emit("Py_DECREF(value);", 0) - def emitSeq(self, field, value, depth, emit): - emit("seq = %s;" % value, 0) - emit("n = asdl_seq_LEN(seq);", 0) - emit("value = PyList_New(n);", 0) - emit("if (!value) goto failed;", 0) - emit("for (i = 0; i < n; i++) {", 0) - self.set("value", field, "asdl_seq_GET(seq, i)", depth + 1) - emit("if (!value1) goto failed;", 1) - emit("PyList_SET_ITEM(value, i, value1);", 1) - emit("value1 = NULL;", 1) - emit("}", 0) - def set(self, field, value, depth): if field.seq: # XXX should really check for is_simple, but that requires a symbol table @@ -1313,7 +1272,6 @@ def set(self, field, value, depth): else: self.emit("value = ast2obj_list(state, (asdl_seq*)%s, ast2obj_%s);" % (value, field.type), depth) else: - ctype = get_c_type(field.type) self.emit("value = ast2obj_%s(state, %s);" % (field.type, value), depth, reflow=False) diff --git a/Parser/pegen.c b/Parser/pegen.c index 1de495eaf398e..efa5ed9f288ee 100644 --- a/Parser/pegen.c +++ b/Parser/pegen.c @@ -1897,7 +1897,7 @@ _PyPegen_empty_arguments(Parser *p) return NULL; } - return _Py_arguments(posonlyargs, posargs, NULL, kwonlyargs, kwdefaults, NULL, kwdefaults, + return _Py_arguments(posonlyargs, posargs, NULL, kwonlyargs, kwdefaults, NULL, posdefaults, p->arena); } diff --git a/Python/ast.c b/Python/ast.c index 4b7bbd229c99b..5e74f65a2c013 100644 --- a/Python/ast.c +++ b/Python/ast.c @@ -1,18 +1,12 @@ /* - * This file includes functions to transform a concrete syntax tree (CST) to - * an abstract syntax tree (AST). The main function is PyAST_FromNode(). - * + * This file exposes PyAST_Validate interface to check the integrity + * of the given abstract syntax tree (potentially constructed manually). */ #include "Python.h" #include "Python-ast.h" #include "ast.h" -#include "token.h" -#include "pythonrun.h" #include -#include - -#define MAXLEVEL 200 /* Max parentheses level */ static int validate_stmts(asdl_stmt_seq *); static int validate_exprs(asdl_expr_seq*, expr_context_ty, int); @@ -62,7 +56,7 @@ validate_keywords(asdl_keyword_seq *keywords) { Py_ssize_t i; for (i = 0; i < asdl_seq_LEN(keywords); i++) - if (!validate_expr(((keyword_ty)asdl_seq_GET(keywords, i))->value, Load)) + if (!validate_expr((asdl_seq_GET(keywords, i))->value, Load)) return 0; return 1; } @@ -556,7 +550,7 @@ _PyAST_GetDocString(asdl_stmt_seq *body) if (!asdl_seq_LEN(body)) { return NULL; } - stmt_ty st = (stmt_ty)asdl_seq_GET(body, 0); + stmt_ty st = asdl_seq_GET(body, 0); if (st->kind != Expr_kind) { return NULL; } From webhook-mailer at python.org Sat Oct 10 15:23:51 2020 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Sat, 10 Oct 2020 19:23:51 -0000 Subject: [Python-checkins] bpo-41991: Remove _PyObject_HasAttrId (GH-22629) Message-ID: https://github.com/python/cpython/commit/98c4433a81a4cd88c7438adbee1f2aa486188ca3 commit: 98c4433a81a4cd88c7438adbee1f2aa486188ca3 branch: master author: Serhiy Storchaka committer: GitHub date: 2020-10-10T22:23:42+03:00 summary: bpo-41991: Remove _PyObject_HasAttrId (GH-22629) It can silence arbitrary exceptions. files: M Include/cpython/object.h M Objects/object.c M Objects/unionobject.c M Python/errors.c M Python/pythonrun.c diff --git a/Include/cpython/object.h b/Include/cpython/object.h index ae3920d4508e1..875a600f79565 100644 --- a/Include/cpython/object.h +++ b/Include/cpython/object.h @@ -306,7 +306,6 @@ PyAPI_FUNC(int) _PyObject_IsFreed(PyObject *); PyAPI_FUNC(int) _PyObject_IsAbstract(PyObject *); PyAPI_FUNC(PyObject *) _PyObject_GetAttrId(PyObject *, struct _Py_Identifier *); PyAPI_FUNC(int) _PyObject_SetAttrId(PyObject *, struct _Py_Identifier *, PyObject *); -PyAPI_FUNC(int) _PyObject_HasAttrId(PyObject *, struct _Py_Identifier *); /* Replacements of PyObject_GetAttr() and _PyObject_GetAttrId() which don't raise AttributeError. diff --git a/Objects/object.c b/Objects/object.c index 9889503cfd893..7bc3e48d40a6f 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -854,17 +854,6 @@ _PyObject_GetAttrId(PyObject *v, _Py_Identifier *name) return result; } -int -_PyObject_HasAttrId(PyObject *v, _Py_Identifier *name) -{ - int result; - PyObject *oname = _PyUnicode_FromId(name); /* borrowed */ - if (!oname) - return -1; - result = PyObject_HasAttr(v, oname); - return result; -} - int _PyObject_SetAttrId(PyObject *v, _Py_Identifier *name, PyObject *w) { diff --git a/Objects/unionobject.c b/Objects/unionobject.c index 8cfb2a664753f..89fdaf42560c1 100644 --- a/Objects/unionobject.c +++ b/Objects/unionobject.c @@ -311,21 +311,22 @@ union_repr_item(_PyUnicodeWriter *writer, PyObject *p) _Py_IDENTIFIER(__args__); PyObject *qualname = NULL; PyObject *module = NULL; + PyObject *tmp; PyObject *r = NULL; int err; - int has_origin = _PyObject_HasAttrId(p, &PyId___origin__); - if (has_origin < 0) { + if (_PyObject_LookupAttrId(p, &PyId___origin__, &tmp) < 0) { goto exit; } - if (has_origin) { - int has_args = _PyObject_HasAttrId(p, &PyId___args__); - if (has_args < 0) { + if (tmp) { + Py_DECREF(tmp); + if (_PyObject_LookupAttrId(p, &PyId___args__, &tmp) < 0) { goto exit; } - if (has_args) { + if (tmp) { // It looks like a GenericAlias + Py_DECREF(tmp); goto use_repr; } } diff --git a/Python/errors.c b/Python/errors.c index 720f18bc224d4..02cf47992b695 100644 --- a/Python/errors.c +++ b/Python/errors.c @@ -1593,9 +1593,18 @@ PyErr_SyntaxLocationObject(PyObject *filename, int lineno, int col_offset) } Py_DECREF(tmp); } + else { + _PyErr_Clear(tstate); + } } if (exc != PyExc_SyntaxError) { - if (!_PyObject_HasAttrId(v, &PyId_msg)) { + if (_PyObject_LookupAttrId(v, &PyId_msg, &tmp) < 0) { + _PyErr_Clear(tstate); + } + else if (tmp) { + Py_DECREF(tmp); + } + else { tmp = PyObject_Str(v); if (tmp) { if (_PyObject_SetAttrId(v, &PyId_msg, tmp)) { @@ -1607,7 +1616,13 @@ PyErr_SyntaxLocationObject(PyObject *filename, int lineno, int col_offset) _PyErr_Clear(tstate); } } - if (!_PyObject_HasAttrId(v, &PyId_print_file_and_line)) { + if (_PyObject_LookupAttrId(v, &PyId_print_file_and_line, &tmp) < 0) { + _PyErr_Clear(tstate); + } + else if (tmp) { + Py_DECREF(tmp); + } + else { if (_PyObject_SetAttrId(v, &PyId_print_file_and_line, Py_None)) { _PyErr_Clear(tstate); diff --git a/Python/pythonrun.c b/Python/pythonrun.c index ff80103050e4e..a45ca3b18311d 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -770,7 +770,7 @@ static void print_exception(PyObject *f, PyObject *value) { int err = 0; - PyObject *type, *tb; + PyObject *type, *tb, *tmp; _Py_IDENTIFIER(print_file_and_line); if (!PyExceptionInstance_Check(value)) { @@ -789,10 +789,12 @@ print_exception(PyObject *f, PyObject *value) if (tb && tb != Py_None) err = PyTraceBack_Print(tb, f); if (err == 0 && - _PyObject_HasAttrId(value, &PyId_print_file_and_line)) + (err = _PyObject_LookupAttrId(value, &PyId_print_file_and_line, &tmp)) > 0) { PyObject *message, *filename, *text; Py_ssize_t lineno, offset; + err = 0; + Py_DECREF(tmp); if (!parse_syntax_error(value, &message, &filename, &lineno, &offset, &text)) PyErr_Clear(); From webhook-mailer at python.org Sat Oct 10 18:19:54 2020 From: webhook-mailer at python.org (Batuhan Taskaya) Date: Sat, 10 Oct 2020 22:19:54 -0000 Subject: [Python-checkins] bpo-38605: bump the magic number for 'annotations' future (#22630) Message-ID: https://github.com/python/cpython/commit/22220ae216b11644b23511c6287a52e7d28aeb9f commit: 22220ae216b11644b23511c6287a52e7d28aeb9f branch: master author: Batuhan Taskaya committer: GitHub date: 2020-10-10T15:19:46-07:00 summary: bpo-38605: bump the magic number for 'annotations' future (#22630) files: M Lib/importlib/_bootstrap_external.py M Python/importlib_external.h diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index 4f06039f3d23c..b08ad032ab309 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -277,6 +277,7 @@ def _write_atomic(path, data, mode=0o666): # Python 3.9a2 3423 (add IS_OP, CONTAINS_OP and JUMP_IF_NOT_EXC_MATCH bytecodes #39156) # Python 3.9a2 3424 (simplify bytecodes for *value unpacking) # Python 3.9a2 3425 (simplify bytecodes for **value unpacking) +# Python 3.10a1 3430 (Make 'annotations' future by default) # # MAGIC must change whenever the bytecode emitted by the compiler may no @@ -286,7 +287,7 @@ def _write_atomic(path, data, mode=0o666): # Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array # in PC/launcher.c must also be updated. -MAGIC_NUMBER = (3425).to_bytes(2, 'little') + b'\r\n' +MAGIC_NUMBER = (3430).to_bytes(2, 'little') + b'\r\n' _RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c _PYCACHE = '__pycache__' diff --git a/Python/importlib_external.h b/Python/importlib_external.h index 0ef1b45594fbf..6daddb1fb8dfb 100644 --- a/Python/importlib_external.h +++ b/Python/importlib_external.h @@ -285,7 +285,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,218,13,95,119,114,105,116,101,95,97,116,111,109,105,99, 120,0,0,0,115,28,0,0,0,0,5,16,1,6,1,22, 255,4,2,2,3,14,1,40,1,16,1,12,1,2,1,14, - 1,12,1,6,1,114,69,0,0,0,105,97,13,0,0,114, + 1,12,1,6,1,114,69,0,0,0,105,102,13,0,0,114, 28,0,0,0,114,17,0,0,0,115,2,0,0,0,13,10, 90,11,95,95,112,121,99,97,99,104,101,95,95,122,4,111, 112,116,45,122,3,46,112,121,122,4,46,112,121,99,78,41, @@ -399,7 +399,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 90,15,97,108,109,111,115,116,95,102,105,108,101,110,97,109, 101,218,8,102,105,108,101,110,97,109,101,114,5,0,0,0, 114,5,0,0,0,114,8,0,0,0,218,17,99,97,99,104, - 101,95,102,114,111,109,95,115,111,117,114,99,101,45,1,0, + 101,95,102,114,111,109,95,115,111,117,114,99,101,46,1,0, 0,115,72,0,0,0,0,18,8,1,6,1,2,255,4,2, 8,1,4,1,8,1,12,1,10,1,12,1,16,1,8,1, 8,1,8,1,24,1,8,1,12,1,6,2,8,1,8,1, @@ -480,7 +480,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,90,9,111,112,116,95,108,101,118,101,108,90,13,98, 97,115,101,95,102,105,108,101,110,97,109,101,114,5,0,0, 0,114,5,0,0,0,114,8,0,0,0,218,17,115,111,117, - 114,99,101,95,102,114,111,109,95,99,97,99,104,101,116,1, + 114,99,101,95,102,114,111,109,95,99,97,99,104,101,117,1, 0,0,115,60,0,0,0,0,9,12,1,8,1,10,1,12, 1,4,1,10,1,12,1,14,1,16,1,4,1,4,1,12, 1,8,1,8,1,2,255,8,2,10,1,8,1,16,1,10, @@ -516,7 +516,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 120,116,101,110,115,105,111,110,218,11,115,111,117,114,99,101, 95,112,97,116,104,114,5,0,0,0,114,5,0,0,0,114, 8,0,0,0,218,15,95,103,101,116,95,115,111,117,114,99, - 101,102,105,108,101,156,1,0,0,115,20,0,0,0,0,7, + 101,102,105,108,101,157,1,0,0,115,20,0,0,0,0,7, 12,1,4,1,16,1,24,1,4,1,2,1,12,1,16,1, 18,1,114,108,0,0,0,99,1,0,0,0,0,0,0,0, 0,0,0,0,1,0,0,0,8,0,0,0,67,0,0,0, @@ -529,7 +529,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 101,114,101,0,0,0,114,97,0,0,0,114,82,0,0,0, 114,88,0,0,0,41,1,114,96,0,0,0,114,5,0,0, 0,114,5,0,0,0,114,8,0,0,0,218,11,95,103,101, - 116,95,99,97,99,104,101,100,175,1,0,0,115,16,0,0, + 116,95,99,97,99,104,101,100,176,1,0,0,115,16,0,0, 0,0,1,14,1,2,1,10,1,12,1,6,1,14,1,4, 2,114,112,0,0,0,99,1,0,0,0,0,0,0,0,0, 0,0,0,2,0,0,0,8,0,0,0,67,0,0,0,115, @@ -543,7 +543,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,233,128,0,0,0,41,3,114,49,0,0,0,114,51, 0,0,0,114,50,0,0,0,41,2,114,44,0,0,0,114, 52,0,0,0,114,5,0,0,0,114,5,0,0,0,114,8, - 0,0,0,218,10,95,99,97,108,99,95,109,111,100,101,187, + 0,0,0,218,10,95,99,97,108,99,95,109,111,100,101,188, 1,0,0,115,12,0,0,0,0,2,2,1,14,1,12,1, 10,3,8,1,114,114,0,0,0,99,1,0,0,0,0,0, 0,0,0,0,0,0,3,0,0,0,8,0,0,0,3,0, @@ -582,7 +582,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 103,115,218,6,107,119,97,114,103,115,169,1,218,6,109,101, 116,104,111,100,114,5,0,0,0,114,8,0,0,0,218,19, 95,99,104,101,99,107,95,110,97,109,101,95,119,114,97,112, - 112,101,114,207,1,0,0,115,18,0,0,0,0,1,8,1, + 112,101,114,208,1,0,0,115,18,0,0,0,0,1,8,1, 8,1,10,1,4,1,8,255,2,1,2,255,6,2,122,40, 95,99,104,101,99,107,95,110,97,109,101,46,60,108,111,99, 97,108,115,62,46,95,99,104,101,99,107,95,110,97,109,101, @@ -600,7 +600,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 95,100,105,99,116,95,95,218,6,117,112,100,97,116,101,41, 3,90,3,110,101,119,90,3,111,108,100,114,67,0,0,0, 114,5,0,0,0,114,5,0,0,0,114,8,0,0,0,218, - 5,95,119,114,97,112,218,1,0,0,115,8,0,0,0,0, + 5,95,119,114,97,112,219,1,0,0,115,8,0,0,0,0, 1,8,1,10,1,20,1,122,26,95,99,104,101,99,107,95, 110,97,109,101,46,60,108,111,99,97,108,115,62,46,95,119, 114,97,112,41,1,78,41,3,218,10,95,98,111,111,116,115, @@ -608,7 +608,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 114,114,111,114,41,3,114,122,0,0,0,114,123,0,0,0, 114,133,0,0,0,114,5,0,0,0,114,121,0,0,0,114, 8,0,0,0,218,11,95,99,104,101,99,107,95,110,97,109, - 101,199,1,0,0,115,14,0,0,0,0,8,14,7,2,1, + 101,200,1,0,0,115,14,0,0,0,0,8,14,7,2,1, 10,1,12,2,14,5,10,1,114,136,0,0,0,99,2,0, 0,0,0,0,0,0,0,0,0,0,5,0,0,0,6,0, 0,0,67,0,0,0,115,60,0,0,0,124,0,160,0,124, @@ -636,7 +636,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 111,97,100,101,114,218,8,112,111,114,116,105,111,110,115,218, 3,109,115,103,114,5,0,0,0,114,5,0,0,0,114,8, 0,0,0,218,17,95,102,105,110,100,95,109,111,100,117,108, - 101,95,115,104,105,109,227,1,0,0,115,10,0,0,0,0, + 101,95,115,104,105,109,228,1,0,0,115,10,0,0,0,0, 10,14,1,16,1,4,1,22,1,114,143,0,0,0,99,3, 0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,4, 0,0,0,67,0,0,0,115,166,0,0,0,124,0,100,1, @@ -703,7 +703,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 99,95,100,101,116,97,105,108,115,90,5,109,97,103,105,99, 114,92,0,0,0,114,2,0,0,0,114,5,0,0,0,114, 5,0,0,0,114,8,0,0,0,218,13,95,99,108,97,115, - 115,105,102,121,95,112,121,99,244,1,0,0,115,28,0,0, + 115,105,102,121,95,112,121,99,245,1,0,0,115,28,0,0, 0,0,16,12,1,8,1,16,1,12,1,16,1,12,1,10, 1,12,1,8,1,16,2,8,1,16,1,16,1,114,152,0, 0,0,99,5,0,0,0,0,0,0,0,0,0,0,0,6, @@ -758,7 +758,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 114,116,0,0,0,114,151,0,0,0,114,92,0,0,0,114, 5,0,0,0,114,5,0,0,0,114,8,0,0,0,218,23, 95,118,97,108,105,100,97,116,101,95,116,105,109,101,115,116, - 97,109,112,95,112,121,99,21,2,0,0,115,16,0,0,0, + 97,109,112,95,112,121,99,22,2,0,0,115,16,0,0,0, 0,19,24,1,10,1,12,1,16,1,8,1,22,255,2,2, 114,156,0,0,0,99,4,0,0,0,0,0,0,0,0,0, 0,0,4,0,0,0,4,0,0,0,67,0,0,0,115,42, @@ -804,7 +804,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 218,11,115,111,117,114,99,101,95,104,97,115,104,114,116,0, 0,0,114,151,0,0,0,114,5,0,0,0,114,5,0,0, 0,114,8,0,0,0,218,18,95,118,97,108,105,100,97,116, - 101,95,104,97,115,104,95,112,121,99,49,2,0,0,115,12, + 101,95,104,97,115,104,95,112,121,99,50,2,0,0,115,12, 0,0,0,0,17,16,1,2,1,8,255,4,2,2,254,114, 158,0,0,0,99,4,0,0,0,0,0,0,0,0,0,0, 0,5,0,0,0,5,0,0,0,67,0,0,0,115,80,0, @@ -828,7 +828,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,0,114,116,0,0,0,114,106,0,0,0,114,107,0, 0,0,218,4,99,111,100,101,114,5,0,0,0,114,5,0, 0,0,114,8,0,0,0,218,17,95,99,111,109,112,105,108, - 101,95,98,121,116,101,99,111,100,101,73,2,0,0,115,18, + 101,95,98,121,116,101,99,111,100,101,74,2,0,0,115,18, 0,0,0,0,2,10,1,10,1,12,1,8,1,12,1,4, 2,10,1,4,255,114,165,0,0,0,114,73,0,0,0,99, 3,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0, @@ -847,7 +847,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 114,155,0,0,0,114,26,0,0,0,114,5,0,0,0,114, 5,0,0,0,114,8,0,0,0,218,22,95,99,111,100,101, 95,116,111,95,116,105,109,101,115,116,97,109,112,95,112,121, - 99,86,2,0,0,115,12,0,0,0,0,2,8,1,14,1, + 99,87,2,0,0,115,12,0,0,0,0,2,8,1,14,1, 14,1,14,1,16,1,114,170,0,0,0,84,99,3,0,0, 0,0,0,0,0,0,0,0,0,5,0,0,0,5,0,0, 0,67,0,0,0,115,80,0,0,0,116,0,116,1,131,1, @@ -865,7 +865,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 7,99,104,101,99,107,101,100,114,26,0,0,0,114,2,0, 0,0,114,5,0,0,0,114,5,0,0,0,114,8,0,0, 0,218,17,95,99,111,100,101,95,116,111,95,104,97,115,104, - 95,112,121,99,96,2,0,0,115,14,0,0,0,0,2,8, + 95,112,121,99,97,2,0,0,115,14,0,0,0,0,2,8, 1,12,1,14,1,16,1,10,1,16,1,114,171,0,0,0, 99,1,0,0,0,0,0,0,0,0,0,0,0,5,0,0, 0,6,0,0,0,67,0,0,0,115,62,0,0,0,100,1, @@ -892,7 +892,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 108,105,110,101,218,8,101,110,99,111,100,105,110,103,90,15, 110,101,119,108,105,110,101,95,100,101,99,111,100,101,114,114, 5,0,0,0,114,5,0,0,0,114,8,0,0,0,218,13, - 100,101,99,111,100,101,95,115,111,117,114,99,101,107,2,0, + 100,101,99,111,100,101,95,115,111,117,114,99,101,108,2,0, 0,115,10,0,0,0,0,5,8,1,12,1,10,1,12,1, 114,176,0,0,0,169,2,114,140,0,0,0,218,26,115,117, 98,109,111,100,117,108,101,95,115,101,97,114,99,104,95,108, @@ -954,7 +954,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,90,7,100,105,114,110,97,109,101,114,5,0,0,0,114, 5,0,0,0,114,8,0,0,0,218,23,115,112,101,99,95, 102,114,111,109,95,102,105,108,101,95,108,111,99,97,116,105, - 111,110,124,2,0,0,115,62,0,0,0,0,12,8,4,4, + 111,110,125,2,0,0,115,62,0,0,0,0,12,8,4,4, 1,10,2,2,1,14,1,12,1,6,2,10,8,16,1,6, 3,8,1,14,1,14,1,10,1,6,1,6,2,4,3,8, 2,10,1,2,1,14,1,12,1,6,2,4,1,8,2,6, @@ -991,7 +991,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 65,76,95,77,65,67,72,73,78,69,41,2,218,3,99,108, 115,114,7,0,0,0,114,5,0,0,0,114,5,0,0,0, 114,8,0,0,0,218,14,95,111,112,101,110,95,114,101,103, - 105,115,116,114,121,204,2,0,0,115,8,0,0,0,0,2, + 105,115,116,114,121,205,2,0,0,115,8,0,0,0,0,2, 2,1,16,1,12,1,122,36,87,105,110,100,111,119,115,82, 101,103,105,115,116,114,121,70,105,110,100,101,114,46,95,111, 112,101,110,95,114,101,103,105,115,116,114,121,99,2,0,0, @@ -1018,7 +1018,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 95,107,101,121,114,7,0,0,0,90,4,104,107,101,121,218, 8,102,105,108,101,112,97,116,104,114,5,0,0,0,114,5, 0,0,0,114,8,0,0,0,218,16,95,115,101,97,114,99, - 104,95,114,101,103,105,115,116,114,121,211,2,0,0,115,24, + 104,95,114,101,103,105,115,116,114,121,212,2,0,0,115,24, 0,0,0,0,2,6,1,8,2,6,1,6,1,16,255,6, 2,2,1,12,1,46,1,12,1,8,1,122,38,87,105,110, 100,111,119,115,82,101,103,105,115,116,114,121,70,105,110,100, @@ -1040,7 +1040,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,0,114,44,0,0,0,218,6,116,97,114,103,101,116, 114,199,0,0,0,114,140,0,0,0,114,189,0,0,0,114, 187,0,0,0,114,5,0,0,0,114,5,0,0,0,114,8, - 0,0,0,218,9,102,105,110,100,95,115,112,101,99,226,2, + 0,0,0,218,9,102,105,110,100,95,115,112,101,99,227,2, 0,0,115,28,0,0,0,0,2,10,1,8,1,4,1,2, 1,12,1,12,1,8,1,14,1,14,1,6,1,8,1,2, 254,6,3,122,31,87,105,110,100,111,119,115,82,101,103,105, @@ -1059,7 +1059,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 78,169,2,114,203,0,0,0,114,140,0,0,0,169,4,114, 193,0,0,0,114,139,0,0,0,114,44,0,0,0,114,187, 0,0,0,114,5,0,0,0,114,5,0,0,0,114,8,0, - 0,0,218,11,102,105,110,100,95,109,111,100,117,108,101,242, + 0,0,218,11,102,105,110,100,95,109,111,100,117,108,101,243, 2,0,0,115,8,0,0,0,0,7,12,1,8,1,6,2, 122,33,87,105,110,100,111,119,115,82,101,103,105,115,116,114, 121,70,105,110,100,101,114,46,102,105,110,100,95,109,111,100, @@ -1069,7 +1069,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 11,99,108,97,115,115,109,101,116,104,111,100,114,194,0,0, 0,114,200,0,0,0,114,203,0,0,0,114,206,0,0,0, 114,5,0,0,0,114,5,0,0,0,114,5,0,0,0,114, - 8,0,0,0,114,191,0,0,0,192,2,0,0,115,28,0, + 8,0,0,0,114,191,0,0,0,193,2,0,0,115,28,0, 0,0,8,2,4,3,2,255,2,4,2,255,2,3,4,2, 2,1,10,6,2,1,10,14,2,1,12,15,2,1,114,191, 0,0,0,99,0,0,0,0,0,0,0,0,0,0,0,0, @@ -1105,7 +1105,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,114,139,0,0,0,114,96,0,0,0,90,13,102,105,108, 101,110,97,109,101,95,98,97,115,101,90,9,116,97,105,108, 95,110,97,109,101,114,5,0,0,0,114,5,0,0,0,114, - 8,0,0,0,114,182,0,0,0,5,3,0,0,115,8,0, + 8,0,0,0,114,182,0,0,0,6,3,0,0,115,8,0, 0,0,0,3,18,1,16,1,14,1,122,24,95,76,111,97, 100,101,114,66,97,115,105,99,115,46,105,115,95,112,97,99, 107,97,103,101,99,2,0,0,0,0,0,0,0,0,0,0, @@ -1116,7 +1116,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 105,111,110,46,78,114,5,0,0,0,169,2,114,118,0,0, 0,114,187,0,0,0,114,5,0,0,0,114,5,0,0,0, 114,8,0,0,0,218,13,99,114,101,97,116,101,95,109,111, - 100,117,108,101,13,3,0,0,115,2,0,0,0,0,1,122, + 100,117,108,101,14,3,0,0,115,2,0,0,0,0,1,122, 27,95,76,111,97,100,101,114,66,97,115,105,99,115,46,99, 114,101,97,116,101,95,109,111,100,117,108,101,99,2,0,0, 0,0,0,0,0,0,0,0,0,3,0,0,0,5,0,0, @@ -1136,7 +1136,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 99,114,131,0,0,0,41,3,114,118,0,0,0,218,6,109, 111,100,117,108,101,114,164,0,0,0,114,5,0,0,0,114, 5,0,0,0,114,8,0,0,0,218,11,101,120,101,99,95, - 109,111,100,117,108,101,16,3,0,0,115,12,0,0,0,0, + 109,111,100,117,108,101,17,3,0,0,115,12,0,0,0,0, 2,12,1,8,1,6,1,4,255,6,2,122,25,95,76,111, 97,100,101,114,66,97,115,105,99,115,46,101,120,101,99,95, 109,111,100,117,108,101,99,2,0,0,0,0,0,0,0,0, @@ -1148,13 +1148,13 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 117,108,101,95,115,104,105,109,169,2,114,118,0,0,0,114, 139,0,0,0,114,5,0,0,0,114,5,0,0,0,114,8, 0,0,0,218,11,108,111,97,100,95,109,111,100,117,108,101, - 24,3,0,0,115,2,0,0,0,0,2,122,25,95,76,111, + 25,3,0,0,115,2,0,0,0,0,2,122,25,95,76,111, 97,100,101,114,66,97,115,105,99,115,46,108,111,97,100,95, 109,111,100,117,108,101,78,41,8,114,125,0,0,0,114,124, 0,0,0,114,126,0,0,0,114,127,0,0,0,114,182,0, 0,0,114,212,0,0,0,114,217,0,0,0,114,220,0,0, 0,114,5,0,0,0,114,5,0,0,0,114,5,0,0,0, - 114,8,0,0,0,114,208,0,0,0,0,3,0,0,115,10, + 114,8,0,0,0,114,208,0,0,0,1,3,0,0,115,10, 0,0,0,8,2,4,3,8,8,8,3,8,8,114,208,0, 0,0,99,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,3,0,0,0,64,0,0,0,115,74,0,0,0, @@ -1179,7 +1179,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 10,32,32,32,32,32,32,32,32,78,41,1,114,50,0,0, 0,169,2,114,118,0,0,0,114,44,0,0,0,114,5,0, 0,0,114,5,0,0,0,114,8,0,0,0,218,10,112,97, - 116,104,95,109,116,105,109,101,31,3,0,0,115,2,0,0, + 116,104,95,109,116,105,109,101,32,3,0,0,115,2,0,0, 0,0,6,122,23,83,111,117,114,99,101,76,111,97,100,101, 114,46,112,97,116,104,95,109,116,105,109,101,99,2,0,0, 0,0,0,0,0,0,0,0,0,2,0,0,0,4,0,0, @@ -1213,7 +1213,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 108,101,100,46,10,32,32,32,32,32,32,32,32,114,169,0, 0,0,41,1,114,223,0,0,0,114,222,0,0,0,114,5, 0,0,0,114,5,0,0,0,114,8,0,0,0,218,10,112, - 97,116,104,95,115,116,97,116,115,39,3,0,0,115,2,0, + 97,116,104,95,115,116,97,116,115,40,3,0,0,115,2,0, 0,0,0,12,122,23,83,111,117,114,99,101,76,111,97,100, 101,114,46,112,97,116,104,95,115,116,97,116,115,99,4,0, 0,0,0,0,0,0,0,0,0,0,4,0,0,0,4,0, @@ -1237,7 +1237,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,114,107,0,0,0,90,10,99,97,99,104,101,95,112, 97,116,104,114,26,0,0,0,114,5,0,0,0,114,5,0, 0,0,114,8,0,0,0,218,15,95,99,97,99,104,101,95, - 98,121,116,101,99,111,100,101,53,3,0,0,115,2,0,0, + 98,121,116,101,99,111,100,101,54,3,0,0,115,2,0,0, 0,0,8,122,28,83,111,117,114,99,101,76,111,97,100,101, 114,46,95,99,97,99,104,101,95,98,121,116,101,99,111,100, 101,99,3,0,0,0,0,0,0,0,0,0,0,0,3,0, @@ -1254,7 +1254,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 108,101,115,46,10,32,32,32,32,32,32,32,32,78,114,5, 0,0,0,41,3,114,118,0,0,0,114,44,0,0,0,114, 26,0,0,0,114,5,0,0,0,114,5,0,0,0,114,8, - 0,0,0,114,225,0,0,0,63,3,0,0,115,2,0,0, + 0,0,0,114,225,0,0,0,64,3,0,0,115,2,0,0, 0,0,1,122,21,83,111,117,114,99,101,76,111,97,100,101, 114,46,115,101,116,95,100,97,116,97,99,2,0,0,0,0, 0,0,0,0,0,0,0,5,0,0,0,10,0,0,0,67, @@ -1275,7 +1275,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,0,41,5,114,118,0,0,0,114,139,0,0,0,114, 44,0,0,0,114,174,0,0,0,218,3,101,120,99,114,5, 0,0,0,114,5,0,0,0,114,8,0,0,0,218,10,103, - 101,116,95,115,111,117,114,99,101,70,3,0,0,115,20,0, + 101,116,95,115,111,117,114,99,101,71,3,0,0,115,20,0, 0,0,0,2,10,1,2,1,14,1,14,1,4,1,2,255, 4,1,2,255,24,2,122,23,83,111,117,114,99,101,76,111, 97,100,101,114,46,103,101,116,95,115,111,117,114,99,101,114, @@ -1298,7 +1298,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,114,26,0,0,0,114,44,0,0,0,114,230,0,0, 0,114,5,0,0,0,114,5,0,0,0,114,8,0,0,0, 218,14,115,111,117,114,99,101,95,116,111,95,99,111,100,101, - 80,3,0,0,115,6,0,0,0,0,5,12,1,4,255,122, + 81,3,0,0,115,6,0,0,0,0,5,12,1,4,255,122, 27,83,111,117,114,99,101,76,111,97,100,101,114,46,115,111, 117,114,99,101,95,116,111,95,99,111,100,101,99,2,0,0, 0,0,0,0,0,0,0,0,0,15,0,0,0,9,0,0, @@ -1374,7 +1374,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 115,116,114,26,0,0,0,114,151,0,0,0,114,2,0,0, 0,90,10,98,121,116,101,115,95,100,97,116,97,90,11,99, 111,100,101,95,111,98,106,101,99,116,114,5,0,0,0,114, - 5,0,0,0,114,8,0,0,0,114,213,0,0,0,88,3, + 5,0,0,0,114,8,0,0,0,114,213,0,0,0,89,3, 0,0,115,152,0,0,0,0,7,10,1,4,1,4,1,4, 1,4,1,4,1,2,1,12,1,12,1,12,2,2,1,14, 1,12,1,8,2,12,1,2,1,14,1,12,1,6,3,2, @@ -1391,7 +1391,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,114,226,0,0,0,114,225,0,0,0,114,229,0,0, 0,114,233,0,0,0,114,213,0,0,0,114,5,0,0,0, 114,5,0,0,0,114,5,0,0,0,114,8,0,0,0,114, - 221,0,0,0,29,3,0,0,115,14,0,0,0,8,2,8, + 221,0,0,0,30,3,0,0,115,14,0,0,0,8,2,8, 8,8,14,8,10,8,7,8,10,14,8,114,221,0,0,0, 99,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,4,0,0,0,0,0,0,0,115,92,0,0,0,101,0, @@ -1418,7 +1418,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 32,32,32,32,32,32,32,102,105,110,100,101,114,46,78,114, 159,0,0,0,41,3,114,118,0,0,0,114,139,0,0,0, 114,44,0,0,0,114,5,0,0,0,114,5,0,0,0,114, - 8,0,0,0,114,209,0,0,0,178,3,0,0,115,4,0, + 8,0,0,0,114,209,0,0,0,179,3,0,0,115,4,0, 0,0,0,3,6,1,122,19,70,105,108,101,76,111,97,100, 101,114,46,95,95,105,110,105,116,95,95,99,2,0,0,0, 0,0,0,0,0,0,0,0,2,0,0,0,2,0,0,0, @@ -1427,7 +1427,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,114,109,0,0,0,169,2,218,9,95,95,99,108,97,115, 115,95,95,114,131,0,0,0,169,2,114,118,0,0,0,90, 5,111,116,104,101,114,114,5,0,0,0,114,5,0,0,0, - 114,8,0,0,0,218,6,95,95,101,113,95,95,184,3,0, + 114,8,0,0,0,218,6,95,95,101,113,95,95,185,3,0, 0,115,6,0,0,0,0,1,12,1,10,255,122,17,70,105, 108,101,76,111,97,100,101,114,46,95,95,101,113,95,95,99, 1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0, @@ -1436,7 +1436,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,114,109,0,0,0,169,3,218,4,104,97,115,104,114,116, 0,0,0,114,44,0,0,0,169,1,114,118,0,0,0,114, 5,0,0,0,114,5,0,0,0,114,8,0,0,0,218,8, - 95,95,104,97,115,104,95,95,188,3,0,0,115,2,0,0, + 95,95,104,97,115,104,95,95,189,3,0,0,115,2,0,0, 0,0,1,122,19,70,105,108,101,76,111,97,100,101,114,46, 95,95,104,97,115,104,95,95,99,2,0,0,0,0,0,0, 0,0,0,0,0,2,0,0,0,3,0,0,0,3,0,0, @@ -1450,7 +1450,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 101,97,100,46,10,10,32,32,32,32,32,32,32,32,41,3, 218,5,115,117,112,101,114,114,239,0,0,0,114,220,0,0, 0,114,219,0,0,0,169,1,114,241,0,0,0,114,5,0, - 0,0,114,8,0,0,0,114,220,0,0,0,191,3,0,0, + 0,0,114,8,0,0,0,114,220,0,0,0,192,3,0,0, 115,2,0,0,0,0,10,122,22,70,105,108,101,76,111,97, 100,101,114,46,108,111,97,100,95,109,111,100,117,108,101,99, 2,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0, @@ -1460,7 +1460,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 117,114,99,101,32,102,105,108,101,32,97,115,32,102,111,117, 110,100,32,98,121,32,116,104,101,32,102,105,110,100,101,114, 46,114,48,0,0,0,114,219,0,0,0,114,5,0,0,0, - 114,5,0,0,0,114,8,0,0,0,114,179,0,0,0,203, + 114,5,0,0,0,114,8,0,0,0,114,179,0,0,0,204, 3,0,0,115,2,0,0,0,0,3,122,23,70,105,108,101, 76,111,97,100,101,114,46,103,101,116,95,102,105,108,101,110, 97,109,101,99,2,0,0,0,0,0,0,0,0,0,0,0, @@ -1482,7 +1482,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 84,0,0,0,90,4,114,101,97,100,114,65,0,0,0,41, 3,114,118,0,0,0,114,44,0,0,0,114,68,0,0,0, 114,5,0,0,0,114,5,0,0,0,114,8,0,0,0,114, - 227,0,0,0,208,3,0,0,115,10,0,0,0,0,2,14, + 227,0,0,0,209,3,0,0,115,10,0,0,0,0,2,14, 1,16,1,40,2,14,1,122,19,70,105,108,101,76,111,97, 100,101,114,46,103,101,116,95,100,97,116,97,99,2,0,0, 0,0,0,0,0,0,0,0,0,3,0,0,0,2,0,0, @@ -1494,7 +1494,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 114,118,0,0,0,114,216,0,0,0,114,253,0,0,0,114, 5,0,0,0,114,5,0,0,0,114,8,0,0,0,218,19, 103,101,116,95,114,101,115,111,117,114,99,101,95,114,101,97, - 100,101,114,217,3,0,0,115,4,0,0,0,0,2,12,1, + 100,101,114,218,3,0,0,115,4,0,0,0,0,2,12,1, 122,30,70,105,108,101,76,111,97,100,101,114,46,103,101,116, 95,114,101,115,111,117,114,99,101,95,114,101,97,100,101,114, 41,13,114,125,0,0,0,114,124,0,0,0,114,126,0,0, @@ -1503,7 +1503,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 179,0,0,0,114,227,0,0,0,114,254,0,0,0,90,13, 95,95,99,108,97,115,115,99,101,108,108,95,95,114,5,0, 0,0,114,5,0,0,0,114,249,0,0,0,114,8,0,0, - 0,114,239,0,0,0,173,3,0,0,115,22,0,0,0,8, + 0,114,239,0,0,0,174,3,0,0,115,22,0,0,0,8, 2,4,3,8,6,8,4,8,3,2,1,14,11,2,1,10, 4,8,9,2,1,114,239,0,0,0,99,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,64, @@ -1525,7 +1525,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 3,114,49,0,0,0,218,8,115,116,95,109,116,105,109,101, 90,7,115,116,95,115,105,122,101,41,3,114,118,0,0,0, 114,44,0,0,0,114,238,0,0,0,114,5,0,0,0,114, - 5,0,0,0,114,8,0,0,0,114,224,0,0,0,227,3, + 5,0,0,0,114,8,0,0,0,114,224,0,0,0,228,3, 0,0,115,4,0,0,0,0,2,8,1,122,27,83,111,117, 114,99,101,70,105,108,101,76,111,97,100,101,114,46,112,97, 116,104,95,115,116,97,116,115,99,4,0,0,0,0,0,0, @@ -1536,7 +1536,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,114,225,0,0,0,41,5,114,118,0,0,0,114,107,0, 0,0,114,106,0,0,0,114,26,0,0,0,114,52,0,0, 0,114,5,0,0,0,114,5,0,0,0,114,8,0,0,0, - 114,226,0,0,0,232,3,0,0,115,4,0,0,0,0,2, + 114,226,0,0,0,233,3,0,0,115,4,0,0,0,0,2, 8,1,122,32,83,111,117,114,99,101,70,105,108,101,76,111, 97,100,101,114,46,95,99,97,99,104,101,95,98,121,116,101, 99,111,100,101,114,60,0,0,0,114,1,1,0,0,99,3, @@ -1571,7 +1571,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,218,6,112,97,114,101,110,116,114,96,0,0,0,114,37, 0,0,0,114,33,0,0,0,114,228,0,0,0,114,5,0, 0,0,114,5,0,0,0,114,8,0,0,0,114,225,0,0, - 0,237,3,0,0,115,46,0,0,0,0,2,12,1,4,2, + 0,238,3,0,0,115,46,0,0,0,0,2,12,1,4,2, 12,1,12,1,12,2,12,1,10,1,2,1,14,1,12,2, 8,1,14,3,6,1,4,255,4,2,26,1,2,1,12,1, 16,1,14,2,8,1,2,255,122,25,83,111,117,114,99,101, @@ -1580,7 +1580,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 114,126,0,0,0,114,127,0,0,0,114,224,0,0,0,114, 226,0,0,0,114,225,0,0,0,114,5,0,0,0,114,5, 0,0,0,114,5,0,0,0,114,8,0,0,0,114,255,0, - 0,0,223,3,0,0,115,8,0,0,0,8,2,4,2,8, + 0,0,224,3,0,0,115,8,0,0,0,8,2,4,2,8, 5,8,5,114,255,0,0,0,99,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,2,0,0,0,64,0,0, 0,115,32,0,0,0,101,0,90,1,100,0,90,2,100,1, @@ -1602,7 +1602,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,0,41,5,114,118,0,0,0,114,139,0,0,0,114, 44,0,0,0,114,26,0,0,0,114,151,0,0,0,114,5, 0,0,0,114,5,0,0,0,114,8,0,0,0,114,213,0, - 0,0,16,4,0,0,115,22,0,0,0,0,1,10,1,10, + 0,0,17,4,0,0,115,22,0,0,0,0,1,10,1,10, 4,2,1,2,254,6,4,12,1,2,1,14,1,2,1,2, 253,122,29,83,111,117,114,99,101,108,101,115,115,70,105,108, 101,76,111,97,100,101,114,46,103,101,116,95,99,111,100,101, @@ -1612,14 +1612,14 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 101,32,97,115,32,116,104,101,114,101,32,105,115,32,110,111, 32,115,111,117,114,99,101,32,99,111,100,101,46,78,114,5, 0,0,0,114,219,0,0,0,114,5,0,0,0,114,5,0, - 0,0,114,8,0,0,0,114,229,0,0,0,32,4,0,0, + 0,0,114,8,0,0,0,114,229,0,0,0,33,4,0,0, 115,2,0,0,0,0,2,122,31,83,111,117,114,99,101,108, 101,115,115,70,105,108,101,76,111,97,100,101,114,46,103,101, 116,95,115,111,117,114,99,101,78,41,6,114,125,0,0,0, 114,124,0,0,0,114,126,0,0,0,114,127,0,0,0,114, 213,0,0,0,114,229,0,0,0,114,5,0,0,0,114,5, 0,0,0,114,5,0,0,0,114,8,0,0,0,114,5,1, - 0,0,12,4,0,0,115,6,0,0,0,8,2,4,2,8, + 0,0,13,4,0,0,115,6,0,0,0,8,2,4,2,8, 16,114,5,1,0,0,99,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,3,0,0,0,64,0,0,0,115, 92,0,0,0,101,0,90,1,100,0,90,2,100,1,90,3, @@ -1640,7 +1640,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 100,0,83,0,114,109,0,0,0,114,159,0,0,0,41,3, 114,118,0,0,0,114,116,0,0,0,114,44,0,0,0,114, 5,0,0,0,114,5,0,0,0,114,8,0,0,0,114,209, - 0,0,0,49,4,0,0,115,4,0,0,0,0,1,6,1, + 0,0,0,50,4,0,0,115,4,0,0,0,0,1,6,1, 122,28,69,120,116,101,110,115,105,111,110,70,105,108,101,76, 111,97,100,101,114,46,95,95,105,110,105,116,95,95,99,2, 0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,2, @@ -1648,7 +1648,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 124,1,106,0,107,2,111,22,124,0,106,1,124,1,106,1, 107,2,83,0,114,109,0,0,0,114,240,0,0,0,114,242, 0,0,0,114,5,0,0,0,114,5,0,0,0,114,8,0, - 0,0,114,243,0,0,0,53,4,0,0,115,6,0,0,0, + 0,0,114,243,0,0,0,54,4,0,0,115,6,0,0,0, 0,1,12,1,10,255,122,26,69,120,116,101,110,115,105,111, 110,70,105,108,101,76,111,97,100,101,114,46,95,95,101,113, 95,95,99,1,0,0,0,0,0,0,0,0,0,0,0,1, @@ -1656,7 +1656,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 116,0,124,0,106,1,131,1,116,0,124,0,106,2,131,1, 65,0,83,0,114,109,0,0,0,114,244,0,0,0,114,246, 0,0,0,114,5,0,0,0,114,5,0,0,0,114,8,0, - 0,0,114,247,0,0,0,57,4,0,0,115,2,0,0,0, + 0,0,114,247,0,0,0,58,4,0,0,115,2,0,0,0, 0,1,122,28,69,120,116,101,110,115,105,111,110,70,105,108, 101,76,111,97,100,101,114,46,95,95,104,97,115,104,95,95, 99,2,0,0,0,0,0,0,0,0,0,0,0,3,0,0, @@ -1673,7 +1673,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 110,97,109,105,99,114,149,0,0,0,114,116,0,0,0,114, 44,0,0,0,41,3,114,118,0,0,0,114,187,0,0,0, 114,216,0,0,0,114,5,0,0,0,114,5,0,0,0,114, - 8,0,0,0,114,212,0,0,0,60,4,0,0,115,14,0, + 8,0,0,0,114,212,0,0,0,61,4,0,0,115,14,0, 0,0,0,2,4,1,6,255,4,2,6,1,8,255,4,2, 122,33,69,120,116,101,110,115,105,111,110,70,105,108,101,76, 111,97,100,101,114,46,99,114,101,97,116,101,95,109,111,100, @@ -1691,7 +1691,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 99,114,149,0,0,0,114,116,0,0,0,114,44,0,0,0, 169,2,114,118,0,0,0,114,216,0,0,0,114,5,0,0, 0,114,5,0,0,0,114,8,0,0,0,114,217,0,0,0, - 68,4,0,0,115,8,0,0,0,0,2,14,1,6,1,8, + 69,4,0,0,115,8,0,0,0,0,2,14,1,6,1,8, 255,122,31,69,120,116,101,110,115,105,111,110,70,105,108,101, 76,111,97,100,101,114,46,101,120,101,99,95,109,111,100,117, 108,101,99,2,0,0,0,0,0,0,0,0,0,0,0,2, @@ -1709,7 +1709,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,0,169,2,114,32,0,0,0,218,6,115,117,102,102, 105,120,169,1,90,9,102,105,108,101,95,110,97,109,101,114, 5,0,0,0,114,8,0,0,0,218,9,60,103,101,110,101, - 120,112,114,62,77,4,0,0,115,4,0,0,0,4,1,2, + 120,112,114,62,78,4,0,0,115,4,0,0,0,4,1,2, 255,122,49,69,120,116,101,110,115,105,111,110,70,105,108,101, 76,111,97,100,101,114,46,105,115,95,112,97,99,107,97,103, 101,46,60,108,111,99,97,108,115,62,46,60,103,101,110,101, @@ -1717,7 +1717,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 218,3,97,110,121,218,18,69,88,84,69,78,83,73,79,78, 95,83,85,70,70,73,88,69,83,114,219,0,0,0,114,5, 0,0,0,114,9,1,0,0,114,8,0,0,0,114,182,0, - 0,0,74,4,0,0,115,8,0,0,0,0,2,14,1,12, + 0,0,75,4,0,0,115,8,0,0,0,0,2,14,1,12, 1,2,255,122,30,69,120,116,101,110,115,105,111,110,70,105, 108,101,76,111,97,100,101,114,46,105,115,95,112,97,99,107, 97,103,101,99,2,0,0,0,0,0,0,0,0,0,0,0, @@ -1728,7 +1728,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 111,116,32,99,114,101,97,116,101,32,97,32,99,111,100,101, 32,111,98,106,101,99,116,46,78,114,5,0,0,0,114,219, 0,0,0,114,5,0,0,0,114,5,0,0,0,114,8,0, - 0,0,114,213,0,0,0,80,4,0,0,115,2,0,0,0, + 0,0,114,213,0,0,0,81,4,0,0,115,2,0,0,0, 0,2,122,28,69,120,116,101,110,115,105,111,110,70,105,108, 101,76,111,97,100,101,114,46,103,101,116,95,99,111,100,101, 99,2,0,0,0,0,0,0,0,0,0,0,0,2,0,0, @@ -1738,14 +1738,14 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 111,100,117,108,101,115,32,104,97,118,101,32,110,111,32,115, 111,117,114,99,101,32,99,111,100,101,46,78,114,5,0,0, 0,114,219,0,0,0,114,5,0,0,0,114,5,0,0,0, - 114,8,0,0,0,114,229,0,0,0,84,4,0,0,115,2, + 114,8,0,0,0,114,229,0,0,0,85,4,0,0,115,2, 0,0,0,0,2,122,30,69,120,116,101,110,115,105,111,110, 70,105,108,101,76,111,97,100,101,114,46,103,101,116,95,115, 111,117,114,99,101,99,2,0,0,0,0,0,0,0,0,0, 0,0,2,0,0,0,1,0,0,0,67,0,0,0,115,6, 0,0,0,124,0,106,0,83,0,114,250,0,0,0,114,48, 0,0,0,114,219,0,0,0,114,5,0,0,0,114,5,0, - 0,0,114,8,0,0,0,114,179,0,0,0,88,4,0,0, + 0,0,114,8,0,0,0,114,179,0,0,0,89,4,0,0, 115,2,0,0,0,0,3,122,32,69,120,116,101,110,115,105, 111,110,70,105,108,101,76,111,97,100,101,114,46,103,101,116, 95,102,105,108,101,110,97,109,101,78,41,14,114,125,0,0, @@ -1754,7 +1754,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 212,0,0,0,114,217,0,0,0,114,182,0,0,0,114,213, 0,0,0,114,229,0,0,0,114,136,0,0,0,114,179,0, 0,0,114,5,0,0,0,114,5,0,0,0,114,5,0,0, - 0,114,8,0,0,0,114,252,0,0,0,41,4,0,0,115, + 0,114,8,0,0,0,114,252,0,0,0,42,4,0,0,115, 22,0,0,0,8,2,4,6,8,4,8,4,8,3,8,8, 8,6,8,6,8,4,8,4,2,1,114,252,0,0,0,99, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, @@ -1797,7 +1797,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 114,169,4,114,118,0,0,0,114,116,0,0,0,114,44,0, 0,0,90,11,112,97,116,104,95,102,105,110,100,101,114,114, 5,0,0,0,114,5,0,0,0,114,8,0,0,0,114,209, - 0,0,0,101,4,0,0,115,8,0,0,0,0,1,6,1, + 0,0,0,102,4,0,0,115,8,0,0,0,0,1,6,1, 6,1,14,1,122,23,95,78,97,109,101,115,112,97,99,101, 80,97,116,104,46,95,95,105,110,105,116,95,95,99,1,0, 0,0,0,0,0,0,0,0,0,0,4,0,0,0,3,0, @@ -1814,7 +1814,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 4,114,118,0,0,0,114,4,1,0,0,218,3,100,111,116, 90,2,109,101,114,5,0,0,0,114,5,0,0,0,114,8, 0,0,0,218,23,95,102,105,110,100,95,112,97,114,101,110, - 116,95,112,97,116,104,95,110,97,109,101,115,107,4,0,0, + 116,95,112,97,116,104,95,110,97,109,101,115,108,4,0,0, 115,8,0,0,0,0,2,18,1,8,2,4,3,122,38,95, 78,97,109,101,115,112,97,99,101,80,97,116,104,46,95,102, 105,110,100,95,112,97,114,101,110,116,95,112,97,116,104,95, @@ -1827,7 +1827,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 114,118,0,0,0,90,18,112,97,114,101,110,116,95,109,111, 100,117,108,101,95,110,97,109,101,90,14,112,97,116,104,95, 97,116,116,114,95,110,97,109,101,114,5,0,0,0,114,5, - 0,0,0,114,8,0,0,0,114,16,1,0,0,117,4,0, + 0,0,0,114,8,0,0,0,114,16,1,0,0,118,4,0, 0,115,4,0,0,0,0,1,12,1,122,31,95,78,97,109, 101,115,112,97,99,101,80,97,116,104,46,95,103,101,116,95, 112,97,114,101,110,116,95,112,97,116,104,99,1,0,0,0, @@ -1843,7 +1843,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,0,114,15,1,0,0,41,3,114,118,0,0,0,90, 11,112,97,114,101,110,116,95,112,97,116,104,114,187,0,0, 0,114,5,0,0,0,114,5,0,0,0,114,8,0,0,0, - 218,12,95,114,101,99,97,108,99,117,108,97,116,101,121,4, + 218,12,95,114,101,99,97,108,99,117,108,97,116,101,122,4, 0,0,115,16,0,0,0,0,2,12,1,10,1,14,3,18, 1,6,1,8,1,6,1,122,27,95,78,97,109,101,115,112, 97,99,101,80,97,116,104,46,95,114,101,99,97,108,99,117, @@ -1852,7 +1852,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,116,0,124,0,160,1,161,0,131,1,83,0,114,109, 0,0,0,41,2,218,4,105,116,101,114,114,23,1,0,0, 114,246,0,0,0,114,5,0,0,0,114,5,0,0,0,114, - 8,0,0,0,218,8,95,95,105,116,101,114,95,95,134,4, + 8,0,0,0,218,8,95,95,105,116,101,114,95,95,135,4, 0,0,115,2,0,0,0,0,1,122,23,95,78,97,109,101, 115,112,97,99,101,80,97,116,104,46,95,95,105,116,101,114, 95,95,99,2,0,0,0,0,0,0,0,0,0,0,0,2, @@ -1861,7 +1861,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,169,1,114,23,1,0,0,41,2,114,118,0,0,0,218, 5,105,110,100,101,120,114,5,0,0,0,114,5,0,0,0, 114,8,0,0,0,218,11,95,95,103,101,116,105,116,101,109, - 95,95,137,4,0,0,115,2,0,0,0,0,1,122,26,95, + 95,95,138,4,0,0,115,2,0,0,0,0,1,122,26,95, 78,97,109,101,115,112,97,99,101,80,97,116,104,46,95,95, 103,101,116,105,116,101,109,95,95,99,3,0,0,0,0,0, 0,0,0,0,0,0,3,0,0,0,3,0,0,0,67,0, @@ -1869,7 +1869,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,100,0,83,0,114,109,0,0,0,41,1,114,15,1,0, 0,41,3,114,118,0,0,0,114,27,1,0,0,114,44,0, 0,0,114,5,0,0,0,114,5,0,0,0,114,8,0,0, - 0,218,11,95,95,115,101,116,105,116,101,109,95,95,140,4, + 0,218,11,95,95,115,101,116,105,116,101,109,95,95,141,4, 0,0,115,2,0,0,0,0,1,122,26,95,78,97,109,101, 115,112,97,99,101,80,97,116,104,46,95,95,115,101,116,105, 116,101,109,95,95,99,1,0,0,0,0,0,0,0,0,0, @@ -1877,7 +1877,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,0,116,0,124,0,160,1,161,0,131,1,83,0,114, 109,0,0,0,41,2,114,23,0,0,0,114,23,1,0,0, 114,246,0,0,0,114,5,0,0,0,114,5,0,0,0,114, - 8,0,0,0,218,7,95,95,108,101,110,95,95,143,4,0, + 8,0,0,0,218,7,95,95,108,101,110,95,95,144,4,0, 0,115,2,0,0,0,0,1,122,22,95,78,97,109,101,115, 112,97,99,101,80,97,116,104,46,95,95,108,101,110,95,95, 99,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0, @@ -1886,7 +1886,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 78,97,109,101,115,112,97,99,101,80,97,116,104,40,123,33, 114,125,41,41,2,114,62,0,0,0,114,15,1,0,0,114, 246,0,0,0,114,5,0,0,0,114,5,0,0,0,114,8, - 0,0,0,218,8,95,95,114,101,112,114,95,95,146,4,0, + 0,0,0,218,8,95,95,114,101,112,114,95,95,147,4,0, 0,115,2,0,0,0,0,1,122,23,95,78,97,109,101,115, 112,97,99,101,80,97,116,104,46,95,95,114,101,112,114,95, 95,99,2,0,0,0,0,0,0,0,0,0,0,0,2,0, @@ -1894,7 +1894,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 1,124,0,160,0,161,0,118,0,83,0,114,109,0,0,0, 114,26,1,0,0,169,2,114,118,0,0,0,218,4,105,116, 101,109,114,5,0,0,0,114,5,0,0,0,114,8,0,0, - 0,218,12,95,95,99,111,110,116,97,105,110,115,95,95,149, + 0,218,12,95,95,99,111,110,116,97,105,110,115,95,95,150, 4,0,0,115,2,0,0,0,0,1,122,27,95,78,97,109, 101,115,112,97,99,101,80,97,116,104,46,95,95,99,111,110, 116,97,105,110,115,95,95,99,2,0,0,0,0,0,0,0, @@ -1902,7 +1902,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 115,16,0,0,0,124,0,106,0,160,1,124,1,161,1,1, 0,100,0,83,0,114,109,0,0,0,41,2,114,15,1,0, 0,114,186,0,0,0,114,32,1,0,0,114,5,0,0,0, - 114,5,0,0,0,114,8,0,0,0,114,186,0,0,0,152, + 114,5,0,0,0,114,8,0,0,0,114,186,0,0,0,153, 4,0,0,115,2,0,0,0,0,1,122,21,95,78,97,109, 101,115,112,97,99,101,80,97,116,104,46,97,112,112,101,110, 100,78,41,15,114,125,0,0,0,114,124,0,0,0,114,126, @@ -1911,7 +1911,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,114,28,1,0,0,114,29,1,0,0,114,30,1,0,0, 114,31,1,0,0,114,34,1,0,0,114,186,0,0,0,114, 5,0,0,0,114,5,0,0,0,114,5,0,0,0,114,8, - 0,0,0,114,13,1,0,0,94,4,0,0,115,24,0,0, + 0,0,0,114,13,1,0,0,95,4,0,0,115,24,0,0, 0,8,1,4,6,8,6,8,10,8,4,8,13,8,3,8, 3,8,3,8,3,8,3,8,3,114,13,1,0,0,99,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3, @@ -1927,7 +1927,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 124,1,124,2,124,3,131,3,124,0,95,1,100,0,83,0, 114,109,0,0,0,41,2,114,13,1,0,0,114,15,1,0, 0,114,19,1,0,0,114,5,0,0,0,114,5,0,0,0, - 114,8,0,0,0,114,209,0,0,0,158,4,0,0,115,2, + 114,8,0,0,0,114,209,0,0,0,159,4,0,0,115,2, 0,0,0,0,1,122,25,95,78,97,109,101,115,112,97,99, 101,76,111,97,100,101,114,46,95,95,105,110,105,116,95,95, 99,2,0,0,0,0,0,0,0,0,0,0,0,2,0,0, @@ -1945,20 +1945,20 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,0,114,125,0,0,0,41,2,114,193,0,0,0,114, 216,0,0,0,114,5,0,0,0,114,5,0,0,0,114,8, 0,0,0,218,11,109,111,100,117,108,101,95,114,101,112,114, - 161,4,0,0,115,2,0,0,0,0,7,122,28,95,78,97, + 162,4,0,0,115,2,0,0,0,0,7,122,28,95,78,97, 109,101,115,112,97,99,101,76,111,97,100,101,114,46,109,111, 100,117,108,101,95,114,101,112,114,99,2,0,0,0,0,0, 0,0,0,0,0,0,2,0,0,0,1,0,0,0,67,0, 0,0,115,4,0,0,0,100,1,83,0,41,2,78,84,114, 5,0,0,0,114,219,0,0,0,114,5,0,0,0,114,5, - 0,0,0,114,8,0,0,0,114,182,0,0,0,170,4,0, + 0,0,0,114,8,0,0,0,114,182,0,0,0,171,4,0, 0,115,2,0,0,0,0,1,122,27,95,78,97,109,101,115, 112,97,99,101,76,111,97,100,101,114,46,105,115,95,112,97, 99,107,97,103,101,99,2,0,0,0,0,0,0,0,0,0, 0,0,2,0,0,0,1,0,0,0,67,0,0,0,115,4, 0,0,0,100,1,83,0,41,2,78,114,40,0,0,0,114, 5,0,0,0,114,219,0,0,0,114,5,0,0,0,114,5, - 0,0,0,114,8,0,0,0,114,229,0,0,0,173,4,0, + 0,0,0,114,8,0,0,0,114,229,0,0,0,174,4,0, 0,115,2,0,0,0,0,1,122,27,95,78,97,109,101,115, 112,97,99,101,76,111,97,100,101,114,46,103,101,116,95,115, 111,117,114,99,101,99,2,0,0,0,0,0,0,0,0,0, @@ -1968,20 +1968,20 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 114,105,110,103,62,114,215,0,0,0,84,41,1,114,231,0, 0,0,41,1,114,232,0,0,0,114,219,0,0,0,114,5, 0,0,0,114,5,0,0,0,114,8,0,0,0,114,213,0, - 0,0,176,4,0,0,115,2,0,0,0,0,1,122,25,95, + 0,0,177,4,0,0,115,2,0,0,0,0,1,122,25,95, 78,97,109,101,115,112,97,99,101,76,111,97,100,101,114,46, 103,101,116,95,99,111,100,101,99,2,0,0,0,0,0,0, 0,0,0,0,0,2,0,0,0,1,0,0,0,67,0,0, 0,115,4,0,0,0,100,1,83,0,114,210,0,0,0,114, 5,0,0,0,114,211,0,0,0,114,5,0,0,0,114,5, - 0,0,0,114,8,0,0,0,114,212,0,0,0,179,4,0, + 0,0,0,114,8,0,0,0,114,212,0,0,0,180,4,0, 0,115,2,0,0,0,0,1,122,30,95,78,97,109,101,115, 112,97,99,101,76,111,97,100,101,114,46,99,114,101,97,116, 101,95,109,111,100,117,108,101,99,2,0,0,0,0,0,0, 0,0,0,0,0,2,0,0,0,1,0,0,0,67,0,0, 0,115,4,0,0,0,100,0,83,0,114,109,0,0,0,114, 5,0,0,0,114,6,1,0,0,114,5,0,0,0,114,5, - 0,0,0,114,8,0,0,0,114,217,0,0,0,182,4,0, + 0,0,0,114,8,0,0,0,114,217,0,0,0,183,4,0, 0,115,2,0,0,0,0,1,122,28,95,78,97,109,101,115, 112,97,99,101,76,111,97,100,101,114,46,101,120,101,99,95, 109,111,100,117,108,101,99,2,0,0,0,0,0,0,0,0, @@ -1999,7 +1999,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 116,104,32,112,97,116,104,32,123,33,114,125,41,4,114,134, 0,0,0,114,149,0,0,0,114,15,1,0,0,114,218,0, 0,0,114,219,0,0,0,114,5,0,0,0,114,5,0,0, - 0,114,8,0,0,0,114,220,0,0,0,185,4,0,0,115, + 0,114,8,0,0,0,114,220,0,0,0,186,4,0,0,115, 8,0,0,0,0,7,6,1,4,255,4,2,122,28,95,78, 97,109,101,115,112,97,99,101,76,111,97,100,101,114,46,108, 111,97,100,95,109,111,100,117,108,101,78,41,12,114,125,0, @@ -2008,7 +2008,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 114,229,0,0,0,114,213,0,0,0,114,212,0,0,0,114, 217,0,0,0,114,220,0,0,0,114,5,0,0,0,114,5, 0,0,0,114,5,0,0,0,114,8,0,0,0,114,35,1, - 0,0,157,4,0,0,115,18,0,0,0,8,1,8,3,2, + 0,0,158,4,0,0,115,18,0,0,0,8,1,8,3,2, 1,10,8,8,3,8,3,8,3,8,3,8,3,114,35,1, 0,0,99,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,4,0,0,0,64,0,0,0,115,118,0,0,0, @@ -2045,7 +2045,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 114,128,0,0,0,114,38,1,0,0,41,3,114,193,0,0, 0,114,116,0,0,0,218,6,102,105,110,100,101,114,114,5, 0,0,0,114,5,0,0,0,114,8,0,0,0,114,38,1, - 0,0,203,4,0,0,115,10,0,0,0,0,4,22,1,8, + 0,0,204,4,0,0,115,10,0,0,0,0,4,22,1,8, 1,10,1,10,1,122,28,80,97,116,104,70,105,110,100,101, 114,46,105,110,118,97,108,105,100,97,116,101,95,99,97,99, 104,101,115,99,2,0,0,0,0,0,0,0,0,0,0,0, @@ -2065,7 +2065,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 117,0,0,0,41,3,114,193,0,0,0,114,44,0,0,0, 90,4,104,111,111,107,114,5,0,0,0,114,5,0,0,0, 114,8,0,0,0,218,11,95,112,97,116,104,95,104,111,111, - 107,115,213,4,0,0,115,16,0,0,0,0,3,16,1,12, + 107,115,214,4,0,0,115,16,0,0,0,0,3,16,1,12, 1,10,1,2,1,14,1,12,1,10,2,122,22,80,97,116, 104,70,105,110,100,101,114,46,95,112,97,116,104,95,104,111, 111,107,115,99,2,0,0,0,0,0,0,0,0,0,0,0, @@ -2096,7 +2096,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 114,114,111,114,114,44,1,0,0,41,3,114,193,0,0,0, 114,44,0,0,0,114,42,1,0,0,114,5,0,0,0,114, 5,0,0,0,114,8,0,0,0,218,20,95,112,97,116,104, - 95,105,109,112,111,114,116,101,114,95,99,97,99,104,101,226, + 95,105,109,112,111,114,116,101,114,95,99,97,99,104,101,227, 4,0,0,115,22,0,0,0,0,8,8,1,2,1,12,1, 12,3,8,1,2,1,14,1,12,1,10,1,16,1,122,31, 80,97,116,104,70,105,110,100,101,114,46,95,112,97,116,104, @@ -2114,7 +2114,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,0,114,139,0,0,0,114,42,1,0,0,114,140,0, 0,0,114,141,0,0,0,114,187,0,0,0,114,5,0,0, 0,114,5,0,0,0,114,8,0,0,0,218,16,95,108,101, - 103,97,99,121,95,103,101,116,95,115,112,101,99,248,4,0, + 103,97,99,121,95,103,101,116,95,115,112,101,99,249,4,0, 0,115,18,0,0,0,0,4,10,1,16,2,10,1,4,1, 8,1,12,1,12,1,6,1,122,27,80,97,116,104,70,105, 110,100,101,114,46,95,108,101,103,97,99,121,95,103,101,116, @@ -2146,7 +2146,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 97,116,104,90,5,101,110,116,114,121,114,42,1,0,0,114, 187,0,0,0,114,141,0,0,0,114,5,0,0,0,114,5, 0,0,0,114,8,0,0,0,218,9,95,103,101,116,95,115, - 112,101,99,7,5,0,0,115,40,0,0,0,0,5,4,1, + 112,101,99,8,5,0,0,115,40,0,0,0,0,5,4,1, 8,1,14,1,2,1,10,1,8,1,10,1,14,2,12,1, 8,1,2,1,10,1,8,1,6,1,8,1,8,5,12,2, 12,1,6,1,122,20,80,97,116,104,70,105,110,100,101,114, @@ -2173,7 +2173,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 193,0,0,0,114,139,0,0,0,114,44,0,0,0,114,202, 0,0,0,114,187,0,0,0,114,50,1,0,0,114,5,0, 0,0,114,5,0,0,0,114,8,0,0,0,114,203,0,0, - 0,39,5,0,0,115,26,0,0,0,0,6,8,1,6,1, + 0,40,5,0,0,115,26,0,0,0,0,6,8,1,6,1, 14,1,8,1,4,1,10,1,6,1,4,3,6,1,16,1, 4,2,4,2,122,20,80,97,116,104,70,105,110,100,101,114, 46,102,105,110,100,95,115,112,101,99,99,3,0,0,0,0, @@ -2193,7 +2193,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 41,32,105,110,115,116,101,97,100,46,10,10,32,32,32,32, 32,32,32,32,78,114,204,0,0,0,114,205,0,0,0,114, 5,0,0,0,114,5,0,0,0,114,8,0,0,0,114,206, - 0,0,0,63,5,0,0,115,8,0,0,0,0,8,12,1, + 0,0,0,64,5,0,0,115,8,0,0,0,0,8,12,1, 8,1,4,1,122,22,80,97,116,104,70,105,110,100,101,114, 46,102,105,110,100,95,109,111,100,117,108,101,99,1,0,0, 0,0,0,0,0,0,0,0,0,4,0,0,0,4,0,0, @@ -2225,7 +2225,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 98,117,116,105,111,110,115,41,4,114,193,0,0,0,114,119, 0,0,0,114,120,0,0,0,114,52,1,0,0,114,5,0, 0,0,114,5,0,0,0,114,8,0,0,0,114,53,1,0, - 0,76,5,0,0,115,4,0,0,0,0,10,12,1,122,29, + 0,77,5,0,0,115,4,0,0,0,0,10,12,1,122,29, 80,97,116,104,70,105,110,100,101,114,46,102,105,110,100,95, 100,105,115,116,114,105,98,117,116,105,111,110,115,41,1,78, 41,2,78,78,41,1,78,41,13,114,125,0,0,0,114,124, @@ -2234,7 +2234,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,114,48,1,0,0,114,51,1,0,0,114,203,0,0,0, 114,206,0,0,0,114,53,1,0,0,114,5,0,0,0,114, 5,0,0,0,114,5,0,0,0,114,8,0,0,0,114,37, - 1,0,0,199,4,0,0,115,34,0,0,0,8,2,4,2, + 1,0,0,200,4,0,0,115,34,0,0,0,8,2,4,2, 2,1,10,9,2,1,10,12,2,1,10,21,2,1,10,14, 2,1,12,31,2,1,12,23,2,1,12,12,2,1,114,37, 1,0,0,99,0,0,0,0,0,0,0,0,0,0,0,0, @@ -2279,7 +2279,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 2,86,0,1,0,113,2,100,0,83,0,114,109,0,0,0, 114,5,0,0,0,114,7,1,0,0,169,1,114,140,0,0, 0,114,5,0,0,0,114,8,0,0,0,114,10,1,0,0, - 105,5,0,0,243,0,0,0,0,122,38,70,105,108,101,70, + 106,5,0,0,243,0,0,0,0,122,38,70,105,108,101,70, 105,110,100,101,114,46,95,95,105,110,105,116,95,95,46,60, 108,111,99,97,108,115,62,46,60,103,101,110,101,120,112,114, 62,114,71,0,0,0,114,104,0,0,0,78,41,7,114,167, @@ -2291,7 +2291,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,218,14,108,111,97,100,101,114,95,100,101,116,97,105, 108,115,90,7,108,111,97,100,101,114,115,114,189,0,0,0, 114,5,0,0,0,114,55,1,0,0,114,8,0,0,0,114, - 209,0,0,0,99,5,0,0,115,16,0,0,0,0,4,4, + 209,0,0,0,100,5,0,0,115,16,0,0,0,0,4,4, 1,12,1,26,1,6,2,10,1,6,1,8,1,122,19,70, 105,108,101,70,105,110,100,101,114,46,95,95,105,110,105,116, 95,95,99,1,0,0,0,0,0,0,0,0,0,0,0,1, @@ -2301,7 +2301,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 101,99,116,111,114,121,32,109,116,105,109,101,46,114,104,0, 0,0,78,41,1,114,58,1,0,0,114,246,0,0,0,114, 5,0,0,0,114,5,0,0,0,114,8,0,0,0,114,38, - 1,0,0,113,5,0,0,115,2,0,0,0,0,2,122,28, + 1,0,0,114,5,0,0,115,2,0,0,0,0,2,122,28, 70,105,108,101,70,105,110,100,101,114,46,105,110,118,97,108, 105,100,97,116,101,95,99,97,99,104,101,115,99,2,0,0, 0,0,0,0,0,0,0,0,0,3,0,0,0,3,0,0, @@ -2324,7 +2324,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 114,203,0,0,0,114,140,0,0,0,114,178,0,0,0,41, 3,114,118,0,0,0,114,139,0,0,0,114,187,0,0,0, 114,5,0,0,0,114,5,0,0,0,114,8,0,0,0,114, - 137,0,0,0,119,5,0,0,115,8,0,0,0,0,7,10, + 137,0,0,0,120,5,0,0,115,8,0,0,0,0,7,10, 1,8,1,8,1,122,22,70,105,108,101,70,105,110,100,101, 114,46,102,105,110,100,95,108,111,97,100,101,114,99,6,0, 0,0,0,0,0,0,0,0,0,0,7,0,0,0,6,0, @@ -2334,7 +2334,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 190,0,0,0,41,7,114,118,0,0,0,114,188,0,0,0, 114,139,0,0,0,114,44,0,0,0,90,4,115,109,115,108, 114,202,0,0,0,114,140,0,0,0,114,5,0,0,0,114, - 5,0,0,0,114,8,0,0,0,114,51,1,0,0,131,5, + 5,0,0,0,114,8,0,0,0,114,51,1,0,0,132,5, 0,0,115,8,0,0,0,0,1,10,1,8,1,2,255,122, 20,70,105,108,101,70,105,110,100,101,114,46,95,103,101,116, 95,115,112,101,99,78,99,3,0,0,0,0,0,0,0,0, @@ -2389,7 +2389,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 13,105,110,105,116,95,102,105,108,101,110,97,109,101,90,9, 102,117,108,108,95,112,97,116,104,114,187,0,0,0,114,5, 0,0,0,114,5,0,0,0,114,8,0,0,0,114,203,0, - 0,0,136,5,0,0,115,72,0,0,0,0,5,4,1,14, + 0,0,137,5,0,0,115,72,0,0,0,0,5,4,1,14, 1,2,1,24,1,12,1,10,1,10,1,8,1,6,2,6, 1,6,1,10,2,6,1,4,2,8,1,12,1,14,1,8, 1,10,1,8,1,24,4,8,2,14,1,16,1,16,1,12, @@ -2420,7 +2420,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 124,1,160,0,161,0,146,2,113,4,83,0,114,5,0,0, 0,41,1,114,105,0,0,0,41,2,114,32,0,0,0,90, 2,102,110,114,5,0,0,0,114,5,0,0,0,114,8,0, - 0,0,218,9,60,115,101,116,99,111,109,112,62,213,5,0, + 0,0,218,9,60,115,101,116,99,111,109,112,62,214,5,0, 0,114,56,1,0,0,122,41,70,105,108,101,70,105,110,100, 101,114,46,95,102,105,108,108,95,99,97,99,104,101,46,60, 108,111,99,97,108,115,62,46,60,115,101,116,99,111,109,112, @@ -2437,7 +2437,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 120,95,99,111,110,116,101,110,116,115,114,33,1,0,0,114, 116,0,0,0,114,20,1,0,0,114,8,1,0,0,90,8, 110,101,119,95,110,97,109,101,114,5,0,0,0,114,5,0, - 0,0,114,8,0,0,0,114,63,1,0,0,184,5,0,0, + 0,0,114,8,0,0,0,114,63,1,0,0,185,5,0,0, 115,34,0,0,0,0,2,6,1,2,1,22,1,18,3,10, 3,12,1,12,7,6,1,8,1,16,1,4,1,18,2,4, 1,12,1,6,1,12,1,122,22,70,105,108,101,70,105,110, @@ -2476,14 +2476,14 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,0,169,2,114,193,0,0,0,114,62,1,0,0,114, 5,0,0,0,114,8,0,0,0,218,24,112,97,116,104,95, 104,111,111,107,95,102,111,114,95,70,105,108,101,70,105,110, - 100,101,114,225,5,0,0,115,6,0,0,0,0,2,8,1, + 100,101,114,226,5,0,0,115,6,0,0,0,0,2,8,1, 12,1,122,54,70,105,108,101,70,105,110,100,101,114,46,112, 97,116,104,95,104,111,111,107,46,60,108,111,99,97,108,115, 62,46,112,97,116,104,95,104,111,111,107,95,102,111,114,95, 70,105,108,101,70,105,110,100,101,114,114,5,0,0,0,41, 3,114,193,0,0,0,114,62,1,0,0,114,69,1,0,0, 114,5,0,0,0,114,68,1,0,0,114,8,0,0,0,218, - 9,112,97,116,104,95,104,111,111,107,215,5,0,0,115,4, + 9,112,97,116,104,95,104,111,111,107,216,5,0,0,115,4, 0,0,0,0,10,14,6,122,20,70,105,108,101,70,105,110, 100,101,114,46,112,97,116,104,95,104,111,111,107,99,1,0, 0,0,0,0,0,0,0,0,0,0,1,0,0,0,3,0, @@ -2492,7 +2492,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 70,105,110,100,101,114,40,123,33,114,125,41,41,2,114,62, 0,0,0,114,44,0,0,0,114,246,0,0,0,114,5,0, 0,0,114,5,0,0,0,114,8,0,0,0,114,31,1,0, - 0,233,5,0,0,115,2,0,0,0,0,1,122,19,70,105, + 0,234,5,0,0,115,2,0,0,0,0,1,122,19,70,105, 108,101,70,105,110,100,101,114,46,95,95,114,101,112,114,95, 95,41,1,78,41,15,114,125,0,0,0,114,124,0,0,0, 114,126,0,0,0,114,127,0,0,0,114,209,0,0,0,114, @@ -2500,7 +2500,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,0,114,51,1,0,0,114,203,0,0,0,114,63,1, 0,0,114,207,0,0,0,114,70,1,0,0,114,31,1,0, 0,114,5,0,0,0,114,5,0,0,0,114,5,0,0,0, - 114,8,0,0,0,114,54,1,0,0,90,5,0,0,115,22, + 114,8,0,0,0,114,54,1,0,0,91,5,0,0,115,22, 0,0,0,8,2,4,7,8,14,8,4,4,2,8,12,8, 5,10,48,8,31,2,1,10,17,114,54,1,0,0,99,4, 0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,8, @@ -2523,7 +2523,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,90,8,112,97,116,104,110,97,109,101,90,9,99,112,97, 116,104,110,97,109,101,114,140,0,0,0,114,187,0,0,0, 114,5,0,0,0,114,5,0,0,0,114,8,0,0,0,218, - 14,95,102,105,120,95,117,112,95,109,111,100,117,108,101,239, + 14,95,102,105,120,95,117,112,95,109,111,100,117,108,101,240, 5,0,0,115,34,0,0,0,0,2,10,1,10,1,4,1, 4,1,8,1,8,1,12,2,10,1,4,1,14,1,2,1, 8,1,8,1,8,1,12,1,12,2,114,75,1,0,0,99, @@ -2543,7 +2543,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 114,5,1,0,0,114,88,0,0,0,41,3,90,10,101,120, 116,101,110,115,105,111,110,115,90,6,115,111,117,114,99,101, 90,8,98,121,116,101,99,111,100,101,114,5,0,0,0,114, - 5,0,0,0,114,8,0,0,0,114,184,0,0,0,6,6, + 5,0,0,0,114,8,0,0,0,114,184,0,0,0,7,6, 0,0,115,8,0,0,0,0,5,12,1,8,1,8,1,114, 184,0,0,0,99,1,0,0,0,0,0,0,0,0,0,0, 0,10,0,0,0,9,0,0,0,67,0,0,0,115,130,1, @@ -2591,7 +2591,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 100,0,107,2,86,0,1,0,113,2,100,1,83,0,41,2, 114,39,0,0,0,78,41,1,114,23,0,0,0,41,2,114, 32,0,0,0,114,94,0,0,0,114,5,0,0,0,114,5, - 0,0,0,114,8,0,0,0,114,10,1,0,0,35,6,0, + 0,0,0,114,8,0,0,0,114,10,1,0,0,36,6,0, 0,114,56,1,0,0,122,25,95,115,101,116,117,112,46,60, 108,111,99,97,108,115,62,46,60,103,101,110,101,120,112,114, 62,114,73,0,0,0,122,30,105,109,112,111,114,116,108,105, @@ -2603,7 +2603,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,93,14,125,1,100,0,124,1,155,0,157,2,146,2,113, 4,83,0,41,1,114,74,0,0,0,114,5,0,0,0,41, 2,114,32,0,0,0,218,1,115,114,5,0,0,0,114,5, - 0,0,0,114,8,0,0,0,114,64,1,0,0,52,6,0, + 0,0,0,114,8,0,0,0,114,64,1,0,0,53,6,0, 0,114,56,1,0,0,122,25,95,115,101,116,117,112,46,60, 108,111,99,97,108,115,62,46,60,115,101,116,99,111,109,112, 62,41,3,114,64,0,0,0,114,75,0,0,0,114,160,0, @@ -2624,7 +2624,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 110,97,109,101,115,90,12,98,117,105,108,116,105,110,95,110, 97,109,101,90,14,98,117,105,108,116,105,110,95,109,111,100, 117,108,101,114,5,0,0,0,114,5,0,0,0,114,8,0, - 0,0,218,6,95,115,101,116,117,112,17,6,0,0,115,70, + 0,0,218,6,95,115,101,116,117,112,18,6,0,0,115,70, 0,0,0,0,8,4,1,6,1,6,2,10,3,22,1,12, 2,22,1,8,1,10,1,10,1,6,2,2,1,10,1,10, 1,12,1,10,2,8,2,12,1,12,1,18,1,22,3,8, @@ -2644,7 +2644,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,114,37,1,0,0,41,2,114,81,1,0,0,90,17,115, 117,112,112,111,114,116,101,100,95,108,111,97,100,101,114,115, 114,5,0,0,0,114,5,0,0,0,114,8,0,0,0,218, - 8,95,105,110,115,116,97,108,108,74,6,0,0,115,8,0, + 8,95,105,110,115,116,97,108,108,75,6,0,0,115,8,0, 0,0,0,2,8,1,6,1,20,1,114,84,1,0,0,41, 1,114,60,0,0,0,41,1,78,41,3,78,78,78,41,2, 114,73,0,0,0,114,73,0,0,0,41,1,84,41,1,78, @@ -2678,7 +2678,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 100,117,108,101,62,1,0,0,0,115,126,0,0,0,4,22, 4,1,4,1,2,1,2,255,4,4,8,17,8,5,8,5, 8,6,8,6,8,12,8,10,8,9,8,5,8,7,8,9, - 10,22,10,127,0,20,16,1,12,2,4,1,4,2,6,2, + 10,22,10,127,0,21,16,1,12,2,4,1,4,2,6,2, 6,2,8,2,16,71,8,40,8,19,8,12,8,12,8,28, 8,17,8,33,8,28,8,24,10,13,10,10,10,11,8,14, 6,3,4,1,2,255,12,68,14,64,14,29,16,127,0,17, From webhook-mailer at python.org Sun Oct 11 02:10:29 2020 From: webhook-mailer at python.org (abdo) Date: Sun, 11 Oct 2020 06:10:29 -0000 Subject: [Python-checkins] Fix typo in typing.rst (GH-22625) Message-ID: https://github.com/python/cpython/commit/0ff8a3b374286d2218fc18f47556a5ace202dad3 commit: 0ff8a3b374286d2218fc18f47556a5ace202dad3 branch: master author: abdo committer: GitHub date: 2020-10-11T11:40:21+05:30 summary: Fix typo in typing.rst (GH-22625) files: M Doc/library/typing.rst diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index f4b2718cdc2f8..6111603a995c5 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -1723,7 +1723,7 @@ Constant If ``from __future__ import annotations`` is used in Python 3.7 or later, annotations are not evaluated at function definition time. - Instead, the are stored as strings in ``__annotations__``, + Instead, they are stored as strings in ``__annotations__``, This makes it unnecessary to use quotes around the annotation. (see :pep:`563`). From webhook-mailer at python.org Sun Oct 11 04:06:52 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Sun, 11 Oct 2020 08:06:52 -0000 Subject: [Python-checkins] Fix typo in typing.rst (GH-22625) Message-ID: https://github.com/python/cpython/commit/f1c70cf9e850b58a5b676e469dfc0c01e3f6778a commit: f1c70cf9e850b58a5b676e469dfc0c01e3f6778a branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-11T01:06:48-07:00 summary: Fix typo in typing.rst (GH-22625) (cherry picked from commit 0ff8a3b374286d2218fc18f47556a5ace202dad3) Co-authored-by: abdo files: M Doc/library/typing.rst diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 3900e49679a38..108d2eeeaf41f 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -1691,7 +1691,7 @@ Constant If ``from __future__ import annotations`` is used in Python 3.7 or later, annotations are not evaluated at function definition time. - Instead, the are stored as strings in ``__annotations__``, + Instead, they are stored as strings in ``__annotations__``, This makes it unnecessary to use quotes around the annotation. (see :pep:`563`). From webhook-mailer at python.org Sun Oct 11 08:30:48 2020 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Sun, 11 Oct 2020 12:30:48 -0000 Subject: [Python-checkins] bpo-42002: Clean up initialization of the sys module. (GH-22642) Message-ID: https://github.com/python/cpython/commit/fa1d83db62a545580d9a1a585b2c1fb55961a5c3 commit: fa1d83db62a545580d9a1a585b2c1fb55961a5c3 branch: master author: Serhiy Storchaka committer: GitHub date: 2020-10-11T15:30:43+03:00 summary: bpo-42002: Clean up initialization of the sys module. (GH-22642) Makes the code clearer and make errors handling more correct. files: M Python/sysmodule.c diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 9fcdb5dbc49b1..bfcf4e85140a8 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -2649,18 +2649,7 @@ static struct PyModuleDef sysmodule = { }; /* Updating the sys namespace, returning NULL pointer on error */ -#define SET_SYS_FROM_STRING_BORROW(key, value) \ - do { \ - PyObject *v = (value); \ - if (v == NULL) { \ - goto err_occurred; \ - } \ - res = PyDict_SetItemString(sysdict, key, v); \ - if (res < 0) { \ - goto err_occurred; \ - } \ - } while (0) -#define SET_SYS_FROM_STRING(key, value) \ +#define SET_SYS(key, value) \ do { \ PyObject *v = (value); \ if (v == NULL) { \ @@ -2673,6 +2662,9 @@ static struct PyModuleDef sysmodule = { } \ } while (0) +#define SET_SYS_FROM_STRING(key, value) \ + SET_SYS(key, PyUnicode_FromString(value)) + static PyStatus _PySys_InitCore(PyThreadState *tstate, PyObject *sysdict) { @@ -2681,65 +2673,48 @@ _PySys_InitCore(PyThreadState *tstate, PyObject *sysdict) /* stdin/stdout/stderr are set in pylifecycle.c */ - SET_SYS_FROM_STRING_BORROW("__displayhook__", - PyDict_GetItemString(sysdict, "displayhook")); - SET_SYS_FROM_STRING_BORROW("__excepthook__", - PyDict_GetItemString(sysdict, "excepthook")); - SET_SYS_FROM_STRING_BORROW( - "__breakpointhook__", - PyDict_GetItemString(sysdict, "breakpointhook")); - SET_SYS_FROM_STRING_BORROW("__unraisablehook__", - PyDict_GetItemString(sysdict, "unraisablehook")); - - SET_SYS_FROM_STRING("version", - PyUnicode_FromString(Py_GetVersion())); - SET_SYS_FROM_STRING("hexversion", - PyLong_FromLong(PY_VERSION_HEX)); - SET_SYS_FROM_STRING("_git", - Py_BuildValue("(szz)", "CPython", _Py_gitidentifier(), - _Py_gitversion())); - SET_SYS_FROM_STRING("_framework", PyUnicode_FromString(_PYTHONFRAMEWORK)); - SET_SYS_FROM_STRING("api_version", - PyLong_FromLong(PYTHON_API_VERSION)); - SET_SYS_FROM_STRING("copyright", - PyUnicode_FromString(Py_GetCopyright())); - SET_SYS_FROM_STRING("platform", - PyUnicode_FromString(Py_GetPlatform())); - SET_SYS_FROM_STRING("maxsize", - PyLong_FromSsize_t(PY_SSIZE_T_MAX)); - SET_SYS_FROM_STRING("float_info", - PyFloat_GetInfo()); - SET_SYS_FROM_STRING("int_info", - PyLong_GetInfo()); +#define COPY_SYS_ATTR(tokey, fromkey) \ + SET_SYS(tokey, PyMapping_GetItemString(sysdict, fromkey)) + + COPY_SYS_ATTR("__displayhook__", "displayhook"); + COPY_SYS_ATTR("__excepthook__", "excepthook"); + COPY_SYS_ATTR("__breakpointhook__", "breakpointhook"); + COPY_SYS_ATTR("__unraisablehook__", "unraisablehook"); + +#undef COPY_SYS_ATTR + + SET_SYS_FROM_STRING("version", Py_GetVersion()); + SET_SYS("hexversion", PyLong_FromLong(PY_VERSION_HEX)); + SET_SYS("_git", Py_BuildValue("(szz)", "CPython", _Py_gitidentifier(), + _Py_gitversion())); + SET_SYS_FROM_STRING("_framework", _PYTHONFRAMEWORK); + SET_SYS("api_version", PyLong_FromLong(PYTHON_API_VERSION)); + SET_SYS_FROM_STRING("copyright", Py_GetCopyright()); + SET_SYS_FROM_STRING("platform", Py_GetPlatform()); + SET_SYS("maxsize", PyLong_FromSsize_t(PY_SSIZE_T_MAX)); + SET_SYS("float_info", PyFloat_GetInfo()); + SET_SYS("int_info", PyLong_GetInfo()); /* initialize hash_info */ if (Hash_InfoType.tp_name == NULL) { if (PyStructSequence_InitType2(&Hash_InfoType, &hash_info_desc) < 0) { goto type_init_failed; } } - SET_SYS_FROM_STRING("hash_info", - get_hash_info(tstate)); - SET_SYS_FROM_STRING("maxunicode", - PyLong_FromLong(0x10FFFF)); - SET_SYS_FROM_STRING("builtin_module_names", - list_builtin_module_names()); + SET_SYS("hash_info", get_hash_info(tstate)); + SET_SYS("maxunicode", PyLong_FromLong(0x10FFFF)); + SET_SYS("builtin_module_names", list_builtin_module_names()); #if PY_BIG_ENDIAN - SET_SYS_FROM_STRING("byteorder", - PyUnicode_FromString("big")); + SET_SYS_FROM_STRING("byteorder", "big"); #else - SET_SYS_FROM_STRING("byteorder", - PyUnicode_FromString("little")); + SET_SYS_FROM_STRING("byteorder", "little"); #endif #ifdef MS_COREDLL - SET_SYS_FROM_STRING("dllhandle", - PyLong_FromVoidPtr(PyWin_DLLhModule)); - SET_SYS_FROM_STRING("winver", - PyUnicode_FromString(PyWin_DLLVersionString)); + SET_SYS("dllhandle", PyLong_FromVoidPtr(PyWin_DLLhModule)); + SET_SYS_FROM_STRING("winver", PyWin_DLLVersionString); #endif #ifdef ABIFLAGS - SET_SYS_FROM_STRING("abiflags", - PyUnicode_FromString(ABIFLAGS)); + SET_SYS_FROM_STRING("abiflags", ABIFLAGS); #endif /* version_info */ @@ -2750,7 +2725,7 @@ _PySys_InitCore(PyThreadState *tstate, PyObject *sysdict) } } version_info = make_version_info(tstate); - SET_SYS_FROM_STRING("version_info", version_info); + SET_SYS("version_info", version_info); /* prevent user from creating new instances */ VersionInfoType.tp_init = NULL; VersionInfoType.tp_new = NULL; @@ -2760,7 +2735,7 @@ _PySys_InitCore(PyThreadState *tstate, PyObject *sysdict) } /* implementation */ - SET_SYS_FROM_STRING("implementation", make_impl_info(version_info)); + SET_SYS("implementation", make_impl_info(version_info)); /* flags */ if (FlagsType.tp_name == 0) { @@ -2769,7 +2744,7 @@ _PySys_InitCore(PyThreadState *tstate, PyObject *sysdict) } } /* Set flags to their default values (updated by _PySys_InitMain()) */ - SET_SYS_FROM_STRING("flags", make_flags(tstate)); + SET_SYS("flags", make_flags(tstate)); #if defined(MS_WINDOWS) /* getwindowsversion */ @@ -2790,14 +2765,12 @@ _PySys_InitCore(PyThreadState *tstate, PyObject *sysdict) /* float repr style: 0.03 (short) vs 0.029999999999999999 (legacy) */ #ifndef PY_NO_SHORT_FLOAT_REPR - SET_SYS_FROM_STRING("float_repr_style", - PyUnicode_FromString("short")); + SET_SYS_FROM_STRING("float_repr_style", "short"); #else - SET_SYS_FROM_STRING("float_repr_style", - PyUnicode_FromString("legacy")); + SET_SYS_FROM_STRING("float_repr_style", "legacy"); #endif - SET_SYS_FROM_STRING("thread_info", PyThread_GetInfo()); + SET_SYS("thread_info", PyThread_GetInfo()); /* initialize asyncgen_hooks */ if (AsyncGenHooksType.tp_name == NULL) { @@ -2819,20 +2792,6 @@ _PySys_InitCore(PyThreadState *tstate, PyObject *sysdict) return _PyStatus_ERR("can't initialize sys module"); } -/* Updating the sys namespace, returning integer error codes */ -#define SET_SYS_FROM_STRING_INT_RESULT(key, value) \ - do { \ - PyObject *v = (value); \ - if (v == NULL) \ - return -1; \ - res = PyDict_SetItemString(sysdict, key, v); \ - Py_DECREF(v); \ - if (res < 0) { \ - return res; \ - } \ - } while (0) - - static int sys_add_xoption(PyObject *opts, const wchar_t *s) { @@ -2895,24 +2854,10 @@ _PySys_InitMain(PyThreadState *tstate) int res; #define COPY_LIST(KEY, VALUE) \ - do { \ - PyObject *list = _PyWideStringList_AsList(&(VALUE)); \ - if (list == NULL) { \ - return -1; \ - } \ - SET_SYS_FROM_STRING_BORROW(KEY, list); \ - Py_DECREF(list); \ - } while (0) + SET_SYS(KEY, _PyWideStringList_AsList(&(VALUE))); #define SET_SYS_FROM_WSTR(KEY, VALUE) \ - do { \ - PyObject *str = PyUnicode_FromWideChar(VALUE, -1); \ - if (str == NULL) { \ - return -1; \ - } \ - SET_SYS_FROM_STRING_BORROW(KEY, str); \ - Py_DECREF(str); \ - } while (0) + SET_SYS(KEY, PyUnicode_FromWideChar(VALUE, -1)); COPY_LIST("path", config->module_search_paths); @@ -2934,19 +2879,14 @@ _PySys_InitMain(PyThreadState *tstate) COPY_LIST("orig_argv", config->orig_argv); COPY_LIST("warnoptions", config->warnoptions); - PyObject *xoptions = sys_create_xoptions_dict(config); - if (xoptions == NULL) { - return -1; - } - SET_SYS_FROM_STRING_BORROW("_xoptions", xoptions); - Py_DECREF(xoptions); + SET_SYS("_xoptions", sys_create_xoptions_dict(config)); #undef COPY_LIST #undef SET_SYS_FROM_WSTR /* Set flags to their final values */ - SET_SYS_FROM_STRING_INT_RESULT("flags", make_flags(tstate)); + SET_SYS("flags", make_flags(tstate)); /* prevent user from creating new instances */ FlagsType.tp_init = NULL; FlagsType.tp_new = NULL; @@ -2958,8 +2898,7 @@ _PySys_InitMain(PyThreadState *tstate) _PyErr_Clear(tstate); } - SET_SYS_FROM_STRING_INT_RESULT("dont_write_bytecode", - PyBool_FromLong(!config->write_bytecode)); + SET_SYS("dont_write_bytecode", PyBool_FromLong(!config->write_bytecode)); if (get_warnoptions(tstate) == NULL) { return -1; @@ -2978,9 +2917,8 @@ _PySys_InitMain(PyThreadState *tstate) return -1; } +#undef SET_SYS #undef SET_SYS_FROM_STRING -#undef SET_SYS_FROM_STRING_BORROW -#undef SET_SYS_FROM_STRING_INT_RESULT /* Set up a preliminary stderr printer until we have enough From webhook-mailer at python.org Sun Oct 11 09:51:19 2020 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Sun, 11 Oct 2020 13:51:19 -0000 Subject: [Python-checkins] bpo-41993: Fix possible issues in remove_module() (GH-22631) Message-ID: https://github.com/python/cpython/commit/8287aadb75f6bd0154996424819334cd3839707c commit: 8287aadb75f6bd0154996424819334cd3839707c branch: master author: Serhiy Storchaka committer: GitHub date: 2020-10-11T16:51:07+03:00 summary: bpo-41993: Fix possible issues in remove_module() (GH-22631) * PyMapping_HasKey() is not safe because it silences all exceptions and can return incorrect result. * Informative exceptions from PyMapping_DelItem() are overridden with RuntimeError and the original exception raised before calling remove_module() is lost. * There is a race condition between PyMapping_HasKey() and PyMapping_DelItem(). files: A Misc/NEWS.d/next/Core and Builtins/2020-10-10-13-53-52.bpo-41993.YMzixQ.rst M Python/import.c diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-10-10-13-53-52.bpo-41993.YMzixQ.rst b/Misc/NEWS.d/next/Core and Builtins/2020-10-10-13-53-52.bpo-41993.YMzixQ.rst new file mode 100644 index 0000000000000..3669cf11ea4cd --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2020-10-10-13-53-52.bpo-41993.YMzixQ.rst @@ -0,0 +1,2 @@ +Fixed potential issues with removing not completely initialized module from +``sys.modules`` when import fails. diff --git a/Python/import.c b/Python/import.c index 505688400ef3e..26b80f320c343 100644 --- a/Python/import.c +++ b/Python/import.c @@ -902,7 +902,11 @@ PyImport_AddModule(const char *name) } -/* Remove name from sys.modules, if it's there. */ +/* Remove name from sys.modules, if it's there. + * Can be called with an exception raised. + * If fail to remove name a new exception will be chained with the old + * exception, otherwise the old exception is preserved. + */ static void remove_module(PyThreadState *tstate, PyObject *name) { @@ -910,18 +914,17 @@ remove_module(PyThreadState *tstate, PyObject *name) _PyErr_Fetch(tstate, &type, &value, &traceback); PyObject *modules = tstate->interp->modules; - if (!PyMapping_HasKey(modules, name)) { - goto out; + if (PyDict_CheckExact(modules)) { + PyObject *mod = _PyDict_Pop(modules, name, Py_None); + Py_XDECREF(mod); } - if (PyMapping_DelItem(modules, name) < 0) { - _PyErr_SetString(tstate, PyExc_RuntimeError, - "deleting key in sys.modules failed"); - _PyErr_ChainExceptions(type, value, traceback); - return; + else if (PyMapping_DelItem(modules, name) < 0) { + if (_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) { + _PyErr_Clear(tstate); + } } -out: - _PyErr_Restore(tstate, type, value, traceback); + _PyErr_ChainExceptions(type, value, traceback); } From webhook-mailer at python.org Sun Oct 11 13:32:23 2020 From: webhook-mailer at python.org (Andre Delfino) Date: Sun, 11 Oct 2020 17:32:23 -0000 Subject: [Python-checkins] [3.8] [doc] Fix link to abc.collections.Iterable (GH-22502) (#22504) Message-ID: https://github.com/python/cpython/commit/b15fff620f8e7ef3353300dff69706e300123cab commit: b15fff620f8e7ef3353300dff69706e300123cab branch: 3.8 author: Andre Delfino committer: GitHub date: 2020-10-11T10:32:02-07:00 summary: [3.8] [doc] Fix link to abc.collections.Iterable (GH-22502) (#22504) Automerge-Triggered-By: @gvanrossum. (cherry picked from commit d4b9edd5052a2d9ae3d2be69975cc933afb37737) Co-authored-by: Andre Delfino files: M Doc/library/typing.rst diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 9785f79287674..351c0bee75c83 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -1167,7 +1167,7 @@ The module defines the following classes, functions and decorators: Such a protocol can be used with :func:`isinstance` and :func:`issubclass`. This raises :exc:`TypeError` when applied to a non-protocol class. This allows a simple-minded structural check, very similar to "one trick ponies" - in :mod:`collections.abc` such as :class:`Iterable`. For example:: + in :mod:`collections.abc` such as :class:`~collections.abc.Iterable`. For example:: @runtime_checkable class Closable(Protocol): From webhook-mailer at python.org Sun Oct 11 14:13:52 2020 From: webhook-mailer at python.org (Gaurav Kamath) Date: Sun, 11 Oct 2020 18:13:52 -0000 Subject: [Python-checkins] Fix typo (GH-22582) Message-ID: https://github.com/python/cpython/commit/8197a93208fea3b56e012272b6cf3f0c2c582d4a commit: 8197a93208fea3b56e012272b6cf3f0c2c582d4a branch: master author: Gaurav Kamath committer: GitHub date: 2020-10-11T11:13:43-07:00 summary: Fix typo (GH-22582) /af/of/s Automerge-Triggered-By: @Mariatta files: M Doc/library/math.rst diff --git a/Doc/library/math.rst b/Doc/library/math.rst index bbf64643ff59f..145bac4966e18 100644 --- a/Doc/library/math.rst +++ b/Doc/library/math.rst @@ -130,7 +130,7 @@ Number-theoretic and representation functions Return the greatest common divisor of the specified integer arguments. If any of the arguments is nonzero, then the returned value is the largest - positive integer that is a divisor af all arguments. If all arguments + positive integer that is a divisor of all arguments. If all arguments are zero, then the returned value is ``0``. ``gcd()`` without arguments returns ``0``. From webhook-mailer at python.org Sun Oct 11 14:21:39 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Sun, 11 Oct 2020 18:21:39 -0000 Subject: [Python-checkins] bpo-41376: Fix the documentation of `site.getusersitepackages()` (GH-21602) (GH-22592) Message-ID: https://github.com/python/cpython/commit/1006f63a8b4327e8a74eb978b175b5234bb46c6c commit: 1006f63a8b4327e8a74eb978b175b5234bb46c6c branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-11T19:21:31+01:00 summary: bpo-41376: Fix the documentation of `site.getusersitepackages()` (GH-21602) (GH-22592) `site.getusersitepackages()` returns the location of the user-specific site-packages directory even when the user-specific site-packages is disabled. ``` $ python -s -m site sys.path = [ '/home/user/conda/lib/python37.zip', '/home/user/conda/lib/python3.7', '/home/user/conda/lib/python3.7/lib-dynload', '/home/user/conda/lib/python3.7/site-packages', ] USER_BASE: '/home/user/.local' (exists) USER_SITE: '/home/user/.local/lib/python3.7/site-packages' (doesn't exist) ENABLE_USER_SITE: False ``` It was not practical to prevent the function from returning None if user-specific site-packages are disabled, since there are other uses of the function which are relying on this behaviour (e.g. `python -m site`). (cherry picked from commit 35f041dd0171f575fc3adce1709b31fdf45a5ff6) Co-authored-by: Phil Elson Co-authored-by: Phil Elson files: M Doc/library/site.rst diff --git a/Doc/library/site.rst b/Doc/library/site.rst index b424e1ba348d8..2e3646f6a74f8 100644 --- a/Doc/library/site.rst +++ b/Doc/library/site.rst @@ -231,7 +231,9 @@ Module contents Return the path of the user-specific site-packages directory, :data:`USER_SITE`. If it is not initialized yet, this function will also set - it, respecting :envvar:`PYTHONNOUSERSITE` and :data:`USER_BASE`. + it, respecting :data:`USER_BASE`. To determine if the user-specific + site-packages was added to ``sys.path`` :data:`ENABLE_USER_SITE` should be + used. .. versionadded:: 3.2 From webhook-mailer at python.org Sun Oct 11 14:21:55 2020 From: webhook-mailer at python.org (chilaxan) Date: Sun, 11 Oct 2020 18:21:55 -0000 Subject: [Python-checkins] Fix typo in listobject.h (GH-22588) Message-ID: https://github.com/python/cpython/commit/10c98db7f5ccf0af9d8803a132ca8641193ebda1 commit: 10c98db7f5ccf0af9d8803a132ca8641193ebda1 branch: master author: chilaxan committer: GitHub date: 2020-10-11T19:21:51+01:00 summary: Fix typo in listobject.h (GH-22588) files: M Include/cpython/listobject.h diff --git a/Include/cpython/listobject.h b/Include/cpython/listobject.h index 70b9d83d8a232..e1b9462d5b361 100644 --- a/Include/cpython/listobject.h +++ b/Include/cpython/listobject.h @@ -26,7 +26,7 @@ PyAPI_FUNC(void) _PyList_DebugMallocStats(FILE *out); /* Macro, trading safety for speed */ -/* Cast argument to PyTupleObject* type. */ +/* Cast argument to PyListObject* type. */ #define _PyList_CAST(op) (assert(PyList_Check(op)), (PyListObject *)(op)) #define PyList_GET_ITEM(op, i) (_PyList_CAST(op)->ob_item[i]) From webhook-mailer at python.org Sun Oct 11 14:26:58 2020 From: webhook-mailer at python.org (Anthony Sottile) Date: Sun, 11 Oct 2020 18:26:58 -0000 Subject: [Python-checkins] Fix .. code-block :: directives in decimal.rst (GH-22571) Message-ID: https://github.com/python/cpython/commit/d5752aa5c91c56910efe226d0a000bb4481e4f87 commit: d5752aa5c91c56910efe226d0a000bb4481e4f87 branch: master author: Anthony Sottile committer: GitHub date: 2020-10-11T19:26:50+01:00 summary: Fix .. code-block :: directives in decimal.rst (GH-22571) files: M Doc/c-api/decimal.rst diff --git a/Doc/c-api/decimal.rst b/Doc/c-api/decimal.rst index f530571ebae57..94cc4a7b84579 100644 --- a/Doc/c-api/decimal.rst +++ b/Doc/c-api/decimal.rst @@ -16,7 +16,7 @@ Initialize Typically, a C extension module that uses the decimal API will do these steps in its init function: -.. code-block:: +.. code-block:: c #include "pydecimal.h" @@ -88,7 +88,7 @@ Data structures The conversion functions use the following status codes and data structures: -.. code-block:: +.. code-block:: c /* status cases for getting a triple */ enum mpd_triple_class { @@ -126,7 +126,7 @@ Functions For simplicity, the usage of the function and all special cases are explained in code form and comments: -.. code-block:: +.. code-block:: c triple = PyDec_AsUint128Triple(dec); switch (triple.tag) { From webhook-mailer at python.org Sun Oct 11 14:54:20 2020 From: webhook-mailer at python.org (Kyle Evans) Date: Sun, 11 Oct 2020 18:54:20 -0000 Subject: [Python-checkins] bpo-40422: create a common _Py_closerange API (GH-19754) Message-ID: https://github.com/python/cpython/commit/c230fde8475e4e5581e74a4235654d17ccf4cff8 commit: c230fde8475e4e5581e74a4235654d17ccf4cff8 branch: master author: Kyle Evans committer: GitHub date: 2020-10-11T11:54:11-07:00 summary: bpo-40422: create a common _Py_closerange API (GH-19754) Such an API can be used both for os.closerange and subprocess. For the latter, this yields potential improvement for platforms that have fdwalk but wouldn't have used it there. This will prove even more beneficial later for platforms that have close_range(2), as the new API will prefer that over all else if it's available. The new API is structured to look more like close_range(2), closing from [start, end] rather than the [low, high) of os.closerange(). Automerge-Triggered-By: @gpshead files: A Misc/NEWS.d/next/C API/2020-10-10-14-05-24.bpo-40422.sh8IDY.rst M Modules/_posixsubprocess.c M Modules/posixmodule.c M Modules/posixmodule.h diff --git a/Misc/NEWS.d/next/C API/2020-10-10-14-05-24.bpo-40422.sh8IDY.rst b/Misc/NEWS.d/next/C API/2020-10-10-14-05-24.bpo-40422.sh8IDY.rst new file mode 100644 index 0000000000000..1b6d9e034b529 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2020-10-10-14-05-24.bpo-40422.sh8IDY.rst @@ -0,0 +1 @@ +Add `_Py_closerange` function to provide performant closing of a range of file descriptors. \ No newline at end of file diff --git a/Modules/_posixsubprocess.c b/Modules/_posixsubprocess.c index 5d1691ace4192..ed046fc5c1ba9 100644 --- a/Modules/_posixsubprocess.c +++ b/Modules/_posixsubprocess.c @@ -250,7 +250,6 @@ _close_fds_by_brute_force(long start_fd, PyObject *py_fds_to_keep) long end_fd = safe_get_max_fd(); Py_ssize_t num_fds_to_keep = PyTuple_GET_SIZE(py_fds_to_keep); Py_ssize_t keep_seq_idx; - int fd_num; /* As py_fds_to_keep is sorted we can loop through the list closing * fds in between any in the keep list falling within our range. */ for (keep_seq_idx = 0; keep_seq_idx < num_fds_to_keep; ++keep_seq_idx) { @@ -258,21 +257,11 @@ _close_fds_by_brute_force(long start_fd, PyObject *py_fds_to_keep) int keep_fd = PyLong_AsLong(py_keep_fd); if (keep_fd < start_fd) continue; - for (fd_num = start_fd; fd_num < keep_fd; ++fd_num) { - close(fd_num); - } + _Py_closerange(start_fd, keep_fd - 1); start_fd = keep_fd + 1; } if (start_fd <= end_fd) { -#if defined(__FreeBSD__) - /* Any errors encountered while closing file descriptors are ignored */ - closefrom(start_fd); -#else - for (fd_num = start_fd; fd_num < end_fd; ++fd_num) { - /* Ignore errors */ - (void)close(fd_num); - } -#endif + _Py_closerange(start_fd, end_fd); } } diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 165625c9a670a..321eaec63c4a4 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -8740,8 +8740,23 @@ os_close_impl(PyObject *module, int fd) Py_RETURN_NONE; } +/* Our selection logic for which function to use is as follows: + * 1. If closefrom(2) is available, we'll attempt to use that next if we're + * closing up to sysconf(_SC_OPEN_MAX). + * 1a. Fallback to fdwalk(3) if we're not closing up to sysconf(_SC_OPEN_MAX), + * as that will be more performant if the range happens to have any chunk of + * non-opened fd in the middle. + * 1b. If fdwalk(3) isn't available, just do a plain close(2) loop. + */ +#ifdef __FreeBSD__ +#define USE_CLOSEFROM +#endif /* __FreeBSD__ */ #ifdef HAVE_FDWALK +#define USE_FDWALK +#endif /* HAVE_FDWALK */ + +#ifdef USE_FDWALK static int _fdwalk_close_func(void *lohi, int fd) { @@ -8757,7 +8772,36 @@ _fdwalk_close_func(void *lohi, int fd) } return 0; } -#endif /* HAVE_FDWALK */ +#endif /* USE_FDWALK */ + +/* Closes all file descriptors in [first, last], ignoring errors. */ +void +_Py_closerange(int first, int last) +{ + first = Py_MAX(first, 0); +#ifdef USE_CLOSEFROM + if (last >= sysconf(_SC_OPEN_MAX)) { + /* Any errors encountered while closing file descriptors are ignored */ + closefrom(first); + } + else +#endif /* USE_CLOSEFROM */ +#ifdef USE_FDWALK + { + int lohi[2]; + lohi[0] = first; + lohi[1] = last + 1; + fdwalk(_fdwalk_close_func, lohi); + } +#else + { + for (int i = first; i <= last; i++) { + /* Ignore errors */ + (void)close(i); + } + } +#endif /* USE_FDWALK */ +} /*[clinic input] os.closerange @@ -8773,31 +8817,9 @@ static PyObject * os_closerange_impl(PyObject *module, int fd_low, int fd_high) /*[clinic end generated code: output=0ce5c20fcda681c2 input=5855a3d053ebd4ec]*/ { -#ifdef HAVE_FDWALK - int lohi[2]; -#endif Py_BEGIN_ALLOW_THREADS _Py_BEGIN_SUPPRESS_IPH -#ifdef HAVE_FDWALK - lohi[0] = Py_MAX(fd_low, 0); - lohi[1] = fd_high; - fdwalk(_fdwalk_close_func, lohi); -#else - fd_low = Py_MAX(fd_low, 0); -#ifdef __FreeBSD__ - if (fd_high >= sysconf(_SC_OPEN_MAX)) { - /* Any errors encountered while closing file descriptors are ignored */ - closefrom(fd_low); - } - else -#endif - { - for (int i = fd_low; i < fd_high; i++) { - /* Ignore errors */ - (void)close(i); - } - } -#endif + _Py_closerange(fd_low, fd_high - 1); _Py_END_SUPPRESS_IPH Py_END_ALLOW_THREADS Py_RETURN_NONE; diff --git a/Modules/posixmodule.h b/Modules/posixmodule.h index 1e00562abc337..749833f71cd4d 100644 --- a/Modules/posixmodule.h +++ b/Modules/posixmodule.h @@ -28,6 +28,8 @@ PyAPI_FUNC(int) _Py_Sigset_Converter(PyObject *, void *); #endif /* HAVE_SIGSET_T */ #endif /* Py_LIMITED_API */ +PyAPI_FUNC(void) _Py_closerange(int first, int last); + #ifdef __cplusplus } #endif From webhook-mailer at python.org Sun Oct 11 16:18:57 2020 From: webhook-mailer at python.org (Kyle Evans) Date: Sun, 11 Oct 2020 20:18:57 -0000 Subject: [Python-checkins] bpo-40423: Optimization: use close_range(2) if available (GH-22651) Message-ID: https://github.com/python/cpython/commit/1800c600801709958af66bebfa683a4e7570810f commit: 1800c600801709958af66bebfa683a4e7570810f branch: master author: Kyle Evans committer: GitHub date: 2020-10-11T13:18:53-07:00 summary: bpo-40423: Optimization: use close_range(2) if available (GH-22651) close_range(2) should be preferred at all times if it's available, otherwise we'll use closefrom(2) if available with a fallback to fdwalk(3) or plain old loop over fd range in order of most efficient to least. [note that this version does check for ENOSYS, but currently ignores all other errors] Automerge-Triggered-By: @pablogsal files: A Misc/NEWS.d/next/C API/2020-10-11-19-17-44.bpo-40423.GsmgEj.rst M Modules/posixmodule.c M configure M configure.ac M pyconfig.h.in diff --git a/Misc/NEWS.d/next/C API/2020-10-11-19-17-44.bpo-40423.GsmgEj.rst b/Misc/NEWS.d/next/C API/2020-10-11-19-17-44.bpo-40423.GsmgEj.rst new file mode 100644 index 0000000000000..44e571ebf86da --- /dev/null +++ b/Misc/NEWS.d/next/C API/2020-10-11-19-17-44.bpo-40423.GsmgEj.rst @@ -0,0 +1,3 @@ +The :mod:`subprocess` module and ``os.closerange`` will now use the +``close_range(low, high, flags)`` syscall when it is available for more +efficient closing of ranges of descriptors. \ No newline at end of file diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 321eaec63c4a4..2e0caaa3e561b 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -8741,12 +8741,15 @@ os_close_impl(PyObject *module, int fd) } /* Our selection logic for which function to use is as follows: - * 1. If closefrom(2) is available, we'll attempt to use that next if we're + * 1. If close_range(2) is available, always prefer that; it's better for + * contiguous ranges like this than fdwalk(3) which entails iterating over + * the entire fd space and simply doing nothing for those outside the range. + * 2. If closefrom(2) is available, we'll attempt to use that next if we're * closing up to sysconf(_SC_OPEN_MAX). - * 1a. Fallback to fdwalk(3) if we're not closing up to sysconf(_SC_OPEN_MAX), + * 2a. Fallback to fdwalk(3) if we're not closing up to sysconf(_SC_OPEN_MAX), * as that will be more performant if the range happens to have any chunk of * non-opened fd in the middle. - * 1b. If fdwalk(3) isn't available, just do a plain close(2) loop. + * 2b. If fdwalk(3) isn't available, just do a plain close(2) loop. */ #ifdef __FreeBSD__ #define USE_CLOSEFROM @@ -8779,6 +8782,14 @@ void _Py_closerange(int first, int last) { first = Py_MAX(first, 0); +#ifdef HAVE_CLOSE_RANGE + if (close_range(first, last, 0) == 0 || errno != ENOSYS) { + /* Any errors encountered while closing file descriptors are ignored; + * ENOSYS means no kernel support, though, + * so we'll fallback to the other methods. */ + } + else +#endif /* HAVE_CLOSE_RANGE */ #ifdef USE_CLOSEFROM if (last >= sysconf(_SC_OPEN_MAX)) { /* Any errors encountered while closing file descriptors are ignored */ diff --git a/configure b/configure index ad74754e9a721..89577d85a4148 100755 --- a/configure +++ b/configure @@ -11672,8 +11672,8 @@ fi # checks for library functions for ac_func in alarm accept4 setitimer getitimer bind_textdomain_codeset chown \ - clock confstr copy_file_range ctermid dup3 execv explicit_bzero explicit_memset \ - faccessat fchmod fchmodat fchown fchownat \ + clock confstr close_range copy_file_range ctermid dup3 execv explicit_bzero \ + explicit_memset faccessat fchmod fchmodat fchown fchownat \ fdwalk fexecve fdopendir fork fpathconf fstatat ftime ftruncate futimesat \ futimens futimes gai_strerror getentropy \ getgrgid_r getgrnam_r \ diff --git a/configure.ac b/configure.ac index f0bc8c625844b..3ec274c576edf 100644 --- a/configure.ac +++ b/configure.ac @@ -3664,8 +3664,8 @@ fi # checks for library functions AC_CHECK_FUNCS(alarm accept4 setitimer getitimer bind_textdomain_codeset chown \ - clock confstr copy_file_range ctermid dup3 execv explicit_bzero explicit_memset \ - faccessat fchmod fchmodat fchown fchownat \ + clock confstr close_range copy_file_range ctermid dup3 execv explicit_bzero \ + explicit_memset faccessat fchmod fchmodat fchown fchownat \ fdwalk fexecve fdopendir fork fpathconf fstatat ftime ftruncate futimesat \ futimens futimes gai_strerror getentropy \ getgrgid_r getgrnam_r \ diff --git a/pyconfig.h.in b/pyconfig.h.in index c162a3c33e57b..298cb4fa12f80 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -139,6 +139,9 @@ /* Define to 1 if you have the `clock_settime' function. */ #undef HAVE_CLOCK_SETTIME +/* Define to 1 if you have the `close_range' function. */ +#undef HAVE_CLOSE_RANGE + /* Define if the C compiler supports computed gotos. */ #undef HAVE_COMPUTED_GOTOS From webhook-mailer at python.org Sun Oct 11 16:35:00 2020 From: webhook-mailer at python.org (Pablo Galindo) Date: Sun, 11 Oct 2020 20:35:00 -0000 Subject: [Python-checkins] bpo-41971: Fix test failure in test.test_tools.test_c_analyzer when mutating global state (GH-22652) Message-ID: https://github.com/python/cpython/commit/47ecfd8030c391779dd9d3b4b07e6b01a8392481 commit: 47ecfd8030c391779dd9d3b4b07e6b01a8392481 branch: master author: Pablo Galindo committer: GitHub date: 2020-10-11T21:34:51+01:00 summary: bpo-41971: Fix test failure in test.test_tools.test_c_analyzer when mutating global state (GH-22652) files: M Tools/c-analyzer/c_analyzer/common/files.py diff --git a/Tools/c-analyzer/c_analyzer/common/files.py b/Tools/c-analyzer/c_analyzer/common/files.py index f630afe625924..a8a044757d00b 100644 --- a/Tools/c-analyzer/c_analyzer/common/files.py +++ b/Tools/c-analyzer/c_analyzer/common/files.py @@ -60,7 +60,7 @@ def glob_tree(root, *, def iter_files(root, suffix=None, relparent=None, *, - get_files=os.walk, + get_files=None, _glob=glob_tree, _walk=walk_tree, ): @@ -75,6 +75,8 @@ def iter_files(root, suffix=None, relparent=None, *, if "relparent" is provided then it is used to resolve each filename as a relative path. """ + if get_files is None: + get_files = os.walk if not isinstance(root, str): roots = root for root in roots: From webhook-mailer at python.org Sun Oct 11 16:54:43 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Sun, 11 Oct 2020 20:54:43 -0000 Subject: [Python-checkins] bpo-41971: Fix test failure in test.test_tools.test_c_analyzer when mutating global state (GH-22652) (GH-22653) Message-ID: https://github.com/python/cpython/commit/4af672921949b8a10340d5e29b456091060cedeb commit: 4af672921949b8a10340d5e29b456091060cedeb branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-11T21:54:35+01:00 summary: bpo-41971: Fix test failure in test.test_tools.test_c_analyzer when mutating global state (GH-22652) (GH-22653) (cherry picked from commit 47ecfd8030c391779dd9d3b4b07e6b01a8392481) Co-authored-by: Pablo Galindo Co-authored-by: Pablo Galindo files: M Tools/c-analyzer/c_analyzer/common/files.py diff --git a/Tools/c-analyzer/c_analyzer/common/files.py b/Tools/c-analyzer/c_analyzer/common/files.py index f630afe625924..a8a044757d00b 100644 --- a/Tools/c-analyzer/c_analyzer/common/files.py +++ b/Tools/c-analyzer/c_analyzer/common/files.py @@ -60,7 +60,7 @@ def glob_tree(root, *, def iter_files(root, suffix=None, relparent=None, *, - get_files=os.walk, + get_files=None, _glob=glob_tree, _walk=walk_tree, ): @@ -75,6 +75,8 @@ def iter_files(root, suffix=None, relparent=None, *, if "relparent" is provided then it is used to resolve each filename as a relative path. """ + if get_files is None: + get_files = os.walk if not isinstance(root, str): roots = root for root in roots: From webhook-mailer at python.org Sun Oct 11 18:37:28 2020 From: webhook-mailer at python.org (Victor Stinner) Date: Sun, 11 Oct 2020 22:37:28 -0000 Subject: [Python-checkins] bpo-41739: Fix test_logging.test_race_between_set_target_and_flush() (GH-22655) Message-ID: https://github.com/python/cpython/commit/13ff396c019d548ba181cf22c6f39309a300723c commit: 13ff396c019d548ba181cf22c6f39309a300723c branch: master author: Victor Stinner committer: GitHub date: 2020-10-12T00:37:20+02:00 summary: bpo-41739: Fix test_logging.test_race_between_set_target_and_flush() (GH-22655) The test now waits until all threads complete to avoid leaking running threads. Also, use regular threads rather than daemon threads. files: A Misc/NEWS.d/next/Tests/2020-10-12-00-11-47.bpo-41739.wSCc4K.rst M Lib/test/test_logging.py diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index 4cd8c7e25daa9..7c98e19b7408f 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -1164,22 +1164,27 @@ def test_race_between_set_target_and_flush(self): class MockRaceConditionHandler: def __init__(self, mem_hdlr): self.mem_hdlr = mem_hdlr + self.threads = [] def removeTarget(self): self.mem_hdlr.setTarget(None) def handle(self, msg): - t = threading.Thread(target=self.removeTarget) - t.daemon = True - t.start() + thread = threading.Thread(target=self.removeTarget) + self.threads.append(thread) + thread.start() target = MockRaceConditionHandler(self.mem_hdlr) - self.mem_hdlr.setTarget(target) + try: + self.mem_hdlr.setTarget(target) - for _ in range(10): - time.sleep(0.005) - self.mem_logger.info("not flushed") - self.mem_logger.warning("flushed") + for _ in range(10): + time.sleep(0.005) + self.mem_logger.info("not flushed") + self.mem_logger.warning("flushed") + finally: + for thread in target.threads: + threading_helper.join_thread(thread) class ExceptionFormatter(logging.Formatter): diff --git a/Misc/NEWS.d/next/Tests/2020-10-12-00-11-47.bpo-41739.wSCc4K.rst b/Misc/NEWS.d/next/Tests/2020-10-12-00-11-47.bpo-41739.wSCc4K.rst new file mode 100644 index 0000000000000..7aee2b9444472 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2020-10-12-00-11-47.bpo-41739.wSCc4K.rst @@ -0,0 +1,2 @@ +Fix test_logging.test_race_between_set_target_and_flush(): the test now +waits until all threads complete to avoid leaking running threads. From webhook-mailer at python.org Mon Oct 12 00:30:56 2020 From: webhook-mailer at python.org (Victor Stinner) Date: Mon, 12 Oct 2020 04:30:56 -0000 Subject: [Python-checkins] bpo-41739: Fix test_logging.test_race_between_set_target_and_flush() (GH-22655) (GH-22656) Message-ID: https://github.com/python/cpython/commit/f5393dc2a0ced7bf670ebc56b5fd10a3eb328d1a commit: f5393dc2a0ced7bf670ebc56b5fd10a3eb328d1a branch: 3.9 author: Victor Stinner committer: GitHub date: 2020-10-12T05:30:48+01:00 summary: bpo-41739: Fix test_logging.test_race_between_set_target_and_flush() (GH-22655) (GH-22656) The test now waits until all threads complete to avoid leaking running threads. Also, use regular threads rather than daemon threads. (cherry picked from commit 13ff396c019d548ba181cf22c6f39309a300723c) files: A Misc/NEWS.d/next/Tests/2020-10-12-00-11-47.bpo-41739.wSCc4K.rst M Lib/test/test_logging.py diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index 62759c07017a3..410eae2208688 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -1161,22 +1161,27 @@ def test_race_between_set_target_and_flush(self): class MockRaceConditionHandler: def __init__(self, mem_hdlr): self.mem_hdlr = mem_hdlr + self.threads = [] def removeTarget(self): self.mem_hdlr.setTarget(None) def handle(self, msg): - t = threading.Thread(target=self.removeTarget) - t.daemon = True - t.start() + thread = threading.Thread(target=self.removeTarget) + self.threads.append(thread) + thread.start() target = MockRaceConditionHandler(self.mem_hdlr) - self.mem_hdlr.setTarget(target) + try: + self.mem_hdlr.setTarget(target) - for _ in range(10): - time.sleep(0.005) - self.mem_logger.info("not flushed") - self.mem_logger.warning("flushed") + for _ in range(10): + time.sleep(0.005) + self.mem_logger.info("not flushed") + self.mem_logger.warning("flushed") + finally: + for thread in target.threads: + support.join_thread(thread) class ExceptionFormatter(logging.Formatter): diff --git a/Misc/NEWS.d/next/Tests/2020-10-12-00-11-47.bpo-41739.wSCc4K.rst b/Misc/NEWS.d/next/Tests/2020-10-12-00-11-47.bpo-41739.wSCc4K.rst new file mode 100644 index 0000000000000..7aee2b9444472 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2020-10-12-00-11-47.bpo-41739.wSCc4K.rst @@ -0,0 +1,2 @@ +Fix test_logging.test_race_between_set_target_and_flush(): the test now +waits until all threads complete to avoid leaking running threads. From webhook-mailer at python.org Mon Oct 12 04:51:41 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Mon, 12 Oct 2020 08:51:41 -0000 Subject: [Python-checkins] bpo-41739: Fix test_logging.test_race_between_set_target_and_flush() (GH-22655) (GH-22656) (GH-22662) Message-ID: https://github.com/python/cpython/commit/33057c70920787627f2c94d08eb5d33c31f8bdd9 commit: 33057c70920787627f2c94d08eb5d33c31f8bdd9 branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-12T10:51:10+02:00 summary: bpo-41739: Fix test_logging.test_race_between_set_target_and_flush() (GH-22655) (GH-22656) (GH-22662) The test now waits until all threads complete to avoid leaking running threads. Also, use regular threads rather than daemon threads. (cherry picked from commit 13ff396c019d548ba181cf22c6f39309a300723c) (cherry picked from commit f5393dc2a0ced7bf670ebc56b5fd10a3eb328d1a) Co-authored-by: Victor Stinner Co-authored-by: Victor Stinner files: A Misc/NEWS.d/next/Tests/2020-10-12-00-11-47.bpo-41739.wSCc4K.rst M Lib/test/test_logging.py diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index cea51b455eed2..b9392aefd4476 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -1177,22 +1177,27 @@ def test_race_between_set_target_and_flush(self): class MockRaceConditionHandler: def __init__(self, mem_hdlr): self.mem_hdlr = mem_hdlr + self.threads = [] def removeTarget(self): self.mem_hdlr.setTarget(None) def handle(self, msg): - t = threading.Thread(target=self.removeTarget) - t.daemon = True - t.start() + thread = threading.Thread(target=self.removeTarget) + self.threads.append(thread) + thread.start() target = MockRaceConditionHandler(self.mem_hdlr) - self.mem_hdlr.setTarget(target) + try: + self.mem_hdlr.setTarget(target) - for _ in range(10): - time.sleep(0.005) - self.mem_logger.info("not flushed") - self.mem_logger.warning("flushed") + for _ in range(10): + time.sleep(0.005) + self.mem_logger.info("not flushed") + self.mem_logger.warning("flushed") + finally: + for thread in target.threads: + support.join_thread(thread) class ExceptionFormatter(logging.Formatter): diff --git a/Misc/NEWS.d/next/Tests/2020-10-12-00-11-47.bpo-41739.wSCc4K.rst b/Misc/NEWS.d/next/Tests/2020-10-12-00-11-47.bpo-41739.wSCc4K.rst new file mode 100644 index 0000000000000..7aee2b9444472 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2020-10-12-00-11-47.bpo-41739.wSCc4K.rst @@ -0,0 +1,2 @@ +Fix test_logging.test_race_between_set_target_and_flush(): the test now +waits until all threads complete to avoid leaking running threads. From webhook-mailer at python.org Mon Oct 12 09:52:53 2020 From: webhook-mailer at python.org (Andre Delfino) Date: Mon, 12 Oct 2020 13:52:53 -0000 Subject: [Python-checkins] [doc] Remove mention of async and await as soft keywords (GH-22144) Message-ID: https://github.com/python/cpython/commit/8adf8d1ec45fe0d87e2312a13a3ec0b8ab03fea1 commit: 8adf8d1ec45fe0d87e2312a13a3ec0b8ab03fea1 branch: master author: Andre Delfino committer: GitHub date: 2020-10-12T14:52:30+01:00 summary: [doc] Remove mention of async and await as soft keywords (GH-22144) files: M Doc/reference/compound_stmts.rst diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst index 04a3948d0c9dc..62986cb151964 100644 --- a/Doc/reference/compound_stmts.rst +++ b/Doc/reference/compound_stmts.rst @@ -768,10 +768,8 @@ Coroutine function definition keyword: await Execution of Python coroutines can be suspended and resumed at many points -(see :term:`coroutine`). Inside the body of a coroutine function, ``await`` and -``async`` identifiers become reserved keywords; :keyword:`await` expressions, -:keyword:`async for` and :keyword:`async with` can only be used in -coroutine function bodies. +(see :term:`coroutine`). :keyword:`await` expressions, :keyword:`async for` and +:keyword:`async with` can only be used in the body of a coroutine function. Functions defined with ``async def`` syntax are always coroutine functions, even if they do not contain ``await`` or ``async`` keywords. @@ -785,6 +783,9 @@ An example of a coroutine function:: do_stuff() await some_coroutine() +.. versionchanged:: 3.7 + ``await`` and ``async`` are now keywords; previously they were only + treated as such inside the body of a coroutine function. .. index:: statement: async for .. _`async for`: From webhook-mailer at python.org Mon Oct 12 09:53:15 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Mon, 12 Oct 2020 13:53:15 -0000 Subject: [Python-checkins] Fix typo (GH-22582) Message-ID: https://github.com/python/cpython/commit/372a9e21359331da0c33a49edb0b2c9e551f581d commit: 372a9e21359331da0c33a49edb0b2c9e551f581d branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-12T06:53:10-07:00 summary: Fix typo (GH-22582) /af/of/s Automerge-Triggered-By: @Mariatta (cherry picked from commit 8197a93208fea3b56e012272b6cf3f0c2c582d4a) Co-authored-by: Gaurav Kamath files: M Doc/library/math.rst diff --git a/Doc/library/math.rst b/Doc/library/math.rst index 6ec1feee35a6d..b20e557b5c610 100644 --- a/Doc/library/math.rst +++ b/Doc/library/math.rst @@ -130,7 +130,7 @@ Number-theoretic and representation functions Return the greatest common divisor of the specified integer arguments. If any of the arguments is nonzero, then the returned value is the largest - positive integer that is a divisor af all arguments. If all arguments + positive integer that is a divisor of all arguments. If all arguments are zero, then the returned value is ``0``. ``gcd()`` without arguments returns ``0``. From webhook-mailer at python.org Mon Oct 12 10:33:44 2020 From: webhook-mailer at python.org (linchiwei123) Date: Mon, 12 Oct 2020 14:33:44 -0000 Subject: [Python-checkins] [doc] Fix typo in the graphlib docs (GH-22661) Message-ID: https://github.com/python/cpython/commit/abe244c458f2b1a727af4d2a56f71813e501bcb4 commit: abe244c458f2b1a727af4d2a56f71813e501bcb4 branch: master author: linchiwei123 <40888469+linchiwei123 at users.noreply.github.com> committer: GitHub date: 2020-10-12T07:33:34-07:00 summary: [doc] Fix typo in the graphlib docs (GH-22661) Automerge-Triggered-By: @pablogsal files: M Lib/graphlib.py diff --git a/Lib/graphlib.py b/Lib/graphlib.py index 948f62f1dc303..d0e7a4814c565 100644 --- a/Lib/graphlib.py +++ b/Lib/graphlib.py @@ -22,7 +22,8 @@ def __init__(self, node): class CycleError(ValueError): - """Subclass of ValueError raised by TopologicalSorterif cycles exist in the graph + """Subclass of ValueError raised by TopologicalSorter.prepare if cycles + exist in the working graph. If multiple cycles exist, only one undefined choice among them will be reported and included in the exception. The detected cycle can be accessed via the second @@ -129,7 +130,7 @@ def get_ready(self): return result def is_active(self): - """Return True if more progress can be made and ``False`` otherwise. + """Return ``True`` if more progress can be made and ``False`` otherwise. Progress can be made if cycles do not block the resolution and either there are still nodes ready that haven't yet been returned by "get_ready" or the @@ -149,7 +150,7 @@ def done(self, *nodes): """Marks a set of nodes returned by "get_ready" as processed. This method unblocks any successor of each node in *nodes* for being returned - in the future by a a call to "get_ready" + in the future by a call to "get_ready". Raises :exec:`ValueError` if any node in *nodes* has already been marked as processed by a previous call to this method, if a node was not added to the From webhook-mailer at python.org Mon Oct 12 15:10:50 2020 From: webhook-mailer at python.org (Vladimir Matveev) Date: Mon, 12 Oct 2020 19:10:50 -0000 Subject: [Python-checkins] Delete PyGen_Send (#22663) Message-ID: https://github.com/python/cpython/commit/24a54c0bd48d9f6f1a1289ca57afb381bc4b280e commit: 24a54c0bd48d9f6f1a1289ca57afb381bc4b280e branch: master author: Vladimir Matveev committer: GitHub date: 2020-10-12T12:10:42-07:00 summary: Delete PyGen_Send (#22663) files: M Doc/c-api/gen.rst M Doc/data/refcounts.dat M Doc/whatsnew/3.10.rst M Include/genobject.h M Misc/NEWS.d/3.10.0a1.rst M Objects/abstract.c M Objects/genobject.c diff --git a/Doc/c-api/gen.rst b/Doc/c-api/gen.rst index 600f53486f79d..74410927bfde1 100644 --- a/Doc/c-api/gen.rst +++ b/Doc/c-api/gen.rst @@ -42,13 +42,3 @@ than explicitly calling :c:func:`PyGen_New` or :c:func:`PyGen_NewWithQualName`. with ``__name__`` and ``__qualname__`` set to *name* and *qualname*. A reference to *frame* is stolen by this function. The *frame* argument must not be ``NULL``. - -.. c:function:: PySendResult PyGen_Send(PyGenObject *gen, PyObject *arg, PyObject **presult) - - Sends the *arg* value into the generator *gen*. Coroutine objects - are also allowed to be as the *gen* argument but they need to be - explicitly casted to PyGenObject*. Returns: - - - ``PYGEN_RETURN`` if generator returns. Return value is returned via *presult*. - - ``PYGEN_NEXT`` if generator yields. Yielded value is returned via *presult*. - - ``PYGEN_ERROR`` if generator has raised and exception. *presult* is set to ``NULL``. diff --git a/Doc/data/refcounts.dat b/Doc/data/refcounts.dat index 87ce5d03d0064..d01e99ca5e319 100644 --- a/Doc/data/refcounts.dat +++ b/Doc/data/refcounts.dat @@ -959,11 +959,6 @@ PyGen_NewWithQualName:PyFrameObject*:frame:0: PyGen_NewWithQualName:PyObject*:name:0: PyGen_NewWithQualName:PyObject*:qualname:0: -PyGen_Send:int::: -PyGen_Send:PyGenObject*:gen:0: -PyGen_Send:PyObject*:arg:0: -PyGen_Send:PyObject**:presult:+1: - PyCoro_CheckExact:int::: PyCoro_CheckExact:PyObject*:ob:0: diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index 1c50978a8b750..c8ddcd2d24296 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -314,7 +314,7 @@ New Features search function. (Contributed by Hai Shi in :issue:`41842`.) -* The :c:func:`PyIter_Send` and :c:func:`PyGen_Send` functions were added to allow +* The :c:func:`PyIter_Send` function was added to allow sending value into iterator without raising ``StopIteration`` exception. (Contributed by Vladimir Matveev in :issue:`41756`.) diff --git a/Include/genobject.h b/Include/genobject.h index e719b25a80072..e965334a0140c 100644 --- a/Include/genobject.h +++ b/Include/genobject.h @@ -45,15 +45,6 @@ PyAPI_FUNC(int) _PyGen_FetchStopIterationValue(PyObject **); PyObject *_PyGen_yf(PyGenObject *); PyAPI_FUNC(void) _PyGen_Finalize(PyObject *self); -/* Sends the value into the generator or the coroutine. Returns: - - PYGEN_RETURN (0) if generator has returned. - 'result' parameter is filled with return value - - PYGEN_ERROR (-1) if exception was raised. - 'result' parameter is NULL - - PYGEN_NEXT (1) if generator has yielded. - 'result' parameter is filled with yielded value. */ -PyAPI_FUNC(PySendResult) PyGen_Send(PyGenObject *, PyObject *, PyObject **); - #ifndef Py_LIMITED_API typedef struct { _PyGenObject_HEAD(cr) diff --git a/Misc/NEWS.d/3.10.0a1.rst b/Misc/NEWS.d/3.10.0a1.rst index 725dfd16b180a..044bd20594cc3 100644 --- a/Misc/NEWS.d/3.10.0a1.rst +++ b/Misc/NEWS.d/3.10.0a1.rst @@ -133,16 +133,6 @@ Port the :mod:`_lsprof` extension module to multi-phase initialization .. -.. bpo: 41756 -.. date: 2020-09-12-12-55-45 -.. nonce: 1h0tbV -.. section: Core and Builtins - -Add PyGen_Send function to allow sending value into generator/coroutine -without raising StopIteration exception to signal return - -.. - .. bpo: 1635741 .. date: 2020-09-08-21-58-47 .. nonce: vdjSLH diff --git a/Objects/abstract.c b/Objects/abstract.c index 502a2d64e25e1..562549876beed 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -2669,31 +2669,6 @@ PyIter_Next(PyObject *iter) return result; } -PySendResult -PyIter_Send(PyObject *iter, PyObject *arg, PyObject **result) -{ - _Py_IDENTIFIER(send); - assert(result != NULL); - - if (PyGen_CheckExact(iter) || PyCoro_CheckExact(iter)) { - return PyGen_Send((PyGenObject *)iter, arg, result); - } - - if (arg == Py_None && PyIter_Check(iter)) { - *result = Py_TYPE(iter)->tp_iternext(iter); - } - else { - *result = _PyObject_CallMethodIdOneArg(iter, &PyId_send, arg); - } - if (*result != NULL) { - return PYGEN_NEXT; - } - if (_PyGen_FetchStopIterationValue(result) == 0) { - return PYGEN_RETURN; - } - return PYGEN_ERROR; -} - /* * Flatten a sequence of bytes() objects into a C array of * NULL terminated string pointers with a NULL char* terminating the array. diff --git a/Objects/genobject.c b/Objects/genobject.c index eb134ebf4bc87..c1b26e9da33be 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -269,13 +269,29 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, } PySendResult -PyGen_Send(PyGenObject *gen, PyObject *arg, PyObject **result) +PyIter_Send(PyObject *iter, PyObject *arg, PyObject **result) { - assert(PyGen_CheckExact(gen) || PyCoro_CheckExact(gen)); - assert(result != NULL); + _Py_IDENTIFIER(send); assert(arg != NULL); + assert(result != NULL); + + if (PyGen_CheckExact(iter) || PyCoro_CheckExact(iter)) { + return gen_send_ex2((PyGenObject *)iter, arg, result, 0, 0); + } - return gen_send_ex2(gen, arg, result, 0, 0); + if (arg == Py_None && PyIter_Check(iter)) { + *result = Py_TYPE(iter)->tp_iternext(iter); + } + else { + *result = _PyObject_CallMethodIdOneArg(iter, &PyId_send, arg); + } + if (*result != NULL) { + return PYGEN_NEXT; + } + if (_PyGen_FetchStopIterationValue(result) == 0) { + return PYGEN_RETURN; + } + return PYGEN_ERROR; } static PyObject * From webhook-mailer at python.org Mon Oct 12 16:27:09 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Mon, 12 Oct 2020 20:27:09 -0000 Subject: [Python-checkins] [doc] Fix typo in the graphlib docs (GH-22661) (GH-22669) Message-ID: https://github.com/python/cpython/commit/85d59644d9c53dcf4654f0177e2243b86d2a26c9 commit: 85d59644d9c53dcf4654f0177e2243b86d2a26c9 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-12T21:27:05+01:00 summary: [doc] Fix typo in the graphlib docs (GH-22661) (GH-22669) Automerge-Triggered-By: @pablogsal (cherry picked from commit abe244c458f2b1a727af4d2a56f71813e501bcb4) Co-authored-by: linchiwei123 <40888469+linchiwei123 at users.noreply.github.com> Co-authored-by: linchiwei123 <40888469+linchiwei123 at users.noreply.github.com> files: M Lib/graphlib.py diff --git a/Lib/graphlib.py b/Lib/graphlib.py index 948f62f1dc303..d0e7a4814c565 100644 --- a/Lib/graphlib.py +++ b/Lib/graphlib.py @@ -22,7 +22,8 @@ def __init__(self, node): class CycleError(ValueError): - """Subclass of ValueError raised by TopologicalSorterif cycles exist in the graph + """Subclass of ValueError raised by TopologicalSorter.prepare if cycles + exist in the working graph. If multiple cycles exist, only one undefined choice among them will be reported and included in the exception. The detected cycle can be accessed via the second @@ -129,7 +130,7 @@ def get_ready(self): return result def is_active(self): - """Return True if more progress can be made and ``False`` otherwise. + """Return ``True`` if more progress can be made and ``False`` otherwise. Progress can be made if cycles do not block the resolution and either there are still nodes ready that haven't yet been returned by "get_ready" or the @@ -149,7 +150,7 @@ def done(self, *nodes): """Marks a set of nodes returned by "get_ready" as processed. This method unblocks any successor of each node in *nodes* for being returned - in the future by a a call to "get_ready" + in the future by a call to "get_ready". Raises :exec:`ValueError` if any node in *nodes* has already been marked as processed by a previous call to this method, if a node was not added to the From webhook-mailer at python.org Mon Oct 12 17:06:28 2020 From: webhook-mailer at python.org (Yannick Jadoul) Date: Mon, 12 Oct 2020 21:06:28 -0000 Subject: [Python-checkins] bpo-42015: Reorder dereferencing calls in meth_dealloc, to make sure m_self is kept alive long enough (GH-22670) Message-ID: https://github.com/python/cpython/commit/04b8631d84a870dda456ef86039c1baf34d08500 commit: 04b8631d84a870dda456ef86039c1baf34d08500 branch: master author: Yannick Jadoul committer: GitHub date: 2020-10-13T00:06:19+03:00 summary: bpo-42015: Reorder dereferencing calls in meth_dealloc, to make sure m_self is kept alive long enough (GH-22670) files: A Misc/NEWS.d/next/C API/2020-10-12-20-13-58.bpo-42015.X4H2_V.rst M Objects/methodobject.c diff --git a/Misc/NEWS.d/next/C API/2020-10-12-20-13-58.bpo-42015.X4H2_V.rst b/Misc/NEWS.d/next/C API/2020-10-12-20-13-58.bpo-42015.X4H2_V.rst new file mode 100644 index 0000000000000..d77619f64bb17 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2020-10-12-20-13-58.bpo-42015.X4H2_V.rst @@ -0,0 +1,3 @@ +Fix potential crash in deallocating method objects when dynamically +allocated `PyMethodDef`'s lifetime is managed through the ``self`` +argument of a `PyCFunction`. diff --git a/Objects/methodobject.c b/Objects/methodobject.c index 5659f2143d182..7b430416c5a04 100644 --- a/Objects/methodobject.c +++ b/Objects/methodobject.c @@ -164,9 +164,11 @@ meth_dealloc(PyCFunctionObject *m) if (m->m_weakreflist != NULL) { PyObject_ClearWeakRefs((PyObject*) m); } + // Dereference class before m_self: PyCFunction_GET_CLASS accesses + // PyMethodDef m_ml, which could be kept alive by m_self + Py_XDECREF(PyCFunction_GET_CLASS(m)); Py_XDECREF(m->m_self); Py_XDECREF(m->m_module); - Py_XDECREF(PyCFunction_GET_CLASS(m)); PyObject_GC_Del(m); } @@ -243,9 +245,9 @@ meth_get__qualname__(PyCFunctionObject *m, void *closure) static int meth_traverse(PyCFunctionObject *m, visitproc visit, void *arg) { + Py_VISIT(PyCFunction_GET_CLASS(m)); Py_VISIT(m->m_self); Py_VISIT(m->m_module); - Py_VISIT(PyCFunction_GET_CLASS(m)); return 0; } From webhook-mailer at python.org Mon Oct 12 17:29:12 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Mon, 12 Oct 2020 21:29:12 -0000 Subject: [Python-checkins] bpo-42015: Reorder dereferencing calls in meth_dealloc, to make sure m_self is kept alive long enough (GH-22670) Message-ID: https://github.com/python/cpython/commit/8a12503b4532e33d590ecea7eb94cd0e6b0f1488 commit: 8a12503b4532e33d590ecea7eb94cd0e6b0f1488 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-12T14:29:01-07:00 summary: bpo-42015: Reorder dereferencing calls in meth_dealloc, to make sure m_self is kept alive long enough (GH-22670) (cherry picked from commit 04b8631d84a870dda456ef86039c1baf34d08500) Co-authored-by: Yannick Jadoul files: A Misc/NEWS.d/next/C API/2020-10-12-20-13-58.bpo-42015.X4H2_V.rst M Objects/methodobject.c diff --git a/Misc/NEWS.d/next/C API/2020-10-12-20-13-58.bpo-42015.X4H2_V.rst b/Misc/NEWS.d/next/C API/2020-10-12-20-13-58.bpo-42015.X4H2_V.rst new file mode 100644 index 0000000000000..d77619f64bb17 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2020-10-12-20-13-58.bpo-42015.X4H2_V.rst @@ -0,0 +1,3 @@ +Fix potential crash in deallocating method objects when dynamically +allocated `PyMethodDef`'s lifetime is managed through the ``self`` +argument of a `PyCFunction`. diff --git a/Objects/methodobject.c b/Objects/methodobject.c index 5659f2143d182..7b430416c5a04 100644 --- a/Objects/methodobject.c +++ b/Objects/methodobject.c @@ -164,9 +164,11 @@ meth_dealloc(PyCFunctionObject *m) if (m->m_weakreflist != NULL) { PyObject_ClearWeakRefs((PyObject*) m); } + // Dereference class before m_self: PyCFunction_GET_CLASS accesses + // PyMethodDef m_ml, which could be kept alive by m_self + Py_XDECREF(PyCFunction_GET_CLASS(m)); Py_XDECREF(m->m_self); Py_XDECREF(m->m_module); - Py_XDECREF(PyCFunction_GET_CLASS(m)); PyObject_GC_Del(m); } @@ -243,9 +245,9 @@ meth_get__qualname__(PyCFunctionObject *m, void *closure) static int meth_traverse(PyCFunctionObject *m, visitproc visit, void *arg) { + Py_VISIT(PyCFunction_GET_CLASS(m)); Py_VISIT(m->m_self); Py_VISIT(m->m_module); - Py_VISIT(PyCFunction_GET_CLASS(m)); return 0; } From webhook-mailer at python.org Mon Oct 12 19:34:42 2020 From: webhook-mailer at python.org (Saiyang Gou) Date: Mon, 12 Oct 2020 23:34:42 -0000 Subject: [Python-checkins] Fix typo in "Context manager types" section in typing.rst (GH-22676) Message-ID: https://github.com/python/cpython/commit/ba06a70c822ede62688136add488eb78957689fe commit: ba06a70c822ede62688136add488eb78957689fe branch: master author: Saiyang Gou committer: GitHub date: 2020-10-12T16:34:33-07:00 summary: Fix typo in "Context manager types" section in typing.rst (GH-22676) Fix typo in the "Context manager types" section in `typing.rst`. Automerge-Triggered-By: @gvanrossum files: M Doc/library/typing.rst diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 6111603a995c5..5fb78bbde69b1 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -1478,7 +1478,7 @@ Context manager types .. versionadded:: 3.6.0 .. deprecated:: 3.9 - :class:`collections.contextlib.AbstractContextManager` now supports ``[]``. See :pep:`585`. + :class:`contextlib.AbstractContextManager` now supports ``[]``. See :pep:`585`. .. class:: AsyncContextManager(Generic[T_co]) @@ -1488,7 +1488,7 @@ Context manager types .. versionadded:: 3.6.2 .. deprecated:: 3.9 - :class:`collections.contextlib.AbstractAsyncContextManager` now supports ``[]``. See :pep:`585`. + :class:`contextlib.AbstractAsyncContextManager` now supports ``[]``. See :pep:`585`. Protocols --------- From webhook-mailer at python.org Mon Oct 12 19:52:12 2020 From: webhook-mailer at python.org (Saiyang Gou) Date: Mon, 12 Oct 2020 23:52:12 -0000 Subject: [Python-checkins] [3.9] bpo-39481: Fix duplicate SimpleQueue type in test_genericalias.py (GH-22619) (#22624) Message-ID: https://github.com/python/cpython/commit/1c217652b690c184b43d8e2b6529a60a14e64328 commit: 1c217652b690c184b43d8e2b6529a60a14e64328 branch: 3.9 author: Saiyang Gou committer: GitHub date: 2020-10-12T16:52:03-07:00 summary: [3.9] bpo-39481: Fix duplicate SimpleQueue type in test_genericalias.py (GH-22619) (#22624) There are two different `SimpleQueue` types imported (from `multiprocessing.queues` and `queue`) in `Lib/test/test_genericalias.py`, the second one shadowing the first one, making the first one not actually tested. Fix by using different names. Automerge-Triggered-By: @gvanrossum. (cherry picked from commit b2c0a43699bd9023a69e3fa554f5488a2e17e278) Co-authored-by: Saiyang Gou files: M Lib/test/test_genericalias.py diff --git a/Lib/test/test_genericalias.py b/Lib/test/test_genericalias.py index ec1acd475ce5a..240aad02518ff 100644 --- a/Lib/test/test_genericalias.py +++ b/Lib/test/test_genericalias.py @@ -26,7 +26,7 @@ except ImportError: # multiprocessing.shared_memory is not available on e.g. Android ShareableList = None -from multiprocessing.queues import SimpleQueue +from multiprocessing.queues import SimpleQueue as MPSimpleQueue from os import DirEntry from re import Pattern, Match from types import GenericAlias, MappingProxyType, AsyncGeneratorType @@ -79,7 +79,7 @@ def test_subscriptable(self): SplitResult, ParseResult, ValueProxy, ApplyResult, WeakSet, ReferenceType, ref, - ShareableList, SimpleQueue, + ShareableList, MPSimpleQueue, Future, _WorkItem, Morsel, ): From webhook-mailer at python.org Mon Oct 12 19:53:24 2020 From: webhook-mailer at python.org (Kyle Evans) Date: Mon, 12 Oct 2020 23:53:24 -0000 Subject: [Python-checkins] bpo-40422: Move _Py_*_SUPPRESS_IPH bits into _Py_closerange (GH-22672) Message-ID: https://github.com/python/cpython/commit/64eb259cc1e42a5f74b5911a518d2c50daa8d50b commit: 64eb259cc1e42a5f74b5911a518d2c50daa8d50b branch: master author: Kyle Evans committer: GitHub date: 2020-10-12T16:53:16-07:00 summary: bpo-40422: Move _Py_*_SUPPRESS_IPH bits into _Py_closerange (GH-22672) This suppression is no longer needed in os_closerange_impl, as it just invokes the internal _Py_closerange implementation. On the other hand, consumers of _Py_closerange may not have any other reason to suppress invalid parameter issues, so narrow the scope to here. files: M Modules/posixmodule.c diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 2e0caaa3e561b..de81db8b84fe1 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -8782,6 +8782,7 @@ void _Py_closerange(int first, int last) { first = Py_MAX(first, 0); + _Py_BEGIN_SUPPRESS_IPH #ifdef HAVE_CLOSE_RANGE if (close_range(first, last, 0) == 0 || errno != ENOSYS) { /* Any errors encountered while closing file descriptors are ignored; @@ -8812,6 +8813,7 @@ _Py_closerange(int first, int last) } } #endif /* USE_FDWALK */ + _Py_END_SUPPRESS_IPH } /*[clinic input] @@ -8829,9 +8831,7 @@ os_closerange_impl(PyObject *module, int fd_low, int fd_high) /*[clinic end generated code: output=0ce5c20fcda681c2 input=5855a3d053ebd4ec]*/ { Py_BEGIN_ALLOW_THREADS - _Py_BEGIN_SUPPRESS_IPH _Py_closerange(fd_low, fd_high - 1); - _Py_END_SUPPRESS_IPH Py_END_ALLOW_THREADS Py_RETURN_NONE; } From webhook-mailer at python.org Mon Oct 12 19:55:34 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Mon, 12 Oct 2020 23:55:34 -0000 Subject: [Python-checkins] Fix typo in "Context manager types" section in typing.rst (GH-22676) Message-ID: https://github.com/python/cpython/commit/15ef19f7d7d703bba65da9e2c10fa7851049686a commit: 15ef19f7d7d703bba65da9e2c10fa7851049686a branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-12T16:55:24-07:00 summary: Fix typo in "Context manager types" section in typing.rst (GH-22676) Fix typo in the "Context manager types" section in `typing.rst`. Automerge-Triggered-By: @gvanrossum (cherry picked from commit ba06a70c822ede62688136add488eb78957689fe) Co-authored-by: Saiyang Gou files: M Doc/library/typing.rst diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 108d2eeeaf41f..e8f34e44a0c0e 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -1461,7 +1461,7 @@ Context manager types .. versionadded:: 3.6.0 .. deprecated:: 3.9 - :class:`collections.contextlib.AbstractContextManager` now supports ``[]``. See :pep:`585`. + :class:`contextlib.AbstractContextManager` now supports ``[]``. See :pep:`585`. .. class:: AsyncContextManager(Generic[T_co]) @@ -1471,7 +1471,7 @@ Context manager types .. versionadded:: 3.6.2 .. deprecated:: 3.9 - :class:`collections.contextlib.AbstractAsyncContextManager` now supports ``[]``. See :pep:`585`. + :class:`contextlib.AbstractAsyncContextManager` now supports ``[]``. See :pep:`585`. Protocols --------- From webhook-mailer at python.org Tue Oct 13 02:46:48 2020 From: webhook-mailer at python.org (Yunlongs) Date: Tue, 13 Oct 2020 06:46:48 -0000 Subject: [Python-checkins] bpo-41995: Fix null ptr deref in tracemalloc_copy_trace() (GH-22660) Message-ID: https://github.com/python/cpython/commit/66c28f50c76e4f23af7146e0e580457c5fd6bde7 commit: 66c28f50c76e4f23af7146e0e580457c5fd6bde7 branch: master author: Yunlongs committer: GitHub date: 2020-10-13T08:46:31+02:00 summary: bpo-41995: Fix null ptr deref in tracemalloc_copy_trace() (GH-22660) Fix a null pointer dereference in tracemalloc_copy_trace() of _tracemalloc. files: M Modules/_tracemalloc.c diff --git a/Modules/_tracemalloc.c b/Modules/_tracemalloc.c index fc91622d3925b..04f6c243b5ca4 100644 --- a/Modules/_tracemalloc.c +++ b/Modules/_tracemalloc.c @@ -1199,7 +1199,7 @@ tracemalloc_copy_trace(_Py_hashtable_t *traces, trace_t *trace = (trace_t *)value; trace_t *trace2 = raw_malloc(sizeof(trace_t)); - if (traces2 == NULL) { + if (trace2 == NULL) { return -1; } *trace2 = *trace; From webhook-mailer at python.org Tue Oct 13 03:09:17 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Tue, 13 Oct 2020 07:09:17 -0000 Subject: [Python-checkins] bpo-41995: Fix null ptr deref in tracemalloc_copy_trace() (GH-22660) Message-ID: https://github.com/python/cpython/commit/afe86066e748076f970ccd277fc64fc51bea189b commit: afe86066e748076f970ccd277fc64fc51bea189b branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-13T00:09:08-07:00 summary: bpo-41995: Fix null ptr deref in tracemalloc_copy_trace() (GH-22660) Fix a null pointer dereference in tracemalloc_copy_trace() of _tracemalloc. (cherry picked from commit 66c28f50c76e4f23af7146e0e580457c5fd6bde7) Co-authored-by: Yunlongs files: M Modules/_tracemalloc.c diff --git a/Modules/_tracemalloc.c b/Modules/_tracemalloc.c index fc91622d3925b..04f6c243b5ca4 100644 --- a/Modules/_tracemalloc.c +++ b/Modules/_tracemalloc.c @@ -1199,7 +1199,7 @@ tracemalloc_copy_trace(_Py_hashtable_t *traces, trace_t *trace = (trace_t *)value; trace_t *trace2 = raw_malloc(sizeof(trace_t)); - if (traces2 == NULL) { + if (trace2 == NULL) { return -1; } *trace2 = *trace; From webhook-mailer at python.org Tue Oct 13 11:19:23 2020 From: webhook-mailer at python.org (Dong-hee Na) Date: Tue, 13 Oct 2020 15:19:23 -0000 Subject: [Python-checkins] [3.8] bpo-32793: Fix a duplicate debug message in smtplib (GH-15341) (GH-22683) Message-ID: https://github.com/python/cpython/commit/76b1913daf883b6592815d139f62f3a7fbe3c322 commit: 76b1913daf883b6592815d139f62f3a7fbe3c322 branch: 3.8 author: Dong-hee Na committer: GitHub date: 2020-10-14T00:18:58+09:00 summary: [3.8] bpo-32793: Fix a duplicate debug message in smtplib (GH-15341) (GH-22683) _get_socket() already prints a debug message for the host and port. https://bugs.python.org/issue32793 Automerge-Triggered-By: @maxking (cherry picked from commit 46a7564578f208df1e0c54fc0520d3b7ca32c981) Co-authored-by: Zackery Spytz Co-authored-by: Zackery Spytz files: A Misc/NEWS.d/next/Library/2019-08-20-05-17-32.bpo-32793.cgpXl6.rst M Lib/smtplib.py diff --git a/Lib/smtplib.py b/Lib/smtplib.py index 8e3d4bf0ccf3a..6513842eb6c61 100755 --- a/Lib/smtplib.py +++ b/Lib/smtplib.py @@ -333,8 +333,6 @@ def connect(self, host='localhost', port=0, source_address=None): raise OSError("nonnumeric port") if not port: port = self.default_port - if self.debuglevel > 0: - self._print_debug('connect:', (host, port)) sys.audit("smtplib.connect", self, host, port) self.sock = self._get_socket(host, port, self.timeout) self.file = None diff --git a/Misc/NEWS.d/next/Library/2019-08-20-05-17-32.bpo-32793.cgpXl6.rst b/Misc/NEWS.d/next/Library/2019-08-20-05-17-32.bpo-32793.cgpXl6.rst new file mode 100644 index 0000000000000..f715a816efda9 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-08-20-05-17-32.bpo-32793.cgpXl6.rst @@ -0,0 +1 @@ +Fix a duplicated debug message when :meth:`smtplib.SMTP.connect` is called. From webhook-mailer at python.org Tue Oct 13 13:27:01 2020 From: webhook-mailer at python.org (Vladimir Matveev) Date: Tue, 13 Oct 2020 17:27:01 -0000 Subject: [Python-checkins] bpo-41756: Export PyGen_Send and wrap it in if-defs (#22677) Message-ID: https://github.com/python/cpython/commit/cfb0f57ff876ab3d04ff144f19eda58844981643 commit: cfb0f57ff876ab3d04ff144f19eda58844981643 branch: master author: Vladimir Matveev committer: GitHub date: 2020-10-13T10:26:51-07:00 summary: bpo-41756: Export PyGen_Send and wrap it in if-defs (#22677) files: M Doc/c-api/iter.rst M Include/abstract.h M PC/python3dll.c diff --git a/Doc/c-api/iter.rst b/Doc/c-api/iter.rst index a068a43c86b6c..68df6f6e89f51 100644 --- a/Doc/c-api/iter.rst +++ b/Doc/c-api/iter.rst @@ -50,6 +50,8 @@ something like this:: The enum value used to represent different results of :c:func:`PyIter_Send`. + .. versionadded:: 3.10 + .. c:function:: PySendResult PyIter_Send(PyObject *iter, PyObject *arg, PyObject **presult) @@ -58,3 +60,5 @@ something like this:: - ``PYGEN_RETURN`` if iterator returns. Return value is returned via *presult*. - ``PYGEN_NEXT`` if iterator yields. Yielded value is returned via *presult*. - ``PYGEN_ERROR`` if iterator has raised and exception. *presult* is set to ``NULL``. + + .. versionadded:: 3.10 diff --git a/Include/abstract.h b/Include/abstract.h index 716cd4b5ebbba..28e576b92935f 100644 --- a/Include/abstract.h +++ b/Include/abstract.h @@ -338,6 +338,7 @@ PyAPI_FUNC(int) PyIter_Check(PyObject *); NULL with an exception means an error occurred. */ PyAPI_FUNC(PyObject *) PyIter_Next(PyObject *); +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030A0000 typedef enum { PYGEN_RETURN = 0, PYGEN_ERROR = -1, @@ -353,6 +354,7 @@ typedef enum { - PYGEN_NEXT (1) if generator has yielded. 'result' parameter is filled with yielded value. */ PyAPI_FUNC(PySendResult) PyIter_Send(PyObject *, PyObject *, PyObject **); +#endif /* === Number Protocol ================================================== */ diff --git a/PC/python3dll.c b/PC/python3dll.c index ff69ea7ca5efa..153ba612b7804 100644 --- a/PC/python3dll.c +++ b/PC/python3dll.c @@ -287,6 +287,7 @@ EXPORT_FUNC(PyInterpreterState_GetID) EXPORT_FUNC(PyInterpreterState_New) EXPORT_FUNC(PyIter_Check) EXPORT_FUNC(PyIter_Next) +EXPORT_FUNC(PyIter_Send) EXPORT_FUNC(PyList_Append) EXPORT_FUNC(PyList_AsTuple) EXPORT_FUNC(PyList_GetItem) From webhook-mailer at python.org Tue Oct 13 14:54:31 2020 From: webhook-mailer at python.org (Raymond Hettinger) Date: Tue, 13 Oct 2020 18:54:31 -0000 Subject: [Python-checkins] Add recipe for a version of random() with a larger population (GH-22664) Message-ID: https://github.com/python/cpython/commit/8b2ff4c03d150c43df3e8438d323b7f7bfe3353c commit: 8b2ff4c03d150c43df3e8438d323b7f7bfe3353c branch: master author: Raymond Hettinger committer: GitHub date: 2020-10-13T11:54:21-07:00 summary: Add recipe for a version of random() with a larger population (GH-22664) files: M Doc/library/random.rst diff --git a/Doc/library/random.rst b/Doc/library/random.rst index af5131df280c2..c19a8e0a7cb5b 100644 --- a/Doc/library/random.rst +++ b/Doc/library/random.rst @@ -391,8 +391,8 @@ change across Python versions, but two aspects are guaranteed not to change: .. _random-examples: -Examples and Recipes --------------------- +Examples +-------- Basic examples:: @@ -516,6 +516,52 @@ Simulation of arrival times and service deliveries for a multiserver queue:: print(f'Mean wait: {mean(waits):.1f} Max wait: {max(waits):.1f}') print('Quartiles:', [round(q, 1) for q in quantiles(waits)]) +Recipes +------- + +The default :func:`.random` returns multiples of 2??? in the range +*0.0 ? x < 1.0*. All such numbers are evenly spaced and exactly +representable as Python floats. However, many floats in that interval +are not possible selections. For example, ``0.05954861408025609`` +isn't an integer multiple of 2???. + +The following recipe takes a different approach. All floats in the +interval are possible selections. Conceptually it works by choosing +from evenly spaced multiples of 2????? and then rounding down to the +nearest representable float. + +For efficiency, the actual mechanics involve calling +:func:`~math.ldexp` to construct a representable float. The mantissa +comes from a uniform distribution of integers in the range *2?? ? +mantissa < 2??*. The exponent comes from a geometric distribution +where exponents smaller than *-53* occur half as often as the next +larger exponent. + +:: + + from random import Random + from math import ldexp + + class FullRandom(Random): + + def random(self): + mantissa = 0x10_0000_0000_0000 | self.getrandbits(52) + exponent = -53 + x = 0 + while not x: + x = self.getrandbits(32) + exponent += x.bit_length() - 32 + return ldexp(mantissa, exponent) + +All of the real valued distributions will use the new method:: + + >>> fr = FullRandom() + >>> fr.random() + 0.05954861408025609 + >>> fr.expovariate(0.25) + 8.87925541791544 + + .. seealso:: `Statistics for Hackers `_ @@ -536,3 +582,8 @@ Simulation of arrival times and service deliveries for a multiserver queue:: a tutorial by `Peter Norvig `_ covering the basics of probability theory, how to write simulations, and how to perform data analysis using Python. + + `Generating Pseudo-random Floating-Point Values + `_ a + paper by Allen B. Downey describing ways to generate more + fine-grained floats than normally generated by :func:`.random`. From webhook-mailer at python.org Tue Oct 13 15:38:37 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Tue, 13 Oct 2020 19:38:37 -0000 Subject: [Python-checkins] Add recipe for a version of random() with a larger population (GH-22664) (GH-22684) Message-ID: https://github.com/python/cpython/commit/5f0007f0f8ffb6be5cc2945b77868405665ecbad commit: 5f0007f0f8ffb6be5cc2945b77868405665ecbad branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-13T12:38:13-07:00 summary: Add recipe for a version of random() with a larger population (GH-22664) (GH-22684) files: M Doc/library/random.rst diff --git a/Doc/library/random.rst b/Doc/library/random.rst index 90366f499cae6..c43307ca4be31 100644 --- a/Doc/library/random.rst +++ b/Doc/library/random.rst @@ -391,8 +391,8 @@ change across Python versions, but two aspects are guaranteed not to change: .. _random-examples: -Examples and Recipes --------------------- +Examples +-------- Basic examples:: @@ -516,6 +516,52 @@ Simulation of arrival times and service deliveries for a multiserver queue:: print(f'Mean wait: {mean(waits):.1f}. Stdev wait: {stdev(waits):.1f}.') print(f'Median wait: {median(waits):.1f}. Max wait: {max(waits):.1f}.') +Recipes +------- + +The default :func:`.random` returns multiples of 2??? in the range +*0.0 ? x < 1.0*. All such numbers are evenly spaced and exactly +representable as Python floats. However, many floats in that interval +are not possible selections. For example, ``0.05954861408025609`` +isn't an integer multiple of 2???. + +The following recipe takes a different approach. All floats in the +interval are possible selections. Conceptually it works by choosing +from evenly spaced multiples of 2????? and then rounding down to the +nearest representable float. + +For efficiency, the actual mechanics involve calling +:func:`~math.ldexp` to construct a representable float. The mantissa +comes from a uniform distribution of integers in the range *2?? ? +mantissa < 2??*. The exponent comes from a geometric distribution +where exponents smaller than *-53* occur half as often as the next +larger exponent. + +:: + + from random import Random + from math import ldexp + + class FullRandom(Random): + + def random(self): + mantissa = 0x10_0000_0000_0000 | self.getrandbits(52) + exponent = -53 + x = 0 + while not x: + x = self.getrandbits(32) + exponent += x.bit_length() - 32 + return ldexp(mantissa, exponent) + +All of the real valued distributions will use the new method:: + + >>> fr = FullRandom() + >>> fr.random() + 0.05954861408025609 + >>> fr.expovariate(0.25) + 8.87925541791544 + + .. seealso:: `Statistics for Hackers `_ @@ -536,3 +582,8 @@ Simulation of arrival times and service deliveries for a multiserver queue:: a tutorial by `Peter Norvig `_ covering the basics of probability theory, how to write simulations, and how to perform data analysis using Python. + + `Generating Pseudo-random Floating-Point Values + `_ a + paper by Allen B. Downey describing ways to generate more + fine-grained floats than normally generated by :func:`.random`. From webhook-mailer at python.org Tue Oct 13 16:04:51 2020 From: webhook-mailer at python.org (Kyle Evans) Date: Tue, 13 Oct 2020 20:04:51 -0000 Subject: [Python-checkins] bpo-40422: Move _Py_closerange to fileutils.c (GH-22680) Message-ID: https://github.com/python/cpython/commit/7992579cd27f14f472acc37aca537eec55f681ef commit: 7992579cd27f14f472acc37aca537eec55f681ef branch: master author: Kyle Evans committer: GitHub date: 2020-10-13T22:04:44+02:00 summary: bpo-40422: Move _Py_closerange to fileutils.c (GH-22680) This API is relatively lightweight and organizationally, given that it's used by multiple modules, it makes sense to move it to fileutils. Requires making sure that _posixsubprocess is compiled with the appropriate Py_BUIILD_CORE_BUILTIN macro. files: M Include/internal/pycore_fileutils.h M Modules/Setup M Modules/_posixsubprocess.c M Modules/posixmodule.c M Modules/posixmodule.h M Python/fileutils.c M setup.py diff --git a/Include/internal/pycore_fileutils.h b/Include/internal/pycore_fileutils.h index bbee58617fd05..9cb5fc66ee2e0 100644 --- a/Include/internal/pycore_fileutils.h +++ b/Include/internal/pycore_fileutils.h @@ -48,6 +48,8 @@ PyAPI_FUNC(int) _Py_GetLocaleconvNumeric( PyObject **decimal_point, PyObject **thousands_sep); +PyAPI_FUNC(void) _Py_closerange(int first, int last); + #ifdef __cplusplus } #endif diff --git a/Modules/Setup b/Modules/Setup index 470bf6bc2efbf..87f3a7cb43a02 100644 --- a/Modules/Setup +++ b/Modules/Setup @@ -226,7 +226,7 @@ _symtable symtablemodule.c #termios termios.c # Steen Lumholt's termios module #resource resource.c # Jeremy Hylton's rlimit interface -#_posixsubprocess _posixsubprocess.c # POSIX subprocess module helper +#_posixsubprocess -DPy_BUILD_CORE_BUILTIN _posixsubprocess.c # POSIX subprocess module helper # Multimedia modules -- off by default. # These don't work for 64-bit platforms!!! diff --git a/Modules/_posixsubprocess.c b/Modules/_posixsubprocess.c index ed046fc5c1ba9..d08c47980e9c6 100644 --- a/Modules/_posixsubprocess.c +++ b/Modules/_posixsubprocess.c @@ -1,5 +1,6 @@ /* Authors: Gregory P. Smith & Jeffrey Yasskin */ #include "Python.h" +#include "pycore_fileutils.h" #if defined(HAVE_PIPE2) && !defined(_GNU_SOURCE) # define _GNU_SOURCE #endif diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index de81db8b84fe1..6ce0bcb9fe8ca 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -22,6 +22,7 @@ #define PY_SSIZE_T_CLEAN #include "Python.h" +#include "pycore_fileutils.h" #ifdef MS_WINDOWS /* include early to avoid conflict with pycore_condvar.h: @@ -8740,82 +8741,6 @@ os_close_impl(PyObject *module, int fd) Py_RETURN_NONE; } -/* Our selection logic for which function to use is as follows: - * 1. If close_range(2) is available, always prefer that; it's better for - * contiguous ranges like this than fdwalk(3) which entails iterating over - * the entire fd space and simply doing nothing for those outside the range. - * 2. If closefrom(2) is available, we'll attempt to use that next if we're - * closing up to sysconf(_SC_OPEN_MAX). - * 2a. Fallback to fdwalk(3) if we're not closing up to sysconf(_SC_OPEN_MAX), - * as that will be more performant if the range happens to have any chunk of - * non-opened fd in the middle. - * 2b. If fdwalk(3) isn't available, just do a plain close(2) loop. - */ -#ifdef __FreeBSD__ -#define USE_CLOSEFROM -#endif /* __FreeBSD__ */ - -#ifdef HAVE_FDWALK -#define USE_FDWALK -#endif /* HAVE_FDWALK */ - -#ifdef USE_FDWALK -static int -_fdwalk_close_func(void *lohi, int fd) -{ - int lo = ((int *)lohi)[0]; - int hi = ((int *)lohi)[1]; - - if (fd >= hi) { - return 1; - } - else if (fd >= lo) { - /* Ignore errors */ - (void)close(fd); - } - return 0; -} -#endif /* USE_FDWALK */ - -/* Closes all file descriptors in [first, last], ignoring errors. */ -void -_Py_closerange(int first, int last) -{ - first = Py_MAX(first, 0); - _Py_BEGIN_SUPPRESS_IPH -#ifdef HAVE_CLOSE_RANGE - if (close_range(first, last, 0) == 0 || errno != ENOSYS) { - /* Any errors encountered while closing file descriptors are ignored; - * ENOSYS means no kernel support, though, - * so we'll fallback to the other methods. */ - } - else -#endif /* HAVE_CLOSE_RANGE */ -#ifdef USE_CLOSEFROM - if (last >= sysconf(_SC_OPEN_MAX)) { - /* Any errors encountered while closing file descriptors are ignored */ - closefrom(first); - } - else -#endif /* USE_CLOSEFROM */ -#ifdef USE_FDWALK - { - int lohi[2]; - lohi[0] = first; - lohi[1] = last + 1; - fdwalk(_fdwalk_close_func, lohi); - } -#else - { - for (int i = first; i <= last; i++) { - /* Ignore errors */ - (void)close(i); - } - } -#endif /* USE_FDWALK */ - _Py_END_SUPPRESS_IPH -} - /*[clinic input] os.closerange diff --git a/Modules/posixmodule.h b/Modules/posixmodule.h index 749833f71cd4d..1e00562abc337 100644 --- a/Modules/posixmodule.h +++ b/Modules/posixmodule.h @@ -28,8 +28,6 @@ PyAPI_FUNC(int) _Py_Sigset_Converter(PyObject *, void *); #endif /* HAVE_SIGSET_T */ #endif /* Py_LIMITED_API */ -PyAPI_FUNC(void) _Py_closerange(int first, int last); - #ifdef __cplusplus } #endif diff --git a/Python/fileutils.c b/Python/fileutils.c index 50ef3c174acc8..b79067f2b5d38 100644 --- a/Python/fileutils.c +++ b/Python/fileutils.c @@ -2106,3 +2106,79 @@ _Py_GetLocaleconvNumeric(struct lconv *lc, PyMem_Free(oldloc); return res; } + +/* Our selection logic for which function to use is as follows: + * 1. If close_range(2) is available, always prefer that; it's better for + * contiguous ranges like this than fdwalk(3) which entails iterating over + * the entire fd space and simply doing nothing for those outside the range. + * 2. If closefrom(2) is available, we'll attempt to use that next if we're + * closing up to sysconf(_SC_OPEN_MAX). + * 2a. Fallback to fdwalk(3) if we're not closing up to sysconf(_SC_OPEN_MAX), + * as that will be more performant if the range happens to have any chunk of + * non-opened fd in the middle. + * 2b. If fdwalk(3) isn't available, just do a plain close(2) loop. + */ +#ifdef __FreeBSD__ +# define USE_CLOSEFROM +#endif /* __FreeBSD__ */ + +#ifdef HAVE_FDWALK +# define USE_FDWALK +#endif /* HAVE_FDWALK */ + +#ifdef USE_FDWALK +static int +_fdwalk_close_func(void *lohi, int fd) +{ + int lo = ((int *)lohi)[0]; + int hi = ((int *)lohi)[1]; + + if (fd >= hi) { + return 1; + } + else if (fd >= lo) { + /* Ignore errors */ + (void)close(fd); + } + return 0; +} +#endif /* USE_FDWALK */ + +/* Closes all file descriptors in [first, last], ignoring errors. */ +void +_Py_closerange(int first, int last) +{ + first = Py_MAX(first, 0); + _Py_BEGIN_SUPPRESS_IPH +#ifdef HAVE_CLOSE_RANGE + if (close_range(first, last, 0) == 0 || errno != ENOSYS) { + /* Any errors encountered while closing file descriptors are ignored; + * ENOSYS means no kernel support, though, + * so we'll fallback to the other methods. */ + } + else +#endif /* HAVE_CLOSE_RANGE */ +#ifdef USE_CLOSEFROM + if (last >= sysconf(_SC_OPEN_MAX)) { + /* Any errors encountered while closing file descriptors are ignored */ + closefrom(first); + } + else +#endif /* USE_CLOSEFROM */ +#ifdef USE_FDWALK + { + int lohi[2]; + lohi[0] = first; + lohi[1] = last + 1; + fdwalk(_fdwalk_close_func, lohi); + } +#else + { + for (int i = first; i <= last; i++) { + /* Ignore errors */ + (void)close(i); + } + } +#endif /* USE_FDWALK */ + _Py_END_SUPPRESS_IPH +} diff --git a/setup.py b/setup.py index 476f8c414978e..d3fd7bca6438a 100644 --- a/setup.py +++ b/setup.py @@ -950,7 +950,8 @@ def detect_simple_extensions(self): self.add(Extension('_csv', ['_csv.c'])) # POSIX subprocess module helper. - self.add(Extension('_posixsubprocess', ['_posixsubprocess.c'])) + self.add(Extension('_posixsubprocess', ['_posixsubprocess.c'], + extra_compile_args=['-DPy_BUILD_CORE_MODULE'])) def detect_test_extensions(self): # Python C API test module From webhook-mailer at python.org Tue Oct 13 19:41:37 2020 From: webhook-mailer at python.org (Raymond Hettinger) Date: Tue, 13 Oct 2020 23:41:37 -0000 Subject: [Python-checkins] Improve recipe readability (GH-22685) Message-ID: https://github.com/python/cpython/commit/f2bd04f6891071742eb000edde1ca81590349103 commit: f2bd04f6891071742eb000edde1ca81590349103 branch: master author: Raymond Hettinger committer: GitHub date: 2020-10-13T16:41:26-07:00 summary: Improve recipe readability (GH-22685) files: M Doc/library/random.rst diff --git a/Doc/library/random.rst b/Doc/library/random.rst index c19a8e0a7cb5b..635f9e1c032da 100644 --- a/Doc/library/random.rst +++ b/Doc/library/random.rst @@ -253,6 +253,8 @@ Functions for sequences order so that the sample is reproducible. +.. _real-valued-distributions: + Real-valued distributions ------------------------- @@ -516,26 +518,42 @@ Simulation of arrival times and service deliveries for a multiserver queue:: print(f'Mean wait: {mean(waits):.1f} Max wait: {max(waits):.1f}') print('Quartiles:', [round(q, 1) for q in quantiles(waits)]) +.. seealso:: + + `Statistics for Hackers `_ + a video tutorial by + `Jake Vanderplas `_ + on statistical analysis using just a few fundamental concepts + including simulation, sampling, shuffling, and cross-validation. + + `Economics Simulation + `_ + a simulation of a marketplace by + `Peter Norvig `_ that shows effective + use of many of the tools and distributions provided by this module + (gauss, uniform, sample, betavariate, choice, triangular, and randrange). + + `A Concrete Introduction to Probability (using Python) + `_ + a tutorial by `Peter Norvig `_ covering + the basics of probability theory, how to write simulations, and + how to perform data analysis using Python. + + Recipes ------- The default :func:`.random` returns multiples of 2??? in the range -*0.0 ? x < 1.0*. All such numbers are evenly spaced and exactly +*0.0 ? x < 1.0*. All such numbers are evenly spaced and are exactly representable as Python floats. However, many floats in that interval are not possible selections. For example, ``0.05954861408025609`` isn't an integer multiple of 2???. The following recipe takes a different approach. All floats in the -interval are possible selections. Conceptually it works by choosing -from evenly spaced multiples of 2????? and then rounding down to the -nearest representable float. - -For efficiency, the actual mechanics involve calling -:func:`~math.ldexp` to construct a representable float. The mantissa -comes from a uniform distribution of integers in the range *2?? ? -mantissa < 2??*. The exponent comes from a geometric distribution -where exponents smaller than *-53* occur half as often as the next -larger exponent. +interval are possible selections. The mantissa comes from a uniform +distribution of integers in the range *2?? ? mantissa < 2??*. The +exponent comes from a geometric distribution where exponents smaller +than *-53* occur half as often as the next larger exponent. :: @@ -553,7 +571,8 @@ larger exponent. exponent += x.bit_length() - 32 return ldexp(mantissa, exponent) -All of the real valued distributions will use the new method:: +All :ref:`real valued distributions ` +in the class will use the new method:: >>> fr = FullRandom() >>> fr.random() @@ -561,27 +580,14 @@ All of the real valued distributions will use the new method:: >>> fr.expovariate(0.25) 8.87925541791544 +The recipe is conceptually equivalent to an algorithm that chooses from +all the multiples of 2????? in the range *0.0 ? x < 1.0*. All such +numbers are evenly spaced, but most have to be rounded down to the +nearest representable Python float. (The value 2????? is the smallest +positive unnormalized float and is equal to ``math.ulp(0.0)``.) -.. seealso:: - - `Statistics for Hackers `_ - a video tutorial by - `Jake Vanderplas `_ - on statistical analysis using just a few fundamental concepts - including simulation, sampling, shuffling, and cross-validation. - - `Economics Simulation - `_ - a simulation of a marketplace by - `Peter Norvig `_ that shows effective - use of many of the tools and distributions provided by this module - (gauss, uniform, sample, betavariate, choice, triangular, and randrange). - `A Concrete Introduction to Probability (using Python) - `_ - a tutorial by `Peter Norvig `_ covering - the basics of probability theory, how to write simulations, and - how to perform data analysis using Python. +.. seealso:: `Generating Pseudo-random Floating-Point Values `_ a From webhook-mailer at python.org Tue Oct 13 20:19:18 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Wed, 14 Oct 2020 00:19:18 -0000 Subject: [Python-checkins] Improve recipe readability (GH-22685) (GH-22686) Message-ID: https://github.com/python/cpython/commit/270a2fbc55f14cf6334b67a7903050ed2e88c1b3 commit: 270a2fbc55f14cf6334b67a7903050ed2e88c1b3 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-13T17:19:05-07:00 summary: Improve recipe readability (GH-22685) (GH-22686) files: M Doc/library/random.rst diff --git a/Doc/library/random.rst b/Doc/library/random.rst index c43307ca4be31..d37fd9cbd8e17 100644 --- a/Doc/library/random.rst +++ b/Doc/library/random.rst @@ -253,6 +253,8 @@ Functions for sequences order so that the sample is reproducible. +.. _real-valued-distributions: + Real-valued distributions ------------------------- @@ -516,26 +518,42 @@ Simulation of arrival times and service deliveries for a multiserver queue:: print(f'Mean wait: {mean(waits):.1f}. Stdev wait: {stdev(waits):.1f}.') print(f'Median wait: {median(waits):.1f}. Max wait: {max(waits):.1f}.') +.. seealso:: + + `Statistics for Hackers `_ + a video tutorial by + `Jake Vanderplas `_ + on statistical analysis using just a few fundamental concepts + including simulation, sampling, shuffling, and cross-validation. + + `Economics Simulation + `_ + a simulation of a marketplace by + `Peter Norvig `_ that shows effective + use of many of the tools and distributions provided by this module + (gauss, uniform, sample, betavariate, choice, triangular, and randrange). + + `A Concrete Introduction to Probability (using Python) + `_ + a tutorial by `Peter Norvig `_ covering + the basics of probability theory, how to write simulations, and + how to perform data analysis using Python. + + Recipes ------- The default :func:`.random` returns multiples of 2??? in the range -*0.0 ? x < 1.0*. All such numbers are evenly spaced and exactly +*0.0 ? x < 1.0*. All such numbers are evenly spaced and are exactly representable as Python floats. However, many floats in that interval are not possible selections. For example, ``0.05954861408025609`` isn't an integer multiple of 2???. The following recipe takes a different approach. All floats in the -interval are possible selections. Conceptually it works by choosing -from evenly spaced multiples of 2????? and then rounding down to the -nearest representable float. - -For efficiency, the actual mechanics involve calling -:func:`~math.ldexp` to construct a representable float. The mantissa -comes from a uniform distribution of integers in the range *2?? ? -mantissa < 2??*. The exponent comes from a geometric distribution -where exponents smaller than *-53* occur half as often as the next -larger exponent. +interval are possible selections. The mantissa comes from a uniform +distribution of integers in the range *2?? ? mantissa < 2??*. The +exponent comes from a geometric distribution where exponents smaller +than *-53* occur half as often as the next larger exponent. :: @@ -553,7 +571,8 @@ larger exponent. exponent += x.bit_length() - 32 return ldexp(mantissa, exponent) -All of the real valued distributions will use the new method:: +All :ref:`real valued distributions ` +in the class will use the new method:: >>> fr = FullRandom() >>> fr.random() @@ -561,27 +580,14 @@ All of the real valued distributions will use the new method:: >>> fr.expovariate(0.25) 8.87925541791544 +The recipe is conceptually equivalent to an algorithm that chooses from +all the multiples of 2????? in the range *0.0 ? x < 1.0*. All such +numbers are evenly spaced, but most have to be rounded down to the +nearest representable Python float. (The value 2????? is the smallest +positive unnormalized float and is equal to ``math.ulp(0.0)``.) -.. seealso:: - - `Statistics for Hackers `_ - a video tutorial by - `Jake Vanderplas `_ - on statistical analysis using just a few fundamental concepts - including simulation, sampling, shuffling, and cross-validation. - - `Economics Simulation - `_ - a simulation of a marketplace by - `Peter Norvig `_ that shows effective - use of many of the tools and distributions provided by this module - (gauss, uniform, sample, betavariate, choice, triangular, and randrange). - `A Concrete Introduction to Probability (using Python) - `_ - a tutorial by `Peter Norvig `_ covering - the basics of probability theory, how to write simulations, and - how to perform data analysis using Python. +.. seealso:: `Generating Pseudo-random Floating-Point Values `_ a From webhook-mailer at python.org Tue Oct 13 21:39:04 2020 From: webhook-mailer at python.org (Ned Deily) Date: Wed, 14 Oct 2020 01:39:04 -0000 Subject: [Python-checkins] bpo-41939: always enable test_site.test_license_exists_at_url (GH-22688) Message-ID: https://github.com/python/cpython/commit/6a48518e8dac3521ff387ee67cdf33783114a257 commit: 6a48518e8dac3521ff387ee67cdf33783114a257 branch: master author: Ned Deily committer: GitHub date: 2020-10-13T21:38:56-04:00 summary: bpo-41939: always enable test_site.test_license_exists_at_url (GH-22688) files: M Lib/test/test_site.py diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py index a475ed1ab4c46..2e70880f56d14 100644 --- a/Lib/test/test_site.py +++ b/Lib/test/test_site.py @@ -516,8 +516,6 @@ def test_sitecustomize_executed(self): @test.support.requires_resource('network') @test.support.system_must_validate_cert - @unittest.skipUnless(sys.version_info[3] == 'final', - 'only for released versions') @unittest.skipUnless(hasattr(urllib.request, "HTTPSHandler"), 'need SSL support to download license') def test_license_exists_at_url(self): From webhook-mailer at python.org Tue Oct 13 21:49:33 2020 From: webhook-mailer at python.org (Kevin Adler) Date: Wed, 14 Oct 2020 01:49:33 -0000 Subject: [Python-checkins] closes bpo-42029: Remove dynload_dl (GH-22687) Message-ID: https://github.com/python/cpython/commit/0cafcd3c56c9475913d8d4fd0223c297dbb70ac6 commit: 0cafcd3c56c9475913d8d4fd0223c297dbb70ac6 branch: master author: Kevin Adler committer: GitHub date: 2020-10-13T20:49:24-05:00 summary: closes bpo-42029: Remove dynload_dl (GH-22687) All references to this dynamic loading method were removed in b9949db, when support for this method was dropped, but the implementation code was not dropped (seemingly in oversight). files: D Python/dynload_dl.c diff --git a/Python/dynload_dl.c b/Python/dynload_dl.c deleted file mode 100644 index 2bec645fbd7af..0000000000000 --- a/Python/dynload_dl.c +++ /dev/null @@ -1,23 +0,0 @@ - -/* Support for dynamic loading of extension modules */ - -#include "dl.h" - -#include "Python.h" -#include "importdl.h" - - -extern char *Py_GetProgramName(void); - -const char *_PyImport_DynLoadFiletab[] = {".o", NULL}; - - -dl_funcptr _PyImport_FindSharedFuncptr(const char *prefix, - const char *shortname, - const char *pathname, FILE *fp) -{ - char funcname[258]; - - PyOS_snprintf(funcname, sizeof(funcname), "%.20s_%.200s", prefix, shortname); - return dl_loadmod(Py_GetProgramName(), pathname, funcname); -} From webhook-mailer at python.org Tue Oct 13 21:59:21 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Wed, 14 Oct 2020 01:59:21 -0000 Subject: [Python-checkins] bpo-41939: always enable test_site.test_license_exists_at_url (GH-22688) Message-ID: https://github.com/python/cpython/commit/8b4642d3288e187faad24283c949ecf53fecad5b commit: 8b4642d3288e187faad24283c949ecf53fecad5b branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-13T18:59:12-07:00 summary: bpo-41939: always enable test_site.test_license_exists_at_url (GH-22688) (cherry picked from commit 6a48518e8dac3521ff387ee67cdf33783114a257) Co-authored-by: Ned Deily files: M Lib/test/test_site.py diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py index b95c108771d8c..6b3dba0f0a290 100644 --- a/Lib/test/test_site.py +++ b/Lib/test/test_site.py @@ -492,8 +492,6 @@ def test_sitecustomize_executed(self): @test.support.requires_resource('network') @test.support.system_must_validate_cert - @unittest.skipUnless(sys.version_info[3] == 'final', - 'only for released versions') @unittest.skipUnless(hasattr(urllib.request, "HTTPSHandler"), 'need SSL support to download license') def test_license_exists_at_url(self): From webhook-mailer at python.org Tue Oct 13 21:59:51 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Wed, 14 Oct 2020 01:59:51 -0000 Subject: [Python-checkins] bpo-41939: always enable test_site.test_license_exists_at_url (GH-22688) Message-ID: https://github.com/python/cpython/commit/881a13cad534cf3fe4474579c22235a15d3ba27b commit: 881a13cad534cf3fe4474579c22235a15d3ba27b branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-13T18:59:41-07:00 summary: bpo-41939: always enable test_site.test_license_exists_at_url (GH-22688) (cherry picked from commit 6a48518e8dac3521ff387ee67cdf33783114a257) Co-authored-by: Ned Deily files: M Lib/test/test_site.py diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py index ece6a07280634..ffba139dbeade 100644 --- a/Lib/test/test_site.py +++ b/Lib/test/test_site.py @@ -500,8 +500,6 @@ def test_sitecustomize_executed(self): @test.support.requires_resource('network') @test.support.system_must_validate_cert - @unittest.skipUnless(sys.version_info[3] == 'final', - 'only for released versions') @unittest.skipUnless(hasattr(urllib.request, "HTTPSHandler"), 'need SSL support to download license') def test_license_exists_at_url(self): From webhook-mailer at python.org Wed Oct 14 05:10:19 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Wed, 14 Oct 2020 09:10:19 -0000 Subject: [Python-checkins] bpo-41993: Fix possible issues in remove_module() (GH-22631) (GH-22647) Message-ID: https://github.com/python/cpython/commit/391a544f2a52673f6630c672e89840fd6ac36723 commit: 391a544f2a52673f6630c672e89840fd6ac36723 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-14T12:09:44+03:00 summary: bpo-41993: Fix possible issues in remove_module() (GH-22631) (GH-22647) * PyMapping_HasKey() is not safe because it silences all exceptions and can return incorrect result. * Informative exceptions from PyMapping_DelItem() are overridden with RuntimeError and the original exception raised before calling remove_module() is lost. * There is a race condition between PyMapping_HasKey() and PyMapping_DelItem(). (cherry picked from commit 8287aadb75f6bd0154996424819334cd3839707c) Co-authored-by: Serhiy Storchaka files: A Misc/NEWS.d/next/Core and Builtins/2020-10-10-13-53-52.bpo-41993.YMzixQ.rst M Python/import.c diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-10-10-13-53-52.bpo-41993.YMzixQ.rst b/Misc/NEWS.d/next/Core and Builtins/2020-10-10-13-53-52.bpo-41993.YMzixQ.rst new file mode 100644 index 0000000000000..3669cf11ea4cd --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2020-10-10-13-53-52.bpo-41993.YMzixQ.rst @@ -0,0 +1,2 @@ +Fixed potential issues with removing not completely initialized module from +``sys.modules`` when import fails. diff --git a/Python/import.c b/Python/import.c index 0e2e7c370868f..5e39a2fb9a02e 100644 --- a/Python/import.c +++ b/Python/import.c @@ -905,7 +905,11 @@ PyImport_AddModule(const char *name) } -/* Remove name from sys.modules, if it's there. */ +/* Remove name from sys.modules, if it's there. + * Can be called with an exception raised. + * If fail to remove name a new exception will be chained with the old + * exception, otherwise the old exception is preserved. + */ static void remove_module(PyThreadState *tstate, PyObject *name) { @@ -913,18 +917,17 @@ remove_module(PyThreadState *tstate, PyObject *name) _PyErr_Fetch(tstate, &type, &value, &traceback); PyObject *modules = tstate->interp->modules; - if (!PyMapping_HasKey(modules, name)) { - goto out; + if (PyDict_CheckExact(modules)) { + PyObject *mod = _PyDict_Pop(modules, name, Py_None); + Py_XDECREF(mod); } - if (PyMapping_DelItem(modules, name) < 0) { - _PyErr_SetString(tstate, PyExc_RuntimeError, - "deleting key in sys.modules failed"); - _PyErr_ChainExceptions(type, value, traceback); - return; + else if (PyMapping_DelItem(modules, name) < 0) { + if (_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) { + _PyErr_Clear(tstate); + } } -out: - _PyErr_Restore(tstate, type, value, traceback); + _PyErr_ChainExceptions(type, value, traceback); } From webhook-mailer at python.org Wed Oct 14 06:02:59 2020 From: webhook-mailer at python.org (Anatoliy Platonov) Date: Wed, 14 Oct 2020 10:02:59 -0000 Subject: [Python-checkins] bpo-41876: Overload __repr__ for tkinter Font objects (GH-22450) Message-ID: https://github.com/python/cpython/commit/b4d895336a4692c95b4533adcc5c63a489e5e4e4 commit: b4d895336a4692c95b4533adcc5c63a489e5e4e4 branch: master author: Anatoliy Platonov <31926941+p4m-dev at users.noreply.github.com> committer: GitHub date: 2020-10-14T13:02:51+03:00 summary: bpo-41876: Overload __repr__ for tkinter Font objects (GH-22450) files: A Misc/NEWS.d/next/Library/2020-09-29-16-23-54.bpo-41876.QicdDU.rst M Lib/tkinter/font.py M Lib/tkinter/test/test_tkinter/test_font.py M Misc/ACKS diff --git a/Lib/tkinter/font.py b/Lib/tkinter/font.py index 15ad7ab4b63a8..a9f79d8e456bb 100644 --- a/Lib/tkinter/font.py +++ b/Lib/tkinter/font.py @@ -100,6 +100,10 @@ def __init__(self, root=None, font=None, name=None, exists=False, def __str__(self): return self.name + def __repr__(self): + return f"<{self.__class__.__module__}.{self.__class__.__qualname__}" \ + f" object {self.name!r}>" + def __eq__(self, other): if not isinstance(other, Font): return NotImplemented diff --git a/Lib/tkinter/test/test_tkinter/test_font.py b/Lib/tkinter/test/test_tkinter/test_font.py index a021ea336807b..6d1eea44b4d2f 100644 --- a/Lib/tkinter/test/test_tkinter/test_font.py +++ b/Lib/tkinter/test/test_tkinter/test_font.py @@ -101,6 +101,12 @@ def test_names(self): self.assertTrue(name) self.assertIn(fontname, names) + def test_repr(self): + self.assertEqual( + repr(self.font), f'' + ) + + tests_gui = (FontTest, ) if __name__ == "__main__": diff --git a/Misc/ACKS b/Misc/ACKS index 660b8ef7504ee..7f4a9bcbc0f8c 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1353,6 +1353,7 @@ Zero Piraeus Antoine Pitrou Jean-Fran?ois Pi?ronne Oleg Plakhotnyuk +Anatoliy Platonov Marcel Plch Remi Pointel Jon Poler diff --git a/Misc/NEWS.d/next/Library/2020-09-29-16-23-54.bpo-41876.QicdDU.rst b/Misc/NEWS.d/next/Library/2020-09-29-16-23-54.bpo-41876.QicdDU.rst new file mode 100644 index 0000000000000..d4f5f0a37bf48 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-09-29-16-23-54.bpo-41876.QicdDU.rst @@ -0,0 +1 @@ +Tkinter font class repr uses font name \ No newline at end of file From webhook-mailer at python.org Wed Oct 14 11:43:39 2020 From: webhook-mailer at python.org (Hai Shi) Date: Wed, 14 Oct 2020 15:43:39 -0000 Subject: [Python-checkins] bpo-39337: encodings.normalize_encoding() now ignores non-ASCII characters (GH-22219) Message-ID: https://github.com/python/cpython/commit/c5b049b91ca50c615f9a5425055c2b79a82ac547 commit: c5b049b91ca50c615f9a5425055c2b79a82ac547 branch: master author: Hai Shi committer: GitHub date: 2020-10-14T17:43:31+02:00 summary: bpo-39337: encodings.normalize_encoding() now ignores non-ASCII characters (GH-22219) files: A Misc/NEWS.d/next/Library/2020-09-13-02-02-18.bpo-39337.L3NXTt.rst M Doc/whatsnew/3.10.rst M Lib/encodings/__init__.py M Lib/test/test_codecs.py diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index c8ddcd2d24296..738ef974e7867 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -186,6 +186,11 @@ by :func:`curses.color_content`, :func:`curses.init_color`, support is provided by the underlying ncurses library. (Contributed by Jeffrey Kintscher and Hans Petter Jansson in :issue:`36982`.) +encodings +--------- +:func:`encodings.normalize_encoding` now ignores non-ASCII characters. +(Contributed by Hai Shi in :issue:`39337`.) + glob ---- diff --git a/Lib/encodings/__init__.py b/Lib/encodings/__init__.py index ddd5afdcf2dab..4b37d3321c903 100644 --- a/Lib/encodings/__init__.py +++ b/Lib/encodings/__init__.py @@ -61,7 +61,8 @@ def normalize_encoding(encoding): if c.isalnum() or c == '.': if punct and chars: chars.append('_') - chars.append(c) + if c.isascii(): + chars.append(c) punct = False else: punct = True diff --git a/Lib/test/test_codecs.py b/Lib/test/test_codecs.py index ddf4e08af6247..09ceef76eb098 100644 --- a/Lib/test/test_codecs.py +++ b/Lib/test/test_codecs.py @@ -3417,7 +3417,7 @@ def test_rot13_func(self): class CodecNameNormalizationTest(unittest.TestCase): """Test codec name normalization""" - def test_normalized_encoding(self): + def test_codecs_lookup(self): FOUND = (1, 2, 3, 4) NOT_FOUND = (None, None, None, None) def search_function(encoding): @@ -3439,6 +3439,18 @@ def search_function(encoding): self.assertEqual(NOT_FOUND, codecs.lookup('BBB.8')) self.assertEqual(NOT_FOUND, codecs.lookup('a\xe9\u20ac-8')) + def test_encodings_normalize_encoding(self): + # encodings.normalize_encoding() ignores non-ASCII characters. + normalize = encodings.normalize_encoding + self.assertEqual(normalize('utf_8'), 'utf_8') + self.assertEqual(normalize('utf\xE9\u20AC\U0010ffff-8'), 'utf_8') + self.assertEqual(normalize('utf 8'), 'utf_8') + # encodings.normalize_encoding() doesn't convert + # characters to lower case. + self.assertEqual(normalize('UTF 8'), 'UTF_8') + self.assertEqual(normalize('utf.8'), 'utf.8') + self.assertEqual(normalize('utf...8'), 'utf...8') + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Library/2020-09-13-02-02-18.bpo-39337.L3NXTt.rst b/Misc/NEWS.d/next/Library/2020-09-13-02-02-18.bpo-39337.L3NXTt.rst new file mode 100644 index 0000000000000..c2b4dbe4d12e8 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-09-13-02-02-18.bpo-39337.L3NXTt.rst @@ -0,0 +1 @@ +:func:`encodings.normalize_encoding` now ignores non-ASCII characters. From webhook-mailer at python.org Wed Oct 14 13:04:12 2020 From: webhook-mailer at python.org (Raymond Hettinger) Date: Wed, 14 Oct 2020 17:04:12 -0000 Subject: [Python-checkins] Update timings for the final release (GH-22697) Message-ID: https://github.com/python/cpython/commit/302b6166fbb15c51f58b040c62e987d486742189 commit: 302b6166fbb15c51f58b040c62e987d486742189 branch: master author: Raymond Hettinger committer: GitHub date: 2020-10-14T10:04:04-07:00 summary: Update timings for the final release (GH-22697) files: M Doc/whatsnew/3.9.rst diff --git a/Doc/whatsnew/3.9.rst b/Doc/whatsnew/3.9.rst index 95188b7493ad4..22fdbd565a166 100644 --- a/Doc/whatsnew/3.9.rst +++ b/Doc/whatsnew/3.9.rst @@ -652,41 +652,41 @@ Here's a summary of performance improvements from Python 3.4 through Python 3.9: -------------- --- --- --- --- --- --- Variable and attribute read access: - read_local 7.1 7.1 5.4 5.1 3.9 4.0 - read_nonlocal 7.1 8.1 5.8 5.4 4.4 4.8 - read_global 15.5 19.0 14.3 13.6 7.6 7.7 - read_builtin 21.1 21.6 18.5 19.0 7.5 7.7 - read_classvar_from_class 25.6 26.5 20.7 19.5 18.4 18.6 - read_classvar_from_instance 22.8 23.5 18.8 17.1 16.4 20.1 - read_instancevar 32.4 33.1 28.0 26.3 25.4 27.7 - read_instancevar_slots 27.8 31.3 20.8 20.8 20.2 24.5 - read_namedtuple 73.8 57.5 45.0 46.8 18.4 23.2 - read_boundmethod 37.6 37.9 29.6 26.9 27.7 45.9 + read_local 7.1 7.1 5.4 5.1 3.9 3.9 + read_nonlocal 7.1 8.1 5.8 5.4 4.4 4.5 + read_global 15.5 19.0 14.3 13.6 7.6 7.8 + read_builtin 21.1 21.6 18.5 19.0 7.5 7.8 + read_classvar_from_class 25.6 26.5 20.7 19.5 18.4 17.9 + read_classvar_from_instance 22.8 23.5 18.8 17.1 16.4 16.9 + read_instancevar 32.4 33.1 28.0 26.3 25.4 25.3 + read_instancevar_slots 27.8 31.3 20.8 20.8 20.2 20.5 + read_namedtuple 73.8 57.5 45.0 46.8 18.4 18.7 + read_boundmethod 37.6 37.9 29.6 26.9 27.7 41.1 Variable and attribute write access: - write_local 8.7 9.3 5.5 5.3 4.3 4.2 - write_nonlocal 10.5 11.1 5.6 5.5 4.7 4.9 - write_global 19.7 21.2 18.0 18.0 15.8 17.2 - write_classvar 92.9 96.0 104.6 102.1 39.2 43.2 - write_instancevar 44.6 45.8 40.0 38.9 35.5 40.7 - write_instancevar_slots 35.6 36.1 27.3 26.6 25.7 27.7 + write_local 8.7 9.3 5.5 5.3 4.3 4.3 + write_nonlocal 10.5 11.1 5.6 5.5 4.7 4.8 + write_global 19.7 21.2 18.0 18.0 15.8 16.7 + write_classvar 92.9 96.0 104.6 102.1 39.2 39.8 + write_instancevar 44.6 45.8 40.0 38.9 35.5 37.4 + write_instancevar_slots 35.6 36.1 27.3 26.6 25.7 25.8 Data structure read access: - read_list 24.2 24.5 20.8 20.8 19.0 21.1 - read_deque 24.7 25.5 20.2 20.6 19.8 21.6 - read_dict 24.3 25.7 22.3 23.0 21.0 22.5 - read_strdict 22.6 24.3 19.5 21.2 18.9 21.6 + read_list 24.2 24.5 20.8 20.8 19.0 19.5 + read_deque 24.7 25.5 20.2 20.6 19.8 20.2 + read_dict 24.3 25.7 22.3 23.0 21.0 22.4 + read_strdict 22.6 24.3 19.5 21.2 18.9 21.5 Data structure write access: - write_list 27.1 28.5 22.5 21.6 20.0 21.6 - write_deque 28.7 30.1 22.7 21.8 23.5 23.2 - write_dict 31.4 33.3 29.3 29.2 24.7 27.8 - write_strdict 28.4 29.9 27.5 25.2 23.1 29.8 + write_list 27.1 28.5 22.5 21.6 20.0 20.0 + write_deque 28.7 30.1 22.7 21.8 23.5 21.7 + write_dict 31.4 33.3 29.3 29.2 24.7 25.4 + write_strdict 28.4 29.9 27.5 25.2 23.1 24.5 Stack (or queue) operations: - list_append_pop 93.4 112.7 75.4 74.2 50.8 53.9 - deque_append_pop 43.5 57.0 49.4 49.2 42.5 45.5 - deque_append_popleft 43.7 57.3 49.7 49.7 42.8 45.5 + list_append_pop 93.4 112.7 75.4 74.2 50.8 50.6 + deque_append_pop 43.5 57.0 49.4 49.2 42.5 44.2 + deque_append_popleft 43.7 57.3 49.7 49.7 42.8 46.4 Timing loop: loop_overhead 0.5 0.6 0.4 0.3 0.3 0.3 From webhook-mailer at python.org Wed Oct 14 13:29:20 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Wed, 14 Oct 2020 17:29:20 -0000 Subject: [Python-checkins] Update timings for the final release (GH-22697) (GH-22698) Message-ID: https://github.com/python/cpython/commit/69f040c0009dc4ca72f487446e74fdb2f91c95dc commit: 69f040c0009dc4ca72f487446e74fdb2f91c95dc branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-14T10:29:10-07:00 summary: Update timings for the final release (GH-22697) (GH-22698) files: M Doc/whatsnew/3.9.rst diff --git a/Doc/whatsnew/3.9.rst b/Doc/whatsnew/3.9.rst index 9a09e710a3d2e..a91494a918061 100644 --- a/Doc/whatsnew/3.9.rst +++ b/Doc/whatsnew/3.9.rst @@ -781,41 +781,41 @@ Here's a summary of performance improvements from Python 3.4 through Python 3.9: -------------- --- --- --- --- --- --- Variable and attribute read access: - read_local 7.1 7.1 5.4 5.1 3.9 4.0 - read_nonlocal 7.1 8.1 5.8 5.4 4.4 4.8 - read_global 15.5 19.0 14.3 13.6 7.6 7.7 - read_builtin 21.1 21.6 18.5 19.0 7.5 7.7 - read_classvar_from_class 25.6 26.5 20.7 19.5 18.4 18.6 - read_classvar_from_instance 22.8 23.5 18.8 17.1 16.4 20.1 - read_instancevar 32.4 33.1 28.0 26.3 25.4 27.7 - read_instancevar_slots 27.8 31.3 20.8 20.8 20.2 24.5 - read_namedtuple 73.8 57.5 45.0 46.8 18.4 23.2 - read_boundmethod 37.6 37.9 29.6 26.9 27.7 45.9 + read_local 7.1 7.1 5.4 5.1 3.9 3.9 + read_nonlocal 7.1 8.1 5.8 5.4 4.4 4.5 + read_global 15.5 19.0 14.3 13.6 7.6 7.8 + read_builtin 21.1 21.6 18.5 19.0 7.5 7.8 + read_classvar_from_class 25.6 26.5 20.7 19.5 18.4 17.9 + read_classvar_from_instance 22.8 23.5 18.8 17.1 16.4 16.9 + read_instancevar 32.4 33.1 28.0 26.3 25.4 25.3 + read_instancevar_slots 27.8 31.3 20.8 20.8 20.2 20.5 + read_namedtuple 73.8 57.5 45.0 46.8 18.4 18.7 + read_boundmethod 37.6 37.9 29.6 26.9 27.7 41.1 Variable and attribute write access: - write_local 8.7 9.3 5.5 5.3 4.3 4.2 - write_nonlocal 10.5 11.1 5.6 5.5 4.7 4.9 - write_global 19.7 21.2 18.0 18.0 15.8 17.2 - write_classvar 92.9 96.0 104.6 102.1 39.2 43.2 - write_instancevar 44.6 45.8 40.0 38.9 35.5 40.7 - write_instancevar_slots 35.6 36.1 27.3 26.6 25.7 27.7 + write_local 8.7 9.3 5.5 5.3 4.3 4.3 + write_nonlocal 10.5 11.1 5.6 5.5 4.7 4.8 + write_global 19.7 21.2 18.0 18.0 15.8 16.7 + write_classvar 92.9 96.0 104.6 102.1 39.2 39.8 + write_instancevar 44.6 45.8 40.0 38.9 35.5 37.4 + write_instancevar_slots 35.6 36.1 27.3 26.6 25.7 25.8 Data structure read access: - read_list 24.2 24.5 20.8 20.8 19.0 21.1 - read_deque 24.7 25.5 20.2 20.6 19.8 21.6 - read_dict 24.3 25.7 22.3 23.0 21.0 22.5 - read_strdict 22.6 24.3 19.5 21.2 18.9 21.6 + read_list 24.2 24.5 20.8 20.8 19.0 19.5 + read_deque 24.7 25.5 20.2 20.6 19.8 20.2 + read_dict 24.3 25.7 22.3 23.0 21.0 22.4 + read_strdict 22.6 24.3 19.5 21.2 18.9 21.5 Data structure write access: - write_list 27.1 28.5 22.5 21.6 20.0 21.6 - write_deque 28.7 30.1 22.7 21.8 23.5 23.2 - write_dict 31.4 33.3 29.3 29.2 24.7 27.8 - write_strdict 28.4 29.9 27.5 25.2 23.1 29.8 + write_list 27.1 28.5 22.5 21.6 20.0 20.0 + write_deque 28.7 30.1 22.7 21.8 23.5 21.7 + write_dict 31.4 33.3 29.3 29.2 24.7 25.4 + write_strdict 28.4 29.9 27.5 25.2 23.1 24.5 Stack (or queue) operations: - list_append_pop 93.4 112.7 75.4 74.2 50.8 53.9 - deque_append_pop 43.5 57.0 49.4 49.2 42.5 45.5 - deque_append_popleft 43.7 57.3 49.7 49.7 42.8 45.5 + list_append_pop 93.4 112.7 75.4 74.2 50.8 50.6 + deque_append_pop 43.5 57.0 49.4 49.2 42.5 44.2 + deque_append_popleft 43.7 57.3 49.7 49.7 42.8 46.4 Timing loop: loop_overhead 0.5 0.6 0.4 0.3 0.3 0.3 From webhook-mailer at python.org Wed Oct 14 21:44:16 2020 From: webhook-mailer at python.org (Brandt Bucher) Date: Thu, 15 Oct 2020 01:44:16 -0000 Subject: [Python-checkins] bpo-41984: GC track all user classes (GH-22701) Message-ID: https://github.com/python/cpython/commit/c13b847a6f913b72eeb71651ff626390b738d973 commit: c13b847a6f913b72eeb71651ff626390b738d973 branch: master author: Brandt Bucher committer: GitHub date: 2020-10-14T18:44:07-07:00 summary: bpo-41984: GC track all user classes (GH-22701) files: A Misc/NEWS.d/next/Core and Builtins/2020-10-14-16-19-43.bpo-41984.SEtKMr.rst M Lib/test/test_finalization.py M Lib/test/test_gc.py M Modules/_testcapimodule.c M Objects/typeobject.c diff --git a/Lib/test/test_finalization.py b/Lib/test/test_finalization.py index 35d7913e5b89b..1d134430909d8 100644 --- a/Lib/test/test_finalization.py +++ b/Lib/test/test_finalization.py @@ -16,6 +16,15 @@ def __new__(cls, *args, **kwargs): raise TypeError('requires _testcapi.with_tp_del') return C +try: + from _testcapi import without_gc +except ImportError: + def without_gc(cls): + class C: + def __new__(cls, *args, **kwargs): + raise TypeError('requires _testcapi.without_gc') + return C + from test import support @@ -94,9 +103,11 @@ def check_sanity(self): assert self.id_ == id(self) + at without_gc class NonGC(NonGCSimpleBase): __slots__ = () + at without_gc class NonGCResurrector(NonGCSimpleBase): __slots__ = () @@ -109,8 +120,14 @@ def side_effect(self): class Simple(SimpleBase): pass -class SimpleResurrector(NonGCResurrector, SimpleBase): - pass +# Can't inherit from NonGCResurrector, in case importing without_gc fails. +class SimpleResurrector(SimpleBase): + + def side_effect(self): + """ + Resurrect self by storing self in a class-wide list. + """ + self.survivors.append(self) class TestBase: @@ -178,6 +195,7 @@ def test_simple_resurrect(self): self.assert_survivors([]) self.assertIs(wr(), None) + @support.cpython_only def test_non_gc(self): with SimpleBase.test(): s = NonGC() @@ -191,6 +209,7 @@ def test_non_gc(self): self.assert_del_calls(ids) self.assert_survivors([]) + @support.cpython_only def test_non_gc_resurrect(self): with SimpleBase.test(): s = NonGCResurrector() diff --git a/Lib/test/test_gc.py b/Lib/test/test_gc.py index 1b096efdbcf5f..ba66737015906 100644 --- a/Lib/test/test_gc.py +++ b/Lib/test/test_gc.py @@ -582,9 +582,9 @@ class UserIntSlots(int): self.assertTrue(gc.is_tracked(UserInt())) self.assertTrue(gc.is_tracked([])) self.assertTrue(gc.is_tracked(set())) - self.assertFalse(gc.is_tracked(UserClassSlots())) - self.assertFalse(gc.is_tracked(UserFloatSlots())) - self.assertFalse(gc.is_tracked(UserIntSlots())) + self.assertTrue(gc.is_tracked(UserClassSlots())) + self.assertTrue(gc.is_tracked(UserFloatSlots())) + self.assertTrue(gc.is_tracked(UserIntSlots())) def test_is_finalized(self): # Objects not tracked by the always gc return false diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-10-14-16-19-43.bpo-41984.SEtKMr.rst b/Misc/NEWS.d/next/Core and Builtins/2020-10-14-16-19-43.bpo-41984.SEtKMr.rst new file mode 100644 index 0000000000000..e70d5dc2b8dde --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2020-10-14-16-19-43.bpo-41984.SEtKMr.rst @@ -0,0 +1,2 @@ +The garbage collector now tracks all user-defined classes. Patch by Brandt +Bucher. diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 8c7544fa90e28..28d2c124d5177 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -3888,6 +3888,25 @@ with_tp_del(PyObject *self, PyObject *args) return obj; } +static PyObject * +without_gc(PyObject *Py_UNUSED(self), PyObject *obj) +{ + PyTypeObject *tp = (PyTypeObject*)obj; + if (!PyType_Check(obj) || !PyType_HasFeature(tp, Py_TPFLAGS_HEAPTYPE)) { + return PyErr_Format(PyExc_TypeError, "heap type expected, got %R", obj); + } + if (PyType_IS_GC(tp)) { + // Don't try this at home, kids: + tp->tp_flags -= Py_TPFLAGS_HAVE_GC; + tp->tp_free = PyObject_Del; + tp->tp_traverse = NULL; + tp->tp_clear = NULL; + } + assert(!PyType_IS_GC(tp)); + Py_INCREF(obj); + return obj; +} + static PyMethodDef ml; static PyObject * @@ -5805,6 +5824,7 @@ static PyMethodDef TestMethods[] = { {"meth_fastcall", (PyCFunction)(void(*)(void))meth_fastcall, METH_FASTCALL}, {"meth_fastcall_keywords", (PyCFunction)(void(*)(void))meth_fastcall_keywords, METH_FASTCALL|METH_KEYWORDS}, {"pynumber_tobase", pynumber_tobase, METH_VARARGS}, + {"without_gc", without_gc, METH_O}, {NULL, NULL} /* sentinel */ }; diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 3bb2c338fe0b5..36c7662e081a4 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -2612,10 +2612,10 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) slots = NULL; /* Initialize tp_flags */ + // All heap types need GC, since we can create a reference cycle by storing + // an instance on one of its parents: type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HEAPTYPE | - Py_TPFLAGS_BASETYPE; - if (base->tp_flags & Py_TPFLAGS_HAVE_GC) - type->tp_flags |= Py_TPFLAGS_HAVE_GC; + Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC; /* Initialize essential fields */ type->tp_as_async = &et->as_async; @@ -2815,21 +2815,11 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) } type->tp_dealloc = subtype_dealloc; - /* Enable GC unless this class is not adding new instance variables and - the base class did not use GC. */ - if ((base->tp_flags & Py_TPFLAGS_HAVE_GC) || - type->tp_basicsize > base->tp_basicsize) - type->tp_flags |= Py_TPFLAGS_HAVE_GC; - /* Always override allocation strategy to use regular heap */ type->tp_alloc = PyType_GenericAlloc; - if (type->tp_flags & Py_TPFLAGS_HAVE_GC) { - type->tp_free = PyObject_GC_Del; - type->tp_traverse = subtype_traverse; - type->tp_clear = subtype_clear; - } - else - type->tp_free = PyObject_Del; + type->tp_free = PyObject_GC_Del; + type->tp_traverse = subtype_traverse; + type->tp_clear = subtype_clear; /* store type in class' cell if one is supplied */ cell = _PyDict_GetItemIdWithError(dict, &PyId___classcell__); From webhook-mailer at python.org Wed Oct 14 21:53:35 2020 From: webhook-mailer at python.org (Kevin Adler) Date: Thu, 15 Oct 2020 01:53:35 -0000 Subject: [Python-checkins] bpo-41894: Fix UnicodeDecodeError while loading native module (GH-22466) Message-ID: https://github.com/python/cpython/commit/2d2af320d94afc6561e8f8adf174c9d3fd9065bc commit: 2d2af320d94afc6561e8f8adf174c9d3fd9065bc branch: master author: Kevin Adler committer: GitHub date: 2020-10-15T10:53:27+09:00 summary: bpo-41894: Fix UnicodeDecodeError while loading native module (GH-22466) When running in a non-UTF-8 locale, if an error occurs while importing a native Python module (say because a dependent share library is missing), the error message string returned may contain non-ASCII code points causing a UnicodeDecodeError. PyUnicode_DecodeFSDefault is used for buffers which may contain filesystem paths. For consistency with os.strerror(), PyUnicode_DecodeLocale is used for buffers which contain system error messages. While the shortname parameter is always encoded in ASCII according to PEP 489, it is left decoded using PyUnicode_FromString to minimize the changes and since it should not affect the decoding (albeit _potentially_ slower). In dynload_hpux, since the error buffer contains a message generated from a static ASCII string and the module filesystem path, PyUnicode_DecodeFSDefault is used instead of PyUnicode_DecodeLocale as is used elsewhere. * bpo-41894: Fix bugs in dynload error msg handling For both dynload_aix and dynload_hpux, properly handle the possibility that decoding strings may return NULL and when such an error happens, properly decrement any previously decoded strings and return early. In addition, in dynload_aix, ensure that we pass the decoded string *object* pathname_ob to PyErr_SetImportError instead of the original pathname buffer. Co-authored-by: Serhiy Storchaka files: A Misc/NEWS.d/next/Core and Builtins/2020-10-02-11-35-33.bpo-41894.ffmtOt.rst M Python/dynload_aix.c M Python/dynload_hpux.c M Python/dynload_shlib.c diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-10-02-11-35-33.bpo-41894.ffmtOt.rst b/Misc/NEWS.d/next/Core and Builtins/2020-10-02-11-35-33.bpo-41894.ffmtOt.rst new file mode 100644 index 0000000000000..571f5dae1a4a1 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2020-10-02-11-35-33.bpo-41894.ffmtOt.rst @@ -0,0 +1,3 @@ +When loading a native module and a load failure occurs, prevent a possible +UnicodeDecodeError when not running in a UTF-8 locale by decoding the load +error message using the current locale's encoding. diff --git a/Python/dynload_aix.c b/Python/dynload_aix.c index 684f10a8b9193..97f7698ef4b2d 100644 --- a/Python/dynload_aix.c +++ b/Python/dynload_aix.c @@ -144,10 +144,16 @@ aix_loaderror(const char *pathname) ERRBUF_APPEND(message[i]); ERRBUF_APPEND("\n"); } - errbuf[strlen(errbuf)-1] = '\0'; /* trim off last newline */ - pathname_ob = PyUnicode_FromString(pathname); - errbuf_ob = PyUnicode_FromString(errbuf); - PyErr_SetImportError(errbuf_ob, NULL, pathname); + /* Subtract 1 from the length to trim off trailing newline */ + errbuf_ob = PyUnicode_DecodeLocaleAndSize(errbuf, strlen(errbuf)-1, "surrogateescape"); + if (errbuf_ob == NULL) + return; + pathname_ob = PyUnicode_DecodeFSDefault(pathname); + if (pathname_ob == NULL) { + Py_DECREF(errbuf_ob); + return; + } + PyErr_SetImportError(errbuf_ob, NULL, pathname_ob); Py_DECREF(pathname_ob); Py_DECREF(errbuf_ob); return; diff --git a/Python/dynload_hpux.c b/Python/dynload_hpux.c index 4b964a69d3bde..e36d608c6dca4 100644 --- a/Python/dynload_hpux.c +++ b/Python/dynload_hpux.c @@ -36,9 +36,20 @@ dl_funcptr _PyImport_FindSharedFuncptr(const char *prefix, char buf[256]; PyOS_snprintf(buf, sizeof(buf), "Failed to load %.200s", pathname); - PyObject *buf_ob = PyUnicode_FromString(buf); + PyObject *buf_ob = PyUnicode_DecodeFSDefault(buf); + if (buf_ob == NULL) + return NULL; PyObject *shortname_ob = PyUnicode_FromString(shortname); - PyObject *pathname_ob = PyUnicode_FromString(pathname); + if (shortname_ob == NULL) { + Py_DECREF(buf_ob); + return NULL; + } + PyObject *pathname_ob = PyUnicode_DecodeFSDefault(pathname); + if (pathname_ob == NULL) { + Py_DECREF(buf_ob); + Py_DECREF(shortname_ob); + return NULL; + } PyErr_SetImportError(buf_ob, shortname_ob, pathname_ob); Py_DECREF(buf_ob); Py_DECREF(shortname_ob); diff --git a/Python/dynload_shlib.c b/Python/dynload_shlib.c index 082154dd91b1f..23828898d35a5 100644 --- a/Python/dynload_shlib.c +++ b/Python/dynload_shlib.c @@ -106,7 +106,7 @@ _PyImport_FindSharedFuncptr(const char *prefix, const char *error = dlerror(); if (error == NULL) error = "unknown dlopen() error"; - error_ob = PyUnicode_FromString(error); + error_ob = PyUnicode_DecodeLocale(error, "surrogateescape"); if (error_ob == NULL) return NULL; mod_name = PyUnicode_FromString(shortname); @@ -114,7 +114,7 @@ _PyImport_FindSharedFuncptr(const char *prefix, Py_DECREF(error_ob); return NULL; } - path = PyUnicode_FromString(pathname); + path = PyUnicode_DecodeFSDefault(pathname); if (path == NULL) { Py_DECREF(error_ob); Py_DECREF(mod_name); From webhook-mailer at python.org Wed Oct 14 22:11:16 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Thu, 15 Oct 2020 02:11:16 -0000 Subject: [Python-checkins] bpo-41894: Fix UnicodeDecodeError while loading native module (GH-22466) Message-ID: https://github.com/python/cpython/commit/47ca6799725bb4c40953bb26ebcd726d1d766361 commit: 47ca6799725bb4c40953bb26ebcd726d1d766361 branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-14T19:11:08-07:00 summary: bpo-41894: Fix UnicodeDecodeError while loading native module (GH-22466) When running in a non-UTF-8 locale, if an error occurs while importing a native Python module (say because a dependent share library is missing), the error message string returned may contain non-ASCII code points causing a UnicodeDecodeError. PyUnicode_DecodeFSDefault is used for buffers which may contain filesystem paths. For consistency with os.strerror(), PyUnicode_DecodeLocale is used for buffers which contain system error messages. While the shortname parameter is always encoded in ASCII according to PEP 489, it is left decoded using PyUnicode_FromString to minimize the changes and since it should not affect the decoding (albeit _potentially_ slower). In dynload_hpux, since the error buffer contains a message generated from a static ASCII string and the module filesystem path, PyUnicode_DecodeFSDefault is used instead of PyUnicode_DecodeLocale as is used elsewhere. * bpo-41894: Fix bugs in dynload error msg handling For both dynload_aix and dynload_hpux, properly handle the possibility that decoding strings may return NULL and when such an error happens, properly decrement any previously decoded strings and return early. In addition, in dynload_aix, ensure that we pass the decoded string *object* pathname_ob to PyErr_SetImportError instead of the original pathname buffer. Co-authored-by: Serhiy Storchaka (cherry picked from commit 2d2af320d94afc6561e8f8adf174c9d3fd9065bc) Co-authored-by: Kevin Adler files: A Misc/NEWS.d/next/Core and Builtins/2020-10-02-11-35-33.bpo-41894.ffmtOt.rst M Python/dynload_aix.c M Python/dynload_hpux.c M Python/dynload_shlib.c diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-10-02-11-35-33.bpo-41894.ffmtOt.rst b/Misc/NEWS.d/next/Core and Builtins/2020-10-02-11-35-33.bpo-41894.ffmtOt.rst new file mode 100644 index 0000000000000..571f5dae1a4a1 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2020-10-02-11-35-33.bpo-41894.ffmtOt.rst @@ -0,0 +1,3 @@ +When loading a native module and a load failure occurs, prevent a possible +UnicodeDecodeError when not running in a UTF-8 locale by decoding the load +error message using the current locale's encoding. diff --git a/Python/dynload_aix.c b/Python/dynload_aix.c index b3ff8e288c1a6..25fe19e601acb 100644 --- a/Python/dynload_aix.c +++ b/Python/dynload_aix.c @@ -144,10 +144,16 @@ aix_loaderror(const char *pathname) ERRBUF_APPEND(message[i]); ERRBUF_APPEND("\n"); } - errbuf[strlen(errbuf)-1] = '\0'; /* trim off last newline */ - pathname_ob = PyUnicode_FromString(pathname); - errbuf_ob = PyUnicode_FromString(errbuf); - PyErr_SetImportError(errbuf_ob, NULL, pathname); + /* Subtract 1 from the length to trim off trailing newline */ + errbuf_ob = PyUnicode_DecodeLocaleAndSize(errbuf, strlen(errbuf)-1, "surrogateescape"); + if (errbuf_ob == NULL) + return; + pathname_ob = PyUnicode_DecodeFSDefault(pathname); + if (pathname_ob == NULL) { + Py_DECREF(errbuf_ob); + return; + } + PyErr_SetImportError(errbuf_ob, NULL, pathname_ob); Py_DECREF(pathname_ob); Py_DECREF(errbuf_ob); return; diff --git a/Python/dynload_hpux.c b/Python/dynload_hpux.c index e59d00435ec7d..9fa003c944a7b 100644 --- a/Python/dynload_hpux.c +++ b/Python/dynload_hpux.c @@ -37,9 +37,20 @@ dl_funcptr _PyImport_FindSharedFuncptr(const char *prefix, char buf[256]; PyOS_snprintf(buf, sizeof(buf), "Failed to load %.200s", pathname); - PyObject *buf_ob = PyUnicode_FromString(buf); + PyObject *buf_ob = PyUnicode_DecodeFSDefault(buf); + if (buf_ob == NULL) + return NULL; PyObject *shortname_ob = PyUnicode_FromString(shortname); - PyObject *pathname_ob = PyUnicode_FromString(pathname); + if (shortname_ob == NULL) { + Py_DECREF(buf_ob); + return NULL; + } + PyObject *pathname_ob = PyUnicode_DecodeFSDefault(pathname); + if (pathname_ob == NULL) { + Py_DECREF(buf_ob); + Py_DECREF(shortname_ob); + return NULL; + } PyErr_SetImportError(buf_ob, shortname_ob, pathname_ob); Py_DECREF(buf_ob); Py_DECREF(shortname_ob); diff --git a/Python/dynload_shlib.c b/Python/dynload_shlib.c index c51f97abd2863..23e58ed046e30 100644 --- a/Python/dynload_shlib.c +++ b/Python/dynload_shlib.c @@ -105,7 +105,7 @@ _PyImport_FindSharedFuncptr(const char *prefix, const char *error = dlerror(); if (error == NULL) error = "unknown dlopen() error"; - error_ob = PyUnicode_FromString(error); + error_ob = PyUnicode_DecodeLocale(error, "surrogateescape"); if (error_ob == NULL) return NULL; mod_name = PyUnicode_FromString(shortname); @@ -113,7 +113,7 @@ _PyImport_FindSharedFuncptr(const char *prefix, Py_DECREF(error_ob); return NULL; } - path = PyUnicode_FromString(pathname); + path = PyUnicode_DecodeFSDefault(pathname); if (path == NULL) { Py_DECREF(error_ob); Py_DECREF(mod_name); From webhook-mailer at python.org Wed Oct 14 22:25:50 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Thu, 15 Oct 2020 02:25:50 -0000 Subject: [Python-checkins] bpo-41894: Fix UnicodeDecodeError while loading native module (GH-22466) Message-ID: https://github.com/python/cpython/commit/f07448bef48d645c8cee862b1f25a99003a6140e commit: f07448bef48d645c8cee862b1f25a99003a6140e branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-14T19:25:45-07:00 summary: bpo-41894: Fix UnicodeDecodeError while loading native module (GH-22466) When running in a non-UTF-8 locale, if an error occurs while importing a native Python module (say because a dependent share library is missing), the error message string returned may contain non-ASCII code points causing a UnicodeDecodeError. PyUnicode_DecodeFSDefault is used for buffers which may contain filesystem paths. For consistency with os.strerror(), PyUnicode_DecodeLocale is used for buffers which contain system error messages. While the shortname parameter is always encoded in ASCII according to PEP 489, it is left decoded using PyUnicode_FromString to minimize the changes and since it should not affect the decoding (albeit _potentially_ slower). In dynload_hpux, since the error buffer contains a message generated from a static ASCII string and the module filesystem path, PyUnicode_DecodeFSDefault is used instead of PyUnicode_DecodeLocale as is used elsewhere. * bpo-41894: Fix bugs in dynload error msg handling For both dynload_aix and dynload_hpux, properly handle the possibility that decoding strings may return NULL and when such an error happens, properly decrement any previously decoded strings and return early. In addition, in dynload_aix, ensure that we pass the decoded string *object* pathname_ob to PyErr_SetImportError instead of the original pathname buffer. Co-authored-by: Serhiy Storchaka (cherry picked from commit 2d2af320d94afc6561e8f8adf174c9d3fd9065bc) Co-authored-by: Kevin Adler files: A Misc/NEWS.d/next/Core and Builtins/2020-10-02-11-35-33.bpo-41894.ffmtOt.rst M Python/dynload_aix.c M Python/dynload_hpux.c M Python/dynload_shlib.c diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-10-02-11-35-33.bpo-41894.ffmtOt.rst b/Misc/NEWS.d/next/Core and Builtins/2020-10-02-11-35-33.bpo-41894.ffmtOt.rst new file mode 100644 index 0000000000000..571f5dae1a4a1 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2020-10-02-11-35-33.bpo-41894.ffmtOt.rst @@ -0,0 +1,3 @@ +When loading a native module and a load failure occurs, prevent a possible +UnicodeDecodeError when not running in a UTF-8 locale by decoding the load +error message using the current locale's encoding. diff --git a/Python/dynload_aix.c b/Python/dynload_aix.c index 684f10a8b9193..97f7698ef4b2d 100644 --- a/Python/dynload_aix.c +++ b/Python/dynload_aix.c @@ -144,10 +144,16 @@ aix_loaderror(const char *pathname) ERRBUF_APPEND(message[i]); ERRBUF_APPEND("\n"); } - errbuf[strlen(errbuf)-1] = '\0'; /* trim off last newline */ - pathname_ob = PyUnicode_FromString(pathname); - errbuf_ob = PyUnicode_FromString(errbuf); - PyErr_SetImportError(errbuf_ob, NULL, pathname); + /* Subtract 1 from the length to trim off trailing newline */ + errbuf_ob = PyUnicode_DecodeLocaleAndSize(errbuf, strlen(errbuf)-1, "surrogateescape"); + if (errbuf_ob == NULL) + return; + pathname_ob = PyUnicode_DecodeFSDefault(pathname); + if (pathname_ob == NULL) { + Py_DECREF(errbuf_ob); + return; + } + PyErr_SetImportError(errbuf_ob, NULL, pathname_ob); Py_DECREF(pathname_ob); Py_DECREF(errbuf_ob); return; diff --git a/Python/dynload_hpux.c b/Python/dynload_hpux.c index 4b964a69d3bde..e36d608c6dca4 100644 --- a/Python/dynload_hpux.c +++ b/Python/dynload_hpux.c @@ -36,9 +36,20 @@ dl_funcptr _PyImport_FindSharedFuncptr(const char *prefix, char buf[256]; PyOS_snprintf(buf, sizeof(buf), "Failed to load %.200s", pathname); - PyObject *buf_ob = PyUnicode_FromString(buf); + PyObject *buf_ob = PyUnicode_DecodeFSDefault(buf); + if (buf_ob == NULL) + return NULL; PyObject *shortname_ob = PyUnicode_FromString(shortname); - PyObject *pathname_ob = PyUnicode_FromString(pathname); + if (shortname_ob == NULL) { + Py_DECREF(buf_ob); + return NULL; + } + PyObject *pathname_ob = PyUnicode_DecodeFSDefault(pathname); + if (pathname_ob == NULL) { + Py_DECREF(buf_ob); + Py_DECREF(shortname_ob); + return NULL; + } PyErr_SetImportError(buf_ob, shortname_ob, pathname_ob); Py_DECREF(buf_ob); Py_DECREF(shortname_ob); diff --git a/Python/dynload_shlib.c b/Python/dynload_shlib.c index 082154dd91b1f..23828898d35a5 100644 --- a/Python/dynload_shlib.c +++ b/Python/dynload_shlib.c @@ -106,7 +106,7 @@ _PyImport_FindSharedFuncptr(const char *prefix, const char *error = dlerror(); if (error == NULL) error = "unknown dlopen() error"; - error_ob = PyUnicode_FromString(error); + error_ob = PyUnicode_DecodeLocale(error, "surrogateescape"); if (error_ob == NULL) return NULL; mod_name = PyUnicode_FromString(shortname); @@ -114,7 +114,7 @@ _PyImport_FindSharedFuncptr(const char *prefix, Py_DECREF(error_ob); return NULL; } - path = PyUnicode_FromString(pathname); + path = PyUnicode_DecodeFSDefault(pathname); if (path == NULL) { Py_DECREF(error_ob); Py_DECREF(mod_name); From webhook-mailer at python.org Wed Oct 14 23:38:34 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Thu, 15 Oct 2020 03:38:34 -0000 Subject: [Python-checkins] bpo-41984: GC track all user classes (GH-22701/GH-22702) Message-ID: https://github.com/python/cpython/commit/d197b2bb3e401bed53987b65a7ceb6c712c4f5bd commit: d197b2bb3e401bed53987b65a7ceb6c712c4f5bd branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-14T20:38:25-07:00 summary: bpo-41984: GC track all user classes (GH-22701/GH-22702) (cherry picked from commit c13b847a6f913b72eeb71651ff626390b738d973) files: A Misc/NEWS.d/next/Core and Builtins/2020-10-14-16-19-43.bpo-41984.SEtKMr.rst M Lib/test/test_finalization.py M Lib/test/test_gc.py M Modules/_testcapimodule.c M Objects/typeobject.c diff --git a/Lib/test/test_finalization.py b/Lib/test/test_finalization.py index 35d7913e5b89b..1d134430909d8 100644 --- a/Lib/test/test_finalization.py +++ b/Lib/test/test_finalization.py @@ -16,6 +16,15 @@ def __new__(cls, *args, **kwargs): raise TypeError('requires _testcapi.with_tp_del') return C +try: + from _testcapi import without_gc +except ImportError: + def without_gc(cls): + class C: + def __new__(cls, *args, **kwargs): + raise TypeError('requires _testcapi.without_gc') + return C + from test import support @@ -94,9 +103,11 @@ def check_sanity(self): assert self.id_ == id(self) + at without_gc class NonGC(NonGCSimpleBase): __slots__ = () + at without_gc class NonGCResurrector(NonGCSimpleBase): __slots__ = () @@ -109,8 +120,14 @@ def side_effect(self): class Simple(SimpleBase): pass -class SimpleResurrector(NonGCResurrector, SimpleBase): - pass +# Can't inherit from NonGCResurrector, in case importing without_gc fails. +class SimpleResurrector(SimpleBase): + + def side_effect(self): + """ + Resurrect self by storing self in a class-wide list. + """ + self.survivors.append(self) class TestBase: @@ -178,6 +195,7 @@ def test_simple_resurrect(self): self.assert_survivors([]) self.assertIs(wr(), None) + @support.cpython_only def test_non_gc(self): with SimpleBase.test(): s = NonGC() @@ -191,6 +209,7 @@ def test_non_gc(self): self.assert_del_calls(ids) self.assert_survivors([]) + @support.cpython_only def test_non_gc_resurrect(self): with SimpleBase.test(): s = NonGCResurrector() diff --git a/Lib/test/test_gc.py b/Lib/test/test_gc.py index acb6391944bc0..38c9cb7123311 100644 --- a/Lib/test/test_gc.py +++ b/Lib/test/test_gc.py @@ -581,9 +581,9 @@ class UserIntSlots(int): self.assertTrue(gc.is_tracked(UserInt())) self.assertTrue(gc.is_tracked([])) self.assertTrue(gc.is_tracked(set())) - self.assertFalse(gc.is_tracked(UserClassSlots())) - self.assertFalse(gc.is_tracked(UserFloatSlots())) - self.assertFalse(gc.is_tracked(UserIntSlots())) + self.assertTrue(gc.is_tracked(UserClassSlots())) + self.assertTrue(gc.is_tracked(UserFloatSlots())) + self.assertTrue(gc.is_tracked(UserIntSlots())) def test_is_finalized(self): # Objects not tracked by the always gc return false diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-10-14-16-19-43.bpo-41984.SEtKMr.rst b/Misc/NEWS.d/next/Core and Builtins/2020-10-14-16-19-43.bpo-41984.SEtKMr.rst new file mode 100644 index 0000000000000..e70d5dc2b8dde --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2020-10-14-16-19-43.bpo-41984.SEtKMr.rst @@ -0,0 +1,2 @@ +The garbage collector now tracks all user-defined classes. Patch by Brandt +Bucher. diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index b2d070cffaca0..54c1e62a28241 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -3629,6 +3629,25 @@ with_tp_del(PyObject *self, PyObject *args) return obj; } +static PyObject * +without_gc(PyObject *Py_UNUSED(self), PyObject *obj) +{ + PyTypeObject *tp = (PyTypeObject*)obj; + if (!PyType_Check(obj) || !PyType_HasFeature(tp, Py_TPFLAGS_HEAPTYPE)) { + return PyErr_Format(PyExc_TypeError, "heap type expected, got %R", obj); + } + if (PyType_IS_GC(tp)) { + // Don't try this at home, kids: + tp->tp_flags -= Py_TPFLAGS_HAVE_GC; + tp->tp_free = PyObject_Del; + tp->tp_traverse = NULL; + tp->tp_clear = NULL; + } + assert(!PyType_IS_GC(tp)); + Py_INCREF(obj); + return obj; +} + static PyMethodDef ml; static PyObject * @@ -5535,6 +5554,7 @@ static PyMethodDef TestMethods[] = { {"meth_fastcall", (PyCFunction)(void(*)(void))meth_fastcall, METH_FASTCALL}, {"meth_fastcall_keywords", (PyCFunction)(void(*)(void))meth_fastcall_keywords, METH_FASTCALL|METH_KEYWORDS}, {"pynumber_tobase", pynumber_tobase, METH_VARARGS}, + {"without_gc", without_gc, METH_O}, {NULL, NULL} /* sentinel */ }; diff --git a/Objects/typeobject.c b/Objects/typeobject.c index f65434fdb7a75..1a12b0ce07fcf 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -2605,10 +2605,10 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) slots = NULL; /* Initialize tp_flags */ + // All heap types need GC, since we can create a reference cycle by storing + // an instance on one of its parents: type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HEAPTYPE | - Py_TPFLAGS_BASETYPE; - if (base->tp_flags & Py_TPFLAGS_HAVE_GC) - type->tp_flags |= Py_TPFLAGS_HAVE_GC; + Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC; /* Initialize essential fields */ type->tp_as_async = &et->as_async; @@ -2808,21 +2808,11 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) } type->tp_dealloc = subtype_dealloc; - /* Enable GC unless this class is not adding new instance variables and - the base class did not use GC. */ - if ((base->tp_flags & Py_TPFLAGS_HAVE_GC) || - type->tp_basicsize > base->tp_basicsize) - type->tp_flags |= Py_TPFLAGS_HAVE_GC; - /* Always override allocation strategy to use regular heap */ type->tp_alloc = PyType_GenericAlloc; - if (type->tp_flags & Py_TPFLAGS_HAVE_GC) { - type->tp_free = PyObject_GC_Del; - type->tp_traverse = subtype_traverse; - type->tp_clear = subtype_clear; - } - else - type->tp_free = PyObject_Del; + type->tp_free = PyObject_GC_Del; + type->tp_traverse = subtype_traverse; + type->tp_clear = subtype_clear; /* store type in class' cell if one is supplied */ cell = _PyDict_GetItemIdWithError(dict, &PyId___classcell__); From webhook-mailer at python.org Thu Oct 15 02:42:08 2020 From: webhook-mailer at python.org (Raymond Hettinger) Date: Thu, 15 Oct 2020 06:42:08 -0000 Subject: [Python-checkins] Minor clarification (GH-22708) Message-ID: https://github.com/python/cpython/commit/b67cbbda3a022cec5e2ad929f0531162166e7c8d commit: b67cbbda3a022cec5e2ad929f0531162166e7c8d branch: master author: Raymond Hettinger committer: GitHub date: 2020-10-14T23:41:55-07:00 summary: Minor clarification (GH-22708) files: M Doc/library/random.rst diff --git a/Doc/library/random.rst b/Doc/library/random.rst index 635f9e1c032da..5a9359484d11a 100644 --- a/Doc/library/random.rst +++ b/Doc/library/random.rst @@ -545,9 +545,9 @@ Recipes The default :func:`.random` returns multiples of 2??? in the range *0.0 ? x < 1.0*. All such numbers are evenly spaced and are exactly -representable as Python floats. However, many floats in that interval -are not possible selections. For example, ``0.05954861408025609`` -isn't an integer multiple of 2???. +representable as Python floats. However, many other representable +floats in that interval are not possible selections. For example, +``0.05954861408025609`` isn't an integer multiple of 2???. The following recipe takes a different approach. All floats in the interval are possible selections. The mantissa comes from a uniform From webhook-mailer at python.org Thu Oct 15 03:09:41 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Thu, 15 Oct 2020 07:09:41 -0000 Subject: [Python-checkins] Minor clarification (GH-22708) (GH-22709) Message-ID: https://github.com/python/cpython/commit/78723b378b2529dd79f6754a514681e4920aef3e commit: 78723b378b2529dd79f6754a514681e4920aef3e branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-15T00:09:33-07:00 summary: Minor clarification (GH-22708) (GH-22709) files: M Doc/library/random.rst diff --git a/Doc/library/random.rst b/Doc/library/random.rst index d37fd9cbd8e17..f9535d7d1b9a2 100644 --- a/Doc/library/random.rst +++ b/Doc/library/random.rst @@ -545,9 +545,9 @@ Recipes The default :func:`.random` returns multiples of 2??? in the range *0.0 ? x < 1.0*. All such numbers are evenly spaced and are exactly -representable as Python floats. However, many floats in that interval -are not possible selections. For example, ``0.05954861408025609`` -isn't an integer multiple of 2???. +representable as Python floats. However, many other representable +floats in that interval are not possible selections. For example, +``0.05954861408025609`` isn't an integer multiple of 2???. The following recipe takes a different approach. All floats in the interval are possible selections. The mantissa comes from a uniform From webhook-mailer at python.org Thu Oct 15 08:20:46 2020 From: webhook-mailer at python.org (Erlend Egeberg Aasland) Date: Thu, 15 Oct 2020 12:20:46 -0000 Subject: [Python-checkins] bpo-42021: Fix possible ref leaks during _sqlite3 module init (GH-22673) Message-ID: https://github.com/python/cpython/commit/644e94272a89196801825cb69a56377bf62d256a commit: 644e94272a89196801825cb69a56377bf62d256a branch: master author: Erlend Egeberg Aasland committer: GitHub date: 2020-10-15T21:20:15+09:00 summary: bpo-42021: Fix possible ref leaks during _sqlite3 module init (GH-22673) files: A Misc/NEWS.d/next/Library/2020-10-12-21-21-24.bpo-42021.8yv_8-.rst M Modules/_sqlite/microprotocols.c M Modules/_sqlite/microprotocols.h M Modules/_sqlite/module.c diff --git a/Misc/NEWS.d/next/Library/2020-10-12-21-21-24.bpo-42021.8yv_8-.rst b/Misc/NEWS.d/next/Library/2020-10-12-21-21-24.bpo-42021.8yv_8-.rst new file mode 100644 index 0000000000000..7d71e9a70079b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-10-12-21-21-24.bpo-42021.8yv_8-.rst @@ -0,0 +1 @@ +Fix possible ref leaks in :mod:`sqlite3` module init. diff --git a/Modules/_sqlite/microprotocols.c b/Modules/_sqlite/microprotocols.c index 64095adb4db2b..ddc30e8a89b46 100644 --- a/Modules/_sqlite/microprotocols.c +++ b/Modules/_sqlite/microprotocols.c @@ -37,14 +37,19 @@ static PyObject *psyco_adapters = NULL; /* pysqlite_microprotocols_init - initialize the adapters dictionary */ int -pysqlite_microprotocols_init(PyObject *dict) +pysqlite_microprotocols_init(PyObject *module) { /* create adapters dictionary and put it in module namespace */ if ((psyco_adapters = PyDict_New()) == NULL) { return -1; } - return PyDict_SetItemString(dict, "adapters", psyco_adapters); + if (PyModule_AddObject(module, "adapters", psyco_adapters) < 0) { + Py_DECREF(psyco_adapters); + return -1; + } + + return 0; } diff --git a/Modules/_sqlite/microprotocols.h b/Modules/_sqlite/microprotocols.h index 5418c2b98fd75..87df6bac55797 100644 --- a/Modules/_sqlite/microprotocols.h +++ b/Modules/_sqlite/microprotocols.h @@ -38,7 +38,7 @@ /** exported functions **/ /* used by module.c to init the microprotocols system */ -extern int pysqlite_microprotocols_init(PyObject *dict); +extern int pysqlite_microprotocols_init(PyObject *module); extern int pysqlite_microprotocols_add( PyTypeObject *type, PyObject *proto, PyObject *cast); extern PyObject *pysqlite_microprotocols_adapt( diff --git a/Modules/_sqlite/module.c b/Modules/_sqlite/module.c index 102026663abd8..0297e2fab292e 100644 --- a/Modules/_sqlite/module.c +++ b/Modules/_sqlite/module.c @@ -236,14 +236,17 @@ PyDoc_STRVAR(enable_callback_tracebacks_doc, \n\ Enable or disable callback functions throwing errors to stderr."); -static void converters_init(PyObject* dict) +static void converters_init(PyObject* module) { _pysqlite_converters = PyDict_New(); if (!_pysqlite_converters) { return; } - PyDict_SetItemString(dict, "converters", _pysqlite_converters); + if (PyModule_AddObject(module, "converters", _pysqlite_converters) < 0) { + Py_DECREF(_pysqlite_converters); + } + return; } static PyMethodDef module_methods[] = { @@ -264,59 +267,52 @@ static PyMethodDef module_methods[] = { {NULL, NULL} }; -struct _IntConstantPair { - const char *constant_name; - int constant_value; -}; - -typedef struct _IntConstantPair IntConstantPair; - -static const IntConstantPair _int_constants[] = { - {"PARSE_DECLTYPES", PARSE_DECLTYPES}, - {"PARSE_COLNAMES", PARSE_COLNAMES}, - - {"SQLITE_OK", SQLITE_OK}, - {"SQLITE_DENY", SQLITE_DENY}, - {"SQLITE_IGNORE", SQLITE_IGNORE}, - {"SQLITE_CREATE_INDEX", SQLITE_CREATE_INDEX}, - {"SQLITE_CREATE_TABLE", SQLITE_CREATE_TABLE}, - {"SQLITE_CREATE_TEMP_INDEX", SQLITE_CREATE_TEMP_INDEX}, - {"SQLITE_CREATE_TEMP_TABLE", SQLITE_CREATE_TEMP_TABLE}, - {"SQLITE_CREATE_TEMP_TRIGGER", SQLITE_CREATE_TEMP_TRIGGER}, - {"SQLITE_CREATE_TEMP_VIEW", SQLITE_CREATE_TEMP_VIEW}, - {"SQLITE_CREATE_TRIGGER", SQLITE_CREATE_TRIGGER}, - {"SQLITE_CREATE_VIEW", SQLITE_CREATE_VIEW}, - {"SQLITE_DELETE", SQLITE_DELETE}, - {"SQLITE_DROP_INDEX", SQLITE_DROP_INDEX}, - {"SQLITE_DROP_TABLE", SQLITE_DROP_TABLE}, - {"SQLITE_DROP_TEMP_INDEX", SQLITE_DROP_TEMP_INDEX}, - {"SQLITE_DROP_TEMP_TABLE", SQLITE_DROP_TEMP_TABLE}, - {"SQLITE_DROP_TEMP_TRIGGER", SQLITE_DROP_TEMP_TRIGGER}, - {"SQLITE_DROP_TEMP_VIEW", SQLITE_DROP_TEMP_VIEW}, - {"SQLITE_DROP_TRIGGER", SQLITE_DROP_TRIGGER}, - {"SQLITE_DROP_VIEW", SQLITE_DROP_VIEW}, - {"SQLITE_INSERT", SQLITE_INSERT}, - {"SQLITE_PRAGMA", SQLITE_PRAGMA}, - {"SQLITE_READ", SQLITE_READ}, - {"SQLITE_SELECT", SQLITE_SELECT}, - {"SQLITE_TRANSACTION", SQLITE_TRANSACTION}, - {"SQLITE_UPDATE", SQLITE_UPDATE}, - {"SQLITE_ATTACH", SQLITE_ATTACH}, - {"SQLITE_DETACH", SQLITE_DETACH}, - {"SQLITE_ALTER_TABLE", SQLITE_ALTER_TABLE}, - {"SQLITE_REINDEX", SQLITE_REINDEX}, - {"SQLITE_ANALYZE", SQLITE_ANALYZE}, - {"SQLITE_CREATE_VTABLE", SQLITE_CREATE_VTABLE}, - {"SQLITE_DROP_VTABLE", SQLITE_DROP_VTABLE}, - {"SQLITE_FUNCTION", SQLITE_FUNCTION}, - {"SQLITE_SAVEPOINT", SQLITE_SAVEPOINT}, +static int add_integer_constants(PyObject *module) { + int ret = 0; + + ret += PyModule_AddIntMacro(module, PARSE_DECLTYPES); + ret += PyModule_AddIntMacro(module, PARSE_COLNAMES); + ret += PyModule_AddIntMacro(module, SQLITE_OK); + ret += PyModule_AddIntMacro(module, SQLITE_DENY); + ret += PyModule_AddIntMacro(module, SQLITE_IGNORE); + ret += PyModule_AddIntMacro(module, SQLITE_CREATE_INDEX); + ret += PyModule_AddIntMacro(module, SQLITE_CREATE_TABLE); + ret += PyModule_AddIntMacro(module, SQLITE_CREATE_TEMP_INDEX); + ret += PyModule_AddIntMacro(module, SQLITE_CREATE_TEMP_TABLE); + ret += PyModule_AddIntMacro(module, SQLITE_CREATE_TEMP_TRIGGER); + ret += PyModule_AddIntMacro(module, SQLITE_CREATE_TEMP_VIEW); + ret += PyModule_AddIntMacro(module, SQLITE_CREATE_TRIGGER); + ret += PyModule_AddIntMacro(module, SQLITE_CREATE_VIEW); + ret += PyModule_AddIntMacro(module, SQLITE_DELETE); + ret += PyModule_AddIntMacro(module, SQLITE_DROP_INDEX); + ret += PyModule_AddIntMacro(module, SQLITE_DROP_TABLE); + ret += PyModule_AddIntMacro(module, SQLITE_DROP_TEMP_INDEX); + ret += PyModule_AddIntMacro(module, SQLITE_DROP_TEMP_TABLE); + ret += PyModule_AddIntMacro(module, SQLITE_DROP_TEMP_TRIGGER); + ret += PyModule_AddIntMacro(module, SQLITE_DROP_TEMP_VIEW); + ret += PyModule_AddIntMacro(module, SQLITE_DROP_TRIGGER); + ret += PyModule_AddIntMacro(module, SQLITE_DROP_VIEW); + ret += PyModule_AddIntMacro(module, SQLITE_INSERT); + ret += PyModule_AddIntMacro(module, SQLITE_PRAGMA); + ret += PyModule_AddIntMacro(module, SQLITE_READ); + ret += PyModule_AddIntMacro(module, SQLITE_SELECT); + ret += PyModule_AddIntMacro(module, SQLITE_TRANSACTION); + ret += PyModule_AddIntMacro(module, SQLITE_UPDATE); + ret += PyModule_AddIntMacro(module, SQLITE_ATTACH); + ret += PyModule_AddIntMacro(module, SQLITE_DETACH); + ret += PyModule_AddIntMacro(module, SQLITE_ALTER_TABLE); + ret += PyModule_AddIntMacro(module, SQLITE_REINDEX); + ret += PyModule_AddIntMacro(module, SQLITE_ANALYZE); + ret += PyModule_AddIntMacro(module, SQLITE_CREATE_VTABLE); + ret += PyModule_AddIntMacro(module, SQLITE_DROP_VTABLE); + ret += PyModule_AddIntMacro(module, SQLITE_FUNCTION); + ret += PyModule_AddIntMacro(module, SQLITE_SAVEPOINT); #if SQLITE_VERSION_NUMBER >= 3008003 - {"SQLITE_RECURSIVE", SQLITE_RECURSIVE}, + ret += PyModule_AddIntMacro(module, SQLITE_RECURSIVE); #endif - {"SQLITE_DONE", SQLITE_DONE}, - {(char*)NULL, 0} -}; - + ret += PyModule_AddIntMacro(module, SQLITE_DONE); + return ret; +} static struct PyModuleDef _sqlite3module = { PyModuleDef_HEAD_INIT, @@ -338,11 +334,21 @@ do { \ } \ } while (0) +#define ADD_EXCEPTION(module, name, exc, base) \ +do { \ + exc = PyErr_NewException(MODULE_NAME "." name, base, NULL); \ + if (!exc) { \ + goto error; \ + } \ + if (PyModule_AddObject(module, name, exc) < 0) { \ + Py_DECREF(exc); \ + goto error; \ + } \ +} while (0) + PyMODINIT_FUNC PyInit__sqlite3(void) { - PyObject *module, *dict; - PyObject *tmp_obj; - int i; + PyObject *module; if (sqlite3_libversion_number() < 3007003) { PyErr_SetString(PyExc_ImportError, MODULE_NAME ": SQLite 3.7.3 or higher required"); @@ -368,65 +374,21 @@ PyMODINIT_FUNC PyInit__sqlite3(void) ADD_TYPE(module, *pysqlite_PrepareProtocolType); ADD_TYPE(module, *pysqlite_RowType); - if (!(dict = PyModule_GetDict(module))) { - goto error; - } - /*** Create DB-API Exception hierarchy */ - - if (!(pysqlite_Error = PyErr_NewException(MODULE_NAME ".Error", PyExc_Exception, NULL))) { - goto error; - } - PyDict_SetItemString(dict, "Error", pysqlite_Error); - - if (!(pysqlite_Warning = PyErr_NewException(MODULE_NAME ".Warning", PyExc_Exception, NULL))) { - goto error; - } - PyDict_SetItemString(dict, "Warning", pysqlite_Warning); + ADD_EXCEPTION(module, "Error", pysqlite_Error, PyExc_Exception); + ADD_EXCEPTION(module, "Warning", pysqlite_Warning, PyExc_Exception); /* Error subclasses */ - - if (!(pysqlite_InterfaceError = PyErr_NewException(MODULE_NAME ".InterfaceError", pysqlite_Error, NULL))) { - goto error; - } - PyDict_SetItemString(dict, "InterfaceError", pysqlite_InterfaceError); - - if (!(pysqlite_DatabaseError = PyErr_NewException(MODULE_NAME ".DatabaseError", pysqlite_Error, NULL))) { - goto error; - } - PyDict_SetItemString(dict, "DatabaseError", pysqlite_DatabaseError); + ADD_EXCEPTION(module, "InterfaceError", pysqlite_InterfaceError, pysqlite_Error); + ADD_EXCEPTION(module, "DatabaseError", pysqlite_DatabaseError, pysqlite_Error); /* pysqlite_DatabaseError subclasses */ - - if (!(pysqlite_InternalError = PyErr_NewException(MODULE_NAME ".InternalError", pysqlite_DatabaseError, NULL))) { - goto error; - } - PyDict_SetItemString(dict, "InternalError", pysqlite_InternalError); - - if (!(pysqlite_OperationalError = PyErr_NewException(MODULE_NAME ".OperationalError", pysqlite_DatabaseError, NULL))) { - goto error; - } - PyDict_SetItemString(dict, "OperationalError", pysqlite_OperationalError); - - if (!(pysqlite_ProgrammingError = PyErr_NewException(MODULE_NAME ".ProgrammingError", pysqlite_DatabaseError, NULL))) { - goto error; - } - PyDict_SetItemString(dict, "ProgrammingError", pysqlite_ProgrammingError); - - if (!(pysqlite_IntegrityError = PyErr_NewException(MODULE_NAME ".IntegrityError", pysqlite_DatabaseError,NULL))) { - goto error; - } - PyDict_SetItemString(dict, "IntegrityError", pysqlite_IntegrityError); - - if (!(pysqlite_DataError = PyErr_NewException(MODULE_NAME ".DataError", pysqlite_DatabaseError, NULL))) { - goto error; - } - PyDict_SetItemString(dict, "DataError", pysqlite_DataError); - - if (!(pysqlite_NotSupportedError = PyErr_NewException(MODULE_NAME ".NotSupportedError", pysqlite_DatabaseError, NULL))) { - goto error; - } - PyDict_SetItemString(dict, "NotSupportedError", pysqlite_NotSupportedError); + ADD_EXCEPTION(module, "InternalError", pysqlite_InternalError, pysqlite_DatabaseError); + ADD_EXCEPTION(module, "OperationalError", pysqlite_OperationalError, pysqlite_DatabaseError); + ADD_EXCEPTION(module, "ProgrammingError", pysqlite_ProgrammingError, pysqlite_DatabaseError); + ADD_EXCEPTION(module, "IntegrityError", pysqlite_IntegrityError, pysqlite_DatabaseError); + ADD_EXCEPTION(module, "DataError", pysqlite_DataError, pysqlite_DatabaseError); + ADD_EXCEPTION(module, "NotSupportedError", pysqlite_NotSupportedError, pysqlite_DatabaseError); /* In Python 2.x, setting Connection.text_factory to OptimizedUnicode caused Unicode objects to be returned for @@ -434,35 +396,31 @@ PyMODINIT_FUNC PyInit__sqlite3(void) Now OptimizedUnicode is an alias for str, so it has no effect. */ Py_INCREF((PyObject*)&PyUnicode_Type); - PyDict_SetItemString(dict, "OptimizedUnicode", (PyObject*)&PyUnicode_Type); + if (PyModule_AddObject(module, "OptimizedUnicode", (PyObject*)&PyUnicode_Type) < 0) { + Py_DECREF((PyObject*)&PyUnicode_Type); + goto error; + } /* Set integer constants */ - for (i = 0; _int_constants[i].constant_name != NULL; i++) { - tmp_obj = PyLong_FromLong(_int_constants[i].constant_value); - if (!tmp_obj) { - goto error; - } - PyDict_SetItemString(dict, _int_constants[i].constant_name, tmp_obj); - Py_DECREF(tmp_obj); + if (add_integer_constants(module) < 0) { + goto error; } - if (!(tmp_obj = PyUnicode_FromString(PYSQLITE_VERSION))) { + if (PyModule_AddStringConstant(module, "version", PYSQLITE_VERSION) < 0) { goto error; } - PyDict_SetItemString(dict, "version", tmp_obj); - Py_DECREF(tmp_obj); - if (!(tmp_obj = PyUnicode_FromString(sqlite3_libversion()))) { + if (PyModule_AddStringConstant(module, "sqlite_version", sqlite3_libversion())) { goto error; } - PyDict_SetItemString(dict, "sqlite_version", tmp_obj); - Py_DECREF(tmp_obj); /* initialize microprotocols layer */ - pysqlite_microprotocols_init(dict); + if (pysqlite_microprotocols_init(module) < 0) { + goto error; + } /* initialize the default converters */ - converters_init(dict); + converters_init(module); error: if (PyErr_Occurred()) From webhook-mailer at python.org Thu Oct 15 10:22:28 2020 From: webhook-mailer at python.org (Victor Stinner) Date: Thu, 15 Oct 2020 14:22:28 -0000 Subject: [Python-checkins] bpo-1635741: Add a global module state to unicodedata (GH-22712) Message-ID: https://github.com/python/cpython/commit/e6b8c5263a7fcf5b95d0fd4c900e5949eeb6630d commit: e6b8c5263a7fcf5b95d0fd4c900e5949eeb6630d branch: master author: Victor Stinner committer: GitHub date: 2020-10-15T16:22:19+02:00 summary: bpo-1635741: Add a global module state to unicodedata (GH-22712) Prepare unicodedata to add a state per module: start with a global "module" state, pass it to subfunctions which access &UCD_Type. This change also prepares the conversion of the UCD_Type static type to a heap type. files: M Modules/unicodedata.c diff --git a/Modules/unicodedata.c b/Modules/unicodedata.c index 8e11cfc4dafa9..941fd2faa742a 100644 --- a/Modules/unicodedata.c +++ b/Modules/unicodedata.c @@ -93,22 +93,33 @@ static PyMemberDef DB_members[] = { /* forward declaration */ static PyTypeObject UCD_Type; -// Check if self is an instance of UCD_Type. +typedef struct { + // Borrowed reference to &UCD_Type. It is used to prepare the code + // to convert the UCD_Type static type to a heap type. + PyTypeObject *ucd_type; +} unicodedata_module_state; + +// bpo-1635741: Temporary global state until the unicodedata module +// gets a real module state. +static unicodedata_module_state global_module_state; + +// Check if self is an instance of ucd_type. // Return 0 if self is NULL (when the PyCapsule C API is used). #define UCD_Check(self, ucd_type) (self != NULL && Py_IS_TYPE(self, ucd_type)) static PyObject* -new_previous_version(const char*name, const change_record* (*getrecord)(Py_UCS4), +new_previous_version(unicodedata_module_state *state, + const char*name, const change_record* (*getrecord)(Py_UCS4), Py_UCS4 (*normalization)(Py_UCS4)) { - PreviousDBVersion *self; - self = PyObject_New(PreviousDBVersion, &UCD_Type); - if (self == NULL) - return NULL; - self->name = name; - self->getrecord = getrecord; - self->normalization = normalization; - return (PyObject*)self; + PreviousDBVersion *self; + self = PyObject_New(PreviousDBVersion, state->ucd_type); + if (self == NULL) + return NULL; + self->name = name; + self->getrecord = getrecord; + self->normalization = normalization; + return (PyObject*)self; } @@ -134,11 +145,12 @@ unicodedata_UCD_decimal_impl(PyObject *self, int chr, PyObject *default_value) /*[clinic end generated code: output=be23376e1a185231 input=933f8107993f23d0]*/ { + unicodedata_module_state *state = &global_module_state; int have_old = 0; long rc; Py_UCS4 c = (Py_UCS4)chr; - if (UCD_Check(self, &UCD_Type)) { + if (UCD_Check(self, state->ucd_type)) { const change_record *old = get_old_record(self, c); if (old->category_changed == 0) { /* unassigned */ @@ -222,11 +234,12 @@ unicodedata_UCD_numeric_impl(PyObject *self, int chr, PyObject *default_value) /*[clinic end generated code: output=53ce281fe85b10c4 input=fdf5871a5542893c]*/ { + unicodedata_module_state *state = &global_module_state; int have_old = 0; double rc; Py_UCS4 c = (Py_UCS4)chr; - if (UCD_Check(self, &UCD_Type)) { + if (UCD_Check(self, state->ucd_type)) { const change_record *old = get_old_record(self, c); if (old->category_changed == 0) { /* unassigned */ @@ -268,10 +281,11 @@ static PyObject * unicodedata_UCD_category_impl(PyObject *self, int chr) /*[clinic end generated code: output=8571539ee2e6783a input=27d6f3d85050bc06]*/ { + unicodedata_module_state *state = &global_module_state; int index; Py_UCS4 c = (Py_UCS4)chr; index = (int) _getrecord_ex(c)->category; - if (UCD_Check(self, &UCD_Type)) { + if (UCD_Check(self, state->ucd_type)) { const change_record *old = get_old_record(self, c); if (old->category_changed != 0xFF) index = old->category_changed; @@ -295,10 +309,11 @@ static PyObject * unicodedata_UCD_bidirectional_impl(PyObject *self, int chr) /*[clinic end generated code: output=d36310ce2039bb92 input=b3d8f42cebfcf475]*/ { + unicodedata_module_state *state = &global_module_state; int index; Py_UCS4 c = (Py_UCS4)chr; index = (int) _getrecord_ex(c)->bidirectional; - if (UCD_Check(self, &UCD_Type)) { + if (UCD_Check(self, state->ucd_type)) { const change_record *old = get_old_record(self, c); if (old->category_changed == 0) index = 0; /* unassigned */ @@ -324,10 +339,11 @@ static int unicodedata_UCD_combining_impl(PyObject *self, int chr) /*[clinic end generated code: output=cad056d0cb6a5920 input=9f2d6b2a95d0a22a]*/ { + unicodedata_module_state *state = &global_module_state; int index; Py_UCS4 c = (Py_UCS4)chr; index = (int) _getrecord_ex(c)->combining; - if (UCD_Check(self, &UCD_Type)) { + if (UCD_Check(self, state->ucd_type)) { const change_record *old = get_old_record(self, c); if (old->category_changed == 0) index = 0; /* unassigned */ @@ -352,10 +368,11 @@ static int unicodedata_UCD_mirrored_impl(PyObject *self, int chr) /*[clinic end generated code: output=2532dbf8121b50e6 input=5dd400d351ae6f3b]*/ { + unicodedata_module_state *state = &global_module_state; int index; Py_UCS4 c = (Py_UCS4)chr; index = (int) _getrecord_ex(c)->mirrored; - if (UCD_Check(self, &UCD_Type)) { + if (UCD_Check(self, state->ucd_type)) { const change_record *old = get_old_record(self, c); if (old->category_changed == 0) index = 0; /* unassigned */ @@ -379,10 +396,11 @@ static PyObject * unicodedata_UCD_east_asian_width_impl(PyObject *self, int chr) /*[clinic end generated code: output=484e8537d9ee8197 input=c4854798aab026e0]*/ { + unicodedata_module_state *state = &global_module_state; int index; Py_UCS4 c = (Py_UCS4)chr; index = (int) _getrecord_ex(c)->east_asian_width; - if (UCD_Check(self, &UCD_Type)) { + if (UCD_Check(self, state->ucd_type)) { const change_record *old = get_old_record(self, c); if (old->category_changed == 0) index = 0; /* unassigned */ @@ -408,6 +426,7 @@ static PyObject * unicodedata_UCD_decomposition_impl(PyObject *self, int chr) /*[clinic end generated code: output=7d699f3ec7565d27 input=e4c12459ad68507b]*/ { + unicodedata_module_state *state = &global_module_state; char decomp[256]; int code, index, count; size_t i; @@ -416,7 +435,7 @@ unicodedata_UCD_decomposition_impl(PyObject *self, int chr) code = (int)c; - if (UCD_Check(self, &UCD_Type)) { + if (UCD_Check(self, state->ucd_type)) { const change_record *old = get_old_record(self, c); if (old->category_changed == 0) return PyUnicode_FromString(""); /* unassigned */ @@ -459,11 +478,12 @@ unicodedata_UCD_decomposition_impl(PyObject *self, int chr) } static void -get_decomp_record(PyObject *self, Py_UCS4 code, int *index, int *prefix, int *count) +get_decomp_record(unicodedata_module_state *state, PyObject *self, + Py_UCS4 code, int *index, int *prefix, int *count) { if (code >= 0x110000) { *index = 0; - } else if (UCD_Check(self, &UCD_Type) && + } else if (UCD_Check(self, state->ucd_type) && get_old_record(self, code)->category_changed==0) { /* unassigned in old version */ *index = 0; @@ -493,7 +513,8 @@ get_decomp_record(PyObject *self, Py_UCS4 code, int *index, int *prefix, int *co #define SCount (LCount*NCount) static PyObject* -nfd_nfkd(PyObject *self, PyObject *input, int k) +nfd_nfkd(unicodedata_module_state *state, PyObject *self, + PyObject *input, int k) { PyObject *result; Py_UCS4 *output; @@ -561,7 +582,7 @@ nfd_nfkd(PyObject *self, PyObject *input, int k) continue; } /* normalization changes */ - if (UCD_Check(self, &UCD_Type)) { + if (UCD_Check(self, state->ucd_type)) { Py_UCS4 value = ((PreviousDBVersion*)self)->normalization(code); if (value != 0) { stack[stackptr++] = value; @@ -570,7 +591,7 @@ nfd_nfkd(PyObject *self, PyObject *input, int k) } /* Other decompositions. */ - get_decomp_record(self, code, &index, &prefix, &count); + get_decomp_record(state, self, code, &index, &prefix, &count); /* Copy character if it is not decomposable, or has a compatibility decomposition, but we do NFD. */ @@ -642,7 +663,7 @@ find_nfc_index(const struct reindex* nfc, Py_UCS4 code) } static PyObject* -nfc_nfkc(PyObject *self, PyObject *input, int k) +nfc_nfkc(unicodedata_module_state *state, PyObject *self, PyObject *input, int k) { PyObject *result; int kind; @@ -654,7 +675,7 @@ nfc_nfkc(PyObject *self, PyObject *input, int k) Py_ssize_t skipped[20]; int cskipped = 0; - result = nfd_nfkd(self, input, k); + result = nfd_nfkd(state, self, input, k); if (!result) return NULL; /* result will be "ready". */ @@ -797,12 +818,12 @@ typedef enum {YES = 0, MAYBE = 1, NO = 2} QuickcheckResult; * https://www.unicode.org/reports/tr15/#Detecting_Normalization_Forms */ static QuickcheckResult -is_normalized_quickcheck(PyObject *self, PyObject *input, - bool nfc, bool k, bool yes_only) +is_normalized_quickcheck(unicodedata_module_state *state, PyObject *self, + PyObject *input, bool nfc, bool k, bool yes_only) { /* An older version of the database is requested, quickchecks must be disabled. */ - if (UCD_Check(self, &UCD_Type)) + if (UCD_Check(self, state->ucd_type)) return NO; Py_ssize_t i, len; @@ -862,6 +883,7 @@ unicodedata_UCD_is_normalized_impl(PyObject *self, PyObject *form, PyObject *input) /*[clinic end generated code: output=11e5a3694e723ca5 input=a544f14cea79e508]*/ { + unicodedata_module_state *state = &global_module_state; if (PyUnicode_READY(input) == -1) { return NULL; } @@ -897,10 +919,10 @@ unicodedata_UCD_is_normalized_impl(PyObject *self, PyObject *form, return NULL; } - m = is_normalized_quickcheck(self, input, nfc, k, false); + m = is_normalized_quickcheck(state, self, input, nfc, k, false); if (m == MAYBE) { - cmp = (nfc ? nfc_nfkc : nfd_nfkd)(self, input, k); + cmp = (nfc ? nfc_nfkc : nfd_nfkd)(state, self, input, k); if (cmp == NULL) { return NULL; } @@ -935,6 +957,7 @@ unicodedata_UCD_normalize_impl(PyObject *self, PyObject *form, PyObject *input) /*[clinic end generated code: output=05ca4385a2ad6983 input=3a5206c0ad2833fb]*/ { + unicodedata_module_state *state = &global_module_state; if (PyUnicode_GET_LENGTH(input) == 0) { /* Special case empty input strings, since resizing them later would cause internal errors. */ @@ -943,32 +966,36 @@ unicodedata_UCD_normalize_impl(PyObject *self, PyObject *form, } if (_PyUnicode_EqualToASCIIId(form, &PyId_NFC)) { - if (is_normalized_quickcheck(self, input, true, false, true) == YES) { + if (is_normalized_quickcheck(state, self, input, + true, false, true) == YES) { Py_INCREF(input); return input; } - return nfc_nfkc(self, input, 0); + return nfc_nfkc(state, self, input, 0); } if (_PyUnicode_EqualToASCIIId(form, &PyId_NFKC)) { - if (is_normalized_quickcheck(self, input, true, true, true) == YES) { + if (is_normalized_quickcheck(state, self, input, + true, true, true) == YES) { Py_INCREF(input); return input; } - return nfc_nfkc(self, input, 1); + return nfc_nfkc(state, self, input, 1); } if (_PyUnicode_EqualToASCIIId(form, &PyId_NFD)) { - if (is_normalized_quickcheck(self, input, false, false, true) == YES) { + if (is_normalized_quickcheck(state, self, input, + false, false, true) == YES) { Py_INCREF(input); return input; } - return nfd_nfkd(self, input, 0); + return nfd_nfkd(state, self, input, 0); } if (_PyUnicode_EqualToASCIIId(form, &PyId_NFKD)) { - if (is_normalized_quickcheck(self, input, false, true, true) == YES) { + if (is_normalized_quickcheck(state, self, input, + false, true, true) == YES) { Py_INCREF(input); return input; } - return nfd_nfkd(self, input, 1); + return nfd_nfkd(state, self, input, 1); } PyErr_SetString(PyExc_ValueError, "invalid normalization form"); return NULL; @@ -1051,8 +1078,8 @@ is_unified_ideograph(Py_UCS4 code) (cp < named_sequences_end)) static int -_getucname(PyObject *self, Py_UCS4 code, char* buffer, int buflen, - int with_alias_and_seq) +_getucname(unicodedata_module_state *state, PyObject *self, + Py_UCS4 code, char* buffer, int buflen, int with_alias_and_seq) { /* Find the name associated with the given code point. * If with_alias_and_seq is 1, check for names in the Private Use Area 15 @@ -1069,7 +1096,7 @@ _getucname(PyObject *self, Py_UCS4 code, char* buffer, int buflen, if (!with_alias_and_seq && (IS_ALIAS(code) || IS_NAMED_SEQ(code))) return 0; - if (UCD_Check(self, &UCD_Type)) { + if (UCD_Check(self, state->ucd_type)) { /* in 3.2.0 there are no aliases and named sequences */ const change_record *old; if (IS_ALIAS(code) || IS_NAMED_SEQ(code)) @@ -1153,12 +1180,22 @@ _getucname(PyObject *self, Py_UCS4 code, char* buffer, int buflen, } static int -_cmpname(PyObject *self, int code, const char* name, int namelen) +capi_getucname(PyObject *self, Py_UCS4 code, char* buffer, int buflen, + int with_alias_and_seq) +{ + unicodedata_module_state *state = &global_module_state; + return _getucname(state, self, code, buffer, buflen, with_alias_and_seq); + +} + +static int +_cmpname(unicodedata_module_state *state, PyObject *self, + int code, const char* name, int namelen) { /* check if code corresponds to the given name */ int i; char buffer[NAME_MAXLEN+1]; - if (!_getucname(self, code, buffer, NAME_MAXLEN, 1)) + if (!_getucname(state, self, code, buffer, NAME_MAXLEN, 1)) return 0; for (i = 0; i < namelen; i++) { if (Py_TOUPPER(name[i]) != buffer[i]) @@ -1203,8 +1240,8 @@ _check_alias_and_seq(unsigned int cp, Py_UCS4* code, int with_named_seq) } static int -_getcode(PyObject* self, const char* name, int namelen, Py_UCS4* code, - int with_named_seq) +_getcode(unicodedata_module_state *state, PyObject* self, + const char* name, int namelen, Py_UCS4* code, int with_named_seq) { /* Return the code point associated with the given name. * Named aliases are resolved too (unless self != NULL (i.e. we are using @@ -1265,8 +1302,9 @@ _getcode(PyObject* self, const char* name, int namelen, Py_UCS4* code, v = code_hash[i]; if (!v) return 0; - if (_cmpname(self, v, name, namelen)) + if (_cmpname(state, self, v, name, namelen)) { return _check_alias_and_seq(v, code, with_named_seq); + } incr = (h ^ (h >> 3)) & mask; if (!incr) incr = mask; @@ -1275,19 +1313,29 @@ _getcode(PyObject* self, const char* name, int namelen, Py_UCS4* code, v = code_hash[i]; if (!v) return 0; - if (_cmpname(self, v, name, namelen)) + if (_cmpname(state, self, v, name, namelen)) { return _check_alias_and_seq(v, code, with_named_seq); + } incr = incr << 1; if (incr > mask) incr = incr ^ code_poly; } } +static int +capi_getcode(PyObject* self, const char* name, int namelen, Py_UCS4* code, + int with_named_seq) +{ + unicodedata_module_state *state = &global_module_state; + return _getcode(state, self, name, namelen, code, with_named_seq); + +} + static const _PyUnicode_Name_CAPI hashAPI = { sizeof(_PyUnicode_Name_CAPI), - _getucname, - _getcode + capi_getucname, + capi_getcode }; /* -------------------------------------------------------------------- */ @@ -1311,10 +1359,11 @@ static PyObject * unicodedata_UCD_name_impl(PyObject *self, int chr, PyObject *default_value) /*[clinic end generated code: output=6bbb37a326407707 input=3e0367f534de56d9]*/ { + unicodedata_module_state *state = &global_module_state; char name[NAME_MAXLEN+1]; Py_UCS4 c = (Py_UCS4)chr; - if (!_getucname(self, c, name, NAME_MAXLEN, 0)) { + if (!_getucname(state, self, c, name, NAME_MAXLEN, 0)) { if (default_value == NULL) { PyErr_SetString(PyExc_ValueError, "no such name"); return NULL; @@ -1346,6 +1395,7 @@ unicodedata_UCD_lookup_impl(PyObject *self, const char *name, Py_ssize_clean_t name_length) /*[clinic end generated code: output=765cb8186788e6be input=a557be0f8607a0d6]*/ { + unicodedata_module_state *state = &global_module_state; Py_UCS4 code; unsigned int index; if (name_length > NAME_MAXLEN) { @@ -1353,7 +1403,7 @@ unicodedata_UCD_lookup_impl(PyObject *self, const char *name, return NULL; } - if (!_getcode(self, name, (int)name_length, &code, 1)) { + if (!_getcode(state, self, name, (int)name_length, &code, 1)) { PyErr_Format(PyExc_KeyError, "undefined character name '%s'", name); return NULL; } @@ -1458,19 +1508,22 @@ PyMODINIT_FUNC PyInit_unicodedata(void) { PyObject *m, *v; + unicodedata_module_state *state = &global_module_state; Py_SET_TYPE(&UCD_Type, &PyType_Type); + state->ucd_type = &UCD_Type; m = PyModule_Create(&unicodedatamodule); if (!m) return NULL; PyModule_AddStringConstant(m, "unidata_version", UNIDATA_VERSION); - Py_INCREF(&UCD_Type); - PyModule_AddObject(m, "UCD", (PyObject*)&UCD_Type); + Py_INCREF(state->ucd_type); + PyModule_AddObject(m, "UCD", (PyObject*)state->ucd_type); /* Previous versions */ - v = new_previous_version("3.2.0", get_change_3_2_0, normalization_3_2_0); + v = new_previous_version(state, "3.2.0", + get_change_3_2_0, normalization_3_2_0); if (v != NULL) PyModule_AddObject(m, "ucd_3_2_0", v); From webhook-mailer at python.org Thu Oct 15 11:51:56 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Thu, 15 Oct 2020 15:51:56 -0000 Subject: [Python-checkins] bpo-41984: GC track all user classes (GH-22701/GH-22707) Message-ID: https://github.com/python/cpython/commit/aeb66c1abbf4ec214e2e80eb972546996d1a1571 commit: aeb66c1abbf4ec214e2e80eb972546996d1a1571 branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-15T08:51:48-07:00 summary: bpo-41984: GC track all user classes (GH-22701/GH-22707) (cherry picked from commit c13b847a6f913b72eeb71651ff626390b738d973) files: A Misc/NEWS.d/next/Core and Builtins/2020-10-14-16-19-43.bpo-41984.SEtKMr.rst M Lib/test/test_finalization.py M Lib/test/test_gc.py M Modules/_testcapimodule.c M Objects/typeobject.c diff --git a/Lib/test/test_finalization.py b/Lib/test/test_finalization.py index 35d7913e5b89b..1d134430909d8 100644 --- a/Lib/test/test_finalization.py +++ b/Lib/test/test_finalization.py @@ -16,6 +16,15 @@ def __new__(cls, *args, **kwargs): raise TypeError('requires _testcapi.with_tp_del') return C +try: + from _testcapi import without_gc +except ImportError: + def without_gc(cls): + class C: + def __new__(cls, *args, **kwargs): + raise TypeError('requires _testcapi.without_gc') + return C + from test import support @@ -94,9 +103,11 @@ def check_sanity(self): assert self.id_ == id(self) + at without_gc class NonGC(NonGCSimpleBase): __slots__ = () + at without_gc class NonGCResurrector(NonGCSimpleBase): __slots__ = () @@ -109,8 +120,14 @@ def side_effect(self): class Simple(SimpleBase): pass -class SimpleResurrector(NonGCResurrector, SimpleBase): - pass +# Can't inherit from NonGCResurrector, in case importing without_gc fails. +class SimpleResurrector(SimpleBase): + + def side_effect(self): + """ + Resurrect self by storing self in a class-wide list. + """ + self.survivors.append(self) class TestBase: @@ -178,6 +195,7 @@ def test_simple_resurrect(self): self.assert_survivors([]) self.assertIs(wr(), None) + @support.cpython_only def test_non_gc(self): with SimpleBase.test(): s = NonGC() @@ -191,6 +209,7 @@ def test_non_gc(self): self.assert_del_calls(ids) self.assert_survivors([]) + @support.cpython_only def test_non_gc_resurrect(self): with SimpleBase.test(): s = NonGCResurrector() diff --git a/Lib/test/test_gc.py b/Lib/test/test_gc.py index f52db1eab169c..58d461334b955 100644 --- a/Lib/test/test_gc.py +++ b/Lib/test/test_gc.py @@ -576,9 +576,9 @@ class UserIntSlots(int): self.assertTrue(gc.is_tracked(UserInt())) self.assertTrue(gc.is_tracked([])) self.assertTrue(gc.is_tracked(set())) - self.assertFalse(gc.is_tracked(UserClassSlots())) - self.assertFalse(gc.is_tracked(UserFloatSlots())) - self.assertFalse(gc.is_tracked(UserIntSlots())) + self.assertTrue(gc.is_tracked(UserClassSlots())) + self.assertTrue(gc.is_tracked(UserFloatSlots())) + self.assertTrue(gc.is_tracked(UserIntSlots())) def test_bug1055820b(self): # Corresponds to temp2b.py in the bug report. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-10-14-16-19-43.bpo-41984.SEtKMr.rst b/Misc/NEWS.d/next/Core and Builtins/2020-10-14-16-19-43.bpo-41984.SEtKMr.rst new file mode 100644 index 0000000000000..e70d5dc2b8dde --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2020-10-14-16-19-43.bpo-41984.SEtKMr.rst @@ -0,0 +1,2 @@ +The garbage collector now tracks all user-defined classes. Patch by Brandt +Bucher. diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index af28af50c0650..5089f593da4d6 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -3511,6 +3511,25 @@ with_tp_del(PyObject *self, PyObject *args) return obj; } +static PyObject * +without_gc(PyObject *Py_UNUSED(self), PyObject *obj) +{ + PyTypeObject *tp = (PyTypeObject*)obj; + if (!PyType_Check(obj) || !PyType_HasFeature(tp, Py_TPFLAGS_HEAPTYPE)) { + return PyErr_Format(PyExc_TypeError, "heap type expected, got %R", obj); + } + if (PyType_IS_GC(tp)) { + // Don't try this at home, kids: + tp->tp_flags -= Py_TPFLAGS_HAVE_GC; + tp->tp_free = PyObject_Del; + tp->tp_traverse = NULL; + tp->tp_clear = NULL; + } + assert(!PyType_IS_GC(tp)); + Py_INCREF(obj); + return obj; +} + static PyMethodDef ml; static PyObject * @@ -5325,6 +5344,7 @@ static PyMethodDef TestMethods[] = { #endif {"write_unraisable_exc", test_write_unraisable_exc, METH_VARARGS}, {"pynumber_tobase", pynumber_tobase, METH_VARARGS}, + {"without_gc", without_gc, METH_O}, {NULL, NULL} /* sentinel */ }; diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 0acde714a87b1..21b1360b4bbca 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -2577,10 +2577,10 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) slots = NULL; /* Initialize tp_flags */ + // All heap types need GC, since we can create a reference cycle by storing + // an instance on one of its parents: type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HEAPTYPE | - Py_TPFLAGS_BASETYPE; - if (base->tp_flags & Py_TPFLAGS_HAVE_GC) - type->tp_flags |= Py_TPFLAGS_HAVE_GC; + Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC; /* Initialize essential fields */ type->tp_as_async = &et->as_async; @@ -2777,21 +2777,11 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) } type->tp_dealloc = subtype_dealloc; - /* Enable GC unless this class is not adding new instance variables and - the base class did not use GC. */ - if ((base->tp_flags & Py_TPFLAGS_HAVE_GC) || - type->tp_basicsize > base->tp_basicsize) - type->tp_flags |= Py_TPFLAGS_HAVE_GC; - /* Always override allocation strategy to use regular heap */ type->tp_alloc = PyType_GenericAlloc; - if (type->tp_flags & Py_TPFLAGS_HAVE_GC) { - type->tp_free = PyObject_GC_Del; - type->tp_traverse = subtype_traverse; - type->tp_clear = subtype_clear; - } - else - type->tp_free = PyObject_Del; + type->tp_free = PyObject_GC_Del; + type->tp_traverse = subtype_traverse; + type->tp_clear = subtype_clear; /* store type in class' cell if one is supplied */ cell = _PyDict_GetItemIdWithError(dict, &PyId___classcell__); From webhook-mailer at python.org Thu Oct 15 13:25:39 2020 From: webhook-mailer at python.org (Xie Yanbo) Date: Thu, 15 Oct 2020 17:25:39 -0000 Subject: [Python-checkins] [3.9] Fix incorrect parameter name (GH-22613) (GH-22628) Message-ID: https://github.com/python/cpython/commit/b30934e9afb0af3f8e2e5f0992445be775b3c630 commit: b30934e9afb0af3f8e2e5f0992445be775b3c630 branch: 3.9 author: Xie Yanbo committer: GitHub date: 2020-10-15T22:55:28+05:30 summary: [3.9] Fix incorrect parameter name (GH-22613) (GH-22628) Automerge-Triggered-By: @Mariatta (cherry picked from commit a42759351bff7b07fa8bf2cece0088f8539721d1) Co-authored-by: Xie Yanbo files: M Doc/library/shutil.rst diff --git a/Doc/library/shutil.rst b/Doc/library/shutil.rst index 1b094aeb9ca3d..fd3ce7445b511 100644 --- a/Doc/library/shutil.rst +++ b/Doc/library/shutil.rst @@ -349,7 +349,7 @@ Directory and files operations will be created in or as *dst* and *src* will be removed. If *copy_function* is given, it must be a callable that takes two arguments - *src* and *dst*, and will be used to copy *src* to *dest* if + *src* and *dst*, and will be used to copy *src* to *dst* if :func:`os.rename` cannot be used. If the source is a directory, :func:`copytree` is called, passing it the :func:`copy_function`. The default *copy_function* is :func:`copy2`. Using :func:`~shutil.copy` as the From webhook-mailer at python.org Thu Oct 15 15:06:33 2020 From: webhook-mailer at python.org (Saiyang Gou) Date: Thu, 15 Oct 2020 19:06:33 -0000 Subject: [Python-checkins] Document that `test.support.bytecode_helper` is new in 3.9 (GH-22618) Message-ID: https://github.com/python/cpython/commit/cf693e537dc8aaa14315a7f59baec4a31d1167d3 commit: cf693e537dc8aaa14315a7f59baec4a31d1167d3 branch: master author: Saiyang Gou committer: GitHub date: 2020-10-15T16:06:23-03:00 summary: Document that `test.support.bytecode_helper` is new in 3.9 (GH-22618) files: M Doc/library/test.rst diff --git a/Doc/library/test.rst b/Doc/library/test.rst index 6495b4844449e..ce6b868458ea4 100644 --- a/Doc/library/test.rst +++ b/Doc/library/test.rst @@ -1168,6 +1168,8 @@ script execution tests. The :mod:`test.support.bytecode_helper` module provides support for testing and inspecting bytecode generation. +.. versionadded:: 3.9 + The module defines the following class: .. class:: BytecodeTestCase(unittest.TestCase) From webhook-mailer at python.org Thu Oct 15 17:05:21 2020 From: webhook-mailer at python.org (Jason R. Coombs) Date: Thu, 15 Oct 2020 21:05:21 -0000 Subject: [Python-checkins] [3.8] bpo-41855: Fix duplicate results in FastPath.zip_children() (#22404) Message-ID: https://github.com/python/cpython/commit/967fddae2fe48f297563c358bdbdde1e2cfed4ee commit: 967fddae2fe48f297563c358bdbdde1e2cfed4ee branch: 3.8 author: Jason R. Coombs committer: GitHub date: 2020-10-15T17:05:12-04:00 summary: [3.8] bpo-41855: Fix duplicate results in FastPath.zip_children() (#22404) * bpo-41855: Backport fixes from importlib_metadata 1.5.2. * Add blurb. * Add anchor for finders and loaders files: A Misc/NEWS.d/next/Library/2020-09-24-16-45-59.bpo-41855.q6Y1nm.rst M Doc/library/importlib.metadata.rst M Doc/reference/import.rst M Lib/importlib/metadata.py M Lib/test/test_importlib/test_zip.py diff --git a/Doc/library/importlib.metadata.rst b/Doc/library/importlib.metadata.rst index dc6b66ca384d0..15e58b860d97d 100644 --- a/Doc/library/importlib.metadata.rst +++ b/Doc/library/importlib.metadata.rst @@ -1,8 +1,8 @@ .. _using: -========================== - Using importlib.metadata -========================== +================================= + Using :mod:`!importlib.metadata` +================================= .. note:: This functionality is provisional and may deviate from the usual @@ -12,8 +12,8 @@ package metadata. Built in part on Python's import system, this library intends to replace similar functionality in the `entry point API`_ and `metadata API`_ of ``pkg_resources``. Along with -``importlib.resources`` in `Python 3.7 -and newer`_ (backported as `importlib_resources`_ for older versions of +:mod:`importlib.resources` in Python 3.7 +and newer (backported as `importlib_resources`_ for older versions of Python), this can eliminate the need to use the older and less efficient ``pkg_resources`` package. @@ -21,9 +21,9 @@ By "installed package" we generally mean a third-party package installed into Python's ``site-packages`` directory via tools such as `pip `_. Specifically, it means a package with either a discoverable ``dist-info`` or ``egg-info`` -directory, and metadata defined by `PEP 566`_ or its older specifications. +directory, and metadata defined by :pep:`566` or its older specifications. By default, package metadata can live on the file system or in zip archives on -``sys.path``. Through an extension mechanism, the metadata can live almost +:data:`sys.path`. Through an extension mechanism, the metadata can live almost anywhere. @@ -134,7 +134,7 @@ Distribution files You can also get the full set of files contained within a distribution. The ``files()`` function takes a distribution package name and returns all of the files installed by this distribution. Each file object returned is a -``PackagePath``, a `pathlib.Path`_ derived object with additional ``dist``, +``PackagePath``, a :class:`pathlib.Path` derived object with additional ``dist``, ``size``, and ``hash`` properties as indicated by the metadata. For example:: >>> util = [p for p in files('wheel') if 'util.py' in str(p)][0] # doctest: +SKIP @@ -203,18 +203,18 @@ instance:: >>> d.metadata['License'] # doctest: +SKIP 'MIT' -The full set of available metadata is not described here. See `PEP 566 -`_ for additional details. +The full set of available metadata is not described here. See :pep:`566` +for additional details. Extending the search algorithm ============================== -Because package metadata is not available through ``sys.path`` searches, or +Because package metadata is not available through :data:`sys.path` searches, or package loaders directly, the metadata for a package is found through import -system `finders`_. To find a distribution package's metadata, -``importlib.metadata`` queries the list of `meta path finders`_ on -`sys.meta_path`_. +system :ref:`finders `. To find a distribution package's metadata, +``importlib.metadata`` queries the list of :term:`meta path finders ` on +:data:`sys.meta_path`. The default ``PathFinder`` for Python includes a hook that calls into ``importlib.metadata.MetadataPathFinder`` for finding distributions @@ -224,7 +224,7 @@ The abstract class :py:class:`importlib.abc.MetaPathFinder` defines the interface expected of finders by Python's import system. ``importlib.metadata`` extends this protocol by looking for an optional ``find_distributions`` callable on the finders from -``sys.meta_path`` and presents this extended interface as the +:data:`sys.meta_path` and presents this extended interface as the ``DistributionFinder`` abstract base class, which defines this abstract method:: @@ -247,20 +247,13 @@ a custom finder, return instances of this derived ``Distribution`` in the .. _`entry point API`: https://setuptools.readthedocs.io/en/latest/pkg_resources.html#entry-points .. _`metadata API`: https://setuptools.readthedocs.io/en/latest/pkg_resources.html#metadata-api -.. _`Python 3.7 and newer`: https://docs.python.org/3/library/importlib.html#module-importlib.resources .. _`importlib_resources`: https://importlib-resources.readthedocs.io/en/latest/index.html -.. _`PEP 566`: https://www.python.org/dev/peps/pep-0566/ -.. _`finders`: https://docs.python.org/3/reference/import.html#finders-and-loaders -.. _`meta path finders`: https://docs.python.org/3/glossary.html#term-meta-path-finder -.. _`sys.meta_path`: https://docs.python.org/3/library/sys.html#sys.meta_path -.. _`pathlib.Path`: https://docs.python.org/3/library/pathlib.html#pathlib.Path .. rubric:: Footnotes .. [#f1] Technically, the returned distribution metadata object is an - `email.message.Message - `_ + :class:`email.message.EmailMessage` instance, but this is an implementation detail, and not part of the stable API. You should only use dictionary-like methods and syntax to access the metadata contents. diff --git a/Doc/reference/import.rst b/Doc/reference/import.rst index 1c98aab7d83aa..ca232007dd7f0 100644 --- a/Doc/reference/import.rst +++ b/Doc/reference/import.rst @@ -202,6 +202,8 @@ named module, the two module objects will *not* be the same. By contrast, reinitialise the module contents by rerunning the module's code. +.. _finders-and-loaders: + Finders and loaders ------------------- diff --git a/Lib/importlib/metadata.py b/Lib/importlib/metadata.py index 831f593277ccd..9d2285cb4d46d 100644 --- a/Lib/importlib/metadata.py +++ b/Lib/importlib/metadata.py @@ -408,8 +408,8 @@ def zip_children(self): names = zip_path.root.namelist() self.joinpath = zip_path.joinpath - return ( - posixpath.split(child)[0] + return dict.fromkeys( + child.split(posixpath.sep, 1)[0] for child in names ) @@ -475,7 +475,6 @@ def _search_paths(cls, name, paths): ) - class PathDistribution(Distribution): def __init__(self, path): """Construct a distribution from a path to the metadata directory. diff --git a/Lib/test/test_importlib/test_zip.py b/Lib/test/test_importlib/test_zip.py index 9568c226af033..18d39f69aee97 100644 --- a/Lib/test/test_importlib/test_zip.py +++ b/Lib/test/test_importlib/test_zip.py @@ -3,11 +3,15 @@ from contextlib import ExitStack from importlib.metadata import ( - distribution, entry_points, files, PackageNotFoundError, version, + distribution, entry_points, files, PackageNotFoundError, + version, distributions, ) from importlib.resources import path +from test.support import requires_zlib + + at requires_zlib class TestZip(unittest.TestCase): root = 'test.test_importlib.data' @@ -46,7 +50,12 @@ def test_files(self): path = str(file.dist.locate_file(file)) assert '.whl/' in path, path + def test_one_distribution(self): + dists = list(distributions(path=sys.path[:1])) + assert len(dists) == 1 + + at requires_zlib class TestEgg(TestZip): def setUp(self): # Find the path to the example-*.egg so we can add it to the front of diff --git a/Misc/NEWS.d/next/Library/2020-09-24-16-45-59.bpo-41855.q6Y1nm.rst b/Misc/NEWS.d/next/Library/2020-09-24-16-45-59.bpo-41855.q6Y1nm.rst new file mode 100644 index 0000000000000..c499c10e66120 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-09-24-16-45-59.bpo-41855.q6Y1nm.rst @@ -0,0 +1,4 @@ +In ``importlib.metadata``, fix issue where multiple children can be returned +from ``FastPath.zip_children()``. Backport of +`python-devs/importlib_metadata#117 +`_. From webhook-mailer at python.org Fri Oct 16 04:34:47 2020 From: webhook-mailer at python.org (Hai Shi) Date: Fri, 16 Oct 2020 08:34:47 -0000 Subject: [Python-checkins] bpo-41919, test_codecs: Move codecs.register calls to setUp() (GH-22513) Message-ID: https://github.com/python/cpython/commit/c9f696cb96d1c362d5cad871f61da520572d9b08 commit: c9f696cb96d1c362d5cad871f61da520572d9b08 branch: master author: Hai Shi committer: GitHub date: 2020-10-16T10:34:15+02:00 summary: bpo-41919, test_codecs: Move codecs.register calls to setUp() (GH-22513) * Move the codecs' (un)register operation to testcases. * Remove _codecs._forget_codec() and _PyCodec_Forget() files: M Lib/test/test_charmapcodec.py M Lib/test/test_codecs.py M Lib/test/test_io.py M Lib/test/test_unicode.py M Modules/_codecsmodule.c M Modules/clinic/_codecsmodule.c.h M Python/codecs.c diff --git a/Lib/test/test_charmapcodec.py b/Lib/test/test_charmapcodec.py index 0d4594d8c05f2..3f628902a1fd3 100644 --- a/Lib/test/test_charmapcodec.py +++ b/Lib/test/test_charmapcodec.py @@ -20,12 +20,15 @@ def codec_search_function(encoding): return tuple(testcodec.getregentry()) return None -codecs.register(codec_search_function) - # test codec's name (see test/testcodec.py) codecname = 'testcodec' class CharmapCodecTest(unittest.TestCase): + + def setUp(self): + codecs.register(codec_search_function) + self.addCleanup(codecs.unregister, codec_search_function) + def test_constructorx(self): self.assertEqual(str(b'abc', codecname), 'abc') self.assertEqual(str(b'xdef', codecname), 'abcdef') diff --git a/Lib/test/test_codecs.py b/Lib/test/test_codecs.py index 09ceef76eb098..9be8281ce5af5 100644 --- a/Lib/test/test_codecs.py +++ b/Lib/test/test_codecs.py @@ -2754,29 +2754,14 @@ def test_uu_invalid(self): def _get_test_codec(codec_name): return _TEST_CODECS.get(codec_name) -codecs.register(_get_test_codec) # Returns None, not usable as a decorator - -try: - # Issue #22166: Also need to clear the internal cache in CPython - from _codecs import _forget_codec -except ImportError: - def _forget_codec(codec_name): - pass class ExceptionChainingTest(unittest.TestCase): def setUp(self): - # There's no way to unregister a codec search function, so we just - # ensure we render this one fairly harmless after the test - # case finishes by using the test case repr as the codec name - # The codecs module normalizes codec names, although this doesn't - # appear to be formally documented... - # We also make sure we use a truly unique id for the custom codec - # to avoid issues with the codec cache when running these tests - # multiple times (e.g. when hunting for refleaks) - unique_id = repr(self) + str(id(self)) - self.codec_name = encodings.normalize_encoding(unique_id).lower() + self.codec_name = 'exception_chaining_test' + codecs.register(_get_test_codec) + self.addCleanup(codecs.unregister, _get_test_codec) # We store the object to raise on the instance because of a bad # interaction between the codec caching (which means we can't @@ -2791,10 +2776,6 @@ def tearDown(self): _TEST_CODECS.pop(self.codec_name, None) # Issue #22166: Also pop from caches to avoid appearance of ref leaks encodings._cache.pop(self.codec_name, None) - try: - _forget_codec(self.codec_name) - except KeyError: - pass def set_codec(self, encode, decode): codec_info = codecs.CodecInfo(encode, decode, diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index 85fac30e300a6..fbaea3aaec3cb 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -2529,10 +2529,6 @@ def lookupTestDecoder(cls, name): streamreader=None, streamwriter=None, incrementaldecoder=cls) -# Register the previous decoder for testing. -# Disabled by default, tests will enable it. -codecs.register(StatefulIncrementalDecoder.lookupTestDecoder) - class StatefulIncrementalDecoderTest(unittest.TestCase): """ @@ -2583,6 +2579,9 @@ def setUp(self): self.testdata = b"AAA\r\nBBB\rCCC\r\nDDD\nEEE\r\n" self.normalized = b"AAA\nBBB\nCCC\nDDD\nEEE\n".decode("ascii") os_helper.unlink(os_helper.TESTFN) + codecs.register(StatefulIncrementalDecoder.lookupTestDecoder) + self.addCleanup(codecs.unregister, + StatefulIncrementalDecoder.lookupTestDecoder) def tearDown(self): os_helper.unlink(os_helper.TESTFN) diff --git a/Lib/test/test_unicode.py b/Lib/test/test_unicode.py index d485bc7ede2b9..90b0965582272 100644 --- a/Lib/test/test_unicode.py +++ b/Lib/test/test_unicode.py @@ -36,7 +36,6 @@ def decode2(input, errors="strict"): return (encode2, decode2, None, None) else: return None -codecs.register(search_function) def duplicate_string(text): """ @@ -58,6 +57,10 @@ class UnicodeTest(string_tests.CommonTest, type2test = str + def setUp(self): + codecs.register(search_function) + self.addCleanup(codecs.unregister, search_function) + def checkequalnofix(self, result, object, methodname, *args): method = getattr(object, methodname) realresult = method(*args) diff --git a/Modules/_codecsmodule.c b/Modules/_codecsmodule.c index 08a3d4ab024cc..2e8cb97fe77c9 100644 --- a/Modules/_codecsmodule.c +++ b/Modules/_codecsmodule.c @@ -160,25 +160,6 @@ _codecs_decode_impl(PyObject *module, PyObject *obj, const char *encoding, /* --- Helpers ------------------------------------------------------------ */ -/*[clinic input] -_codecs._forget_codec - - encoding: str - / - -Purge the named codec from the internal codec lookup cache -[clinic start generated code]*/ - -static PyObject * -_codecs__forget_codec_impl(PyObject *module, const char *encoding) -/*[clinic end generated code: output=0bde9f0a5b084aa2 input=18d5d92d0e386c38]*/ -{ - if (_PyCodec_Forget(encoding) < 0) { - return NULL; - }; - Py_RETURN_NONE; -} - static PyObject *codec_tuple(PyObject *decoded, Py_ssize_t len) @@ -1057,7 +1038,6 @@ static PyMethodDef _codecs_functions[] = { _CODECS_CODE_PAGE_DECODE_METHODDEF _CODECS_REGISTER_ERROR_METHODDEF _CODECS_LOOKUP_ERROR_METHODDEF - _CODECS__FORGET_CODEC_METHODDEF {NULL, NULL} /* sentinel */ }; diff --git a/Modules/clinic/_codecsmodule.c.h b/Modules/clinic/_codecsmodule.c.h index e2ebb6861299e..43378f94f9845 100644 --- a/Modules/clinic/_codecsmodule.c.h +++ b/Modules/clinic/_codecsmodule.c.h @@ -217,43 +217,6 @@ _codecs_decode(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObje return return_value; } -PyDoc_STRVAR(_codecs__forget_codec__doc__, -"_forget_codec($module, encoding, /)\n" -"--\n" -"\n" -"Purge the named codec from the internal codec lookup cache"); - -#define _CODECS__FORGET_CODEC_METHODDEF \ - {"_forget_codec", (PyCFunction)_codecs__forget_codec, METH_O, _codecs__forget_codec__doc__}, - -static PyObject * -_codecs__forget_codec_impl(PyObject *module, const char *encoding); - -static PyObject * -_codecs__forget_codec(PyObject *module, PyObject *arg) -{ - PyObject *return_value = NULL; - const char *encoding; - - if (!PyUnicode_Check(arg)) { - _PyArg_BadArgument("_forget_codec", "argument", "str", arg); - goto exit; - } - Py_ssize_t encoding_length; - encoding = PyUnicode_AsUTF8AndSize(arg, &encoding_length); - if (encoding == NULL) { - goto exit; - } - if (strlen(encoding) != (size_t)encoding_length) { - PyErr_SetString(PyExc_ValueError, "embedded null character"); - goto exit; - } - return_value = _codecs__forget_codec_impl(module, encoding); - -exit: - return return_value; -} - PyDoc_STRVAR(_codecs_escape_decode__doc__, "escape_decode($module, data, errors=None, /)\n" "--\n" @@ -2838,4 +2801,4 @@ _codecs_lookup_error(PyObject *module, PyObject *arg) #ifndef _CODECS_CODE_PAGE_ENCODE_METHODDEF #define _CODECS_CODE_PAGE_ENCODE_METHODDEF #endif /* !defined(_CODECS_CODE_PAGE_ENCODE_METHODDEF) */ -/*[clinic end generated code: output=9a97e2ddf3e69072 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=557c3b37e4c492ac input=a9049054013a1b77]*/ diff --git a/Python/codecs.c b/Python/codecs.c index a8233a73c4ed3..ade14187204f0 100644 --- a/Python/codecs.c +++ b/Python/codecs.c @@ -208,31 +208,6 @@ PyObject *_PyCodec_Lookup(const char *encoding) return NULL; } -int _PyCodec_Forget(const char *encoding) -{ - PyObject *v; - int result; - - PyInterpreterState *interp = _PyInterpreterState_GET(); - if (interp->codec_search_path == NULL) { - return -1; - } - - /* Convert the encoding to a normalized Python string: all - characters are converted to lower case, spaces and hyphens are - replaced with underscores. */ - v = normalizestring(encoding); - if (v == NULL) { - return -1; - } - - /* Drop the named codec from the internal cache */ - result = PyDict_DelItem(interp->codec_search_cache, v); - Py_DECREF(v); - - return result; -} - /* Codec registry encoding check API. */ int PyCodec_KnownEncoding(const char *encoding) From webhook-mailer at python.org Fri Oct 16 10:14:26 2020 From: webhook-mailer at python.org (Necdet Can Atesman) Date: Fri, 16 Oct 2020 14:14:26 -0000 Subject: [Python-checkins] bpo-42011: Update documentation of logging.Filter.filter() (GH-22692) Message-ID: https://github.com/python/cpython/commit/e9959c71185d0850c84e3aba0301fbc238f194a9 commit: e9959c71185d0850c84e3aba0301fbc238f194a9 branch: master author: Necdet Can Atesman committer: GitHub date: 2020-10-16T15:14:07+01:00 summary: bpo-42011: Update documentation of logging.Filter.filter() (GH-22692) files: M Lib/logging/__init__.py diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py index 787cb4eefa106..265e286101e91 100644 --- a/Lib/logging/__init__.py +++ b/Lib/logging/__init__.py @@ -763,8 +763,8 @@ def filter(self, record): """ Determine if the specified record is to be logged. - Is the specified record to be logged? Returns 0 for no, nonzero for - yes. If deemed appropriate, the record may be modified in-place. + Returns True if the record should be logged, or False otherwise. + If deemed appropriate, the record may be modified in-place. """ if self.nlen == 0: return True From webhook-mailer at python.org Fri Oct 16 13:20:38 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Fri, 16 Oct 2020 17:20:38 -0000 Subject: [Python-checkins] bpo-42011: Update documentation of logging.Filter.filter() (GH-22692) (GH-22724) Message-ID: https://github.com/python/cpython/commit/8f7eab788aedcf1a23ef9be767e1b3502a6bbd5c commit: 8f7eab788aedcf1a23ef9be767e1b3502a6bbd5c branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-16T18:20:24+01:00 summary: bpo-42011: Update documentation of logging.Filter.filter() (GH-22692) (GH-22724) (cherry picked from commit e9959c71185d0850c84e3aba0301fbc238f194a9) files: M Lib/logging/__init__.py diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py index f5b7e2eb38910..79e0787af8596 100644 --- a/Lib/logging/__init__.py +++ b/Lib/logging/__init__.py @@ -753,8 +753,8 @@ def filter(self, record): """ Determine if the specified record is to be logged. - Is the specified record to be logged? Returns 0 for no, nonzero for - yes. If deemed appropriate, the record may be modified in-place. + Returns True if the record should be logged, or False otherwise. + If deemed appropriate, the record may be modified in-place. """ if self.nlen == 0: return True From webhook-mailer at python.org Fri Oct 16 13:21:58 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Fri, 16 Oct 2020 17:21:58 -0000 Subject: [Python-checkins] bpo-42011: Update documentation of logging.Filter.filter() (GH-22692) (GH-22725) Message-ID: https://github.com/python/cpython/commit/dff9161821032acfd2588d42d88511ebdbabaaf3 commit: dff9161821032acfd2588d42d88511ebdbabaaf3 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-16T18:21:49+01:00 summary: bpo-42011: Update documentation of logging.Filter.filter() (GH-22692) (GH-22725) (cherry picked from commit e9959c71185d0850c84e3aba0301fbc238f194a9) files: M Lib/logging/__init__.py diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py index 4013e56fc3de4..7b169a16fbb70 100644 --- a/Lib/logging/__init__.py +++ b/Lib/logging/__init__.py @@ -748,8 +748,8 @@ def filter(self, record): """ Determine if the specified record is to be logged. - Is the specified record to be logged? Returns 0 for no, nonzero for - yes. If deemed appropriate, the record may be modified in-place. + Returns True if the record should be logged, or False otherwise. + If deemed appropriate, the record may be modified in-place. """ if self.nlen == 0: return True From webhook-mailer at python.org Fri Oct 16 14:03:36 2020 From: webhook-mailer at python.org (Kevin Adler) Date: Fri, 16 Oct 2020 18:03:36 -0000 Subject: [Python-checkins] closes bpo-42030: Remove legacy AIX dynload support (GH-22717) Message-ID: https://github.com/python/cpython/commit/1dd6d956a3ddf2cf6d4a69241dba8cd1379421b9 commit: 1dd6d956a3ddf2cf6d4a69241dba8cd1379421b9 branch: master author: Kevin Adler committer: GitHub date: 2020-10-16T13:03:28-05:00 summary: closes bpo-42030: Remove legacy AIX dynload support (GH-22717) Since c19c5a6, AIX builds have defaulted to using dynload_shlib over dynload_aix when dlopen is available. This function has been available since AIX 4.3, which went out of support in 2003, the same year the previously referenced commit was made. It has been nearly 20 years since a version of AIX has been supported which has not used dynload_shlib so there's no reason to keep this legacy code around. files: A Misc/NEWS.d/next/Core and Builtins/2020-10-15-21-55-32.bpo-42030.PmU2CA.rst D Python/dynload_aix.c M configure M configure.ac diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-10-15-21-55-32.bpo-42030.PmU2CA.rst b/Misc/NEWS.d/next/Core and Builtins/2020-10-15-21-55-32.bpo-42030.PmU2CA.rst new file mode 100644 index 0000000000000..e8c691d809614 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2020-10-15-21-55-32.bpo-42030.PmU2CA.rst @@ -0,0 +1,3 @@ +Support for the legacy AIX-specific shared library loading support has been +removed. All versions of AIX since 4.3 have supported and defaulted to using +the common Unix mechanism instead. diff --git a/Python/dynload_aix.c b/Python/dynload_aix.c deleted file mode 100644 index 97f7698ef4b2d..0000000000000 --- a/Python/dynload_aix.c +++ /dev/null @@ -1,190 +0,0 @@ - -/* Support for dynamic loading of extension modules */ - -#include "Python.h" -#include "importdl.h" - -#include /* for global errno */ -#include /* for strerror() */ -#include /* for malloc(), free() */ -#include - - -#ifdef AIX_GENUINE_CPLUSPLUS -#include -#define aix_load loadAndInit -#else -#define aix_load load -#endif - - -extern char *Py_GetProgramName(void); - -typedef struct Module { - struct Module *next; - void *entry; -} Module, *ModulePtr; - -const char *_PyImport_DynLoadFiletab[] = {".so", NULL}; - -static int -aix_getoldmodules(void **modlistptr) -{ - ModulePtr modptr, prevmodptr; - struct ld_info *ldiptr; - char *ldibuf; - int errflag, bufsize = 1024; - unsigned int offset; - char *progname = Py_GetProgramName(); - - /* - -- Get the list of loaded modules into ld_info structures. - */ - if ((ldibuf = malloc(bufsize)) == NULL) { - PyErr_SetString(PyExc_ImportError, strerror(errno)); - return -1; - } - while ((errflag = loadquery(L_GETINFO, ldibuf, bufsize)) == -1 - && errno == ENOMEM) { - free(ldibuf); - bufsize += 1024; - if ((ldibuf = malloc(bufsize)) == NULL) { - PyErr_SetString(PyExc_ImportError, strerror(errno)); - return -1; - } - } - if (errflag == -1) { - PyErr_SetString(PyExc_ImportError, strerror(errno)); - return -1; - } - /* - -- Make the modules list from the ld_info structures. - */ - ldiptr = (struct ld_info *)ldibuf; - prevmodptr = NULL; - do { - if (strstr(progname, ldiptr->ldinfo_filename) == NULL && - strstr(ldiptr->ldinfo_filename, "python") == NULL) { - /* - -- Extract only the modules belonging to the main - -- executable + those containing "python" as a - -- substring (like the "python[version]" binary or - -- "libpython[version].a" in case it's a shared lib). - */ - offset = (unsigned int)ldiptr->ldinfo_next; - ldiptr = (struct ld_info *)((char*)ldiptr + offset); - continue; - } - if ((modptr = (ModulePtr)malloc(sizeof(Module))) == NULL) { - PyErr_SetString(PyExc_ImportError, strerror(errno)); - while (*modlistptr) { - modptr = (ModulePtr)*modlistptr; - *modlistptr = (void *)modptr->next; - free(modptr); - } - return -1; - } - modptr->entry = ldiptr->ldinfo_dataorg; - modptr->next = NULL; - if (prevmodptr == NULL) - *modlistptr = (void *)modptr; - else - prevmodptr->next = modptr; - prevmodptr = modptr; - offset = (unsigned int)ldiptr->ldinfo_next; - ldiptr = (struct ld_info *)((char*)ldiptr + offset); - } while (offset); - free(ldibuf); - return 0; -} - - -static void -aix_loaderror(const char *pathname) -{ - - char *message[1024], errbuf[1024]; - PyObject *pathname_ob = NULL; - PyObject *errbuf_ob = NULL; - int i,j; - - struct errtab { - int errNo; - char *errstr; - } load_errtab[] = { - {L_ERROR_TOOMANY, "too many errors, rest skipped."}, - {L_ERROR_NOLIB, "can't load library:"}, - {L_ERROR_UNDEF, "can't find symbol in library:"}, - {L_ERROR_RLDBAD, - "RLD index out of range or bad relocation type:"}, - {L_ERROR_FORMAT, "not a valid, executable xcoff file:"}, - {L_ERROR_MEMBER, - "file not an archive or does not contain requested member:"}, - {L_ERROR_TYPE, "symbol table mismatch:"}, - {L_ERROR_ALIGN, "text alignment in file is wrong."}, - {L_ERROR_SYSTEM, "System error:"}, - {L_ERROR_ERRNO, NULL} - }; - -#define ERRBUF_APPEND(s) strncat(errbuf, s, sizeof(errbuf)-strlen(errbuf)-1) - - PyOS_snprintf(errbuf, sizeof(errbuf), "from module %.200s ", pathname); - - if (!loadquery(L_GETMESSAGES, &message[0], sizeof(message))) { - ERRBUF_APPEND(strerror(errno)); - ERRBUF_APPEND("\n"); - } - for(i = 0; message[i] && *message[i]; i++) { - int nerr = atoi(message[i]); - for (j=0; j < Py_ARRAY_LENGTH(load_errtab); j++) { - if (nerr == load_errtab[j].errNo && load_errtab[j].errstr) - ERRBUF_APPEND(load_errtab[j].errstr); - } - while (Py_ISDIGIT(*message[i])) message[i]++ ; - ERRBUF_APPEND(message[i]); - ERRBUF_APPEND("\n"); - } - /* Subtract 1 from the length to trim off trailing newline */ - errbuf_ob = PyUnicode_DecodeLocaleAndSize(errbuf, strlen(errbuf)-1, "surrogateescape"); - if (errbuf_ob == NULL) - return; - pathname_ob = PyUnicode_DecodeFSDefault(pathname); - if (pathname_ob == NULL) { - Py_DECREF(errbuf_ob); - return; - } - PyErr_SetImportError(errbuf_ob, NULL, pathname_ob); - Py_DECREF(pathname_ob); - Py_DECREF(errbuf_ob); - return; -} - - -dl_funcptr _PyImport_FindSharedFuncptr(const char *prefix, - const char *shortname, - const char *pathname, FILE *fp) -{ - dl_funcptr p; - - /* - -- Invoke load() with L_NOAUTODEFER leaving the imported symbols - -- of the shared module unresolved. Thus we have to resolve them - -- explicitly with loadbind. The new module is loaded, then we - -- resolve its symbols using the list of already loaded modules - -- (only those that belong to the python executable). Get these - -- with loadquery(L_GETINFO). - */ - - static void *staticmodlistptr = NULL; - - if (!staticmodlistptr) - if (aix_getoldmodules(&staticmodlistptr) == -1) - return NULL; - p = (dl_funcptr) aix_load((char *)pathname, L_NOAUTODEFER, 0); - if (p == NULL) { - aix_loaderror(pathname); - return NULL; - } - - return p; -} diff --git a/configure b/configure index 89577d85a4148..88b78947767fa 100755 --- a/configure +++ b/configure @@ -11625,12 +11625,6 @@ $as_echo_n "checking DYNLOADFILE... " >&6; } if test -z "$DYNLOADFILE" then case $ac_sys_system/$ac_sys_release in - AIX*) # Use dynload_shlib.c and dlopen() if we have it; otherwise dynload_aix.c - if test "$ac_cv_func_dlopen" = yes - then DYNLOADFILE="dynload_shlib.o" - else DYNLOADFILE="dynload_aix.o" - fi - ;; hp*|HP*) DYNLOADFILE="dynload_hpux.o";; *) # use dynload_shlib.c and dlopen() if we have it; otherwise stub diff --git a/configure.ac b/configure.ac index 3ec274c576edf..6ffe90a4c426c 100644 --- a/configure.ac +++ b/configure.ac @@ -3622,12 +3622,6 @@ AC_MSG_CHECKING(DYNLOADFILE) if test -z "$DYNLOADFILE" then case $ac_sys_system/$ac_sys_release in - AIX*) # Use dynload_shlib.c and dlopen() if we have it; otherwise dynload_aix.c - if test "$ac_cv_func_dlopen" = yes - then DYNLOADFILE="dynload_shlib.o" - else DYNLOADFILE="dynload_aix.o" - fi - ;; hp*|HP*) DYNLOADFILE="dynload_hpux.o";; *) # use dynload_shlib.c and dlopen() if we have it; otherwise stub From webhook-mailer at python.org Fri Oct 16 14:44:26 2020 From: webhook-mailer at python.org (Zackery Spytz) Date: Fri, 16 Oct 2020 18:44:26 -0000 Subject: [Python-checkins] bpo-40341: Remove some "discouraged solutions" in Doc/faq/programming.rst (GH-22726) Message-ID: https://github.com/python/cpython/commit/a22a19f3548f6064035e7c59a19cda1e9506db92 commit: a22a19f3548f6064035e7c59a19cda1e9506db92 branch: master author: Zackery Spytz committer: GitHub date: 2020-10-16T11:44:17-07:00 summary: bpo-40341: Remove some "discouraged solutions" in Doc/faq/programming.rst (GH-22726) files: M Doc/faq/programming.rst diff --git a/Doc/faq/programming.rst b/Doc/faq/programming.rst index 7bcedb0b5d75b..2d542cfb1dbb4 100644 --- a/Doc/faq/programming.rst +++ b/Doc/faq/programming.rst @@ -942,7 +942,7 @@ There are various techniques. f() -* Use :func:`locals` or :func:`eval` to resolve the function name:: +* Use :func:`locals` to resolve the function name:: def myFunc(): print("hello") @@ -952,12 +952,6 @@ There are various techniques. f = locals()[fname] f() - f = eval(fname) - f() - - Note: Using :func:`eval` is slow and dangerous. If you don't have absolute - control over the contents of the string, someone could pass a string that - resulted in an arbitrary function being executed. Is there an equivalent to Perl's chomp() for removing trailing newlines from strings? ------------------------------------------------------------------------------------- @@ -1381,20 +1375,6 @@ out the element you want. :: ['else', 'sort', 'to', 'something'] -An alternative for the last step is:: - - >>> result = [] - >>> for p in pairs: result.append(p[1]) - -If you find this more legible, you might prefer to use this instead of the final -list comprehension. However, it is almost twice as slow for long lists. Why? -First, the ``append()`` operation has to reallocate memory, and while it uses -some tricks to avoid doing that each time, it still has to do it occasionally, -and that costs quite a bit. Second, the expression "result.append" requires an -extra attribute lookup, and third, there's a speed reduction from having to make -all those function calls. - - Objects ======= From webhook-mailer at python.org Fri Oct 16 16:28:08 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Fri, 16 Oct 2020 20:28:08 -0000 Subject: [Python-checkins] bpo-40341: Remove some "discouraged solutions" in Doc/faq/programming.rst (GH-22726) (GH-22727) Message-ID: https://github.com/python/cpython/commit/0fbddb14dc03f61738af01af88e7d8aa8df07336 commit: 0fbddb14dc03f61738af01af88e7d8aa8df07336 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-16T13:27:59-07:00 summary: bpo-40341: Remove some "discouraged solutions" in Doc/faq/programming.rst (GH-22726) (GH-22727) (cherry picked from commit a22a19f3548f6064035e7c59a19cda1e9506db92) Co-authored-by: Zackery Spytz Co-authored-by: Zackery Spytz files: M Doc/faq/programming.rst diff --git a/Doc/faq/programming.rst b/Doc/faq/programming.rst index 7c6fc51c6b6eb..106450fb78670 100644 --- a/Doc/faq/programming.rst +++ b/Doc/faq/programming.rst @@ -942,7 +942,7 @@ There are various techniques. f() -* Use :func:`locals` or :func:`eval` to resolve the function name:: +* Use :func:`locals` to resolve the function name:: def myFunc(): print("hello") @@ -952,12 +952,6 @@ There are various techniques. f = locals()[fname] f() - f = eval(fname) - f() - - Note: Using :func:`eval` is slow and dangerous. If you don't have absolute - control over the contents of the string, someone could pass a string that - resulted in an arbitrary function being executed. Is there an equivalent to Perl's chomp() for removing trailing newlines from strings? ------------------------------------------------------------------------------------- @@ -1381,20 +1375,6 @@ out the element you want. :: ['else', 'sort', 'to', 'something'] -An alternative for the last step is:: - - >>> result = [] - >>> for p in pairs: result.append(p[1]) - -If you find this more legible, you might prefer to use this instead of the final -list comprehension. However, it is almost twice as slow for long lists. Why? -First, the ``append()`` operation has to reallocate memory, and while it uses -some tricks to avoid doing that each time, it still has to do it occasionally, -and that costs quite a bit. Second, the expression "result.append" requires an -extra attribute lookup, and third, there's a speed reduction from having to make -all those function calls. - - Objects ======= From webhook-mailer at python.org Fri Oct 16 22:34:09 2020 From: webhook-mailer at python.org (Andre Delfino) Date: Sat, 17 Oct 2020 02:34:09 -0000 Subject: [Python-checkins] [doc] Add mentions of PEP 613 (TypeAlias) to docs (GH-22733) Message-ID: https://github.com/python/cpython/commit/975d10a4f8f5d99b01d02fc5f99305a86827f28e commit: 975d10a4f8f5d99b01d02fc5f99305a86827f28e branch: master author: Andre Delfino committer: GitHub date: 2020-10-16T19:34:01-07:00 summary: [doc] Add mentions of PEP 613 (TypeAlias) to docs (GH-22733) files: M Doc/library/typing.rst diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 5fb78bbde69b1..402dd24fde6e0 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -18,7 +18,7 @@ -------------- This module provides runtime support for type hints as specified by -:pep:`484`, :pep:`526`, :pep:`544`, :pep:`586`, :pep:`589`, and :pep:`591`. +:pep:`484`, :pep:`526`, :pep:`544`, :pep:`586`, :pep:`589`, :pep:`591`, and :pep:`613`. The most fundamental support consists of the types :data:`Any`, :data:`Union`, :data:`Tuple`, :data:`Callable`, :class:`TypeVar`, and :class:`Generic`. For full specification please see :pep:`484`. For @@ -500,6 +500,8 @@ These can be used as types in annotations and do not support ``[]``. Factors: TypeAlias = list[int] + See :pep:`613` for more details about explicit type aliases. + .. versionadded:: 3.10 Special forms From webhook-mailer at python.org Sat Oct 17 16:38:30 2020 From: webhook-mailer at python.org (Max Bernstein) Date: Sat, 17 Oct 2020 20:38:30 -0000 Subject: [Python-checkins] bpo-42065: Fix incorrectly formatted _codecs.charmap_decode error message (GH-19940) Message-ID: https://github.com/python/cpython/commit/3635388f52b42e5280229104747962117104c453 commit: 3635388f52b42e5280229104747962117104c453 branch: master author: Max Bernstein committer: GitHub date: 2020-10-17T23:38:21+03:00 summary: bpo-42065: Fix incorrectly formatted _codecs.charmap_decode error message (GH-19940) files: A Misc/NEWS.d/next/Library/2020-10-17-23-17-18.bpo-42065.85BsRA.rst M Lib/test/test_codecs.py M Objects/unicodeobject.c diff --git a/Lib/test/test_codecs.py b/Lib/test/test_codecs.py index 9be8281ce5af5..328a47b2e3766 100644 --- a/Lib/test/test_codecs.py +++ b/Lib/test/test_codecs.py @@ -2197,6 +2197,18 @@ def test_decode_with_int2str_map(self): ("", len(allbytes)) ) + self.assertRaisesRegex(TypeError, + "character mapping must be in range\\(0x110000\\)", + codecs.charmap_decode, + b"\x00\x01\x02", "strict", {0: "A", 1: 'Bb', 2: -2} + ) + + self.assertRaisesRegex(TypeError, + "character mapping must be in range\\(0x110000\\)", + codecs.charmap_decode, + b"\x00\x01\x02", "strict", {0: "A", 1: 'Bb', 2: 999999999} + ) + def test_decode_with_int2int_map(self): a = ord('a') b = ord('b') diff --git a/Misc/NEWS.d/next/Library/2020-10-17-23-17-18.bpo-42065.85BsRA.rst b/Misc/NEWS.d/next/Library/2020-10-17-23-17-18.bpo-42065.85BsRA.rst new file mode 100644 index 0000000000000..83c86c0799ebf --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-10-17-23-17-18.bpo-42065.85BsRA.rst @@ -0,0 +1,3 @@ +Fix an incorrectly formatted error from :meth:`_codecs.charmap_decode` when +called with a mapped value outside the range of valid Unicode code points. +PR by Max Bernstein. diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 01e5c728b383f..c4e73ebd45d20 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -8304,7 +8304,7 @@ charmap_decode_mapping(const char *s, goto Undefined; if (value < 0 || value > MAX_UNICODE) { PyErr_Format(PyExc_TypeError, - "character mapping must be in range(0x%lx)", + "character mapping must be in range(0x%x)", (unsigned long)MAX_UNICODE + 1); goto onError; } From webhook-mailer at python.org Sun Oct 18 02:00:36 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Sun, 18 Oct 2020 06:00:36 -0000 Subject: [Python-checkins] bpo-42065: Fix incorrectly formatted _codecs.charmap_decode error message (GH-19940) Message-ID: https://github.com/python/cpython/commit/6a2aa4994e99ae8e43011a0290646fde4724a003 commit: 6a2aa4994e99ae8e43011a0290646fde4724a003 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-18T09:00:18+03:00 summary: bpo-42065: Fix incorrectly formatted _codecs.charmap_decode error message (GH-19940) (cherry picked from commit 3635388f52b42e5280229104747962117104c453) Co-authored-by: Max Bernstein files: A Misc/NEWS.d/next/Library/2020-10-17-23-17-18.bpo-42065.85BsRA.rst M Lib/test/test_codecs.py M Objects/unicodeobject.c diff --git a/Lib/test/test_codecs.py b/Lib/test/test_codecs.py index 54a3520802a4f..8d112a171d7c4 100644 --- a/Lib/test/test_codecs.py +++ b/Lib/test/test_codecs.py @@ -2183,6 +2183,18 @@ def test_decode_with_int2str_map(self): ("", len(allbytes)) ) + self.assertRaisesRegex(TypeError, + "character mapping must be in range\\(0x110000\\)", + codecs.charmap_decode, + b"\x00\x01\x02", "strict", {0: "A", 1: 'Bb', 2: -2} + ) + + self.assertRaisesRegex(TypeError, + "character mapping must be in range\\(0x110000\\)", + codecs.charmap_decode, + b"\x00\x01\x02", "strict", {0: "A", 1: 'Bb', 2: 999999999} + ) + def test_decode_with_int2int_map(self): a = ord('a') b = ord('b') diff --git a/Misc/NEWS.d/next/Library/2020-10-17-23-17-18.bpo-42065.85BsRA.rst b/Misc/NEWS.d/next/Library/2020-10-17-23-17-18.bpo-42065.85BsRA.rst new file mode 100644 index 0000000000000..83c86c0799ebf --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-10-17-23-17-18.bpo-42065.85BsRA.rst @@ -0,0 +1,3 @@ +Fix an incorrectly formatted error from :meth:`_codecs.charmap_decode` when +called with a mapped value outside the range of valid Unicode code points. +PR by Max Bernstein. diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 3c2383d57c863..d5fbf158d50b4 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -8160,7 +8160,7 @@ charmap_decode_mapping(const char *s, goto Undefined; if (value < 0 || value > MAX_UNICODE) { PyErr_Format(PyExc_TypeError, - "character mapping must be in range(0x%lx)", + "character mapping must be in range(0x%x)", (unsigned long)MAX_UNICODE + 1); goto onError; } From webhook-mailer at python.org Sun Oct 18 02:17:00 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Sun, 18 Oct 2020 06:17:00 -0000 Subject: [Python-checkins] bpo-42065: Fix incorrectly formatted _codecs.charmap_decode error message (GH-19940) Message-ID: https://github.com/python/cpython/commit/400175ad91c1aa8abc6dfce108446e4210d0c157 commit: 400175ad91c1aa8abc6dfce108446e4210d0c157 branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-18T09:16:52+03:00 summary: bpo-42065: Fix incorrectly formatted _codecs.charmap_decode error message (GH-19940) (cherry picked from commit 3635388f52b42e5280229104747962117104c453) Co-authored-by: Max Bernstein files: A Misc/NEWS.d/next/Library/2020-10-17-23-17-18.bpo-42065.85BsRA.rst M Lib/test/test_codecs.py M Objects/unicodeobject.c diff --git a/Lib/test/test_codecs.py b/Lib/test/test_codecs.py index 0fd258ca8abf6..d1faf0126c1ee 100644 --- a/Lib/test/test_codecs.py +++ b/Lib/test/test_codecs.py @@ -2183,6 +2183,18 @@ def test_decode_with_int2str_map(self): ("", len(allbytes)) ) + self.assertRaisesRegex(TypeError, + "character mapping must be in range\\(0x110000\\)", + codecs.charmap_decode, + b"\x00\x01\x02", "strict", {0: "A", 1: 'Bb', 2: -2} + ) + + self.assertRaisesRegex(TypeError, + "character mapping must be in range\\(0x110000\\)", + codecs.charmap_decode, + b"\x00\x01\x02", "strict", {0: "A", 1: 'Bb', 2: 999999999} + ) + def test_decode_with_int2int_map(self): a = ord('a') b = ord('b') diff --git a/Misc/NEWS.d/next/Library/2020-10-17-23-17-18.bpo-42065.85BsRA.rst b/Misc/NEWS.d/next/Library/2020-10-17-23-17-18.bpo-42065.85BsRA.rst new file mode 100644 index 0000000000000..83c86c0799ebf --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-10-17-23-17-18.bpo-42065.85BsRA.rst @@ -0,0 +1,3 @@ +Fix an incorrectly formatted error from :meth:`_codecs.charmap_decode` when +called with a mapped value outside the range of valid Unicode code points. +PR by Max Bernstein. diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 01df54da0051d..a038e0d4c06e7 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -7998,7 +7998,7 @@ charmap_decode_mapping(const char *s, goto Undefined; if (value < 0 || value > MAX_UNICODE) { PyErr_Format(PyExc_TypeError, - "character mapping must be in range(0x%lx)", + "character mapping must be in range(0x%x)", (unsigned long)MAX_UNICODE + 1); goto onError; } From webhook-mailer at python.org Sun Oct 18 10:48:47 2020 From: webhook-mailer at python.org (Ma Lin) Date: Sun, 18 Oct 2020 14:48:47 -0000 Subject: [Python-checkins] bpo-38252: Use 8-byte step to detect ASCII sequence in 64bit Windows build (GH-16334) Message-ID: https://github.com/python/cpython/commit/a0c603cb9d4dbb9909979313a88bcd1f5fde4f62 commit: a0c603cb9d4dbb9909979313a88bcd1f5fde4f62 branch: master author: Ma Lin committer: GitHub date: 2020-10-18T17:48:38+03:00 summary: bpo-38252: Use 8-byte step to detect ASCII sequence in 64bit Windows build (GH-16334) files: A Misc/NEWS.d/next/Windows/2020-10-18-18-43-45.bpo-38252.7Nlepg.rst M Objects/bytes_methods.c M Objects/stringlib/codecs.h M Objects/stringlib/find_max_char.h M Objects/unicodeobject.c diff --git a/Misc/NEWS.d/next/Windows/2020-10-18-18-43-45.bpo-38252.7Nlepg.rst b/Misc/NEWS.d/next/Windows/2020-10-18-18-43-45.bpo-38252.7Nlepg.rst new file mode 100644 index 0000000000000..c103e6cfcc75a --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2020-10-18-18-43-45.bpo-38252.7Nlepg.rst @@ -0,0 +1 @@ +Use 8-byte step to detect ASCII sequence in 64-bit Windows build. diff --git a/Objects/bytes_methods.c b/Objects/bytes_methods.c index 72daa1fdd554e..1512086e6131f 100644 --- a/Objects/bytes_methods.c +++ b/Objects/bytes_methods.c @@ -100,14 +100,14 @@ Return True if B is empty or all characters in B are ASCII,\n\ False otherwise."); // Optimization is copied from ascii_decode in unicodeobject.c -/* Mask to quickly check whether a C 'long' contains a +/* Mask to quickly check whether a C 'size_t' contains a non-ASCII, UTF8-encoded char. */ -#if (SIZEOF_LONG == 8) -# define ASCII_CHAR_MASK 0x8080808080808080UL -#elif (SIZEOF_LONG == 4) -# define ASCII_CHAR_MASK 0x80808080UL +#if (SIZEOF_SIZE_T == 8) +# define ASCII_CHAR_MASK 0x8080808080808080ULL +#elif (SIZEOF_SIZE_T == 4) +# define ASCII_CHAR_MASK 0x80808080U #else -# error C 'long' size should be either 4 or 8! +# error C 'size_t' size should be either 4 or 8! #endif PyObject* @@ -115,20 +115,20 @@ _Py_bytes_isascii(const char *cptr, Py_ssize_t len) { const char *p = cptr; const char *end = p + len; - const char *aligned_end = (const char *) _Py_ALIGN_DOWN(end, SIZEOF_LONG); + const char *aligned_end = (const char *) _Py_ALIGN_DOWN(end, SIZEOF_SIZE_T); while (p < end) { /* Fast path, see in STRINGLIB(utf8_decode) in stringlib/codecs.h for an explanation. */ - if (_Py_IS_ALIGNED(p, SIZEOF_LONG)) { + if (_Py_IS_ALIGNED(p, SIZEOF_SIZE_T)) { /* Help allocation */ const char *_p = p; while (_p < aligned_end) { - unsigned long value = *(const unsigned long *) _p; + size_t value = *(const size_t *) _p; if (value & ASCII_CHAR_MASK) { Py_RETURN_FALSE; } - _p += SIZEOF_LONG; + _p += SIZEOF_SIZE_T; } p = _p; if (_p == end) diff --git a/Objects/stringlib/codecs.h b/Objects/stringlib/codecs.h index 197605b012e5c..b6ca404b1a2d7 100644 --- a/Objects/stringlib/codecs.h +++ b/Objects/stringlib/codecs.h @@ -6,14 +6,14 @@ #include "pycore_bitutils.h" // _Py_bswap32() -/* Mask to quickly check whether a C 'long' contains a +/* Mask to quickly check whether a C 'size_t' contains a non-ASCII, UTF8-encoded char. */ -#if (SIZEOF_LONG == 8) -# define ASCII_CHAR_MASK 0x8080808080808080UL -#elif (SIZEOF_LONG == 4) -# define ASCII_CHAR_MASK 0x80808080UL +#if (SIZEOF_SIZE_T == 8) +# define ASCII_CHAR_MASK 0x8080808080808080ULL +#elif (SIZEOF_SIZE_T == 4) +# define ASCII_CHAR_MASK 0x80808080U #else -# error C 'long' size should be either 4 or 8! +# error C 'size_t' size should be either 4 or 8! #endif /* 10xxxxxx */ @@ -26,7 +26,7 @@ STRINGLIB(utf8_decode)(const char **inptr, const char *end, { Py_UCS4 ch; const char *s = *inptr; - const char *aligned_end = (const char *) _Py_ALIGN_DOWN(end, SIZEOF_LONG); + const char *aligned_end = (const char *) _Py_ALIGN_DOWN(end, SIZEOF_SIZE_T); STRINGLIB_CHAR *p = dest + *outpos; while (s < end) { @@ -36,19 +36,19 @@ STRINGLIB(utf8_decode)(const char **inptr, const char *end, /* Fast path for runs of ASCII characters. Given that common UTF-8 input will consist of an overwhelming majority of ASCII characters, we try to optimize for this case by checking - as many characters as a C 'long' can contain. + as many characters as a C 'size_t' can contain. First, check if we can do an aligned read, as most CPUs have a penalty for unaligned reads. */ - if (_Py_IS_ALIGNED(s, SIZEOF_LONG)) { + if (_Py_IS_ALIGNED(s, SIZEOF_SIZE_T)) { /* Help register allocation */ const char *_s = s; STRINGLIB_CHAR *_p = p; while (_s < aligned_end) { - /* Read a whole long at a time (either 4 or 8 bytes), + /* Read a whole size_t at a time (either 4 or 8 bytes), and do a fast unrolled copy if it only contains ASCII characters. */ - unsigned long value = *(const unsigned long *) _s; + size_t value = *(const size_t *) _s; if (value & ASCII_CHAR_MASK) break; #if PY_LITTLE_ENDIAN @@ -56,14 +56,14 @@ STRINGLIB(utf8_decode)(const char **inptr, const char *end, _p[1] = (STRINGLIB_CHAR)((value >> 8) & 0xFFu); _p[2] = (STRINGLIB_CHAR)((value >> 16) & 0xFFu); _p[3] = (STRINGLIB_CHAR)((value >> 24) & 0xFFu); -# if SIZEOF_LONG == 8 +# if SIZEOF_SIZE_T == 8 _p[4] = (STRINGLIB_CHAR)((value >> 32) & 0xFFu); _p[5] = (STRINGLIB_CHAR)((value >> 40) & 0xFFu); _p[6] = (STRINGLIB_CHAR)((value >> 48) & 0xFFu); _p[7] = (STRINGLIB_CHAR)((value >> 56) & 0xFFu); # endif #else -# if SIZEOF_LONG == 8 +# if SIZEOF_SIZE_T == 8 _p[0] = (STRINGLIB_CHAR)((value >> 56) & 0xFFu); _p[1] = (STRINGLIB_CHAR)((value >> 48) & 0xFFu); _p[2] = (STRINGLIB_CHAR)((value >> 40) & 0xFFu); @@ -79,8 +79,8 @@ STRINGLIB(utf8_decode)(const char **inptr, const char *end, _p[3] = (STRINGLIB_CHAR)(value & 0xFFu); # endif #endif - _s += SIZEOF_LONG; - _p += SIZEOF_LONG; + _s += SIZEOF_SIZE_T; + _p += SIZEOF_SIZE_T; } s = _s; p = _p; diff --git a/Objects/stringlib/find_max_char.h b/Objects/stringlib/find_max_char.h index f4e0a7761d311..3319a46461451 100644 --- a/Objects/stringlib/find_max_char.h +++ b/Objects/stringlib/find_max_char.h @@ -4,14 +4,14 @@ # error "find_max_char.h is specific to Unicode" #endif -/* Mask to quickly check whether a C 'long' contains a +/* Mask to quickly check whether a C 'size_t' contains a non-ASCII, UTF8-encoded char. */ -#if (SIZEOF_LONG == 8) -# define UCS1_ASCII_CHAR_MASK 0x8080808080808080UL -#elif (SIZEOF_LONG == 4) -# define UCS1_ASCII_CHAR_MASK 0x80808080UL +#if (SIZEOF_SIZE_T == 8) +# define UCS1_ASCII_CHAR_MASK 0x8080808080808080ULL +#elif (SIZEOF_SIZE_T == 4) +# define UCS1_ASCII_CHAR_MASK 0x80808080U #else -# error C 'long' size should be either 4 or 8! +# error C 'size_t' size should be either 4 or 8! #endif #if STRINGLIB_SIZEOF_CHAR == 1 @@ -21,17 +21,17 @@ STRINGLIB(find_max_char)(const STRINGLIB_CHAR *begin, const STRINGLIB_CHAR *end) { const unsigned char *p = (const unsigned char *) begin; const unsigned char *aligned_end = - (const unsigned char *) _Py_ALIGN_DOWN(end, SIZEOF_LONG); + (const unsigned char *) _Py_ALIGN_DOWN(end, SIZEOF_SIZE_T); while (p < end) { - if (_Py_IS_ALIGNED(p, SIZEOF_LONG)) { + if (_Py_IS_ALIGNED(p, SIZEOF_SIZE_T)) { /* Help register allocation */ const unsigned char *_p = p; while (_p < aligned_end) { - unsigned long value = *(const unsigned long *) _p; + size_t value = *(const size_t *) _p; if (value & UCS1_ASCII_CHAR_MASK) return 255; - _p += SIZEOF_LONG; + _p += SIZEOF_SIZE_T; } p = _p; if (p == end) diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index c4e73ebd45d20..f963deb0201a4 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -5025,21 +5025,21 @@ PyUnicode_DecodeUTF8(const char *s, #include "stringlib/codecs.h" #include "stringlib/undef.h" -/* Mask to quickly check whether a C 'long' contains a +/* Mask to quickly check whether a C 'size_t' contains a non-ASCII, UTF8-encoded char. */ -#if (SIZEOF_LONG == 8) -# define ASCII_CHAR_MASK 0x8080808080808080UL -#elif (SIZEOF_LONG == 4) -# define ASCII_CHAR_MASK 0x80808080UL +#if (SIZEOF_SIZE_T == 8) +# define ASCII_CHAR_MASK 0x8080808080808080ULL +#elif (SIZEOF_SIZE_T == 4) +# define ASCII_CHAR_MASK 0x80808080U #else -# error C 'long' size should be either 4 or 8! +# error C 'size_t' size should be either 4 or 8! #endif static Py_ssize_t ascii_decode(const char *start, const char *end, Py_UCS1 *dest) { const char *p = start; - const char *aligned_end = (const char *) _Py_ALIGN_DOWN(end, SIZEOF_LONG); + const char *aligned_end = (const char *) _Py_ALIGN_DOWN(end, SIZEOF_SIZE_T); /* * Issue #17237: m68k is a bit different from most architectures in @@ -5049,21 +5049,21 @@ ascii_decode(const char *start, const char *end, Py_UCS1 *dest) * version" will even speed up m68k. */ #if !defined(__m68k__) -#if SIZEOF_LONG <= SIZEOF_VOID_P - assert(_Py_IS_ALIGNED(dest, SIZEOF_LONG)); - if (_Py_IS_ALIGNED(p, SIZEOF_LONG)) { +#if SIZEOF_SIZE_T <= SIZEOF_VOID_P + assert(_Py_IS_ALIGNED(dest, SIZEOF_SIZE_T)); + if (_Py_IS_ALIGNED(p, SIZEOF_SIZE_T)) { /* Fast path, see in STRINGLIB(utf8_decode) for an explanation. */ /* Help allocation */ const char *_p = p; Py_UCS1 * q = dest; while (_p < aligned_end) { - unsigned long value = *(const unsigned long *) _p; + size_t value = *(const size_t *) _p; if (value & ASCII_CHAR_MASK) break; - *((unsigned long *)q) = value; - _p += SIZEOF_LONG; - q += SIZEOF_LONG; + *((size_t *)q) = value; + _p += SIZEOF_SIZE_T; + q += SIZEOF_SIZE_T; } p = _p; while (p < end) { @@ -5078,14 +5078,14 @@ ascii_decode(const char *start, const char *end, Py_UCS1 *dest) while (p < end) { /* Fast path, see in STRINGLIB(utf8_decode) in stringlib/codecs.h for an explanation. */ - if (_Py_IS_ALIGNED(p, SIZEOF_LONG)) { + if (_Py_IS_ALIGNED(p, SIZEOF_SIZE_T)) { /* Help allocation */ const char *_p = p; while (_p < aligned_end) { - unsigned long value = *(const unsigned long *) _p; + size_t value = *(const size_t *) _p; if (value & ASCII_CHAR_MASK) break; - _p += SIZEOF_LONG; + _p += SIZEOF_SIZE_T; } p = _p; if (_p == end) From webhook-mailer at python.org Sun Oct 18 10:49:57 2020 From: webhook-mailer at python.org (scaramallion) Date: Sun, 18 Oct 2020 14:49:57 -0000 Subject: [Python-checkins] bpo-41966: Fix pickling pure datetime.time subclasses (GH-22731) Message-ID: https://github.com/python/cpython/commit/c304c9a7efa8751b5bc7526fa95cd5f30aac2b92 commit: c304c9a7efa8751b5bc7526fa95cd5f30aac2b92 branch: master author: scaramallion committer: GitHub date: 2020-10-18T17:49:48+03:00 summary: bpo-41966: Fix pickling pure datetime.time subclasses (GH-22731) files: A Misc/NEWS.d/next/Library/2020-10-17-07-52-53.bpo-41966.gwEQRZ.rst M Lib/datetime.py M Lib/test/datetimetester.py M Misc/ACKS diff --git a/Lib/datetime.py b/Lib/datetime.py index ea86bcb8b2388..b896b94b0fe0e 100644 --- a/Lib/datetime.py +++ b/Lib/datetime.py @@ -1548,7 +1548,7 @@ def __setstate(self, string, tzinfo): self._tzinfo = tzinfo def __reduce_ex__(self, protocol): - return (time, self._getstate(protocol)) + return (self.__class__, self._getstate(protocol)) def __reduce__(self): return self.__reduce_ex__(2) diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index 8b61c26f9e5c2..775d3151ae290 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -1781,6 +1781,7 @@ def test_pickling_subclass_date(self): green = pickler.dumps(orig, proto) derived = unpickler.loads(green) self.assertEqual(orig, derived) + self.assertTrue(isinstance(derived, SubclassDate)) def test_backdoor_resistance(self): # For fast unpickling, the constructor accepts a pickle byte string. @@ -2308,6 +2309,7 @@ def test_pickling_subclass_datetime(self): green = pickler.dumps(orig, proto) derived = unpickler.loads(green) self.assertEqual(orig, derived) + self.assertTrue(isinstance(derived, SubclassDatetime)) def test_compat_unpickle(self): tests = [ @@ -3357,6 +3359,7 @@ def test_pickling_subclass_time(self): green = pickler.dumps(orig, proto) derived = unpickler.loads(green) self.assertEqual(orig, derived) + self.assertTrue(isinstance(derived, SubclassTime)) def test_compat_unpickle(self): tests = [ diff --git a/Misc/ACKS b/Misc/ACKS index 7f4a9bcbc0f8c..d81d0a255145c 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -787,6 +787,7 @@ Meador Inge Peter Ingebretson Tony Ingraldi John Interrante +Dean Inwood Bob Ippolito Roger Irwin Atsuo Ishimoto diff --git a/Misc/NEWS.d/next/Library/2020-10-17-07-52-53.bpo-41966.gwEQRZ.rst b/Misc/NEWS.d/next/Library/2020-10-17-07-52-53.bpo-41966.gwEQRZ.rst new file mode 100644 index 0000000000000..0e7fad40077be --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-10-17-07-52-53.bpo-41966.gwEQRZ.rst @@ -0,0 +1,2 @@ +Fix pickling pure Python :class:`datetime.time` subclasses. Patch by Dean +Inwood. From webhook-mailer at python.org Sun Oct 18 10:54:15 2020 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Sun, 18 Oct 2020 14:54:15 -0000 Subject: [Python-checkins] bpo-20184: Convert termios to Argument Clinic. (GH-22693) Message-ID: https://github.com/python/cpython/commit/1bcaa81e520e30f920bd69e46fbf1d67a9107ce1 commit: 1bcaa81e520e30f920bd69e46fbf1d67a9107ce1 branch: master author: Serhiy Storchaka committer: GitHub date: 2020-10-18T17:54:06+03:00 summary: bpo-20184: Convert termios to Argument Clinic. (GH-22693) files: A Modules/clinic/termios.c.h M Modules/termios.c diff --git a/Modules/clinic/termios.c.h b/Modules/clinic/termios.c.h new file mode 100644 index 0000000000000..a45c1f64262f8 --- /dev/null +++ b/Modules/clinic/termios.c.h @@ -0,0 +1,225 @@ +/*[clinic input] +preserve +[clinic start generated code]*/ + +PyDoc_STRVAR(termios_tcgetattr__doc__, +"tcgetattr($module, fd, /)\n" +"--\n" +"\n" +"Get the tty attributes for file descriptor fd.\n" +"\n" +"Returns a list [iflag, oflag, cflag, lflag, ispeed, ospeed, cc]\n" +"where cc is a list of the tty special characters (each a string of\n" +"length 1, except the items with indices VMIN and VTIME, which are\n" +"integers when these fields are defined). The interpretation of the\n" +"flags and the speeds as well as the indexing in the cc array must be\n" +"done using the symbolic constants defined in this module."); + +#define TERMIOS_TCGETATTR_METHODDEF \ + {"tcgetattr", (PyCFunction)termios_tcgetattr, METH_O, termios_tcgetattr__doc__}, + +static PyObject * +termios_tcgetattr_impl(PyObject *module, int fd); + +static PyObject * +termios_tcgetattr(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + int fd; + + if (!_PyLong_FileDescriptor_Converter(arg, &fd)) { + goto exit; + } + return_value = termios_tcgetattr_impl(module, fd); + +exit: + return return_value; +} + +PyDoc_STRVAR(termios_tcsetattr__doc__, +"tcsetattr($module, fd, when, attributes, /)\n" +"--\n" +"\n" +"Set the tty attributes for file descriptor fd.\n" +"\n" +"The attributes to be set are taken from the attributes argument, which\n" +"is a list like the one returned by tcgetattr(). The when argument\n" +"determines when the attributes are changed: termios.TCSANOW to\n" +"change immediately, termios.TCSADRAIN to change after transmitting all\n" +"queued output, or termios.TCSAFLUSH to change after transmitting all\n" +"queued output and discarding all queued input."); + +#define TERMIOS_TCSETATTR_METHODDEF \ + {"tcsetattr", (PyCFunction)(void(*)(void))termios_tcsetattr, METH_FASTCALL, termios_tcsetattr__doc__}, + +static PyObject * +termios_tcsetattr_impl(PyObject *module, int fd, int when, PyObject *term); + +static PyObject * +termios_tcsetattr(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + int fd; + int when; + PyObject *term; + + if (!_PyArg_CheckPositional("tcsetattr", nargs, 3, 3)) { + goto exit; + } + if (!_PyLong_FileDescriptor_Converter(args[0], &fd)) { + goto exit; + } + when = _PyLong_AsInt(args[1]); + if (when == -1 && PyErr_Occurred()) { + goto exit; + } + term = args[2]; + return_value = termios_tcsetattr_impl(module, fd, when, term); + +exit: + return return_value; +} + +PyDoc_STRVAR(termios_tcsendbreak__doc__, +"tcsendbreak($module, fd, duration, /)\n" +"--\n" +"\n" +"Send a break on file descriptor fd.\n" +"\n" +"A zero duration sends a break for 0.25-0.5 seconds; a nonzero duration\n" +"has a system dependent meaning."); + +#define TERMIOS_TCSENDBREAK_METHODDEF \ + {"tcsendbreak", (PyCFunction)(void(*)(void))termios_tcsendbreak, METH_FASTCALL, termios_tcsendbreak__doc__}, + +static PyObject * +termios_tcsendbreak_impl(PyObject *module, int fd, int duration); + +static PyObject * +termios_tcsendbreak(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + int fd; + int duration; + + if (!_PyArg_CheckPositional("tcsendbreak", nargs, 2, 2)) { + goto exit; + } + if (!_PyLong_FileDescriptor_Converter(args[0], &fd)) { + goto exit; + } + duration = _PyLong_AsInt(args[1]); + if (duration == -1 && PyErr_Occurred()) { + goto exit; + } + return_value = termios_tcsendbreak_impl(module, fd, duration); + +exit: + return return_value; +} + +PyDoc_STRVAR(termios_tcdrain__doc__, +"tcdrain($module, fd, /)\n" +"--\n" +"\n" +"Wait until all output written to file descriptor fd has been transmitted."); + +#define TERMIOS_TCDRAIN_METHODDEF \ + {"tcdrain", (PyCFunction)termios_tcdrain, METH_O, termios_tcdrain__doc__}, + +static PyObject * +termios_tcdrain_impl(PyObject *module, int fd); + +static PyObject * +termios_tcdrain(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + int fd; + + if (!_PyLong_FileDescriptor_Converter(arg, &fd)) { + goto exit; + } + return_value = termios_tcdrain_impl(module, fd); + +exit: + return return_value; +} + +PyDoc_STRVAR(termios_tcflush__doc__, +"tcflush($module, fd, queue, /)\n" +"--\n" +"\n" +"Discard queued data on file descriptor fd.\n" +"\n" +"The queue selector specifies which queue: termios.TCIFLUSH for the input\n" +"queue, termios.TCOFLUSH for the output queue, or termios.TCIOFLUSH for\n" +"both queues."); + +#define TERMIOS_TCFLUSH_METHODDEF \ + {"tcflush", (PyCFunction)(void(*)(void))termios_tcflush, METH_FASTCALL, termios_tcflush__doc__}, + +static PyObject * +termios_tcflush_impl(PyObject *module, int fd, int queue); + +static PyObject * +termios_tcflush(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + int fd; + int queue; + + if (!_PyArg_CheckPositional("tcflush", nargs, 2, 2)) { + goto exit; + } + if (!_PyLong_FileDescriptor_Converter(args[0], &fd)) { + goto exit; + } + queue = _PyLong_AsInt(args[1]); + if (queue == -1 && PyErr_Occurred()) { + goto exit; + } + return_value = termios_tcflush_impl(module, fd, queue); + +exit: + return return_value; +} + +PyDoc_STRVAR(termios_tcflow__doc__, +"tcflow($module, fd, action, /)\n" +"--\n" +"\n" +"Suspend or resume input or output on file descriptor fd.\n" +"\n" +"The action argument can be termios.TCOOFF to suspend output,\n" +"termios.TCOON to restart output, termios.TCIOFF to suspend input,\n" +"or termios.TCION to restart input."); + +#define TERMIOS_TCFLOW_METHODDEF \ + {"tcflow", (PyCFunction)(void(*)(void))termios_tcflow, METH_FASTCALL, termios_tcflow__doc__}, + +static PyObject * +termios_tcflow_impl(PyObject *module, int fd, int action); + +static PyObject * +termios_tcflow(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + int fd; + int action; + + if (!_PyArg_CheckPositional("tcflow", nargs, 2, 2)) { + goto exit; + } + if (!_PyLong_FileDescriptor_Converter(args[0], &fd)) { + goto exit; + } + action = _PyLong_AsInt(args[1]); + if (action == -1 && PyErr_Occurred()) { + goto exit; + } + return_value = termios_tcflow_impl(module, fd, action); + +exit: + return return_value; +} +/*[clinic end generated code: output=a129179f1e2545cc input=a9049054013a1b77]*/ diff --git a/Modules/termios.c b/Modules/termios.c index 79b60ffaaba4a..a6649598ec171 100644 --- a/Modules/termios.c +++ b/Modules/termios.c @@ -29,6 +29,13 @@ #include #endif +/*[clinic input] +module termios +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=01105c85d0ca7252]*/ + +#include "clinic/termios.c.h" + PyDoc_STRVAR(termios__doc__, "This module provides an interface to the Posix calls for tty I/O control.\n\ For a complete description of these calls, see the Posix or Unix manual\n\ @@ -53,26 +60,26 @@ get_termios_state(PyObject *module) static struct PyModuleDef termiosmodule; -PyDoc_STRVAR(termios_tcgetattr__doc__, -"tcgetattr(fd) -> list_of_attrs\n\ -\n\ -Get the tty attributes for file descriptor fd, as follows:\n\ -[iflag, oflag, cflag, lflag, ispeed, ospeed, cc] where cc is a list\n\ -of the tty special characters (each a string of length 1, except the items\n\ -with indices VMIN and VTIME, which are integers when these fields are\n\ -defined). The interpretation of the flags and the speeds as well as the\n\ -indexing in the cc array must be done using the symbolic constants defined\n\ -in this module."); +/*[clinic input] +termios.tcgetattr + + fd: fildes + / + +Get the tty attributes for file descriptor fd. + +Returns a list [iflag, oflag, cflag, lflag, ispeed, ospeed, cc] +where cc is a list of the tty special characters (each a string of +length 1, except the items with indices VMIN and VTIME, which are +integers when these fields are defined). The interpretation of the +flags and the speeds as well as the indexing in the cc array must be +done using the symbolic constants defined in this module. +[clinic start generated code]*/ static PyObject * -termios_tcgetattr(PyObject *module, PyObject *args) +termios_tcgetattr_impl(PyObject *module, int fd) +/*[clinic end generated code: output=2b3da39db870e629 input=54dad9779ebe74b1]*/ { - int fd; - if (!PyArg_ParseTuple(args, "O&:tcgetattr", - _PyLong_FileDescriptor_Converter, (void*)&fd)) { - return NULL; - } - termiosmodulestate *state = PyModule_GetState(module); struct termios mode; if (tcgetattr(fd, &mode) == -1) { @@ -131,27 +138,28 @@ termios_tcgetattr(PyObject *module, PyObject *args) return NULL; } -PyDoc_STRVAR(termios_tcsetattr__doc__, -"tcsetattr(fd, when, attributes) -> None\n\ -\n\ -Set the tty attributes for file descriptor fd.\n\ -The attributes to be set are taken from the attributes argument, which\n\ -is a list like the one returned by tcgetattr(). The when argument\n\ -determines when the attributes are changed: termios.TCSANOW to\n\ -change immediately, termios.TCSADRAIN to change after transmitting all\n\ -queued output, or termios.TCSAFLUSH to change after transmitting all\n\ -queued output and discarding all queued input. "); +/*[clinic input] +termios.tcsetattr + + fd: fildes + when: int + attributes as term: object + / + +Set the tty attributes for file descriptor fd. + +The attributes to be set are taken from the attributes argument, which +is a list like the one returned by tcgetattr(). The when argument +determines when the attributes are changed: termios.TCSANOW to +change immediately, termios.TCSADRAIN to change after transmitting all +queued output, or termios.TCSAFLUSH to change after transmitting all +queued output and discarding all queued input. +[clinic start generated code]*/ static PyObject * -termios_tcsetattr(PyObject *module, PyObject *args) +termios_tcsetattr_impl(PyObject *module, int fd, int when, PyObject *term) +/*[clinic end generated code: output=bcd2b0a7b98a4bf5 input=5dafabdd5a08f018]*/ { - int fd, when; - PyObject *term; - if (!PyArg_ParseTuple(args, "O&iO:tcsetattr", - _PyLong_FileDescriptor_Converter, &fd, &when, &term)) { - return NULL; - } - if (!PyList_Check(term) || PyList_Size(term) != 7) { PyErr_SetString(PyExc_TypeError, "tcsetattr, arg 3: must be 7 element list"); @@ -209,22 +217,23 @@ termios_tcsetattr(PyObject *module, PyObject *args) Py_RETURN_NONE; } -PyDoc_STRVAR(termios_tcsendbreak__doc__, -"tcsendbreak(fd, duration) -> None\n\ -\n\ -Send a break on file descriptor fd.\n\ -A zero duration sends a break for 0.25-0.5 seconds; a nonzero duration\n\ -has a system dependent meaning."); +/*[clinic input] +termios.tcsendbreak + + fd: fildes + duration: int + / + +Send a break on file descriptor fd. + +A zero duration sends a break for 0.25-0.5 seconds; a nonzero duration +has a system dependent meaning. +[clinic start generated code]*/ static PyObject * -termios_tcsendbreak(PyObject *module, PyObject *args) +termios_tcsendbreak_impl(PyObject *module, int fd, int duration) +/*[clinic end generated code: output=5945f589b5d3ac66 input=dc2f32417691f8ed]*/ { - int fd, duration; - if (!PyArg_ParseTuple(args, "O&i:tcsendbreak", - _PyLong_FileDescriptor_Converter, &fd, &duration)) { - return NULL; - } - termiosmodulestate *state = PyModule_GetState(module); if (tcsendbreak(fd, duration) == -1) { return PyErr_SetFromErrno(state->TermiosError); @@ -233,20 +242,19 @@ termios_tcsendbreak(PyObject *module, PyObject *args) Py_RETURN_NONE; } -PyDoc_STRVAR(termios_tcdrain__doc__, -"tcdrain(fd) -> None\n\ -\n\ -Wait until all output written to file descriptor fd has been transmitted."); +/*[clinic input] +termios.tcdrain + + fd: fildes + / + +Wait until all output written to file descriptor fd has been transmitted. +[clinic start generated code]*/ static PyObject * -termios_tcdrain(PyObject *module, PyObject *args) +termios_tcdrain_impl(PyObject *module, int fd) +/*[clinic end generated code: output=5fd86944c6255955 input=c99241b140b32447]*/ { - int fd; - if (!PyArg_ParseTuple(args, "O&:tcdrain", - _PyLong_FileDescriptor_Converter, &fd)) { - return NULL; - } - termiosmodulestate *state = PyModule_GetState(module); if (tcdrain(fd) == -1) { return PyErr_SetFromErrno(state->TermiosError); @@ -255,23 +263,24 @@ termios_tcdrain(PyObject *module, PyObject *args) Py_RETURN_NONE; } -PyDoc_STRVAR(termios_tcflush__doc__, -"tcflush(fd, queue) -> None\n\ -\n\ -Discard queued data on file descriptor fd.\n\ -The queue selector specifies which queue: termios.TCIFLUSH for the input\n\ -queue, termios.TCOFLUSH for the output queue, or termios.TCIOFLUSH for\n\ -both queues. "); +/*[clinic input] +termios.tcflush + + fd: fildes + queue: int + / + +Discard queued data on file descriptor fd. + +The queue selector specifies which queue: termios.TCIFLUSH for the input +queue, termios.TCOFLUSH for the output queue, or termios.TCIOFLUSH for +both queues. +[clinic start generated code]*/ static PyObject * -termios_tcflush(PyObject *module, PyObject *args) +termios_tcflush_impl(PyObject *module, int fd, int queue) +/*[clinic end generated code: output=2424f80312ec2f21 input=0f7d08122ddc07b5]*/ { - int fd, queue; - if (!PyArg_ParseTuple(args, "O&i:tcflush", - _PyLong_FileDescriptor_Converter, &fd, &queue)) { - return NULL; - } - termiosmodulestate *state = PyModule_GetState(module); if (tcflush(fd, queue) == -1) { return PyErr_SetFromErrno(state->TermiosError); @@ -280,23 +289,24 @@ termios_tcflush(PyObject *module, PyObject *args) Py_RETURN_NONE; } -PyDoc_STRVAR(termios_tcflow__doc__, -"tcflow(fd, action) -> None\n\ -\n\ -Suspend or resume input or output on file descriptor fd.\n\ -The action argument can be termios.TCOOFF to suspend output,\n\ -termios.TCOON to restart output, termios.TCIOFF to suspend input,\n\ -or termios.TCION to restart input."); +/*[clinic input] +termios.tcflow + + fd: fildes + action: int + / + +Suspend or resume input or output on file descriptor fd. + +The action argument can be termios.TCOOFF to suspend output, +termios.TCOON to restart output, termios.TCIOFF to suspend input, +or termios.TCION to restart input. +[clinic start generated code]*/ static PyObject * -termios_tcflow(PyObject *module, PyObject *args) +termios_tcflow_impl(PyObject *module, int fd, int action) +/*[clinic end generated code: output=afd10928e6ea66eb input=c6aff0640b6efd9c]*/ { - int fd, action; - if (!PyArg_ParseTuple(args, "O&i:tcflow", - _PyLong_FileDescriptor_Converter, &fd, &action)) { - return NULL; - } - termiosmodulestate *state = PyModule_GetState(module); if (tcflow(fd, action) == -1) { return PyErr_SetFromErrno(state->TermiosError); @@ -307,18 +317,12 @@ termios_tcflow(PyObject *module, PyObject *args) static PyMethodDef termios_methods[] = { - {"tcgetattr", termios_tcgetattr, - METH_VARARGS, termios_tcgetattr__doc__}, - {"tcsetattr", termios_tcsetattr, - METH_VARARGS, termios_tcsetattr__doc__}, - {"tcsendbreak", termios_tcsendbreak, - METH_VARARGS, termios_tcsendbreak__doc__}, - {"tcdrain", termios_tcdrain, - METH_VARARGS, termios_tcdrain__doc__}, - {"tcflush", termios_tcflush, - METH_VARARGS, termios_tcflush__doc__}, - {"tcflow", termios_tcflow, - METH_VARARGS, termios_tcflow__doc__}, + TERMIOS_TCGETATTR_METHODDEF + TERMIOS_TCSETATTR_METHODDEF + TERMIOS_TCSENDBREAK_METHODDEF + TERMIOS_TCDRAIN_METHODDEF + TERMIOS_TCFLUSH_METHODDEF + TERMIOS_TCFLOW_METHODDEF {NULL, NULL} }; From webhook-mailer at python.org Sun Oct 18 11:33:05 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Sun, 18 Oct 2020 15:33:05 -0000 Subject: [Python-checkins] bpo-41966: Fix pickling pure datetime.time subclasses (GH-22731) (GH-22747) Message-ID: https://github.com/python/cpython/commit/a055ced9d43630cadc3c1d5edab0884f2c5131ea commit: a055ced9d43630cadc3c1d5edab0884f2c5131ea branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-18T18:32:56+03:00 summary: bpo-41966: Fix pickling pure datetime.time subclasses (GH-22731) (GH-22747) (cherry picked from commit c304c9a7efa8751b5bc7526fa95cd5f30aac2b92) Co-authored-by: scaramallion files: A Misc/NEWS.d/next/Library/2020-10-17-07-52-53.bpo-41966.gwEQRZ.rst M Lib/datetime.py M Lib/test/datetimetester.py M Misc/ACKS diff --git a/Lib/datetime.py b/Lib/datetime.py index 2294ac2b6873a..e508d996fb1ed 100644 --- a/Lib/datetime.py +++ b/Lib/datetime.py @@ -1579,7 +1579,7 @@ def __setstate(self, string, tzinfo): self._tzinfo = tzinfo def __reduce_ex__(self, protocol): - return (time, self._getstate(protocol)) + return (self.__class__, self._getstate(protocol)) def __reduce__(self): return self.__reduce_ex__(2) diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index a9741d6d4062f..b37ef91707872 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -1781,6 +1781,7 @@ def test_pickling_subclass_date(self): green = pickler.dumps(orig, proto) derived = unpickler.loads(green) self.assertEqual(orig, derived) + self.assertTrue(isinstance(derived, SubclassDate)) def test_backdoor_resistance(self): # For fast unpickling, the constructor accepts a pickle byte string. @@ -2308,6 +2309,7 @@ def test_pickling_subclass_datetime(self): green = pickler.dumps(orig, proto) derived = unpickler.loads(green) self.assertEqual(orig, derived) + self.assertTrue(isinstance(derived, SubclassDatetime)) def test_compat_unpickle(self): tests = [ @@ -3357,6 +3359,7 @@ def test_pickling_subclass_time(self): green = pickler.dumps(orig, proto) derived = unpickler.loads(green) self.assertEqual(orig, derived) + self.assertTrue(isinstance(derived, SubclassTime)) def test_compat_unpickle(self): tests = [ diff --git a/Misc/ACKS b/Misc/ACKS index a16f15a74ebdc..3125600284daf 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -781,6 +781,7 @@ Meador Inge Peter Ingebretson Tony Ingraldi John Interrante +Dean Inwood Bob Ippolito Roger Irwin Atsuo Ishimoto diff --git a/Misc/NEWS.d/next/Library/2020-10-17-07-52-53.bpo-41966.gwEQRZ.rst b/Misc/NEWS.d/next/Library/2020-10-17-07-52-53.bpo-41966.gwEQRZ.rst new file mode 100644 index 0000000000000..0e7fad40077be --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-10-17-07-52-53.bpo-41966.gwEQRZ.rst @@ -0,0 +1,2 @@ +Fix pickling pure Python :class:`datetime.time` subclasses. Patch by Dean +Inwood. From webhook-mailer at python.org Sun Oct 18 11:33:32 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Sun, 18 Oct 2020 15:33:32 -0000 Subject: [Python-checkins] bpo-41966: Fix pickling pure datetime.time subclasses (GH-22731) (GH-22748) Message-ID: https://github.com/python/cpython/commit/1040299e9283609f3de0f6e32a0d43458fe7f4f6 commit: 1040299e9283609f3de0f6e32a0d43458fe7f4f6 branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-18T18:33:28+03:00 summary: bpo-41966: Fix pickling pure datetime.time subclasses (GH-22731) (GH-22748) (cherry picked from commit c304c9a7efa8751b5bc7526fa95cd5f30aac2b92) Co-authored-by: scaramallion files: A Misc/NEWS.d/next/Library/2020-10-17-07-52-53.bpo-41966.gwEQRZ.rst M Lib/datetime.py M Lib/test/datetimetester.py M Misc/ACKS diff --git a/Lib/datetime.py b/Lib/datetime.py index 96d112c197069..9777e88df6ca4 100644 --- a/Lib/datetime.py +++ b/Lib/datetime.py @@ -1545,7 +1545,7 @@ def __setstate(self, string, tzinfo): self._tzinfo = tzinfo def __reduce_ex__(self, protocol): - return (time, self._getstate(protocol)) + return (self.__class__, self._getstate(protocol)) def __reduce__(self): return self.__reduce_ex__(2) diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index d1a3c2ff9abc2..bb94f8fb3b195 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -1750,6 +1750,7 @@ def test_pickling_subclass_date(self): green = pickler.dumps(orig, proto) derived = unpickler.loads(green) self.assertEqual(orig, derived) + self.assertTrue(isinstance(derived, SubclassDate)) def test_backdoor_resistance(self): # For fast unpickling, the constructor accepts a pickle byte string. @@ -2277,6 +2278,7 @@ def test_pickling_subclass_datetime(self): green = pickler.dumps(orig, proto) derived = unpickler.loads(green) self.assertEqual(orig, derived) + self.assertTrue(isinstance(derived, SubclassDatetime)) def test_compat_unpickle(self): tests = [ @@ -3326,6 +3328,7 @@ def test_pickling_subclass_time(self): green = pickler.dumps(orig, proto) derived = unpickler.loads(green) self.assertEqual(orig, derived) + self.assertTrue(isinstance(derived, SubclassTime)) def test_compat_unpickle(self): tests = [ diff --git a/Misc/ACKS b/Misc/ACKS index 8d355da8fde05..5584d5d54a0b2 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -759,6 +759,7 @@ Meador Inge Peter Ingebretson Tony Ingraldi John Interrante +Dean Inwood Bob Ippolito Roger Irwin Atsuo Ishimoto diff --git a/Misc/NEWS.d/next/Library/2020-10-17-07-52-53.bpo-41966.gwEQRZ.rst b/Misc/NEWS.d/next/Library/2020-10-17-07-52-53.bpo-41966.gwEQRZ.rst new file mode 100644 index 0000000000000..0e7fad40077be --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-10-17-07-52-53.bpo-41966.gwEQRZ.rst @@ -0,0 +1,2 @@ +Fix pickling pure Python :class:`datetime.time` subclasses. Patch by Dean +Inwood. From webhook-mailer at python.org Sun Oct 18 11:39:30 2020 From: webhook-mailer at python.org (Shantanu) Date: Sun, 18 Oct 2020 15:39:30 -0000 Subject: [Python-checkins] 3.9 whatsnew: fix bpo issue for AST change (GH-22742) Message-ID: https://github.com/python/cpython/commit/67f04878debbcec60191cddbddf9c83e8b9b36fe commit: 67f04878debbcec60191cddbddf9c83e8b9b36fe branch: master author: Shantanu <12621235+hauntsaninja at users.noreply.github.com> committer: GitHub date: 2020-10-18T18:39:26+03:00 summary: 3.9 whatsnew: fix bpo issue for AST change (GH-22742) files: M Doc/whatsnew/3.9.rst diff --git a/Doc/whatsnew/3.9.rst b/Doc/whatsnew/3.9.rst index 22fdbd565a166..89017981f6d49 100644 --- a/Doc/whatsnew/3.9.rst +++ b/Doc/whatsnew/3.9.rst @@ -766,7 +766,7 @@ Deprecated and will be removed in future Python versions. ``value`` itself should be used instead of ``Index(value)``. ``Tuple(slices, Load())`` should be used instead of ``ExtSlice(slices)``. - (Contributed by Serhiy Storchaka in :issue:`32892`.) + (Contributed by Serhiy Storchaka in :issue:`34822`.) * :mod:`ast` classes ``Suite``, ``Param``, ``AugLoad`` and ``AugStore`` are considered deprecated and will be removed in future Python versions. From webhook-mailer at python.org Sun Oct 18 13:01:27 2020 From: webhook-mailer at python.org (Irit Katriel) Date: Sun, 18 Oct 2020 17:01:27 -0000 Subject: [Python-checkins] bpo-28660: Make TextWrapper break long words on hyphens (GH-22721) Message-ID: https://github.com/python/cpython/commit/b81c833ab51fb7d7f0f8eaace37f60ef7455aa85 commit: b81c833ab51fb7d7f0f8eaace37f60ef7455aa85 branch: master author: Irit Katriel committer: GitHub date: 2020-10-18T20:01:15+03:00 summary: bpo-28660: Make TextWrapper break long words on hyphens (GH-22721) files: A Misc/NEWS.d/next/Library/2020-10-16-16-08-04.bpo-28660.eX9pvD.rst M Lib/test/test_textwrap.py M Lib/textwrap.py diff --git a/Lib/test/test_textwrap.py b/Lib/test/test_textwrap.py index ed97f70ba1fa4..dfbc2b93dfc0d 100644 --- a/Lib/test/test_textwrap.py +++ b/Lib/test/test_textwrap.py @@ -640,6 +640,78 @@ def test_max_lines_long(self): max_lines=4) +class LongWordWithHyphensTestCase(BaseTestCase): + def setUp(self): + self.wrapper = TextWrapper() + self.text1 = '''\ +We used enyzme 2-succinyl-6-hydroxy-2,4-cyclohexadiene-1-carboxylate synthase. +''' + self.text2 = '''\ +1234567890-1234567890--this_is_a_very_long_option_indeed-good-bye" +''' + + def test_break_long_words_on_hyphen(self): + expected = ['We used enyzme 2-succinyl-6-hydroxy-2,4-', + 'cyclohexadiene-1-carboxylate synthase.'] + self.check_wrap(self.text1, 50, expected) + + expected = ['We used', 'enyzme 2-', 'succinyl-', '6-hydroxy-', '2,4-', + 'cyclohexad', 'iene-1-', 'carboxylat', 'e', 'synthase.'] + self.check_wrap(self.text1, 10, expected) + + expected = ['1234567890', '-123456789', '0--this_is', '_a_very_lo', + 'ng_option_', 'indeed-', 'good-bye"'] + self.check_wrap(self.text2, 10, expected) + + def test_break_long_words_not_on_hyphen(self): + expected = ['We used enyzme 2-succinyl-6-hydroxy-2,4-cyclohexad', + 'iene-1-carboxylate synthase.'] + self.check_wrap(self.text1, 50, expected, break_on_hyphens=False) + + expected = ['We used', 'enyzme 2-s', 'uccinyl-6-', 'hydroxy-2,', + '4-cyclohex', 'adiene-1-c', 'arboxylate', 'synthase.'] + self.check_wrap(self.text1, 10, expected, break_on_hyphens=False) + + expected = ['1234567890', '-123456789', '0--this_is', '_a_very_lo', + 'ng_option_', 'indeed-', 'good-bye"'] + self.check_wrap(self.text2, 10, expected) + + def test_break_on_hyphen_but_not_long_words(self): + expected = ['We used enyzme', + '2-succinyl-6-hydroxy-2,4-cyclohexadiene-1-carboxylate', + 'synthase.'] + + self.check_wrap(self.text1, 50, expected, break_long_words=False) + + expected = ['We used', 'enyzme', + '2-succinyl-6-hydroxy-2,4-cyclohexadiene-1-carboxylate', + 'synthase.'] + self.check_wrap(self.text1, 10, expected, break_long_words=False) + + expected = ['1234567890', '-123456789', '0--this_is', '_a_very_lo', + 'ng_option_', 'indeed-', 'good-bye"'] + self.check_wrap(self.text2, 10, expected) + + + def test_do_not_break_long_words_or_on_hyphens(self): + expected = ['We used enyzme', + '2-succinyl-6-hydroxy-2,4-cyclohexadiene-1-carboxylate', + 'synthase.'] + self.check_wrap(self.text1, 50, expected, + break_long_words=False, + break_on_hyphens=False) + + expected = ['We used', 'enyzme', + '2-succinyl-6-hydroxy-2,4-cyclohexadiene-1-carboxylate', + 'synthase.'] + self.check_wrap(self.text1, 10, expected, + break_long_words=False, + break_on_hyphens=False) + + expected = ['1234567890', '-123456789', '0--this_is', '_a_very_lo', + 'ng_option_', 'indeed-', 'good-bye"'] + self.check_wrap(self.text2, 10, expected) + class IndentTestCases(BaseTestCase): # called before each test method diff --git a/Lib/textwrap.py b/Lib/textwrap.py index 30e693c8de035..841de9baecf5d 100644 --- a/Lib/textwrap.py +++ b/Lib/textwrap.py @@ -215,8 +215,16 @@ def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width): # If we're allowed to break long words, then do so: put as much # of the next chunk onto the current line as will fit. if self.break_long_words: - cur_line.append(reversed_chunks[-1][:space_left]) - reversed_chunks[-1] = reversed_chunks[-1][space_left:] + end = space_left + chunk = reversed_chunks[-1] + if self.break_on_hyphens and len(chunk) > space_left: + # break after last hyphen, but only if there are + # non-hyphens before it + hyphen = chunk.rfind('-', 0, space_left) + if hyphen > 0 and any(c != '-' for c in chunk[:hyphen]): + end = hyphen + 1 + cur_line.append(chunk[:end]) + reversed_chunks[-1] = chunk[end:] # Otherwise, we have to preserve the long word intact. Only add # it to the current line if there's nothing already there -- diff --git a/Misc/NEWS.d/next/Library/2020-10-16-16-08-04.bpo-28660.eX9pvD.rst b/Misc/NEWS.d/next/Library/2020-10-16-16-08-04.bpo-28660.eX9pvD.rst new file mode 100644 index 0000000000000..d67993492f9ff --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-10-16-16-08-04.bpo-28660.eX9pvD.rst @@ -0,0 +1 @@ +:func:`textwrap.wrap` now attempts to break long words after hyphens when ``break_long_words=True`` and ``break_on_hyphens=True``. From webhook-mailer at python.org Sun Oct 18 13:17:01 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Sun, 18 Oct 2020 17:17:01 -0000 Subject: [Python-checkins] 3.9 whatsnew: fix bpo issue for AST change (GH-22742) (GH-22749) Message-ID: https://github.com/python/cpython/commit/e5c7ac7a22629cc1d96cef256433f03088b2dd8a commit: e5c7ac7a22629cc1d96cef256433f03088b2dd8a branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-18T20:16:54+03:00 summary: 3.9 whatsnew: fix bpo issue for AST change (GH-22742) (GH-22749) (cherry picked from commit 67f04878debbcec60191cddbddf9c83e8b9b36fe) Co-authored-by: Shantanu <12621235+hauntsaninja at users.noreply.github.com> files: M Doc/whatsnew/3.9.rst diff --git a/Doc/whatsnew/3.9.rst b/Doc/whatsnew/3.9.rst index a91494a918061..da0718561537b 100644 --- a/Doc/whatsnew/3.9.rst +++ b/Doc/whatsnew/3.9.rst @@ -891,7 +891,7 @@ Deprecated and will be removed in future Python versions. ``value`` itself should be used instead of ``Index(value)``. ``Tuple(slices, Load())`` should be used instead of ``ExtSlice(slices)``. - (Contributed by Serhiy Storchaka in :issue:`32892`.) + (Contributed by Serhiy Storchaka in :issue:`34822`.) * :mod:`ast` classes ``Suite``, ``Param``, ``AugLoad`` and ``AugStore`` are considered deprecated and will be removed in future Python versions. From webhook-mailer at python.org Sun Oct 18 16:48:39 2020 From: webhook-mailer at python.org (Anthony Sottile) Date: Sun, 18 Oct 2020 20:48:39 -0000 Subject: [Python-checkins] bpo-40492: Fix --outfile with relative path when the program changes it working dir (GH-19910) Message-ID: https://github.com/python/cpython/commit/3c0ac18504cfeed822439024339d5717f42bdd66 commit: 3c0ac18504cfeed822439024339d5717f42bdd66 branch: master author: Anthony Sottile committer: GitHub date: 2020-10-18T23:48:31+03:00 summary: bpo-40492: Fix --outfile with relative path when the program changes it working dir (GH-19910) files: A Misc/NEWS.d/next/Library/2020-05-04-12-16-00.bpo-40492.ONk9Na.rst M Lib/cProfile.py M Lib/profile.py M Lib/test/test_profile.py diff --git a/Lib/cProfile.py b/Lib/cProfile.py index 4f202038d6126..59b4699feb506 100755 --- a/Lib/cProfile.py +++ b/Lib/cProfile.py @@ -152,6 +152,11 @@ def main(): (options, args) = parser.parse_args() sys.argv[:] = args + # The script that we're profiling may chdir, so capture the absolute path + # to the output file at startup. + if options.outfile is not None: + options.outfile = os.path.abspath(options.outfile) + if len(args) > 0: if options.module: code = "run_module(modname, run_name='__main__')" diff --git a/Lib/profile.py b/Lib/profile.py index aad458dc951f4..5cb017ed83009 100755 --- a/Lib/profile.py +++ b/Lib/profile.py @@ -571,6 +571,11 @@ def main(): (options, args) = parser.parse_args() sys.argv[:] = args + # The script that we're profiling may chdir, so capture the absolute path + # to the output file at startup. + if options.outfile is not None: + options.outfile = os.path.abspath(options.outfile) + if len(args) > 0: if options.module: import runpy diff --git a/Lib/test/test_profile.py b/Lib/test/test_profile.py index 738be85bedf3c..1bdf30acbb54b 100644 --- a/Lib/test/test_profile.py +++ b/Lib/test/test_profile.py @@ -7,7 +7,7 @@ from difflib import unified_diff from io import StringIO from test.support import run_unittest -from test.support.os_helper import TESTFN, unlink +from test.support.os_helper import TESTFN, unlink, temp_dir, change_cwd from contextlib import contextmanager import profile @@ -112,6 +112,20 @@ def test_run_profile_as_module(self): assert_python_ok('-m', self.profilermodule.__name__, '-m', 'timeit', '-n', '1') + def test_output_file_when_changing_directory(self): + with temp_dir() as tmpdir, change_cwd(tmpdir): + os.mkdir('dest') + with open('demo.py', 'w') as f: + f.write('import os; os.chdir("dest")') + + assert_python_ok( + '-m', self.profilermodule.__name__, + '-o', 'out.pstats', + 'demo.py', + ) + + self.assertTrue(os.path.exists('out.pstats')) + def regenerate_expected_output(filename, cls): filename = filename.rstrip('co') diff --git a/Misc/NEWS.d/next/Library/2020-05-04-12-16-00.bpo-40492.ONk9Na.rst b/Misc/NEWS.d/next/Library/2020-05-04-12-16-00.bpo-40492.ONk9Na.rst new file mode 100644 index 0000000000000..86bc08c79e21e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-05-04-12-16-00.bpo-40492.ONk9Na.rst @@ -0,0 +1,3 @@ +Fix ``--outfile`` for :mod:`cProfile` / :mod:`profile` not writing the output +file in the original directory when the program being profiled changes the +working directory. PR by Anthony Sottile. From webhook-mailer at python.org Sun Oct 18 17:06:42 2020 From: webhook-mailer at python.org (Irit Katriel) Date: Sun, 18 Oct 2020 21:06:42 -0000 Subject: [Python-checkins] [3.8] bpo-32498: Improve exception message on passing bytes to urllib.parse.unquote (GH-22746) Message-ID: https://github.com/python/cpython/commit/1a3f7c042a32fb813835243bd7f96e47c665bfdc commit: 1a3f7c042a32fb813835243bd7f96e47c665bfdc branch: 3.8 author: Irit Katriel committer: GitHub date: 2020-10-19T00:06:34+03:00 summary: [3.8] bpo-32498: Improve exception message on passing bytes to urllib.parse.unquote (GH-22746) files: A Misc/NEWS.d/next/Library/2020-10-18-19-22-39.bpo-32498.MoqSgo.rst M Lib/test/test_urllib.py M Lib/urllib/parse.py diff --git a/Lib/test/test_urllib.py b/Lib/test/test_urllib.py index 1c247c5d1543a..8626687151185 100644 --- a/Lib/test/test_urllib.py +++ b/Lib/test/test_urllib.py @@ -1245,6 +1245,12 @@ def test_unquote_with_unicode(self): self.assertEqual(expect, result, "using unquote(): %r != %r" % (expect, result)) + def test_unquoting_with_bytes_input(self): + # Bytes not supported yet + with self.assertRaisesRegex(TypeError, 'Expected str, got bytes'): + given = b'bl\xc3\xa5b\xc3\xa6rsyltet\xc3\xb8y' + urllib.parse.unquote(given) + class urlencode_Tests(unittest.TestCase): """Tests for urlencode()""" diff --git a/Lib/urllib/parse.py b/Lib/urllib/parse.py index e2b6f133e1cd9..95be7181133b4 100644 --- a/Lib/urllib/parse.py +++ b/Lib/urllib/parse.py @@ -631,6 +631,8 @@ def unquote(string, encoding='utf-8', errors='replace'): unquote('abc%20def') -> 'abc def'. """ + if isinstance(string, bytes): + raise TypeError('Expected str, got bytes') if '%' not in string: string.split return string diff --git a/Misc/NEWS.d/next/Library/2020-10-18-19-22-39.bpo-32498.MoqSgo.rst b/Misc/NEWS.d/next/Library/2020-10-18-19-22-39.bpo-32498.MoqSgo.rst new file mode 100644 index 0000000000000..3083ded758a8f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-10-18-19-22-39.bpo-32498.MoqSgo.rst @@ -0,0 +1,3 @@ +Clearer exception message when passing an argument of type bytes to +:func:`urllib.parse.unquote`. This is only for 3.8; in 3.9 and later this +function accepts bytes inputs as well. PR by Irit Katriel. From webhook-mailer at python.org Sun Oct 18 17:14:23 2020 From: webhook-mailer at python.org (Anthony Sottile) Date: Sun, 18 Oct 2020 21:14:23 -0000 Subject: [Python-checkins] bpo-40492: Fix --outfile with relative path when the program changes it working dir (GH-19910) Message-ID: https://github.com/python/cpython/commit/1c5a65723e623be32e246f58b8797a263f616295 commit: 1c5a65723e623be32e246f58b8797a263f616295 branch: 3.8 author: Anthony Sottile committer: GitHub date: 2020-10-19T00:14:18+03:00 summary: bpo-40492: Fix --outfile with relative path when the program changes it working dir (GH-19910) (cherry picked from commit 3c0ac18504cfeed822439024339d5717f42bdd66) files: A Misc/NEWS.d/next/Library/2020-05-04-12-16-00.bpo-40492.ONk9Na.rst M Lib/cProfile.py M Lib/profile.py M Lib/test/test_profile.py diff --git a/Lib/cProfile.py b/Lib/cProfile.py index 369d02e22e24a..47aacf9e2d494 100755 --- a/Lib/cProfile.py +++ b/Lib/cProfile.py @@ -168,6 +168,11 @@ def main(): (options, args) = parser.parse_args() sys.argv[:] = args + # The script that we're profiling may chdir, so capture the absolute path + # to the output file at startup. + if options.outfile is not None: + options.outfile = os.path.abspath(options.outfile) + if len(args) > 0: if options.module: code = "run_module(modname, run_name='__main__')" diff --git a/Lib/profile.py b/Lib/profile.py index 1346297c04a59..9df4435c5ae83 100755 --- a/Lib/profile.py +++ b/Lib/profile.py @@ -587,6 +587,11 @@ def main(): (options, args) = parser.parse_args() sys.argv[:] = args + # The script that we're profiling may chdir, so capture the absolute path + # to the output file at startup. + if options.outfile is not None: + options.outfile = os.path.abspath(options.outfile) + if len(args) > 0: if options.module: import runpy diff --git a/Lib/test/test_profile.py b/Lib/test/test_profile.py index 01a8a6eaf5a23..233649899ec20 100644 --- a/Lib/test/test_profile.py +++ b/Lib/test/test_profile.py @@ -6,7 +6,7 @@ import os from difflib import unified_diff from io import StringIO -from test.support import TESTFN, run_unittest, unlink +from test.support import TESTFN, run_unittest, unlink, temp_dir, change_cwd from contextlib import contextmanager import profile @@ -111,6 +111,20 @@ def test_run_profile_as_module(self): assert_python_ok('-m', self.profilermodule.__name__, '-m', 'timeit', '-n', '1') + def test_output_file_when_changing_directory(self): + with temp_dir() as tmpdir, change_cwd(tmpdir): + os.mkdir('dest') + with open('demo.py', 'w') as f: + f.write('import os; os.chdir("dest")') + + assert_python_ok( + '-m', self.profilermodule.__name__, + '-o', 'out.pstats', + 'demo.py', + ) + + self.assertTrue(os.path.exists('out.pstats')) + def regenerate_expected_output(filename, cls): filename = filename.rstrip('co') diff --git a/Misc/NEWS.d/next/Library/2020-05-04-12-16-00.bpo-40492.ONk9Na.rst b/Misc/NEWS.d/next/Library/2020-05-04-12-16-00.bpo-40492.ONk9Na.rst new file mode 100644 index 0000000000000..86bc08c79e21e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-05-04-12-16-00.bpo-40492.ONk9Na.rst @@ -0,0 +1,3 @@ +Fix ``--outfile`` for :mod:`cProfile` / :mod:`profile` not writing the output +file in the original directory when the program being profiled changes the +working directory. PR by Anthony Sottile. From webhook-mailer at python.org Sun Oct 18 17:16:27 2020 From: webhook-mailer at python.org (Anthony Sottile) Date: Sun, 18 Oct 2020 21:16:27 -0000 Subject: [Python-checkins] bpo-40492: Fix --outfile with relative path when the program changes it working dir (GH-19910) Message-ID: https://github.com/python/cpython/commit/7c949020ef2520d7a7cbc978f0b34423e6c2a94c commit: 7c949020ef2520d7a7cbc978f0b34423e6c2a94c branch: 3.9 author: Anthony Sottile committer: GitHub date: 2020-10-19T00:16:22+03:00 summary: bpo-40492: Fix --outfile with relative path when the program changes it working dir (GH-19910) (cherry picked from commit 3c0ac18504cfeed822439024339d5717f42bdd66) files: A Misc/NEWS.d/next/Library/2020-05-04-12-16-00.bpo-40492.ONk9Na.rst M Lib/cProfile.py M Lib/profile.py M Lib/test/test_profile.py diff --git a/Lib/cProfile.py b/Lib/cProfile.py index 4f202038d6126..59b4699feb506 100755 --- a/Lib/cProfile.py +++ b/Lib/cProfile.py @@ -152,6 +152,11 @@ def main(): (options, args) = parser.parse_args() sys.argv[:] = args + # The script that we're profiling may chdir, so capture the absolute path + # to the output file at startup. + if options.outfile is not None: + options.outfile = os.path.abspath(options.outfile) + if len(args) > 0: if options.module: code = "run_module(modname, run_name='__main__')" diff --git a/Lib/profile.py b/Lib/profile.py index aad458dc951f4..5cb017ed83009 100755 --- a/Lib/profile.py +++ b/Lib/profile.py @@ -571,6 +571,11 @@ def main(): (options, args) = parser.parse_args() sys.argv[:] = args + # The script that we're profiling may chdir, so capture the absolute path + # to the output file at startup. + if options.outfile is not None: + options.outfile = os.path.abspath(options.outfile) + if len(args) > 0: if options.module: import runpy diff --git a/Lib/test/test_profile.py b/Lib/test/test_profile.py index 01a8a6eaf5a23..233649899ec20 100644 --- a/Lib/test/test_profile.py +++ b/Lib/test/test_profile.py @@ -6,7 +6,7 @@ import os from difflib import unified_diff from io import StringIO -from test.support import TESTFN, run_unittest, unlink +from test.support import TESTFN, run_unittest, unlink, temp_dir, change_cwd from contextlib import contextmanager import profile @@ -111,6 +111,20 @@ def test_run_profile_as_module(self): assert_python_ok('-m', self.profilermodule.__name__, '-m', 'timeit', '-n', '1') + def test_output_file_when_changing_directory(self): + with temp_dir() as tmpdir, change_cwd(tmpdir): + os.mkdir('dest') + with open('demo.py', 'w') as f: + f.write('import os; os.chdir("dest")') + + assert_python_ok( + '-m', self.profilermodule.__name__, + '-o', 'out.pstats', + 'demo.py', + ) + + self.assertTrue(os.path.exists('out.pstats')) + def regenerate_expected_output(filename, cls): filename = filename.rstrip('co') diff --git a/Misc/NEWS.d/next/Library/2020-05-04-12-16-00.bpo-40492.ONk9Na.rst b/Misc/NEWS.d/next/Library/2020-05-04-12-16-00.bpo-40492.ONk9Na.rst new file mode 100644 index 0000000000000..86bc08c79e21e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-05-04-12-16-00.bpo-40492.ONk9Na.rst @@ -0,0 +1,3 @@ +Fix ``--outfile`` for :mod:`cProfile` / :mod:`profile` not writing the output +file in the original directory when the program being profiled changes the +working directory. PR by Anthony Sottile. From webhook-mailer at python.org Sun Oct 18 21:14:33 2020 From: webhook-mailer at python.org (Batuhan Taskaya) Date: Mon, 19 Oct 2020 01:14:33 -0000 Subject: [Python-checkins] bpo-40484: Document compiler flags under AST module (GH-19885) Message-ID: https://github.com/python/cpython/commit/155938907c2b3df71608ddeaa0a43d2ec1f2c699 commit: 155938907c2b3df71608ddeaa0a43d2ec1f2c699 branch: master author: Batuhan Taskaya committer: GitHub date: 2020-10-19T02:14:11+01:00 summary: bpo-40484: Document compiler flags under AST module (GH-19885) Co-authored-by: Pablo Galindo Co-authored-by: Shantanu files: M Doc/library/ast.rst M Doc/library/functions.rst diff --git a/Doc/library/ast.rst b/Doc/library/ast.rst index 62138efcce911..f95ee1d5c69c2 100644 --- a/Doc/library/ast.rst +++ b/Doc/library/ast.rst @@ -1756,6 +1756,34 @@ and classes for traversing abstract syntax trees: Added the *indent* option. +.. _ast-compiler-flags: + +Compiler Flags +-------------- + +The following flags may be passed to :func:`compile` in order to change +effects on the compilation of a program: + +.. data:: PyCF_ALLOW_TOP_LEVEL_AWAIT + + Enables support for top-level ``await``, ``async for``, ``async with`` + and async comprehensions. + + .. versionadded:: 3.8 + +.. data:: PyCF_ONLY_AST + + Generates and returns an abstract syntax tree instead of returning a + compiled code object. + +.. data:: PyCF_TYPE_COMMENTS + + Enables support for :pep:`484` and :pep:`526` style type comments + (``# type: ``, ``# type: ignore ``). + + .. versionadded:: 3.8 + + .. _ast-cli: Command-Line Usage diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index 263c52a63dea8..a052e724c5ebe 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -259,26 +259,24 @@ are always available. They are listed here in alphabetical order. interactive statement (in the latter case, expression statements that evaluate to something other than ``None`` will be printed). - The optional arguments *flags* and *dont_inherit* control which :ref:`future - statements ` affect the compilation of *source*. If neither - is present (or both are zero) the code is compiled with those future - statements that are in effect in the code that is calling :func:`compile`. If the - *flags* argument is given and *dont_inherit* is not (or is zero) then the - future statements specified by the *flags* argument are used in addition to - those that would be used anyway. If *dont_inherit* is a non-zero integer then - the *flags* argument is it -- the future statements in effect around the call - to compile are ignored. - - Future statements are specified by bits which can be bitwise ORed together to - specify multiple statements. The bitfield required to specify a given feature - can be found as the :attr:`~__future__._Feature.compiler_flag` attribute on - the :class:`~__future__._Feature` instance in the :mod:`__future__` module. - - The optional argument *flags* also controls whether the compiled source is - allowed to contain top-level ``await``, ``async for`` and ``async with``. - When the bit ``ast.PyCF_ALLOW_TOP_LEVEL_AWAIT`` is set, the return code - object has ``CO_COROUTINE`` set in ``co_code``, and can be interactively - executed via ``await eval(code_object)``. + The optional argument *flags* and *dont_inherit* controls which + :ref:`compiler options ` should be activated + and which :ref:`future features ` should be allowed. If neither + is present (or both are zero) the code is compiled with the same flags that + affect the code that is calling :func:`compile`. If the *flags* + argument is given and *dont_inherit* is not (or is zero) then the compiler + options and the future statements specified by the *flags* argument are used + in addition to those that would be used anyway. If *dont_inherit* is a + non-zero integer then the *flags* argument is it -- the flags (future + features and compiler options) in the surrounding code are ignored. + + Compiler options and future statements are specified by bits which can be + bitwise ORed together to specify multiple options. The bitfield required to + specify a given future feature can be found as the + :attr:`~__future__._Feature.compiler_flag` attribute on the + :class:`~__future__._Feature` instance in the :mod:`__future__` module. + :ref:`Compiler flags ` can be found in :mod:`ast` + module, with ``PyCF_`` prefix. The argument *optimize* specifies the optimization level of the compiler; the default value of ``-1`` selects the optimization level of the interpreter as From webhook-mailer at python.org Mon Oct 19 03:34:00 2020 From: webhook-mailer at python.org (Bar Harel) Date: Mon, 19 Oct 2020 07:34:00 -0000 Subject: [Python-checkins] bpo-19270: Fixed sched.scheduler.cancel to cancel correct event (GH-22729) Message-ID: https://github.com/python/cpython/commit/5368c2b6e23660cbce7e38dc68f859c66ac349ee commit: 5368c2b6e23660cbce7e38dc68f859c66ac349ee branch: master author: Bar Harel committer: GitHub date: 2020-10-19T10:33:43+03:00 summary: bpo-19270: Fixed sched.scheduler.cancel to cancel correct event (GH-22729) files: A Misc/NEWS.d/next/Library/2020-10-16-22-48-01.bpo-19270.jd_gkA.rst M Lib/sched.py M Lib/test/test_sched.py diff --git a/Lib/sched.py b/Lib/sched.py index ff87874a3a4b7..14613cf29874d 100644 --- a/Lib/sched.py +++ b/Lib/sched.py @@ -26,23 +26,19 @@ import time import heapq from collections import namedtuple +from itertools import count import threading from time import monotonic as _time __all__ = ["scheduler"] -class Event(namedtuple('Event', 'time, priority, action, argument, kwargs')): - __slots__ = [] - def __eq__(s, o): return (s.time, s.priority) == (o.time, o.priority) - def __lt__(s, o): return (s.time, s.priority) < (o.time, o.priority) - def __le__(s, o): return (s.time, s.priority) <= (o.time, o.priority) - def __gt__(s, o): return (s.time, s.priority) > (o.time, o.priority) - def __ge__(s, o): return (s.time, s.priority) >= (o.time, o.priority) - +Event = namedtuple('Event', 'time, priority, sequence, action, argument, kwargs') Event.time.__doc__ = ('''Numeric type compatible with the return value of the timefunc function passed to the constructor.''') Event.priority.__doc__ = ('''Events scheduled for the same time will be executed in the order of their priority.''') +Event.sequence.__doc__ = ('''A continually increasing sequence number that + separates events if time and priority are equal.''') Event.action.__doc__ = ('''Executing the event means executing action(*argument, **kwargs)''') Event.argument.__doc__ = ('''argument is a sequence holding the positional @@ -61,6 +57,7 @@ def __init__(self, timefunc=_time, delayfunc=time.sleep): self._lock = threading.RLock() self.timefunc = timefunc self.delayfunc = delayfunc + self._sequence_generator = count() def enterabs(self, time, priority, action, argument=(), kwargs=_sentinel): """Enter a new event in the queue at an absolute time. @@ -71,8 +68,10 @@ def enterabs(self, time, priority, action, argument=(), kwargs=_sentinel): """ if kwargs is _sentinel: kwargs = {} - event = Event(time, priority, action, argument, kwargs) + with self._lock: + event = Event(time, priority, next(self._sequence_generator), + action, argument, kwargs) heapq.heappush(self._queue, event) return event # The ID @@ -136,7 +135,8 @@ def run(self, blocking=True): with lock: if not q: break - time, priority, action, argument, kwargs = q[0] + (time, priority, sequence, action, + argument, kwargs) = q[0] now = timefunc() if time > now: delay = True diff --git a/Lib/test/test_sched.py b/Lib/test/test_sched.py index 491d7b3a745b4..7ae7baae85e2c 100644 --- a/Lib/test/test_sched.py +++ b/Lib/test/test_sched.py @@ -142,6 +142,17 @@ def test_cancel_concurrent(self): self.assertTrue(q.empty()) self.assertEqual(timer.time(), 4) + def test_cancel_correct_event(self): + # bpo-19270 + events = [] + scheduler = sched.scheduler() + scheduler.enterabs(1, 1, events.append, ("a",)) + b = scheduler.enterabs(1, 1, events.append, ("b",)) + scheduler.enterabs(1, 1, events.append, ("c",)) + scheduler.cancel(b) + scheduler.run() + self.assertEqual(events, ["a", "c"]) + def test_empty(self): l = [] fun = lambda x: l.append(x) diff --git a/Misc/NEWS.d/next/Library/2020-10-16-22-48-01.bpo-19270.jd_gkA.rst b/Misc/NEWS.d/next/Library/2020-10-16-22-48-01.bpo-19270.jd_gkA.rst new file mode 100644 index 0000000000000..6330a91a44c54 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-10-16-22-48-01.bpo-19270.jd_gkA.rst @@ -0,0 +1,2 @@ +:meth:`sched.scheduler.cancel()` will now cancel the correct event, if two +events with same priority are scheduled for the same time. Patch by Bar Harel. From webhook-mailer at python.org Mon Oct 19 08:20:37 2020 From: webhook-mailer at python.org (Mark Shannon) Date: Mon, 19 Oct 2020 12:20:37 -0000 Subject: [Python-checkins] Correct name of bytecode in change note. (GH-22723) Message-ID: https://github.com/python/cpython/commit/b580ed1d9d55461d8dde027411b90be26cae131e commit: b580ed1d9d55461d8dde027411b90be26cae131e branch: master author: Mark Shannon committer: GitHub date: 2020-10-19T13:20:33+01:00 summary: Correct name of bytecode in change note. (GH-22723) files: M Misc/NEWS.d/3.9.0a2.rst diff --git a/Misc/NEWS.d/3.9.0a2.rst b/Misc/NEWS.d/3.9.0a2.rst index 1fd23b763e2a1..226ea0d3df224 100644 --- a/Misc/NEWS.d/3.9.0a2.rst +++ b/Misc/NEWS.d/3.9.0a2.rst @@ -236,7 +236,7 @@ coroutine of an asynchronous generator. Removed WITH_CLEANUP_START, WITH_CLEANUP_FINISH, BEGIN_FINALLY, END_FINALLY, CALL_FINALLY and POP_FINALLY bytecodes. Replaced with RERAISE and -WITH_EXCEPT_FINISH bytecodes. The compiler now generates different code for +WITH_EXCEPT_START bytecodes. The compiler now generates different code for exceptional and non-exceptional branches for 'with' and 'try-except' statements. For 'try-finally' statements the 'finally' block is replicated for each exit from the 'try' body. From webhook-mailer at python.org Mon Oct 19 10:53:02 2020 From: webhook-mailer at python.org (Saiyang Gou) Date: Mon, 19 Oct 2020 14:53:02 -0000 Subject: [Python-checkins] bpo-41192: Fix some broken anchors for audit event entries (#21310) Message-ID: https://github.com/python/cpython/commit/ebc8c3828779374b9be4fae5c8ffc0059d36ac8c commit: ebc8c3828779374b9be4fae5c8ffc0059d36ac8c branch: master author: Saiyang Gou committer: GitHub date: 2020-10-19T10:52:42-04:00 summary: bpo-41192: Fix some broken anchors for audit event entries (#21310) files: M Doc/library/ctypes.rst M Doc/library/poplib.rst M Doc/using/cmdline.rst diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index 2d6c6d0a1c3c5..c172d5377636a 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -1618,7 +1618,7 @@ They are instances of a private class: ``ctypes.seh_exception`` with argument ``code`` will be raised, allowing an audit hook to replace the exception with its own. -.. audit-event:: ctypes.call_function func_pointer,arguments ctype-foreign-functions +.. audit-event:: ctypes.call_function func_pointer,arguments foreign-functions Some ways to invoke foreign function calls may raise an auditing event ``ctypes.call_function`` with arguments ``function pointer`` and ``arguments``. @@ -2545,4 +2545,3 @@ Arrays and pointers Returns the object to which to pointer points. Assigning to this attribute changes the pointer to point to the assigned object. - diff --git a/Doc/library/poplib.rst b/Doc/library/poplib.rst index 2f349b35b7e0f..9bf9212d917a5 100644 --- a/Doc/library/poplib.rst +++ b/Doc/library/poplib.rst @@ -67,7 +67,7 @@ The :mod:`poplib` module provides two classes: .. audit-event:: poplib.connect self,host,port poplib.POP3_SSL - .. audit-event:: poplib.putline self,line popplib.POP3_SSL + .. audit-event:: poplib.putline self,line poplib.POP3_SSL All commands will raise an :ref:`auditing event ` ``poplib.putline`` with arguments ``self`` and ``line``, diff --git a/Doc/using/cmdline.rst b/Doc/using/cmdline.rst index 8c65d99ef31f9..603b0e105fb43 100644 --- a/Doc/using/cmdline.rst +++ b/Doc/using/cmdline.rst @@ -558,7 +558,7 @@ conflict. the interactive session. You can also change the prompts :data:`sys.ps1` and :data:`sys.ps2` and the hook :data:`sys.__interactivehook__` in this file. - .. audit-event:: cpython.run_startup filename PYTHONSTARTUP + .. audit-event:: cpython.run_startup filename envvar-PYTHONSTARTUP Raises an :ref:`auditing event ` ``cpython.run_startup`` with the filename as the argument when called on startup. From webhook-mailer at python.org Mon Oct 19 11:39:30 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Mon, 19 Oct 2020 15:39:30 -0000 Subject: [Python-checkins] bpo-41192: Fix some broken anchors for audit event entries (GH-21310) (GH-22767) Message-ID: https://github.com/python/cpython/commit/2ee6ef98b4cf94f4c21cc4301d4e226b70809c7e commit: 2ee6ef98b4cf94f4c21cc4301d4e226b70809c7e branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-19T11:39:25-04:00 summary: bpo-41192: Fix some broken anchors for audit event entries (GH-21310) (GH-22767) (cherry picked from commit ebc8c3828779374b9be4fae5c8ffc0059d36ac8c) Co-authored-by: Saiyang Gou files: M Doc/library/ctypes.rst M Doc/library/poplib.rst M Doc/using/cmdline.rst diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index 2d6c6d0a1c3c5..c172d5377636a 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -1618,7 +1618,7 @@ They are instances of a private class: ``ctypes.seh_exception`` with argument ``code`` will be raised, allowing an audit hook to replace the exception with its own. -.. audit-event:: ctypes.call_function func_pointer,arguments ctype-foreign-functions +.. audit-event:: ctypes.call_function func_pointer,arguments foreign-functions Some ways to invoke foreign function calls may raise an auditing event ``ctypes.call_function`` with arguments ``function pointer`` and ``arguments``. @@ -2545,4 +2545,3 @@ Arrays and pointers Returns the object to which to pointer points. Assigning to this attribute changes the pointer to point to the assigned object. - diff --git a/Doc/library/poplib.rst b/Doc/library/poplib.rst index 2f349b35b7e0f..9bf9212d917a5 100644 --- a/Doc/library/poplib.rst +++ b/Doc/library/poplib.rst @@ -67,7 +67,7 @@ The :mod:`poplib` module provides two classes: .. audit-event:: poplib.connect self,host,port poplib.POP3_SSL - .. audit-event:: poplib.putline self,line popplib.POP3_SSL + .. audit-event:: poplib.putline self,line poplib.POP3_SSL All commands will raise an :ref:`auditing event ` ``poplib.putline`` with arguments ``self`` and ``line``, diff --git a/Doc/using/cmdline.rst b/Doc/using/cmdline.rst index f91ab020da5cf..95342f3a510e1 100644 --- a/Doc/using/cmdline.rst +++ b/Doc/using/cmdline.rst @@ -555,7 +555,7 @@ conflict. the interactive session. You can also change the prompts :data:`sys.ps1` and :data:`sys.ps2` and the hook :data:`sys.__interactivehook__` in this file. - .. audit-event:: cpython.run_startup filename PYTHONSTARTUP + .. audit-event:: cpython.run_startup filename envvar-PYTHONSTARTUP Raises an :ref:`auditing event ` ``cpython.run_startup`` with the filename as the argument when called on startup. From webhook-mailer at python.org Mon Oct 19 11:44:36 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Mon, 19 Oct 2020 15:44:36 -0000 Subject: [Python-checkins] Correct name of bytecode in change note. (GH-22723) (#22765) Message-ID: https://github.com/python/cpython/commit/8f0b8b5749fe5f8273afe96ab5e96e2d25811469 commit: 8f0b8b5749fe5f8273afe96ab5e96e2d25811469 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-19T17:44:25+02:00 summary: Correct name of bytecode in change note. (GH-22723) (#22765) (cherry picked from commit b580ed1d9d55461d8dde027411b90be26cae131e) Co-authored-by: Mark Shannon files: M Misc/NEWS.d/3.9.0a2.rst diff --git a/Misc/NEWS.d/3.9.0a2.rst b/Misc/NEWS.d/3.9.0a2.rst index 1fd23b763e2a1..226ea0d3df224 100644 --- a/Misc/NEWS.d/3.9.0a2.rst +++ b/Misc/NEWS.d/3.9.0a2.rst @@ -236,7 +236,7 @@ coroutine of an asynchronous generator. Removed WITH_CLEANUP_START, WITH_CLEANUP_FINISH, BEGIN_FINALLY, END_FINALLY, CALL_FINALLY and POP_FINALLY bytecodes. Replaced with RERAISE and -WITH_EXCEPT_FINISH bytecodes. The compiler now generates different code for +WITH_EXCEPT_START bytecodes. The compiler now generates different code for exceptional and non-exceptional branches for 'with' and 'try-except' statements. For 'try-finally' statements the 'finally' block is replicated for each exit from the 'try' body. From webhook-mailer at python.org Mon Oct 19 11:55:30 2020 From: webhook-mailer at python.org (Steve Dower) Date: Mon, 19 Oct 2020 15:55:30 -0000 Subject: [Python-checkins] bpo-39107: Updated Tcl and Tk to 8.6.10 in Windows installer (GH-22405) Message-ID: https://github.com/python/cpython/commit/985f0ab3ad5e8e9a8d7fc53026c38390b1f2b466 commit: 985f0ab3ad5e8e9a8d7fc53026c38390b1f2b466 branch: master author: Steve Dower committer: GitHub date: 2020-10-19T16:55:10+01:00 summary: bpo-39107: Updated Tcl and Tk to 8.6.10 in Windows installer (GH-22405) files: A Misc/NEWS.d/next/Windows/2020-09-24-23-09-40.bpo-39107.GbUZvD.rst M PCbuild/get_externals.bat M PCbuild/tcltk.props diff --git a/Misc/NEWS.d/next/Windows/2020-09-24-23-09-40.bpo-39107.GbUZvD.rst b/Misc/NEWS.d/next/Windows/2020-09-24-23-09-40.bpo-39107.GbUZvD.rst new file mode 100644 index 0000000000000..889ad481404e4 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2020-09-24-23-09-40.bpo-39107.GbUZvD.rst @@ -0,0 +1 @@ +Update Tcl and Tk to 8.6.10 in Windows installer. diff --git a/PCbuild/get_externals.bat b/PCbuild/get_externals.bat index 3bb281904f6a6..799c46641f5ac 100644 --- a/PCbuild/get_externals.bat +++ b/PCbuild/get_externals.bat @@ -55,8 +55,8 @@ 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 "%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% tcl-core-8.6.10.0 +if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tk-8.6.10.0 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tix-8.4.3.6 set libraries=%libraries% xz-5.2.2 set libraries=%libraries% zlib-1.2.11 @@ -78,7 +78,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 "%IncludeTkinter%"=="false" set binaries=%binaries% tcltk-8.6.9.0 +if NOT "%IncludeTkinter%"=="false" set binaries=%binaries% tcltk-8.6.10.0 if NOT "%IncludeSSLSrc%"=="false" set binaries=%binaries% nasm-2.11.06 for %%b in (%binaries%) do ( diff --git a/PCbuild/tcltk.props b/PCbuild/tcltk.props index 7fcd3e1c618c4..fb61bee514721 100644 --- a/PCbuild/tcltk.props +++ b/PCbuild/tcltk.props @@ -4,7 +4,7 @@ 8 6 - 9 + 10 0 $(TclMajorVersion) $(TclMinorVersion) @@ -36,8 +36,7 @@ Release Debug $(BuildDirTop)_$(TclMachine) - $(BuildDirTop)_VC13 - $(BuildDirTop)_VC13 + $(BuildDirTop)_VC13 $(BuildDirTop)_VC12 $(BuildDirTop)_VC11 $(BuildDirTop)_VC10 From webhook-mailer at python.org Mon Oct 19 14:13:57 2020 From: webhook-mailer at python.org (Ronald Oussoren) Date: Mon, 19 Oct 2020 18:13:57 -0000 Subject: [Python-checkins] bpo-42051: Reject XML entity declarations in plist files (#22760) Message-ID: https://github.com/python/cpython/commit/05ee790f4d1cd8725a90b54268fc1dfe5b4d1fa2 commit: 05ee790f4d1cd8725a90b54268fc1dfe5b4d1fa2 branch: master author: Ronald Oussoren committer: GitHub date: 2020-10-19T20:13:49+02:00 summary: bpo-42051: Reject XML entity declarations in plist files (#22760) files: A Misc/NEWS.d/next/Security/2020-10-19-10-56-27.bpo-42051.EU_B7u.rst M Lib/plistlib.py M Lib/test/test_plistlib.py diff --git a/Lib/plistlib.py b/Lib/plistlib.py index aff5fe36ca38b..ba7ac1936479f 100644 --- a/Lib/plistlib.py +++ b/Lib/plistlib.py @@ -173,9 +173,16 @@ def parse(self, fileobj): self.parser.StartElementHandler = self.handle_begin_element self.parser.EndElementHandler = self.handle_end_element self.parser.CharacterDataHandler = self.handle_data + self.parser.EntityDeclHandler = self.handle_entity_decl self.parser.ParseFile(fileobj) return self.root + def handle_entity_decl(self, entity_name, is_parameter_entity, value, base, system_id, public_id, notation_name): + # Reject plist files with entity declarations to avoid XML vulnerabilies in expat. + # Regular plist files don't contain those declerations, and Apple's plutil tool does not + # accept them either. + raise InvalidFileException("XML entity declarations are not supported in plist files") + def handle_begin_element(self, element, attrs): self.data = [] handler = getattr(self, "begin_" + element, None) diff --git a/Lib/test/test_plistlib.py b/Lib/test/test_plistlib.py index e5c9b5b6b2cfe..cb071da1f33a1 100644 --- a/Lib/test/test_plistlib.py +++ b/Lib/test/test_plistlib.py @@ -106,6 +106,19 @@ AAABOQ=='''), } +XML_PLIST_WITH_ENTITY=b'''\ + + + ]> + + + A + &entity; + + +''' + class TestPlistlib(unittest.TestCase): @@ -524,6 +537,11 @@ def test_modified_uid_huge(self): with self.assertRaises(OverflowError): plistlib.dumps(huge_uid, fmt=plistlib.FMT_BINARY) + def test_xml_plist_with_entity_decl(self): + with self.assertRaisesRegex(plistlib.InvalidFileException, + "XML entity declarations are not supported"): + plistlib.loads(XML_PLIST_WITH_ENTITY, fmt=plistlib.FMT_XML) + class TestBinaryPlistlib(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Security/2020-10-19-10-56-27.bpo-42051.EU_B7u.rst b/Misc/NEWS.d/next/Security/2020-10-19-10-56-27.bpo-42051.EU_B7u.rst new file mode 100644 index 0000000000000..e865ed12a0387 --- /dev/null +++ b/Misc/NEWS.d/next/Security/2020-10-19-10-56-27.bpo-42051.EU_B7u.rst @@ -0,0 +1,3 @@ +The :mod:`plistlib` module no longer accepts entity declarations in XML +plist files to avoid XML vulnerabilities. This should not affect users as +entity declarations are not used in regular plist files. From webhook-mailer at python.org Mon Oct 19 14:16:26 2020 From: webhook-mailer at python.org (Ronald Oussoren) Date: Mon, 19 Oct 2020 18:16:26 -0000 Subject: [Python-checkins] bpo-41471: Ignore invalid prefix lengths in system proxy settings on macOS (GH-22762) Message-ID: https://github.com/python/cpython/commit/93a1ccabdede416425473329b8c718d507c55e29 commit: 93a1ccabdede416425473329b8c718d507c55e29 branch: master author: Ronald Oussoren committer: GitHub date: 2020-10-19T20:16:21+02:00 summary: bpo-41471: Ignore invalid prefix lengths in system proxy settings on macOS (GH-22762) files: A Misc/NEWS.d/next/macOS/2020-10-19-12-25-19.bpo-41471.gwA7un.rst M Lib/test/test_urllib2.py M Lib/urllib/request.py diff --git a/Lib/test/test_urllib2.py b/Lib/test/test_urllib2.py index f202f970ccb38..12ad6ae79d5ba 100644 --- a/Lib/test/test_urllib2.py +++ b/Lib/test/test_urllib2.py @@ -1449,6 +1449,18 @@ def test_osx_proxy_bypass(self): bypass = {'exclude_simple': True, 'exceptions': []} self.assertTrue(_proxy_bypass_macosx_sysconf('test', bypass)) + # Check that invalid prefix lengths are ignored + bypass = { + 'exclude_simple': False, + 'exceptions': [ '10.0.0.0/40', '172.19.10.0/24' ] + } + host = '172.19.10.5' + self.assertTrue(_proxy_bypass_macosx_sysconf(host, bypass), + 'expected bypass of %s to be True' % host) + host = '10.0.1.5' + self.assertFalse(_proxy_bypass_macosx_sysconf(host, bypass), + 'expected bypass of %s to be False' % host) + def check_basic_auth(self, headers, realm): with self.subTest(realm=realm, headers=headers): opener = OpenerDirector() diff --git a/Lib/urllib/request.py b/Lib/urllib/request.py index 2a3d71554f4bf..a8c870b9778eb 100644 --- a/Lib/urllib/request.py +++ b/Lib/urllib/request.py @@ -2596,6 +2596,11 @@ def ip2num(ipAddr): mask = 8 * (m.group(1).count('.') + 1) else: mask = int(mask[1:]) + + if mask < 0 or mask > 32: + # System libraries ignore invalid prefix lengths + continue + mask = 32 - mask if (hostIP >> mask) == (base >> mask): diff --git a/Misc/NEWS.d/next/macOS/2020-10-19-12-25-19.bpo-41471.gwA7un.rst b/Misc/NEWS.d/next/macOS/2020-10-19-12-25-19.bpo-41471.gwA7un.rst new file mode 100644 index 0000000000000..db5dd00b19b0d --- /dev/null +++ b/Misc/NEWS.d/next/macOS/2020-10-19-12-25-19.bpo-41471.gwA7un.rst @@ -0,0 +1 @@ +Ignore invalid prefix lengths in system proxy excludes. From webhook-mailer at python.org Mon Oct 19 14:52:35 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Mon, 19 Oct 2020 18:52:35 -0000 Subject: [Python-checkins] bpo-41192: Fix some broken anchors for audit event entries (GH-21310) (GH-22770) Message-ID: https://github.com/python/cpython/commit/ec18c468b8aed4b4a3d1942ae18b96d8efebe04b commit: ec18c468b8aed4b4a3d1942ae18b96d8efebe04b branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-19T14:52:27-04:00 summary: bpo-41192: Fix some broken anchors for audit event entries (GH-21310) (GH-22770) (cherry picked from commit ebc8c3828779374b9be4fae5c8ffc0059d36ac8c) Co-authored-by: Saiyang Gou files: M Doc/library/ctypes.rst M Doc/library/poplib.rst M Doc/using/cmdline.rst diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index 2d6c6d0a1c3c5..c172d5377636a 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -1618,7 +1618,7 @@ They are instances of a private class: ``ctypes.seh_exception`` with argument ``code`` will be raised, allowing an audit hook to replace the exception with its own. -.. audit-event:: ctypes.call_function func_pointer,arguments ctype-foreign-functions +.. audit-event:: ctypes.call_function func_pointer,arguments foreign-functions Some ways to invoke foreign function calls may raise an auditing event ``ctypes.call_function`` with arguments ``function pointer`` and ``arguments``. @@ -2545,4 +2545,3 @@ Arrays and pointers Returns the object to which to pointer points. Assigning to this attribute changes the pointer to point to the assigned object. - diff --git a/Doc/library/poplib.rst b/Doc/library/poplib.rst index 28b42fa60c185..e87dc4da97912 100644 --- a/Doc/library/poplib.rst +++ b/Doc/library/poplib.rst @@ -64,7 +64,7 @@ The :mod:`poplib` module provides two classes: .. audit-event:: poplib.connect self,host,port poplib.POP3_SSL - .. audit-event:: poplib.putline self,line popplib.POP3_SSL + .. audit-event:: poplib.putline self,line poplib.POP3_SSL All commands will raise an :ref:`auditing event ` ``poplib.putline`` with arguments ``self`` and ``line``, diff --git a/Doc/using/cmdline.rst b/Doc/using/cmdline.rst index d3f04004efc75..5aee334660664 100644 --- a/Doc/using/cmdline.rst +++ b/Doc/using/cmdline.rst @@ -551,7 +551,7 @@ conflict. the interactive session. You can also change the prompts :data:`sys.ps1` and :data:`sys.ps2` and the hook :data:`sys.__interactivehook__` in this file. - .. audit-event:: cpython.run_startup filename PYTHONSTARTUP + .. audit-event:: cpython.run_startup filename envvar-PYTHONSTARTUP Raises an :ref:`auditing event ` ``cpython.run_startup`` with the filename as the argument when called on startup. From webhook-mailer at python.org Mon Oct 19 15:13:50 2020 From: webhook-mailer at python.org (Raymond Hettinger) Date: Mon, 19 Oct 2020 19:13:50 -0000 Subject: [Python-checkins] bpo-42084: Language aware diff headers (GH-22776) Message-ID: https://github.com/python/cpython/commit/95ad890a7b0341d8d2fde13f824bc24c65a8ece0 commit: 95ad890a7b0341d8d2fde13f824bc24c65a8ece0 branch: master author: Raymond Hettinger committer: GitHub date: 2020-10-19T12:13:01-07:00 summary: bpo-42084: Language aware diff headers (GH-22776) files: M .gitattributes diff --git a/.gitattributes b/.gitattributes index 598d8f0f159ae..3827d09709d4a 100644 --- a/.gitattributes +++ b/.gitattributes @@ -57,3 +57,13 @@ Doc/library/token-list.inc linguist-generated=true Include/token.h linguist-generated=true Lib/token.py linguist-generated=true Parser/token.c linguist-generated=true + +# Language aware diff headers +# https://tekin.co.uk/2020/10/better-git-diff-output-for-ruby-python-elixir-and-more +# https://gist.github.com/tekin/12500956bd56784728e490d8cef9cb81 +*.c diff=cpp +*.h diff=cpp +*.css diff=css +*.html diff=html +*.py diff=python +*.md diff=markdown From webhook-mailer at python.org Mon Oct 19 15:29:43 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Mon, 19 Oct 2020 19:29:43 -0000 Subject: [Python-checkins] bpo-40484: Document compiler flags under AST module (GH-19885) (GH-22758) Message-ID: https://github.com/python/cpython/commit/a358a0ad7140ee636ab2631cfee0f365a5dbd4ea commit: a358a0ad7140ee636ab2631cfee0f365a5dbd4ea branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-19T20:29:38+01:00 summary: bpo-40484: Document compiler flags under AST module (GH-19885) (GH-22758) Co-authored-by: Pablo Galindo Co-authored-by: Shantanu (cherry picked from commit 155938907c2b3df71608ddeaa0a43d2ec1f2c699) Co-authored-by: Batuhan Taskaya Co-authored-by: Batuhan Taskaya files: M Doc/library/ast.rst M Doc/library/functions.rst diff --git a/Doc/library/ast.rst b/Doc/library/ast.rst index 755c60fba6411..932f7258f9dcd 100644 --- a/Doc/library/ast.rst +++ b/Doc/library/ast.rst @@ -1753,6 +1753,34 @@ and classes for traversing abstract syntax trees: Added the *indent* option. +.. _ast-compiler-flags: + +Compiler Flags +-------------- + +The following flags may be passed to :func:`compile` in order to change +effects on the compilation of a program: + +.. data:: PyCF_ALLOW_TOP_LEVEL_AWAIT + + Enables support for top-level ``await``, ``async for``, ``async with`` + and async comprehensions. + + .. versionadded:: 3.8 + +.. data:: PyCF_ONLY_AST + + Generates and returns an abstract syntax tree instead of returning a + compiled code object. + +.. data:: PyCF_TYPE_COMMENTS + + Enables support for :pep:`484` and :pep:`526` style type comments + (``# type: ``, ``# type: ignore ``). + + .. versionadded:: 3.8 + + .. _ast-cli: Command-Line Usage diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index 124085ed988de..101d118291d7f 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -245,26 +245,24 @@ are always available. They are listed here in alphabetical order. interactive statement (in the latter case, expression statements that evaluate to something other than ``None`` will be printed). - The optional arguments *flags* and *dont_inherit* control which :ref:`future - statements ` affect the compilation of *source*. If neither - is present (or both are zero) the code is compiled with those future - statements that are in effect in the code that is calling :func:`compile`. If the - *flags* argument is given and *dont_inherit* is not (or is zero) then the - future statements specified by the *flags* argument are used in addition to - those that would be used anyway. If *dont_inherit* is a non-zero integer then - the *flags* argument is it -- the future statements in effect around the call - to compile are ignored. - - Future statements are specified by bits which can be bitwise ORed together to - specify multiple statements. The bitfield required to specify a given feature - can be found as the :attr:`~__future__._Feature.compiler_flag` attribute on - the :class:`~__future__._Feature` instance in the :mod:`__future__` module. - - The optional argument *flags* also controls whether the compiled source is - allowed to contain top-level ``await``, ``async for`` and ``async with``. - When the bit ``ast.PyCF_ALLOW_TOP_LEVEL_AWAIT`` is set, the return code - object has ``CO_COROUTINE`` set in ``co_code``, and can be interactively - executed via ``await eval(code_object)``. + The optional argument *flags* and *dont_inherit* controls which + :ref:`compiler options ` should be activated + and which :ref:`future features ` should be allowed. If neither + is present (or both are zero) the code is compiled with the same flags that + affect the code that is calling :func:`compile`. If the *flags* + argument is given and *dont_inherit* is not (or is zero) then the compiler + options and the future statements specified by the *flags* argument are used + in addition to those that would be used anyway. If *dont_inherit* is a + non-zero integer then the *flags* argument is it -- the flags (future + features and compiler options) in the surrounding code are ignored. + + Compiler options and future statements are specified by bits which can be + bitwise ORed together to specify multiple options. The bitfield required to + specify a given future feature can be found as the + :attr:`~__future__._Feature.compiler_flag` attribute on the + :class:`~__future__._Feature` instance in the :mod:`__future__` module. + :ref:`Compiler flags ` can be found in :mod:`ast` + module, with ``PyCF_`` prefix. The argument *optimize* specifies the optimization level of the compiler; the default value of ``-1`` selects the optimization level of the interpreter as From webhook-mailer at python.org Mon Oct 19 16:05:09 2020 From: webhook-mailer at python.org (Steve Dower) Date: Mon, 19 Oct 2020 20:05:09 -0000 Subject: [Python-checkins] Remove extra pip package (GH-22778) Message-ID: https://github.com/python/cpython/commit/6b487e07d0525f7721dcb4dc1aa96eb2eec97a78 commit: 6b487e07d0525f7721dcb4dc1aa96eb2eec97a78 branch: 3.9 author: Steve Dower committer: GitHub date: 2020-10-19T21:05:04+01:00 summary: Remove extra pip package (GH-22778) files: D Lib/ensurepip/_bundled/pip-20.2.1-py2.py3-none-any.whl diff --git a/Lib/ensurepip/_bundled/pip-20.2.1-py2.py3-none-any.whl b/Lib/ensurepip/_bundled/pip-20.2.1-py2.py3-none-any.whl deleted file mode 100644 index 3d0d3f8ae7fac..0000000000000 Binary files a/Lib/ensurepip/_bundled/pip-20.2.1-py2.py3-none-any.whl and /dev/null differ From webhook-mailer at python.org Mon Oct 19 16:22:43 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Mon, 19 Oct 2020 20:22:43 -0000 Subject: [Python-checkins] Remove extra pip package (GH-22778) Message-ID: https://github.com/python/cpython/commit/4764097e4d1c92b0e7610fd4143393a7c69e71ff commit: 4764097e4d1c92b0e7610fd4143393a7c69e71ff branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-19T13:22:34-07:00 summary: Remove extra pip package (GH-22778) (cherry picked from commit 6b487e07d0525f7721dcb4dc1aa96eb2eec97a78) Co-authored-by: Steve Dower files: D Lib/ensurepip/_bundled/pip-20.2.1-py2.py3-none-any.whl diff --git a/Lib/ensurepip/_bundled/pip-20.2.1-py2.py3-none-any.whl b/Lib/ensurepip/_bundled/pip-20.2.1-py2.py3-none-any.whl deleted file mode 100644 index 3d0d3f8ae7fac..0000000000000 Binary files a/Lib/ensurepip/_bundled/pip-20.2.1-py2.py3-none-any.whl and /dev/null differ From webhook-mailer at python.org Mon Oct 19 17:14:32 2020 From: webhook-mailer at python.org (Barry Warsaw) Date: Mon, 19 Oct 2020 21:14:32 -0000 Subject: [Python-checkins] bpo-42089: Sync with current cpython branch of importlib_metadata (GH-22775) Message-ID: https://github.com/python/cpython/commit/96ddc58281d2b6b2258b1a628bccf252090d5611 commit: 96ddc58281d2b6b2258b1a628bccf252090d5611 branch: master author: Barry Warsaw committer: GitHub date: 2020-10-19T14:14:21-07:00 summary: bpo-42089: Sync with current cpython branch of importlib_metadata (GH-22775) ~~The only differences are in the test files.~~ Automerge-Triggered-By: @jaraco files: A Misc/NEWS.d/next/Library/2020-10-19-16-53-19.bpo-42089.R1dthW.rst M Lib/importlib/metadata.py M Lib/test/test_importlib/fixtures.py M Lib/test/test_importlib/test_main.py diff --git a/Lib/importlib/metadata.py b/Lib/importlib/metadata.py index ffa0cba45706d..302d61d505cb3 100644 --- a/Lib/importlib/metadata.py +++ b/Lib/importlib/metadata.py @@ -37,6 +37,15 @@ class PackageNotFoundError(ModuleNotFoundError): """The package was not found.""" + def __str__(self): + tmpl = "No package metadata was found for {self.name}" + return tmpl.format(**locals()) + + @property + def name(self): + name, = self.args + return name + class EntryPoint( collections.namedtuple('EntryPointBase', 'name value group')): diff --git a/Lib/test/test_importlib/fixtures.py b/Lib/test/test_importlib/fixtures.py index 985277f64615f..8fa92909d583e 100644 --- a/Lib/test/test_importlib/fixtures.py +++ b/Lib/test/test_importlib/fixtures.py @@ -6,6 +6,8 @@ import textwrap import contextlib +from test.support.os_helper import FS_NONASCII + @contextlib.contextmanager def tempdir(): @@ -212,12 +214,7 @@ def build_files(file_defs, prefix=pathlib.Path()): class FileBuilder: def unicode_filename(self): - try: - from test.support import os_helper - except ImportError: - # outside CPython, hard-code a unicode snowman - return '?' - return os_helper.FS_NONASCII or \ + return FS_NONASCII or \ self.skip("File system does not support non-ascii.") diff --git a/Lib/test/test_importlib/test_main.py b/Lib/test/test_importlib/test_main.py index 91e501a2eb7cd..a26bab6361548 100644 --- a/Lib/test/test_importlib/test_main.py +++ b/Lib/test/test_importlib/test_main.py @@ -32,6 +32,18 @@ def test_for_name_does_not_exist(self): with self.assertRaises(PackageNotFoundError): Distribution.from_name('does-not-exist') + def test_package_not_found_mentions_metadata(self): + """ + When a package is not found, that could indicate that the + packgae is not installed or that it is installed without + metadata. Ensure the exception mentions metadata to help + guide users toward the cause. See #124. + """ + with self.assertRaises(PackageNotFoundError) as ctx: + Distribution.from_name('does-not-exist') + + assert "metadata" in str(ctx.exception) + def test_new_style_classes(self): self.assertIsInstance(Distribution, type) diff --git a/Misc/NEWS.d/next/Library/2020-10-19-16-53-19.bpo-42089.R1dthW.rst b/Misc/NEWS.d/next/Library/2020-10-19-16-53-19.bpo-42089.R1dthW.rst new file mode 100644 index 0000000000000..3f3affd1a7df7 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-10-19-16-53-19.bpo-42089.R1dthW.rst @@ -0,0 +1,2 @@ +In ``importlib.metadata.PackageNotFoundError``, make reference to the +package metadata being missing to improve the user experience. From webhook-mailer at python.org Mon Oct 19 17:27:25 2020 From: webhook-mailer at python.org (Irit Katriel) Date: Mon, 19 Oct 2020 21:27:25 -0000 Subject: [Python-checkins] bpo-38320: Clarify that expectedFailure is satisfied by either failure or error of the test. (GH-22740) Message-ID: https://github.com/python/cpython/commit/fa8748271a61177e9bf609921fa464cc6990478b commit: fa8748271a61177e9bf609921fa464cc6990478b branch: master author: Irit Katriel committer: GitHub date: 2020-10-19T22:27:16+01:00 summary: bpo-38320: Clarify that expectedFailure is satisfied by either failure or error of the test. (GH-22740) files: M Doc/library/unittest.rst diff --git a/Doc/library/unittest.rst b/Doc/library/unittest.rst index f04ec91270eeb..51e10119d3e8d 100644 --- a/Doc/library/unittest.rst +++ b/Doc/library/unittest.rst @@ -593,8 +593,9 @@ The following decorators and exception implement test skipping and expected fail .. decorator:: expectedFailure - Mark the test as an expected failure. If the test fails it will be - considered a success. If the test passes, it will be considered a failure. + Mark the test as an expected failure or error. If the test fails or errors + it will be considered a success. If the test passes, it will be considered + a failure. .. exception:: SkipTest(reason) @@ -1967,7 +1968,7 @@ Loading and running tests A list containing 2-tuples of :class:`TestCase` instances and strings holding formatted tracebacks. Each tuple represents an expected failure - of the test case. + or error of the test case. .. attribute:: unexpectedSuccesses @@ -2093,8 +2094,8 @@ Loading and running tests .. method:: addExpectedFailure(test, err) - Called when the test case *test* fails, but was marked with the - :func:`expectedFailure` decorator. + Called when the test case *test* fails or errors, but was marked with + the :func:`expectedFailure` decorator. The default implementation appends a tuple ``(test, formatted_err)`` to the instance's :attr:`expectedFailures` attribute, where *formatted_err* From webhook-mailer at python.org Mon Oct 19 17:46:25 2020 From: webhook-mailer at python.org (abdo) Date: Mon, 19 Oct 2020 21:46:25 -0000 Subject: [Python-checkins] Link to the msvcrt module from the Windows FAQ (#22268) Message-ID: https://github.com/python/cpython/commit/5d9e6575177f3e5f9aa20d6a08ae1204f122e672 commit: 5d9e6575177f3e5f9aa20d6a08ae1204f122e672 branch: master author: abdo committer: GitHub date: 2020-10-19T22:46:21+01:00 summary: Link to the msvcrt module from the Windows FAQ (#22268) files: M Doc/faq/windows.rst diff --git a/Doc/faq/windows.rst b/Doc/faq/windows.rst index a181086e9ce62..c550a055529a7 100644 --- a/Doc/faq/windows.rst +++ b/Doc/faq/windows.rst @@ -279,7 +279,7 @@ in batch mode. How do I check for a keypress without blocking? ----------------------------------------------- -Use the msvcrt module. This is a standard Windows-specific extension module. +Use the :mod:`msvcrt` module. This is a standard Windows-specific extension module. It defines a function ``kbhit()`` which checks whether a keyboard hit is present, and ``getch()`` which gets one character without echoing it. From webhook-mailer at python.org Mon Oct 19 17:55:51 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Mon, 19 Oct 2020 21:55:51 -0000 Subject: [Python-checkins] Link to the msvcrt module from the Windows FAQ (GH-22268) Message-ID: https://github.com/python/cpython/commit/e4a03be014841716056e72710f4b7225a25cec6e commit: e4a03be014841716056e72710f4b7225a25cec6e branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-19T14:55:43-07:00 summary: Link to the msvcrt module from the Windows FAQ (GH-22268) (cherry picked from commit 5d9e6575177f3e5f9aa20d6a08ae1204f122e672) Co-authored-by: abdo files: M Doc/faq/windows.rst diff --git a/Doc/faq/windows.rst b/Doc/faq/windows.rst index a181086e9ce62..c550a055529a7 100644 --- a/Doc/faq/windows.rst +++ b/Doc/faq/windows.rst @@ -279,7 +279,7 @@ in batch mode. How do I check for a keypress without blocking? ----------------------------------------------- -Use the msvcrt module. This is a standard Windows-specific extension module. +Use the :mod:`msvcrt` module. This is a standard Windows-specific extension module. It defines a function ``kbhit()`` which checks whether a keyboard hit is present, and ``getch()`` which gets one character without echoing it. From webhook-mailer at python.org Mon Oct 19 18:02:51 2020 From: webhook-mailer at python.org (Michel Samia) Date: Mon, 19 Oct 2020 22:02:51 -0000 Subject: [Python-checkins] bpo-41292: Fixes dead link to cx_freeze from Windows FAQ (GH-21463) Message-ID: https://github.com/python/cpython/commit/33242a9328cb3912f02819d2d092bf89681000b2 commit: 33242a9328cb3912f02819d2d092bf89681000b2 branch: master author: Michel Samia committer: GitHub date: 2020-10-19T23:02:43+01:00 summary: bpo-41292: Fixes dead link to cx_freeze from Windows FAQ (GH-21463) files: M Doc/faq/windows.rst diff --git a/Doc/faq/windows.rst b/Doc/faq/windows.rst index c550a055529a7..c8e9c5fb67030 100644 --- a/Doc/faq/windows.rst +++ b/Doc/faq/windows.rst @@ -140,7 +140,7 @@ offender. How do I make an executable from a Python script? ------------------------------------------------- -See `cx_Freeze `_ for a distutils extension +See `cx_Freeze `_ for a distutils extension that allows you to create console and GUI executables from Python code. `py2exe `_, the most popular extension for building Python 2.x-based executables, does not yet support Python 3 but a version that From webhook-mailer at python.org Mon Oct 19 18:06:13 2020 From: webhook-mailer at python.org (Jason R. Coombs) Date: Mon, 19 Oct 2020 22:06:13 -0000 Subject: [Python-checkins] bpo-16396: Allow wintypes to be imported on non-Windows systems. (GH-21394) Message-ID: https://github.com/python/cpython/commit/5456e78f4593edc277ab72fb9a9db1ebae7d4c2d commit: 5456e78f4593edc277ab72fb9a9db1ebae7d4c2d branch: master author: Jason R. Coombs committer: GitHub date: 2020-10-19T23:06:05+01:00 summary: bpo-16396: Allow wintypes to be imported on non-Windows systems. (GH-21394) Co-authored-by: Christian Heimes files: A Misc/NEWS.d/next/Library/2020-07-08-09-45-00.bpo-16936.z8o8Pn.rst M Lib/ctypes/test/test_wintypes.py M Modules/_ctypes/cfield.c diff --git a/Lib/ctypes/test/test_wintypes.py b/Lib/ctypes/test/test_wintypes.py index 71442df8301af..243d5962ffa7f 100644 --- a/Lib/ctypes/test/test_wintypes.py +++ b/Lib/ctypes/test/test_wintypes.py @@ -1,12 +1,13 @@ -import sys import unittest +# also work on POSIX + from ctypes import * +from ctypes import wintypes + - at unittest.skipUnless(sys.platform.startswith('win'), 'Windows-only test') class WinTypesTest(unittest.TestCase): def test_variant_bool(self): - from ctypes import wintypes # reads 16-bits from memory, anything non-zero is True for true_value in (1, 32767, 32768, 65535, 65537): true = POINTER(c_int16)(c_int16(true_value)) @@ -37,5 +38,6 @@ def test_variant_bool(self): vb.value = [] self.assertIs(vb.value, False) + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Library/2020-07-08-09-45-00.bpo-16936.z8o8Pn.rst b/Misc/NEWS.d/next/Library/2020-07-08-09-45-00.bpo-16936.z8o8Pn.rst new file mode 100644 index 0000000000000..c76db4eedecff --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-07-08-09-45-00.bpo-16936.z8o8Pn.rst @@ -0,0 +1 @@ +Allow ``ctypes.wintypes`` to be imported on non-Windows systems. diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c index 3bd9ae438db44..98939290fb97f 100644 --- a/Modules/_ctypes/cfield.c +++ b/Modules/_ctypes/cfield.c @@ -658,7 +658,11 @@ i_get_sw(void *ptr, Py_ssize_t size) return PyLong_FromLong(val); } -#ifdef MS_WIN32 +#ifndef MS_WIN32 +/* http://msdn.microsoft.com/en-us/library/cc237864.aspx */ +#define VARIANT_FALSE 0x0000 +#define VARIANT_TRUE 0xFFFF +#endif /* short BOOL - VARIANT_BOOL */ static PyObject * vBOOL_set(void *ptr, PyObject *value, Py_ssize_t size) @@ -680,7 +684,6 @@ vBOOL_get(void *ptr, Py_ssize_t size) { return PyBool_FromLong((long)*(short int *)ptr); } -#endif static PyObject * bool_set(void *ptr, PyObject *value, Py_ssize_t size) @@ -1511,8 +1514,8 @@ static struct fielddesc formattable[] = { #endif #ifdef MS_WIN32 { 'X', BSTR_set, BSTR_get, &ffi_type_pointer}, - { 'v', vBOOL_set, vBOOL_get, &ffi_type_sshort}, #endif + { 'v', vBOOL_set, vBOOL_get, &ffi_type_sshort}, #if SIZEOF__BOOL == 1 { '?', bool_set, bool_get, &ffi_type_uchar}, /* Also fallback for no native _Bool support */ #elif SIZEOF__BOOL == SIZEOF_SHORT From webhook-mailer at python.org Mon Oct 19 18:08:41 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Mon, 19 Oct 2020 22:08:41 -0000 Subject: [Python-checkins] Link to the msvcrt module from the Windows FAQ (GH-22268) Message-ID: https://github.com/python/cpython/commit/9b5e373aa8ba988717e664de4bec4120416f50d3 commit: 9b5e373aa8ba988717e664de4bec4120416f50d3 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-19T15:08:36-07:00 summary: Link to the msvcrt module from the Windows FAQ (GH-22268) (cherry picked from commit 5d9e6575177f3e5f9aa20d6a08ae1204f122e672) Co-authored-by: abdo files: M Doc/faq/windows.rst diff --git a/Doc/faq/windows.rst b/Doc/faq/windows.rst index a181086e9ce62..c550a055529a7 100644 --- a/Doc/faq/windows.rst +++ b/Doc/faq/windows.rst @@ -279,7 +279,7 @@ in batch mode. How do I check for a keypress without blocking? ----------------------------------------------- -Use the msvcrt module. This is a standard Windows-specific extension module. +Use the :mod:`msvcrt` module. This is a standard Windows-specific extension module. It defines a function ``kbhit()`` which checks whether a keyboard hit is present, and ``getch()`` which gets one character without echoing it. From webhook-mailer at python.org Mon Oct 19 18:08:41 2020 From: webhook-mailer at python.org (Zackery Spytz) Date: Mon, 19 Oct 2020 22:08:41 -0000 Subject: [Python-checkins] bpo-41217: Fix incorrect note in the asyncio.create_subprocess_shell() docs (GH-21360) Message-ID: https://github.com/python/cpython/commit/4dfb190a33a1deac60306f15d52d2fe11fb93464 commit: 4dfb190a33a1deac60306f15d52d2fe11fb93464 branch: master author: Zackery Spytz committer: GitHub date: 2020-10-19T23:08:34+01:00 summary: bpo-41217: Fix incorrect note in the asyncio.create_subprocess_shell() docs (GH-21360) On Windows, the default asyncio event loop is ProactorEventLoop (as of 3.8). files: M Doc/library/asyncio-subprocess.rst diff --git a/Doc/library/asyncio-subprocess.rst b/Doc/library/asyncio-subprocess.rst index eb1312a949a10..b0330349dfb65 100644 --- a/Doc/library/asyncio-subprocess.rst +++ b/Doc/library/asyncio-subprocess.rst @@ -110,10 +110,8 @@ Creating Subprocesses .. note:: - The default asyncio event loop implementation on **Windows** does not - support subprocesses. Subprocesses are available for Windows if a - :class:`ProactorEventLoop` is used. - See :ref:`Subprocess Support on Windows ` + Subprocesses are available for Windows if a :class:`ProactorEventLoop` is + used. See :ref:`Subprocess Support on Windows ` for details. .. seealso:: From webhook-mailer at python.org Mon Oct 19 18:13:06 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Mon, 19 Oct 2020 22:13:06 -0000 Subject: [Python-checkins] bpo-41292: Fixes dead link to cx_freeze from Windows FAQ (GH-21463) Message-ID: https://github.com/python/cpython/commit/4c8da3230a494dac3d7fd3b4396a60038b2be6de commit: 4c8da3230a494dac3d7fd3b4396a60038b2be6de branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-19T15:12:57-07:00 summary: bpo-41292: Fixes dead link to cx_freeze from Windows FAQ (GH-21463) (cherry picked from commit 33242a9328cb3912f02819d2d092bf89681000b2) Co-authored-by: Michel Samia files: M Doc/faq/windows.rst diff --git a/Doc/faq/windows.rst b/Doc/faq/windows.rst index c550a055529a7..c8e9c5fb67030 100644 --- a/Doc/faq/windows.rst +++ b/Doc/faq/windows.rst @@ -140,7 +140,7 @@ offender. How do I make an executable from a Python script? ------------------------------------------------- -See `cx_Freeze `_ for a distutils extension +See `cx_Freeze `_ for a distutils extension that allows you to create console and GUI executables from Python code. `py2exe `_, the most popular extension for building Python 2.x-based executables, does not yet support Python 3 but a version that From webhook-mailer at python.org Mon Oct 19 18:17:57 2020 From: webhook-mailer at python.org (Alex Gaynor) Date: Mon, 19 Oct 2020 22:17:57 -0000 Subject: [Python-checkins] bpo-41784: make PyUnicode_AsUTF8AndSize part of the limited API (GH-22252) Message-ID: https://github.com/python/cpython/commit/3a8fdb28794b2f19f6c8464378fb8b46bce1f5f4 commit: 3a8fdb28794b2f19f6c8464378fb8b46bce1f5f4 branch: master author: Alex Gaynor committer: GitHub date: 2020-10-19T23:17:50+01:00 summary: bpo-41784: make PyUnicode_AsUTF8AndSize part of the limited API (GH-22252) files: A Misc/NEWS.d/next/C API/2020-09-14-10-17-00.bpo-41784.Yl4gI2.rst M Doc/c-api/unicode.rst M Doc/whatsnew/3.10.rst M Include/cpython/unicodeobject.h M Include/unicodeobject.h M PC/python3dll.c diff --git a/Doc/c-api/unicode.rst b/Doc/c-api/unicode.rst index 5518214a793e0..54bd0a3cbb655 100644 --- a/Doc/c-api/unicode.rst +++ b/Doc/c-api/unicode.rst @@ -1098,6 +1098,9 @@ These are the UTF-8 codec APIs: .. versionchanged:: 3.7 The return type is now ``const char *`` rather of ``char *``. + .. versionchanged:: 3.10 + This function is a part of the :ref:`limited API `. + .. c:function:: const char* PyUnicode_AsUTF8(PyObject *unicode) diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index 738ef974e7867..f57e1b412378e 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -323,6 +323,10 @@ New Features sending value into iterator without raising ``StopIteration`` exception. (Contributed by Vladimir Matveev in :issue:`41756`.) +* Added :c:func:`PyUnicode_AsUTF8AndSize` to the limited C API. + (Contributed by Alex Gaynor in :issue:`41784`.) + + Porting to Python 3.10 ---------------------- diff --git a/Include/cpython/unicodeobject.h b/Include/cpython/unicodeobject.h index 300408cb26299..f1b44554e3078 100644 --- a/Include/cpython/unicodeobject.h +++ b/Include/cpython/unicodeobject.h @@ -726,26 +726,6 @@ PyAPI_FUNC(int) _PyUnicode_FormatAdvancedWriter( /* --- Manage the default encoding ---------------------------------------- */ -/* Returns a pointer to the default encoding (UTF-8) of the - Unicode object unicode and the size of the encoded representation - in bytes stored in *size. - - In case of an error, no *size is set. - - This function caches the UTF-8 encoded string in the unicodeobject - and subsequent calls will return the same string. The memory is released - when the unicodeobject is deallocated. - - _PyUnicode_AsStringAndSize is a #define for PyUnicode_AsUTF8AndSize to - support the previous internal function with the same behaviour. -*/ - -PyAPI_FUNC(const char *) PyUnicode_AsUTF8AndSize( - PyObject *unicode, - Py_ssize_t *size); - -#define _PyUnicode_AsStringAndSize PyUnicode_AsUTF8AndSize - /* Returns a pointer to the default encoding (UTF-8) of the Unicode object unicode. diff --git a/Include/unicodeobject.h b/Include/unicodeobject.h index 90b3299fd26ce..b0ac086a6be23 100644 --- a/Include/unicodeobject.h +++ b/Include/unicodeobject.h @@ -468,6 +468,23 @@ PyAPI_FUNC(PyObject*) PyUnicode_AsUTF8String( PyObject *unicode /* Unicode object */ ); +/* Returns a pointer to the default encoding (UTF-8) of the + Unicode object unicode and the size of the encoded representation + in bytes stored in *size. + + In case of an error, no *size is set. + + This function caches the UTF-8 encoded string in the unicodeobject + and subsequent calls will return the same string. The memory is released + when the unicodeobject is deallocated. +*/ + +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030A0000 +PyAPI_FUNC(const char *) PyUnicode_AsUTF8AndSize( + PyObject *unicode, + Py_ssize_t *size); +#endif + /* --- UTF-32 Codecs ------------------------------------------------------ */ /* Decodes length bytes from a UTF-32 encoded buffer string and returns diff --git a/Misc/NEWS.d/next/C API/2020-09-14-10-17-00.bpo-41784.Yl4gI2.rst b/Misc/NEWS.d/next/C API/2020-09-14-10-17-00.bpo-41784.Yl4gI2.rst new file mode 100644 index 0000000000000..f09e0879ad194 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2020-09-14-10-17-00.bpo-41784.Yl4gI2.rst @@ -0,0 +1 @@ +Added ``PyUnicode_AsUTF8AndSize`` to the limited C API. diff --git a/PC/python3dll.c b/PC/python3dll.c index 153ba612b7804..b9b229ea67d7b 100644 --- a/PC/python3dll.c +++ b/PC/python3dll.c @@ -568,6 +568,7 @@ EXPORT_FUNC(PyUnicode_AsUCS4Copy) EXPORT_FUNC(PyUnicode_AsUnicodeEscapeString) EXPORT_FUNC(PyUnicode_AsUTF16String) EXPORT_FUNC(PyUnicode_AsUTF32String) +EXPORT_FUNC(PyUnicode_AsUTF8AndSize) EXPORT_FUNC(PyUnicode_AsUTF8String) EXPORT_FUNC(PyUnicode_AsWideChar) EXPORT_FUNC(PyUnicode_AsWideCharString) From webhook-mailer at python.org Mon Oct 19 18:19:04 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Mon, 19 Oct 2020 22:19:04 -0000 Subject: [Python-checkins] bpo-41217: Fix incorrect note in the asyncio.create_subprocess_shell() docs (GH-21360) Message-ID: https://github.com/python/cpython/commit/dc785db64d55c3d7a69b10c303e6386e87cfd01b commit: dc785db64d55c3d7a69b10c303e6386e87cfd01b branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-19T15:19:00-07:00 summary: bpo-41217: Fix incorrect note in the asyncio.create_subprocess_shell() docs (GH-21360) On Windows, the default asyncio event loop is ProactorEventLoop (as of 3.8). (cherry picked from commit 4dfb190a33a1deac60306f15d52d2fe11fb93464) Co-authored-by: Zackery Spytz files: M Doc/library/asyncio-subprocess.rst diff --git a/Doc/library/asyncio-subprocess.rst b/Doc/library/asyncio-subprocess.rst index eb1312a949a10..b0330349dfb65 100644 --- a/Doc/library/asyncio-subprocess.rst +++ b/Doc/library/asyncio-subprocess.rst @@ -110,10 +110,8 @@ Creating Subprocesses .. note:: - The default asyncio event loop implementation on **Windows** does not - support subprocesses. Subprocesses are available for Windows if a - :class:`ProactorEventLoop` is used. - See :ref:`Subprocess Support on Windows ` + Subprocesses are available for Windows if a :class:`ProactorEventLoop` is + used. See :ref:`Subprocess Support on Windows ` for details. .. seealso:: From webhook-mailer at python.org Mon Oct 19 18:24:09 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Mon, 19 Oct 2020 22:24:09 -0000 Subject: [Python-checkins] bpo-41292: Fixes dead link to cx_freeze from Windows FAQ (GH-21463) Message-ID: https://github.com/python/cpython/commit/e67f186898b6731b8e8f3f6b07cb0959e9aa0ab6 commit: e67f186898b6731b8e8f3f6b07cb0959e9aa0ab6 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-19T15:24:05-07:00 summary: bpo-41292: Fixes dead link to cx_freeze from Windows FAQ (GH-21463) (cherry picked from commit 33242a9328cb3912f02819d2d092bf89681000b2) Co-authored-by: Michel Samia files: M Doc/faq/windows.rst diff --git a/Doc/faq/windows.rst b/Doc/faq/windows.rst index c550a055529a7..c8e9c5fb67030 100644 --- a/Doc/faq/windows.rst +++ b/Doc/faq/windows.rst @@ -140,7 +140,7 @@ offender. How do I make an executable from a Python script? ------------------------------------------------- -See `cx_Freeze `_ for a distutils extension +See `cx_Freeze `_ for a distutils extension that allows you to create console and GUI executables from Python code. `py2exe `_, the most popular extension for building Python 2.x-based executables, does not yet support Python 3 but a version that From webhook-mailer at python.org Mon Oct 19 18:29:41 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Mon, 19 Oct 2020 22:29:41 -0000 Subject: [Python-checkins] bpo-16396: Allow wintypes to be imported on non-Windows systems. (GH-21394) Message-ID: https://github.com/python/cpython/commit/6e998fad1c92aee9c8c23c5887a7023d76bdf6c2 commit: 6e998fad1c92aee9c8c23c5887a7023d76bdf6c2 branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-19T15:29:37-07:00 summary: bpo-16396: Allow wintypes to be imported on non-Windows systems. (GH-21394) Co-authored-by: Christian Heimes (cherry picked from commit 5456e78f4593edc277ab72fb9a9db1ebae7d4c2d) Co-authored-by: Jason R. Coombs files: A Misc/NEWS.d/next/Library/2020-07-08-09-45-00.bpo-16936.z8o8Pn.rst M Lib/ctypes/test/test_wintypes.py M Modules/_ctypes/cfield.c diff --git a/Lib/ctypes/test/test_wintypes.py b/Lib/ctypes/test/test_wintypes.py index 71442df8301af..243d5962ffa7f 100644 --- a/Lib/ctypes/test/test_wintypes.py +++ b/Lib/ctypes/test/test_wintypes.py @@ -1,12 +1,13 @@ -import sys import unittest +# also work on POSIX + from ctypes import * +from ctypes import wintypes + - at unittest.skipUnless(sys.platform.startswith('win'), 'Windows-only test') class WinTypesTest(unittest.TestCase): def test_variant_bool(self): - from ctypes import wintypes # reads 16-bits from memory, anything non-zero is True for true_value in (1, 32767, 32768, 65535, 65537): true = POINTER(c_int16)(c_int16(true_value)) @@ -37,5 +38,6 @@ def test_variant_bool(self): vb.value = [] self.assertIs(vb.value, False) + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Library/2020-07-08-09-45-00.bpo-16936.z8o8Pn.rst b/Misc/NEWS.d/next/Library/2020-07-08-09-45-00.bpo-16936.z8o8Pn.rst new file mode 100644 index 0000000000000..c76db4eedecff --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-07-08-09-45-00.bpo-16936.z8o8Pn.rst @@ -0,0 +1 @@ +Allow ``ctypes.wintypes`` to be imported on non-Windows systems. diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c index 95367d5093765..1a36356ec76a6 100644 --- a/Modules/_ctypes/cfield.c +++ b/Modules/_ctypes/cfield.c @@ -695,7 +695,11 @@ i_get_sw(void *ptr, Py_ssize_t size) return PyLong_FromLong(val); } -#ifdef MS_WIN32 +#ifndef MS_WIN32 +/* http://msdn.microsoft.com/en-us/library/cc237864.aspx */ +#define VARIANT_FALSE 0x0000 +#define VARIANT_TRUE 0xFFFF +#endif /* short BOOL - VARIANT_BOOL */ static PyObject * vBOOL_set(void *ptr, PyObject *value, Py_ssize_t size) @@ -717,7 +721,6 @@ vBOOL_get(void *ptr, Py_ssize_t size) { return PyBool_FromLong((long)*(short int *)ptr); } -#endif static PyObject * bool_set(void *ptr, PyObject *value, Py_ssize_t size) @@ -1544,8 +1547,8 @@ static struct fielddesc formattable[] = { #endif #ifdef MS_WIN32 { 'X', BSTR_set, BSTR_get, &ffi_type_pointer}, - { 'v', vBOOL_set, vBOOL_get, &ffi_type_sshort}, #endif + { 'v', vBOOL_set, vBOOL_get, &ffi_type_sshort}, #if SIZEOF__BOOL == 1 { '?', bool_set, bool_get, &ffi_type_uchar}, /* Also fallback for no native _Bool support */ #elif SIZEOF__BOOL == SIZEOF_SHORT From webhook-mailer at python.org Mon Oct 19 18:30:43 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Mon, 19 Oct 2020 22:30:43 -0000 Subject: [Python-checkins] bpo-41217: Fix incorrect note in the asyncio.create_subprocess_shell() docs (GH-21360) Message-ID: https://github.com/python/cpython/commit/b62ecc21b451d2b8e041f5367c7b563ac01bbdd8 commit: b62ecc21b451d2b8e041f5367c7b563ac01bbdd8 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-19T15:30:39-07:00 summary: bpo-41217: Fix incorrect note in the asyncio.create_subprocess_shell() docs (GH-21360) On Windows, the default asyncio event loop is ProactorEventLoop (as of 3.8). (cherry picked from commit 4dfb190a33a1deac60306f15d52d2fe11fb93464) Co-authored-by: Zackery Spytz files: M Doc/library/asyncio-subprocess.rst diff --git a/Doc/library/asyncio-subprocess.rst b/Doc/library/asyncio-subprocess.rst index eb1312a949a10..b0330349dfb65 100644 --- a/Doc/library/asyncio-subprocess.rst +++ b/Doc/library/asyncio-subprocess.rst @@ -110,10 +110,8 @@ Creating Subprocesses .. note:: - The default asyncio event loop implementation on **Windows** does not - support subprocesses. Subprocesses are available for Windows if a - :class:`ProactorEventLoop` is used. - See :ref:`Subprocess Support on Windows ` + Subprocesses are available for Windows if a :class:`ProactorEventLoop` is + used. See :ref:`Subprocess Support on Windows ` for details. .. seealso:: From webhook-mailer at python.org Mon Oct 19 18:31:01 2020 From: webhook-mailer at python.org (Jakub Stasiak) Date: Mon, 19 Oct 2020 22:31:01 -0000 Subject: [Python-checkins] bpo-40901: Describe what "interface name" means on Windows (GH-20694) Message-ID: https://github.com/python/cpython/commit/f85658a09878c658ae0e0590bfa30e4ce51c9a04 commit: f85658a09878c658ae0e0590bfa30e4ce51c9a04 branch: master author: Jakub Stasiak committer: GitHub date: 2020-10-19T23:30:58+01:00 summary: bpo-40901: Describe what "interface name" means on Windows (GH-20694) files: M Doc/library/socket.rst diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst index 5bcac20a4c604..faf8a76251420 100755 --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -1091,6 +1091,19 @@ The :mod:`socket` module also offers various network-related services: .. versionchanged:: 3.8 Windows support was added. + .. note:: + + On Windows network interfaces have different names in different contexts + (all names are examples): + + * UUID: ``{FB605B73-AAC2-49A6-9A2F-25416AEA0573}`` + * name: ``ethernet_32770`` + * friendly name: ``vEthernet (nat)`` + * description: ``Hyper-V Virtual Ethernet Adapter`` + + This function returns names of the second form from the list, ``ethernet_32770`` + in this example case. + .. function:: if_nametoindex(if_name) @@ -1105,6 +1118,9 @@ The :mod:`socket` module also offers various network-related services: .. versionchanged:: 3.8 Windows support was added. + .. seealso:: + "Interface name" is a name as documented in :func:`if_nameindex`. + .. function:: if_indextoname(if_index) @@ -1119,6 +1135,9 @@ The :mod:`socket` module also offers various network-related services: .. versionchanged:: 3.8 Windows support was added. + .. seealso:: + "Interface name" is a name as documented in :func:`if_nameindex`. + .. _socket-objects: From webhook-mailer at python.org Mon Oct 19 18:32:44 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Mon, 19 Oct 2020 22:32:44 -0000 Subject: [Python-checkins] bpo-16396: Allow wintypes to be imported on non-Windows systems. (GH-21394) Message-ID: https://github.com/python/cpython/commit/05d52a0ad69cbadd4b048f2a97991e4e58d4a922 commit: 05d52a0ad69cbadd4b048f2a97991e4e58d4a922 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-19T15:32:36-07:00 summary: bpo-16396: Allow wintypes to be imported on non-Windows systems. (GH-21394) Co-authored-by: Christian Heimes (cherry picked from commit 5456e78f4593edc277ab72fb9a9db1ebae7d4c2d) Co-authored-by: Jason R. Coombs files: A Misc/NEWS.d/next/Library/2020-07-08-09-45-00.bpo-16936.z8o8Pn.rst M Lib/ctypes/test/test_wintypes.py M Modules/_ctypes/cfield.c diff --git a/Lib/ctypes/test/test_wintypes.py b/Lib/ctypes/test/test_wintypes.py index 71442df8301af..243d5962ffa7f 100644 --- a/Lib/ctypes/test/test_wintypes.py +++ b/Lib/ctypes/test/test_wintypes.py @@ -1,12 +1,13 @@ -import sys import unittest +# also work on POSIX + from ctypes import * +from ctypes import wintypes + - at unittest.skipUnless(sys.platform.startswith('win'), 'Windows-only test') class WinTypesTest(unittest.TestCase): def test_variant_bool(self): - from ctypes import wintypes # reads 16-bits from memory, anything non-zero is True for true_value in (1, 32767, 32768, 65535, 65537): true = POINTER(c_int16)(c_int16(true_value)) @@ -37,5 +38,6 @@ def test_variant_bool(self): vb.value = [] self.assertIs(vb.value, False) + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Library/2020-07-08-09-45-00.bpo-16936.z8o8Pn.rst b/Misc/NEWS.d/next/Library/2020-07-08-09-45-00.bpo-16936.z8o8Pn.rst new file mode 100644 index 0000000000000..c76db4eedecff --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-07-08-09-45-00.bpo-16936.z8o8Pn.rst @@ -0,0 +1 @@ +Allow ``ctypes.wintypes`` to be imported on non-Windows systems. diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c index a72682d7292ca..2261a59e439f6 100644 --- a/Modules/_ctypes/cfield.c +++ b/Modules/_ctypes/cfield.c @@ -684,7 +684,11 @@ i_get_sw(void *ptr, Py_ssize_t size) return PyLong_FromLong(val); } -#ifdef MS_WIN32 +#ifndef MS_WIN32 +/* http://msdn.microsoft.com/en-us/library/cc237864.aspx */ +#define VARIANT_FALSE 0x0000 +#define VARIANT_TRUE 0xFFFF +#endif /* short BOOL - VARIANT_BOOL */ static PyObject * vBOOL_set(void *ptr, PyObject *value, Py_ssize_t size) @@ -706,7 +710,6 @@ vBOOL_get(void *ptr, Py_ssize_t size) { return PyBool_FromLong((long)*(short int *)ptr); } -#endif static PyObject * bool_set(void *ptr, PyObject *value, Py_ssize_t size) @@ -1538,8 +1541,8 @@ static struct fielddesc formattable[] = { #endif #ifdef MS_WIN32 { 'X', BSTR_set, BSTR_get, &ffi_type_pointer}, - { 'v', vBOOL_set, vBOOL_get, &ffi_type_sshort}, #endif + { 'v', vBOOL_set, vBOOL_get, &ffi_type_sshort}, #if SIZEOF__BOOL == 1 { '?', bool_set, bool_get, &ffi_type_uchar}, /* Also fallback for no native _Bool support */ #elif SIZEOF__BOOL == SIZEOF_SHORT From webhook-mailer at python.org Mon Oct 19 18:39:53 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Mon, 19 Oct 2020 22:39:53 -0000 Subject: [Python-checkins] bpo-40901: Describe what "interface name" means on Windows (GH-20694) Message-ID: https://github.com/python/cpython/commit/609a38a36f3006ee00eccc18b25e3b2d577a3e56 commit: 609a38a36f3006ee00eccc18b25e3b2d577a3e56 branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-19T15:39:50-07:00 summary: bpo-40901: Describe what "interface name" means on Windows (GH-20694) (cherry picked from commit f85658a09878c658ae0e0590bfa30e4ce51c9a04) Co-authored-by: Jakub Stasiak files: M Doc/library/socket.rst diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst index 9ab050e8ab82c..5e496ca4d6a9b 100644 --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -1046,6 +1046,19 @@ The :mod:`socket` module also offers various network-related services: .. versionchanged:: 3.8 Windows support was added. + .. note:: + + On Windows network interfaces have different names in different contexts + (all names are examples): + + * UUID: ``{FB605B73-AAC2-49A6-9A2F-25416AEA0573}`` + * name: ``ethernet_32770`` + * friendly name: ``vEthernet (nat)`` + * description: ``Hyper-V Virtual Ethernet Adapter`` + + This function returns names of the second form from the list, ``ethernet_32770`` + in this example case. + .. function:: if_nametoindex(if_name) @@ -1060,6 +1073,9 @@ The :mod:`socket` module also offers various network-related services: .. versionchanged:: 3.8 Windows support was added. + .. seealso:: + "Interface name" is a name as documented in :func:`if_nameindex`. + .. function:: if_indextoname(if_index) @@ -1074,6 +1090,9 @@ The :mod:`socket` module also offers various network-related services: .. versionchanged:: 3.8 Windows support was added. + .. seealso:: + "Interface name" is a name as documented in :func:`if_nameindex`. + .. _socket-objects: From webhook-mailer at python.org Mon Oct 19 18:44:12 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Mon, 19 Oct 2020 22:44:12 -0000 Subject: [Python-checkins] Document that `test.support.bytecode_helper` is new in 3.9 (GH-22618) (GH-22761) Message-ID: https://github.com/python/cpython/commit/6a9c1268cafb3c91a54b577ae7c7e7a937467a4c commit: 6a9c1268cafb3c91a54b577ae7c7e7a937467a4c branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-19T19:44:06-03:00 summary: Document that `test.support.bytecode_helper` is new in 3.9 (GH-22618) (GH-22761) (cherry picked from commit cf693e537dc8aaa14315a7f59baec4a31d1167d3) Co-authored-by: Saiyang Gou Co-authored-by: Saiyang Gou files: M Doc/library/test.rst diff --git a/Doc/library/test.rst b/Doc/library/test.rst index f7e6eba018161..e24f69cda8c86 100644 --- a/Doc/library/test.rst +++ b/Doc/library/test.rst @@ -1609,6 +1609,8 @@ script execution tests. The :mod:`test.support.bytecode_helper` module provides support for testing and inspecting bytecode generation. +.. versionadded:: 3.9 + The module defines the following class: .. class:: BytecodeTestCase(unittest.TestCase) From webhook-mailer at python.org Mon Oct 19 18:47:45 2020 From: webhook-mailer at python.org (Zackery Spytz) Date: Mon, 19 Oct 2020 22:47:45 -0000 Subject: [Python-checkins] bpo-41845: Move PyObject_GenericGetDict() back into the limited API (GH22646) Message-ID: https://github.com/python/cpython/commit/1438c2ac773e87d1f9c97fc22f2346e16bf48773 commit: 1438c2ac773e87d1f9c97fc22f2346e16bf48773 branch: master author: Zackery Spytz committer: GitHub date: 2020-10-19T23:47:37+01:00 summary: bpo-41845: Move PyObject_GenericGetDict() back into the limited API (GH22646) It was moved out of the limited API in 7d95e4072169911b228c9e42367afb5f17fd3db0. This change re-enables it from 3.10, to avoid generating invalid extension modules for earlier versions. files: A Misc/NEWS.d/next/C API/2020-10-11-05-05-53.bpo-41845.ZFvuQM.rst M Include/cpython/dictobject.h M Include/dictobject.h M PC/python3dll.c diff --git a/Include/cpython/dictobject.h b/Include/cpython/dictobject.h index ffe0e97fb35f3..5a15630cfbac7 100644 --- a/Include/cpython/dictobject.h +++ b/Include/cpython/dictobject.h @@ -41,7 +41,6 @@ PyAPI_FUNC(int) _PyDict_DelItem_KnownHash(PyObject *mp, PyObject *key, PyAPI_FUNC(int) _PyDict_DelItemIf(PyObject *mp, PyObject *key, int (*predicate)(PyObject *value)); PyDictKeysObject *_PyDict_NewKeysForClass(void); -PyAPI_FUNC(PyObject *) PyObject_GenericGetDict(PyObject *, void *); PyAPI_FUNC(int) _PyDict_Next( PyObject *mp, Py_ssize_t *pos, PyObject **key, PyObject **value, Py_hash_t *hash); diff --git a/Include/dictobject.h b/Include/dictobject.h index c88b0aa0a5d0f..da5a36ba07f32 100644 --- a/Include/dictobject.h +++ b/Include/dictobject.h @@ -57,6 +57,9 @@ PyAPI_FUNC(int) PyDict_MergeFromSeq2(PyObject *d, PyAPI_FUNC(PyObject *) PyDict_GetItemString(PyObject *dp, const char *key); PyAPI_FUNC(int) PyDict_SetItemString(PyObject *dp, const char *key, PyObject *item); PyAPI_FUNC(int) PyDict_DelItemString(PyObject *dp, const char *key); +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030A0000 +PyAPI_FUNC(PyObject *) PyObject_GenericGetDict(PyObject *, void *); +#endif /* Dictionary (keys, values, items) views */ diff --git a/Misc/NEWS.d/next/C API/2020-10-11-05-05-53.bpo-41845.ZFvuQM.rst b/Misc/NEWS.d/next/C API/2020-10-11-05-05-53.bpo-41845.ZFvuQM.rst new file mode 100644 index 0000000000000..31d3154c3c60e --- /dev/null +++ b/Misc/NEWS.d/next/C API/2020-10-11-05-05-53.bpo-41845.ZFvuQM.rst @@ -0,0 +1,2 @@ +:c:func:`PyObject_GenericGetDict` is available again in the limited API +when targeting 3.10 or later. diff --git a/PC/python3dll.c b/PC/python3dll.c index b9b229ea67d7b..7e4a510177304 100644 --- a/PC/python3dll.c +++ b/PC/python3dll.c @@ -414,6 +414,7 @@ EXPORT_FUNC(PyObject_GC_Track) EXPORT_FUNC(PyObject_GC_UnTrack) EXPORT_FUNC(PyObject_GenericGetAttr) EXPORT_FUNC(PyObject_GenericSetAttr) +EXPORT_FUNC(PyObject_GenericGetDict) EXPORT_FUNC(PyObject_GenericSetDict) EXPORT_FUNC(PyObject_GetAttr) EXPORT_FUNC(PyObject_GetAttrString) From webhook-mailer at python.org Mon Oct 19 18:49:23 2020 From: webhook-mailer at python.org (Mark Sapiro) Date: Mon, 19 Oct 2020 22:49:23 -0000 Subject: [Python-checkins] bpo-27321 Fix email.generator.py to not replace a non-existent header. (GH-18074) Message-ID: https://github.com/python/cpython/commit/bf838227c35212709dc43b3c3c57f8e1655c1d24 commit: bf838227c35212709dc43b3c3c57f8e1655c1d24 branch: master author: Mark Sapiro committer: GitHub date: 2020-10-19T15:49:19-07:00 summary: bpo-27321 Fix email.generator.py to not replace a non-existent header. (GH-18074) This PR replaces #1977. The reason for the replacement is two-fold. The fix itself is different is that if the CTE header doesn't exist in the original message, it is inserted. This is important because the new CTE could be quoted-printable whereas the original is implicit 8bit. Also the tests are different. The test_nonascii_as_string_without_cte test in #1977 doesn't actually test the issue in that it passes without the fix. The test_nonascii_as_string_without_content_type_and_cte test is improved here, and even though it doesn't fail without the fix, it is included for completeness. Automerge-Triggered-By: @warsaw files: A Misc/NEWS.d/next/Library/2020-01-19-18-40-26.bpo-27321.8e6SpM.rst M Lib/email/generator.py M Lib/test/test_email/test_email.py diff --git a/Lib/email/generator.py b/Lib/email/generator.py index ae670c2353c85..c9b121624e08d 100644 --- a/Lib/email/generator.py +++ b/Lib/email/generator.py @@ -186,7 +186,11 @@ def _write(self, msg): # If we munged the cte, copy the message again and re-fix the CTE. if munge_cte: msg = deepcopy(msg) - msg.replace_header('content-transfer-encoding', munge_cte[0]) + # Preserve the header order if the CTE header already exists. + if msg.get('content-transfer-encoding') is None: + msg['Content-Transfer-Encoding'] = munge_cte[0] + else: + msg.replace_header('content-transfer-encoding', munge_cte[0]) msg.replace_header('content-type', munge_cte[1]) # Write the headers. First we see if the message object wants to # handle that itself. If not, we'll do it generically. diff --git a/Lib/test/test_email/test_email.py b/Lib/test/test_email/test_email.py index ba4ed69cc9b34..044b93862a14e 100644 --- a/Lib/test/test_email/test_email.py +++ b/Lib/test/test_email/test_email.py @@ -312,6 +312,41 @@ def test_as_string_policy(self): g.flatten(msg) self.assertEqual(fullrepr, s.getvalue()) + def test_nonascii_as_string_without_cte(self): + m = textwrap.dedent("""\ + MIME-Version: 1.0 + Content-type: text/plain; charset="iso-8859-1" + + Test if non-ascii messages with no Content-Transfer-Encoding set + can be as_string'd: + F?? b?r + """) + source = m.encode('iso-8859-1') + expected = textwrap.dedent("""\ + MIME-Version: 1.0 + Content-type: text/plain; charset="iso-8859-1" + Content-Transfer-Encoding: quoted-printable + + Test if non-ascii messages with no Content-Transfer-Encoding set + can be as_string'd: + F=F6=F6 b=E4r + """) + msg = email.message_from_bytes(source) + self.assertEqual(msg.as_string(), expected) + + def test_nonascii_as_string_without_content_type_and_cte(self): + m = textwrap.dedent("""\ + MIME-Version: 1.0 + + Test if non-ascii messages with no Content-Type nor + Content-Transfer-Encoding set can be as_string'd: + F?? b?r + """) + source = m.encode('iso-8859-1') + expected = source.decode('ascii', 'replace') + msg = email.message_from_bytes(source) + self.assertEqual(msg.as_string(), expected) + def test_as_bytes(self): msg = self._msgobj('msg_01.txt') with openfile('msg_01.txt') as fp: diff --git a/Misc/NEWS.d/next/Library/2020-01-19-18-40-26.bpo-27321.8e6SpM.rst b/Misc/NEWS.d/next/Library/2020-01-19-18-40-26.bpo-27321.8e6SpM.rst new file mode 100644 index 0000000000000..28acf7f6ef919 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-01-19-18-40-26.bpo-27321.8e6SpM.rst @@ -0,0 +1,2 @@ +Fixed KeyError exception when flattening an email to a string attempts to +replace a non-existent Content-Transfer-Encoding header. From webhook-mailer at python.org Mon Oct 19 18:49:59 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Mon, 19 Oct 2020 22:49:59 -0000 Subject: [Python-checkins] bpo-40901: Describe what "interface name" means on Windows (GH-20694) Message-ID: https://github.com/python/cpython/commit/9308173f880f3c0a6447da8335f3ae68edad3b0f commit: 9308173f880f3c0a6447da8335f3ae68edad3b0f branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-19T15:49:55-07:00 summary: bpo-40901: Describe what "interface name" means on Windows (GH-20694) (cherry picked from commit f85658a09878c658ae0e0590bfa30e4ce51c9a04) Co-authored-by: Jakub Stasiak files: M Doc/library/socket.rst diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst index 5bcac20a4c604..faf8a76251420 100755 --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -1091,6 +1091,19 @@ The :mod:`socket` module also offers various network-related services: .. versionchanged:: 3.8 Windows support was added. + .. note:: + + On Windows network interfaces have different names in different contexts + (all names are examples): + + * UUID: ``{FB605B73-AAC2-49A6-9A2F-25416AEA0573}`` + * name: ``ethernet_32770`` + * friendly name: ``vEthernet (nat)`` + * description: ``Hyper-V Virtual Ethernet Adapter`` + + This function returns names of the second form from the list, ``ethernet_32770`` + in this example case. + .. function:: if_nametoindex(if_name) @@ -1105,6 +1118,9 @@ The :mod:`socket` module also offers various network-related services: .. versionchanged:: 3.8 Windows support was added. + .. seealso:: + "Interface name" is a name as documented in :func:`if_nameindex`. + .. function:: if_indextoname(if_index) @@ -1119,6 +1135,9 @@ The :mod:`socket` module also offers various network-related services: .. versionchanged:: 3.8 Windows support was added. + .. seealso:: + "Interface name" is a name as documented in :func:`if_nameindex`. + .. _socket-objects: From webhook-mailer at python.org Mon Oct 19 19:07:28 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Mon, 19 Oct 2020 23:07:28 -0000 Subject: [Python-checkins] bpo-27321 Fix email.generator.py to not replace a non-existent header. (GH-18074) Message-ID: https://github.com/python/cpython/commit/371146a3f8a989964e2a9c0efc7d776815410fac commit: 371146a3f8a989964e2a9c0efc7d776815410fac branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-19T16:07:20-07:00 summary: bpo-27321 Fix email.generator.py to not replace a non-existent header. (GH-18074) This PR replaces GH-1977. The reason for the replacement is two-fold. The fix itself is different is that if the CTE header doesn't exist in the original message, it is inserted. This is important because the new CTE could be quoted-printable whereas the original is implicit 8bit. Also the tests are different. The test_nonascii_as_string_without_cte test in GH-1977 doesn't actually test the issue in that it passes without the fix. The test_nonascii_as_string_without_content_type_and_cte test is improved here, and even though it doesn't fail without the fix, it is included for completeness. Automerge-Triggered-By: @warsaw (cherry picked from commit bf838227c35212709dc43b3c3c57f8e1655c1d24) Co-authored-by: Mark Sapiro files: A Misc/NEWS.d/next/Library/2020-01-19-18-40-26.bpo-27321.8e6SpM.rst M Lib/email/generator.py M Lib/test/test_email/test_email.py diff --git a/Lib/email/generator.py b/Lib/email/generator.py index ae670c2353c85..c9b121624e08d 100644 --- a/Lib/email/generator.py +++ b/Lib/email/generator.py @@ -186,7 +186,11 @@ def _write(self, msg): # If we munged the cte, copy the message again and re-fix the CTE. if munge_cte: msg = deepcopy(msg) - msg.replace_header('content-transfer-encoding', munge_cte[0]) + # Preserve the header order if the CTE header already exists. + if msg.get('content-transfer-encoding') is None: + msg['Content-Transfer-Encoding'] = munge_cte[0] + else: + msg.replace_header('content-transfer-encoding', munge_cte[0]) msg.replace_header('content-type', munge_cte[1]) # Write the headers. First we see if the message object wants to # handle that itself. If not, we'll do it generically. diff --git a/Lib/test/test_email/test_email.py b/Lib/test/test_email/test_email.py index 9e5c6adca835d..d262dd043c0cd 100644 --- a/Lib/test/test_email/test_email.py +++ b/Lib/test/test_email/test_email.py @@ -311,6 +311,41 @@ def test_as_string_policy(self): g.flatten(msg) self.assertEqual(fullrepr, s.getvalue()) + def test_nonascii_as_string_without_cte(self): + m = textwrap.dedent("""\ + MIME-Version: 1.0 + Content-type: text/plain; charset="iso-8859-1" + + Test if non-ascii messages with no Content-Transfer-Encoding set + can be as_string'd: + F?? b?r + """) + source = m.encode('iso-8859-1') + expected = textwrap.dedent("""\ + MIME-Version: 1.0 + Content-type: text/plain; charset="iso-8859-1" + Content-Transfer-Encoding: quoted-printable + + Test if non-ascii messages with no Content-Transfer-Encoding set + can be as_string'd: + F=F6=F6 b=E4r + """) + msg = email.message_from_bytes(source) + self.assertEqual(msg.as_string(), expected) + + def test_nonascii_as_string_without_content_type_and_cte(self): + m = textwrap.dedent("""\ + MIME-Version: 1.0 + + Test if non-ascii messages with no Content-Type nor + Content-Transfer-Encoding set can be as_string'd: + F?? b?r + """) + source = m.encode('iso-8859-1') + expected = source.decode('ascii', 'replace') + msg = email.message_from_bytes(source) + self.assertEqual(msg.as_string(), expected) + def test_as_bytes(self): msg = self._msgobj('msg_01.txt') with openfile('msg_01.txt') as fp: diff --git a/Misc/NEWS.d/next/Library/2020-01-19-18-40-26.bpo-27321.8e6SpM.rst b/Misc/NEWS.d/next/Library/2020-01-19-18-40-26.bpo-27321.8e6SpM.rst new file mode 100644 index 0000000000000..28acf7f6ef919 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-01-19-18-40-26.bpo-27321.8e6SpM.rst @@ -0,0 +1,2 @@ +Fixed KeyError exception when flattening an email to a string attempts to +replace a non-existent Content-Transfer-Encoding header. From webhook-mailer at python.org Mon Oct 19 19:08:25 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Mon, 19 Oct 2020 23:08:25 -0000 Subject: [Python-checkins] [doc] Add link to Generic in typing (GH-22125) Message-ID: https://github.com/python/cpython/commit/335eb57823779af770d0faec9df27f60c73b8690 commit: 335eb57823779af770d0faec9df27f60c73b8690 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-19T16:08:19-07:00 summary: [doc] Add link to Generic in typing (GH-22125) (cherry picked from commit 5bfd60fc2bf26bd6fa23a3a50c7990b7f68b3ea3) Co-authored-by: Andre Delfino files: M Doc/library/typing.rst diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index e8f34e44a0c0e..67e8a3fa9e50d 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -821,7 +821,7 @@ These are not used in annotations. They are building blocks for creating generic Type variables exist primarily for the benefit of static type checkers. They serve as the parameters for generic types as well - as for generic function definitions. See class Generic for more + as for generic function definitions. See :class:`Generic` for more information on generic types. Generic functions work as follows:: def repeat(x: T, n: int) -> Sequence[T]: From webhook-mailer at python.org Mon Oct 19 19:11:43 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Mon, 19 Oct 2020 23:11:43 -0000 Subject: [Python-checkins] bpo-27321 Fix email.generator.py to not replace a non-existent header. (GH-18074) Message-ID: https://github.com/python/cpython/commit/72ce82abcf9051b18a05350936de7ecab7306662 commit: 72ce82abcf9051b18a05350936de7ecab7306662 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-19T16:11:37-07:00 summary: bpo-27321 Fix email.generator.py to not replace a non-existent header. (GH-18074) This PR replaces GH-1977. The reason for the replacement is two-fold. The fix itself is different is that if the CTE header doesn't exist in the original message, it is inserted. This is important because the new CTE could be quoted-printable whereas the original is implicit 8bit. Also the tests are different. The test_nonascii_as_string_without_cte test in GH-1977 doesn't actually test the issue in that it passes without the fix. The test_nonascii_as_string_without_content_type_and_cte test is improved here, and even though it doesn't fail without the fix, it is included for completeness. Automerge-Triggered-By: @warsaw (cherry picked from commit bf838227c35212709dc43b3c3c57f8e1655c1d24) Co-authored-by: Mark Sapiro files: A Misc/NEWS.d/next/Library/2020-01-19-18-40-26.bpo-27321.8e6SpM.rst M Lib/email/generator.py M Lib/test/test_email/test_email.py diff --git a/Lib/email/generator.py b/Lib/email/generator.py index ae670c2353c85..c9b121624e08d 100644 --- a/Lib/email/generator.py +++ b/Lib/email/generator.py @@ -186,7 +186,11 @@ def _write(self, msg): # If we munged the cte, copy the message again and re-fix the CTE. if munge_cte: msg = deepcopy(msg) - msg.replace_header('content-transfer-encoding', munge_cte[0]) + # Preserve the header order if the CTE header already exists. + if msg.get('content-transfer-encoding') is None: + msg['Content-Transfer-Encoding'] = munge_cte[0] + else: + msg.replace_header('content-transfer-encoding', munge_cte[0]) msg.replace_header('content-type', munge_cte[1]) # Write the headers. First we see if the message object wants to # handle that itself. If not, we'll do it generically. diff --git a/Lib/test/test_email/test_email.py b/Lib/test/test_email/test_email.py index 59eabb0092194..ab68cdd8b5aed 100644 --- a/Lib/test/test_email/test_email.py +++ b/Lib/test/test_email/test_email.py @@ -311,6 +311,41 @@ def test_as_string_policy(self): g.flatten(msg) self.assertEqual(fullrepr, s.getvalue()) + def test_nonascii_as_string_without_cte(self): + m = textwrap.dedent("""\ + MIME-Version: 1.0 + Content-type: text/plain; charset="iso-8859-1" + + Test if non-ascii messages with no Content-Transfer-Encoding set + can be as_string'd: + F?? b?r + """) + source = m.encode('iso-8859-1') + expected = textwrap.dedent("""\ + MIME-Version: 1.0 + Content-type: text/plain; charset="iso-8859-1" + Content-Transfer-Encoding: quoted-printable + + Test if non-ascii messages with no Content-Transfer-Encoding set + can be as_string'd: + F=F6=F6 b=E4r + """) + msg = email.message_from_bytes(source) + self.assertEqual(msg.as_string(), expected) + + def test_nonascii_as_string_without_content_type_and_cte(self): + m = textwrap.dedent("""\ + MIME-Version: 1.0 + + Test if non-ascii messages with no Content-Type nor + Content-Transfer-Encoding set can be as_string'd: + F?? b?r + """) + source = m.encode('iso-8859-1') + expected = source.decode('ascii', 'replace') + msg = email.message_from_bytes(source) + self.assertEqual(msg.as_string(), expected) + def test_as_bytes(self): msg = self._msgobj('msg_01.txt') with openfile('msg_01.txt') as fp: diff --git a/Misc/NEWS.d/next/Library/2020-01-19-18-40-26.bpo-27321.8e6SpM.rst b/Misc/NEWS.d/next/Library/2020-01-19-18-40-26.bpo-27321.8e6SpM.rst new file mode 100644 index 0000000000000..28acf7f6ef919 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-01-19-18-40-26.bpo-27321.8e6SpM.rst @@ -0,0 +1,2 @@ +Fixed KeyError exception when flattening an email to a string attempts to +replace a non-existent Content-Transfer-Encoding header. From webhook-mailer at python.org Mon Oct 19 19:30:11 2020 From: webhook-mailer at python.org (Ruben Vorderman) Date: Mon, 19 Oct 2020 23:30:11 -0000 Subject: [Python-checkins] bpo-41586: Add pipesize parameter to subprocess & F_GETPIPE_SZ and F_SETPIPE_SZ to fcntl. (GH-21921) Message-ID: https://github.com/python/cpython/commit/23c0fb8edd16fe6d796df2853a5369fd783e05b7 commit: 23c0fb8edd16fe6d796df2853a5369fd783e05b7 branch: master author: Ruben Vorderman committer: GitHub date: 2020-10-19T16:30:02-07:00 summary: bpo-41586: Add pipesize parameter to subprocess & F_GETPIPE_SZ and F_SETPIPE_SZ to fcntl. (GH-21921) * Add F_SETPIPE_SZ and F_GETPIPE_SZ to fcntl module * Add pipesize parameter for subprocess.Popen class This will allow the user to control the size of the pipes. On linux the default is 64K. When a pipe is full it blocks for writing. When a pipe is empty it blocks for reading. On processes that are very fast this can lead to a lot of wasted CPU cycles. On a typical Linux system the max pipe size is 1024K which is much better. For high performance-oriented libraries such as xopen it is nice to be able to set the pipe size. The workaround without this feature is to use my_popen_process.stdout.fileno() in conjuction with fcntl and 1031 (value of F_SETPIPE_SZ) to acquire this behavior. files: A Misc/NEWS.d/next/Library/2020-08-19-08-32-13.bpo-41586.IYjmjK.rst M Doc/library/fcntl.rst M Doc/library/subprocess.rst M Lib/subprocess.py M Lib/test/test_fcntl.py M Lib/test/test_subprocess.py M Misc/ACKS M Modules/fcntlmodule.c diff --git a/Doc/library/fcntl.rst b/Doc/library/fcntl.rst index 07a15d27216e9..9d8021150c42f 100644 --- a/Doc/library/fcntl.rst +++ b/Doc/library/fcntl.rst @@ -39,6 +39,11 @@ descriptor. On Linux(>=3.15), the fcntl module exposes the ``F_OFD_GETLK``, ``F_OFD_SETLK`` and ``F_OFD_SETLKW`` constants, which working with open file description locks. +.. versionchanged:: 3.10 + On Linux >= 2.6.11, the fcntl module exposes the ``F_GETPIPE_SZ`` and + ``F_SETPIPE_SZ`` constants, which allow to check and modify a pipe's size + respectively. + The module defines the following functions: diff --git a/Doc/library/subprocess.rst b/Doc/library/subprocess.rst index e37cc980e9757..7993b103f473e 100644 --- a/Doc/library/subprocess.rst +++ b/Doc/library/subprocess.rst @@ -341,7 +341,7 @@ functions. startupinfo=None, creationflags=0, restore_signals=True, \ start_new_session=False, pass_fds=(), \*, group=None, \ extra_groups=None, user=None, umask=-1, \ - encoding=None, errors=None, text=None) + encoding=None, errors=None, text=None, pipesize=-1) Execute a child program in a new process. On POSIX, the class uses :meth:`os.execvp`-like behavior to execute the child program. On Windows, @@ -625,6 +625,14 @@ functions. * :data:`CREATE_DEFAULT_ERROR_MODE` * :data:`CREATE_BREAKAWAY_FROM_JOB` + *pipesize* can be used to change the size of the pipe when + :data:`PIPE` is used for *stdin*, *stdout* or *stderr*. The size of the pipe + is only changed on platforms that support this (only Linux at this time of + writing). Other platforms will ignore this parameter. + + .. versionadded:: 3.10 + The ``pipesize`` parameter was added. + Popen objects are supported as context managers via the :keyword:`with` statement: on exit, standard file descriptors are closed, and the process is waited for. :: diff --git a/Lib/subprocess.py b/Lib/subprocess.py index 86fdf27f9b03b..6a6c2fc98e83f 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -62,6 +62,11 @@ import grp except ImportError: grp = None +try: + import fcntl +except ImportError: + fcntl = None + __all__ = ["Popen", "PIPE", "STDOUT", "call", "check_call", "getstatusoutput", "getoutput", "check_output", "run", "CalledProcessError", "DEVNULL", @@ -756,7 +761,7 @@ def __init__(self, args, bufsize=-1, executable=None, startupinfo=None, creationflags=0, restore_signals=True, start_new_session=False, pass_fds=(), *, user=None, group=None, extra_groups=None, - encoding=None, errors=None, text=None, umask=-1): + encoding=None, errors=None, text=None, umask=-1, pipesize=-1): """Create new Popen instance.""" _cleanup() # Held while anything is calling waitpid before returncode has been @@ -773,6 +778,11 @@ def __init__(self, args, bufsize=-1, executable=None, if not isinstance(bufsize, int): raise TypeError("bufsize must be an integer") + if pipesize is None: + pipesize = -1 # Restore default + if not isinstance(pipesize, int): + raise TypeError("pipesize must be an integer") + if _mswindows: if preexec_fn is not None: raise ValueError("preexec_fn is not supported on Windows " @@ -797,6 +807,7 @@ def __init__(self, args, bufsize=-1, executable=None, self.returncode = None self.encoding = encoding self.errors = errors + self.pipesize = pipesize # Validate the combinations of text and universal_newlines if (text is not None and universal_newlines is not None @@ -1575,6 +1586,8 @@ def _get_handles(self, stdin, stdout, stderr): pass elif stdin == PIPE: p2cread, p2cwrite = os.pipe() + if self.pipesize > 0 and hasattr(fcntl, "F_SETPIPE_SZ"): + fcntl.fcntl(p2cwrite, fcntl.F_SETPIPE_SZ, self.pipesize) elif stdin == DEVNULL: p2cread = self._get_devnull() elif isinstance(stdin, int): @@ -1587,6 +1600,8 @@ def _get_handles(self, stdin, stdout, stderr): pass elif stdout == PIPE: c2pread, c2pwrite = os.pipe() + if self.pipesize > 0 and hasattr(fcntl, "F_SETPIPE_SZ"): + fcntl.fcntl(c2pwrite, fcntl.F_SETPIPE_SZ, self.pipesize) elif stdout == DEVNULL: c2pwrite = self._get_devnull() elif isinstance(stdout, int): @@ -1599,6 +1614,8 @@ def _get_handles(self, stdin, stdout, stderr): pass elif stderr == PIPE: errread, errwrite = os.pipe() + if self.pipesize > 0 and hasattr(fcntl, "F_SETPIPE_SZ"): + fcntl.fcntl(errwrite, fcntl.F_SETPIPE_SZ, self.pipesize) elif stderr == STDOUT: if c2pwrite != -1: errwrite = c2pwrite diff --git a/Lib/test/test_fcntl.py b/Lib/test/test_fcntl.py index 7e1092083269e..8d6e9ff788454 100644 --- a/Lib/test/test_fcntl.py +++ b/Lib/test/test_fcntl.py @@ -190,6 +190,19 @@ def test_fcntl_f_getpath(self): res = fcntl.fcntl(self.f.fileno(), fcntl.F_GETPATH, bytes(len(expected))) self.assertEqual(expected, res) + @unittest.skipIf(not (hasattr(fcntl, "F_SETPIPE_SZ") and hasattr(fcntl, "F_GETPIPE_SZ")), + "F_SETPIPE_SZ and F_GETPIPE_SZ are not available on all unix platforms.") + def test_fcntl_f_pipesize(self): + test_pipe_r, test_pipe_w = os.pipe() + # Get the default pipesize with F_GETPIPE_SZ + pipesize_default = fcntl.fcntl(test_pipe_w, fcntl.F_GETPIPE_SZ) + # Multiply the default with 2 to get a new value. + fcntl.fcntl(test_pipe_w, fcntl.F_SETPIPE_SZ, pipesize_default * 2) + self.assertEqual(fcntl.fcntl(test_pipe_w, fcntl.F_GETPIPE_SZ), pipesize_default * 2) + os.close(test_pipe_r) + os.close(test_pipe_w) + + def test_main(): run_unittest(TestFcntl) diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index 434ba567db0a5..8b576c036ef0d 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -39,6 +39,11 @@ except ImportError: grp = None +try: + import fcntl +except: + fcntl = None + if support.PGO: raise unittest.SkipTest("test is not helpful for PGO") @@ -661,6 +666,46 @@ def test_stdin_devnull(self): p.wait() self.assertEqual(p.stdin, None) + def test_pipesizes(self): + # stdin redirection + pipesize = 16 * 1024 + p = subprocess.Popen([sys.executable, "-c", + 'import sys; sys.stdin.read(); sys.stdout.write("out"); sys.stderr.write("error!")'], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + pipesize=pipesize) + # We only assert pipe size has changed on platforms that support it. + if sys.platform != "win32" and hasattr(fcntl, "F_GETPIPE_SZ"): + for fifo in [p.stdin, p.stdout, p.stderr]: + self.assertEqual(fcntl.fcntl(fifo.fileno(), fcntl.F_GETPIPE_SZ), pipesize) + # Windows pipe size can be acquired with the GetNamedPipeInfoFunction + # https://docs.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-getnamedpipeinfo + # However, this function is not yet in _winapi. + p.stdin.write(b"pear") + p.stdin.close() + p.wait() + + def test_pipesize_default(self): + p = subprocess.Popen([sys.executable, "-c", + 'import sys; sys.stdin.read(); sys.stdout.write("out");' + ' sys.stderr.write("error!")'], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + pipesize=-1) + # UNIX tests using fcntl + if sys.platform != "win32" and hasattr(fcntl, "F_GETPIPE_SZ"): + fp_r, fp_w = os.pipe() + default_pipesize = fcntl.fcntl(fp_w, fcntl.F_GETPIPE_SZ) + for fifo in [p.stdin, p.stdout, p.stderr]: + self.assertEqual( + fcntl.fcntl(fifo.fileno(), fcntl.F_GETPIPE_SZ), default_pipesize) + # On other platforms we cannot test the pipe size (yet). But above code + # using pipesize=-1 should not crash. + p.stdin.close() + p.wait() + def test_env(self): newenv = os.environ.copy() newenv["FRUIT"] = "orange" @@ -3503,7 +3548,7 @@ def test_getoutput(self): def test__all__(self): """Ensure that __all__ is populated properly.""" - intentionally_excluded = {"list2cmdline", "Handle", "pwd", "grp"} + intentionally_excluded = {"list2cmdline", "Handle", "pwd", "grp", "fcntl"} exported = set(subprocess.__all__) possible_exports = set() import types diff --git a/Misc/ACKS b/Misc/ACKS index d81d0a255145c..687bcb2c6abab 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1818,6 +1818,7 @@ Johannes Vogel Michael Vogt Radu Voicilas Alex Volkov +Ruben Vorderman Guido Vranken Martijn Vries Sjoerd de Vries diff --git a/Misc/NEWS.d/next/Library/2020-08-19-08-32-13.bpo-41586.IYjmjK.rst b/Misc/NEWS.d/next/Library/2020-08-19-08-32-13.bpo-41586.IYjmjK.rst new file mode 100644 index 0000000000000..40461679ebdfe --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-08-19-08-32-13.bpo-41586.IYjmjK.rst @@ -0,0 +1,2 @@ +Add F_SETPIPE_SZ and F_GETPIPE_SZ to fcntl module. Allow setting pipesize on +subprocess.Popen. diff --git a/Modules/fcntlmodule.c b/Modules/fcntlmodule.c index afd28106faf4b..cdf0f9bf3790e 100644 --- a/Modules/fcntlmodule.c +++ b/Modules/fcntlmodule.c @@ -565,6 +565,14 @@ all_ins(PyObject* m) if (PyModule_AddIntMacro(m, F_SHLCK)) return -1; #endif +/* Linux specifics */ +#ifdef F_SETPIPE_SZ + if (PyModule_AddIntMacro(m, F_SETPIPE_SZ)) return -1; +#endif +#ifdef F_GETPIPE_SZ + if (PyModule_AddIntMacro(m, F_GETPIPE_SZ)) return -1; +#endif + /* OS X specifics */ #ifdef F_FULLFSYNC if (PyModule_AddIntMacro(m, F_FULLFSYNC)) return -1; From webhook-mailer at python.org Mon Oct 19 21:19:01 2020 From: webhook-mailer at python.org (Justin Turner Arthur) Date: Tue, 20 Oct 2020 01:19:01 -0000 Subject: [Python-checkins] bpo-38912: fix close before connect callback in test_asyncio SSL tests (GH-22691) Message-ID: https://github.com/python/cpython/commit/de73d432bb29f6439f2db16cb991e15e09c70c26 commit: de73d432bb29f6439f2db16cb991e15e09c70c26 branch: master author: Justin Turner Arthur committer: GitHub date: 2020-10-19T21:18:57-04:00 summary: bpo-38912: fix close before connect callback in test_asyncio SSL tests (GH-22691) Reduces the rate at which the ENV CHANGED failure occurs in test_asyncio SSL tests (due to unclosed transport), but does not 100% resolve it. files: M Lib/test/test_asyncio/test_events.py M Misc/ACKS diff --git a/Lib/test/test_asyncio/test_events.py b/Lib/test/test_asyncio/test_events.py index ef9d6fc48e9f8..b8fe466cd53df 100644 --- a/Lib/test/test_asyncio/test_events.py +++ b/Lib/test/test_asyncio/test_events.py @@ -1076,6 +1076,7 @@ def test_create_unix_server_ssl_verified(self): ssl=sslcontext_client, server_hostname='localhost') client, pr = self.loop.run_until_complete(f_c) + self.loop.run_until_complete(proto.connected) # close connection proto.transport.close() @@ -1101,6 +1102,7 @@ def test_create_server_ssl_verified(self): ssl=sslcontext_client, server_hostname='localhost') client, pr = self.loop.run_until_complete(f_c) + self.loop.run_until_complete(proto.connected) # extra info is available self.check_ssl_extra_info(client, peername=(host, port), diff --git a/Misc/ACKS b/Misc/ACKS index 687bcb2c6abab..0aab4567d6ee9 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -70,6 +70,7 @@ Alexandru Ardelean Emmanuel Arias Alicia Arlen Jeffrey Armstrong +Justin Turner Arthur Jason Asbahr David Ascher Ammar Askar From webhook-mailer at python.org Mon Oct 19 21:40:10 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Tue, 20 Oct 2020 01:40:10 -0000 Subject: [Python-checkins] bpo-38912: fix close before connect callback in test_asyncio SSL tests (GH-22691) Message-ID: https://github.com/python/cpython/commit/e43bee7e114ec361efde34f4b546f984574e6653 commit: e43bee7e114ec361efde34f4b546f984574e6653 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-19T18:40:01-07:00 summary: bpo-38912: fix close before connect callback in test_asyncio SSL tests (GH-22691) Reduces the rate at which the ENV CHANGED failure occurs in test_asyncio SSL tests (due to unclosed transport), but does not 100% resolve it. (cherry picked from commit de73d432bb29f6439f2db16cb991e15e09c70c26) Co-authored-by: Justin Turner Arthur files: M Lib/test/test_asyncio/test_events.py M Misc/ACKS diff --git a/Lib/test/test_asyncio/test_events.py b/Lib/test/test_asyncio/test_events.py index 7bc9c9a344c9d..92b1522e956c4 100644 --- a/Lib/test/test_asyncio/test_events.py +++ b/Lib/test/test_asyncio/test_events.py @@ -1075,6 +1075,7 @@ def test_create_unix_server_ssl_verified(self): ssl=sslcontext_client, server_hostname='localhost') client, pr = self.loop.run_until_complete(f_c) + self.loop.run_until_complete(proto.connected) # close connection proto.transport.close() @@ -1100,6 +1101,7 @@ def test_create_server_ssl_verified(self): ssl=sslcontext_client, server_hostname='localhost') client, pr = self.loop.run_until_complete(f_c) + self.loop.run_until_complete(proto.connected) # extra info is available self.check_ssl_extra_info(client, peername=(host, port), diff --git a/Misc/ACKS b/Misc/ACKS index 3125600284daf..2aa9a9de93703 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -70,6 +70,7 @@ Alexandru Ardelean Emmanuel Arias Alicia Arlen Jeffrey Armstrong +Justin Turner Arthur Jason Asbahr David Ascher Ammar Askar From webhook-mailer at python.org Mon Oct 19 21:44:09 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Tue, 20 Oct 2020 01:44:09 -0000 Subject: [Python-checkins] bpo-38912: fix close before connect callback in test_asyncio SSL tests (GH-22691) Message-ID: https://github.com/python/cpython/commit/3faef630a44be2d1ea5516e85e95fef4fd027a95 commit: 3faef630a44be2d1ea5516e85e95fef4fd027a95 branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-19T18:44:05-07:00 summary: bpo-38912: fix close before connect callback in test_asyncio SSL tests (GH-22691) Reduces the rate at which the ENV CHANGED failure occurs in test_asyncio SSL tests (due to unclosed transport), but does not 100% resolve it. (cherry picked from commit de73d432bb29f6439f2db16cb991e15e09c70c26) Co-authored-by: Justin Turner Arthur files: M Lib/test/test_asyncio/test_events.py M Misc/ACKS diff --git a/Lib/test/test_asyncio/test_events.py b/Lib/test/test_asyncio/test_events.py index 85838f17678af..cec7e94a4f3c4 100644 --- a/Lib/test/test_asyncio/test_events.py +++ b/Lib/test/test_asyncio/test_events.py @@ -1071,6 +1071,7 @@ def test_create_unix_server_ssl_verified(self): ssl=sslcontext_client, server_hostname='localhost') client, pr = self.loop.run_until_complete(f_c) + self.loop.run_until_complete(proto.connected) # close connection proto.transport.close() @@ -1096,6 +1097,7 @@ def test_create_server_ssl_verified(self): ssl=sslcontext_client, server_hostname='localhost') client, pr = self.loop.run_until_complete(f_c) + self.loop.run_until_complete(proto.connected) # extra info is available self.check_ssl_extra_info(client, peername=(host, port), diff --git a/Misc/ACKS b/Misc/ACKS index 5584d5d54a0b2..35cabc7248bb4 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -67,6 +67,7 @@ Alexandru Ardelean Emmanuel Arias Alicia Arlen Jeffrey Armstrong +Justin Turner Arthur Jason Asbahr David Ascher Ammar Askar From webhook-mailer at python.org Mon Oct 19 22:34:47 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Tue, 20 Oct 2020 02:34:47 -0000 Subject: [Python-checkins] bpo-42051: Reject XML entity declarations in plist files (GH-22760) Message-ID: https://github.com/python/cpython/commit/479553c7c11306a09ce34edb6ef208133b7b95fe commit: 479553c7c11306a09ce34edb6ef208133b7b95fe branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-19T19:34:37-07:00 summary: bpo-42051: Reject XML entity declarations in plist files (GH-22760) (cherry picked from commit 05ee790f4d1cd8725a90b54268fc1dfe5b4d1fa2) Co-authored-by: Ronald Oussoren files: A Misc/NEWS.d/next/Security/2020-10-19-10-56-27.bpo-42051.EU_B7u.rst M Lib/plistlib.py M Lib/test/test_plistlib.py diff --git a/Lib/plistlib.py b/Lib/plistlib.py index aff5fe36ca38b..ba7ac1936479f 100644 --- a/Lib/plistlib.py +++ b/Lib/plistlib.py @@ -173,9 +173,16 @@ def parse(self, fileobj): self.parser.StartElementHandler = self.handle_begin_element self.parser.EndElementHandler = self.handle_end_element self.parser.CharacterDataHandler = self.handle_data + self.parser.EntityDeclHandler = self.handle_entity_decl self.parser.ParseFile(fileobj) return self.root + def handle_entity_decl(self, entity_name, is_parameter_entity, value, base, system_id, public_id, notation_name): + # Reject plist files with entity declarations to avoid XML vulnerabilies in expat. + # Regular plist files don't contain those declerations, and Apple's plutil tool does not + # accept them either. + raise InvalidFileException("XML entity declarations are not supported in plist files") + def handle_begin_element(self, element, attrs): self.data = [] handler = getattr(self, "begin_" + element, None) diff --git a/Lib/test/test_plistlib.py b/Lib/test/test_plistlib.py index e82a53c533df0..7cc6bad4235cc 100644 --- a/Lib/test/test_plistlib.py +++ b/Lib/test/test_plistlib.py @@ -105,6 +105,19 @@ AAABOQ=='''), } +XML_PLIST_WITH_ENTITY=b'''\ + + + ]> + + + A + &entity; + + +''' + class TestPlistlib(unittest.TestCase): @@ -523,6 +536,11 @@ def test_modified_uid_huge(self): with self.assertRaises(OverflowError): plistlib.dumps(huge_uid, fmt=plistlib.FMT_BINARY) + def test_xml_plist_with_entity_decl(self): + with self.assertRaisesRegex(plistlib.InvalidFileException, + "XML entity declarations are not supported"): + plistlib.loads(XML_PLIST_WITH_ENTITY, fmt=plistlib.FMT_XML) + class TestBinaryPlistlib(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Security/2020-10-19-10-56-27.bpo-42051.EU_B7u.rst b/Misc/NEWS.d/next/Security/2020-10-19-10-56-27.bpo-42051.EU_B7u.rst new file mode 100644 index 0000000000000..e865ed12a0387 --- /dev/null +++ b/Misc/NEWS.d/next/Security/2020-10-19-10-56-27.bpo-42051.EU_B7u.rst @@ -0,0 +1,3 @@ +The :mod:`plistlib` module no longer accepts entity declarations in XML +plist files to avoid XML vulnerabilities. This should not affect users as +entity declarations are not used in regular plist files. From webhook-mailer at python.org Mon Oct 19 22:35:28 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Tue, 20 Oct 2020 02:35:28 -0000 Subject: [Python-checkins] bpo-42051: Reject XML entity declarations in plist files (GH-22760) Message-ID: https://github.com/python/cpython/commit/65894cac0835cb8f469f649e20aa1be8bf89f5ae commit: 65894cac0835cb8f469f649e20aa1be8bf89f5ae branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-19T19:35:23-07:00 summary: bpo-42051: Reject XML entity declarations in plist files (GH-22760) (cherry picked from commit 05ee790f4d1cd8725a90b54268fc1dfe5b4d1fa2) Co-authored-by: Ronald Oussoren files: A Misc/NEWS.d/next/Security/2020-10-19-10-56-27.bpo-42051.EU_B7u.rst M Lib/plistlib.py M Lib/test/test_plistlib.py diff --git a/Lib/plistlib.py b/Lib/plistlib.py index 04f8a87634d35..533ed1351f135 100644 --- a/Lib/plistlib.py +++ b/Lib/plistlib.py @@ -285,9 +285,16 @@ def parse(self, fileobj): self.parser.StartElementHandler = self.handle_begin_element self.parser.EndElementHandler = self.handle_end_element self.parser.CharacterDataHandler = self.handle_data + self.parser.EntityDeclHandler = self.handle_entity_decl self.parser.ParseFile(fileobj) return self.root + def handle_entity_decl(self, entity_name, is_parameter_entity, value, base, system_id, public_id, notation_name): + # Reject plist files with entity declarations to avoid XML vulnerabilies in expat. + # Regular plist files don't contain those declerations, and Apple's plutil tool does not + # accept them either. + raise InvalidFileException("XML entity declarations are not supported in plist files") + def handle_begin_element(self, element, attrs): self.data = [] handler = getattr(self, "begin_" + element, None) diff --git a/Lib/test/test_plistlib.py b/Lib/test/test_plistlib.py index 0d887e210dde5..de1c848ae265f 100644 --- a/Lib/test/test_plistlib.py +++ b/Lib/test/test_plistlib.py @@ -105,6 +105,19 @@ AAABOQ=='''), } +XML_PLIST_WITH_ENTITY=b'''\ + + + ]> + + + A + &entity; + + +''' + class TestPlistlib(unittest.TestCase): @@ -525,6 +538,11 @@ def test_modified_uid_huge(self): with self.assertRaises(OverflowError): plistlib.dumps(huge_uid, fmt=plistlib.FMT_BINARY) + def test_xml_plist_with_entity_decl(self): + with self.assertRaisesRegex(plistlib.InvalidFileException, + "XML entity declarations are not supported"): + plistlib.loads(XML_PLIST_WITH_ENTITY, fmt=plistlib.FMT_XML) + class TestBinaryPlistlib(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Security/2020-10-19-10-56-27.bpo-42051.EU_B7u.rst b/Misc/NEWS.d/next/Security/2020-10-19-10-56-27.bpo-42051.EU_B7u.rst new file mode 100644 index 0000000000000..e865ed12a0387 --- /dev/null +++ b/Misc/NEWS.d/next/Security/2020-10-19-10-56-27.bpo-42051.EU_B7u.rst @@ -0,0 +1,3 @@ +The :mod:`plistlib` module no longer accepts entity declarations in XML +plist files to avoid XML vulnerabilities. This should not affect users as +entity declarations are not used in regular plist files. From webhook-mailer at python.org Mon Oct 19 22:36:35 2020 From: webhook-mailer at python.org (Ned Deily) Date: Tue, 20 Oct 2020 02:36:35 -0000 Subject: [Python-checkins] bpo-42051: Reject XML entity declarations in plist files (#22760) (GH-22801) Message-ID: https://github.com/python/cpython/commit/e512bc799e3864fe3b1351757261762d63471efc commit: e512bc799e3864fe3b1351757261762d63471efc branch: 3.7 author: Ned Deily committer: GitHub date: 2020-10-19T22:36:27-04:00 summary: bpo-42051: Reject XML entity declarations in plist files (#22760) (GH-22801) Co-authored-by: Ronald Oussoren files: A Misc/NEWS.d/next/Security/2020-10-19-10-56-27.bpo-42051.EU_B7u.rst M Lib/plistlib.py M Lib/test/test_plistlib.py diff --git a/Lib/plistlib.py b/Lib/plistlib.py index 33b79a133b0a8..b273a15645d94 100644 --- a/Lib/plistlib.py +++ b/Lib/plistlib.py @@ -257,9 +257,16 @@ def parse(self, fileobj): self.parser.StartElementHandler = self.handle_begin_element self.parser.EndElementHandler = self.handle_end_element self.parser.CharacterDataHandler = self.handle_data + self.parser.EntityDeclHandler = self.handle_entity_decl self.parser.ParseFile(fileobj) return self.root + def handle_entity_decl(self, entity_name, is_parameter_entity, value, base, system_id, public_id, notation_name): + # Reject plist files with entity declarations to avoid XML vulnerabilies in expat. + # Regular plist files don't contain those declerations, and Apple's plutil tool does not + # accept them either. + raise InvalidFileException("XML entity declarations are not supported in plist files") + def handle_begin_element(self, element, attrs): self.data = [] handler = getattr(self, "begin_" + element, None) diff --git a/Lib/test/test_plistlib.py b/Lib/test/test_plistlib.py index 8d8e0a750a224..bfe06fd35831d 100644 --- a/Lib/test/test_plistlib.py +++ b/Lib/test/test_plistlib.py @@ -90,6 +90,19 @@ xQHHAsQC0gAAAAAAAAIBAAAAAAAAADkAAAAAAAAAAAAAAAAAAALs'''), } +XML_PLIST_WITH_ENTITY=b'''\ + + + ]> + + + A + &entity; + + +''' + class TestPlistlib(unittest.TestCase): @@ -443,6 +456,11 @@ def test_xml_encodings(self): pl2 = plistlib.loads(data) self.assertEqual(dict(pl), dict(pl2)) + def test_xml_plist_with_entity_decl(self): + with self.assertRaisesRegex(plistlib.InvalidFileException, + "XML entity declarations are not supported"): + plistlib.loads(XML_PLIST_WITH_ENTITY, fmt=plistlib.FMT_XML) + class TestBinaryPlistlib(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Security/2020-10-19-10-56-27.bpo-42051.EU_B7u.rst b/Misc/NEWS.d/next/Security/2020-10-19-10-56-27.bpo-42051.EU_B7u.rst new file mode 100644 index 0000000000000..e865ed12a0387 --- /dev/null +++ b/Misc/NEWS.d/next/Security/2020-10-19-10-56-27.bpo-42051.EU_B7u.rst @@ -0,0 +1,3 @@ +The :mod:`plistlib` module no longer accepts entity declarations in XML +plist files to avoid XML vulnerabilities. This should not affect users as +entity declarations are not used in regular plist files. From webhook-mailer at python.org Mon Oct 19 22:38:44 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Tue, 20 Oct 2020 02:38:44 -0000 Subject: [Python-checkins] bpo-41944: No longer call eval() on content received via HTTP in the CJK codec tests (GH-22566) (GH-22578) Message-ID: https://github.com/python/cpython/commit/43e523103886af66d6c27cd72431b5d9d14cd2a9 commit: 43e523103886af66d6c27cd72431b5d9d14cd2a9 branch: 3.7 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-19T22:38:40-04:00 summary: bpo-41944: No longer call eval() on content received via HTTP in the CJK codec tests (GH-22566) (GH-22578) (cherry picked from commit 2ef5caa58febc8968e670e39e3d37cf8eef3cab8) Co-authored-by: Serhiy Storchaka files: A Misc/NEWS.d/next/Tests/2020-10-05-17-43-46.bpo-41944.rf1dYb.rst M Lib/test/multibytecodec_support.py diff --git a/Lib/test/multibytecodec_support.py b/Lib/test/multibytecodec_support.py index cca8af67d6d1d..f76c0153f5ecf 100644 --- a/Lib/test/multibytecodec_support.py +++ b/Lib/test/multibytecodec_support.py @@ -305,29 +305,23 @@ def test_mapping_file(self): self._test_mapping_file_plain() def _test_mapping_file_plain(self): - unichrs = lambda s: ''.join(map(chr, map(eval, s.split('+')))) + def unichrs(s): + return ''.join(chr(int(x, 16)) for x in s.split('+')) + urt_wa = {} with self.open_mapping_file() as f: for line in f: if not line: break - data = line.split('#')[0].strip().split() + data = line.split('#')[0].split() if len(data) != 2: continue - csetval = eval(data[0]) - if csetval <= 0x7F: - csetch = bytes([csetval & 0xff]) - elif csetval >= 0x1000000: - csetch = bytes([(csetval >> 24), ((csetval >> 16) & 0xff), - ((csetval >> 8) & 0xff), (csetval & 0xff)]) - elif csetval >= 0x10000: - csetch = bytes([(csetval >> 16), ((csetval >> 8) & 0xff), - (csetval & 0xff)]) - elif csetval >= 0x100: - csetch = bytes([(csetval >> 8), (csetval & 0xff)]) - else: + if data[0][:2] != '0x': + self.fail(f"Invalid line: {line!r}") + csetch = bytes.fromhex(data[0][2:]) + if len(csetch) == 1 and 0x80 <= csetch[0]: continue unich = unichrs(data[1]) diff --git a/Misc/NEWS.d/next/Tests/2020-10-05-17-43-46.bpo-41944.rf1dYb.rst b/Misc/NEWS.d/next/Tests/2020-10-05-17-43-46.bpo-41944.rf1dYb.rst new file mode 100644 index 0000000000000..4f9782f1c85af --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2020-10-05-17-43-46.bpo-41944.rf1dYb.rst @@ -0,0 +1 @@ +Tests for CJK codecs no longer call ``eval()`` on content received via HTTP. From webhook-mailer at python.org Tue Oct 20 00:18:08 2020 From: webhook-mailer at python.org (Ned Deily) Date: Tue, 20 Oct 2020 04:18:08 -0000 Subject: [Python-checkins] Disable macOS CI tests in azure-pipelines (GH-22639) Message-ID: https://github.com/python/cpython/commit/a69002ce6cbf32eb63a6c30deea41fe204015db7 commit: a69002ce6cbf32eb63a6c30deea41fe204015db7 branch: 3.6 author: Ned Deily committer: GitHub date: 2020-10-20T00:18:00-04:00 summary: Disable macOS CI tests in azure-pipelines (GH-22639) files: M .azure-pipelines/ci.yml M .azure-pipelines/macos-steps.yml M .azure-pipelines/pr.yml diff --git a/.azure-pipelines/ci.yml b/.azure-pipelines/ci.yml index 49a7bb6232aa2..8678ff765fbd5 100644 --- a/.azure-pipelines/ci.yml +++ b/.azure-pipelines/ci.yml @@ -30,14 +30,15 @@ jobs: - job: macOS_CI_Tests displayName: macOS CI Tests dependsOn: Prebuild - condition: and(succeeded(), eq(dependencies.Prebuild.outputs['tests.run'], 'true')) + #condition: and(succeeded(), eq(dependencies.Prebuild.outputs['tests.run'], 'true')) + # bpo-39837: macOS tests on Azure Pipelines are disabled variables: testRunTitle: '$(build.sourceBranchName)-macos' testRunPlatform: macos pool: - vmImage: xcode9-macos10.13 + vmImage: macos-10.14 steps: - template: ./macos-steps.yml diff --git a/.azure-pipelines/macos-steps.yml b/.azure-pipelines/macos-steps.yml index 647081689454a..fa38a0df8c87b 100644 --- a/.azure-pipelines/macos-steps.yml +++ b/.azure-pipelines/macos-steps.yml @@ -6,7 +6,7 @@ steps: - script: ./configure --with-pydebug --with-openssl=/usr/local/opt/openssl --prefix=/opt/python-azdev displayName: 'Configure CPython (debug)' -- script: make -s -j4 +- script: make -j4 displayName: 'Build CPython' - script: make pythoninfo @@ -14,6 +14,8 @@ steps: - script: make buildbottest TESTOPTS="-j4 -uall,-cpu --junit-xml=$(build.binariesDirectory)/test-results.xml" displayName: 'Tests' + continueOnError: true + timeoutInMinutes: 30 - task: PublishTestResults at 2 displayName: 'Publish Test Results' diff --git a/.azure-pipelines/pr.yml b/.azure-pipelines/pr.yml index 2d7fba9cf3283..6db2a04592002 100644 --- a/.azure-pipelines/pr.yml +++ b/.azure-pipelines/pr.yml @@ -25,13 +25,15 @@ jobs: displayName: macOS PR Tests dependsOn: Prebuild condition: and(succeeded(), eq(dependencies.Prebuild.outputs['tests.run'], 'true')) + #condition: and(succeeded(), eq(dependencies.Prebuild.outputs['tests.run'], 'true')) + # bpo-39837: macOS tests on Azure Pipelines are disabled variables: testRunTitle: '$(system.pullRequest.TargetBranch)-macos' testRunPlatform: macos pool: - vmImage: xcode9-macos10.13 + vmImage: macos-10.14 steps: - template: ./macos-steps.yml From webhook-mailer at python.org Tue Oct 20 00:38:38 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Tue, 20 Oct 2020 04:38:38 -0000 Subject: [Python-checkins] bpo-42051: Reject XML entity declarations in plist files (GH-22760) (GH-22801) (GH-22804) Message-ID: https://github.com/python/cpython/commit/a158fb9c5138db94adf24fbc5690467cda811163 commit: a158fb9c5138db94adf24fbc5690467cda811163 branch: 3.6 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-20T00:38:30-04:00 summary: bpo-42051: Reject XML entity declarations in plist files (GH-22760) (GH-22801) (GH-22804) Co-authored-by: Ronald Oussoren (cherry picked from commit e512bc799e3864fe3b1351757261762d63471efc) Co-authored-by: Ned Deily files: A Misc/NEWS.d/next/Security/2020-10-19-10-56-27.bpo-42051.EU_B7u.rst M Lib/plistlib.py M Lib/test/test_plistlib.py diff --git a/Lib/plistlib.py b/Lib/plistlib.py index a91864372554b..6e030f25b21f0 100644 --- a/Lib/plistlib.py +++ b/Lib/plistlib.py @@ -322,9 +322,16 @@ def parse(self, fileobj): self.parser.StartElementHandler = self.handle_begin_element self.parser.EndElementHandler = self.handle_end_element self.parser.CharacterDataHandler = self.handle_data + self.parser.EntityDeclHandler = self.handle_entity_decl self.parser.ParseFile(fileobj) return self.root + def handle_entity_decl(self, entity_name, is_parameter_entity, value, base, system_id, public_id, notation_name): + # Reject plist files with entity declarations to avoid XML vulnerabilies in expat. + # Regular plist files don't contain those declerations, and Apple's plutil tool does not + # accept them either. + raise InvalidFileException("XML entity declarations are not supported in plist files") + def handle_begin_element(self, element, attrs): self.data = [] handler = getattr(self, "begin_" + element, None) diff --git a/Lib/test/test_plistlib.py b/Lib/test/test_plistlib.py index d47c607329cd5..abb1b81e6b057 100644 --- a/Lib/test/test_plistlib.py +++ b/Lib/test/test_plistlib.py @@ -90,6 +90,19 @@ xQHHAsQC0gAAAAAAAAIBAAAAAAAAADkAAAAAAAAAAAAAAAAAAALs'''), } +XML_PLIST_WITH_ENTITY=b'''\ + + + ]> + + + A + &entity; + + +''' + class TestPlistlib(unittest.TestCase): @@ -444,6 +457,11 @@ def test_xml_encodings(self): pl2 = plistlib.loads(data) self.assertEqual(dict(pl), dict(pl2)) + def test_xml_plist_with_entity_decl(self): + with self.assertRaisesRegex(plistlib.InvalidFileException, + "XML entity declarations are not supported"): + plistlib.loads(XML_PLIST_WITH_ENTITY, fmt=plistlib.FMT_XML) + class TestBinaryPlistlib(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Security/2020-10-19-10-56-27.bpo-42051.EU_B7u.rst b/Misc/NEWS.d/next/Security/2020-10-19-10-56-27.bpo-42051.EU_B7u.rst new file mode 100644 index 0000000000000..e865ed12a0387 --- /dev/null +++ b/Misc/NEWS.d/next/Security/2020-10-19-10-56-27.bpo-42051.EU_B7u.rst @@ -0,0 +1,3 @@ +The :mod:`plistlib` module no longer accepts entity declarations in XML +plist files to avoid XML vulnerabilities. This should not affect users as +entity declarations are not used in regular plist files. From webhook-mailer at python.org Tue Oct 20 00:46:18 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Tue, 20 Oct 2020 04:46:18 -0000 Subject: [Python-checkins] bpo-41944: No longer call eval() on content received via HTTP in the CJK codec tests (GH-22566) (GH-22579) Message-ID: https://github.com/python/cpython/commit/e912e945f2960029d039d3390ea08835ad39374b commit: e912e945f2960029d039d3390ea08835ad39374b branch: 3.6 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-20T00:46:10-04:00 summary: bpo-41944: No longer call eval() on content received via HTTP in the CJK codec tests (GH-22566) (GH-22579) (cherry picked from commit 2ef5caa58febc8968e670e39e3d37cf8eef3cab8) Co-authored-by: Serhiy Storchaka files: A Misc/NEWS.d/next/Tests/2020-10-05-17-43-46.bpo-41944.rf1dYb.rst M Lib/test/multibytecodec_support.py diff --git a/Lib/test/multibytecodec_support.py b/Lib/test/multibytecodec_support.py index f9884c68ebaa4..98feec2f60b19 100644 --- a/Lib/test/multibytecodec_support.py +++ b/Lib/test/multibytecodec_support.py @@ -300,29 +300,23 @@ def test_mapping_file(self): self._test_mapping_file_plain() def _test_mapping_file_plain(self): - unichrs = lambda s: ''.join(map(chr, map(eval, s.split('+')))) + def unichrs(s): + return ''.join(chr(int(x, 16)) for x in s.split('+')) + urt_wa = {} with self.open_mapping_file() as f: for line in f: if not line: break - data = line.split('#')[0].strip().split() + data = line.split('#')[0].split() if len(data) != 2: continue - csetval = eval(data[0]) - if csetval <= 0x7F: - csetch = bytes([csetval & 0xff]) - elif csetval >= 0x1000000: - csetch = bytes([(csetval >> 24), ((csetval >> 16) & 0xff), - ((csetval >> 8) & 0xff), (csetval & 0xff)]) - elif csetval >= 0x10000: - csetch = bytes([(csetval >> 16), ((csetval >> 8) & 0xff), - (csetval & 0xff)]) - elif csetval >= 0x100: - csetch = bytes([(csetval >> 8), (csetval & 0xff)]) - else: + if data[0][:2] != '0x': + self.fail(f"Invalid line: {line!r}") + csetch = bytes.fromhex(data[0][2:]) + if len(csetch) == 1 and 0x80 <= csetch[0]: continue unich = unichrs(data[1]) diff --git a/Misc/NEWS.d/next/Tests/2020-10-05-17-43-46.bpo-41944.rf1dYb.rst b/Misc/NEWS.d/next/Tests/2020-10-05-17-43-46.bpo-41944.rf1dYb.rst new file mode 100644 index 0000000000000..4f9782f1c85af --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2020-10-05-17-43-46.bpo-41944.rf1dYb.rst @@ -0,0 +1 @@ +Tests for CJK codecs no longer call ``eval()`` on content received via HTTP. From webhook-mailer at python.org Tue Oct 20 01:04:11 2020 From: webhook-mailer at python.org (Raymond Hettinger) Date: Tue, 20 Oct 2020 05:04:11 -0000 Subject: [Python-checkins] bpo-4356: Add key function support to the bisect module (GH-20556) Message-ID: https://github.com/python/cpython/commit/871934d4cf00687b3d1411c6e344af311646c1ae commit: 871934d4cf00687b3d1411c6e344af311646c1ae branch: master author: Raymond Hettinger committer: GitHub date: 2020-10-19T22:04:01-07:00 summary: bpo-4356: Add key function support to the bisect module (GH-20556) files: A Misc/NEWS.d/next/Library/2020-05-31-10-48-47.bpo-4356.P8kXqp.rst M Doc/library/bisect.rst M Doc/tools/susp-ignored.csv M Lib/bisect.py M Lib/test/test_bisect.py M Modules/_bisectmodule.c M Modules/clinic/_bisectmodule.c.h diff --git a/Doc/library/bisect.rst b/Doc/library/bisect.rst index 6bf7814b257f4..f34ee175ba657 100644 --- a/Doc/library/bisect.rst +++ b/Doc/library/bisect.rst @@ -21,7 +21,7 @@ example of the algorithm (the boundary conditions are already right!). The following functions are provided: -.. function:: bisect_left(a, x, lo=0, hi=len(a)) +.. function:: bisect_left(a, x, lo=0, hi=len(a), *, key=None) Locate the insertion point for *x* in *a* to maintain sorted order. The parameters *lo* and *hi* may be used to specify a subset of the list @@ -31,39 +31,106 @@ The following functions are provided: parameter to ``list.insert()`` assuming that *a* is already sorted. The returned insertion point *i* partitions the array *a* into two halves so - that ``all(val < x for val in a[lo:i])`` for the left side and - ``all(val >= x for val in a[i:hi])`` for the right side. + that ``all(val < x for val in a[lo : i])`` for the left side and + ``all(val >= x for val in a[i : hi])`` for the right side. -.. function:: bisect_right(a, x, lo=0, hi=len(a)) + *key* specifies a :term:`key function` of one argument that is used to + extract a comparison key from each input element. The default value is + ``None`` (compare the elements directly). + + .. versionchanged:: 3.10 + Added the *key* parameter. + + +.. function:: bisect_right(a, x, lo=0, hi=len(a), *, key=None) bisect(a, x, lo=0, hi=len(a)) Similar to :func:`bisect_left`, but returns an insertion point which comes after (to the right of) any existing entries of *x* in *a*. The returned insertion point *i* partitions the array *a* into two halves so - that ``all(val <= x for val in a[lo:i])`` for the left side and - ``all(val > x for val in a[i:hi])`` for the right side. + that ``all(val <= x for val in a[lo : i])`` for the left side and + ``all(val > x for val in a[i : hi])`` for the right side. + + *key* specifies a :term:`key function` of one argument that is used to + extract a comparison key from each input element. The default value is + ``None`` (compare the elements directly). + + .. versionchanged:: 3.10 + Added the *key* parameter. + -.. function:: insort_left(a, x, lo=0, hi=len(a)) +.. function:: insort_left(a, x, lo=0, hi=len(a), *, key=None) - Insert *x* in *a* in sorted order. This is equivalent to - ``a.insert(bisect.bisect_left(a, x, lo, hi), x)`` assuming that *a* is - already sorted. Keep in mind that the O(log n) search is dominated by - the slow O(n) insertion step. + Insert *x* in *a* in sorted order. -.. function:: insort_right(a, x, lo=0, hi=len(a)) + *key* specifies a :term:`key function` of one argument that is used to + extract a comparison key from each input element. The default value is + ``None`` (compare the elements directly). + + This function first runs :func:`bisect_left` to locate an insertion point. + Next, it runs the :meth:`insert` method on *a* to insert *x* at the + appropriate position to maintain sort order. + + Keep in mind that the ``O(log n)`` search is dominated by the slow O(n) + insertion step. + + .. versionchanged:: 3.10 + Added the *key* parameter. + + +.. function:: insort_right(a, x, lo=0, hi=len(a), *, key=None) insort(a, x, lo=0, hi=len(a)) Similar to :func:`insort_left`, but inserting *x* in *a* after any existing entries of *x*. + *key* specifies a :term:`key function` of one argument that is used to + extract a comparison key from each input element. The default value is + ``None`` (compare the elements directly). + + This function first runs :func:`bisect_right` to locate an insertion point. + Next, it runs the :meth:`insert` method on *a* to insert *x* at the + appropriate position to maintain sort order. + + Keep in mind that the ``O(log n)`` search is dominated by the slow O(n) + insertion step. + + .. versionchanged:: 3.10 + Added the *key* parameter. + + +Performance Notes +----------------- + +When writing time sensitive code using *bisect()* and *insort()*, keep these +thoughts in mind: + +* Bisection is effective for searching ranges of values. + For locating specific values, dictionaries are more performant. + +* The *insort()* functions are ``O(n)`` because the logarithmic search step + is dominated by the linear time insertion step. + +* The search functions are stateless and discard key function results after + they are used. Consequently, if the search functions are used in a loop, + the key function may be called again and again on the same array elements. + If the key function isn't fast, consider wrapping it with + :func:`functools.cache` to avoid duplicate computations. Alternatively, + consider searching an array of precomputed keys to locate the insertion + point (as shown in the examples section below). + .. seealso:: - `SortedCollection recipe - `_ that uses - bisect to build a full-featured collection class with straight-forward search - methods and support for a key-function. The keys are precomputed to save - unnecessary calls to the key function during searches. + * `Sorted Collections + `_ is a high performance + module that uses *bisect* to managed sorted collections of data. + + * The `SortedCollection recipe + `_ uses + bisect to build a full-featured collection class with straight-forward search + methods and support for a key-function. The keys are precomputed to save + unnecessary calls to the key function during searches. Searching Sorted Lists @@ -110,8 +177,8 @@ lists:: raise ValueError -Other Examples --------------- +Examples +-------- .. _bisect-example: @@ -127,17 +194,12 @@ a 'B', and so on:: >>> [grade(score) for score in [33, 99, 77, 70, 89, 90, 100]] ['F', 'A', 'C', 'C', 'B', 'A', 'A'] -Unlike the :func:`sorted` function, it does not make sense for the :func:`bisect` -functions to have *key* or *reversed* arguments because that would lead to an -inefficient design (successive calls to bisect functions would not "remember" -all of the previous key lookups). - -Instead, it is better to search a list of precomputed keys to find the index -of the record in question:: +One technique to avoid repeated calls to a key function is to search a list of +precomputed keys to find the index of a record:: >>> data = [('red', 5), ('blue', 1), ('yellow', 8), ('black', 0)] - >>> data.sort(key=lambda r: r[1]) - >>> keys = [r[1] for r in data] # precomputed list of keys + >>> data.sort(key=lambda r: r[1]) # Or use operator.itemgetter(1). + >>> keys = [r[1] for r in data] # Precompute a list of keys. >>> data[bisect_left(keys, 0)] ('black', 0) >>> data[bisect_left(keys, 1)] diff --git a/Doc/tools/susp-ignored.csv b/Doc/tools/susp-ignored.csv index 7be8d0abd69a5..f85d6946954d6 100644 --- a/Doc/tools/susp-ignored.csv +++ b/Doc/tools/susp-ignored.csv @@ -111,8 +111,6 @@ howto/urllib2,,:password,"""joe:password at example.com""" library/ast,,:upper,lower:upper library/ast,,:step,lower:upper:step library/audioop,,:ipos,"# factor = audioop.findfactor(in_test[ipos*2:ipos*2+len(out_test)]," -library/bisect,32,:hi,all(val >= x for val in a[i:hi]) -library/bisect,42,:hi,all(val > x for val in a[i:hi]) library/configparser,,:home,my_dir: ${Common:home_dir}/twosheds library/configparser,,:option,${section:option} library/configparser,,:path,python_dir: ${Frameworks:path}/Python/Versions/${Frameworks:Python} diff --git a/Lib/bisect.py b/Lib/bisect.py index 8336a4ed9ca0f..d37da74f7b405 100644 --- a/Lib/bisect.py +++ b/Lib/bisect.py @@ -1,6 +1,7 @@ """Bisection algorithms.""" -def insort_right(a, x, lo=0, hi=None): + +def insort_right(a, x, lo=0, hi=None, *, key=None): """Insert item x in list a, and keep it sorted assuming a is sorted. If x is already in a, insert it to the right of the rightmost x. @@ -8,11 +9,14 @@ def insort_right(a, x, lo=0, hi=None): Optional args lo (default 0) and hi (default len(a)) bound the slice of a to be searched. """ - - lo = bisect_right(a, x, lo, hi) + if key is None: + lo = bisect_right(a, x, lo, hi) + else: + lo = bisect_right(a, key(x), lo, hi, key=key) a.insert(lo, x) -def bisect_right(a, x, lo=0, hi=None): + +def bisect_right(a, x, lo=0, hi=None, *, key=None): """Return the index where to insert item x in list a, assuming a is sorted. The return value i is such that all e in a[:i] have e <= x, and all e in @@ -27,14 +31,26 @@ def bisect_right(a, x, lo=0, hi=None): raise ValueError('lo must be non-negative') if hi is None: hi = len(a) - while lo < hi: - mid = (lo+hi)//2 - # Use __lt__ to match the logic in list.sort() and in heapq - if x < a[mid]: hi = mid - else: lo = mid+1 + # Note, the comparison uses "<" to match the + # __lt__() logic in list.sort() and in heapq. + if key is None: + while lo < hi: + mid = (lo + hi) // 2 + if x < a[mid]: + hi = mid + else: + lo = mid + 1 + else: + while lo < hi: + mid = (lo + hi) // 2 + if x < key(a[mid]): + hi = mid + else: + lo = mid + 1 return lo -def insort_left(a, x, lo=0, hi=None): + +def insort_left(a, x, lo=0, hi=None, *, key=None): """Insert item x in list a, and keep it sorted assuming a is sorted. If x is already in a, insert it to the left of the leftmost x. @@ -43,11 +59,13 @@ def insort_left(a, x, lo=0, hi=None): slice of a to be searched. """ - lo = bisect_left(a, x, lo, hi) + if key is None: + lo = bisect_left(a, x, lo, hi) + else: + lo = bisect_left(a, key(x), lo, hi, key=key) a.insert(lo, x) - -def bisect_left(a, x, lo=0, hi=None): +def bisect_left(a, x, lo=0, hi=None, *, key=None): """Return the index where to insert item x in list a, assuming a is sorted. The return value i is such that all e in a[:i] have e < x, and all e in @@ -62,13 +80,25 @@ def bisect_left(a, x, lo=0, hi=None): raise ValueError('lo must be non-negative') if hi is None: hi = len(a) - while lo < hi: - mid = (lo+hi)//2 - # Use __lt__ to match the logic in list.sort() and in heapq - if a[mid] < x: lo = mid+1 - else: hi = mid + # Note, the comparison uses "<" to match the + # __lt__() logic in list.sort() and in heapq. + if key is None: + while lo < hi: + mid = (lo + hi) // 2 + if a[mid] < x: + lo = mid + 1 + else: + hi = mid + else: + while lo < hi: + mid = (lo + hi) // 2 + if key(a[mid]) < x: + lo = mid + 1 + else: + hi = mid return lo + # Overwrite above definitions with a fast C implementation try: from _bisect import * diff --git a/Lib/test/test_bisect.py b/Lib/test/test_bisect.py index fc7990d765e53..20f8b9d7c0aa8 100644 --- a/Lib/test/test_bisect.py +++ b/Lib/test/test_bisect.py @@ -200,6 +200,63 @@ def test_keyword_args(self): self.module.insort(a=data, x=25, lo=1, hi=3) self.assertEqual(data, [10, 20, 25, 25, 25, 30, 40, 50]) + def test_lookups_with_key_function(self): + mod = self.module + + # Invariant: Index with a keyfunc on an array + # should match the index on an array where + # key function has already been applied. + + keyfunc = abs + arr = sorted([2, -4, 6, 8, -10], key=keyfunc) + precomputed_arr = list(map(keyfunc, arr)) + for x in precomputed_arr: + self.assertEqual( + mod.bisect_left(arr, x, key=keyfunc), + mod.bisect_left(precomputed_arr, x) + ) + self.assertEqual( + mod.bisect_right(arr, x, key=keyfunc), + mod.bisect_right(precomputed_arr, x) + ) + + keyfunc = str.casefold + arr = sorted('aBcDeEfgHhiIiij', key=keyfunc) + precomputed_arr = list(map(keyfunc, arr)) + for x in precomputed_arr: + self.assertEqual( + mod.bisect_left(arr, x, key=keyfunc), + mod.bisect_left(precomputed_arr, x) + ) + self.assertEqual( + mod.bisect_right(arr, x, key=keyfunc), + mod.bisect_right(precomputed_arr, x) + ) + + def test_insort(self): + from random import shuffle + mod = self.module + + # Invariant: As random elements are inserted in + # a target list, the targetlist remains sorted. + keyfunc = abs + data = list(range(-10, 11)) + list(range(-20, 20, 2)) + shuffle(data) + target = [] + for x in data: + mod.insort_left(target, x, key=keyfunc) + self.assertEqual( + sorted(target, key=keyfunc), + target + ) + target = [] + for x in data: + mod.insort_right(target, x, key=keyfunc) + self.assertEqual( + sorted(target, key=keyfunc), + target + ) + class TestBisectPython(TestBisect, unittest.TestCase): module = py_bisect diff --git a/Misc/NEWS.d/next/Library/2020-05-31-10-48-47.bpo-4356.P8kXqp.rst b/Misc/NEWS.d/next/Library/2020-05-31-10-48-47.bpo-4356.P8kXqp.rst new file mode 100644 index 0000000000000..f5211d8a76f74 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-05-31-10-48-47.bpo-4356.P8kXqp.rst @@ -0,0 +1 @@ +Add a key function to the bisect module. diff --git a/Modules/_bisectmodule.c b/Modules/_bisectmodule.c index 277e9755f2721..2d7c15bc1744b 100644 --- a/Modules/_bisectmodule.c +++ b/Modules/_bisectmodule.c @@ -16,7 +16,8 @@ module _bisect _Py_IDENTIFIER(insert); static inline Py_ssize_t -internal_bisect_right(PyObject *list, PyObject *item, Py_ssize_t lo, Py_ssize_t hi) +internal_bisect_right(PyObject *list, PyObject *item, Py_ssize_t lo, Py_ssize_t hi, + PyObject* key) { PyObject *litem; Py_ssize_t mid; @@ -39,6 +40,14 @@ internal_bisect_right(PyObject *list, PyObject *item, Py_ssize_t lo, Py_ssize_t litem = PySequence_GetItem(list, mid); if (litem == NULL) return -1; + if (key != Py_None) { + PyObject *newitem = PyObject_CallOneArg(key, litem); + if (newitem == NULL) { + Py_DECREF(litem); + return -1; + } + Py_SETREF(litem, newitem); + } res = PyObject_RichCompareBool(item, litem, Py_LT); Py_DECREF(litem); if (res < 0) @@ -58,6 +67,8 @@ _bisect.bisect_right -> Py_ssize_t x: object lo: Py_ssize_t = 0 hi: Py_ssize_t(c_default='-1', accept={int, NoneType}) = None + * + key: object = None Return the index where to insert item x in list a, assuming a is sorted. @@ -71,10 +82,10 @@ slice of a to be searched. static Py_ssize_t _bisect_bisect_right_impl(PyObject *module, PyObject *a, PyObject *x, - Py_ssize_t lo, Py_ssize_t hi) -/*[clinic end generated code: output=419e150cf1d2a235 input=e72212b282c83375]*/ + Py_ssize_t lo, Py_ssize_t hi, PyObject *key) +/*[clinic end generated code: output=3a4bc09cc7c8a73d input=1313e9ca20c8bc3c]*/ { - return internal_bisect_right(a, x, lo, hi); + return internal_bisect_right(a, x, lo, hi, key); } /*[clinic input] @@ -84,6 +95,8 @@ _bisect.insort_right x: object lo: Py_ssize_t = 0 hi: Py_ssize_t(c_default='-1', accept={int, NoneType}) = None + * + key: object = None Insert item x in list a, and keep it sorted assuming a is sorted. @@ -95,11 +108,22 @@ slice of a to be searched. static PyObject * _bisect_insort_right_impl(PyObject *module, PyObject *a, PyObject *x, - Py_ssize_t lo, Py_ssize_t hi) -/*[clinic end generated code: output=c2caa3d4cd02035a input=d1c45bfa68182669]*/ + Py_ssize_t lo, Py_ssize_t hi, PyObject *key) +/*[clinic end generated code: output=ac3bf26d07aedda2 input=44e1708e26b7b802]*/ { - PyObject *result; - Py_ssize_t index = internal_bisect_right(a, x, lo, hi); + PyObject *result, *key_x; + Py_ssize_t index; + + if (key == Py_None) { + index = internal_bisect_right(a, x, lo, hi, key); + } else { + key_x = PyObject_CallOneArg(key, x); + if (x == NULL) { + return NULL; + } + index = internal_bisect_right(a, key_x, lo, hi, key); + Py_DECREF(key_x); + } if (index < 0) return NULL; if (PyList_CheckExact(a)) { @@ -117,7 +141,8 @@ _bisect_insort_right_impl(PyObject *module, PyObject *a, PyObject *x, } static inline Py_ssize_t -internal_bisect_left(PyObject *list, PyObject *item, Py_ssize_t lo, Py_ssize_t hi) +internal_bisect_left(PyObject *list, PyObject *item, Py_ssize_t lo, Py_ssize_t hi, + PyObject *key) { PyObject *litem; Py_ssize_t mid; @@ -140,6 +165,14 @@ internal_bisect_left(PyObject *list, PyObject *item, Py_ssize_t lo, Py_ssize_t h litem = PySequence_GetItem(list, mid); if (litem == NULL) return -1; + if (key != Py_None) { + PyObject *newitem = PyObject_CallOneArg(key, litem); + if (newitem == NULL) { + Py_DECREF(litem); + return -1; + } + Py_SETREF(litem, newitem); + } res = PyObject_RichCompareBool(litem, item, Py_LT); Py_DECREF(litem); if (res < 0) @@ -160,6 +193,8 @@ _bisect.bisect_left -> Py_ssize_t x: object lo: Py_ssize_t = 0 hi: Py_ssize_t(c_default='-1', accept={int, NoneType}) = None + * + key: object = None Return the index where to insert item x in list a, assuming a is sorted. @@ -173,10 +208,10 @@ slice of a to be searched. static Py_ssize_t _bisect_bisect_left_impl(PyObject *module, PyObject *a, PyObject *x, - Py_ssize_t lo, Py_ssize_t hi) -/*[clinic end generated code: output=af82168bc2856f24 input=2bd90f34afe5609f]*/ + Py_ssize_t lo, Py_ssize_t hi, PyObject *key) +/*[clinic end generated code: output=70749d6e5cae9284 input=3cbeec690f2f6c6e]*/ { - return internal_bisect_left(a, x, lo, hi); + return internal_bisect_left(a, x, lo, hi, key); } @@ -187,6 +222,8 @@ _bisect.insort_left x: object lo: Py_ssize_t = 0 hi: Py_ssize_t(c_default='-1', accept={int, NoneType}) = None + * + key: object = None Insert item x in list a, and keep it sorted assuming a is sorted. @@ -198,11 +235,22 @@ slice of a to be searched. static PyObject * _bisect_insort_left_impl(PyObject *module, PyObject *a, PyObject *x, - Py_ssize_t lo, Py_ssize_t hi) -/*[clinic end generated code: output=9e8356c0844a182b input=bc4583308bce00cc]*/ + Py_ssize_t lo, Py_ssize_t hi, PyObject *key) +/*[clinic end generated code: output=b1d33e5e7ffff11e input=3ab65d8784f585b1]*/ { - PyObject *result; - Py_ssize_t index = internal_bisect_left(a, x, lo, hi); + PyObject *result, *key_x; + Py_ssize_t index; + + if (key == Py_None) { + index = internal_bisect_left(a, x, lo, hi, key); + } else { + key_x = PyObject_CallOneArg(key, x); + if (x == NULL) { + return NULL; + } + index = internal_bisect_left(a, key_x, lo, hi, key); + Py_DECREF(key_x); + } if (index < 0) return NULL; if (PyList_CheckExact(a)) { diff --git a/Modules/clinic/_bisectmodule.c.h b/Modules/clinic/_bisectmodule.c.h index 07fc9060d1d8f..304e46cf158a7 100644 --- a/Modules/clinic/_bisectmodule.c.h +++ b/Modules/clinic/_bisectmodule.c.h @@ -3,7 +3,7 @@ preserve [clinic start generated code]*/ PyDoc_STRVAR(_bisect_bisect_right__doc__, -"bisect_right($module, /, a, x, lo=0, hi=None)\n" +"bisect_right($module, /, a, x, lo=0, hi=None, *, key=None)\n" "--\n" "\n" "Return the index where to insert item x in list a, assuming a is sorted.\n" @@ -20,20 +20,21 @@ PyDoc_STRVAR(_bisect_bisect_right__doc__, static Py_ssize_t _bisect_bisect_right_impl(PyObject *module, PyObject *a, PyObject *x, - Py_ssize_t lo, Py_ssize_t hi); + Py_ssize_t lo, Py_ssize_t hi, PyObject *key); static PyObject * _bisect_bisect_right(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { PyObject *return_value = NULL; - static const char * const _keywords[] = {"a", "x", "lo", "hi", NULL}; + static const char * const _keywords[] = {"a", "x", "lo", "hi", "key", NULL}; static _PyArg_Parser _parser = {NULL, _keywords, "bisect_right", 0}; - PyObject *argsbuf[4]; + PyObject *argsbuf[5]; Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 2; PyObject *a; PyObject *x; Py_ssize_t lo = 0; Py_ssize_t hi = -1; + PyObject *key = Py_None; Py_ssize_t _return_value; args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 4, 0, argsbuf); @@ -62,11 +63,21 @@ _bisect_bisect_right(PyObject *module, PyObject *const *args, Py_ssize_t nargs, goto skip_optional_pos; } } - if (!_Py_convert_optional_to_ssize_t(args[3], &hi)) { - goto exit; + if (args[3]) { + if (!_Py_convert_optional_to_ssize_t(args[3], &hi)) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_pos; + } } skip_optional_pos: - _return_value = _bisect_bisect_right_impl(module, a, x, lo, hi); + if (!noptargs) { + goto skip_optional_kwonly; + } + key = args[4]; +skip_optional_kwonly: + _return_value = _bisect_bisect_right_impl(module, a, x, lo, hi, key); if ((_return_value == -1) && PyErr_Occurred()) { goto exit; } @@ -77,7 +88,7 @@ _bisect_bisect_right(PyObject *module, PyObject *const *args, Py_ssize_t nargs, } PyDoc_STRVAR(_bisect_insort_right__doc__, -"insort_right($module, /, a, x, lo=0, hi=None)\n" +"insort_right($module, /, a, x, lo=0, hi=None, *, key=None)\n" "--\n" "\n" "Insert item x in list a, and keep it sorted assuming a is sorted.\n" @@ -92,20 +103,21 @@ PyDoc_STRVAR(_bisect_insort_right__doc__, static PyObject * _bisect_insort_right_impl(PyObject *module, PyObject *a, PyObject *x, - Py_ssize_t lo, Py_ssize_t hi); + Py_ssize_t lo, Py_ssize_t hi, PyObject *key); static PyObject * _bisect_insort_right(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { PyObject *return_value = NULL; - static const char * const _keywords[] = {"a", "x", "lo", "hi", NULL}; + static const char * const _keywords[] = {"a", "x", "lo", "hi", "key", NULL}; static _PyArg_Parser _parser = {NULL, _keywords, "insort_right", 0}; - PyObject *argsbuf[4]; + PyObject *argsbuf[5]; Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 2; PyObject *a; PyObject *x; Py_ssize_t lo = 0; Py_ssize_t hi = -1; + PyObject *key = Py_None; args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 4, 0, argsbuf); if (!args) { @@ -133,18 +145,28 @@ _bisect_insort_right(PyObject *module, PyObject *const *args, Py_ssize_t nargs, goto skip_optional_pos; } } - if (!_Py_convert_optional_to_ssize_t(args[3], &hi)) { - goto exit; + if (args[3]) { + if (!_Py_convert_optional_to_ssize_t(args[3], &hi)) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_pos; + } } skip_optional_pos: - return_value = _bisect_insort_right_impl(module, a, x, lo, hi); + if (!noptargs) { + goto skip_optional_kwonly; + } + key = args[4]; +skip_optional_kwonly: + return_value = _bisect_insort_right_impl(module, a, x, lo, hi, key); exit: return return_value; } PyDoc_STRVAR(_bisect_bisect_left__doc__, -"bisect_left($module, /, a, x, lo=0, hi=None)\n" +"bisect_left($module, /, a, x, lo=0, hi=None, *, key=None)\n" "--\n" "\n" "Return the index where to insert item x in list a, assuming a is sorted.\n" @@ -161,20 +183,21 @@ PyDoc_STRVAR(_bisect_bisect_left__doc__, static Py_ssize_t _bisect_bisect_left_impl(PyObject *module, PyObject *a, PyObject *x, - Py_ssize_t lo, Py_ssize_t hi); + Py_ssize_t lo, Py_ssize_t hi, PyObject *key); static PyObject * _bisect_bisect_left(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { PyObject *return_value = NULL; - static const char * const _keywords[] = {"a", "x", "lo", "hi", NULL}; + static const char * const _keywords[] = {"a", "x", "lo", "hi", "key", NULL}; static _PyArg_Parser _parser = {NULL, _keywords, "bisect_left", 0}; - PyObject *argsbuf[4]; + PyObject *argsbuf[5]; Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 2; PyObject *a; PyObject *x; Py_ssize_t lo = 0; Py_ssize_t hi = -1; + PyObject *key = Py_None; Py_ssize_t _return_value; args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 4, 0, argsbuf); @@ -203,11 +226,21 @@ _bisect_bisect_left(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P goto skip_optional_pos; } } - if (!_Py_convert_optional_to_ssize_t(args[3], &hi)) { - goto exit; + if (args[3]) { + if (!_Py_convert_optional_to_ssize_t(args[3], &hi)) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_pos; + } } skip_optional_pos: - _return_value = _bisect_bisect_left_impl(module, a, x, lo, hi); + if (!noptargs) { + goto skip_optional_kwonly; + } + key = args[4]; +skip_optional_kwonly: + _return_value = _bisect_bisect_left_impl(module, a, x, lo, hi, key); if ((_return_value == -1) && PyErr_Occurred()) { goto exit; } @@ -218,7 +251,7 @@ _bisect_bisect_left(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P } PyDoc_STRVAR(_bisect_insort_left__doc__, -"insort_left($module, /, a, x, lo=0, hi=None)\n" +"insort_left($module, /, a, x, lo=0, hi=None, *, key=None)\n" "--\n" "\n" "Insert item x in list a, and keep it sorted assuming a is sorted.\n" @@ -233,20 +266,21 @@ PyDoc_STRVAR(_bisect_insort_left__doc__, static PyObject * _bisect_insort_left_impl(PyObject *module, PyObject *a, PyObject *x, - Py_ssize_t lo, Py_ssize_t hi); + Py_ssize_t lo, Py_ssize_t hi, PyObject *key); static PyObject * _bisect_insort_left(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { PyObject *return_value = NULL; - static const char * const _keywords[] = {"a", "x", "lo", "hi", NULL}; + static const char * const _keywords[] = {"a", "x", "lo", "hi", "key", NULL}; static _PyArg_Parser _parser = {NULL, _keywords, "insort_left", 0}; - PyObject *argsbuf[4]; + PyObject *argsbuf[5]; Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 2; PyObject *a; PyObject *x; Py_ssize_t lo = 0; Py_ssize_t hi = -1; + PyObject *key = Py_None; args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 4, 0, argsbuf); if (!args) { @@ -274,13 +308,23 @@ _bisect_insort_left(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P goto skip_optional_pos; } } - if (!_Py_convert_optional_to_ssize_t(args[3], &hi)) { - goto exit; + if (args[3]) { + if (!_Py_convert_optional_to_ssize_t(args[3], &hi)) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_pos; + } } skip_optional_pos: - return_value = _bisect_insort_left_impl(module, a, x, lo, hi); + if (!noptargs) { + goto skip_optional_kwonly; + } + key = args[4]; +skip_optional_kwonly: + return_value = _bisect_insort_left_impl(module, a, x, lo, hi, key); exit: return return_value; } -/*[clinic end generated code: output=6cf46f205659f01a input=a9049054013a1b77]*/ +/*[clinic end generated code: output=b3a5be025aa4ed7e input=a9049054013a1b77]*/ From webhook-mailer at python.org Tue Oct 20 01:22:53 2020 From: webhook-mailer at python.org (Pablo Galindo) Date: Tue, 20 Oct 2020 05:22:53 -0000 Subject: [Python-checkins] bpo-42093: Add opcode cache for LOAD_ATTR (GH-22803) Message-ID: https://github.com/python/cpython/commit/109826c8508dd02e06ae0f1784f1d202495a8680 commit: 109826c8508dd02e06ae0f1784f1d202495a8680 branch: master author: Pablo Galindo committer: GitHub date: 2020-10-20T06:22:44+01:00 summary: bpo-42093: Add opcode cache for LOAD_ATTR (GH-22803) files: A Misc/NEWS.d/next/Core and Builtins/2020-10-20-04-24-07.bpo-42093.ooZZNh.rst M Doc/whatsnew/3.10.rst M Include/cpython/dictobject.h M Include/internal/pycore_code.h M Objects/codeobject.c M Objects/dictobject.c M Python/ceval.c diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index f57e1b412378e..e275a7cb4573f 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -252,6 +252,9 @@ Optimizations average. (Contributed by Victor Stinner in :issue:`41006`.) +* The ``LOAD_ATTR`` instruction now uses new "per opcode cache" mechanism. + It is about 36% faster now. (Contributed by Pablo Galindo and Yury Selivanov + in :issue:`42093`.) Deprecated ========== diff --git a/Include/cpython/dictobject.h b/Include/cpython/dictobject.h index 5a15630cfbac7..f67c3214ddd9a 100644 --- a/Include/cpython/dictobject.h +++ b/Include/cpython/dictobject.h @@ -71,6 +71,7 @@ PyAPI_FUNC(void) _PyDict_DebugMallocStats(FILE *out); int _PyObjectDict_SetItem(PyTypeObject *tp, PyObject **dictptr, PyObject *name, PyObject *value); PyObject *_PyDict_LoadGlobal(PyDictObject *, PyDictObject *, PyObject *); +Py_ssize_t _PyDict_GetItemHint(PyDictObject *, PyObject *, Py_ssize_t, PyObject **); /* _PyDictView */ diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index 88956f109b4f7..f1e89d96b9ebb 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -10,9 +10,16 @@ typedef struct { uint64_t builtins_ver; /* ma_version of builtin dict */ } _PyOpcache_LoadGlobal; +typedef struct { + PyTypeObject *type; + Py_ssize_t hint; + unsigned int tp_version_tag; +} _PyOpCodeOpt_LoadAttr; + struct _PyOpcache { union { _PyOpcache_LoadGlobal lg; + _PyOpCodeOpt_LoadAttr la; } u; char optimized; }; diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-10-20-04-24-07.bpo-42093.ooZZNh.rst b/Misc/NEWS.d/next/Core and Builtins/2020-10-20-04-24-07.bpo-42093.ooZZNh.rst new file mode 100644 index 0000000000000..36a12c1c1cb58 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2020-10-20-04-24-07.bpo-42093.ooZZNh.rst @@ -0,0 +1,2 @@ +The ``LOAD_ATTR`` instruction now uses new "per opcode cache" mechanism and +it is about 36% faster now. Patch by Pablo Galindo and Yury Selivanov. diff --git a/Objects/codeobject.c b/Objects/codeobject.c index 4ca22fc5029b8..c86d0e1f4ab71 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -301,8 +301,8 @@ _PyCode_InitOpcache(PyCodeObject *co) unsigned char opcode = _Py_OPCODE(opcodes[i]); i++; // 'i' is now aligned to (next_instr - first_instr) - // TODO: LOAD_METHOD, LOAD_ATTR - if (opcode == LOAD_GLOBAL) { + // TODO: LOAD_METHOD + if (opcode == LOAD_GLOBAL || opcode == LOAD_ATTR) { opts++; co->co_opcache_map[i] = (unsigned char)opts; if (opts > 254) { diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 6c3fc62d2ecc7..8e74962345286 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -1437,6 +1437,71 @@ PyDict_GetItem(PyObject *op, PyObject *key) return value; } +Py_ssize_t +_PyDict_GetItemHint(PyDictObject *mp, PyObject *key, + Py_ssize_t hint, PyObject **value) +{ + Py_hash_t hash; + PyThreadState *tstate; + + assert(*value == NULL); + assert(PyDict_CheckExact((PyObject*)mp)); + assert(PyUnicode_CheckExact(key)); + + if (hint >= 0 && hint < _PyDict_KeysSize(mp->ma_keys)) { + PyObject *res = NULL; + + PyDictKeyEntry *ep = DK_ENTRIES(mp->ma_keys) + (size_t)hint; + if (ep->me_key == key) { + if (mp->ma_keys->dk_lookup == lookdict_split) { + assert(mp->ma_values != NULL); + res = mp->ma_values[(size_t)hint]; + } + else { + res = ep->me_value; + } + if (res != NULL) { + *value = res; + return hint; + } + } + } + + if ((hash = ((PyASCIIObject *) key)->hash) == -1) + { + hash = PyObject_Hash(key); + if (hash == -1) { + PyErr_Clear(); + return -1; + } + } + + // We can arrive here with a NULL tstate during initialization: try + // running "python -Wi" for an example related to string interning + tstate = _PyThreadState_UncheckedGet(); + Py_ssize_t ix = 0; + if (tstate != NULL && tstate->curexc_type != NULL) { + /* preserve the existing exception */ + PyObject *err_type, *err_value, *err_tb; + PyErr_Fetch(&err_type, &err_value, &err_tb); + ix = (mp->ma_keys->dk_lookup)(mp, key, hash, value); + /* ignore errors */ + PyErr_Restore(err_type, err_value, err_tb); + if (ix < 0) { + return -1; + } + } + else { + ix = (mp->ma_keys->dk_lookup)(mp, key, hash, value); + if (ix < 0) { + PyErr_Clear(); + return -1; + } + } + + return ix; +} + /* Same as PyDict_GetItemWithError() but with hash supplied by caller. This returns NULL *with* an exception set if an exception occurred. It returns NULL *without* an exception set if the key wasn't present. diff --git a/Python/ceval.c b/Python/ceval.c index 762de577e6b55..fafbf7524bb84 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -111,6 +111,7 @@ static long dxp[256]; #else #define OPCACHE_MIN_RUNS 1024 /* create opcache when code executed this time */ #endif +#define OPCODE_CACHE_MAX_TRIES 20 #define OPCACHE_STATS 0 /* Enable stats */ #if OPCACHE_STATS @@ -120,6 +121,12 @@ static size_t opcache_code_objects_extra_mem = 0; static size_t opcache_global_opts = 0; static size_t opcache_global_hits = 0; static size_t opcache_global_misses = 0; + +static size_t opcache_attr_opts = 0; +static size_t opcache_attr_hits = 0; +static size_t opcache_attr_misses = 0; +static size_t opcache_attr_deopts = 0; +static size_t opcache_attr_total = 0; #endif @@ -365,6 +372,25 @@ _PyEval_Fini(void) opcache_global_opts); fprintf(stderr, "\n"); + + fprintf(stderr, "-- Opcode cache LOAD_ATTR hits = %zd (%d%%)\n", + opcache_attr_hits, + (int) (100.0 * opcache_attr_hits / + opcache_attr_total)); + + fprintf(stderr, "-- Opcode cache LOAD_ATTR misses = %zd (%d%%)\n", + opcache_attr_misses, + (int) (100.0 * opcache_attr_misses / + opcache_attr_total)); + + fprintf(stderr, "-- Opcode cache LOAD_ATTR opts = %zd\n", + opcache_attr_opts); + + fprintf(stderr, "-- Opcode cache LOAD_ATTR deopts = %zd\n", + opcache_attr_deopts); + + fprintf(stderr, "-- Opcode cache LOAD_ATTR total = %zd\n", + opcache_attr_total); #endif } @@ -1224,16 +1250,43 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) do { \ co_opcache = NULL; \ if (co->co_opcache != NULL) { \ - unsigned char co_opt_offset = \ + unsigned char co_opcache_offset = \ co->co_opcache_map[next_instr - first_instr]; \ - if (co_opt_offset > 0) { \ - assert(co_opt_offset <= co->co_opcache_size); \ - co_opcache = &co->co_opcache[co_opt_offset - 1]; \ + if (co_opcache_offset > 0) { \ + assert(co_opcache_offset <= co->co_opcache_size); \ + co_opcache = &co->co_opcache[co_opcache_offset - 1]; \ assert(co_opcache != NULL); \ } \ } \ } while (0) +#define OPCACHE_DEOPT() \ + do { \ + if (co_opcache != NULL) { \ + co_opcache->optimized = -1; \ + unsigned char co_opcache_offset = \ + co->co_opcache_map[next_instr - first_instr]; \ + assert(co_opcache_offset <= co->co_opcache_size); \ + co->co_opcache_map[co_opcache_offset] = 0; \ + co_opcache = NULL; \ + } \ + } while (0) + +#define OPCACHE_DEOPT_LOAD_ATTR() \ + do { \ + if (co_opcache != NULL) { \ + OPCACHE_STAT_ATTR_DEOPT(); \ + OPCACHE_DEOPT(); \ + } \ + } while (0) + +#define OPCACHE_MAYBE_DEOPT_LOAD_ATTR() \ + do { \ + if (co_opcache != NULL && --co_opcache->optimized <= 0) { \ + OPCACHE_DEOPT_LOAD_ATTR(); \ + } \ + } while (0) + #if OPCACHE_STATS #define OPCACHE_STAT_GLOBAL_HIT() \ @@ -1251,12 +1304,43 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) if (co->co_opcache != NULL) opcache_global_opts++; \ } while (0) +#define OPCACHE_STAT_ATTR_HIT() \ + do { \ + if (co->co_opcache != NULL) opcache_attr_hits++; \ + } while (0) + +#define OPCACHE_STAT_ATTR_MISS() \ + do { \ + if (co->co_opcache != NULL) opcache_attr_misses++; \ + } while (0) + +#define OPCACHE_STAT_ATTR_OPT() \ + do { \ + if (co->co_opcache!= NULL) opcache_attr_opts++; \ + } while (0) + +#define OPCACHE_STAT_ATTR_DEOPT() \ + do { \ + if (co->co_opcache != NULL) opcache_attr_deopts++; \ + } while (0) + +#define OPCACHE_STAT_ATTR_TOTAL() \ + do { \ + if (co->co_opcache != NULL) opcache_attr_total++; \ + } while (0) + #else /* OPCACHE_STATS */ #define OPCACHE_STAT_GLOBAL_HIT() #define OPCACHE_STAT_GLOBAL_MISS() #define OPCACHE_STAT_GLOBAL_OPT() +#define OPCACHE_STAT_ATTR_HIT() +#define OPCACHE_STAT_ATTR_MISS() +#define OPCACHE_STAT_ATTR_OPT() +#define OPCACHE_STAT_ATTR_DEOPT() +#define OPCACHE_STAT_ATTR_TOTAL() + #endif /* Start of code */ @@ -3023,7 +3107,134 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) case TARGET(LOAD_ATTR): { PyObject *name = GETITEM(names, oparg); PyObject *owner = TOP(); - PyObject *res = PyObject_GetAttr(owner, name); + + PyTypeObject *type = Py_TYPE(owner); + PyObject *res; + PyObject **dictptr; + PyObject *dict; + _PyOpCodeOpt_LoadAttr *la; + + OPCACHE_STAT_ATTR_TOTAL(); + + OPCACHE_CHECK(); + if (co_opcache != NULL && PyType_HasFeature(type, Py_TPFLAGS_VALID_VERSION_TAG)) + { + if (co_opcache->optimized > 0) { + /* Fast path -- cache hit makes LOAD_ATTR ~30% faster */ + la = &co_opcache->u.la; + if (la->type == type && la->tp_version_tag == type->tp_version_tag) + { + assert(type->tp_dict != NULL); + assert(type->tp_dictoffset > 0); + + dictptr = (PyObject **) ((char *)owner + type->tp_dictoffset); + dict = *dictptr; + if (dict != NULL && PyDict_CheckExact(dict)) { + Py_ssize_t hint = la->hint; + Py_INCREF(dict); + res = NULL; + la->hint = _PyDict_GetItemHint((PyDictObject*)dict, name, hint, &res); + + if (res != NULL) { + if (la->hint == hint && hint >= 0) { + /* Our hint has helped -- cache hit. */ + OPCACHE_STAT_ATTR_HIT(); + } else { + /* The hint we provided didn't work. + Maybe next time? */ + OPCACHE_MAYBE_DEOPT_LOAD_ATTR(); + } + + Py_INCREF(res); + SET_TOP(res); + Py_DECREF(owner); + Py_DECREF(dict); + DISPATCH(); + } else { + // This attribute can be missing sometimes -- we + // don't want to optimize this lookup. + OPCACHE_DEOPT_LOAD_ATTR(); + Py_DECREF(dict); + } + } else { + // There is no dict, or __dict__ doesn't satisfy PyDict_CheckExact + OPCACHE_DEOPT_LOAD_ATTR(); + } + } else { + // The type of the object has either been updated, + // or is different. Maybe it will stabilize? + OPCACHE_MAYBE_DEOPT_LOAD_ATTR(); + } + + OPCACHE_STAT_ATTR_MISS(); + } + + if (co_opcache != NULL && /* co_opcache can be NULL after a DEOPT() call. */ + type->tp_getattro == PyObject_GenericGetAttr) + { + PyObject *descr; + Py_ssize_t ret; + + if (type->tp_dictoffset > 0) { + if (type->tp_dict == NULL) { + if (PyType_Ready(type) < 0) { + Py_DECREF(owner); + SET_TOP(NULL); + goto error; + } + } + + descr = _PyType_Lookup(type, name); + if (descr == NULL || + descr->ob_type->tp_descr_get == NULL || + !PyDescr_IsData(descr)) + { + dictptr = (PyObject **) ((char *)owner + type->tp_dictoffset); + dict = *dictptr; + + if (dict != NULL && PyDict_CheckExact(dict)) { + Py_INCREF(dict); + res = NULL; + ret = _PyDict_GetItemHint((PyDictObject*)dict, name, -1, &res); + if (res != NULL) { + Py_INCREF(res); + Py_DECREF(dict); + Py_DECREF(owner); + SET_TOP(res); + + if (co_opcache->optimized == 0) { + // First time we optimize this opcode. */ + OPCACHE_STAT_ATTR_OPT(); + co_opcache->optimized = OPCODE_CACHE_MAX_TRIES; + } + + la = &co_opcache->u.la; + la->type = type; + la->tp_version_tag = type->tp_version_tag; + la->hint = ret; + + DISPATCH(); + } + Py_DECREF(dict); + } else { + // There is no dict, or __dict__ doesn't satisfy PyDict_CheckExact + OPCACHE_DEOPT_LOAD_ATTR(); + } + } else { + // We failed to find an attribute without a data-like descriptor + OPCACHE_DEOPT_LOAD_ATTR(); + } + } else { + // The object's class does not have a tp_dictoffset we can use + OPCACHE_DEOPT_LOAD_ATTR(); + } + } else if (type->tp_getattro != PyObject_GenericGetAttr) { + OPCACHE_DEOPT_LOAD_ATTR(); + } + } + + /* slow path */ + res = PyObject_GetAttr(owner, name); Py_DECREF(owner); SET_TOP(res); if (res == NULL) From webhook-mailer at python.org Tue Oct 20 03:17:31 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Tue, 20 Oct 2020 07:17:31 -0000 Subject: [Python-checkins] bpo-41471: Ignore invalid prefix lengths in system proxy settings on macOS (GH-22762) (GH-22773) Message-ID: https://github.com/python/cpython/commit/aa7b03b31bf09b21a012b24e21977538e66b0172 commit: aa7b03b31bf09b21a012b24e21977538e66b0172 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-20T09:17:22+02:00 summary: bpo-41471: Ignore invalid prefix lengths in system proxy settings on macOS (GH-22762) (GH-22773) (cherry picked from commit 93a1ccabdede416425473329b8c718d507c55e29) Co-authored-by: Ronald Oussoren files: A Misc/NEWS.d/next/macOS/2020-10-19-12-25-19.bpo-41471.gwA7un.rst M Lib/test/test_urllib2.py M Lib/urllib/request.py diff --git a/Lib/test/test_urllib2.py b/Lib/test/test_urllib2.py index cbfa9ba60c2c8..b71be549d28b5 100644 --- a/Lib/test/test_urllib2.py +++ b/Lib/test/test_urllib2.py @@ -1447,6 +1447,18 @@ def test_osx_proxy_bypass(self): bypass = {'exclude_simple': True, 'exceptions': []} self.assertTrue(_proxy_bypass_macosx_sysconf('test', bypass)) + # Check that invalid prefix lengths are ignored + bypass = { + 'exclude_simple': False, + 'exceptions': [ '10.0.0.0/40', '172.19.10.0/24' ] + } + host = '172.19.10.5' + self.assertTrue(_proxy_bypass_macosx_sysconf(host, bypass), + 'expected bypass of %s to be True' % host) + host = '10.0.1.5' + self.assertFalse(_proxy_bypass_macosx_sysconf(host, bypass), + 'expected bypass of %s to be False' % host) + def check_basic_auth(self, headers, realm): with self.subTest(realm=realm, headers=headers): opener = OpenerDirector() diff --git a/Lib/urllib/request.py b/Lib/urllib/request.py index 2a3d71554f4bf..a8c870b9778eb 100644 --- a/Lib/urllib/request.py +++ b/Lib/urllib/request.py @@ -2596,6 +2596,11 @@ def ip2num(ipAddr): mask = 8 * (m.group(1).count('.') + 1) else: mask = int(mask[1:]) + + if mask < 0 or mask > 32: + # System libraries ignore invalid prefix lengths + continue + mask = 32 - mask if (hostIP >> mask) == (base >> mask): diff --git a/Misc/NEWS.d/next/macOS/2020-10-19-12-25-19.bpo-41471.gwA7un.rst b/Misc/NEWS.d/next/macOS/2020-10-19-12-25-19.bpo-41471.gwA7un.rst new file mode 100644 index 0000000000000..db5dd00b19b0d --- /dev/null +++ b/Misc/NEWS.d/next/macOS/2020-10-19-12-25-19.bpo-41471.gwA7un.rst @@ -0,0 +1 @@ +Ignore invalid prefix lengths in system proxy excludes. From webhook-mailer at python.org Tue Oct 20 03:18:02 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Tue, 20 Oct 2020 07:18:02 -0000 Subject: [Python-checkins] bpo-41471: Ignore invalid prefix lengths in system proxy settings on macOS (GH-22762) (GH-22774) Message-ID: https://github.com/python/cpython/commit/e7c5a43984f82ef9634cb0b2b8c4750b2fd431a0 commit: e7c5a43984f82ef9634cb0b2b8c4750b2fd431a0 branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-20T09:17:58+02:00 summary: bpo-41471: Ignore invalid prefix lengths in system proxy settings on macOS (GH-22762) (GH-22774) (cherry picked from commit 93a1ccabdede416425473329b8c718d507c55e29) Co-authored-by: Ronald Oussoren files: A Misc/NEWS.d/next/macOS/2020-10-19-12-25-19.bpo-41471.gwA7un.rst M Lib/test/test_urllib2.py M Lib/urllib/request.py diff --git a/Lib/test/test_urllib2.py b/Lib/test/test_urllib2.py index 091b3979aa680..0132059fdb0ad 100644 --- a/Lib/test/test_urllib2.py +++ b/Lib/test/test_urllib2.py @@ -1444,6 +1444,18 @@ def test_osx_proxy_bypass(self): bypass = {'exclude_simple': True, 'exceptions': []} self.assertTrue(_proxy_bypass_macosx_sysconf('test', bypass)) + # Check that invalid prefix lengths are ignored + bypass = { + 'exclude_simple': False, + 'exceptions': [ '10.0.0.0/40', '172.19.10.0/24' ] + } + host = '172.19.10.5' + self.assertTrue(_proxy_bypass_macosx_sysconf(host, bypass), + 'expected bypass of %s to be True' % host) + host = '10.0.1.5' + self.assertFalse(_proxy_bypass_macosx_sysconf(host, bypass), + 'expected bypass of %s to be False' % host) + def check_basic_auth(self, headers, realm): with self.subTest(realm=realm, headers=headers): opener = OpenerDirector() diff --git a/Lib/urllib/request.py b/Lib/urllib/request.py index e44073886a6cd..afd634159fa82 100644 --- a/Lib/urllib/request.py +++ b/Lib/urllib/request.py @@ -2604,6 +2604,11 @@ def ip2num(ipAddr): mask = 8 * (m.group(1).count('.') + 1) else: mask = int(mask[1:]) + + if mask < 0 or mask > 32: + # System libraries ignore invalid prefix lengths + continue + mask = 32 - mask if (hostIP >> mask) == (base >> mask): diff --git a/Misc/NEWS.d/next/macOS/2020-10-19-12-25-19.bpo-41471.gwA7un.rst b/Misc/NEWS.d/next/macOS/2020-10-19-12-25-19.bpo-41471.gwA7un.rst new file mode 100644 index 0000000000000..db5dd00b19b0d --- /dev/null +++ b/Misc/NEWS.d/next/macOS/2020-10-19-12-25-19.bpo-41471.gwA7un.rst @@ -0,0 +1 @@ +Ignore invalid prefix lengths in system proxy excludes. From webhook-mailer at python.org Tue Oct 20 03:26:42 2020 From: webhook-mailer at python.org (Ronald Oussoren) Date: Tue, 20 Oct 2020 07:26:42 -0000 Subject: [Python-checkins] bpo-41491: plistlib: accept hexadecimal integer values in xml plist files (GH-22764) Message-ID: https://github.com/python/cpython/commit/3185267400be853404f22a1e06bb9fe1210735c7 commit: 3185267400be853404f22a1e06bb9fe1210735c7 branch: master author: Ronald Oussoren committer: GitHub date: 2020-10-20T09:26:33+02:00 summary: bpo-41491: plistlib: accept hexadecimal integer values in xml plist files (GH-22764) files: A Misc/NEWS.d/next/Library/2020-10-19-14-02-09.bpo-41491.d1BUWH.rst M Lib/plistlib.py M Lib/test/test_plistlib.py diff --git a/Lib/plistlib.py b/Lib/plistlib.py index ba7ac1936479f..a7403510a3216 100644 --- a/Lib/plistlib.py +++ b/Lib/plistlib.py @@ -252,7 +252,11 @@ def end_false(self): self.add_object(False) def end_integer(self): - self.add_object(int(self.get_data())) + raw = self.get_data() + if raw.startswith('0x') or raw.startswith('0X'): + self.add_object(int(raw, 16)) + else: + self.add_object(int(raw)) def end_real(self): self.add_object(float(self.get_data())) diff --git a/Lib/test/test_plistlib.py b/Lib/test/test_plistlib.py index cb071da1f33a1..c9dce0047b79c 100644 --- a/Lib/test/test_plistlib.py +++ b/Lib/test/test_plistlib.py @@ -498,6 +498,19 @@ def test_invalidreal(self): self.assertRaises(ValueError, plistlib.loads, b"not real") + def test_integer_notations(self): + pl = b"456" + value = plistlib.loads(pl) + self.assertEqual(value, 456) + + pl = b"0xa" + value = plistlib.loads(pl) + self.assertEqual(value, 10) + + pl = b"0123" + value = plistlib.loads(pl) + self.assertEqual(value, 123) + def test_xml_encodings(self): base = TESTDATA[plistlib.FMT_XML] diff --git a/Misc/NEWS.d/next/Library/2020-10-19-14-02-09.bpo-41491.d1BUWH.rst b/Misc/NEWS.d/next/Library/2020-10-19-14-02-09.bpo-41491.d1BUWH.rst new file mode 100644 index 0000000000000..4f39c91b284fa --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-10-19-14-02-09.bpo-41491.d1BUWH.rst @@ -0,0 +1 @@ +plistlib: fix parsing XML plists with hexadecimal integer values From webhook-mailer at python.org Tue Oct 20 04:05:26 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Tue, 20 Oct 2020 08:05:26 -0000 Subject: [Python-checkins] bpo-41491: plistlib: accept hexadecimal integer values in xml plist files (GH-22764) (GH-22806) Message-ID: https://github.com/python/cpython/commit/3fc7080220b8dd2e1b067b3224879133d895ea80 commit: 3fc7080220b8dd2e1b067b3224879133d895ea80 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-20T10:05:21+02:00 summary: bpo-41491: plistlib: accept hexadecimal integer values in xml plist files (GH-22764) (GH-22806) (cherry picked from commit 3185267400be853404f22a1e06bb9fe1210735c7) Co-authored-by: Ronald Oussoren files: A Misc/NEWS.d/next/Library/2020-10-19-14-02-09.bpo-41491.d1BUWH.rst M Lib/plistlib.py M Lib/test/test_plistlib.py diff --git a/Lib/plistlib.py b/Lib/plistlib.py index ba7ac1936479f..a7403510a3216 100644 --- a/Lib/plistlib.py +++ b/Lib/plistlib.py @@ -252,7 +252,11 @@ def end_false(self): self.add_object(False) def end_integer(self): - self.add_object(int(self.get_data())) + raw = self.get_data() + if raw.startswith('0x') or raw.startswith('0X'): + self.add_object(int(raw, 16)) + else: + self.add_object(int(raw)) def end_real(self): self.add_object(float(self.get_data())) diff --git a/Lib/test/test_plistlib.py b/Lib/test/test_plistlib.py index 7cc6bad4235cc..f821e1da77484 100644 --- a/Lib/test/test_plistlib.py +++ b/Lib/test/test_plistlib.py @@ -497,6 +497,19 @@ def test_invalidreal(self): self.assertRaises(ValueError, plistlib.loads, b"not real") + def test_integer_notations(self): + pl = b"456" + value = plistlib.loads(pl) + self.assertEqual(value, 456) + + pl = b"0xa" + value = plistlib.loads(pl) + self.assertEqual(value, 10) + + pl = b"0123" + value = plistlib.loads(pl) + self.assertEqual(value, 123) + def test_xml_encodings(self): base = TESTDATA[plistlib.FMT_XML] diff --git a/Misc/NEWS.d/next/Library/2020-10-19-14-02-09.bpo-41491.d1BUWH.rst b/Misc/NEWS.d/next/Library/2020-10-19-14-02-09.bpo-41491.d1BUWH.rst new file mode 100644 index 0000000000000..4f39c91b284fa --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-10-19-14-02-09.bpo-41491.d1BUWH.rst @@ -0,0 +1 @@ +plistlib: fix parsing XML plists with hexadecimal integer values From webhook-mailer at python.org Tue Oct 20 04:05:44 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Tue, 20 Oct 2020 08:05:44 -0000 Subject: [Python-checkins] bpo-41491: plistlib: accept hexadecimal integer values in xml plist files (GH-22764) (GH-22807) Message-ID: https://github.com/python/cpython/commit/d1eb75585ef4c108732ec815cdc0adef087b1c3e commit: d1eb75585ef4c108732ec815cdc0adef087b1c3e branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-20T10:05:40+02:00 summary: bpo-41491: plistlib: accept hexadecimal integer values in xml plist files (GH-22764) (GH-22807) (cherry picked from commit 3185267400be853404f22a1e06bb9fe1210735c7) Co-authored-by: Ronald Oussoren Co-authored-by: Ronald Oussoren files: A Misc/NEWS.d/next/Library/2020-10-19-14-02-09.bpo-41491.d1BUWH.rst M Lib/plistlib.py M Lib/test/test_plistlib.py diff --git a/Lib/plistlib.py b/Lib/plistlib.py index 533ed1351f135..9813613ff153b 100644 --- a/Lib/plistlib.py +++ b/Lib/plistlib.py @@ -364,7 +364,11 @@ def end_false(self): self.add_object(False) def end_integer(self): - self.add_object(int(self.get_data())) + raw = self.get_data() + if raw.startswith('0x') or raw.startswith('0X'): + self.add_object(int(raw, 16)) + else: + self.add_object(int(raw)) def end_real(self): self.add_object(float(self.get_data())) diff --git a/Lib/test/test_plistlib.py b/Lib/test/test_plistlib.py index de1c848ae265f..d714880a7dd25 100644 --- a/Lib/test/test_plistlib.py +++ b/Lib/test/test_plistlib.py @@ -499,6 +499,19 @@ def test_invalidreal(self): self.assertRaises(ValueError, plistlib.loads, b"not real") + def test_integer_notations(self): + pl = b"456" + value = plistlib.loads(pl) + self.assertEqual(value, 456) + + pl = b"0xa" + value = plistlib.loads(pl) + self.assertEqual(value, 10) + + pl = b"0123" + value = plistlib.loads(pl) + self.assertEqual(value, 123) + def test_xml_encodings(self): base = TESTDATA[plistlib.FMT_XML] diff --git a/Misc/NEWS.d/next/Library/2020-10-19-14-02-09.bpo-41491.d1BUWH.rst b/Misc/NEWS.d/next/Library/2020-10-19-14-02-09.bpo-41491.d1BUWH.rst new file mode 100644 index 0000000000000..4f39c91b284fa --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-10-19-14-02-09.bpo-41491.d1BUWH.rst @@ -0,0 +1 @@ +plistlib: fix parsing XML plists with hexadecimal integer values From webhook-mailer at python.org Tue Oct 20 05:09:10 2020 From: webhook-mailer at python.org (Tomer Cohen) Date: Tue, 20 Oct 2020 09:09:10 -0000 Subject: [Python-checkins] Doc: Add missing spaces after period for `posix_spawn` (GH-22730) Message-ID: https://github.com/python/cpython/commit/5b57fa69408c4856bf0ae02f2eaa3db1ef980ea6 commit: 5b57fa69408c4856bf0ae02f2eaa3db1ef980ea6 branch: master author: Tomer Cohen committer: GitHub date: 2020-10-20T18:08:58+09:00 summary: Doc: Add missing spaces after period for `posix_spawn` (GH-22730) files: M Doc/library/os.rst diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 8c3bc5fb87d61..718d98138d267 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -3710,8 +3710,8 @@ written in Python, such as a mail server's external command delivery program. The positional-only arguments *path*, *args*, and *env* are similar to :func:`execve`. - The *path* parameter is the path to the executable file.The *path* should - contain a directory.Use :func:`posix_spawnp` to pass an executable file + The *path* parameter is the path to the executable file. The *path* should + contain a directory. Use :func:`posix_spawnp` to pass an executable file without directory. The *file_actions* argument may be a sequence of tuples describing actions From webhook-mailer at python.org Tue Oct 20 05:10:51 2020 From: webhook-mailer at python.org (Jakub Jelen) Date: Tue, 20 Oct 2020 09:10:51 -0000 Subject: [Python-checkins] md5module: Fix doc strings variable names (GH-22722) Message-ID: https://github.com/python/cpython/commit/d5d052127059fd99c90ea7a2e948a0242a1d7285 commit: d5d052127059fd99c90ea7a2e948a0242a1d7285 branch: master author: Jakub Jelen committer: GitHub date: 2020-10-20T18:10:43+09:00 summary: md5module: Fix doc strings variable names (GH-22722) files: M Modules/md5module.c diff --git a/Modules/md5module.c b/Modules/md5module.c index 5cd4e94510132..9bd2bd17e4fbf 100644 --- a/Modules/md5module.c +++ b/Modules/md5module.c @@ -211,7 +211,7 @@ static void md5_compress(struct md5_state *md5, const unsigned char *buf) /** Initialize the hash state - @param sha1 The hash state you wish to initialize + @param md5 The hash state you wish to initialize */ static void md5_init(struct md5_state *md5) @@ -227,7 +227,7 @@ md5_init(struct md5_state *md5) /** Process a block of memory though the hash - @param sha1 The hash state + @param md5 The hash state @param in The data to hash @param inlen The length of the data (octets) */ @@ -263,7 +263,7 @@ md5_process(struct md5_state *md5, const unsigned char *in, Py_ssize_t inlen) /** Terminate the hash to get the digest - @param sha1 The hash state + @param md5 The hash state @param out [out] The destination of the hash (16 bytes) */ static void From webhook-mailer at python.org Tue Oct 20 07:39:58 2020 From: webhook-mailer at python.org (TIGirardi) Date: Tue, 20 Oct 2020 11:39:58 -0000 Subject: [Python-checkins] bpo-38324: Fix test__locale.py Windows failures (GH-20529) Message-ID: https://github.com/python/cpython/commit/f2312037e3a974d26ed3e23884f94c6af111a27a commit: f2312037e3a974d26ed3e23884f94c6af111a27a branch: master author: TIGirardi committer: GitHub date: 2020-10-20T12:39:52+01:00 summary: bpo-38324: Fix test__locale.py Windows failures (GH-20529) Use wide-char _W_* fields of lconv structure on Windows Remove "ps_AF" from test__locale.known_numerics on Windows files: A Misc/NEWS.d/next/Windows/2020-05-30-02-46-43.bpo-38324.476M-5.rst M Lib/test/test__locale.py M Modules/_localemodule.c M Python/fileutils.c diff --git a/Lib/test/test__locale.py b/Lib/test/test__locale.py index cda0ee91b700f..59a00bad7d98a 100644 --- a/Lib/test/test__locale.py +++ b/Lib/test/test__locale.py @@ -72,6 +72,10 @@ def accept(loc): 'ps_AF': ('\u066b', '\u066c'), } +if sys.platform == 'win32': + # ps_AF doesn't work on Windows: see bpo-38324 (msg361830) + del known_numerics['ps_AF'] + class _LocaleTests(unittest.TestCase): def setUp(self): diff --git a/Misc/NEWS.d/next/Windows/2020-05-30-02-46-43.bpo-38324.476M-5.rst b/Misc/NEWS.d/next/Windows/2020-05-30-02-46-43.bpo-38324.476M-5.rst new file mode 100644 index 0000000000000..c45aa13091429 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2020-05-30-02-46-43.bpo-38324.476M-5.rst @@ -0,0 +1 @@ +Avoid Unicode errors when accessing certain locale data on Windows. diff --git a/Modules/_localemodule.c b/Modules/_localemodule.c index 0fe2e08b41f9f..9c7ce876e4059 100644 --- a/Modules/_localemodule.c +++ b/Modules/_localemodule.c @@ -155,6 +155,7 @@ locale_is_ascii(const char *str) static int locale_decode_monetary(PyObject *dict, struct lconv *lc) { +#ifndef MS_WINDOWS int change_locale; change_locale = (!locale_is_ascii(lc->int_curr_symbol) || !locale_is_ascii(lc->currency_symbol) @@ -190,12 +191,18 @@ locale_decode_monetary(PyObject *dict, struct lconv *lc) } } +#define GET_LOCALE_STRING(ATTR) PyUnicode_DecodeLocale(lc->ATTR, NULL) +#else /* MS_WINDOWS */ +/* Use _W_* fields of Windows struct lconv */ +#define GET_LOCALE_STRING(ATTR) PyUnicode_FromWideChar(lc->_W_ ## ATTR, -1) +#endif /* MS_WINDOWS */ + int res = -1; #define RESULT_STRING(ATTR) \ do { \ PyObject *obj; \ - obj = PyUnicode_DecodeLocale(lc->ATTR, NULL); \ + obj = GET_LOCALE_STRING(ATTR); \ if (obj == NULL) { \ goto done; \ } \ @@ -211,14 +218,17 @@ locale_decode_monetary(PyObject *dict, struct lconv *lc) RESULT_STRING(mon_decimal_point); RESULT_STRING(mon_thousands_sep); #undef RESULT_STRING +#undef GET_LOCALE_STRING res = 0; done: +#ifndef MS_WINDOWS if (loc != NULL) { setlocale(LC_CTYPE, oldloc); } PyMem_Free(oldloc); +#endif return res; } @@ -258,9 +268,15 @@ _locale_localeconv_impl(PyObject *module) Py_DECREF(obj); \ } while (0) +#ifdef MS_WINDOWS +/* Use _W_* fields of Windows struct lconv */ +#define GET_LOCALE_STRING(ATTR) PyUnicode_FromWideChar(lc->_W_ ## ATTR, -1) +#else +#define GET_LOCALE_STRING(ATTR) PyUnicode_DecodeLocale(lc->ATTR, NULL) +#endif #define RESULT_STRING(s)\ do { \ - x = PyUnicode_DecodeLocale(lc->s, NULL); \ + x = GET_LOCALE_STRING(s); \ RESULT(#s, x); \ } while (0) @@ -289,8 +305,10 @@ _locale_localeconv_impl(PyObject *module) RESULT_INT(n_sign_posn); /* Numeric information: LC_NUMERIC encoding */ - PyObject *decimal_point, *thousands_sep; + PyObject *decimal_point = NULL, *thousands_sep = NULL; if (_Py_GetLocaleconvNumeric(lc, &decimal_point, &thousands_sep) < 0) { + Py_XDECREF(decimal_point); + Py_XDECREF(thousands_sep); goto failed; } @@ -319,6 +337,7 @@ _locale_localeconv_impl(PyObject *module) #undef RESULT #undef RESULT_STRING #undef RESULT_INT +#undef GET_LOCALE_STRING } #if defined(HAVE_WCSCOLL) diff --git a/Python/fileutils.c b/Python/fileutils.c index b79067f2b5d38..e125ba46c21ba 100644 --- a/Python/fileutils.c +++ b/Python/fileutils.c @@ -2047,6 +2047,7 @@ _Py_GetLocaleconvNumeric(struct lconv *lc, assert(decimal_point != NULL); assert(thousands_sep != NULL); +#ifndef MS_WINDOWS int change_locale = 0; if ((strlen(lc->decimal_point) > 1 || ((unsigned char)lc->decimal_point[0]) > 127)) { change_locale = 1; @@ -2085,14 +2086,20 @@ _Py_GetLocaleconvNumeric(struct lconv *lc, } } +#define GET_LOCALE_STRING(ATTR) PyUnicode_DecodeLocale(lc->ATTR, NULL) +#else /* MS_WINDOWS */ +/* Use _W_* fields of Windows strcut lconv */ +#define GET_LOCALE_STRING(ATTR) PyUnicode_FromWideChar(lc->_W_ ## ATTR, -1) +#endif /* MS_WINDOWS */ + int res = -1; - *decimal_point = PyUnicode_DecodeLocale(lc->decimal_point, NULL); + *decimal_point = GET_LOCALE_STRING(decimal_point); if (*decimal_point == NULL) { goto done; } - *thousands_sep = PyUnicode_DecodeLocale(lc->thousands_sep, NULL); + *thousands_sep = GET_LOCALE_STRING(thousands_sep); if (*thousands_sep == NULL) { goto done; } @@ -2100,11 +2107,15 @@ _Py_GetLocaleconvNumeric(struct lconv *lc, res = 0; done: +#ifndef MS_WINDOWS if (loc != NULL) { setlocale(LC_CTYPE, oldloc); } PyMem_Free(oldloc); +#endif return res; + +#undef GET_LOCALE_STRING } /* Our selection logic for which function to use is as follows: From webhook-mailer at python.org Tue Oct 20 07:58:18 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Tue, 20 Oct 2020 11:58:18 -0000 Subject: [Python-checkins] bpo-38324: Fix test__locale.py Windows failures (GH-20529) Message-ID: https://github.com/python/cpython/commit/4cde523aa467c92a713674c793ab87e7c56486c4 commit: 4cde523aa467c92a713674c793ab87e7c56486c4 branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-20T04:58:06-07:00 summary: bpo-38324: Fix test__locale.py Windows failures (GH-20529) Use wide-char _W_* fields of lconv structure on Windows Remove "ps_AF" from test__locale.known_numerics on Windows (cherry picked from commit f2312037e3a974d26ed3e23884f94c6af111a27a) Co-authored-by: TIGirardi files: A Misc/NEWS.d/next/Windows/2020-05-30-02-46-43.bpo-38324.476M-5.rst M Lib/test/test__locale.py M Modules/_localemodule.c M Python/fileutils.c diff --git a/Lib/test/test__locale.py b/Lib/test/test__locale.py index ab4e24796188c..560f077274275 100644 --- a/Lib/test/test__locale.py +++ b/Lib/test/test__locale.py @@ -72,6 +72,10 @@ def accept(loc): 'ps_AF': ('\u066b', '\u066c'), } +if sys.platform == 'win32': + # ps_AF doesn't work on Windows: see bpo-38324 (msg361830) + del known_numerics['ps_AF'] + class _LocaleTests(unittest.TestCase): def setUp(self): diff --git a/Misc/NEWS.d/next/Windows/2020-05-30-02-46-43.bpo-38324.476M-5.rst b/Misc/NEWS.d/next/Windows/2020-05-30-02-46-43.bpo-38324.476M-5.rst new file mode 100644 index 0000000000000..c45aa13091429 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2020-05-30-02-46-43.bpo-38324.476M-5.rst @@ -0,0 +1 @@ +Avoid Unicode errors when accessing certain locale data on Windows. diff --git a/Modules/_localemodule.c b/Modules/_localemodule.c index 036bdb301f320..d54f70904e456 100644 --- a/Modules/_localemodule.c +++ b/Modules/_localemodule.c @@ -131,6 +131,7 @@ locale_is_ascii(const char *str) static int locale_decode_monetary(PyObject *dict, struct lconv *lc) { +#ifndef MS_WINDOWS int change_locale; change_locale = (!locale_is_ascii(lc->int_curr_symbol) || !locale_is_ascii(lc->currency_symbol) @@ -166,12 +167,18 @@ locale_decode_monetary(PyObject *dict, struct lconv *lc) } } +#define GET_LOCALE_STRING(ATTR) PyUnicode_DecodeLocale(lc->ATTR, NULL) +#else /* MS_WINDOWS */ +/* Use _W_* fields of Windows struct lconv */ +#define GET_LOCALE_STRING(ATTR) PyUnicode_FromWideChar(lc->_W_ ## ATTR, -1) +#endif /* MS_WINDOWS */ + int res = -1; #define RESULT_STRING(ATTR) \ do { \ PyObject *obj; \ - obj = PyUnicode_DecodeLocale(lc->ATTR, NULL); \ + obj = GET_LOCALE_STRING(ATTR); \ if (obj == NULL) { \ goto done; \ } \ @@ -187,14 +194,17 @@ locale_decode_monetary(PyObject *dict, struct lconv *lc) RESULT_STRING(mon_decimal_point); RESULT_STRING(mon_thousands_sep); #undef RESULT_STRING +#undef GET_LOCALE_STRING res = 0; done: +#ifndef MS_WINDOWS if (loc != NULL) { setlocale(LC_CTYPE, oldloc); } PyMem_Free(oldloc); +#endif return res; } @@ -230,9 +240,15 @@ PyLocale_localeconv(PyObject* self, PyObject *Py_UNUSED(ignored)) Py_DECREF(obj); \ } while (0) +#ifdef MS_WINDOWS +/* Use _W_* fields of Windows struct lconv */ +#define GET_LOCALE_STRING(ATTR) PyUnicode_FromWideChar(lc->_W_ ## ATTR, -1) +#else +#define GET_LOCALE_STRING(ATTR) PyUnicode_DecodeLocale(lc->ATTR, NULL) +#endif #define RESULT_STRING(s)\ do { \ - x = PyUnicode_DecodeLocale(lc->s, NULL); \ + x = GET_LOCALE_STRING(s); \ RESULT(#s, x); \ } while (0) @@ -261,8 +277,10 @@ PyLocale_localeconv(PyObject* self, PyObject *Py_UNUSED(ignored)) RESULT_INT(n_sign_posn); /* Numeric information: LC_NUMERIC encoding */ - PyObject *decimal_point, *thousands_sep; + PyObject *decimal_point = NULL, *thousands_sep = NULL; if (_Py_GetLocaleconvNumeric(lc, &decimal_point, &thousands_sep) < 0) { + Py_XDECREF(decimal_point); + Py_XDECREF(thousands_sep); goto failed; } @@ -291,6 +309,7 @@ PyLocale_localeconv(PyObject* self, PyObject *Py_UNUSED(ignored)) #undef RESULT #undef RESULT_STRING #undef RESULT_INT +#undef GET_LOCALE_STRING } #if defined(HAVE_WCSCOLL) diff --git a/Python/fileutils.c b/Python/fileutils.c index b274116745efe..25516c23bf7ab 100644 --- a/Python/fileutils.c +++ b/Python/fileutils.c @@ -1933,6 +1933,7 @@ _Py_GetLocaleconvNumeric(struct lconv *lc, assert(decimal_point != NULL); assert(thousands_sep != NULL); +#ifndef MS_WINDOWS int change_locale = 0; if ((strlen(lc->decimal_point) > 1 || ((unsigned char)lc->decimal_point[0]) > 127)) { change_locale = 1; @@ -1971,14 +1972,20 @@ _Py_GetLocaleconvNumeric(struct lconv *lc, } } +#define GET_LOCALE_STRING(ATTR) PyUnicode_DecodeLocale(lc->ATTR, NULL) +#else /* MS_WINDOWS */ +/* Use _W_* fields of Windows strcut lconv */ +#define GET_LOCALE_STRING(ATTR) PyUnicode_FromWideChar(lc->_W_ ## ATTR, -1) +#endif /* MS_WINDOWS */ + int res = -1; - *decimal_point = PyUnicode_DecodeLocale(lc->decimal_point, NULL); + *decimal_point = GET_LOCALE_STRING(decimal_point); if (*decimal_point == NULL) { goto done; } - *thousands_sep = PyUnicode_DecodeLocale(lc->thousands_sep, NULL); + *thousands_sep = GET_LOCALE_STRING(thousands_sep); if (*thousands_sep == NULL) { goto done; } @@ -1986,9 +1993,13 @@ _Py_GetLocaleconvNumeric(struct lconv *lc, res = 0; done: +#ifndef MS_WINDOWS if (loc != NULL) { setlocale(LC_CTYPE, oldloc); } PyMem_Free(oldloc); +#endif return res; + +#undef GET_LOCALE_STRING } From webhook-mailer at python.org Tue Oct 20 08:01:17 2020 From: webhook-mailer at python.org (Zackery Spytz) Date: Tue, 20 Oct 2020 12:01:17 -0000 Subject: [Python-checkins] bpo-30612: Tweak Windows registry path syntax in the docs (GH-20281) Message-ID: https://github.com/python/cpython/commit/ff1ae3dd334faa2006394c2318db385cdc42030a commit: ff1ae3dd334faa2006394c2318db385cdc42030a branch: master author: Zackery Spytz committer: GitHub date: 2020-10-20T13:01:12+01:00 summary: bpo-30612: Tweak Windows registry path syntax in the docs (GH-20281) files: M Doc/using/windows.rst diff --git a/Doc/using/windows.rst b/Doc/using/windows.rst index b95a43c853c28..275495bc6d129 100644 --- a/Doc/using/windows.rst +++ b/Doc/using/windows.rst @@ -103,9 +103,9 @@ paths longer than this would not resolve and errors would result. In the latest versions of Windows, this limitation can be expanded to approximately 32,000 characters. Your administrator will need to activate the -"Enable Win32 long paths" group policy, or set the registry value -``HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem at LongPathsEnabled`` -to ``1``. +"Enable Win32 long paths" group policy, or set ``LongPathsEnabled`` to ``1`` +in the registry key +``HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem``. This allows the :func:`open` function, the :mod:`os` module and most other path functionality to accept and return paths longer than 260 characters. From webhook-mailer at python.org Tue Oct 20 08:07:18 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Tue, 20 Oct 2020 12:07:18 -0000 Subject: [Python-checkins] bpo-38324: Fix test__locale.py Windows failures (GH-20529) Message-ID: https://github.com/python/cpython/commit/c17ff5cad2952d9abd63aeae060cae5658257b0c commit: c17ff5cad2952d9abd63aeae060cae5658257b0c branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-20T05:07:14-07:00 summary: bpo-38324: Fix test__locale.py Windows failures (GH-20529) Use wide-char _W_* fields of lconv structure on Windows Remove "ps_AF" from test__locale.known_numerics on Windows (cherry picked from commit f2312037e3a974d26ed3e23884f94c6af111a27a) Co-authored-by: TIGirardi files: A Misc/NEWS.d/next/Windows/2020-05-30-02-46-43.bpo-38324.476M-5.rst M Lib/test/test__locale.py M Modules/_localemodule.c M Python/fileutils.c diff --git a/Lib/test/test__locale.py b/Lib/test/test__locale.py index cda0ee91b700f..59a00bad7d98a 100644 --- a/Lib/test/test__locale.py +++ b/Lib/test/test__locale.py @@ -72,6 +72,10 @@ def accept(loc): 'ps_AF': ('\u066b', '\u066c'), } +if sys.platform == 'win32': + # ps_AF doesn't work on Windows: see bpo-38324 (msg361830) + del known_numerics['ps_AF'] + class _LocaleTests(unittest.TestCase): def setUp(self): diff --git a/Misc/NEWS.d/next/Windows/2020-05-30-02-46-43.bpo-38324.476M-5.rst b/Misc/NEWS.d/next/Windows/2020-05-30-02-46-43.bpo-38324.476M-5.rst new file mode 100644 index 0000000000000..c45aa13091429 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2020-05-30-02-46-43.bpo-38324.476M-5.rst @@ -0,0 +1 @@ +Avoid Unicode errors when accessing certain locale data on Windows. diff --git a/Modules/_localemodule.c b/Modules/_localemodule.c index 0819d0e192408..2e353bba00bf3 100644 --- a/Modules/_localemodule.c +++ b/Modules/_localemodule.c @@ -144,6 +144,7 @@ locale_is_ascii(const char *str) static int locale_decode_monetary(PyObject *dict, struct lconv *lc) { +#ifndef MS_WINDOWS int change_locale; change_locale = (!locale_is_ascii(lc->int_curr_symbol) || !locale_is_ascii(lc->currency_symbol) @@ -179,12 +180,18 @@ locale_decode_monetary(PyObject *dict, struct lconv *lc) } } +#define GET_LOCALE_STRING(ATTR) PyUnicode_DecodeLocale(lc->ATTR, NULL) +#else /* MS_WINDOWS */ +/* Use _W_* fields of Windows struct lconv */ +#define GET_LOCALE_STRING(ATTR) PyUnicode_FromWideChar(lc->_W_ ## ATTR, -1) +#endif /* MS_WINDOWS */ + int res = -1; #define RESULT_STRING(ATTR) \ do { \ PyObject *obj; \ - obj = PyUnicode_DecodeLocale(lc->ATTR, NULL); \ + obj = GET_LOCALE_STRING(ATTR); \ if (obj == NULL) { \ goto done; \ } \ @@ -200,14 +207,17 @@ locale_decode_monetary(PyObject *dict, struct lconv *lc) RESULT_STRING(mon_decimal_point); RESULT_STRING(mon_thousands_sep); #undef RESULT_STRING +#undef GET_LOCALE_STRING res = 0; done: +#ifndef MS_WINDOWS if (loc != NULL) { setlocale(LC_CTYPE, oldloc); } PyMem_Free(oldloc); +#endif return res; } @@ -243,9 +253,15 @@ PyLocale_localeconv(PyObject* self, PyObject *Py_UNUSED(ignored)) Py_DECREF(obj); \ } while (0) +#ifdef MS_WINDOWS +/* Use _W_* fields of Windows struct lconv */ +#define GET_LOCALE_STRING(ATTR) PyUnicode_FromWideChar(lc->_W_ ## ATTR, -1) +#else +#define GET_LOCALE_STRING(ATTR) PyUnicode_DecodeLocale(lc->ATTR, NULL) +#endif #define RESULT_STRING(s)\ do { \ - x = PyUnicode_DecodeLocale(lc->s, NULL); \ + x = GET_LOCALE_STRING(s); \ RESULT(#s, x); \ } while (0) @@ -274,8 +290,10 @@ PyLocale_localeconv(PyObject* self, PyObject *Py_UNUSED(ignored)) RESULT_INT(n_sign_posn); /* Numeric information: LC_NUMERIC encoding */ - PyObject *decimal_point, *thousands_sep; + PyObject *decimal_point = NULL, *thousands_sep = NULL; if (_Py_GetLocaleconvNumeric(lc, &decimal_point, &thousands_sep) < 0) { + Py_XDECREF(decimal_point); + Py_XDECREF(thousands_sep); goto failed; } @@ -304,6 +322,7 @@ PyLocale_localeconv(PyObject* self, PyObject *Py_UNUSED(ignored)) #undef RESULT #undef RESULT_STRING #undef RESULT_INT +#undef GET_LOCALE_STRING } #if defined(HAVE_WCSCOLL) diff --git a/Python/fileutils.c b/Python/fileutils.c index 2c86828ba989a..397ac34e8073d 100644 --- a/Python/fileutils.c +++ b/Python/fileutils.c @@ -2032,6 +2032,7 @@ _Py_GetLocaleconvNumeric(struct lconv *lc, assert(decimal_point != NULL); assert(thousands_sep != NULL); +#ifndef MS_WINDOWS int change_locale = 0; if ((strlen(lc->decimal_point) > 1 || ((unsigned char)lc->decimal_point[0]) > 127)) { change_locale = 1; @@ -2070,14 +2071,20 @@ _Py_GetLocaleconvNumeric(struct lconv *lc, } } +#define GET_LOCALE_STRING(ATTR) PyUnicode_DecodeLocale(lc->ATTR, NULL) +#else /* MS_WINDOWS */ +/* Use _W_* fields of Windows strcut lconv */ +#define GET_LOCALE_STRING(ATTR) PyUnicode_FromWideChar(lc->_W_ ## ATTR, -1) +#endif /* MS_WINDOWS */ + int res = -1; - *decimal_point = PyUnicode_DecodeLocale(lc->decimal_point, NULL); + *decimal_point = GET_LOCALE_STRING(decimal_point); if (*decimal_point == NULL) { goto done; } - *thousands_sep = PyUnicode_DecodeLocale(lc->thousands_sep, NULL); + *thousands_sep = GET_LOCALE_STRING(thousands_sep); if (*thousands_sep == NULL) { goto done; } @@ -2085,9 +2092,13 @@ _Py_GetLocaleconvNumeric(struct lconv *lc, res = 0; done: +#ifndef MS_WINDOWS if (loc != NULL) { setlocale(LC_CTYPE, oldloc); } PyMem_Free(oldloc); +#endif return res; + +#undef GET_LOCALE_STRING } From webhook-mailer at python.org Tue Oct 20 08:11:29 2020 From: webhook-mailer at python.org (Miss Skeleton (bot)) Date: Tue, 20 Oct 2020 12:11:29 -0000 Subject: [Python-checkins] bpo-30612: Tweak Windows registry path syntax in the docs (GH-20281) Message-ID: https://github.com/python/cpython/commit/f53ea8adaecacfe8c1e4a093136fb5318b15d779 commit: f53ea8adaecacfe8c1e4a093136fb5318b15d779 branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2020-10-20T05:11:25-07:00 summary: bpo-30612: Tweak Windows registry path syntax in the docs (GH-20281) (cherry picked from commit ff1ae3dd334faa2006394c2318db385cdc42030a) Co-authored-by: Zackery Spytz files: M Doc/using/windows.rst diff --git a/Doc/using/windows.rst b/Doc/using/windows.rst index 5114a26a57d07..46390707e3108 100644 --- a/Doc/using/windows.rst +++ b/Doc/using/windows.rst @@ -103,9 +103,9 @@ paths longer than this would not resolve and errors would result. In the latest versions of Windows, this limitation can be expanded to approximately 32,000 characters. Your administrator will need to activate the -"Enable Win32 long paths" group policy, or set the registry value -``HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem at LongPathsEnabled`` -to ``1``. +"Enable Win32 long paths" group policy, or set ``LongPathsEnabled`` to ``1`` +in the registry key +``HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem``. This allows the :func:`open` function, the :mod:`os` module and most other path functionality to accept and return paths longer than 260 characters. From webhook-mailer at python.org Tue Oct 20 08:21:15 2020 From: webhook-mailer at python.org (Miro =?utf-8?q?Hron=C4=8Dok?=) Date: Tue, 20 Oct 2020 12:21:15 -0000 Subject: [Python-checkins] bpo-38439: Add 256px IDLE icon to the .ico, drop gifs from it (GH-19648) Message-ID: https://github.com/python/cpython/commit/faddc7449d07e883b8ad8bb95dd68ce6dbdc06e8 commit: faddc7449d07e883b8ad8bb95dd68ce6dbdc06e8 branch: master author: Miro Hron?ok committer: GitHub date: 2020-10-20T13:21:08+01:00 summary: bpo-38439: Add 256px IDLE icon to the .ico, drop gifs from it (GH-19648) files: A Misc/NEWS.d/next/IDLE/2020-04-22-09-37-40.bpo-38439.ieXL-c.rst M Lib/idlelib/Icons/README.txt M Lib/idlelib/Icons/idle.ico diff --git a/Lib/idlelib/Icons/README.txt b/Lib/idlelib/Icons/README.txt index 8b471629ecb3e..d91c4d5d8d8cf 100644 --- a/Lib/idlelib/Icons/README.txt +++ b/Lib/idlelib/Icons/README.txt @@ -7,3 +7,7 @@ https://www.doxdesk.com/software/py/pyicons.html Various different formats and sizes are available at this GitHub Pull Request: https://github.com/python/cpython/pull/17473 + +The idle.ico file was created with ImageMagick: + + $ convert idle_16.png idle_32.png idle_48.png idle_256.png idle.ico diff --git a/Lib/idlelib/Icons/idle.ico b/Lib/idlelib/Icons/idle.ico index 3357aef14888c..2aa9a8300d9e2 100644 Binary files a/Lib/idlelib/Icons/idle.ico and b/Lib/idlelib/Icons/idle.ico differ diff --git a/Misc/NEWS.d/next/IDLE/2020-04-22-09-37-40.bpo-38439.ieXL-c.rst b/Misc/NEWS.d/next/IDLE/2020-04-22-09-37-40.bpo-38439.ieXL-c.rst new file mode 100644 index 0000000000000..d8d59015f20e3 --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2020-04-22-09-37-40.bpo-38439.ieXL-c.rst @@ -0,0 +1,2 @@ +Add a 256?256 pixel IDLE icon to the Windows .ico file. Created by Andrew +Clover. Remove the low-color gif variations from the .ico file. From webhook-mailer at python.org Tue Oct 20 08:23:44 2020 From: webhook-mailer at python.org (miss-islington) Date: Tue, 20 Oct 2020 12:23:44 -0000 Subject: [Python-checkins] bpo-30612: Tweak Windows registry path syntax in the docs (GH-20281) Message-ID: https://github.com/python/cpython/commit/bd9d76bbbb9eb226d99e186dae39e06ba8977485 commit: bd9d76bbbb9eb226d99e186dae39e06ba8977485 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-20T05:23:39-07:00 summary: bpo-30612: Tweak Windows registry path syntax in the docs (GH-20281) (cherry picked from commit ff1ae3dd334faa2006394c2318db385cdc42030a) Co-authored-by: Zackery Spytz files: M Doc/using/windows.rst diff --git a/Doc/using/windows.rst b/Doc/using/windows.rst index b95a43c853c28..275495bc6d129 100644 --- a/Doc/using/windows.rst +++ b/Doc/using/windows.rst @@ -103,9 +103,9 @@ paths longer than this would not resolve and errors would result. In the latest versions of Windows, this limitation can be expanded to approximately 32,000 characters. Your administrator will need to activate the -"Enable Win32 long paths" group policy, or set the registry value -``HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem at LongPathsEnabled`` -to ``1``. +"Enable Win32 long paths" group policy, or set ``LongPathsEnabled`` to ``1`` +in the registry key +``HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem``. This allows the :func:`open` function, the :mod:`os` module and most other path functionality to accept and return paths longer than 260 characters. From webhook-mailer at python.org Tue Oct 20 08:30:10 2020 From: webhook-mailer at python.org (encukou) Date: Tue, 20 Oct 2020 12:30:10 -0000 Subject: [Python-checkins] bpo-41726: Update the refcounts info of PyType_FromModuleAndSpec in refcounts.dat (GH-22112) (GH-22808) Message-ID: https://github.com/python/cpython/commit/38a17d0752781c70fd971f372ddc8d3caf5ded88 commit: 38a17d0752781c70fd971f372ddc8d3caf5ded88 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: encukou date: 2020-10-20T14:30:04+02:00 summary: bpo-41726: Update the refcounts info of PyType_FromModuleAndSpec in refcounts.dat (GH-22112) (GH-22808) Update refcounts info of PyType_FromModuleAndSpec in refcounts.dat (cherry picked from commit 1e2f051a6127904cfee5f9e4021dd6e4a4d51c0f) Co-authored-by: Hai Shi files: A Misc/NEWS.d/next/Documentation/2020-09-08-16-57-09.bpo-41726.g0UXrn.rst M Doc/data/refcounts.dat diff --git a/Doc/data/refcounts.dat b/Doc/data/refcounts.dat index 4dacbe201d22a..aac135113ad7e 100644 --- a/Doc/data/refcounts.dat +++ b/Doc/data/refcounts.dat @@ -2306,6 +2306,11 @@ PyType_CheckExact:PyObject*:o:0: PyType_FromSpec:PyObject*::+1: PyType_FromSpec:PyType_Spec*:spec:: +PyType_FromModuleAndSpec:PyObject*::+1: +PyType_FromModuleAndSpec:PyObject*:module:+1: +PyType_FromModuleAndSpec:PyType_Spec*:spec:: +PyType_FromModuleAndSpec:PyObject*:bases:0: + PyType_FromSpecWithBases:PyObject*::+1: PyType_FromSpecWithBases:PyType_Spec*:spec:: PyType_FromSpecWithBases:PyObject*:bases:0: diff --git a/Misc/NEWS.d/next/Documentation/2020-09-08-16-57-09.bpo-41726.g0UXrn.rst b/Misc/NEWS.d/next/Documentation/2020-09-08-16-57-09.bpo-41726.g0UXrn.rst new file mode 100644 index 0000000000000..1079a757c054a --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2020-09-08-16-57-09.bpo-41726.g0UXrn.rst @@ -0,0 +1 @@ +Update the refcounts info of ``PyType_FromModuleAndSpec``. From webhook-mailer at python.org Tue Oct 20 10:05:18 2020 From: webhook-mailer at python.org (akuchling) Date: Tue, 20 Oct 2020 14:05:18 -0000 Subject: [Python-checkins] bpo-39693: mention KeyError in tarfile extractfile documentation (GH-18639) Message-ID: https://github.com/python/cpython/commit/ec42789e6e14f6b6ac13569aeadc13798d7173a8 commit: ec42789e6e14f6b6ac13569aeadc13798d7173a8 branch: master author: Andrey Doroschenko committer: akuchling date: 2020-10-20T10:05:01-04:00 summary: bpo-39693: mention KeyError in tarfile extractfile documentation (GH-18639) Co-authored-by: Andrey Darascheka files: A Misc/NEWS.d/next/Documentation/2020-02-24-09-02-05.bpo-39693.QXw0Fm.rst M Doc/library/tarfile.rst M Lib/tarfile.py M Misc/ACKS diff --git a/Doc/library/tarfile.rst b/Doc/library/tarfile.rst index cca466b569794..7a114fdf5d54b 100644 --- a/Doc/library/tarfile.rst +++ b/Doc/library/tarfile.rst @@ -445,10 +445,11 @@ be finalized; only the internally used file object will be closed. See the .. method:: TarFile.extractfile(member) - Extract a member from the archive as a file object. *member* may be a filename - or a :class:`TarInfo` object. If *member* is a regular file or a link, an - :class:`io.BufferedReader` object is returned. Otherwise, :const:`None` is - returned. + Extract a member from the archive as a file object. *member* may be + a filename or a :class:`TarInfo` object. If *member* is a regular file or + a link, an :class:`io.BufferedReader` object is returned. For all other + existing members, :const:`None` is returned. If *member* does not appear + in the archive, :exc:`KeyError` is raised. .. versionchanged:: 3.3 Return an :class:`io.BufferedReader` object. diff --git a/Lib/tarfile.py b/Lib/tarfile.py index 1fae29430feff..e42279470dac6 100755 --- a/Lib/tarfile.py +++ b/Lib/tarfile.py @@ -2093,9 +2093,10 @@ def extract(self, member, path="", set_attrs=True, *, numeric_owner=False): def extractfile(self, member): """Extract a member from the archive as a file object. `member' may be - a filename or a TarInfo object. If `member' is a regular file or a - link, an io.BufferedReader object is returned. Otherwise, None is - returned. + a filename or a TarInfo object. If `member' is a regular file or + a link, an io.BufferedReader object is returned. For all other + existing members, None is returned. If `member' does not appear + in the archive, KeyError is raised. """ self._check("r") diff --git a/Misc/ACKS b/Misc/ACKS index 0aab4567d6ee9..404f70ec87fe2 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1957,5 +1957,6 @@ Gennadiy Zlobin Doug Zongker Peter ?strand Vlad Emelianov +Andrey Doroschenko (Entries should be added in rough alphabetical order by last names) diff --git a/Misc/NEWS.d/next/Documentation/2020-02-24-09-02-05.bpo-39693.QXw0Fm.rst b/Misc/NEWS.d/next/Documentation/2020-02-24-09-02-05.bpo-39693.QXw0Fm.rst new file mode 100644 index 0000000000000..86049c536815b --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2020-02-24-09-02-05.bpo-39693.QXw0Fm.rst @@ -0,0 +1 @@ +Fix tarfile's extractfile documentation From webhook-mailer at python.org Tue Oct 20 10:31:21 2020 From: webhook-mailer at python.org (akuchling) Date: Tue, 20 Oct 2020 14:31:21 -0000 Subject: [Python-checkins] bpo-39693: mention KeyError in tarfile extractfile documentation (GH-18639) Message-ID: https://github.com/python/cpython/commit/b249aeae89f55b9da5cdff082ba271e2b15b7825 commit: b249aeae89f55b9da5cdff082ba271e2b15b7825 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: akuchling date: 2020-10-20T10:31:03-04:00 summary: bpo-39693: mention KeyError in tarfile extractfile documentation (GH-18639) Co-authored-by: Andrey Darascheka (cherry picked from commit ec42789e6e14f6b6ac13569aeadc13798d7173a8) Closes bpo-39693. files: A Misc/NEWS.d/next/Documentation/2020-02-24-09-02-05.bpo-39693.QXw0Fm.rst M Doc/library/tarfile.rst M Lib/tarfile.py M Misc/ACKS diff --git a/Doc/library/tarfile.rst b/Doc/library/tarfile.rst index cca466b569794..7a114fdf5d54b 100644 --- a/Doc/library/tarfile.rst +++ b/Doc/library/tarfile.rst @@ -445,10 +445,11 @@ be finalized; only the internally used file object will be closed. See the .. method:: TarFile.extractfile(member) - Extract a member from the archive as a file object. *member* may be a filename - or a :class:`TarInfo` object. If *member* is a regular file or a link, an - :class:`io.BufferedReader` object is returned. Otherwise, :const:`None` is - returned. + Extract a member from the archive as a file object. *member* may be + a filename or a :class:`TarInfo` object. If *member* is a regular file or + a link, an :class:`io.BufferedReader` object is returned. For all other + existing members, :const:`None` is returned. If *member* does not appear + in the archive, :exc:`KeyError` is raised. .. versionchanged:: 3.3 Return an :class:`io.BufferedReader` object. diff --git a/Lib/tarfile.py b/Lib/tarfile.py index 6769066cabd6f..62a6d9d120ffa 100755 --- a/Lib/tarfile.py +++ b/Lib/tarfile.py @@ -2091,9 +2091,10 @@ def extract(self, member, path="", set_attrs=True, *, numeric_owner=False): def extractfile(self, member): """Extract a member from the archive as a file object. `member' may be - a filename or a TarInfo object. If `member' is a regular file or a - link, an io.BufferedReader object is returned. Otherwise, None is - returned. + a filename or a TarInfo object. If `member' is a regular file or + a link, an io.BufferedReader object is returned. For all other + existing members, None is returned. If `member' does not appear + in the archive, KeyError is raised. """ self._check("r") diff --git a/Misc/ACKS b/Misc/ACKS index 2aa9a9de93703..021df26426e36 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1941,5 +1941,6 @@ Gennadiy Zlobin Doug Zongker Peter ?strand Vlad Emelianov +Andrey Doroschenko (Entries should be added in rough alphabetical order by last names) diff --git a/Misc/NEWS.d/next/Documentation/2020-02-24-09-02-05.bpo-39693.QXw0Fm.rst b/Misc/NEWS.d/next/Documentation/2020-02-24-09-02-05.bpo-39693.QXw0Fm.rst new file mode 100644 index 0000000000000..86049c536815b --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2020-02-24-09-02-05.bpo-39693.QXw0Fm.rst @@ -0,0 +1 @@ +Fix tarfile's extractfile documentation From webhook-mailer at python.org Tue Oct 20 10:41:11 2020 From: webhook-mailer at python.org (akuchling) Date: Tue, 20 Oct 2020 14:41:11 -0000 Subject: [Python-checkins] bpo-41192: Clarify the sys module's description of the auditing feature (GH-22768) Message-ID: https://github.com/python/cpython/commit/0c37269be7065b9b15b7b3a4406084f9535a793a commit: 0c37269be7065b9b15b7b3a4406084f9535a793a branch: master author: Andrew Kuchling committer: akuchling date: 2020-10-20T10:41:02-04:00 summary: bpo-41192: Clarify the sys module's description of the auditing feature (GH-22768) Co-authored-by: ?ric Araujo files: M Doc/library/sys.rst diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index d201d7061f980..468a30d326891 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -31,16 +31,22 @@ always available. When an auditing event is raised through the :func:`sys.audit` function, each hook will be called in the order it was added with the event name and the tuple of arguments. Native hooks added by :c:func:`PySys_AddAuditHook` are - called first, followed by hooks added in the current interpreter. + called first, followed by hooks added in the current interpreter. Hooks + can then log the event, raise an exception to abort the operation, + or terminate the process entirely. .. audit-event:: sys.addaudithook "" sys.addaudithook - Raise an auditing event ``sys.addaudithook`` with no arguments. If any + Calling :func:`sys.addaudithook` will itself raise an auditing event + named ``sys.addaudithook`` with no arguments. If any existing hooks raise an exception derived from :class:`RuntimeError`, the new hook will not be added and the exception suppressed. As a result, callers cannot assume that their hook has been added unless they control all existing hooks. + See the :ref:`audit events table ` for all events raised by + CPython, and :pep:`578` for the original design discussion. + .. versionadded:: 3.8 .. versionchanged:: 3.8.1 @@ -81,14 +87,23 @@ always available. .. index:: single: auditing - Raise an auditing event with any active hooks. The event name is a string - identifying the event and its associated schema, which is the number and - types of arguments. The schema for a given event is considered public and - stable API and should not be modified between releases. - - This function will raise the first exception raised by any hook. In general, - these errors should not be handled and should terminate the process as - quickly as possible. + Raise an auditing event and trigger any active auditing hooks. + *event* is a string identifying the event, and *args* may contain + optional arguments with more information about the event. The + number and types of arguments for a given event are considered a + public and stable API and should not be modified between releases. + + For example, one auditing event is named ``os.chdir``. This event has + one argument called *path* that will contain the requested new + working directory. + + :func:`sys.audit` will call the existing auditing hooks, passing + the event name and arguments, and will re-raise the first exception + from any hook. In general, if an exception is raised, it should not + be handled and the process should be terminated as quickly as + possible. This allows hook implementations to decide how to respond + to particular events: they can merely log the event or abort the + operation by raising an exception. Hooks are added using the :func:`sys.addaudithook` or :c:func:`PySys_AddAuditHook` functions. From webhook-mailer at python.org Tue Oct 20 10:54:26 2020 From: webhook-mailer at python.org (zooba) Date: Tue, 20 Oct 2020 14:54:26 -0000 Subject: [Python-checkins] bpo-38439: Update the Windows Store package's icons for IDLE. Artwork by Andrew Clover (GH-22817) Message-ID: https://github.com/python/cpython/commit/6d883fbe14751b58d9ed2fd708322613d8931035 commit: 6d883fbe14751b58d9ed2fd708322613d8931035 branch: master author: Steve Dower committer: zooba date: 2020-10-20T15:54:13+01:00 summary: bpo-38439: Update the Windows Store package's icons for IDLE. Artwork by Andrew Clover (GH-22817) files: A Misc/NEWS.d/next/Windows/2020-10-20-13-19-42.bpo-38439.eMLi-t.rst A PC/icons/idlex150.png A PC/icons/idlex44.png M PC/layout/support/appxmanifest.py diff --git a/Misc/NEWS.d/next/Windows/2020-10-20-13-19-42.bpo-38439.eMLi-t.rst b/Misc/NEWS.d/next/Windows/2020-10-20-13-19-42.bpo-38439.eMLi-t.rst new file mode 100644 index 0000000000000..acbc80c10f5e2 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2020-10-20-13-19-42.bpo-38439.eMLi-t.rst @@ -0,0 +1 @@ +Updates the icons for IDLE in the Windows Store package. diff --git a/PC/icons/idlex150.png b/PC/icons/idlex150.png new file mode 100644 index 0000000000000..806cb0c8aa219 Binary files /dev/null and b/PC/icons/idlex150.png differ diff --git a/PC/icons/idlex44.png b/PC/icons/idlex44.png new file mode 100644 index 0000000000000..3ef66e6c68e6b Binary files /dev/null and b/PC/icons/idlex44.png differ diff --git a/PC/layout/support/appxmanifest.py b/PC/layout/support/appxmanifest.py index 9a7439d027126..747c97a00699d 100644 --- a/PC/layout/support/appxmanifest.py +++ b/PC/layout/support/appxmanifest.py @@ -67,8 +67,8 @@ IDLE_VE_DATA = dict( DisplayName="IDLE (Python {})".format(VER_DOT), Description="IDLE editor for Python {}".format(VER_DOT), - Square150x150Logo="_resources/pythonwx150.png", - Square44x44Logo="_resources/pythonwx44.png", + Square150x150Logo="_resources/idlex150.png", + Square44x44Logo="_resources/idlex44.png", BackgroundColor="transparent", ) @@ -498,6 +498,11 @@ def get_appx_layout(ns): src = icons / "pythonwx{}.png".format(px) yield f"_resources/pythonwx{px}.png", src yield f"_resources/pythonwx{px}$targetsize-{px}_altform-unplated.png", src + if ns.include_idle and ns.include_launchers: + for px in [44, 150]: + src = icons / "idlex{}.png".format(px) + yield f"_resources/idlex{px}.png", src + yield f"_resources/idlex{px}$targetsize-{px}_altform-unplated.png", src yield f"_resources/py.png", icons / "py.png" sccd = ns.source / SCCD_FILENAME if sccd.is_file(): From webhook-mailer at python.org Tue Oct 20 11:01:51 2020 From: webhook-mailer at python.org (miss-islington) Date: Tue, 20 Oct 2020 15:01:51 -0000 Subject: [Python-checkins] Doc: Add missing spaces after period for `posix_spawn` (GH-22730) Message-ID: https://github.com/python/cpython/commit/ccfc05723e293fce4a5102ccf9beccf4e5f56990 commit: ccfc05723e293fce4a5102ccf9beccf4e5f56990 branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-20T08:01:38-07:00 summary: Doc: Add missing spaces after period for `posix_spawn` (GH-22730) (cherry picked from commit 5b57fa69408c4856bf0ae02f2eaa3db1ef980ea6) Co-authored-by: Tomer Cohen files: M Doc/library/os.rst diff --git a/Doc/library/os.rst b/Doc/library/os.rst index c8e316a7403c1..1a6ff40bf09e6 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -3664,8 +3664,8 @@ written in Python, such as a mail server's external command delivery program. The positional-only arguments *path*, *args*, and *env* are similar to :func:`execve`. - The *path* parameter is the path to the executable file.The *path* should - contain a directory.Use :func:`posix_spawnp` to pass an executable file + The *path* parameter is the path to the executable file. The *path* should + contain a directory. Use :func:`posix_spawnp` to pass an executable file without directory. The *file_actions* argument may be a sequence of tuples describing actions From webhook-mailer at python.org Tue Oct 20 11:02:08 2020 From: webhook-mailer at python.org (miss-islington) Date: Tue, 20 Oct 2020 15:02:08 -0000 Subject: [Python-checkins] Doc: Add missing spaces after period for `posix_spawn` (GH-22730) Message-ID: https://github.com/python/cpython/commit/4aed781eb44cedcb9e7c2dbea993f38625961d1e commit: 4aed781eb44cedcb9e7c2dbea993f38625961d1e branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-20T08:01:56-07:00 summary: Doc: Add missing spaces after period for `posix_spawn` (GH-22730) (cherry picked from commit 5b57fa69408c4856bf0ae02f2eaa3db1ef980ea6) Co-authored-by: Tomer Cohen files: M Doc/library/os.rst diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 64bc73589f625..6e287abb6f78a 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -3695,8 +3695,8 @@ written in Python, such as a mail server's external command delivery program. The positional-only arguments *path*, *args*, and *env* are similar to :func:`execve`. - The *path* parameter is the path to the executable file.The *path* should - contain a directory.Use :func:`posix_spawnp` to pass an executable file + The *path* parameter is the path to the executable file. The *path* should + contain a directory. Use :func:`posix_spawnp` to pass an executable file without directory. The *file_actions* argument may be a sequence of tuples describing actions From webhook-mailer at python.org Tue Oct 20 11:15:16 2020 From: webhook-mailer at python.org (akuchling) Date: Tue, 20 Oct 2020 15:15:16 -0000 Subject: [Python-checkins] bpo-41192: Clarify the sys module's description of the auditing feature (GH-22820) Message-ID: https://github.com/python/cpython/commit/a797fd74fe11ef5968b8593b0d39b6aa6ba5aef1 commit: a797fd74fe11ef5968b8593b0d39b6aa6ba5aef1 branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: akuchling date: 2020-10-20T11:15:02-04:00 summary: bpo-41192: Clarify the sys module's description of the auditing feature (GH-22820) Co-authored-by: ?ric Araujo (cherry picked from commit 0c37269be7065b9b15b7b3a4406084f9535a793a) Co-authored-by: Andrew Kuchling Co-authored-by: Andrew Kuchling files: M Doc/library/sys.rst diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index d3473de1292ea..def2d7ea63dcf 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -31,16 +31,22 @@ always available. When an auditing event is raised through the :func:`sys.audit` function, each hook will be called in the order it was added with the event name and the tuple of arguments. Native hooks added by :c:func:`PySys_AddAuditHook` are - called first, followed by hooks added in the current interpreter. + called first, followed by hooks added in the current interpreter. Hooks + can then log the event, raise an exception to abort the operation, + or terminate the process entirely. .. audit-event:: sys.addaudithook "" sys.addaudithook - Raise an auditing event ``sys.addaudithook`` with no arguments. If any + Calling :func:`sys.addaudithook` will itself raise an auditing event + named ``sys.addaudithook`` with no arguments. If any existing hooks raise an exception derived from :class:`RuntimeError`, the new hook will not be added and the exception suppressed. As a result, callers cannot assume that their hook has been added unless they control all existing hooks. + See the :ref:`audit events table ` for all events raised by + CPython, and :pep:`578` for the original design discussion. + .. versionadded:: 3.8 .. versionchanged:: 3.8.1 @@ -79,14 +85,23 @@ always available. .. index:: single: auditing - Raise an auditing event with any active hooks. The event name is a string - identifying the event and its associated schema, which is the number and - types of arguments. The schema for a given event is considered public and - stable API and should not be modified between releases. - - This function will raise the first exception raised by any hook. In general, - these errors should not be handled and should terminate the process as - quickly as possible. + Raise an auditing event and trigger any active auditing hooks. + *event* is a string identifying the event, and *args* may contain + optional arguments with more information about the event. The + number and types of arguments for a given event are considered a + public and stable API and should not be modified between releases. + + For example, one auditing event is named ``os.chdir``. This event has + one argument called *path* that will contain the requested new + working directory. + + :func:`sys.audit` will call the existing auditing hooks, passing + the event name and arguments, and will re-raise the first exception + from any hook. In general, if an exception is raised, it should not + be handled and the process should be terminated as quickly as + possible. This allows hook implementations to decide how to respond + to particular events: they can merely log the event or abort the + operation by raising an exception. Hooks are added using the :func:`sys.addaudithook` or :c:func:`PySys_AddAuditHook` functions. From webhook-mailer at python.org Tue Oct 20 11:37:01 2020 From: webhook-mailer at python.org (akuchling) Date: Tue, 20 Oct 2020 15:37:01 -0000 Subject: [Python-checkins] [3.9] bpo-41192: Clarify the sys module's description of the auditing feature (GH-22821) Message-ID: https://github.com/python/cpython/commit/b948d13ec076e757b7f6904aa67df2f36e4671b7 commit: b948d13ec076e757b7f6904aa67df2f36e4671b7 branch: 3.9 author: Andrew Kuchling committer: akuchling date: 2020-10-20T11:36:52-04:00 summary: [3.9] bpo-41192: Clarify the sys module's description of the auditing feature (GH-22821) Co-authored-by: ?ric Araujo (cherry picked from commit 0c37269be7065b9b15b7b3a4406084f9535a793a) files: M Doc/library/sys.rst diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index 880f252f84aa0..3df529f8100eb 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -31,16 +31,22 @@ always available. When an auditing event is raised through the :func:`sys.audit` function, each hook will be called in the order it was added with the event name and the tuple of arguments. Native hooks added by :c:func:`PySys_AddAuditHook` are - called first, followed by hooks added in the current interpreter. + called first, followed by hooks added in the current interpreter. Hooks + can then log the event, raise an exception to abort the operation, + or terminate the process entirely. .. audit-event:: sys.addaudithook "" sys.addaudithook - Raise an auditing event ``sys.addaudithook`` with no arguments. If any + Calling :func:`sys.addaudithook` will itself raise an auditing event + named ``sys.addaudithook`` with no arguments. If any existing hooks raise an exception derived from :class:`RuntimeError`, the new hook will not be added and the exception suppressed. As a result, callers cannot assume that their hook has been added unless they control all existing hooks. + See the :ref:`audit events table ` for all events raised by + CPython, and :pep:`578` for the original design discussion. + .. versionadded:: 3.8 .. versionchanged:: 3.8.1 @@ -79,14 +85,23 @@ always available. .. index:: single: auditing - Raise an auditing event with any active hooks. The event name is a string - identifying the event and its associated schema, which is the number and - types of arguments. The schema for a given event is considered public and - stable API and should not be modified between releases. - - This function will raise the first exception raised by any hook. In general, - these errors should not be handled and should terminate the process as - quickly as possible. + Raise an auditing event and trigger any active auditing hooks. + *event* is a string identifying the event, and *args* may contain + optional arguments with more information about the event. The + number and types of arguments for a given event are considered a + public and stable API and should not be modified between releases. + + For example, one auditing event is named ``os.chdir``. This event has + one argument called *path* that will contain the requested new + working directory. + + :func:`sys.audit` will call the existing auditing hooks, passing + the event name and arguments, and will re-raise the first exception + from any hook. In general, if an exception is raised, it should not + be handled and the process should be terminated as quickly as + possible. This allows hook implementations to decide how to respond + to particular events: they can merely log the event or abort the + operation by raising an exception. Hooks are added using the :func:`sys.addaudithook` or :c:func:`PySys_AddAuditHook` functions. From webhook-mailer at python.org Tue Oct 20 12:45:46 2020 From: webhook-mailer at python.org (serhiy-storchaka) Date: Tue, 20 Oct 2020 16:45:46 -0000 Subject: [Python-checkins] bpo-38144: Re-add accidentally removed audition for glob. (GH-22805) Message-ID: https://github.com/python/cpython/commit/1d3469988e2c1f53ca84ffdc979d548c04ba3906 commit: 1d3469988e2c1f53ca84ffdc979d548c04ba3906 branch: master author: Serhiy Storchaka committer: serhiy-storchaka date: 2020-10-20T19:45:38+03:00 summary: bpo-38144: Re-add accidentally removed audition for glob. (GH-22805) files: M Lib/glob.py diff --git a/Lib/glob.py b/Lib/glob.py index 3c449a90dffef..a491363f3f939 100644 --- a/Lib/glob.py +++ b/Lib/glob.py @@ -33,6 +33,7 @@ def iglob(pathname, *, root_dir=None, dir_fd=None, recursive=False): If recursive is true, the pattern '**' will match any files and zero or more directories and subdirectories. """ + sys.audit("glob.glob", pathname, recursive) if root_dir is not None: root_dir = os.fspath(root_dir) else: From webhook-mailer at python.org Tue Oct 20 13:17:26 2020 From: webhook-mailer at python.org (miss-islington) Date: Tue, 20 Oct 2020 17:17:26 -0000 Subject: [Python-checkins] bpo-38439: Update the Windows Store package's icons for IDLE. Artwork by Andrew Clover (GH-22817) Message-ID: https://github.com/python/cpython/commit/333782a3f40baf8bb916da4b5fc04b3d2a749e0c commit: 333782a3f40baf8bb916da4b5fc04b3d2a749e0c branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-20T10:17:17-07:00 summary: bpo-38439: Update the Windows Store package's icons for IDLE. Artwork by Andrew Clover (GH-22817) (cherry picked from commit 6d883fbe14751b58d9ed2fd708322613d8931035) Co-authored-by: Steve Dower files: A Misc/NEWS.d/next/Windows/2020-10-20-13-19-42.bpo-38439.eMLi-t.rst A PC/icons/idlex150.png A PC/icons/idlex44.png M PC/layout/support/appxmanifest.py diff --git a/Misc/NEWS.d/next/Windows/2020-10-20-13-19-42.bpo-38439.eMLi-t.rst b/Misc/NEWS.d/next/Windows/2020-10-20-13-19-42.bpo-38439.eMLi-t.rst new file mode 100644 index 0000000000000..acbc80c10f5e2 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2020-10-20-13-19-42.bpo-38439.eMLi-t.rst @@ -0,0 +1 @@ +Updates the icons for IDLE in the Windows Store package. diff --git a/PC/icons/idlex150.png b/PC/icons/idlex150.png new file mode 100644 index 0000000000000..806cb0c8aa219 Binary files /dev/null and b/PC/icons/idlex150.png differ diff --git a/PC/icons/idlex44.png b/PC/icons/idlex44.png new file mode 100644 index 0000000000000..3ef66e6c68e6b Binary files /dev/null and b/PC/icons/idlex44.png differ diff --git a/PC/layout/support/appxmanifest.py b/PC/layout/support/appxmanifest.py index 9a7439d027126..747c97a00699d 100644 --- a/PC/layout/support/appxmanifest.py +++ b/PC/layout/support/appxmanifest.py @@ -67,8 +67,8 @@ IDLE_VE_DATA = dict( DisplayName="IDLE (Python {})".format(VER_DOT), Description="IDLE editor for Python {}".format(VER_DOT), - Square150x150Logo="_resources/pythonwx150.png", - Square44x44Logo="_resources/pythonwx44.png", + Square150x150Logo="_resources/idlex150.png", + Square44x44Logo="_resources/idlex44.png", BackgroundColor="transparent", ) @@ -498,6 +498,11 @@ def get_appx_layout(ns): src = icons / "pythonwx{}.png".format(px) yield f"_resources/pythonwx{px}.png", src yield f"_resources/pythonwx{px}$targetsize-{px}_altform-unplated.png", src + if ns.include_idle and ns.include_launchers: + for px in [44, 150]: + src = icons / "idlex{}.png".format(px) + yield f"_resources/idlex{px}.png", src + yield f"_resources/idlex{px}$targetsize-{px}_altform-unplated.png", src yield f"_resources/py.png", icons / "py.png" sccd = ns.source / SCCD_FILENAME if sccd.is_file(): From webhook-mailer at python.org Tue Oct 20 13:17:35 2020 From: webhook-mailer at python.org (miss-islington) Date: Tue, 20 Oct 2020 17:17:35 -0000 Subject: [Python-checkins] bpo-38439: Add 256px IDLE icon to the .ico, drop gifs from it (GH-19648) Message-ID: https://github.com/python/cpython/commit/d84e411005401b3c22d7f5836fa366e7aeeb1f21 commit: d84e411005401b3c22d7f5836fa366e7aeeb1f21 branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-20T10:17:31-07:00 summary: bpo-38439: Add 256px IDLE icon to the .ico, drop gifs from it (GH-19648) (cherry picked from commit faddc7449d07e883b8ad8bb95dd68ce6dbdc06e8) Co-authored-by: Miro Hron?ok files: A Misc/NEWS.d/next/IDLE/2020-04-22-09-37-40.bpo-38439.ieXL-c.rst M Lib/idlelib/Icons/README.txt M Lib/idlelib/Icons/idle.ico diff --git a/Lib/idlelib/Icons/README.txt b/Lib/idlelib/Icons/README.txt index 8b471629ecb3e..d91c4d5d8d8cf 100644 --- a/Lib/idlelib/Icons/README.txt +++ b/Lib/idlelib/Icons/README.txt @@ -7,3 +7,7 @@ https://www.doxdesk.com/software/py/pyicons.html Various different formats and sizes are available at this GitHub Pull Request: https://github.com/python/cpython/pull/17473 + +The idle.ico file was created with ImageMagick: + + $ convert idle_16.png idle_32.png idle_48.png idle_256.png idle.ico diff --git a/Lib/idlelib/Icons/idle.ico b/Lib/idlelib/Icons/idle.ico index 3357aef14888c..2aa9a8300d9e2 100644 Binary files a/Lib/idlelib/Icons/idle.ico and b/Lib/idlelib/Icons/idle.ico differ diff --git a/Misc/NEWS.d/next/IDLE/2020-04-22-09-37-40.bpo-38439.ieXL-c.rst b/Misc/NEWS.d/next/IDLE/2020-04-22-09-37-40.bpo-38439.ieXL-c.rst new file mode 100644 index 0000000000000..d8d59015f20e3 --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2020-04-22-09-37-40.bpo-38439.ieXL-c.rst @@ -0,0 +1,2 @@ +Add a 256?256 pixel IDLE icon to the Windows .ico file. Created by Andrew +Clover. Remove the low-color gif variations from the .ico file. From webhook-mailer at python.org Tue Oct 20 13:20:13 2020 From: webhook-mailer at python.org (miss-islington) Date: Tue, 20 Oct 2020 17:20:13 -0000 Subject: [Python-checkins] bpo-38439: Update the Windows Store package's icons for IDLE. Artwork by Andrew Clover (GH-22817) Message-ID: https://github.com/python/cpython/commit/d12afba507990b6d75f701b398d4f342576be5d7 commit: d12afba507990b6d75f701b398d4f342576be5d7 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-20T10:20:05-07:00 summary: bpo-38439: Update the Windows Store package's icons for IDLE. Artwork by Andrew Clover (GH-22817) (cherry picked from commit 6d883fbe14751b58d9ed2fd708322613d8931035) Co-authored-by: Steve Dower files: A Misc/NEWS.d/next/Windows/2020-10-20-13-19-42.bpo-38439.eMLi-t.rst A PC/icons/idlex150.png A PC/icons/idlex44.png M PC/layout/support/appxmanifest.py diff --git a/Misc/NEWS.d/next/Windows/2020-10-20-13-19-42.bpo-38439.eMLi-t.rst b/Misc/NEWS.d/next/Windows/2020-10-20-13-19-42.bpo-38439.eMLi-t.rst new file mode 100644 index 0000000000000..acbc80c10f5e2 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2020-10-20-13-19-42.bpo-38439.eMLi-t.rst @@ -0,0 +1 @@ +Updates the icons for IDLE in the Windows Store package. diff --git a/PC/icons/idlex150.png b/PC/icons/idlex150.png new file mode 100644 index 0000000000000..806cb0c8aa219 Binary files /dev/null and b/PC/icons/idlex150.png differ diff --git a/PC/icons/idlex44.png b/PC/icons/idlex44.png new file mode 100644 index 0000000000000..3ef66e6c68e6b Binary files /dev/null and b/PC/icons/idlex44.png differ diff --git a/PC/layout/support/appxmanifest.py b/PC/layout/support/appxmanifest.py index 9a7439d027126..747c97a00699d 100644 --- a/PC/layout/support/appxmanifest.py +++ b/PC/layout/support/appxmanifest.py @@ -67,8 +67,8 @@ IDLE_VE_DATA = dict( DisplayName="IDLE (Python {})".format(VER_DOT), Description="IDLE editor for Python {}".format(VER_DOT), - Square150x150Logo="_resources/pythonwx150.png", - Square44x44Logo="_resources/pythonwx44.png", + Square150x150Logo="_resources/idlex150.png", + Square44x44Logo="_resources/idlex44.png", BackgroundColor="transparent", ) @@ -498,6 +498,11 @@ def get_appx_layout(ns): src = icons / "pythonwx{}.png".format(px) yield f"_resources/pythonwx{px}.png", src yield f"_resources/pythonwx{px}$targetsize-{px}_altform-unplated.png", src + if ns.include_idle and ns.include_launchers: + for px in [44, 150]: + src = icons / "idlex{}.png".format(px) + yield f"_resources/idlex{px}.png", src + yield f"_resources/idlex{px}$targetsize-{px}_altform-unplated.png", src yield f"_resources/py.png", icons / "py.png" sccd = ns.source / SCCD_FILENAME if sccd.is_file(): From webhook-mailer at python.org Tue Oct 20 15:23:23 2020 From: webhook-mailer at python.org (akuchling) Date: Tue, 20 Oct 2020 19:23:23 -0000 Subject: [Python-checkins] bpo-41192: Add documentation of undocumented audit events (GH-21308) Message-ID: https://github.com/python/cpython/commit/3f7e990fd49dbabc1dde41ce58c1bdda9f56ead7 commit: 3f7e990fd49dbabc1dde41ce58c1bdda9f56ead7 branch: master author: Saiyang Gou committer: akuchling date: 2020-10-20T15:23:15-04:00 summary: bpo-41192: Add documentation of undocumented audit events (GH-21308) files: M Doc/c-api/file.rst M Doc/library/audit_events.rst M Doc/library/functions.rst M Doc/library/types.rst M Doc/reference/datamodel.rst diff --git a/Doc/c-api/file.rst b/Doc/c-api/file.rst index ea027ee975c65..ed3735aa83608 100644 --- a/Doc/c-api/file.rst +++ b/Doc/c-api/file.rst @@ -82,6 +82,8 @@ the :mod:`io` APIs instead. This function is safe to call before :c:func:`Py_Initialize`. + .. audit-event:: setopencodehook "" c.PyFile_SetOpenCodeHook + .. versionadded:: 3.8 diff --git a/Doc/library/audit_events.rst b/Doc/library/audit_events.rst index 3c68a1515b3ef..367d56e4e3735 100644 --- a/Doc/library/audit_events.rst +++ b/Doc/library/audit_events.rst @@ -19,3 +19,29 @@ information on handling these events. specific documentation for actual events raised. .. audit-event-table:: + +The following events are raised internally and do not correspond to any +public API of CPython: + ++--------------------------+-------------------------------------------+ +| Audit event | Arguments | ++==========================+===========================================+ +| _winapi.CreateFile | ``file_name``, ``desired_access``, | +| | ``share_mode``, ``creation_disposition``, | +| | ``flags_and_attributes`` | ++--------------------------+-------------------------------------------+ +| _winapi.CreateJunction | ``src_path``, ``dst_path`` | ++--------------------------+-------------------------------------------+ +| _winapi.CreateNamedPipe | ``name``, ``open_mode``, ``pipe_mode`` | ++--------------------------+-------------------------------------------+ +| _winapi.CreatePipe | | ++--------------------------+-------------------------------------------+ +| _winapi.CreateProcess | ``application_name``, ``command_line``, | +| | ``current_directory`` | ++--------------------------+-------------------------------------------+ +| _winapi.OpenProcess | ``process_id``, ``desired_access`` | ++--------------------------+-------------------------------------------+ +| _winapi.TerminateProcess | ``handle``, ``exit_code`` | ++--------------------------+-------------------------------------------+ +| ctypes.PyObj_FromPtr | ``obj`` | ++--------------------------+-------------------------------------------+ diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index a052e724c5ebe..b33aa81c643dc 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -783,6 +783,8 @@ are always available. They are listed here in alphabetical order. .. impl-detail:: This is the address of the object in memory. + .. audit-event:: builtins.id id id + .. function:: input([prompt]) diff --git a/Doc/library/types.rst b/Doc/library/types.rst index e4a8dec5cb95a..a5cf9ab1344a3 100644 --- a/Doc/library/types.rst +++ b/Doc/library/types.rst @@ -116,6 +116,11 @@ Standard names are defined for the following types: The type of user-defined functions and functions created by :keyword:`lambda` expressions. + .. audit-event:: function.__new__ code types.FunctionType + + The audit event only occurs for direct instantiation of function objects, + and is not raised for normal compilation. + .. data:: GeneratorType @@ -145,10 +150,11 @@ Standard names are defined for the following types: The type for code objects such as returned by :func:`compile`. - .. audit-event:: code.__new__ code,filename,name,argcount,posonlyargcount,kwonlyargcount,nlocals,stacksize,flags CodeType + .. audit-event:: code.__new__ code,filename,name,argcount,posonlyargcount,kwonlyargcount,nlocals,stacksize,flags types.CodeType Note that the audited arguments may not match the names or positions - required by the initializer. + required by the initializer. The audit event only occurs for direct + instantiation of code objects, and is not raised for normal compilation. .. method:: CodeType.replace(**kwargs) diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 4396f1b9b7299..29e64cba2ca3a 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -1540,6 +1540,12 @@ access (use of, assignment to, or deletion of ``x.name``) for class instances. result of implicit invocation via language syntax or built-in functions. See :ref:`special-lookup`. + .. audit-event:: object.__getattr__ obj,name object.__getattribute__ + + For certain sensitive attribute accesses, raises an + :ref:`auditing event ` ``object.__getattr__`` with arguments + ``obj`` and ``name``. + .. method:: object.__setattr__(self, name, value) @@ -1551,12 +1557,24 @@ access (use of, assignment to, or deletion of ``x.name``) for class instances. call the base class method with the same name, for example, ``object.__setattr__(self, name, value)``. + .. audit-event:: object.__setattr__ obj,name,value object.__setattr__ + + For certain sensitive attribute assignments, raises an + :ref:`auditing event ` ``object.__setattr__`` with arguments + ``obj``, ``name``, ``value``. + .. method:: object.__delattr__(self, name) Like :meth:`__setattr__` but for attribute deletion instead of assignment. This should only be implemented if ``del obj.name`` is meaningful for the object. + .. audit-event:: object.__delattr__ obj,name object.__delattr__ + + For certain sensitive attribute deletions, raises an + :ref:`auditing event ` ``object.__delattr__`` with arguments + ``obj`` and ``name``. + .. method:: object.__dir__(self) From webhook-mailer at python.org Tue Oct 20 16:01:04 2020 From: webhook-mailer at python.org (miss-islington) Date: Tue, 20 Oct 2020 20:01:04 -0000 Subject: [Python-checkins] Minor tweaks to typing union objects doc (GH-22741) Message-ID: https://github.com/python/cpython/commit/3393624b6da4b3ebe6448d1e8050ee0a8d208936 commit: 3393624b6da4b3ebe6448d1e8050ee0a8d208936 branch: master author: Andre Delfino committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-20T13:00:56-07:00 summary: Minor tweaks to typing union objects doc (GH-22741) Automerge-Triggered-By: @merwok files: M Doc/library/stdtypes.rst diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 5c6acc66bb4cc..2fc7a6118a322 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -4798,7 +4798,7 @@ type hinting syntax compared to :data:`typing.Union`. .. describe:: isinstance(obj, union_object) - Calls to :func:`isinstance` are also supported with a Union object:: + Calls to :func:`isinstance` are also supported with a union object:: >>> isinstance("", int | str) True @@ -4819,7 +4819,7 @@ type hinting syntax compared to :data:`typing.Union`. .. describe:: issubclass(obj, union_object) - Calls to :func:`issubclass` are also supported with a Union Object.:: + Calls to :func:`issubclass` are also supported with a union object:: >>> issubclass(bool, int | str) True @@ -4837,7 +4837,7 @@ type hinting syntax compared to :data:`typing.Union`. File "", line 1, in TypeError: issubclass() argument 2 cannot contain a parameterized generic -The type for the Union object is :data:`types.Union`. An object cannot be +The type of a union object is :data:`types.Union`. An object cannot be instantiated from the type:: >>> import types From webhook-mailer at python.org Tue Oct 20 16:02:28 2020 From: webhook-mailer at python.org (pfmoore) Date: Tue, 20 Oct 2020 20:02:28 -0000 Subject: [Python-checkins] bpo-42041: Clarify how subprocess searches for the executable (GH-22715) Message-ID: https://github.com/python/cpython/commit/5ab27cc518f614a0b954ff3eb125290f264242d5 commit: 5ab27cc518f614a0b954ff3eb125290f264242d5 branch: master author: Paul Moore committer: pfmoore date: 2020-10-20T21:02:24+01:00 summary: bpo-42041: Clarify how subprocess searches for the executable (GH-22715) Clarify in the subprocess documentation how searching for the executable to run works, noting that ``sys.executable`` is the recommended way to find the current interpreter. files: M Doc/library/subprocess.rst diff --git a/Doc/library/subprocess.rst b/Doc/library/subprocess.rst index 7993b103f473e..85d0f46624cea 100644 --- a/Doc/library/subprocess.rst +++ b/Doc/library/subprocess.rst @@ -344,7 +344,7 @@ functions. encoding=None, errors=None, text=None, pipesize=-1) Execute a child program in a new process. On POSIX, the class uses - :meth:`os.execvp`-like behavior to execute the child program. On Windows, + :meth:`os.execvpe`-like behavior to execute the child program. On Windows, the class uses the Windows ``CreateProcess()`` function. The arguments to :class:`Popen` are as follows. @@ -356,6 +356,25 @@ functions. arguments for additional differences from the default behavior. Unless otherwise stated, it is recommended to pass *args* as a sequence. + .. warning:: + + For maximum reliability, use a fully-qualified path for the executable. + To search for an unqualified name on :envvar:`PATH`, use + :meth:`shutil.which`. On all platforms, passing :data:`sys.executable` + is the recommended way to launch the current Python interpreter again, + and use the ``-m`` command-line format to launch an installed module. + + Resolving the path of *executable* (or the first item of *args*) is + platform dependent. For POSIX, see :meth:`os.execvpe`, and note that + when resolving or searching for the executable path, *cwd* overrides the + current working directory and *env* can override the ``PATH`` + environment variable. For Windows, see the documentation of the + ``lpApplicationName`` and ``lpCommandLine`` parameters of WinAPI + ``CreateProcess``, and note that when resolving or searching for the + executable path with ``shell=False``, *cwd* does not override the + current working directory and *env* cannot override the ``PATH`` + environment variable. Using a full path avoids all of these variations. + An example of passing some arguments to an external program as a sequence is:: @@ -524,7 +543,7 @@ functions. If *cwd* is not ``None``, the function changes the working directory to *cwd* before executing the child. *cwd* can be a string, bytes or - :term:`path-like ` object. In particular, the function + :term:`path-like ` object. In POSIX, the function looks for *executable* (or for the first item in *args*) relative to *cwd* if the executable path is a relative path. From webhook-mailer at python.org Tue Oct 20 16:05:17 2020 From: webhook-mailer at python.org (akuchling) Date: Tue, 20 Oct 2020 20:05:17 -0000 Subject: [Python-checkins] bpo-41192: Add documentation of undocumented audit events (GH-22831) Message-ID: https://github.com/python/cpython/commit/46130f85381f697dc7f609b2659cc046d2370417 commit: 46130f85381f697dc7f609b2659cc046d2370417 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: akuchling date: 2020-10-20T16:05:13-04:00 summary: bpo-41192: Add documentation of undocumented audit events (GH-22831) (cherry picked from commit 3f7e990fd49dbabc1dde41ce58c1bdda9f56ead7) files: M Doc/c-api/file.rst M Doc/library/audit_events.rst M Doc/library/functions.rst M Doc/library/types.rst M Doc/reference/datamodel.rst diff --git a/Doc/c-api/file.rst b/Doc/c-api/file.rst index ea027ee975c65..ed3735aa83608 100644 --- a/Doc/c-api/file.rst +++ b/Doc/c-api/file.rst @@ -82,6 +82,8 @@ the :mod:`io` APIs instead. This function is safe to call before :c:func:`Py_Initialize`. + .. audit-event:: setopencodehook "" c.PyFile_SetOpenCodeHook + .. versionadded:: 3.8 diff --git a/Doc/library/audit_events.rst b/Doc/library/audit_events.rst index 3c68a1515b3ef..367d56e4e3735 100644 --- a/Doc/library/audit_events.rst +++ b/Doc/library/audit_events.rst @@ -19,3 +19,29 @@ information on handling these events. specific documentation for actual events raised. .. audit-event-table:: + +The following events are raised internally and do not correspond to any +public API of CPython: + ++--------------------------+-------------------------------------------+ +| Audit event | Arguments | ++==========================+===========================================+ +| _winapi.CreateFile | ``file_name``, ``desired_access``, | +| | ``share_mode``, ``creation_disposition``, | +| | ``flags_and_attributes`` | ++--------------------------+-------------------------------------------+ +| _winapi.CreateJunction | ``src_path``, ``dst_path`` | ++--------------------------+-------------------------------------------+ +| _winapi.CreateNamedPipe | ``name``, ``open_mode``, ``pipe_mode`` | ++--------------------------+-------------------------------------------+ +| _winapi.CreatePipe | | ++--------------------------+-------------------------------------------+ +| _winapi.CreateProcess | ``application_name``, ``command_line``, | +| | ``current_directory`` | ++--------------------------+-------------------------------------------+ +| _winapi.OpenProcess | ``process_id``, ``desired_access`` | ++--------------------------+-------------------------------------------+ +| _winapi.TerminateProcess | ``handle``, ``exit_code`` | ++--------------------------+-------------------------------------------+ +| ctypes.PyObj_FromPtr | ``obj`` | ++--------------------------+-------------------------------------------+ diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index 101d118291d7f..ca6dabfcdddaf 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -766,6 +766,8 @@ are always available. They are listed here in alphabetical order. .. impl-detail:: This is the address of the object in memory. + .. audit-event:: builtins.id id id + .. function:: input([prompt]) diff --git a/Doc/library/types.rst b/Doc/library/types.rst index 79acdf4499afd..a88e882305ff3 100644 --- a/Doc/library/types.rst +++ b/Doc/library/types.rst @@ -109,6 +109,11 @@ Standard names are defined for the following types: The type of user-defined functions and functions created by :keyword:`lambda` expressions. + .. audit-event:: function.__new__ code types.FunctionType + + The audit event only occurs for direct instantiation of function objects, + and is not raised for normal compilation. + .. data:: GeneratorType @@ -138,10 +143,11 @@ Standard names are defined for the following types: The type for code objects such as returned by :func:`compile`. - .. audit-event:: code.__new__ code,filename,name,argcount,posonlyargcount,kwonlyargcount,nlocals,stacksize,flags CodeType + .. audit-event:: code.__new__ code,filename,name,argcount,posonlyargcount,kwonlyargcount,nlocals,stacksize,flags types.CodeType Note that the audited arguments may not match the names or positions - required by the initializer. + required by the initializer. The audit event only occurs for direct + instantiation of code objects, and is not raised for normal compilation. .. method:: CodeType.replace(**kwargs) diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 354c6ce53f76f..aaa161aad2773 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -1540,6 +1540,12 @@ access (use of, assignment to, or deletion of ``x.name``) for class instances. result of implicit invocation via language syntax or built-in functions. See :ref:`special-lookup`. + .. audit-event:: object.__getattr__ obj,name object.__getattribute__ + + For certain sensitive attribute accesses, raises an + :ref:`auditing event ` ``object.__getattr__`` with arguments + ``obj`` and ``name``. + .. method:: object.__setattr__(self, name, value) @@ -1551,12 +1557,24 @@ access (use of, assignment to, or deletion of ``x.name``) for class instances. call the base class method with the same name, for example, ``object.__setattr__(self, name, value)``. + .. audit-event:: object.__setattr__ obj,name,value object.__setattr__ + + For certain sensitive attribute assignments, raises an + :ref:`auditing event ` ``object.__setattr__`` with arguments + ``obj``, ``name``, ``value``. + .. method:: object.__delattr__(self, name) Like :meth:`__setattr__` but for attribute deletion instead of assignment. This should only be implemented if ``del obj.name`` is meaningful for the object. + .. audit-event:: object.__delattr__ obj,name object.__delattr__ + + For certain sensitive attribute deletions, raises an + :ref:`auditing event ` ``object.__delattr__`` with arguments + ``obj`` and ``name``. + .. method:: object.__dir__(self) From webhook-mailer at python.org Tue Oct 20 16:06:00 2020 From: webhook-mailer at python.org (akuchling) Date: Tue, 20 Oct 2020 20:06:00 -0000 Subject: [Python-checkins] bpo-41192: Add documentation of undocumented audit events (GH-22832) Message-ID: https://github.com/python/cpython/commit/058affc09f0f105444ec62c880907a512b785b72 commit: 058affc09f0f105444ec62c880907a512b785b72 branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: akuchling date: 2020-10-20T16:05:55-04:00 summary: bpo-41192: Add documentation of undocumented audit events (GH-22832) (cherry picked from commit 3f7e990fd49dbabc1dde41ce58c1bdda9f56ead7) files: M Doc/c-api/file.rst M Doc/library/audit_events.rst M Doc/library/functions.rst M Doc/library/types.rst M Doc/reference/datamodel.rst diff --git a/Doc/c-api/file.rst b/Doc/c-api/file.rst index ea027ee975c65..ed3735aa83608 100644 --- a/Doc/c-api/file.rst +++ b/Doc/c-api/file.rst @@ -82,6 +82,8 @@ the :mod:`io` APIs instead. This function is safe to call before :c:func:`Py_Initialize`. + .. audit-event:: setopencodehook "" c.PyFile_SetOpenCodeHook + .. versionadded:: 3.8 diff --git a/Doc/library/audit_events.rst b/Doc/library/audit_events.rst index 3c68a1515b3ef..367d56e4e3735 100644 --- a/Doc/library/audit_events.rst +++ b/Doc/library/audit_events.rst @@ -19,3 +19,29 @@ information on handling these events. specific documentation for actual events raised. .. audit-event-table:: + +The following events are raised internally and do not correspond to any +public API of CPython: + ++--------------------------+-------------------------------------------+ +| Audit event | Arguments | ++==========================+===========================================+ +| _winapi.CreateFile | ``file_name``, ``desired_access``, | +| | ``share_mode``, ``creation_disposition``, | +| | ``flags_and_attributes`` | ++--------------------------+-------------------------------------------+ +| _winapi.CreateJunction | ``src_path``, ``dst_path`` | ++--------------------------+-------------------------------------------+ +| _winapi.CreateNamedPipe | ``name``, ``open_mode``, ``pipe_mode`` | ++--------------------------+-------------------------------------------+ +| _winapi.CreatePipe | | ++--------------------------+-------------------------------------------+ +| _winapi.CreateProcess | ``application_name``, ``command_line``, | +| | ``current_directory`` | ++--------------------------+-------------------------------------------+ +| _winapi.OpenProcess | ``process_id``, ``desired_access`` | ++--------------------------+-------------------------------------------+ +| _winapi.TerminateProcess | ``handle``, ``exit_code`` | ++--------------------------+-------------------------------------------+ +| ctypes.PyObj_FromPtr | ``obj`` | ++--------------------------+-------------------------------------------+ diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index 6003898d5f6b8..76a0ba4b0c766 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -767,6 +767,8 @@ are always available. They are listed here in alphabetical order. .. impl-detail:: This is the address of the object in memory. + .. audit-event:: builtins.id id id + .. function:: input([prompt]) diff --git a/Doc/library/types.rst b/Doc/library/types.rst index 3529c2b0edb89..71d7a126ac320 100644 --- a/Doc/library/types.rst +++ b/Doc/library/types.rst @@ -109,6 +109,11 @@ Standard names are defined for the following types: The type of user-defined functions and functions created by :keyword:`lambda` expressions. + .. audit-event:: function.__new__ code types.FunctionType + + The audit event only occurs for direct instantiation of function objects, + and is not raised for normal compilation. + .. data:: GeneratorType @@ -138,10 +143,11 @@ Standard names are defined for the following types: The type for code objects such as returned by :func:`compile`. - .. audit-event:: code.__new__ code,filename,name,argcount,posonlyargcount,kwonlyargcount,nlocals,stacksize,flags CodeType + .. audit-event:: code.__new__ code,filename,name,argcount,posonlyargcount,kwonlyargcount,nlocals,stacksize,flags types.CodeType Note that the audited arguments may not match the names or positions - required by the initializer. + required by the initializer. The audit event only occurs for direct + instantiation of code objects, and is not raised for normal compilation. .. method:: CodeType.replace(**kwargs) diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 924f6d9e2d7e0..c5d8f3a009bc5 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -1535,6 +1535,12 @@ access (use of, assignment to, or deletion of ``x.name``) for class instances. result of implicit invocation via language syntax or built-in functions. See :ref:`special-lookup`. + .. audit-event:: object.__getattr__ obj,name object.__getattribute__ + + For certain sensitive attribute accesses, raises an + :ref:`auditing event ` ``object.__getattr__`` with arguments + ``obj`` and ``name``. + .. method:: object.__setattr__(self, name, value) @@ -1546,12 +1552,24 @@ access (use of, assignment to, or deletion of ``x.name``) for class instances. call the base class method with the same name, for example, ``object.__setattr__(self, name, value)``. + .. audit-event:: object.__setattr__ obj,name,value object.__setattr__ + + For certain sensitive attribute assignments, raises an + :ref:`auditing event ` ``object.__setattr__`` with arguments + ``obj``, ``name``, ``value``. + .. method:: object.__delattr__(self, name) Like :meth:`__setattr__` but for attribute deletion instead of assignment. This should only be implemented if ``del obj.name`` is meaningful for the object. + .. audit-event:: object.__delattr__ obj,name object.__delattr__ + + For certain sensitive attribute deletions, raises an + :ref:`auditing event ` ``object.__delattr__`` with arguments + ``obj`` and ``name``. + .. method:: object.__dir__(self) From webhook-mailer at python.org Tue Oct 20 17:27:19 2020 From: webhook-mailer at python.org (akuchling) Date: Tue, 20 Oct 2020 21:27:19 -0000 Subject: [Python-checkins] bpo-29981: Add examples and update index for set, dict, and generator comprehensions'(GH-20272) Message-ID: https://github.com/python/cpython/commit/2d55aa9e37c9c84f4f6a8135d0326da0bcd8f38b commit: 2d55aa9e37c9c84f4f6a8135d0326da0bcd8f38b branch: master author: Florian Dahlitz committer: akuchling date: 2020-10-20T17:27:07-04:00 summary: bpo-29981: Add examples and update index for set, dict, and generator comprehensions'(GH-20272) Co-authored-by: R?mi Lapeyre files: M Doc/glossary.rst M Doc/library/stdtypes.rst M Doc/reference/expressions.rst M Misc/ACKS diff --git a/Doc/glossary.rst b/Doc/glossary.rst index 9fdbdb1a83f28..4f0654b3254e4 100644 --- a/Doc/glossary.rst +++ b/Doc/glossary.rst @@ -308,6 +308,12 @@ Glossary keys can be any object with :meth:`__hash__` and :meth:`__eq__` methods. Called a hash in Perl. + dictionary comprehension + A compact way to process all or part of the elements in an iterable and + return a dictionary with the results. ``results = {n: n ** 2 for n in + range(10)}`` generates a dictionary containing key ``n`` mapped to + value ``n ** 2``. See :ref:`comprehensions`. + dictionary view The objects returned from :meth:`dict.keys`, :meth:`dict.values`, and :meth:`dict.items` are called dictionary views. They provide a dynamic @@ -1026,6 +1032,12 @@ Glossary interface can be registered explicitly using :func:`~abc.register`. + set comprehension + A compact way to process all or part of the elements in an iterable and + return a set with the results. ``results = {c for c in 'abracadabra' if + c not in 'abc'}`` generates the set of strings ``{'r', 'd'}``. See + :ref:`comprehensions`. + single dispatch A form of :term:`generic function` dispatch where the implementation is chosen based on the type of a single argument. diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 2fc7a6118a322..c74d164463601 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -4140,6 +4140,12 @@ The constructors for both classes work the same: objects. If *iterable* is not specified, a new empty set is returned. + Sets can be created by several means: + + * Use a comma-separated list of elements within braces: ``{'jack', 'sjoerd'}`` + * Use a set comprehension: ``{c for c in 'abracadabra' if c not in 'abc'}`` + * Use the type constructor: ``set()``, ``set('foobar')``, ``set(['a', 'b', 'foo'])`` + Instances of :class:`set` and :class:`frozenset` provide the following operations: @@ -4332,6 +4338,14 @@ pairs within braces, for example: ``{'jack': 4098, 'sjoerd': 4127}`` or ``{4098: Return a new dictionary initialized from an optional positional argument and a possibly empty set of keyword arguments. + Dictionaries can be created by several means: + + * Use a comma-separated list of ``key: value`` pairs within braces: + ``{'jack': 4098, 'sjoerd': 4127}`` or ``{4098: 'jack', 4127: 'sjoerd'}`` + * Use a dict comprehension: ``{}``, ``{x: x ** 2 for x in range(10)}`` + * Use the type constructor: ``dict()``, + ``dict([('foo', 100), ('bar', 200)])``, ``dict(foo=100, bar=200)`` + If no positional argument is given, an empty dictionary is created. If a positional argument is given and it is a mapping object, a dictionary is created with the same key-value pairs as the mapping object. Otherwise, diff --git a/Doc/reference/expressions.rst b/Doc/reference/expressions.rst index b68c29860cf33..81dd6fc860355 100644 --- a/Doc/reference/expressions.rst +++ b/Doc/reference/expressions.rst @@ -162,6 +162,8 @@ ambiguities and allow common typos to pass uncaught. Displays for lists, sets and dictionaries ----------------------------------------- +.. index:: single: comprehensions + For constructing a list, a set or a dictionary Python provides special syntax called "displays", each of them in two flavors: @@ -260,6 +262,7 @@ Set displays .. index:: pair: set; display + pair: set; comprehensions object: set single: {} (curly brackets); set expression single: , (comma); expression list @@ -287,6 +290,7 @@ Dictionary displays .. index:: pair: dictionary; display + pair: dictionary; comprehensions key, datum, key/datum pair object: dictionary single: {} (curly brackets); dictionary expression diff --git a/Misc/ACKS b/Misc/ACKS index 404f70ec87fe2..5285693a6e6d4 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -382,6 +382,7 @@ Brian Curtin Jason Curtis Hakan Celik Paul Dagnelie +Florian Dahlitz Lisandro Dalcin Darren Dale Andrew Dalke From webhook-mailer at python.org Tue Oct 20 19:38:22 2020 From: webhook-mailer at python.org (gvanrossum) Date: Tue, 20 Oct 2020 23:38:22 -0000 Subject: [Python-checkins] bpo-42010: [docs] Clarify subscription of types (GH-22822) Message-ID: https://github.com/python/cpython/commit/7cdf30fff39ea97f403b5472096349998d190e30 commit: 7cdf30fff39ea97f403b5472096349998d190e30 branch: master author: kj <28750310+Fidget-Spinner at users.noreply.github.com> committer: gvanrossum date: 2020-10-20T16:38:08-07:00 summary: bpo-42010: [docs] Clarify subscription of types (GH-22822) files: A Misc/NEWS.d/next/Documentation/2020-10-21-02-21-14.bpo-42010.76vJ0u.rst M Doc/reference/expressions.rst diff --git a/Doc/reference/expressions.rst b/Doc/reference/expressions.rst index 81dd6fc860355..938a9732f5a25 100644 --- a/Doc/reference/expressions.rst +++ b/Doc/reference/expressions.rst @@ -800,8 +800,8 @@ Subscriptions object: dictionary pair: sequence; item -A subscription selects an item of a sequence (string, tuple or list) or mapping -(dictionary) object: +Subscription of a sequence (string, tuple or list) or mapping (dictionary) +object usually selects an item from the collection: .. productionlist:: python-grammar subscription: `primary` "[" `expression_list` "]" @@ -837,6 +837,17 @@ this method will need to explicitly add that support. A string's items are characters. A character is not a separate data type but a string of exactly one character. +.. + At the time of writing this, there is no documentation for generic alias + or PEP 585. Thus the link currently points to PEP 585 itself. + Please change the link for generic alias to reference the correct + documentation once documentation for PEP 585 becomes available. + +Subscription of certain :term:`classes ` or :term:`types ` +creates a `generic alias `_. +In this case, user-defined classes can support subscription by providing a +:meth:`__class_getitem__` classmethod. + .. _slicings: diff --git a/Misc/NEWS.d/next/Documentation/2020-10-21-02-21-14.bpo-42010.76vJ0u.rst b/Misc/NEWS.d/next/Documentation/2020-10-21-02-21-14.bpo-42010.76vJ0u.rst new file mode 100644 index 0000000000000..2a0cbf1075783 --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2020-10-21-02-21-14.bpo-42010.76vJ0u.rst @@ -0,0 +1,4 @@ +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 +:meth:`__class_getitem__` is provided. From webhook-mailer at python.org Tue Oct 20 19:58:57 2020 From: webhook-mailer at python.org (miss-islington) Date: Tue, 20 Oct 2020 23:58:57 -0000 Subject: [Python-checkins] [3.9] bpo-42010: [docs] Clarify subscription of types (GH-22822) (GH-22840) Message-ID: https://github.com/python/cpython/commit/d05514ace652769457e6228f0186d4f830b676b9 commit: d05514ace652769457e6228f0186d4f830b676b9 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-20T16:58:49-07:00 summary: [3.9] bpo-42010: [docs] Clarify subscription of types (GH-22822) (GH-22840) (cherry picked from commit 7cdf30fff39ea97f403b5472096349998d190e30) Co-authored-by: kj <28750310+Fidget-Spinner at users.noreply.github.com> Automerge-Triggered-By: GH:gvanrossum files: A Misc/NEWS.d/next/Documentation/2020-10-21-02-21-14.bpo-42010.76vJ0u.rst M Doc/reference/expressions.rst diff --git a/Doc/reference/expressions.rst b/Doc/reference/expressions.rst index b68c29860cf33..f1f19c3f02d39 100644 --- a/Doc/reference/expressions.rst +++ b/Doc/reference/expressions.rst @@ -796,8 +796,8 @@ Subscriptions object: dictionary pair: sequence; item -A subscription selects an item of a sequence (string, tuple or list) or mapping -(dictionary) object: +Subscription of a sequence (string, tuple or list) or mapping (dictionary) +object usually selects an item from the collection: .. productionlist:: python-grammar subscription: `primary` "[" `expression_list` "]" @@ -833,6 +833,17 @@ this method will need to explicitly add that support. A string's items are characters. A character is not a separate data type but a string of exactly one character. +.. + At the time of writing this, there is no documentation for generic alias + or PEP 585. Thus the link currently points to PEP 585 itself. + Please change the link for generic alias to reference the correct + documentation once documentation for PEP 585 becomes available. + +Subscription of certain :term:`classes ` or :term:`types ` +creates a `generic alias `_. +In this case, user-defined classes can support subscription by providing a +:meth:`__class_getitem__` classmethod. + .. _slicings: diff --git a/Misc/NEWS.d/next/Documentation/2020-10-21-02-21-14.bpo-42010.76vJ0u.rst b/Misc/NEWS.d/next/Documentation/2020-10-21-02-21-14.bpo-42010.76vJ0u.rst new file mode 100644 index 0000000000000..2a0cbf1075783 --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2020-10-21-02-21-14.bpo-42010.76vJ0u.rst @@ -0,0 +1,4 @@ +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 +:meth:`__class_getitem__` is provided. From webhook-mailer at python.org Tue Oct 20 20:37:34 2020 From: webhook-mailer at python.org (gpshead) Date: Wed, 21 Oct 2020 00:37:34 -0000 Subject: [Python-checkins] bpo-41586: Attempt to make the pipesize tests more robust. (GH-22839) Message-ID: https://github.com/python/cpython/commit/786addd9d07b6c712b8ea9ee06e1f9f41c1b67a1 commit: 786addd9d07b6c712b8ea9ee06e1f9f41c1b67a1 branch: master author: Gregory P. Smith committer: gpshead date: 2020-10-20T17:37:20-07:00 summary: bpo-41586: Attempt to make the pipesize tests more robust. (GH-22839) Several buildbots are failing on these, likely due to an inability to set the pipe size to the desired test value. files: M Lib/test/test_fcntl.py M Lib/test/test_subprocess.py diff --git a/Lib/test/test_fcntl.py b/Lib/test/test_fcntl.py index 8d6e9ff788454..83ee7a5b0d012 100644 --- a/Lib/test/test_fcntl.py +++ b/Lib/test/test_fcntl.py @@ -190,17 +190,24 @@ def test_fcntl_f_getpath(self): res = fcntl.fcntl(self.f.fileno(), fcntl.F_GETPATH, bytes(len(expected))) self.assertEqual(expected, res) - @unittest.skipIf(not (hasattr(fcntl, "F_SETPIPE_SZ") and hasattr(fcntl, "F_GETPIPE_SZ")), - "F_SETPIPE_SZ and F_GETPIPE_SZ are not available on all unix platforms.") + @unittest.skipUnless( + hasattr(fcntl, "F_SETPIPE_SZ") and hasattr(fcntl, "F_GETPIPE_SZ"), + "F_SETPIPE_SZ and F_GETPIPE_SZ are not available on all platforms.") def test_fcntl_f_pipesize(self): test_pipe_r, test_pipe_w = os.pipe() - # Get the default pipesize with F_GETPIPE_SZ - pipesize_default = fcntl.fcntl(test_pipe_w, fcntl.F_GETPIPE_SZ) - # Multiply the default with 2 to get a new value. - fcntl.fcntl(test_pipe_w, fcntl.F_SETPIPE_SZ, pipesize_default * 2) - self.assertEqual(fcntl.fcntl(test_pipe_w, fcntl.F_GETPIPE_SZ), pipesize_default * 2) - os.close(test_pipe_r) - os.close(test_pipe_w) + try: + # Get the default pipesize with F_GETPIPE_SZ + pipesize_default = fcntl.fcntl(test_pipe_w, fcntl.F_GETPIPE_SZ) + pipesize = pipesize_default // 2 # A new value to detect change. + if pipesize < 512: # the POSIX minimum + raise unittest.SkitTest( + 'default pipesize too small to perform test.') + fcntl.fcntl(test_pipe_w, fcntl.F_SETPIPE_SZ, pipesize) + self.assertEqual(fcntl.fcntl(test_pipe_w, fcntl.F_GETPIPE_SZ), + pipesize) + finally: + os.close(test_pipe_r) + os.close(test_pipe_w) def test_main(): diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index 8b576c036ef0d..9fc4434649dbc 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -666,45 +666,66 @@ def test_stdin_devnull(self): p.wait() self.assertEqual(p.stdin, None) + @unittest.skipUnless(fcntl and hasattr(fcntl, 'F_GETPIPE_SZ'), + 'fcntl.F_GETPIPE_SZ required for test.') def test_pipesizes(self): - # stdin redirection - pipesize = 16 * 1024 - p = subprocess.Popen([sys.executable, "-c", - 'import sys; sys.stdin.read(); sys.stdout.write("out"); sys.stderr.write("error!")'], - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - pipesize=pipesize) - # We only assert pipe size has changed on platforms that support it. - if sys.platform != "win32" and hasattr(fcntl, "F_GETPIPE_SZ"): + test_pipe_r, test_pipe_w = os.pipe() + try: + # Get the default pipesize with F_GETPIPE_SZ + pipesize_default = fcntl.fcntl(test_pipe_w, fcntl.F_GETPIPE_SZ) + finally: + os.close(test_pipe_r) + os.close(test_pipe_w) + pipesize = pipesize_default // 2 + if pipesize < 512: # the POSIX minimum + raise unittest.SkitTest( + 'default pipesize too small to perform test.') + p = subprocess.Popen( + [sys.executable, "-c", + 'import sys; sys.stdin.read(); sys.stdout.write("out"); ' + 'sys.stderr.write("error!")'], + stdin=subprocess.PIPE, stdout=subprocess.PIPE, + stderr=subprocess.PIPE, pipesize=pipesize) + try: for fifo in [p.stdin, p.stdout, p.stderr]: - self.assertEqual(fcntl.fcntl(fifo.fileno(), fcntl.F_GETPIPE_SZ), pipesize) - # Windows pipe size can be acquired with the GetNamedPipeInfoFunction - # https://docs.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-getnamedpipeinfo - # However, this function is not yet in _winapi. - p.stdin.write(b"pear") - p.stdin.close() - p.wait() + self.assertEqual( + fcntl.fcntl(fifo.fileno(), fcntl.F_GETPIPE_SZ), + pipesize) + # Windows pipe size can be acquired via GetNamedPipeInfoFunction + # https://docs.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-getnamedpipeinfo + # However, this function is not yet in _winapi. + p.stdin.write(b"pear") + p.stdin.close() + finally: + p.kill() + p.wait() + @unittest.skipUnless(fcntl and hasattr(fcntl, 'F_GETPIPE_SZ'), + 'fcntl.F_GETPIPE_SZ required for test.') def test_pipesize_default(self): - p = subprocess.Popen([sys.executable, "-c", - 'import sys; sys.stdin.read(); sys.stdout.write("out");' - ' sys.stderr.write("error!")'], - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - pipesize=-1) - # UNIX tests using fcntl - if sys.platform != "win32" and hasattr(fcntl, "F_GETPIPE_SZ"): + p = subprocess.Popen( + [sys.executable, "-c", + 'import sys; sys.stdin.read(); sys.stdout.write("out"); ' + 'sys.stderr.write("error!")'], + stdin=subprocess.PIPE, stdout=subprocess.PIPE, + stderr=subprocess.PIPE, pipesize=-1) + try: fp_r, fp_w = os.pipe() - default_pipesize = fcntl.fcntl(fp_w, fcntl.F_GETPIPE_SZ) - for fifo in [p.stdin, p.stdout, p.stderr]: - self.assertEqual( - fcntl.fcntl(fifo.fileno(), fcntl.F_GETPIPE_SZ), default_pipesize) - # On other platforms we cannot test the pipe size (yet). But above code - # using pipesize=-1 should not crash. - p.stdin.close() - p.wait() + try: + default_pipesize = fcntl.fcntl(fp_w, fcntl.F_GETPIPE_SZ) + for fifo in [p.stdin, p.stdout, p.stderr]: + self.assertEqual( + fcntl.fcntl(fifo.fileno(), fcntl.F_GETPIPE_SZ), + default_pipesize) + finally: + os.close(fp_r) + os.close(fp_w) + # On other platforms we cannot test the pipe size (yet). But above + # code using pipesize=-1 should not crash. + p.stdin.close() + finally: + p.kill() + p.wait() def test_env(self): newenv = os.environ.copy() From webhook-mailer at python.org Tue Oct 20 20:56:15 2020 From: webhook-mailer at python.org (terryjreedy) Date: Wed, 21 Oct 2020 00:56:15 -0000 Subject: [Python-checkins] Update idlelib/help.html to current Sphinx output (GH-22833) Message-ID: https://github.com/python/cpython/commit/a460d45063844a21c20fa8b0d23878165f99f3b5 commit: a460d45063844a21c20fa8b0d23878165f99f3b5 branch: master author: Terry Jan Reedy committer: terryjreedy date: 2020-10-20T20:56:01-04:00 summary: Update idlelib/help.html to current Sphinx output (GH-22833) idle.rst is unchanged files: M Lib/idlelib/help.html diff --git a/Lib/idlelib/help.html b/Lib/idlelib/help.html index 0edd3917e1ffa..170999e128017 100644 --- a/Lib/idlelib/help.html +++ b/Lib/idlelib/help.html @@ -1,23 +1,24 @@ - + - IDLE — Python 3.10.0a0 documentation + + IDLE — Python 3.10.0a1 documentation - - - - - + + + + + - + @@ -71,11 +72,12 @@

Navigation

  • - 3.10.0a0 Documentation » + 3.10.0a1 Documentation »
  • +
  • @@ -426,30 +428,30 @@

    Key bindingsCommand key on macOS.

    • Backspace deletes to the left; Del deletes to the right

    • -
    • C-Backspace delete word left; C-Del delete word to the right

    • -
    • Arrow keys and Page Up/Page Down to move around

    • -
    • C-LeftArrow and C-RightArrow moves by words

    • +
    • C-Backspace delete word left; C-Del delete word to the right

    • +
    • Arrow keys and Page Up/Page Down to move around

    • +
    • C-LeftArrow and C-RightArrow moves by words

    • Home/End go to begin/end of line

    • -
    • C-Home/C-End go to begin/end of file

    • +
    • C-Home/C-End go to begin/end of file

    • Some useful Emacs bindings are inherited from Tcl/Tk:

        -
      • C-a beginning of line

      • -
      • C-e end of line

      • -
      • C-k kill line (but doesn?t put it in clipboard)

      • -
      • C-l center window around the insertion point

      • -
      • C-b go backward one character without deleting (usually you can +

      • C-a beginning of line

      • +
      • C-e end of line

      • +
      • C-k kill line (but doesn?t put it in clipboard)

      • +
      • C-l center window around the insertion point

      • +
      • C-b go backward one character without deleting (usually you can also use the cursor key for this)

      • -
      • C-f go forward one character without deleting (usually you can +

      • C-f go forward one character without deleting (usually you can also use the cursor key for this)

      • -
      • C-p go up one line (usually you can also use the cursor key for +

      • C-p go up one line (usually you can also use the cursor key for this)

      • -
      • C-d delete next character

      • +
      • C-d delete next character

    -

    Standard keybindings (like C-c to copy and C-v to paste) +

    Standard keybindings (like C-c to copy and C-v to paste) may work. Keybindings are selected in the Configure IDLE dialog.

  • -
  • C-d sends end-of-file; closes window if typed at a >>> prompt

  • -
  • Alt-/ (Expand word) is also useful to reduce typing

    +
  • C-c interrupts executing command

  • +
  • C-d sends end-of-file; closes window if typed at a >>> prompt

  • +
  • Alt-/ (Expand word) is also useful to reduce typing

    Command history

      -
    • Alt-p retrieves previous command matching what you have typed. On -macOS use C-p.

    • -
    • Alt-n retrieves next. On macOS use C-n.

    • +
    • Alt-p retrieves previous command matching what you have typed. On +macOS use C-p.

    • +
    • Alt-n retrieves next. On macOS use C-n.

    • Return while on any previous command retrieves that command

  • @@ -852,6 +854,7 @@

    Extensions @@ -947,11 +950,12 @@

    Navigation

  • - 3.10.0a0 Documentation » + 3.10.0a1 Documentation »
  • +
  • @@ -978,11 +982,11 @@

    Navigation



    - Last updated on Sep 22, 2020. + Last updated on Oct 20, 2020. Found a bug?
    - Created using Sphinx 2.1.1. + Created using Sphinx 3.2.1. From webhook-mailer at python.org Tue Oct 20 21:15:32 2020 From: webhook-mailer at python.org (miss-islington) Date: Wed, 21 Oct 2020 01:15:32 -0000 Subject: [Python-checkins] Update idlelib/help.html to current Sphinx output (GH-22833) Message-ID: https://github.com/python/cpython/commit/a1296ed60b2aacd44ccc490d725c0bac2ae9877a commit: a1296ed60b2aacd44ccc490d725c0bac2ae9877a branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-20T18:15:23-07:00 summary: Update idlelib/help.html to current Sphinx output (GH-22833) idle.rst is unchanged (cherry picked from commit a460d45063844a21c20fa8b0d23878165f99f3b5) Co-authored-by: Terry Jan Reedy files: M Lib/idlelib/help.html diff --git a/Lib/idlelib/help.html b/Lib/idlelib/help.html index 0edd3917e1ffa..170999e128017 100644 --- a/Lib/idlelib/help.html +++ b/Lib/idlelib/help.html @@ -1,23 +1,24 @@ - + - IDLE — Python 3.10.0a0 documentation + + IDLE — Python 3.10.0a1 documentation - - - - - + + + + + - + @@ -71,11 +72,12 @@

    Navigation

  • - 3.10.0a0 Documentation » + 3.10.0a1 Documentation »
  • +
  • @@ -426,30 +428,30 @@

    Key bindingsCommand key on macOS.

    • Backspace deletes to the left; Del deletes to the right

    • -
    • C-Backspace delete word left; C-Del delete word to the right

    • -
    • Arrow keys and Page Up/Page Down to move around

    • -
    • C-LeftArrow and C-RightArrow moves by words

    • +
    • C-Backspace delete word left; C-Del delete word to the right

    • +
    • Arrow keys and Page Up/Page Down to move around

    • +
    • C-LeftArrow and C-RightArrow moves by words

    • Home/End go to begin/end of line

    • -
    • C-Home/C-End go to begin/end of file

    • +
    • C-Home/C-End go to begin/end of file

    • Some useful Emacs bindings are inherited from Tcl/Tk:

        -
      • C-a beginning of line

      • -
      • C-e end of line

      • -
      • C-k kill line (but doesn?t put it in clipboard)

      • -
      • C-l center window around the insertion point

      • -
      • C-b go backward one character without deleting (usually you can +

      • C-a beginning of line

      • +
      • C-e end of line

      • +
      • C-k kill line (but doesn?t put it in clipboard)

      • +
      • C-l center window around the insertion point

      • +
      • C-b go backward one character without deleting (usually you can also use the cursor key for this)

      • -
      • C-f go forward one character without deleting (usually you can +

      • C-f go forward one character without deleting (usually you can also use the cursor key for this)

      • -
      • C-p go up one line (usually you can also use the cursor key for +

      • C-p go up one line (usually you can also use the cursor key for this)

      • -
      • C-d delete next character

      • +
      • C-d delete next character

    -

    Standard keybindings (like C-c to copy and C-v to paste) +

    Standard keybindings (like C-c to copy and C-v to paste) may work. Keybindings are selected in the Configure IDLE dialog.

  • -
  • C-d sends end-of-file; closes window if typed at a >>> prompt

  • -
  • Alt-/ (Expand word) is also useful to reduce typing

    +
  • C-c interrupts executing command

  • +
  • C-d sends end-of-file; closes window if typed at a >>> prompt

  • +
  • Alt-/ (Expand word) is also useful to reduce typing

    Command history

      -
    • Alt-p retrieves previous command matching what you have typed. On -macOS use C-p.

    • -
    • Alt-n retrieves next. On macOS use C-n.

    • +
    • Alt-p retrieves previous command matching what you have typed. On +macOS use C-p.

    • +
    • Alt-n retrieves next. On macOS use C-n.

    • Return while on any previous command retrieves that command

  • @@ -852,6 +854,7 @@

    Extensions @@ -947,11 +950,12 @@

    Navigation

  • - 3.10.0a0 Documentation » + 3.10.0a1 Documentation »
  • +
  • @@ -978,11 +982,11 @@

    Navigation



    - Last updated on Sep 22, 2020. + Last updated on Oct 20, 2020. Found a bug?
    - Created using Sphinx 2.1.1. + Created using Sphinx 3.2.1. From webhook-mailer at python.org Tue Oct 20 21:29:22 2020 From: webhook-mailer at python.org (methane) Date: Wed, 21 Oct 2020 01:29:22 -0000 Subject: [Python-checkins] bpo-41902: Micro optimization for compute_item of range (GH-22492) Message-ID: https://github.com/python/cpython/commit/25492a5b59c5b74328278f195540e318ab87674f commit: 25492a5b59c5b74328278f195540e318ab87674f branch: master author: Dong-hee Na committer: methane date: 2020-10-21T10:29:14+09:00 summary: bpo-41902: Micro optimization for compute_item of range (GH-22492) files: A Misc/NEWS.d/next/Core and Builtins/2020-10-02-13-32-05.bpo-41902.ZKTxzW.rst M Objects/rangeobject.c diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-10-02-13-32-05.bpo-41902.ZKTxzW.rst b/Misc/NEWS.d/next/Core and Builtins/2020-10-02-13-32-05.bpo-41902.ZKTxzW.rst new file mode 100644 index 0000000000000..b118a6a36fae3 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2020-10-02-13-32-05.bpo-41902.ZKTxzW.rst @@ -0,0 +1,3 @@ +Micro optimization when compute :c:member:`~PySequenceMethods.sq_item` and +:c:member:`~PyMappingMethods.mp_subscript` of :class:`range`. Patch by +Dong-hee Na. diff --git a/Objects/rangeobject.c b/Objects/rangeobject.c index ba6d425717495..eaa48d5f44fcc 100644 --- a/Objects/rangeobject.c +++ b/Objects/rangeobject.c @@ -254,11 +254,17 @@ compute_item(rangeobject *r, PyObject *i) /* PyLong equivalent to: * return r->start + (i * r->step) */ - incr = PyNumber_Multiply(i, r->step); - if (!incr) - return NULL; - result = PyNumber_Add(r->start, incr); - Py_DECREF(incr); + if (r->step == _PyLong_One) { + result = PyNumber_Add(r->start, i); + } + else { + incr = PyNumber_Multiply(i, r->step); + if (!incr) { + return NULL; + } + result = PyNumber_Add(r->start, incr); + Py_DECREF(incr); + } return result; } From webhook-mailer at python.org Tue Oct 20 22:08:27 2020 From: webhook-mailer at python.org (miss-islington) Date: Wed, 21 Oct 2020 02:08:27 -0000 Subject: [Python-checkins] bpo-23706: Add newline parameter to pathlib.Path.write_text (GH-22420) (GH-22420) Message-ID: https://github.com/python/cpython/commit/5f227413400c4dfdba210cc0f8c9305421638bc1 commit: 5f227413400c4dfdba210cc0f8c9305421638bc1 branch: master author: ?????? committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-20T19:08:19-07:00 summary: bpo-23706: Add newline parameter to pathlib.Path.write_text (GH-22420) (GH-22420) * Add _newline_ parameter to `pathlib.Path.write_text()` * Update documentation of `pathlib.Path.write_text()` * Add test case for `pathlib.Path.write_text()` calls with _newline_ parameter passed Automerge-Triggered-By: GH:methane files: A Misc/NEWS.d/next/Library/2020-09-30-11-05-11.bpo-23706.dHTGjF.rst M Doc/library/pathlib.rst M Lib/pathlib.py M Lib/test/test_pathlib.py diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst index 9526a03b05398..9de72bb725c75 100644 --- a/Doc/library/pathlib.rst +++ b/Doc/library/pathlib.rst @@ -1166,7 +1166,7 @@ call fails (for example because the path doesn't exist). .. versionadded:: 3.5 -.. method:: Path.write_text(data, encoding=None, errors=None) +.. method:: Path.write_text(data, encoding=None, errors=None, newline=None) Open the file pointed to in text mode, write *data* to it, and close the file:: @@ -1182,6 +1182,9 @@ call fails (for example because the path doesn't exist). .. versionadded:: 3.5 + .. versionchanged:: 3.10 + The *newline* parameter was added. + Correspondence to tools in the :mod:`os` module ----------------------------------------------- diff --git a/Lib/pathlib.py b/Lib/pathlib.py index 147be2ff0dddf..178c5b981d8e5 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -1264,14 +1264,14 @@ def write_bytes(self, data): with self.open(mode='wb') as f: return f.write(view) - def write_text(self, data, encoding=None, errors=None): + def write_text(self, data, encoding=None, errors=None, newline=None): """ Open the file in text mode, write to it, and close the file. """ if not isinstance(data, str): raise TypeError('data must be str, not %s' % data.__class__.__name__) - with self.open(mode='w', encoding=encoding, errors=errors) as f: + with self.open(mode='w', encoding=encoding, errors=errors, newline=newline) as f: return f.write(data) def readlink(self): diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py index 2cb6738a295b6..17292dc1abf73 100644 --- a/Lib/test/test_pathlib.py +++ b/Lib/test/test_pathlib.py @@ -1510,6 +1510,26 @@ def test_read_write_text(self): self.assertRaises(TypeError, (p / 'fileA').write_text, b'somebytes') self.assertEqual((p / 'fileA').read_text(encoding='latin-1'), '?bcdefg') + def test_write_text_with_newlines(self): + p = self.cls(BASE) + # Check that `\n` character change nothing + (p / 'fileA').write_text('abcde\r\nfghlk\n\rmnopq', newline='\n') + self.assertEqual((p / 'fileA').read_bytes(), + b'abcde\r\nfghlk\n\rmnopq') + # Check that `\r` character replaces `\n` + (p / 'fileA').write_text('abcde\r\nfghlk\n\rmnopq', newline='\r') + self.assertEqual((p / 'fileA').read_bytes(), + b'abcde\r\rfghlk\r\rmnopq') + # Check that `\r\n` character replaces `\n` + (p / 'fileA').write_text('abcde\r\nfghlk\n\rmnopq', newline='\r\n') + self.assertEqual((p / 'fileA').read_bytes(), + b'abcde\r\r\nfghlk\r\n\rmnopq') + # Check that no argument passed will change `\n` to `os.linesep` + os_linesep_byte = bytes(os.linesep, encoding='ascii') + (p / 'fileA').write_text('abcde\nfghlk\n\rmnopq') + self.assertEqual((p / 'fileA').read_bytes(), + b'abcde' + os_linesep_byte + b'fghlk' + os_linesep_byte + b'\rmnopq') + def test_iterdir(self): P = self.cls p = P(BASE) diff --git a/Misc/NEWS.d/next/Library/2020-09-30-11-05-11.bpo-23706.dHTGjF.rst b/Misc/NEWS.d/next/Library/2020-09-30-11-05-11.bpo-23706.dHTGjF.rst new file mode 100644 index 0000000000000..b9a69a55e6a8d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-09-30-11-05-11.bpo-23706.dHTGjF.rst @@ -0,0 +1 @@ +Added *newline* parameter to ``pathlib.Path.write_text()``. From webhook-mailer at python.org Tue Oct 20 22:30:10 2020 From: webhook-mailer at python.org (corona10) Date: Wed, 21 Oct 2020 02:30:10 -0000 Subject: [Python-checkins] bpo-41902: Micro optimization for range.index if step is 1 (GH-22479) Message-ID: https://github.com/python/cpython/commit/c0f22fb8b3006936757cebb959cee94e285bc503 commit: c0f22fb8b3006936757cebb959cee94e285bc503 branch: master author: Dong-hee Na committer: corona10 date: 2020-10-21T11:29:56+09:00 summary: bpo-41902: Micro optimization for range.index if step is 1 (GH-22479) files: A Misc/NEWS.d/next/Core and Builtins/2020-10-01-22-44-23.bpo-41902.iLoMVF.rst M Objects/rangeobject.c diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-10-01-22-44-23.bpo-41902.iLoMVF.rst b/Misc/NEWS.d/next/Core and Builtins/2020-10-01-22-44-23.bpo-41902.iLoMVF.rst new file mode 100644 index 0000000000000..738ef5aec9503 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2020-10-01-22-44-23.bpo-41902.iLoMVF.rst @@ -0,0 +1 @@ +Micro optimization for range.index if step is 1. Patch by Dong-hee Na. diff --git a/Objects/rangeobject.c b/Objects/rangeobject.c index eaa48d5f44fcc..babf55b108b9a 100644 --- a/Objects/rangeobject.c +++ b/Objects/rangeobject.c @@ -582,13 +582,19 @@ range_index(rangeobject *r, PyObject *ob) return NULL; if (contains) { - PyObject *idx, *tmp = PyNumber_Subtract(ob, r->start); - if (tmp == NULL) + PyObject *idx = PyNumber_Subtract(ob, r->start); + if (idx == NULL) { return NULL; + } + + if (r->step == _PyLong_One) { + return idx; + } + /* idx = (ob - r.start) // r.step */ - idx = PyNumber_FloorDivide(tmp, r->step); - Py_DECREF(tmp); - return idx; + PyObject *sidx = PyNumber_FloorDivide(idx, r->step); + Py_DECREF(idx); + return sidx; } /* object is not in the range */ From webhook-mailer at python.org Wed Oct 21 01:15:27 2020 From: webhook-mailer at python.org (methane) Date: Wed, 21 Oct 2020 05:15:27 -0000 Subject: [Python-checkins] bpo-41744: Package python.props with correct name in NuGet package (GH-22154) Message-ID: https://github.com/python/cpython/commit/d0bfce992c4ce0e6e71f13a993c91903a97a62f3 commit: d0bfce992c4ce0e6e71f13a993c91903a97a62f3 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: methane date: 2020-10-21T14:15:18+09:00 summary: bpo-41744: Package python.props with correct name in NuGet package (GH-22154) NuGet automatically includes .props file from the build directory in the target using the package, but only if the .props file has the correct name: it must be $(id).props Rename python.props correspondingly in all the nuspec variants. Also keep python.props as it were for backward compatibility. (cherry picked from commit 7c11a9accabe3720940f334eb1226bb7bb9179c7) Co-authored-by: V?clav Slav?k files: A Misc/NEWS.d/next/Windows/2020-09-11-17-59-33.bpo-41744.e_ugDQ.rst M Tools/nuget/python.nuspec M Tools/nuget/pythonarm32.nuspec M Tools/nuget/pythondaily.nuspec M Tools/nuget/pythonx86.nuspec diff --git a/Misc/NEWS.d/next/Windows/2020-09-11-17-59-33.bpo-41744.e_ugDQ.rst b/Misc/NEWS.d/next/Windows/2020-09-11-17-59-33.bpo-41744.e_ugDQ.rst new file mode 100644 index 0000000000000..6106d6604c7dd --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2020-09-11-17-59-33.bpo-41744.e_ugDQ.rst @@ -0,0 +1 @@ +Fixes automatic import of props file when using the Nuget package. \ No newline at end of file diff --git a/Tools/nuget/python.nuspec b/Tools/nuget/python.nuspec index 8f98e808916a3..2da5f2037eb23 100644 --- a/Tools/nuget/python.nuspec +++ b/Tools/nuget/python.nuspec @@ -13,6 +13,6 @@ - + diff --git a/Tools/nuget/pythonarm32.nuspec b/Tools/nuget/pythonarm32.nuspec index 273d79a0312bd..2d197931edb32 100644 --- a/Tools/nuget/pythonarm32.nuspec +++ b/Tools/nuget/pythonarm32.nuspec @@ -14,6 +14,7 @@ - + + diff --git a/Tools/nuget/pythondaily.nuspec b/Tools/nuget/pythondaily.nuspec index 5cf55806ddfb3..7df1983f42a6f 100644 --- a/Tools/nuget/pythondaily.nuspec +++ b/Tools/nuget/pythondaily.nuspec @@ -13,6 +13,7 @@ - + + diff --git a/Tools/nuget/pythonx86.nuspec b/Tools/nuget/pythonx86.nuspec index 27ef67e7f5d36..ea878ba0bf390 100644 --- a/Tools/nuget/pythonx86.nuspec +++ b/Tools/nuget/pythonx86.nuspec @@ -13,6 +13,7 @@ - + + From webhook-mailer at python.org Wed Oct 21 01:16:30 2020 From: webhook-mailer at python.org (methane) Date: Wed, 21 Oct 2020 05:16:30 -0000 Subject: [Python-checkins] bpo-41646: Mention path-like objects support in the docs for shutil.copy() (GH-22208) Message-ID: https://github.com/python/cpython/commit/6443a8ccc886749f5e83a8ca073006742b605d90 commit: 6443a8ccc886749f5e83a8ca073006742b605d90 branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: methane date: 2020-10-21T14:16:21+09:00 summary: bpo-41646: Mention path-like objects support in the docs for shutil.copy() (GH-22208) (cherry picked from commit 8f2b991eef062c22c429a96983c78be007180b66) Co-authored-by: Zackery Spytz files: M Doc/library/shutil.rst diff --git a/Doc/library/shutil.rst b/Doc/library/shutil.rst index 00c7d1049e6a0..b5eeec85fb85d 100644 --- a/Doc/library/shutil.rst +++ b/Doc/library/shutil.rst @@ -158,9 +158,9 @@ Directory and files operations .. function:: copy(src, dst, *, follow_symlinks=True) Copies the file *src* to the file or directory *dst*. *src* and *dst* - should be strings. If *dst* specifies a directory, the file will be - copied into *dst* using the base filename from *src*. Returns the - path to the newly created file. + should be :term:`path-like objects ` or strings. If + *dst* specifies a directory, the file will be copied into *dst* using the + base filename from *src*. Returns the path to the newly created file. If *follow_symlinks* is false, and *src* is a symbolic link, *dst* will be created as a symbolic link. If *follow_symlinks* From webhook-mailer at python.org Wed Oct 21 01:16:51 2020 From: webhook-mailer at python.org (methane) Date: Wed, 21 Oct 2020 05:16:51 -0000 Subject: [Python-checkins] bpo-41646: Mention path-like objects support in the docs for shutil.copy() (GH-22208) Message-ID: https://github.com/python/cpython/commit/19019eccdeeb8dea027bd7766ca9fe2892972da4 commit: 19019eccdeeb8dea027bd7766ca9fe2892972da4 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: methane date: 2020-10-21T14:16:42+09:00 summary: bpo-41646: Mention path-like objects support in the docs for shutil.copy() (GH-22208) (cherry picked from commit 8f2b991eef062c22c429a96983c78be007180b66) Co-authored-by: Zackery Spytz files: M Doc/library/shutil.rst diff --git a/Doc/library/shutil.rst b/Doc/library/shutil.rst index fd3ce7445b511..3f5122760ee16 100644 --- a/Doc/library/shutil.rst +++ b/Doc/library/shutil.rst @@ -158,9 +158,9 @@ Directory and files operations .. function:: copy(src, dst, *, follow_symlinks=True) Copies the file *src* to the file or directory *dst*. *src* and *dst* - should be strings. If *dst* specifies a directory, the file will be - copied into *dst* using the base filename from *src*. Returns the - path to the newly created file. + should be :term:`path-like objects ` or strings. If + *dst* specifies a directory, the file will be copied into *dst* using the + base filename from *src*. Returns the path to the newly created file. If *follow_symlinks* is false, and *src* is a symbolic link, *dst* will be created as a symbolic link. If *follow_symlinks* From webhook-mailer at python.org Wed Oct 21 01:25:18 2020 From: webhook-mailer at python.org (methane) Date: Wed, 21 Oct 2020 05:25:18 -0000 Subject: [Python-checkins] Doc: Do not encourage using a base class name in a derived class (GH-22177) Message-ID: https://github.com/python/cpython/commit/4642ccd1c3e460cb2746d3f2095f1c1d1bafa4fe commit: 4642ccd1c3e460cb2746d3f2095f1c1d1bafa4fe branch: master author: Andre Delfino committer: methane date: 2020-10-21T14:25:05+09:00 summary: Doc: Do not encourage using a base class name in a derived class (GH-22177) files: M Doc/faq/programming.rst diff --git a/Doc/faq/programming.rst b/Doc/faq/programming.rst index 2d542cfb1dbb4..57ab3e265f791 100644 --- a/Doc/faq/programming.rst +++ b/Doc/faq/programming.rst @@ -1517,18 +1517,18 @@ order` (MRO) with ``type(self).__mro__``, and return the next in line after How can I organize my code to make it easier to change the base class? ---------------------------------------------------------------------- -You could define an alias for the base class, assign the real base class to it -before your class definition, and use the alias throughout your class. Then all +You could assign the base class to an alias and derive from the alias. Then all you have to change is the value assigned to the alias. Incidentally, this trick is also handy if you want to decide dynamically (e.g. depending on availability of resources) which base class to use. Example:: - BaseAlias = + class Base: + ... + + BaseAlias = Base class Derived(BaseAlias): - def meth(self): - BaseAlias.meth(self) - ... + ... How do I create static class data and static class methods? From webhook-mailer at python.org Wed Oct 21 01:29:10 2020 From: webhook-mailer at python.org (miss-islington) Date: Wed, 21 Oct 2020 05:29:10 -0000 Subject: [Python-checkins] bpo-41316: Make tarfile follow specs for FNAME (GH-21511) Message-ID: https://github.com/python/cpython/commit/7917170c5b4793ca9443f753aaecb8fbb3ad54ef commit: 7917170c5b4793ca9443f753aaecb8fbb3ad54ef branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-20T22:29:00-07:00 summary: bpo-41316: Make tarfile follow specs for FNAME (GH-21511) tarfile writes full path to FNAME field of GZIP format instead of just basename if user specified absolute path. Some archive viewers may process file incorrectly. Also it creates security issue because anyone can know structure of directories on system and know username or other personal information. RFC1952 says about FNAME: This is the original name of the file being compressed, with any directory components removed. So tarfile must remove directory names from FNAME and write only basename of file. Automerge-Triggered-By: @jaraco (cherry picked from commit 22748a83d927d3da1beaed771be30887c42b2500) Co-authored-by: Artem Bulgakov files: A Misc/NEWS.d/next/Library/2020-07-28-12-08-58.bpo-41316.bSCbK4.rst M Lib/tarfile.py M Lib/test/test_tarfile.py M Misc/ACKS diff --git a/Lib/tarfile.py b/Lib/tarfile.py index 62a6d9d120ffa..e42279470dac6 100755 --- a/Lib/tarfile.py +++ b/Lib/tarfile.py @@ -420,6 +420,8 @@ def _init_write_gz(self): self.__write(b"\037\213\010\010" + timestamp + b"\002\377") if self.name.endswith(".gz"): self.name = self.name[:-3] + # Honor "directory components removed" from RFC1952 + self.name = os.path.basename(self.name) # RFC1952 says we must use ISO-8859-1 for the FNAME field. self.__write(self.name.encode("iso-8859-1", "replace") + NUL) diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py index 3ddeb97f5268f..1e5186a90b37f 100644 --- a/Lib/test/test_tarfile.py +++ b/Lib/test/test_tarfile.py @@ -1416,12 +1416,15 @@ def write(self, data): pax_headers={'non': 'empty'}) self.assertFalse(f.closed) + class GzipWriteTest(GzipTest, WriteTest): pass + class Bz2WriteTest(Bz2Test, WriteTest): pass + class LzmaWriteTest(LzmaTest, WriteTest): pass @@ -1464,8 +1467,17 @@ def test_file_mode(self): finally: os.umask(original_umask) + class GzipStreamWriteTest(GzipTest, StreamWriteTest): - pass + def test_source_directory_not_leaked(self): + """ + Ensure the source directory is not included in the tar header + per bpo-41316. + """ + tarfile.open(tmpname, self.mode).close() + payload = pathlib.Path(tmpname).read_text(encoding='latin-1') + assert os.path.dirname(tmpname) not in payload + class Bz2StreamWriteTest(Bz2Test, StreamWriteTest): decompressor = bz2.BZ2Decompressor if bz2 else None diff --git a/Misc/ACKS b/Misc/ACKS index 021df26426e36..f06fad7926d46 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -243,6 +243,7 @@ Colm Buckley Erik de Bueger Jan-Hein B?hrman Lars Buitinck +Artem Bulgakov Dick Bulterman Bill Bumgarner Jimmy Burgett diff --git a/Misc/NEWS.d/next/Library/2020-07-28-12-08-58.bpo-41316.bSCbK4.rst b/Misc/NEWS.d/next/Library/2020-07-28-12-08-58.bpo-41316.bSCbK4.rst new file mode 100644 index 0000000000000..139a170866ed4 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-07-28-12-08-58.bpo-41316.bSCbK4.rst @@ -0,0 +1 @@ +Fix the :mod:`tarfile` module to write only basename of TAR file to GZIP compression header. \ No newline at end of file From webhook-mailer at python.org Wed Oct 21 01:29:54 2020 From: webhook-mailer at python.org (miss-islington) Date: Wed, 21 Oct 2020 05:29:54 -0000 Subject: [Python-checkins] bpo-41316: Make tarfile follow specs for FNAME (GH-21511) Message-ID: https://github.com/python/cpython/commit/e866f33a48ee24e447fafd181f0da5f9584e0340 commit: e866f33a48ee24e447fafd181f0da5f9584e0340 branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-20T22:29:44-07:00 summary: bpo-41316: Make tarfile follow specs for FNAME (GH-21511) tarfile writes full path to FNAME field of GZIP format instead of just basename if user specified absolute path. Some archive viewers may process file incorrectly. Also it creates security issue because anyone can know structure of directories on system and know username or other personal information. RFC1952 says about FNAME: This is the original name of the file being compressed, with any directory components removed. So tarfile must remove directory names from FNAME and write only basename of file. Automerge-Triggered-By: @jaraco (cherry picked from commit 22748a83d927d3da1beaed771be30887c42b2500) Co-authored-by: Artem Bulgakov files: A Misc/NEWS.d/next/Library/2020-07-28-12-08-58.bpo-41316.bSCbK4.rst M Lib/tarfile.py M Lib/test/test_tarfile.py M Misc/ACKS diff --git a/Lib/tarfile.py b/Lib/tarfile.py index 7a69e1b1aa544..39f63b9cfc1ac 100755 --- a/Lib/tarfile.py +++ b/Lib/tarfile.py @@ -420,6 +420,8 @@ def _init_write_gz(self): self.__write(b"\037\213\010\010" + timestamp + b"\002\377") if self.name.endswith(".gz"): self.name = self.name[:-3] + # Honor "directory components removed" from RFC1952 + self.name = os.path.basename(self.name) # RFC1952 says we must use ISO-8859-1 for the FNAME field. self.__write(self.name.encode("iso-8859-1", "replace") + NUL) diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py index b512168d6ea87..be717e315700c 100644 --- a/Lib/test/test_tarfile.py +++ b/Lib/test/test_tarfile.py @@ -1384,12 +1384,15 @@ def write(self, data): pax_headers={'non': 'empty'}) self.assertFalse(f.closed) + class GzipWriteTest(GzipTest, WriteTest): pass + class Bz2WriteTest(Bz2Test, WriteTest): pass + class LzmaWriteTest(LzmaTest, WriteTest): pass @@ -1432,8 +1435,17 @@ def test_file_mode(self): finally: os.umask(original_umask) + class GzipStreamWriteTest(GzipTest, StreamWriteTest): - pass + def test_source_directory_not_leaked(self): + """ + Ensure the source directory is not included in the tar header + per bpo-41316. + """ + tarfile.open(tmpname, self.mode).close() + payload = pathlib.Path(tmpname).read_text(encoding='latin-1') + assert os.path.dirname(tmpname) not in payload + class Bz2StreamWriteTest(Bz2Test, StreamWriteTest): decompressor = bz2.BZ2Decompressor if bz2 else None diff --git a/Misc/ACKS b/Misc/ACKS index 35cabc7248bb4..b0246402c354d 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -237,6 +237,7 @@ Colm Buckley Erik de Bueger Jan-Hein B?hrman Lars Buitinck +Artem Bulgakov Dick Bulterman Bill Bumgarner Jimmy Burgett diff --git a/Misc/NEWS.d/next/Library/2020-07-28-12-08-58.bpo-41316.bSCbK4.rst b/Misc/NEWS.d/next/Library/2020-07-28-12-08-58.bpo-41316.bSCbK4.rst new file mode 100644 index 0000000000000..139a170866ed4 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-07-28-12-08-58.bpo-41316.bSCbK4.rst @@ -0,0 +1 @@ +Fix the :mod:`tarfile` module to write only basename of TAR file to GZIP compression header. \ No newline at end of file From webhook-mailer at python.org Wed Oct 21 01:45:18 2020 From: webhook-mailer at python.org (methane) Date: Wed, 21 Oct 2020 05:45:18 -0000 Subject: [Python-checkins] Doc: Remove old Python version from future stmt (GH-21802) Message-ID: https://github.com/python/cpython/commit/eba109a28f48b20b05f08abbe6604eebb07788f9 commit: eba109a28f48b20b05f08abbe6604eebb07788f9 branch: master author: Andre Delfino committer: methane date: 2020-10-21T14:45:13+09:00 summary: Doc: Remove old Python version from future stmt (GH-21802) files: M Doc/reference/simple_stmts.rst diff --git a/Doc/reference/simple_stmts.rst b/Doc/reference/simple_stmts.rst index 93be32713ff32..f8ab2e918c6a1 100644 --- a/Doc/reference/simple_stmts.rst +++ b/Doc/reference/simple_stmts.rst @@ -874,8 +874,8 @@ can appear before a future statement are: * blank lines, and * other future statements. -The only feature in Python 3.7 that requires using the future statement is -``annotations``. +The only feature that requires using the future statement is +``annotations`` (see :pep:`563`). All historical features enabled by the future statement are still recognized by Python 3. The list includes ``absolute_import``, ``division``, From webhook-mailer at python.org Wed Oct 21 01:54:45 2020 From: webhook-mailer at python.org (miss-islington) Date: Wed, 21 Oct 2020 05:54:45 -0000 Subject: [Python-checkins] Doc: Remove old Python version from future stmt (GH-21802) Message-ID: https://github.com/python/cpython/commit/490f30dacdd4909aa055b800f5ac9dd0f57d56b8 commit: 490f30dacdd4909aa055b800f5ac9dd0f57d56b8 branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-20T22:54:36-07:00 summary: Doc: Remove old Python version from future stmt (GH-21802) (cherry picked from commit eba109a28f48b20b05f08abbe6604eebb07788f9) Co-authored-by: Andre Delfino files: M Doc/reference/simple_stmts.rst diff --git a/Doc/reference/simple_stmts.rst b/Doc/reference/simple_stmts.rst index 93be32713ff32..f8ab2e918c6a1 100644 --- a/Doc/reference/simple_stmts.rst +++ b/Doc/reference/simple_stmts.rst @@ -874,8 +874,8 @@ can appear before a future statement are: * blank lines, and * other future statements. -The only feature in Python 3.7 that requires using the future statement is -``annotations``. +The only feature that requires using the future statement is +``annotations`` (see :pep:`563`). All historical features enabled by the future statement are still recognized by Python 3. The list includes ``absolute_import``, ``division``, From webhook-mailer at python.org Wed Oct 21 02:06:41 2020 From: webhook-mailer at python.org (miss-islington) Date: Wed, 21 Oct 2020 06:06:41 -0000 Subject: [Python-checkins] Doc: Remove old Python version from future stmt (GH-21802) Message-ID: https://github.com/python/cpython/commit/9e935adf74d134d136820ff75838ac2193d2bb55 commit: 9e935adf74d134d136820ff75838ac2193d2bb55 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-20T23:06:29-07:00 summary: Doc: Remove old Python version from future stmt (GH-21802) (cherry picked from commit eba109a28f48b20b05f08abbe6604eebb07788f9) Co-authored-by: Andre Delfino files: M Doc/reference/simple_stmts.rst diff --git a/Doc/reference/simple_stmts.rst b/Doc/reference/simple_stmts.rst index 93be32713ff32..f8ab2e918c6a1 100644 --- a/Doc/reference/simple_stmts.rst +++ b/Doc/reference/simple_stmts.rst @@ -874,8 +874,8 @@ can appear before a future statement are: * blank lines, and * other future statements. -The only feature in Python 3.7 that requires using the future statement is -``annotations``. +The only feature that requires using the future statement is +``annotations`` (see :pep:`563`). All historical features enabled by the future statement are still recognized by Python 3. The list includes ``absolute_import``, ``division``, From webhook-mailer at python.org Wed Oct 21 04:11:23 2020 From: webhook-mailer at python.org (methane) Date: Wed, 21 Oct 2020 08:11:23 -0000 Subject: [Python-checkins] Doc: Do not encourage using a base class name in a derived class (GH-22177) Message-ID: https://github.com/python/cpython/commit/f72101bb62d587bfe6c74cc9dd302a354520e964 commit: f72101bb62d587bfe6c74cc9dd302a354520e964 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: methane date: 2020-10-21T17:11:13+09:00 summary: Doc: Do not encourage using a base class name in a derived class (GH-22177) (cherry picked from commit 4642ccd1c3e460cb2746d3f2095f1c1d1bafa4fe) Co-authored-by: Andre Delfino files: M Doc/faq/programming.rst diff --git a/Doc/faq/programming.rst b/Doc/faq/programming.rst index 106450fb78670..1d1155953e12f 100644 --- a/Doc/faq/programming.rst +++ b/Doc/faq/programming.rst @@ -1518,18 +1518,18 @@ provide the ``self`` argument. How can I organize my code to make it easier to change the base class? ---------------------------------------------------------------------- -You could define an alias for the base class, assign the real base class to it -before your class definition, and use the alias throughout your class. Then all +You could assign the base class to an alias and derive from the alias. Then all you have to change is the value assigned to the alias. Incidentally, this trick is also handy if you want to decide dynamically (e.g. depending on availability of resources) which base class to use. Example:: - BaseAlias = + class Base: + ... + + BaseAlias = Base class Derived(BaseAlias): - def meth(self): - BaseAlias.meth(self) - ... + ... How do I create static class data and static class methods? From webhook-mailer at python.org Wed Oct 21 04:25:18 2020 From: webhook-mailer at python.org (methane) Date: Wed, 21 Oct 2020 08:25:18 -0000 Subject: [Python-checkins] Doc: Do not suggest `s[::-1]` for reversed order (GH-22457) Message-ID: https://github.com/python/cpython/commit/fb2e94692e3a8eb66915575f4a122d56fe8999a0 commit: fb2e94692e3a8eb66915575f4a122d56fe8999a0 branch: master author: Andre Delfino committer: methane date: 2020-10-21T17:25:07+09:00 summary: Doc: Do not suggest `s[::-1]` for reversed order (GH-22457) files: M Doc/faq/programming.rst M Doc/tools/susp-ignored.csv diff --git a/Doc/faq/programming.rst b/Doc/faq/programming.rst index 57ab3e265f791..b75c60a17a269 100644 --- a/Doc/faq/programming.rst +++ b/Doc/faq/programming.rst @@ -1116,7 +1116,7 @@ trailing newline from a string. How do I iterate over a sequence in reverse order? -------------------------------------------------- -Use the :func:`reversed` built-in function, which is new in Python 2.4:: +Use the :func:`reversed` built-in function:: for x in reversed(sequence): ... # do something with x ... @@ -1124,11 +1124,6 @@ Use the :func:`reversed` built-in function, which is new in Python 2.4:: This won't touch your original sequence, but build a new copy with reversed order to iterate over. -With Python 2.3, you can use an extended slice syntax:: - - for x in sequence[::-1]: - ... # do something with x ... - How do you remove duplicates from a list? ----------------------------------------- diff --git a/Doc/tools/susp-ignored.csv b/Doc/tools/susp-ignored.csv index f85d6946954d6..99d6decc4ece1 100644 --- a/Doc/tools/susp-ignored.csv +++ b/Doc/tools/susp-ignored.csv @@ -12,7 +12,6 @@ extending/extending,,:myfunction,"PyArg_ParseTuple(args, ""D:myfunction"", &c);" extending/extending,,:set,"if (PyArg_ParseTuple(args, ""O:set_callback"", &temp)) {" extending/newtypes,,:call,"if (!PyArg_ParseTuple(args, ""sss:call"", &arg1, &arg2, &arg3)) {" faq/programming,,:chr,">=4.0) or 1+f(xc,yc,x*x-y*y+xc,2.0*x*y+yc,k-1,f):f(xc,yc,x,y,k,f):chr(" -faq/programming,,::,for x in sequence[::-1]: faq/programming,,:reduce,"print((lambda Ru,Ro,Iu,Io,IM,Sx,Sy:reduce(lambda x,y:x+y,map(lambda y," faq/programming,,:reduce,"Sx=Sx,Sy=Sy:reduce(lambda x,y:x+y,map(lambda x,xc=Ru,yc=yc,Ru=Ru,Ro=Ro," faq/windows,,:d48eceb,"Python 3.6.4 (v3.6.4:d48eceb, Dec 19 2017, 06:04:45) [MSC v.1900 32 bit (Intel)] on win32" From webhook-mailer at python.org Wed Oct 21 04:34:47 2020 From: webhook-mailer at python.org (miss-islington) Date: Wed, 21 Oct 2020 08:34:47 -0000 Subject: [Python-checkins] Doc: Do not suggest `s[::-1]` for reversed order (GH-22457) Message-ID: https://github.com/python/cpython/commit/62eb403ad5caa4b7cc2b105699373254b2908ebb commit: 62eb403ad5caa4b7cc2b105699373254b2908ebb branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-21T01:34:36-07:00 summary: Doc: Do not suggest `s[::-1]` for reversed order (GH-22457) (cherry picked from commit fb2e94692e3a8eb66915575f4a122d56fe8999a0) Co-authored-by: Andre Delfino files: M Doc/faq/programming.rst M Doc/tools/susp-ignored.csv diff --git a/Doc/faq/programming.rst b/Doc/faq/programming.rst index a64e8aca94468..b16b8c8d38059 100644 --- a/Doc/faq/programming.rst +++ b/Doc/faq/programming.rst @@ -1121,7 +1121,7 @@ trailing newline from a string. How do I iterate over a sequence in reverse order? -------------------------------------------------- -Use the :func:`reversed` built-in function, which is new in Python 2.4:: +Use the :func:`reversed` built-in function:: for x in reversed(sequence): ... # do something with x ... @@ -1129,11 +1129,6 @@ Use the :func:`reversed` built-in function, which is new in Python 2.4:: This won't touch your original sequence, but build a new copy with reversed order to iterate over. -With Python 2.3, you can use an extended slice syntax:: - - for x in sequence[::-1]: - ... # do something with x ... - How do you remove duplicates from a list? ----------------------------------------- diff --git a/Doc/tools/susp-ignored.csv b/Doc/tools/susp-ignored.csv index 33fbaf69c3fbd..dd6aa38d72adc 100644 --- a/Doc/tools/susp-ignored.csv +++ b/Doc/tools/susp-ignored.csv @@ -12,7 +12,6 @@ extending/extending,,:myfunction,"PyArg_ParseTuple(args, ""D:myfunction"", &c);" extending/extending,,:set,"if (PyArg_ParseTuple(args, ""O:set_callback"", &temp)) {" extending/newtypes,,:call,"if (!PyArg_ParseTuple(args, ""sss:call"", &arg1, &arg2, &arg3)) {" faq/programming,,:chr,">=4.0) or 1+f(xc,yc,x*x-y*y+xc,2.0*x*y+yc,k-1,f):f(xc,yc,x,y,k,f):chr(" -faq/programming,,::,for x in sequence[::-1]: faq/programming,,:reduce,"print((lambda Ru,Ro,Iu,Io,IM,Sx,Sy:reduce(lambda x,y:x+y,map(lambda y," faq/programming,,:reduce,"Sx=Sx,Sy=Sy:reduce(lambda x,y:x+y,map(lambda x,xc=Ru,yc=yc,Ru=Ru,Ro=Ro," faq/windows,,:d48eceb,"Python 3.6.4 (v3.6.4:d48eceb, Dec 19 2017, 06:04:45) [MSC v.1900 32 bit (Intel)] on win32" From webhook-mailer at python.org Wed Oct 21 04:36:09 2020 From: webhook-mailer at python.org (methane) Date: Wed, 21 Oct 2020 08:36:09 -0000 Subject: [Python-checkins] Doc: Fix a typo/error in the docs for cached bytecode (GH-22445) Message-ID: https://github.com/python/cpython/commit/cb115e36e1aba04b90b0ecac6f043e60064ac65b commit: cb115e36e1aba04b90b0ecac6f043e60064ac65b branch: master author: Zackery Spytz committer: methane date: 2020-10-21T17:36:03+09:00 summary: Doc: Fix a typo/error in the docs for cached bytecode (GH-22445) files: M Doc/reference/import.rst diff --git a/Doc/reference/import.rst b/Doc/reference/import.rst index 4c36e15dc0606..213b31487ac61 100644 --- a/Doc/reference/import.rst +++ b/Doc/reference/import.rst @@ -680,7 +680,7 @@ Here are the exact rules used: Cached bytecode invalidation ---------------------------- -Before Python loads cached bytecode from ``.pyc`` file, it checks whether the +Before Python loads cached bytecode from a ``.pyc`` file, it checks whether the cache is up-to-date with the source ``.py`` file. By default, Python does this by storing the source's last-modified timestamp and size in the cache file when writing it. At runtime, the import system then validates the cache file by From webhook-mailer at python.org Wed Oct 21 04:46:34 2020 From: webhook-mailer at python.org (miss-islington) Date: Wed, 21 Oct 2020 08:46:34 -0000 Subject: [Python-checkins] Doc: Fix a typo/error in the docs for cached bytecode (GH-22445) Message-ID: https://github.com/python/cpython/commit/a8e6af798e6bfc0d9479a4d16e2bc8cc1a4ad283 commit: a8e6af798e6bfc0d9479a4d16e2bc8cc1a4ad283 branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-21T01:46:22-07:00 summary: Doc: Fix a typo/error in the docs for cached bytecode (GH-22445) (cherry picked from commit cb115e36e1aba04b90b0ecac6f043e60064ac65b) Co-authored-by: Zackery Spytz files: M Doc/reference/import.rst diff --git a/Doc/reference/import.rst b/Doc/reference/import.rst index ca232007dd7f0..594bb78e9ecf6 100644 --- a/Doc/reference/import.rst +++ b/Doc/reference/import.rst @@ -680,7 +680,7 @@ Here are the exact rules used: Cached bytecode invalidation ---------------------------- -Before Python loads cached bytecode from ``.pyc`` file, it checks whether the +Before Python loads cached bytecode from a ``.pyc`` file, it checks whether the cache is up-to-date with the source ``.py`` file. By default, Python does this by storing the source's last-modified timestamp and size in the cache file when writing it. At runtime, the import system then validates the cache file by From webhook-mailer at python.org Wed Oct 21 04:47:59 2020 From: webhook-mailer at python.org (miss-islington) Date: Wed, 21 Oct 2020 08:47:59 -0000 Subject: [Python-checkins] Doc: Do not suggest `s[::-1]` for reversed order (GH-22457) Message-ID: https://github.com/python/cpython/commit/03bfb07937cbb91f3dcdbc5f6f644b481e331a49 commit: 03bfb07937cbb91f3dcdbc5f6f644b481e331a49 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-21T01:47:54-07:00 summary: Doc: Do not suggest `s[::-1]` for reversed order (GH-22457) (cherry picked from commit fb2e94692e3a8eb66915575f4a122d56fe8999a0) Co-authored-by: Andre Delfino files: M Doc/faq/programming.rst M Doc/tools/susp-ignored.csv diff --git a/Doc/faq/programming.rst b/Doc/faq/programming.rst index 1d1155953e12f..8df62c5604597 100644 --- a/Doc/faq/programming.rst +++ b/Doc/faq/programming.rst @@ -1116,7 +1116,7 @@ trailing newline from a string. How do I iterate over a sequence in reverse order? -------------------------------------------------- -Use the :func:`reversed` built-in function, which is new in Python 2.4:: +Use the :func:`reversed` built-in function:: for x in reversed(sequence): ... # do something with x ... @@ -1124,11 +1124,6 @@ Use the :func:`reversed` built-in function, which is new in Python 2.4:: This won't touch your original sequence, but build a new copy with reversed order to iterate over. -With Python 2.3, you can use an extended slice syntax:: - - for x in sequence[::-1]: - ... # do something with x ... - How do you remove duplicates from a list? ----------------------------------------- diff --git a/Doc/tools/susp-ignored.csv b/Doc/tools/susp-ignored.csv index 7be8d0abd69a5..e03c68fa98a0b 100644 --- a/Doc/tools/susp-ignored.csv +++ b/Doc/tools/susp-ignored.csv @@ -12,7 +12,6 @@ extending/extending,,:myfunction,"PyArg_ParseTuple(args, ""D:myfunction"", &c);" extending/extending,,:set,"if (PyArg_ParseTuple(args, ""O:set_callback"", &temp)) {" extending/newtypes,,:call,"if (!PyArg_ParseTuple(args, ""sss:call"", &arg1, &arg2, &arg3)) {" faq/programming,,:chr,">=4.0) or 1+f(xc,yc,x*x-y*y+xc,2.0*x*y+yc,k-1,f):f(xc,yc,x,y,k,f):chr(" -faq/programming,,::,for x in sequence[::-1]: faq/programming,,:reduce,"print((lambda Ru,Ro,Iu,Io,IM,Sx,Sy:reduce(lambda x,y:x+y,map(lambda y," faq/programming,,:reduce,"Sx=Sx,Sy=Sy:reduce(lambda x,y:x+y,map(lambda x,xc=Ru,yc=yc,Ru=Ru,Ro=Ro," faq/windows,,:d48eceb,"Python 3.6.4 (v3.6.4:d48eceb, Dec 19 2017, 06:04:45) [MSC v.1900 32 bit (Intel)] on win32" From webhook-mailer at python.org Wed Oct 21 04:55:43 2020 From: webhook-mailer at python.org (methane) Date: Wed, 21 Oct 2020 08:55:43 -0000 Subject: [Python-checkins] bpo-41819: Fix compiler warning in init_dump_ascii_wstr() (GH-22332) Message-ID: https://github.com/python/cpython/commit/c756c2b507b088919ac0c1aa8b0d8c8bdbdd75ee commit: c756c2b507b088919ac0c1aa8b0d8c8bdbdd75ee branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: methane date: 2020-10-21T17:55:38+09:00 summary: bpo-41819: Fix compiler warning in init_dump_ascii_wstr() (GH-22332) Fix the compiler warning: format specifies type `wint_t` (aka `int`) but the argument has type `unsigned int` (cherry picked from commit c322948892438a387d752ec18d1eb512699a4d67) Co-authored-by: Samuel Marks <807580+SamuelMarks at users.noreply.github.com> files: M Python/initconfig.c diff --git a/Python/initconfig.c b/Python/initconfig.c index b28e0a076468e..69711d8eab3fc 100644 --- a/Python/initconfig.c +++ b/Python/initconfig.c @@ -2593,7 +2593,7 @@ init_dump_ascii_wstr(const wchar_t *str) if (ch == L'\'') { PySys_WriteStderr("\\'"); } else if (0x20 <= ch && ch < 0x7f) { - PySys_WriteStderr("%lc", ch); + PySys_WriteStderr("%c", ch); } else if (ch <= 0xff) { PySys_WriteStderr("\\x%02x", ch); From webhook-mailer at python.org Wed Oct 21 04:57:42 2020 From: webhook-mailer at python.org (miss-islington) Date: Wed, 21 Oct 2020 08:57:42 -0000 Subject: [Python-checkins] Doc: Fix a typo/error in the docs for cached bytecode (GH-22445) Message-ID: https://github.com/python/cpython/commit/fcd776c6c201fa21e2596394bc9da296fb70f630 commit: fcd776c6c201fa21e2596394bc9da296fb70f630 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-21T01:57:37-07:00 summary: Doc: Fix a typo/error in the docs for cached bytecode (GH-22445) (cherry picked from commit cb115e36e1aba04b90b0ecac6f043e60064ac65b) Co-authored-by: Zackery Spytz files: M Doc/reference/import.rst diff --git a/Doc/reference/import.rst b/Doc/reference/import.rst index 4c36e15dc0606..213b31487ac61 100644 --- a/Doc/reference/import.rst +++ b/Doc/reference/import.rst @@ -680,7 +680,7 @@ Here are the exact rules used: Cached bytecode invalidation ---------------------------- -Before Python loads cached bytecode from ``.pyc`` file, it checks whether the +Before Python loads cached bytecode from a ``.pyc`` file, it checks whether the cache is up-to-date with the source ``.py`` file. By default, Python does this by storing the source's last-modified timestamp and size in the cache file when writing it. At runtime, the import system then validates the cache file by From webhook-mailer at python.org Wed Oct 21 09:40:54 2020 From: webhook-mailer at python.org (miss-islington) Date: Wed, 21 Oct 2020 13:40:54 -0000 Subject: [Python-checkins] Update tzdata to 2020.3 (GH-22856) Message-ID: https://github.com/python/cpython/commit/9a1ad2cf02e52eccc3204e43752c8742d4c77f6b commit: 9a1ad2cf02e52eccc3204e43752c8742d4c77f6b branch: master author: Paul Ganssle committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-21T06:40:43-07:00 summary: Update tzdata to 2020.3 (GH-22856) TBH I had forgotten that we pin this ?, and it's been updated quite a few times since we added this. files: M Misc/requirements-test.txt diff --git a/Misc/requirements-test.txt b/Misc/requirements-test.txt index 6e46c12e4f9d1..60e7ed20a3d51 100644 --- a/Misc/requirements-test.txt +++ b/Misc/requirements-test.txt @@ -1 +1 @@ -tzdata==2020.1rc0 +tzdata==2020.3 From webhook-mailer at python.org Wed Oct 21 09:49:31 2020 From: webhook-mailer at python.org (ericvsmith) Date: Wed, 21 Oct 2020 13:49:31 -0000 Subject: [Python-checkins] bpo-41747: Ensure all dataclass methods uses their parents' qualname (GH-22155) Message-ID: https://github.com/python/cpython/commit/c7437e2c0216e05fbf17bf96294cb20954e36e48 commit: c7437e2c0216e05fbf17bf96294cb20954e36e48 branch: master author: Batuhan Taskaya committer: ericvsmith date: 2020-10-21T09:49:22-04:00 summary: bpo-41747: Ensure all dataclass methods uses their parents' qualname (GH-22155) * bpo-41747: Ensure all dataclass methods uses their parents' qualname Co-authored-by: Serhiy Storchaka files: A Misc/NEWS.d/next/Library/2020-09-08-23-41-29.bpo-41747.M6wLKv.rst M Lib/dataclasses.py M Lib/test/test_dataclasses.py diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py index adfb9b7240b9f..0c4b47564b060 100644 --- a/Lib/dataclasses.py +++ b/Lib/dataclasses.py @@ -8,7 +8,7 @@ import functools import abc import _thread -from types import GenericAlias +from types import FunctionType, GenericAlias __all__ = ['dataclass', @@ -757,12 +757,19 @@ def _get_field(cls, a_name, a_type): return f +def _set_qualname(cls, value): + # Ensure that the functions returned from _create_fn uses the proper + # __qualname__ (the class they belong to). + if isinstance(value, FunctionType): + value.__qualname__ = f"{cls.__qualname__}.{value.__name__}" + return value def _set_new_attribute(cls, name, value): # Never overwrites an existing attribute. Returns True if the # attribute already exists. if name in cls.__dict__: return True + _set_qualname(cls, value) setattr(cls, name, value) return False @@ -777,7 +784,7 @@ def _hash_set_none(cls, fields, globals): def _hash_add(cls, fields, globals): flds = [f for f in fields if (f.compare if f.hash is None else f.hash)] - return _hash_fn(flds, globals) + return _set_qualname(cls, _hash_fn(flds, globals)) def _hash_exception(cls, fields, globals): # Raise an exception. diff --git a/Lib/test/test_dataclasses.py b/Lib/test/test_dataclasses.py index 7c1d9c568f4ef..8887eb6461bc3 100644 --- a/Lib/test/test_dataclasses.py +++ b/Lib/test/test_dataclasses.py @@ -1936,6 +1936,30 @@ class R: self.assertEqual(new_sample.x, another_new_sample.x) self.assertEqual(sample.y, another_new_sample.y) + def test_dataclasses_qualnames(self): + @dataclass(order=True, unsafe_hash=True, frozen=True) + class A: + x: int + y: int + + self.assertEqual(A.__init__.__name__, "__init__") + for function in ( + '__eq__', + '__lt__', + '__le__', + '__gt__', + '__ge__', + '__hash__', + '__init__', + '__repr__', + '__setattr__', + '__delattr__', + ): + self.assertEqual(getattr(A, function).__qualname__, f"TestCase.test_dataclasses_qualnames..A.{function}") + + with self.assertRaisesRegex(TypeError, r"A\.__init__\(\) missing"): + A() + class TestFieldNoAnnotation(unittest.TestCase): def test_field_without_annotation(self): diff --git a/Misc/NEWS.d/next/Library/2020-09-08-23-41-29.bpo-41747.M6wLKv.rst b/Misc/NEWS.d/next/Library/2020-09-08-23-41-29.bpo-41747.M6wLKv.rst new file mode 100644 index 0000000000000..0869462f5bf9d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-09-08-23-41-29.bpo-41747.M6wLKv.rst @@ -0,0 +1,3 @@ +Ensure all methods that generated from :func:`dataclasses.dataclass` +objects now have the proper ``__qualname__`` attribute referring to +the class they belong to. Patch by Batuhan Taskaya. From webhook-mailer at python.org Wed Oct 21 12:07:22 2020 From: webhook-mailer at python.org (miss-islington) Date: Wed, 21 Oct 2020 16:07:22 -0000 Subject: [Python-checkins] bpo-38439: Add 256px IDLE icon to the .ico, drop gifs from it (GH-19648) Message-ID: https://github.com/python/cpython/commit/871c6ccf4a883fbc05410284b21e6d668b832f2c commit: 871c6ccf4a883fbc05410284b21e6d668b832f2c branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-21T09:07:13-07:00 summary: bpo-38439: Add 256px IDLE icon to the .ico, drop gifs from it (GH-19648) (cherry picked from commit faddc7449d07e883b8ad8bb95dd68ce6dbdc06e8) Co-authored-by: Miro Hron?ok files: A Misc/NEWS.d/next/IDLE/2020-04-22-09-37-40.bpo-38439.ieXL-c.rst M Lib/idlelib/Icons/README.txt M Lib/idlelib/Icons/idle.ico diff --git a/Lib/idlelib/Icons/README.txt b/Lib/idlelib/Icons/README.txt index 8b471629ecb3e..d91c4d5d8d8cf 100644 --- a/Lib/idlelib/Icons/README.txt +++ b/Lib/idlelib/Icons/README.txt @@ -7,3 +7,7 @@ https://www.doxdesk.com/software/py/pyicons.html Various different formats and sizes are available at this GitHub Pull Request: https://github.com/python/cpython/pull/17473 + +The idle.ico file was created with ImageMagick: + + $ convert idle_16.png idle_32.png idle_48.png idle_256.png idle.ico diff --git a/Lib/idlelib/Icons/idle.ico b/Lib/idlelib/Icons/idle.ico index 3357aef14888c..2aa9a8300d9e2 100644 Binary files a/Lib/idlelib/Icons/idle.ico and b/Lib/idlelib/Icons/idle.ico differ diff --git a/Misc/NEWS.d/next/IDLE/2020-04-22-09-37-40.bpo-38439.ieXL-c.rst b/Misc/NEWS.d/next/IDLE/2020-04-22-09-37-40.bpo-38439.ieXL-c.rst new file mode 100644 index 0000000000000..d8d59015f20e3 --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2020-04-22-09-37-40.bpo-38439.ieXL-c.rst @@ -0,0 +1,2 @@ +Add a 256?256 pixel IDLE icon to the Windows .ico file. Created by Andrew +Clover. Remove the low-color gif variations from the .ico file. From webhook-mailer at python.org Wed Oct 21 12:21:05 2020 From: webhook-mailer at python.org (miss-islington) Date: Wed, 21 Oct 2020 16:21:05 -0000 Subject: [Python-checkins] [3.8] bpo-39524: Fixed doc-string in ast._pad_whitespace (GH-18340) (GH-22857) Message-ID: https://github.com/python/cpython/commit/f3982d666c1df290373a4810edd501b2176b45c7 commit: f3982d666c1df290373a4810edd501b2176b45c7 branch: 3.8 author: Irit Katriel committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-21T09:20:56-07:00 summary: [3.8] bpo-39524: Fixed doc-string in ast._pad_whitespace (GH-18340) (GH-22857) Automerge-Triggered-By: GH:zware files: M Lib/ast.py diff --git a/Lib/ast.py b/Lib/ast.py index d29db80a085fd..d197f68a97e18 100644 --- a/Lib/ast.py +++ b/Lib/ast.py @@ -285,7 +285,7 @@ def _splitlines_no_ff(source): def _pad_whitespace(source): - """Replace all chars except '\f\t' in a line with spaces.""" + r"""Replace all chars except '\f\t' in a line with spaces.""" result = '' for c in source: if c in '\f\t': From webhook-mailer at python.org Wed Oct 21 13:13:55 2020 From: webhook-mailer at python.org (miss-islington) Date: Wed, 21 Oct 2020 17:13:55 -0000 Subject: [Python-checkins] bpo-39416: Document some restrictions on the default string representations of numeric classes (GH-18111) Message-ID: https://github.com/python/cpython/commit/c60394c7fc9cc09b16e9675a3eeb5844b6d8523f commit: c60394c7fc9cc09b16e9675a3eeb5844b6d8523f branch: master author: kpinc committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-21T10:13:50-07:00 summary: bpo-39416: Document some restrictions on the default string representations of numeric classes (GH-18111) [bpo-39416](): Document string representations of the Numeric classes This is a change to the specification of the Python language. The idea here is to put sane minimal limits on the Python language's default representations of its Numeric classes. That way "Marty's Robotic Massage Parlor and Python Interpreter" implementation of Python won't do anything too crazy. Some discussion in the email thread: Subject: Documenting Python's float.__str__() https://mail.python.org/archives/list/python-dev at python.org/thread/FV22TKT3S2Q3P7PNN6MCXI6IX3HRRNAL/ files: A Misc/NEWS.d/next/Documentation/2020-01-22-05-14-53.bpo-39416.uYjhEm.rst M Doc/reference/datamodel.rst diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 29e64cba2ca3a..d9b72a9a942ed 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -187,6 +187,24 @@ Ellipsis related to mathematical numbers, but subject to the limitations of numerical representation in computers. + The string representations of the Numeric classes, computed by + :meth:`__repr__` and :meth:`__str__`, have the following + properties: + + * They are valid numeric literals which, when passed to their + class constructor, produce an object having the value of the + original numeric. + + * The representation is in base 10, when possible. + + * Leading zeros, possibly excepting a single zero before a + decimal point, are not shown. + + * Trailing zeros, possibly excepting a single zero after a + decimal point, are not shown. + + * A sign is shown only when the number is negative. + Python distinguishes between integers, floating point numbers, and complex numbers: diff --git a/Misc/NEWS.d/next/Documentation/2020-01-22-05-14-53.bpo-39416.uYjhEm.rst b/Misc/NEWS.d/next/Documentation/2020-01-22-05-14-53.bpo-39416.uYjhEm.rst new file mode 100644 index 0000000000000..279a5f18ff855 --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2020-01-22-05-14-53.bpo-39416.uYjhEm.rst @@ -0,0 +1 @@ +Document some restrictions on the default string representations of numeric classes. From webhook-mailer at python.org Wed Oct 21 14:56:26 2020 From: webhook-mailer at python.org (miss-islington) Date: Wed, 21 Oct 2020 18:56:26 -0000 Subject: [Python-checkins] [3.9] bpo-39416: Document some restrictions on the default string representations of numeric classes (GH-18111) (GH-22860) Message-ID: https://github.com/python/cpython/commit/ec62b47ebc8f29007942c8e9f3f260af91ca58cb commit: ec62b47ebc8f29007942c8e9f3f260af91ca58cb branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-21T11:56:16-07:00 summary: [3.9] bpo-39416: Document some restrictions on the default string representations of numeric classes (GH-18111) (GH-22860) [[bpo-39416]()](): Document string representations of the Numeric classes This is a change to the specification of the Python language. The idea here is to put sane minimal limits on the Python language's default representations of its Numeric classes. That way "Marty's Robotic Massage Parlor and Python Interpreter" implementation of Python won't do anything too crazy. Some discussion in the email thread: Subject: Documenting Python's float.__str__() https://mail.python.org/archives/list/python-dev at python.org/thread/FV22TKT3S2Q3P7PNN6MCXI6IX3HRRNAL/ (cherry picked from commit c60394c7fc9cc09b16e9675a3eeb5844b6d8523f) Co-authored-by: kpinc Automerge-Triggered-By: GH:merwok files: A Misc/NEWS.d/next/Documentation/2020-01-22-05-14-53.bpo-39416.uYjhEm.rst M Doc/reference/datamodel.rst diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index aaa161aad2773..c0e7edb2421b0 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -187,6 +187,24 @@ Ellipsis related to mathematical numbers, but subject to the limitations of numerical representation in computers. + The string representations of the Numeric classes, computed by + :meth:`__repr__` and :meth:`__str__`, have the following + properties: + + * They are valid numeric literals which, when passed to their + class constructor, produce an object having the value of the + original numeric. + + * The representation is in base 10, when possible. + + * Leading zeros, possibly excepting a single zero before a + decimal point, are not shown. + + * Trailing zeros, possibly excepting a single zero after a + decimal point, are not shown. + + * A sign is shown only when the number is negative. + Python distinguishes between integers, floating point numbers, and complex numbers: diff --git a/Misc/NEWS.d/next/Documentation/2020-01-22-05-14-53.bpo-39416.uYjhEm.rst b/Misc/NEWS.d/next/Documentation/2020-01-22-05-14-53.bpo-39416.uYjhEm.rst new file mode 100644 index 0000000000000..279a5f18ff855 --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2020-01-22-05-14-53.bpo-39416.uYjhEm.rst @@ -0,0 +1 @@ +Document some restrictions on the default string representations of numeric classes. From webhook-mailer at python.org Wed Oct 21 14:56:26 2020 From: webhook-mailer at python.org (miss-islington) Date: Wed, 21 Oct 2020 18:56:26 -0000 Subject: [Python-checkins] [3.8] bpo-39416: Document some restrictions on the default string representations of numeric classes (GH-18111) (GH-22861) Message-ID: https://github.com/python/cpython/commit/89fac4c3748aa7eb23d09922331e90a62ce782fd commit: 89fac4c3748aa7eb23d09922331e90a62ce782fd branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-21T11:56:20-07:00 summary: [3.8] bpo-39416: Document some restrictions on the default string representations of numeric classes (GH-18111) (GH-22861) [[bpo-39416]()](): Document string representations of the Numeric classes This is a change to the specification of the Python language. The idea here is to put sane minimal limits on the Python language's default representations of its Numeric classes. That way "Marty's Robotic Massage Parlor and Python Interpreter" implementation of Python won't do anything too crazy. Some discussion in the email thread: Subject: Documenting Python's float.__str__() https://mail.python.org/archives/list/python-dev at python.org/thread/FV22TKT3S2Q3P7PNN6MCXI6IX3HRRNAL/ (cherry picked from commit c60394c7fc9cc09b16e9675a3eeb5844b6d8523f) Co-authored-by: kpinc Automerge-Triggered-By: GH:merwok files: A Misc/NEWS.d/next/Documentation/2020-01-22-05-14-53.bpo-39416.uYjhEm.rst M Doc/reference/datamodel.rst diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index c5d8f3a009bc5..14c38453c3577 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -182,6 +182,24 @@ Ellipsis related to mathematical numbers, but subject to the limitations of numerical representation in computers. + The string representations of the Numeric classes, computed by + :meth:`__repr__` and :meth:`__str__`, have the following + properties: + + * They are valid numeric literals which, when passed to their + class constructor, produce an object having the value of the + original numeric. + + * The representation is in base 10, when possible. + + * Leading zeros, possibly excepting a single zero before a + decimal point, are not shown. + + * Trailing zeros, possibly excepting a single zero after a + decimal point, are not shown. + + * A sign is shown only when the number is negative. + Python distinguishes between integers, floating point numbers, and complex numbers: diff --git a/Misc/NEWS.d/next/Documentation/2020-01-22-05-14-53.bpo-39416.uYjhEm.rst b/Misc/NEWS.d/next/Documentation/2020-01-22-05-14-53.bpo-39416.uYjhEm.rst new file mode 100644 index 0000000000000..279a5f18ff855 --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2020-01-22-05-14-53.bpo-39416.uYjhEm.rst @@ -0,0 +1 @@ +Document some restrictions on the default string representations of numeric classes. From webhook-mailer at python.org Wed Oct 21 15:05:57 2020 From: webhook-mailer at python.org (miss-islington) Date: Wed, 21 Oct 2020 19:05:57 -0000 Subject: [Python-checkins] bpo-41959: Fix grammar around class asyncio.MultiLoopChildWatcher text (GH-22580) Message-ID: https://github.com/python/cpython/commit/caff2934f46510920a6169e192707d59e9c55f6b commit: caff2934f46510920a6169e192707d59e9c55f6b branch: master author: Ra?l Cumplido committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-21T12:05:48-07:00 summary: bpo-41959: Fix grammar around class asyncio.MultiLoopChildWatcher text (GH-22580) While translating the following document to Spanish we found there is a grammar issue on the original documentation. files: M Doc/library/asyncio-policy.rst diff --git a/Doc/library/asyncio-policy.rst b/Doc/library/asyncio-policy.rst index d9d3232d2408b..88e69ceff9adc 100644 --- a/Doc/library/asyncio-policy.rst +++ b/Doc/library/asyncio-policy.rst @@ -219,7 +219,7 @@ implementation used by the asyncio event loop: This implementation registers a :py:data:`SIGCHLD` signal handler on instantiation. That can break third-party code that installs a custom handler for - `SIGCHLD`. signal). + :py:data:`SIGCHLD` signal. The watcher avoids disrupting other code spawning processes by polling every process explicitly on a :py:data:`SIGCHLD` signal. From webhook-mailer at python.org Wed Oct 21 15:53:23 2020 From: webhook-mailer at python.org (lysnikolaou) Date: Wed, 21 Oct 2020 19:53:23 -0000 Subject: [Python-checkins] bpo-41746: Cast to typed seqs in CHECK macros to avoid type erasure (GH-22864) Message-ID: https://github.com/python/cpython/commit/2e5ca9e3f68b33abb7d2c66d22ffc18dec40641a commit: 2e5ca9e3f68b33abb7d2c66d22ffc18dec40641a branch: master author: Lysandros Nikolaou committer: lysnikolaou date: 2020-10-21T22:53:14+03:00 summary: bpo-41746: Cast to typed seqs in CHECK macros to avoid type erasure (GH-22864) files: M Grammar/python.gram M Parser/parser.c M Parser/pegen.h diff --git a/Grammar/python.gram b/Grammar/python.gram index 2f52bd7f2f6a4..d2d6fc0d3398b 100644 --- a/Grammar/python.gram +++ b/Grammar/python.gram @@ -36,11 +36,17 @@ fstring[expr_ty]: star_expressions # type_expressions allow */** but ignore them type_expressions[asdl_expr_seq*]: | a=','.expression+ ',' '*' b=expression ',' '**' c=expression { - (asdl_expr_seq*)_PyPegen_seq_append_to_end(p, CHECK(_PyPegen_seq_append_to_end(p, a, b)), c) } + (asdl_expr_seq*)_PyPegen_seq_append_to_end( + p, + CHECK(asdl_seq*, _PyPegen_seq_append_to_end(p, a, b)), + c) } | a=','.expression+ ',' '*' b=expression { (asdl_expr_seq*)_PyPegen_seq_append_to_end(p, a, b) } | a=','.expression+ ',' '**' b=expression { (asdl_expr_seq*)_PyPegen_seq_append_to_end(p, a, b) } | '*' a=expression ',' '**' b=expression { - (asdl_expr_seq*)_PyPegen_seq_append_to_end(p, CHECK(_PyPegen_singleton_seq(p, a)), b) } + (asdl_expr_seq*)_PyPegen_seq_append_to_end( + p, + CHECK(asdl_seq*, _PyPegen_singleton_seq(p, a)), + b) } | '*' a=expression { (asdl_expr_seq*)_PyPegen_singleton_seq(p, a) } | '**' a=expression { (asdl_expr_seq*)_PyPegen_singleton_seq(p, a) } | a[asdl_expr_seq*]=','.expression+ {a} @@ -50,7 +56,7 @@ statement[asdl_stmt_seq*]: a=compound_stmt { (asdl_stmt_seq*)_PyPegen_singleton_ statement_newline[asdl_stmt_seq*]: | a=compound_stmt NEWLINE { (asdl_stmt_seq*)_PyPegen_singleton_seq(p, a) } | simple_stmt - | NEWLINE { (asdl_stmt_seq*)_PyPegen_singleton_seq(p, CHECK(_Py_Pass(EXTRA))) } + | NEWLINE { (asdl_stmt_seq*)_PyPegen_singleton_seq(p, CHECK(stmt_ty, _Py_Pass(EXTRA))) } | ENDMARKER { _PyPegen_interactive_exit(p) } simple_stmt[asdl_stmt_seq*]: | a=small_stmt !';' NEWLINE { (asdl_stmt_seq*)_PyPegen_singleton_seq(p, a) } # Not needed, there for speedup @@ -84,13 +90,14 @@ compound_stmt[stmt_ty]: assignment[stmt_ty]: | a=NAME ':' b=expression c=['=' d=annotated_rhs { d }] { CHECK_VERSION( + stmt_ty, 6, "Variable annotation syntax is", - _Py_AnnAssign(CHECK(_PyPegen_set_expr_context(p, a, Store)), b, c, 1, EXTRA) + _Py_AnnAssign(CHECK(expr_ty, _PyPegen_set_expr_context(p, a, Store)), b, c, 1, EXTRA) ) } | a=('(' b=single_target ')' { b } | single_subscript_attribute_target) ':' b=expression c=['=' d=annotated_rhs { d }] { - CHECK_VERSION(6, "Variable annotations syntax is", _Py_AnnAssign(a, b, c, 0, EXTRA)) } + CHECK_VERSION(stmt_ty, 6, "Variable annotations syntax is", _Py_AnnAssign(a, b, c, 0, EXTRA)) } | a[asdl_expr_seq*]=(z=star_targets '=' { z })+ b=(yield_expr | star_expressions) !'=' tc=[TYPE_COMMENT] { _Py_Assign(a, b, NEW_TYPE_COMMENT(p, tc), EXTRA) } | a=single_target b=augassign ~ c=(yield_expr | star_expressions) { @@ -101,7 +108,7 @@ augassign[AugOperator*]: | '+=' { _PyPegen_augoperator(p, Add) } | '-=' { _PyPegen_augoperator(p, Sub) } | '*=' { _PyPegen_augoperator(p, Mult) } - | '@=' { CHECK_VERSION(5, "The '@' operator is", _PyPegen_augoperator(p, MatMult)) } + | '@=' { CHECK_VERSION(AugOperator*, 5, "The '@' operator is", _PyPegen_augoperator(p, MatMult)) } | '/=' { _PyPegen_augoperator(p, Div) } | '%=' { _PyPegen_augoperator(p, Mod) } | '&=' { _PyPegen_augoperator(p, BitAnd) } @@ -113,9 +120,9 @@ augassign[AugOperator*]: | '//=' { _PyPegen_augoperator(p, FloorDiv) } global_stmt[stmt_ty]: 'global' a[asdl_expr_seq*]=','.NAME+ { - _Py_Global(CHECK(_PyPegen_map_names_to_ids(p, a)), EXTRA) } + _Py_Global(CHECK(asdl_identifier_seq*, _PyPegen_map_names_to_ids(p, a)), EXTRA) } nonlocal_stmt[stmt_ty]: 'nonlocal' a[asdl_expr_seq*]=','.NAME+ { - _Py_Nonlocal(CHECK(_PyPegen_map_names_to_ids(p, a)), EXTRA) } + _Py_Nonlocal(CHECK(asdl_identifier_seq*, _PyPegen_map_names_to_ids(p, a)), EXTRA) } yield_stmt[stmt_ty]: y=yield_expr { _Py_Expr(y, EXTRA) } @@ -136,7 +143,7 @@ import_from[stmt_ty]: import_from_targets[asdl_alias_seq*]: | '(' a=import_from_as_names [','] ')' { a } | import_from_as_names !',' - | '*' { (asdl_alias_seq*)_PyPegen_singleton_seq(p, CHECK(_PyPegen_alias_for_star(p))) } + | '*' { (asdl_alias_seq*)_PyPegen_singleton_seq(p, CHECK(alias_ty, _PyPegen_alias_for_star(p))) } | invalid_import_from_targets import_from_as_names[asdl_alias_seq*]: | a[asdl_alias_seq*]=','.import_from_as_name+ { a } @@ -155,10 +162,12 @@ dotted_name[expr_ty]: | NAME if_stmt[stmt_ty]: - | 'if' a=named_expression ':' b=block c=elif_stmt { _Py_If(a, b, CHECK((asdl_stmt_seq*)_PyPegen_singleton_seq(p, c)), EXTRA) } + | 'if' a=named_expression ':' b=block c=elif_stmt { + _Py_If(a, b, CHECK(asdl_stmt_seq*, _PyPegen_singleton_seq(p, c)), EXTRA) } | 'if' a=named_expression ':' b=block c=[else_block] { _Py_If(a, b, c, EXTRA) } elif_stmt[stmt_ty]: - | 'elif' a=named_expression ':' b=block c=elif_stmt { _Py_If(a, b, CHECK(_PyPegen_singleton_seq(p, c)), EXTRA) } + | 'elif' a=named_expression ':' b=block c=elif_stmt { + _Py_If(a, b, CHECK(asdl_stmt_seq*, _PyPegen_singleton_seq(p, c)), EXTRA) } | 'elif' a=named_expression ':' b=block c=[else_block] { _Py_If(a, b, c, EXTRA) } else_block[asdl_stmt_seq*]: 'else' ':' b=block { b } @@ -169,7 +178,7 @@ for_stmt[stmt_ty]: | 'for' t=star_targets 'in' ~ ex=star_expressions ':' tc=[TYPE_COMMENT] b=block el=[else_block] { _Py_For(t, ex, b, el, NEW_TYPE_COMMENT(p, tc), EXTRA) } | ASYNC 'for' t=star_targets 'in' ~ ex=star_expressions ':' tc=[TYPE_COMMENT] b=block el=[else_block] { - CHECK_VERSION(5, "Async for loops are", _Py_AsyncFor(t, ex, b, el, NEW_TYPE_COMMENT(p, tc), EXTRA)) } + CHECK_VERSION(stmt_ty, 5, "Async for loops are", _Py_AsyncFor(t, ex, b, el, NEW_TYPE_COMMENT(p, tc), EXTRA)) } | invalid_for_target with_stmt[stmt_ty]: @@ -178,9 +187,9 @@ with_stmt[stmt_ty]: | 'with' a[asdl_withitem_seq*]=','.with_item+ ':' tc=[TYPE_COMMENT] b=block { _Py_With(a, b, NEW_TYPE_COMMENT(p, tc), EXTRA) } | ASYNC 'with' '(' a[asdl_withitem_seq*]=','.with_item+ ','? ')' ':' b=block { - CHECK_VERSION(5, "Async with statements are", _Py_AsyncWith(a, b, NULL, EXTRA)) } + CHECK_VERSION(stmt_ty, 5, "Async with statements are", _Py_AsyncWith(a, b, NULL, EXTRA)) } | ASYNC 'with' a[asdl_withitem_seq*]=','.with_item+ ':' tc=[TYPE_COMMENT] b=block { - CHECK_VERSION(5, "Async with statements are", _Py_AsyncWith(a, b, NEW_TYPE_COMMENT(p, tc), EXTRA)) } + CHECK_VERSION(stmt_ty, 5, "Async with statements are", _Py_AsyncWith(a, b, NEW_TYPE_COMMENT(p, tc), EXTRA)) } with_item[withitem_ty]: | e=expression 'as' t=star_target &(',' | ')' | ':') { _Py_withitem(e, t, p->arena) } | invalid_with_item @@ -209,14 +218,15 @@ function_def[stmt_ty]: function_def_raw[stmt_ty]: | 'def' n=NAME '(' params=[params] ')' a=['->' z=expression { z }] ':' tc=[func_type_comment] b=block { _Py_FunctionDef(n->v.Name.id, - (params) ? params : CHECK(_PyPegen_empty_arguments(p)), + (params) ? params : CHECK(arguments_ty, _PyPegen_empty_arguments(p)), b, NULL, a, NEW_TYPE_COMMENT(p, tc), EXTRA) } | ASYNC 'def' n=NAME '(' params=[params] ')' a=['->' z=expression { z }] ':' tc=[func_type_comment] b=block { CHECK_VERSION( + stmt_ty, 5, "Async functions are", _Py_AsyncFunctionDef(n->v.Name.id, - (params) ? params : CHECK(_PyPegen_empty_arguments(p)), + (params) ? params : CHECK(arguments_ty, _PyPegen_empty_arguments(p)), b, NULL, a, NEW_TYPE_COMMENT(p, tc), EXTRA) ) } func_type_comment[Token*]: @@ -304,8 +314,8 @@ block[asdl_stmt_seq*] (memo): expressions_list[asdl_expr_seq*]: a[asdl_expr_seq*]=','.star_expression+ [','] { a } star_expressions[expr_ty]: | a=star_expression b=(',' c=star_expression { c })+ [','] { - _Py_Tuple(CHECK(_PyPegen_seq_insert_in_front(p, a, b)), Load, EXTRA) } - | a=star_expression ',' { _Py_Tuple(CHECK(_PyPegen_singleton_seq(p, a)), Load, EXTRA) } + _Py_Tuple(CHECK(asdl_expr_seq*, _PyPegen_seq_insert_in_front(p, a, b)), Load, EXTRA) } + | a=star_expression ',' { _Py_Tuple(CHECK(asdl_expr_seq*, _PyPegen_singleton_seq(p, a)), Load, EXTRA) } | star_expression star_expression[expr_ty] (memo): | '*' a=bitwise_or { _Py_Starred(a, Load, EXTRA) } @@ -316,7 +326,7 @@ star_named_expression[expr_ty]: | '*' a=bitwise_or { _Py_Starred(a, Load, EXTRA) } | named_expression named_expression[expr_ty]: - | a=NAME ':=' ~ b=expression { _Py_NamedExpr(CHECK(_PyPegen_set_expr_context(p, a, Store)), b, EXTRA) } + | a=NAME ':=' ~ b=expression { _Py_NamedExpr(CHECK(expr_ty, _PyPegen_set_expr_context(p, a, Store)), b, EXTRA) } | expression !':=' | invalid_named_expression @@ -324,8 +334,8 @@ annotated_rhs[expr_ty]: yield_expr | star_expressions expressions[expr_ty]: | a=expression b=(',' c=expression { c })+ [','] { - _Py_Tuple(CHECK(_PyPegen_seq_insert_in_front(p, a, b)), Load, EXTRA) } - | a=expression ',' { _Py_Tuple(CHECK(_PyPegen_singleton_seq(p, a)), Load, EXTRA) } + _Py_Tuple(CHECK(asdl_expr_seq*, _PyPegen_seq_insert_in_front(p, a, b)), Load, EXTRA) } + | a=expression ',' { _Py_Tuple(CHECK(asdl_expr_seq*, _PyPegen_singleton_seq(p, a)), Load, EXTRA) } | expression expression[expr_ty] (memo): | a=disjunction 'if' b=disjunction 'else' c=expression { _Py_IfExp(b, a, c, EXTRA) } @@ -333,7 +343,8 @@ expression[expr_ty] (memo): | lambdef lambdef[expr_ty]: - | 'lambda' a=[lambda_params] ':' b=expression { _Py_Lambda((a) ? a : CHECK(_PyPegen_empty_arguments(p)), b, EXTRA) } + | 'lambda' a=[lambda_params] ':' b=expression { + _Py_Lambda((a) ? a : CHECK(arguments_ty, _PyPegen_empty_arguments(p)), b, EXTRA) } lambda_params[arguments_ty]: | invalid_lambda_parameters @@ -384,13 +395,13 @@ lambda_param[arg_ty]: a=NAME { _Py_arg(a->v.Name.id, NULL, NULL, EXTRA) } disjunction[expr_ty] (memo): | a=conjunction b=('or' c=conjunction { c })+ { _Py_BoolOp( Or, - CHECK(_PyPegen_seq_insert_in_front(p, a, b)), + CHECK(asdl_expr_seq*, _PyPegen_seq_insert_in_front(p, a, b)), EXTRA) } | conjunction conjunction[expr_ty] (memo): | a=inversion b=('and' c=inversion { c })+ { _Py_BoolOp( And, - CHECK(_PyPegen_seq_insert_in_front(p, a, b)), + CHECK(asdl_expr_seq*, _PyPegen_seq_insert_in_front(p, a, b)), EXTRA) } | inversion inversion[expr_ty] (memo): @@ -398,7 +409,11 @@ inversion[expr_ty] (memo): | comparison comparison[expr_ty]: | a=bitwise_or b=compare_op_bitwise_or_pair+ { - _Py_Compare(a, CHECK(_PyPegen_get_cmpops(p, b)), CHECK(_PyPegen_get_exprs(p, b)), EXTRA) } + _Py_Compare( + a, + CHECK(asdl_int_seq*, _PyPegen_get_cmpops(p, b)), + CHECK(asdl_expr_seq*, _PyPegen_get_exprs(p, b)), + EXTRA) } | bitwise_or compare_op_bitwise_or_pair[CmpopExprPair*]: | eq_bitwise_or @@ -446,7 +461,7 @@ term[expr_ty]: | a=term '/' b=factor { _Py_BinOp(a, Div, b, EXTRA) } | a=term '//' b=factor { _Py_BinOp(a, FloorDiv, b, EXTRA) } | a=term '%' b=factor { _Py_BinOp(a, Mod, b, EXTRA) } - | a=term '@' b=factor { CHECK_VERSION(5, "The '@' operator is", _Py_BinOp(a, MatMult, b, EXTRA)) } + | a=term '@' b=factor { CHECK_VERSION(expr_ty, 5, "The '@' operator is", _Py_BinOp(a, MatMult, b, EXTRA)) } | factor factor[expr_ty] (memo): | '+' a=factor { _Py_UnaryOp(UAdd, a, EXTRA) } @@ -457,11 +472,11 @@ power[expr_ty]: | a=await_primary '**' b=factor { _Py_BinOp(a, Pow, b, EXTRA) } | await_primary await_primary[expr_ty] (memo): - | AWAIT a=primary { CHECK_VERSION(5, "Await expressions are", _Py_Await(a, EXTRA)) } + | AWAIT a=primary { CHECK_VERSION(expr_ty, 5, "Await expressions are", _Py_Await(a, EXTRA)) } | primary primary[expr_ty]: | a=primary '.' b=NAME { _Py_Attribute(a, b->v.Name.id, Load, EXTRA) } - | a=primary b=genexp { _Py_Call(a, CHECK(_PyPegen_singleton_seq(p, b)), NULL, EXTRA) } + | a=primary b=genexp { _Py_Call(a, CHECK(asdl_expr_seq*, (asdl_expr_seq*)_PyPegen_singleton_seq(p, b)), NULL, EXTRA) } | a=primary '(' b=[arguments] ')' { _Py_Call(a, (b) ? ((expr_ty) b)->v.Call.args : NULL, @@ -509,7 +524,10 @@ setcomp[expr_ty]: | invalid_comprehension dict[expr_ty]: | '{' a=[double_starred_kvpairs] '}' { - _Py_Dict(CHECK(_PyPegen_get_keys(p, a)), CHECK(_PyPegen_get_values(p, a)), EXTRA) } + _Py_Dict( + CHECK(asdl_expr_seq*, _PyPegen_get_keys(p, a)), + CHECK(asdl_expr_seq*, _PyPegen_get_values(p, a)), + EXTRA) } dictcomp[expr_ty]: | '{' a=kvpair b=for_if_clauses '}' { _Py_DictComp(a->key, a->value, b, EXTRA) } | invalid_dict_comprehension @@ -522,7 +540,7 @@ for_if_clauses[asdl_comprehension_seq*]: | a[asdl_comprehension_seq*]=for_if_clause+ { a } for_if_clause[comprehension_ty]: | ASYNC 'for' a=star_targets 'in' ~ b=disjunction c[asdl_expr_seq*]=('if' z=disjunction { z })* { - CHECK_VERSION(6, "Async comprehensions are", _Py_comprehension(a, b, c, 1, p->arena)) } + CHECK_VERSION(comprehension_ty, 6, "Async comprehensions are", _Py_comprehension(a, b, c, 1, p->arena)) } | 'for' a=star_targets 'in' ~ b=disjunction c[asdl_expr_seq*]=('if' z=disjunction { z })* { _Py_comprehension(a, b, c, 0, p->arena) } | invalid_for_target @@ -537,8 +555,8 @@ arguments[expr_ty] (memo): args[expr_ty]: | a[asdl_expr_seq*]=','.(starred_expression | named_expression !'=')+ b=[',' k=kwargs {k}] { _PyPegen_collect_call_seqs(p, a, b, EXTRA) } | a=kwargs { _Py_Call(_PyPegen_dummy_name(p), - CHECK_NULL_ALLOWED(_PyPegen_seq_extract_starred_exprs(p, a)), - CHECK_NULL_ALLOWED(_PyPegen_seq_delete_starred_exprs(p, a)), + CHECK_NULL_ALLOWED(asdl_expr_seq*, _PyPegen_seq_extract_starred_exprs(p, a)), + CHECK_NULL_ALLOWED(asdl_keyword_seq*, _PyPegen_seq_delete_starred_exprs(p, a)), EXTRA) } kwargs[asdl_seq*]: | a=','.kwarg_or_starred+ ',' b=','.kwarg_or_double_starred+ { _PyPegen_join_sequences(p, a, b) } @@ -548,24 +566,24 @@ starred_expression[expr_ty]: | '*' a=expression { _Py_Starred(a, Load, EXTRA) } kwarg_or_starred[KeywordOrStarred*]: | a=NAME '=' b=expression { - _PyPegen_keyword_or_starred(p, CHECK(_Py_keyword(a->v.Name.id, b, EXTRA)), 1) } + _PyPegen_keyword_or_starred(p, CHECK(keyword_ty, _Py_keyword(a->v.Name.id, b, EXTRA)), 1) } | a=starred_expression { _PyPegen_keyword_or_starred(p, a, 0) } | invalid_kwarg kwarg_or_double_starred[KeywordOrStarred*]: | a=NAME '=' b=expression { - _PyPegen_keyword_or_starred(p, CHECK(_Py_keyword(a->v.Name.id, b, EXTRA)), 1) } - | '**' a=expression { _PyPegen_keyword_or_starred(p, CHECK(_Py_keyword(NULL, a, EXTRA)), 1) } + _PyPegen_keyword_or_starred(p, CHECK(keyword_ty, _Py_keyword(a->v.Name.id, b, EXTRA)), 1) } + | '**' a=expression { _PyPegen_keyword_or_starred(p, CHECK(keyword_ty, _Py_keyword(NULL, a, EXTRA)), 1) } | invalid_kwarg # NOTE: star_targets may contain *bitwise_or, targets may not. 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) } + _Py_Tuple(CHECK(asdl_expr_seq*, _PyPegen_seq_insert_in_front(p, a, b)), Store, EXTRA) } star_targets_seq[asdl_expr_seq*]: a[asdl_expr_seq*]=','.star_target+ [','] { a } star_target[expr_ty] (memo): | '*' a=(!'*' star_target) { - _Py_Starred(CHECK(_PyPegen_set_expr_context(p, a, Store)), Store, EXTRA) } + _Py_Starred(CHECK(expr_ty, _PyPegen_set_expr_context(p, a, Store)), Store, EXTRA) } | 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 @@ -602,7 +620,8 @@ target[expr_ty] (memo): t_primary[expr_ty]: | a=t_primary '.' b=NAME &t_lookahead { _Py_Attribute(a, b->v.Name.id, Load, EXTRA) } | a=t_primary '[' b=slices ']' &t_lookahead { _Py_Subscript(a, b, Load, EXTRA) } - | a=t_primary b=genexp &t_lookahead { _Py_Call(a, CHECK(_PyPegen_singleton_seq(p, b)), NULL, EXTRA) } + | a=t_primary b=genexp &t_lookahead { + _Py_Call(a, CHECK(asdl_expr_seq*, (asdl_expr_seq*)_PyPegen_singleton_seq(p, b)), NULL, EXTRA) } | a=t_primary '(' b=[arguments] ')' &t_lookahead { _Py_Call(a, (b) ? ((expr_ty) b)->v.Call.args : NULL, diff --git a/Parser/parser.c b/Parser/parser.c index 0d92256a3ebe2..09be03c633a26 100644 --- a/Parser/parser.c +++ b/Parser/parser.c @@ -978,7 +978,7 @@ type_expressions_rule(Parser *p) ) { D(fprintf(stderr, "%*c+ type_expressions[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.expression+ ',' '*' expression ',' '**' expression")); - _res = ( asdl_expr_seq * ) _PyPegen_seq_append_to_end ( p , CHECK ( _PyPegen_seq_append_to_end ( p , a , b ) ) , c ); + _res = ( asdl_expr_seq * ) _PyPegen_seq_append_to_end ( p , CHECK ( asdl_seq * , _PyPegen_seq_append_to_end ( p , a , b ) ) , c ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; D(p->level--); @@ -1080,7 +1080,7 @@ type_expressions_rule(Parser *p) ) { D(fprintf(stderr, "%*c+ type_expressions[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*' expression ',' '**' expression")); - _res = ( asdl_expr_seq * ) _PyPegen_seq_append_to_end ( p , CHECK ( _PyPegen_singleton_seq ( p , a ) ) , b ); + _res = ( asdl_expr_seq * ) _PyPegen_seq_append_to_end ( p , CHECK ( asdl_seq * , _PyPegen_singleton_seq ( p , a ) ) , b ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; D(p->level--); @@ -1369,7 +1369,7 @@ statement_newline_rule(Parser *p) UNUSED(_end_lineno); // Only used by EXTRA macro int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro - _res = ( asdl_stmt_seq * ) _PyPegen_singleton_seq ( p , CHECK ( _Py_Pass ( EXTRA ) ) ); + _res = ( asdl_stmt_seq * ) _PyPegen_singleton_seq ( p , CHECK ( stmt_ty , _Py_Pass ( EXTRA ) ) ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; D(p->level--); @@ -2077,7 +2077,7 @@ assignment_rule(Parser *p) UNUSED(_end_lineno); // Only used by EXTRA macro int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro - _res = CHECK_VERSION ( 6 , "Variable annotation syntax is" , _Py_AnnAssign ( CHECK ( _PyPegen_set_expr_context ( p , a , Store ) ) , b , c , 1 , EXTRA ) ); + _res = CHECK_VERSION ( stmt_ty , 6 , "Variable annotation syntax is" , _Py_AnnAssign ( CHECK ( expr_ty , _PyPegen_set_expr_context ( p , a , Store ) ) , b , c , 1 , EXTRA ) ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; D(p->level--); @@ -2119,7 +2119,7 @@ assignment_rule(Parser *p) UNUSED(_end_lineno); // Only used by EXTRA macro int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro - _res = CHECK_VERSION ( 6 , "Variable annotations syntax is" , _Py_AnnAssign ( a , b , c , 0 , EXTRA ) ); + _res = CHECK_VERSION ( stmt_ty , 6 , "Variable annotations syntax is" , _Py_AnnAssign ( a , b , c , 0 , EXTRA ) ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; D(p->level--); @@ -2351,7 +2351,7 @@ augassign_rule(Parser *p) ) { D(fprintf(stderr, "%*c+ augassign[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'@='")); - _res = CHECK_VERSION ( 5 , "The '@' operator is" , _PyPegen_augoperator ( p , MatMult ) ); + _res = CHECK_VERSION ( AugOperator * , 5 , "The '@' operator is" , _PyPegen_augoperator ( p , MatMult ) ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; D(p->level--); @@ -2629,7 +2629,7 @@ global_stmt_rule(Parser *p) UNUSED(_end_lineno); // Only used by EXTRA macro int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro - _res = _Py_Global ( CHECK ( _PyPegen_map_names_to_ids ( p , a ) ) , EXTRA ); + _res = _Py_Global ( CHECK ( asdl_identifier_seq * , _PyPegen_map_names_to_ids ( p , a ) ) , EXTRA ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; D(p->level--); @@ -2691,7 +2691,7 @@ nonlocal_stmt_rule(Parser *p) UNUSED(_end_lineno); // Only used by EXTRA macro int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro - _res = _Py_Nonlocal ( CHECK ( _PyPegen_map_names_to_ids ( p , a ) ) , EXTRA ); + _res = _Py_Nonlocal ( CHECK ( asdl_identifier_seq * , _PyPegen_map_names_to_ids ( p , a ) ) , EXTRA ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; D(p->level--); @@ -3230,7 +3230,7 @@ import_from_targets_rule(Parser *p) ) { D(fprintf(stderr, "%*c+ import_from_targets[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*'")); - _res = ( asdl_alias_seq * ) _PyPegen_singleton_seq ( p , CHECK ( _PyPegen_alias_for_star ( p ) ) ); + _res = ( asdl_alias_seq * ) _PyPegen_singleton_seq ( p , CHECK ( alias_ty , _PyPegen_alias_for_star ( p ) ) ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; D(p->level--); @@ -3588,7 +3588,7 @@ if_stmt_rule(Parser *p) UNUSED(_end_lineno); // Only used by EXTRA macro int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro - _res = _Py_If ( a , b , CHECK ( ( asdl_stmt_seq * ) _PyPegen_singleton_seq ( p , c ) ) , EXTRA ); + _res = _Py_If ( a , b , CHECK ( asdl_stmt_seq * , _PyPegen_singleton_seq ( p , c ) ) , EXTRA ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; D(p->level--); @@ -3706,7 +3706,7 @@ elif_stmt_rule(Parser *p) UNUSED(_end_lineno); // Only used by EXTRA macro int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro - _res = _Py_If ( a , b , CHECK ( _PyPegen_singleton_seq ( p , c ) ) , EXTRA ); + _res = _Py_If ( a , b , CHECK ( asdl_stmt_seq * , _PyPegen_singleton_seq ( p , c ) ) , EXTRA ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; D(p->level--); @@ -4019,7 +4019,7 @@ for_stmt_rule(Parser *p) UNUSED(_end_lineno); // Only used by EXTRA macro int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro - _res = CHECK_VERSION ( 5 , "Async for loops are" , _Py_AsyncFor ( t , ex , b , el , NEW_TYPE_COMMENT ( p , tc ) , EXTRA ) ); + _res = CHECK_VERSION ( stmt_ty , 5 , "Async for loops are" , _Py_AsyncFor ( t , ex , b , el , NEW_TYPE_COMMENT ( p , tc ) , EXTRA ) ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; D(p->level--); @@ -4224,7 +4224,7 @@ with_stmt_rule(Parser *p) UNUSED(_end_lineno); // Only used by EXTRA macro int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro - _res = CHECK_VERSION ( 5 , "Async with statements are" , _Py_AsyncWith ( a , b , NULL , EXTRA ) ); + _res = CHECK_VERSION ( stmt_ty , 5 , "Async with statements are" , _Py_AsyncWith ( a , b , NULL , EXTRA ) ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; D(p->level--); @@ -4272,7 +4272,7 @@ with_stmt_rule(Parser *p) UNUSED(_end_lineno); // Only used by EXTRA macro int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro - _res = CHECK_VERSION ( 5 , "Async with statements are" , _Py_AsyncWith ( a , b , NEW_TYPE_COMMENT ( p , tc ) , EXTRA ) ); + _res = CHECK_VERSION ( stmt_ty , 5 , "Async with statements are" , _Py_AsyncWith ( a , b , NEW_TYPE_COMMENT ( p , tc ) , EXTRA ) ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; D(p->level--); @@ -4950,7 +4950,7 @@ function_def_raw_rule(Parser *p) UNUSED(_end_lineno); // Only used by EXTRA macro int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro - _res = _Py_FunctionDef ( n -> v . Name . id , ( params ) ? params : CHECK ( _PyPegen_empty_arguments ( p ) ) , b , NULL , a , NEW_TYPE_COMMENT ( p , tc ) , EXTRA ); + _res = _Py_FunctionDef ( n -> v . Name . id , ( params ) ? params : CHECK ( arguments_ty , _PyPegen_empty_arguments ( p ) ) , b , NULL , a , NEW_TYPE_COMMENT ( p , tc ) , EXTRA ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; D(p->level--); @@ -5010,7 +5010,7 @@ function_def_raw_rule(Parser *p) UNUSED(_end_lineno); // Only used by EXTRA macro int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro - _res = CHECK_VERSION ( 5 , "Async functions are" , _Py_AsyncFunctionDef ( n -> v . Name . id , ( params ) ? params : CHECK ( _PyPegen_empty_arguments ( p ) ) , b , NULL , a , NEW_TYPE_COMMENT ( p , tc ) , EXTRA ) ); + _res = CHECK_VERSION ( stmt_ty , 5 , "Async functions are" , _Py_AsyncFunctionDef ( n -> v . Name . id , ( params ) ? params : CHECK ( arguments_ty , _PyPegen_empty_arguments ( p ) ) , b , NULL , a , NEW_TYPE_COMMENT ( p , tc ) , EXTRA ) ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; D(p->level--); @@ -6426,7 +6426,7 @@ star_expressions_rule(Parser *p) UNUSED(_end_lineno); // Only used by EXTRA macro int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro - _res = _Py_Tuple ( CHECK ( _PyPegen_seq_insert_in_front ( p , a , b ) ) , Load , EXTRA ); + _res = _Py_Tuple ( CHECK ( asdl_expr_seq * , _PyPegen_seq_insert_in_front ( p , a , b ) ) , Load , EXTRA ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; D(p->level--); @@ -6462,7 +6462,7 @@ star_expressions_rule(Parser *p) UNUSED(_end_lineno); // Only used by EXTRA macro int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro - _res = _Py_Tuple ( CHECK ( _PyPegen_singleton_seq ( p , a ) ) , Load , EXTRA ); + _res = _Py_Tuple ( CHECK ( asdl_expr_seq * , _PyPegen_singleton_seq ( p , a ) ) , Load , EXTRA ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; D(p->level--); @@ -6761,7 +6761,7 @@ named_expression_rule(Parser *p) UNUSED(_end_lineno); // Only used by EXTRA macro int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro - _res = _Py_NamedExpr ( CHECK ( _PyPegen_set_expr_context ( p , a , Store ) ) , b , EXTRA ); + _res = _Py_NamedExpr ( CHECK ( expr_ty , _PyPegen_set_expr_context ( p , a , Store ) ) , b , EXTRA ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; D(p->level--); @@ -6926,7 +6926,7 @@ expressions_rule(Parser *p) UNUSED(_end_lineno); // Only used by EXTRA macro int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro - _res = _Py_Tuple ( CHECK ( _PyPegen_seq_insert_in_front ( p , a , b ) ) , Load , EXTRA ); + _res = _Py_Tuple ( CHECK ( asdl_expr_seq * , _PyPegen_seq_insert_in_front ( p , a , b ) ) , Load , EXTRA ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; D(p->level--); @@ -6962,7 +6962,7 @@ expressions_rule(Parser *p) UNUSED(_end_lineno); // Only used by EXTRA macro int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro - _res = _Py_Tuple ( CHECK ( _PyPegen_singleton_seq ( p , a ) ) , Load , EXTRA ); + _res = _Py_Tuple ( CHECK ( asdl_expr_seq * , _PyPegen_singleton_seq ( p , a ) ) , Load , EXTRA ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; D(p->level--); @@ -7163,7 +7163,7 @@ lambdef_rule(Parser *p) UNUSED(_end_lineno); // Only used by EXTRA macro int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro - _res = _Py_Lambda ( ( a ) ? a : CHECK ( _PyPegen_empty_arguments ( p ) ) , b , EXTRA ); + _res = _Py_Lambda ( ( a ) ? a : CHECK ( arguments_ty , _PyPegen_empty_arguments ( p ) ) , b , EXTRA ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; D(p->level--); @@ -8067,7 +8067,7 @@ disjunction_rule(Parser *p) UNUSED(_end_lineno); // Only used by EXTRA macro int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro - _res = _Py_BoolOp ( Or , CHECK ( _PyPegen_seq_insert_in_front ( p , a , b ) ) , EXTRA ); + _res = _Py_BoolOp ( Or , CHECK ( asdl_expr_seq * , _PyPegen_seq_insert_in_front ( p , a , b ) ) , EXTRA ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; D(p->level--); @@ -8153,7 +8153,7 @@ conjunction_rule(Parser *p) UNUSED(_end_lineno); // Only used by EXTRA macro int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro - _res = _Py_BoolOp ( And , CHECK ( _PyPegen_seq_insert_in_front ( p , a , b ) ) , EXTRA ); + _res = _Py_BoolOp ( And , CHECK ( asdl_expr_seq * , _PyPegen_seq_insert_in_front ( p , a , b ) ) , EXTRA ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; D(p->level--); @@ -8321,7 +8321,7 @@ comparison_rule(Parser *p) UNUSED(_end_lineno); // Only used by EXTRA macro int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro - _res = _Py_Compare ( a , CHECK ( _PyPegen_get_cmpops ( p , b ) ) , CHECK ( _PyPegen_get_exprs ( p , b ) ) , EXTRA ); + _res = _Py_Compare ( a , CHECK ( asdl_int_seq * , _PyPegen_get_cmpops ( p , b ) ) , CHECK ( asdl_expr_seq * , _PyPegen_get_exprs ( p , b ) ) , EXTRA ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; D(p->level--); @@ -9908,7 +9908,7 @@ term_raw(Parser *p) UNUSED(_end_lineno); // Only used by EXTRA macro int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro - _res = CHECK_VERSION ( 5 , "The '@' operator is" , _Py_BinOp ( a , MatMult , b , EXTRA ) ); + _res = CHECK_VERSION ( expr_ty , 5 , "The '@' operator is" , _Py_BinOp ( a , MatMult , b , EXTRA ) ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; D(p->level--); @@ -10235,7 +10235,7 @@ await_primary_rule(Parser *p) UNUSED(_end_lineno); // Only used by EXTRA macro int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro - _res = CHECK_VERSION ( 5 , "Await expressions are" , _Py_Await ( a , EXTRA ) ); + _res = CHECK_VERSION ( expr_ty , 5 , "Await expressions are" , _Py_Await ( a , EXTRA ) ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; D(p->level--); @@ -10391,7 +10391,7 @@ primary_raw(Parser *p) UNUSED(_end_lineno); // Only used by EXTRA macro int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro - _res = _Py_Call ( a , CHECK ( _PyPegen_singleton_seq ( p , b ) ) , NULL , EXTRA ); + _res = _Py_Call ( a , CHECK ( asdl_expr_seq * , ( asdl_expr_seq * ) _PyPegen_singleton_seq ( p , b ) ) , NULL , EXTRA ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; D(p->level--); @@ -11619,7 +11619,7 @@ dict_rule(Parser *p) UNUSED(_end_lineno); // Only used by EXTRA macro int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro - _res = _Py_Dict ( CHECK ( _PyPegen_get_keys ( p , a ) ) , CHECK ( _PyPegen_get_values ( p , a ) ) , EXTRA ); + _res = _Py_Dict ( CHECK ( asdl_expr_seq * , _PyPegen_get_keys ( p , a ) ) , CHECK ( asdl_expr_seq * , _PyPegen_get_values ( p , a ) ) , EXTRA ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; D(p->level--); @@ -11964,7 +11964,7 @@ for_if_clause_rule(Parser *p) ) { D(fprintf(stderr, "%*c+ for_if_clause[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "ASYNC 'for' star_targets 'in' ~ disjunction (('if' disjunction))*")); - _res = CHECK_VERSION ( 6 , "Async comprehensions are" , _Py_comprehension ( a , b , c , 1 , p -> arena ) ); + _res = CHECK_VERSION ( comprehension_ty , 6 , "Async comprehensions are" , _Py_comprehension ( a , b , c , 1 , p -> arena ) ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; D(p->level--); @@ -12297,7 +12297,7 @@ args_rule(Parser *p) UNUSED(_end_lineno); // Only used by EXTRA macro int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro - _res = _Py_Call ( _PyPegen_dummy_name ( p ) , CHECK_NULL_ALLOWED ( _PyPegen_seq_extract_starred_exprs ( p , a ) ) , CHECK_NULL_ALLOWED ( _PyPegen_seq_delete_starred_exprs ( p , a ) ) , EXTRA ); + _res = _Py_Call ( _PyPegen_dummy_name ( p ) , CHECK_NULL_ALLOWED ( asdl_expr_seq * , _PyPegen_seq_extract_starred_exprs ( p , a ) ) , CHECK_NULL_ALLOWED ( asdl_keyword_seq * , _PyPegen_seq_delete_starred_exprs ( p , a ) ) , EXTRA ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; D(p->level--); @@ -12512,7 +12512,7 @@ kwarg_or_starred_rule(Parser *p) UNUSED(_end_lineno); // Only used by EXTRA macro int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro - _res = _PyPegen_keyword_or_starred ( p , CHECK ( _Py_keyword ( a -> v . Name . id , b , EXTRA ) ) , 1 ); + _res = _PyPegen_keyword_or_starred ( p , CHECK ( keyword_ty , _Py_keyword ( a -> v . Name . id , b , EXTRA ) ) , 1 ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; D(p->level--); @@ -12620,7 +12620,7 @@ kwarg_or_double_starred_rule(Parser *p) UNUSED(_end_lineno); // Only used by EXTRA macro int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro - _res = _PyPegen_keyword_or_starred ( p , CHECK ( _Py_keyword ( a -> v . Name . id , b , EXTRA ) ) , 1 ); + _res = _PyPegen_keyword_or_starred ( p , CHECK ( keyword_ty , _Py_keyword ( a -> v . Name . id , b , EXTRA ) ) , 1 ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; D(p->level--); @@ -12656,7 +12656,7 @@ kwarg_or_double_starred_rule(Parser *p) UNUSED(_end_lineno); // Only used by EXTRA macro int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro - _res = _PyPegen_keyword_or_starred ( p , CHECK ( _Py_keyword ( NULL , a , EXTRA ) ) , 1 ); + _res = _PyPegen_keyword_or_starred ( p , CHECK ( keyword_ty , _Py_keyword ( NULL , a , EXTRA ) ) , 1 ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; D(p->level--); @@ -12767,7 +12767,7 @@ star_targets_rule(Parser *p) UNUSED(_end_lineno); // Only used by EXTRA macro int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro - _res = _Py_Tuple ( CHECK ( _PyPegen_seq_insert_in_front ( p , a , b ) ) , Store , EXTRA ); + _res = _Py_Tuple ( CHECK ( asdl_expr_seq * , _PyPegen_seq_insert_in_front ( p , a , b ) ) , Store , EXTRA ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; D(p->level--); @@ -12882,7 +12882,7 @@ star_target_rule(Parser *p) UNUSED(_end_lineno); // Only used by EXTRA macro int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro - _res = _Py_Starred ( CHECK ( _PyPegen_set_expr_context ( p , a , Store ) ) , Store , EXTRA ); + _res = _Py_Starred ( CHECK ( expr_ty , _PyPegen_set_expr_context ( p , a , Store ) ) , Store , EXTRA ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; D(p->level--); @@ -14060,7 +14060,7 @@ t_primary_raw(Parser *p) UNUSED(_end_lineno); // Only used by EXTRA macro int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro - _res = _Py_Call ( a , CHECK ( _PyPegen_singleton_seq ( p , b ) ) , NULL , EXTRA ); + _res = _Py_Call ( a , CHECK ( asdl_expr_seq * , ( asdl_expr_seq * ) _PyPegen_singleton_seq ( p , b ) ) , NULL , EXTRA ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; D(p->level--); diff --git a/Parser/pegen.h b/Parser/pegen.h index 000dc8c462b85..9a280ae240a24 100644 --- a/Parser/pegen.h +++ b/Parser/pegen.h @@ -178,8 +178,8 @@ CHECK_CALL_NULL_ALLOWED(Parser *p, void *result) return result; } -#define CHECK(result) CHECK_CALL(p, result) -#define CHECK_NULL_ALLOWED(result) CHECK_CALL_NULL_ALLOWED(p, result) +#define CHECK(type, result) ((type) CHECK_CALL(p, result)) +#define CHECK_NULL_ALLOWED(type, result) ((type) CHECK_CALL_NULL_ALLOWED(p, result)) PyObject *_PyPegen_new_type_comment(Parser *, char *); @@ -218,7 +218,7 @@ INVALID_VERSION_CHECK(Parser *p, int version, char *msg, void *node) return node; } -#define CHECK_VERSION(version, msg, node) INVALID_VERSION_CHECK(p, version, msg, node) +#define CHECK_VERSION(type, version, msg, node) ((type) INVALID_VERSION_CHECK(p, version, msg, node)) arg_ty _PyPegen_add_type_comment_to_arg(Parser *, arg_ty, Token *); PyObject *_PyPegen_new_identifier(Parser *, char *); @@ -277,7 +277,7 @@ expr_ty _PyPegen_get_invalid_target(expr_ty e, TARGETS_TYPE targets_type); Py_LOCAL_INLINE(void *) _RAISE_SYNTAX_ERROR_INVALID_TARGET(Parser *p, TARGETS_TYPE type, void *e) { - expr_ty invalid_target = CHECK_NULL_ALLOWED(_PyPegen_get_invalid_target(e, type)); + expr_ty invalid_target = CHECK_NULL_ALLOWED(expr_ty, _PyPegen_get_invalid_target(e, type)); if (invalid_target != NULL) { const char *msg; if (type == STAR_TARGETS || type == FOR_TARGETS) { From webhook-mailer at python.org Wed Oct 21 16:33:28 2020 From: webhook-mailer at python.org (miss-islington) Date: Wed, 21 Oct 2020 20:33:28 -0000 Subject: [Python-checkins] [3.8] bpo-41959: Fix grammar around class asyncio.MultiLoopChildWatcher text (GH-22580) (GH-22866) Message-ID: https://github.com/python/cpython/commit/ba666747af83aa465ffa8b55efbbb7f992647e12 commit: ba666747af83aa465ffa8b55efbbb7f992647e12 branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-21T13:33:12-07:00 summary: [3.8] bpo-41959: Fix grammar around class asyncio.MultiLoopChildWatcher text (GH-22580) (GH-22866) While translating the following document to Spanish we found there is a grammar issue on the original documentation. (cherry picked from commit caff2934f46510920a6169e192707d59e9c55f6b) Co-authored-by: Ra?l Cumplido files: M Doc/library/asyncio-policy.rst diff --git a/Doc/library/asyncio-policy.rst b/Doc/library/asyncio-policy.rst index aa8f8f13eae02..2e153d6fe2647 100644 --- a/Doc/library/asyncio-policy.rst +++ b/Doc/library/asyncio-policy.rst @@ -219,7 +219,7 @@ implementation used by the asyncio event loop: This implementation registers a :py:data:`SIGCHLD` signal handler on instantiation. That can break third-party code that installs a custom handler for - `SIGCHLD`. signal). + :py:data:`SIGCHLD` signal. The watcher avoids disrupting other code spawning processes by polling every process explicitly on a :py:data:`SIGCHLD` signal. From webhook-mailer at python.org Wed Oct 21 16:34:24 2020 From: webhook-mailer at python.org (miss-islington) Date: Wed, 21 Oct 2020 20:34:24 -0000 Subject: [Python-checkins] Fix bpo-39416: Change "Numeric" to lower case; an english word, not a class name (GH-22867) Message-ID: https://github.com/python/cpython/commit/f8b1ccd63c94bcde1c15d56d24add89861b6ceee commit: f8b1ccd63c94bcde1c15d56d24add89861b6ceee branch: master author: kpinc committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-21T13:34:15-07:00 summary: Fix bpo-39416: Change "Numeric" to lower case; an english word, not a class name (GH-22867) This is a trivial fix to [bpo-39416](), which didn't come up until it was already committed ``` Change "Numeric" to "numeric". I believe this is trivial enough to not need an issue or a NEWS entry, although I'm unclear on what branches the original pull request received backports. ``` Automerge-Triggered-By: GH:merwok files: M Doc/reference/datamodel.rst diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index d9b72a9a942ed..ab4eb478efb8b 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -187,7 +187,7 @@ Ellipsis related to mathematical numbers, but subject to the limitations of numerical representation in computers. - The string representations of the Numeric classes, computed by + The string representations of the numeric classes, computed by :meth:`__repr__` and :meth:`__str__`, have the following properties: From webhook-mailer at python.org Wed Oct 21 16:34:36 2020 From: webhook-mailer at python.org (asvetlov) Date: Wed, 21 Oct 2020 20:34:36 -0000 Subject: [Python-checkins] bpo-41959: Fix grammar around class asyncio.MultiLoopChildWatcher text (GH-22580) (#22865) Message-ID: https://github.com/python/cpython/commit/c3442fd8a7f6e34ba888efea085c3701b75c0b71 commit: c3442fd8a7f6e34ba888efea085c3701b75c0b71 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: asvetlov date: 2020-10-21T23:34:29+03:00 summary: bpo-41959: Fix grammar around class asyncio.MultiLoopChildWatcher text (GH-22580) (#22865) While translating the following document to Spanish we found there is a grammar issue on the original documentation. (cherry picked from commit caff2934f46510920a6169e192707d59e9c55f6b) Co-authored-by: Ra?l Cumplido Co-authored-by: Ra?l Cumplido files: M Doc/library/asyncio-policy.rst diff --git a/Doc/library/asyncio-policy.rst b/Doc/library/asyncio-policy.rst index d9d3232d2408b..88e69ceff9adc 100644 --- a/Doc/library/asyncio-policy.rst +++ b/Doc/library/asyncio-policy.rst @@ -219,7 +219,7 @@ implementation used by the asyncio event loop: This implementation registers a :py:data:`SIGCHLD` signal handler on instantiation. That can break third-party code that installs a custom handler for - `SIGCHLD`. signal). + :py:data:`SIGCHLD` signal. The watcher avoids disrupting other code spawning processes by polling every process explicitly on a :py:data:`SIGCHLD` signal. From webhook-mailer at python.org Wed Oct 21 16:54:44 2020 From: webhook-mailer at python.org (miss-islington) Date: Wed, 21 Oct 2020 20:54:44 -0000 Subject: [Python-checkins] Fix bpo-39416: Change "Numeric" to lower case; an english word, not a class name (GH-22867) Message-ID: https://github.com/python/cpython/commit/224ed378b9aadad9dbbd890064677433188aecd9 commit: 224ed378b9aadad9dbbd890064677433188aecd9 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-21T13:54:39-07:00 summary: Fix bpo-39416: Change "Numeric" to lower case; an english word, not a class name (GH-22867) This is a trivial fix to [bpo-39416](), which didn't come up until it was already committed ``` Change "Numeric" to "numeric". I believe this is trivial enough to not need an issue or a NEWS entry, although I'm unclear on what branches the original pull request received backports. ``` Automerge-Triggered-By: GH:merwok (cherry picked from commit f8b1ccd63c94bcde1c15d56d24add89861b6ceee) Co-authored-by: kpinc files: M Doc/reference/datamodel.rst diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index c0e7edb2421b0..5ecf5431150b1 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -187,7 +187,7 @@ Ellipsis related to mathematical numbers, but subject to the limitations of numerical representation in computers. - The string representations of the Numeric classes, computed by + The string representations of the numeric classes, computed by :meth:`__repr__` and :meth:`__str__`, have the following properties: From webhook-mailer at python.org Wed Oct 21 17:17:40 2020 From: webhook-mailer at python.org (miss-islington) Date: Wed, 21 Oct 2020 21:17:40 -0000 Subject: [Python-checkins] bpo-35181: Correct importlib documentation for some module attributes (GH-15190) Message-ID: https://github.com/python/cpython/commit/27f1bd8787d24ac53cc3dc6ea5eb00b8a3499839 commit: 27f1bd8787d24ac53cc3dc6ea5eb00b8a3499839 branch: master author: G?ry Ogam committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-21T14:17:35-07:00 summary: bpo-35181: Correct importlib documentation for some module attributes (GH-15190) @ericsnowcurrently This PR will change the following: In the library documentation importlib.rst: - `module.__package__` can be `module.__name__` for packages; - `spec.parent` can be `spec.__name__` for packages; - `spec.loader` is not `None` for namespaces packages. In the language documentation import.rst: - `spec.loader` is not `None` for namespace packages. Automerge-Triggered-By: GH:warsaw files: M Doc/library/importlib.rst M Doc/reference/import.rst diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst index 5fb0a4a120b98..45b62aec9ef53 100644 --- a/Doc/library/importlib.rst +++ b/Doc/library/importlib.rst @@ -438,8 +438,9 @@ ABC hierarchy:: package. This attribute is not set on modules. - :attr:`__package__` - The parent package for the module/package. If the module is - top-level then it has a value of the empty string. The + The fully-qualified name of the package under which the module was + loaded as a submodule (or the empty string for top-level modules). + For packages, it is the same as :attr:`__name__`. The :func:`importlib.util.module_for_loader` decorator can handle the details for :attr:`__package__`. @@ -1347,8 +1348,8 @@ find and load modules. (``__loader__``) - The loader to use for loading. For namespace packages this should be - set to ``None``. + The :term:`Loader ` that should be used when loading + the module. :term:`Finders ` should always set this. .. attribute:: origin @@ -1381,8 +1382,9 @@ find and load modules. (``__package__``) - (Read-only) Fully-qualified name of the package to which the module - belongs as a submodule (or ``None``). + (Read-only) The fully-qualified name of the package under which the module + should be loaded as a submodule (or the empty string for top-level modules). + For packages, it is the same as :attr:`__name__`. .. attribute:: has_location diff --git a/Doc/reference/import.rst b/Doc/reference/import.rst index 213b31487ac61..c5952426fdcf8 100644 --- a/Doc/reference/import.rst +++ b/Doc/reference/import.rst @@ -857,9 +857,8 @@ module. ``find_spec()`` returns a fully populated spec for the module. This spec will always have "loader" set (with one exception). To indicate to the import machinery that the spec represents a namespace -:term:`portion`, the path entry finder sets "loader" on the spec to -``None`` and "submodule_search_locations" to a list containing the -portion. +:term:`portion`, the path entry finder sets "submodule_search_locations" to +a list containing the portion. .. versionchanged:: 3.4 :meth:`~importlib.abc.PathEntryFinder.find_spec` replaced @@ -875,18 +874,7 @@ portion. :meth:`~importlib.abc.PathEntryFinder.find_loader` takes one argument, the fully qualified name of the module being imported. ``find_loader()`` returns a 2-tuple where the first item is the loader and the second item - is a namespace :term:`portion`. When the first item (i.e. the loader) is - ``None``, this means that while the path entry finder does not have a - loader for the named module, it knows that the path entry contributes to - a namespace portion for the named module. This will almost always be the - case where Python is asked to import a namespace package that has no - physical presence on the file system. When a path entry finder returns - ``None`` for the loader, the second item of the 2-tuple return value must - be a sequence, although it can be empty. - - If ``find_loader()`` returns a non-``None`` loader value, the portion is - ignored and the loader is returned from the path based finder, terminating - the search through the path entries. + is a namespace :term:`portion`. For backwards compatibility with other implementations of the import protocol, many path entry finders also support the same, From webhook-mailer at python.org Wed Oct 21 17:27:19 2020 From: webhook-mailer at python.org (miss-islington) Date: Wed, 21 Oct 2020 21:27:19 -0000 Subject: [Python-checkins] bpo-35181: Correct importlib documentation for some module attributes (GH-15190) Message-ID: https://github.com/python/cpython/commit/916ac9520108831d2099b13992a45884b112b193 commit: 916ac9520108831d2099b13992a45884b112b193 branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-21T14:27:10-07:00 summary: bpo-35181: Correct importlib documentation for some module attributes (GH-15190) @ericsnowcurrently This PR will change the following: In the library documentation importlib.rst: - `module.__package__` can be `module.__name__` for packages; - `spec.parent` can be `spec.__name__` for packages; - `spec.loader` is not `None` for namespaces packages. In the language documentation import.rst: - `spec.loader` is not `None` for namespace packages. Automerge-Triggered-By: GH:warsaw (cherry picked from commit 27f1bd8787d24ac53cc3dc6ea5eb00b8a3499839) Co-authored-by: G?ry Ogam files: M Doc/library/importlib.rst M Doc/reference/import.rst diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst index f205f5fee0544..be0dab3d057ca 100644 --- a/Doc/library/importlib.rst +++ b/Doc/library/importlib.rst @@ -438,8 +438,9 @@ ABC hierarchy:: package. This attribute is not set on modules. - :attr:`__package__` - The parent package for the module/package. If the module is - top-level then it has a value of the empty string. The + The fully-qualified name of the package under which the module was + loaded as a submodule (or the empty string for top-level modules). + For packages, it is the same as :attr:`__name__`. The :func:`importlib.util.module_for_loader` decorator can handle the details for :attr:`__package__`. @@ -1310,8 +1311,8 @@ find and load modules. (``__loader__``) - The loader to use for loading. For namespace packages this should be - set to ``None``. + The :term:`Loader ` that should be used when loading + the module. :term:`Finders ` should always set this. .. attribute:: origin @@ -1344,8 +1345,9 @@ find and load modules. (``__package__``) - (Read-only) Fully-qualified name of the package to which the module - belongs as a submodule (or ``None``). + (Read-only) The fully-qualified name of the package under which the module + should be loaded as a submodule (or the empty string for top-level modules). + For packages, it is the same as :attr:`__name__`. .. attribute:: has_location diff --git a/Doc/reference/import.rst b/Doc/reference/import.rst index 594bb78e9ecf6..4027ffdd5e0c0 100644 --- a/Doc/reference/import.rst +++ b/Doc/reference/import.rst @@ -857,9 +857,8 @@ module. ``find_spec()`` returns a fully populated spec for the module. This spec will always have "loader" set (with one exception). To indicate to the import machinery that the spec represents a namespace -:term:`portion`, the path entry finder sets "loader" on the spec to -``None`` and "submodule_search_locations" to a list containing the -portion. +:term:`portion`, the path entry finder sets "submodule_search_locations" to +a list containing the portion. .. versionchanged:: 3.4 :meth:`~importlib.abc.PathEntryFinder.find_spec` replaced @@ -875,18 +874,7 @@ portion. :meth:`~importlib.abc.PathEntryFinder.find_loader` takes one argument, the fully qualified name of the module being imported. ``find_loader()`` returns a 2-tuple where the first item is the loader and the second item - is a namespace :term:`portion`. When the first item (i.e. the loader) is - ``None``, this means that while the path entry finder does not have a - loader for the named module, it knows that the path entry contributes to - a namespace portion for the named module. This will almost always be the - case where Python is asked to import a namespace package that has no - physical presence on the file system. When a path entry finder returns - ``None`` for the loader, the second item of the 2-tuple return value must - be a sequence, although it can be empty. - - If ``find_loader()`` returns a non-``None`` loader value, the portion is - ignored and the loader is returned from the path based finder, terminating - the search through the path entries. + is a namespace :term:`portion`. For backwards compatibility with other implementations of the import protocol, many path entry finders also support the same, From webhook-mailer at python.org Wed Oct 21 17:39:51 2020 From: webhook-mailer at python.org (miss-islington) Date: Wed, 21 Oct 2020 21:39:51 -0000 Subject: [Python-checkins] bpo-35181: Correct importlib documentation for some module attributes (GH-15190) Message-ID: https://github.com/python/cpython/commit/6e842bcdf8a47fe081c9f2edc2b8875e1f3e2f18 commit: 6e842bcdf8a47fe081c9f2edc2b8875e1f3e2f18 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-21T14:39:41-07:00 summary: bpo-35181: Correct importlib documentation for some module attributes (GH-15190) @ericsnowcurrently This PR will change the following: In the library documentation importlib.rst: - `module.__package__` can be `module.__name__` for packages; - `spec.parent` can be `spec.__name__` for packages; - `spec.loader` is not `None` for namespaces packages. In the language documentation import.rst: - `spec.loader` is not `None` for namespace packages. Automerge-Triggered-By: GH:warsaw (cherry picked from commit 27f1bd8787d24ac53cc3dc6ea5eb00b8a3499839) Co-authored-by: G?ry Ogam files: M Doc/library/importlib.rst M Doc/reference/import.rst diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst index b51db9e8b3300..9027ba58acb20 100644 --- a/Doc/library/importlib.rst +++ b/Doc/library/importlib.rst @@ -438,8 +438,9 @@ ABC hierarchy:: package. This attribute is not set on modules. - :attr:`__package__` - The parent package for the module/package. If the module is - top-level then it has a value of the empty string. The + The fully-qualified name of the package under which the module was + loaded as a submodule (or the empty string for top-level modules). + For packages, it is the same as :attr:`__name__`. The :func:`importlib.util.module_for_loader` decorator can handle the details for :attr:`__package__`. @@ -1344,8 +1345,8 @@ find and load modules. (``__loader__``) - The loader to use for loading. For namespace packages this should be - set to ``None``. + The :term:`Loader ` that should be used when loading + the module. :term:`Finders ` should always set this. .. attribute:: origin @@ -1378,8 +1379,9 @@ find and load modules. (``__package__``) - (Read-only) Fully-qualified name of the package to which the module - belongs as a submodule (or ``None``). + (Read-only) The fully-qualified name of the package under which the module + should be loaded as a submodule (or the empty string for top-level modules). + For packages, it is the same as :attr:`__name__`. .. attribute:: has_location diff --git a/Doc/reference/import.rst b/Doc/reference/import.rst index 213b31487ac61..c5952426fdcf8 100644 --- a/Doc/reference/import.rst +++ b/Doc/reference/import.rst @@ -857,9 +857,8 @@ module. ``find_spec()`` returns a fully populated spec for the module. This spec will always have "loader" set (with one exception). To indicate to the import machinery that the spec represents a namespace -:term:`portion`, the path entry finder sets "loader" on the spec to -``None`` and "submodule_search_locations" to a list containing the -portion. +:term:`portion`, the path entry finder sets "submodule_search_locations" to +a list containing the portion. .. versionchanged:: 3.4 :meth:`~importlib.abc.PathEntryFinder.find_spec` replaced @@ -875,18 +874,7 @@ portion. :meth:`~importlib.abc.PathEntryFinder.find_loader` takes one argument, the fully qualified name of the module being imported. ``find_loader()`` returns a 2-tuple where the first item is the loader and the second item - is a namespace :term:`portion`. When the first item (i.e. the loader) is - ``None``, this means that while the path entry finder does not have a - loader for the named module, it knows that the path entry contributes to - a namespace portion for the named module. This will almost always be the - case where Python is asked to import a namespace package that has no - physical presence on the file system. When a path entry finder returns - ``None`` for the loader, the second item of the 2-tuple return value must - be a sequence, although it can be empty. - - If ``find_loader()`` returns a non-``None`` loader value, the portion is - ignored and the loader is returned from the path based finder, terminating - the search through the path entries. + is a namespace :term:`portion`. For backwards compatibility with other implementations of the import protocol, many path entry finders also support the same, From webhook-mailer at python.org Wed Oct 21 17:46:57 2020 From: webhook-mailer at python.org (pablogsal) Date: Wed, 21 Oct 2020 21:46:57 -0000 Subject: [Python-checkins] bpo-38980: Add -fno-semantic-interposition when building with optimizations (GH-22862) Message-ID: https://github.com/python/cpython/commit/b451b0e9a772f009f4161f7a46476190d0d17ac1 commit: b451b0e9a772f009f4161f7a46476190d0d17ac1 branch: master author: Pablo Galindo committer: pablogsal date: 2020-10-21T22:46:52+01:00 summary: bpo-38980: Add -fno-semantic-interposition when building with optimizations (GH-22862) files: A Misc/NEWS.d/next/Build/2020-10-21-18-31-54.bpo-38980.xz7BNd.rst M Doc/whatsnew/3.10.rst M configure M configure.ac diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index e275a7cb4573f..e464be6eb7e95 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -256,6 +256,14 @@ Optimizations It is about 36% faster now. (Contributed by Pablo Galindo and Yury Selivanov in :issue:`42093`.) +* When building Python with ``--enable-optimizations`` now + ``-fno-semantic-interposition`` is added to both the compile and link line. + This speeds builds of the Python interpreter created with ``--enable-shared`` + with ``gcc`` by up to 30%. See `this article + `_ + for more details. (Contributed by Victor Stinner and Pablo Galindo in + :issue:`38980`) + Deprecated ========== diff --git a/Misc/NEWS.d/next/Build/2020-10-21-18-31-54.bpo-38980.xz7BNd.rst b/Misc/NEWS.d/next/Build/2020-10-21-18-31-54.bpo-38980.xz7BNd.rst new file mode 100644 index 0000000000000..c9d34cf12c292 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2020-10-21-18-31-54.bpo-38980.xz7BNd.rst @@ -0,0 +1,3 @@ +Add ``-fno-semantic-interposition`` to both the compile and link line when +building with ``--enable-optimizations``. Patch by Victor Stinner and Pablo +Galindo. diff --git a/configure b/configure index 88b78947767fa..df7118f68d3bb 100755 --- a/configure +++ b/configure @@ -6482,6 +6482,14 @@ if test "$Py_OPT" = 'true' ; then DEF_MAKE_ALL_RULE="profile-opt" REQUIRE_PGO="yes" DEF_MAKE_RULE="build_all" + case $CC in + *gcc*) + CFLAGS_NODIST="$CFLAGS_NODIST -fno-semantic-interposition" + LDFLAGS_NODIST="$LDFLAGS_NODIST -fno-semantic-interposition" + ;; + esac + + else DEF_MAKE_ALL_RULE="build_all" REQUIRE_PGO="no" diff --git a/configure.ac b/configure.ac index 6ffe90a4c426c..c63c1e88a2302 100644 --- a/configure.ac +++ b/configure.ac @@ -1318,6 +1318,14 @@ if test "$Py_OPT" = 'true' ; then DEF_MAKE_ALL_RULE="profile-opt" REQUIRE_PGO="yes" DEF_MAKE_RULE="build_all" + case $CC in + *gcc*) + CFLAGS_NODIST="$CFLAGS_NODIST -fno-semantic-interposition" + LDFLAGS_NODIST="$LDFLAGS_NODIST -fno-semantic-interposition" + ;; + esac + + else DEF_MAKE_ALL_RULE="build_all" REQUIRE_PGO="no" From webhook-mailer at python.org Wed Oct 21 19:24:48 2020 From: webhook-mailer at python.org (miss-islington) Date: Wed, 21 Oct 2020 23:24:48 -0000 Subject: [Python-checkins] bpo-41910: specify the default implementations of object.__eq__ and object.__ne__ (GH-22874) Message-ID: https://github.com/python/cpython/commit/3c69f0c933d4790855929f1fcd74e4a0fefb5d52 commit: 3c69f0c933d4790855929f1fcd74e4a0fefb5d52 branch: master author: Brett Cannon committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-21T16:24:38-07:00 summary: bpo-41910: specify the default implementations of object.__eq__ and object.__ne__ (GH-22874) See Objects/typeobject.c:object_richcompare() for the implementation of this in CPython. Automerge-Triggered-By: GH:brettcannon files: A Misc/NEWS.d/next/Core and Builtins/2020-10-21-14-40-54.bpo-41910.CzBMit.rst M Doc/reference/datamodel.rst diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index ab4eb478efb8b..d92e19761a965 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -1395,12 +1395,14 @@ Basic customization context (e.g., in the condition of an ``if`` statement), Python will call :func:`bool` on the value to determine if the result is true or false. - By default, :meth:`__ne__` delegates to :meth:`__eq__` and - inverts the result unless it is ``NotImplemented``. There are no other - implied relationships among the comparison operators, for example, - the truth of ``(x https://github.com/python/cpython/commit/c3538b83816663d7b767391a375179a0ce923990 commit: c3538b83816663d7b767391a375179a0ce923990 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: terryjreedy date: 2020-10-21T20:07:39-04:00 summary: bpo-41910: specify the default implementations of object.__eq__ and object.__ne__ (GH-22874) (#22876) See Objects/typeobject.c:object_richcompare() for the implementation of this in CPython. Co-authored-by: Brett Cannon files: A Misc/NEWS.d/next/Core and Builtins/2020-10-21-14-40-54.bpo-41910.CzBMit.rst M Doc/reference/datamodel.rst diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 5ecf5431150b1..c527719c40d06 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -1395,12 +1395,14 @@ Basic customization context (e.g., in the condition of an ``if`` statement), Python will call :func:`bool` on the value to determine if the result is true or false. - By default, :meth:`__ne__` delegates to :meth:`__eq__` and - inverts the result unless it is ``NotImplemented``. There are no other - implied relationships among the comparison operators, for example, - the truth of ``(x https://github.com/python/cpython/commit/b2b3803081f07600710273b4f902b5be6e5596e7 commit: b2b3803081f07600710273b4f902b5be6e5596e7 branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: terryjreedy date: 2020-10-21T20:07:59-04:00 summary: bpo-41910: specify the default implementations of object.__eq__ and object.__ne__ (GH-22874) (#22877) See Objects/typeobject.c:object_richcompare() for the implementation of this in CPython. Co-authored-by: Brett Cannon files: A Misc/NEWS.d/next/Core and Builtins/2020-10-21-14-40-54.bpo-41910.CzBMit.rst M Doc/reference/datamodel.rst diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 14c38453c3577..da38bba91b6b9 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -1390,12 +1390,14 @@ Basic customization context (e.g., in the condition of an ``if`` statement), Python will call :func:`bool` on the value to determine if the result is true or false. - By default, :meth:`__ne__` delegates to :meth:`__eq__` and - inverts the result unless it is ``NotImplemented``. There are no other - implied relationships among the comparison operators, for example, - the truth of ``(x https://github.com/python/cpython/commit/c8ba47b5518f83b5766fefe6f68557b5033e1d70 commit: c8ba47b5518f83b5766fefe6f68557b5033e1d70 branch: master author: Vladimir Matveev committer: 1st1 date: 2020-10-21T17:49:10-07:00 summary: Delete TaskWakeupMethWrapper_Type and use PyCFunction instead (#22875) files: M Modules/_asynciomodule.c diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index f01e5884c6fe2..90d288f739393 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -93,11 +93,6 @@ typedef struct { PyObject *sw_arg; } TaskStepMethWrapper; -typedef struct { - PyObject_HEAD - TaskObj *ww_task; -} TaskWakeupMethWrapper; - typedef struct { PyObject_HEAD PyObject *rl_loop; @@ -1870,93 +1865,15 @@ TaskStepMethWrapper_new(TaskObj *task, PyObject *arg) return (PyObject*) o; } -/* ----- Task._wakeup wrapper */ - -static PyObject * -TaskWakeupMethWrapper_call(TaskWakeupMethWrapper *o, - PyObject *args, PyObject *kwds) -{ - PyObject *fut; - - if (kwds != NULL && PyDict_GET_SIZE(kwds) != 0) { - PyErr_SetString(PyExc_TypeError, "function takes no keyword arguments"); - return NULL; - } - if (!PyArg_ParseTuple(args, "O", &fut)) { - return NULL; - } - - return task_wakeup(o->ww_task, fut); -} - -static int -TaskWakeupMethWrapper_clear(TaskWakeupMethWrapper *o) -{ - Py_CLEAR(o->ww_task); - return 0; -} - -static int -TaskWakeupMethWrapper_traverse(TaskWakeupMethWrapper *o, - visitproc visit, void *arg) -{ - Py_VISIT(o->ww_task); - return 0; -} - -static void -TaskWakeupMethWrapper_dealloc(TaskWakeupMethWrapper *o) -{ - PyObject_GC_UnTrack(o); - (void)TaskWakeupMethWrapper_clear(o); - Py_TYPE(o)->tp_free(o); -} - -static PyObject * -TaskWakeupMethWrapper_get___self__(TaskWakeupMethWrapper *o, void *Py_UNUSED(ignored)) -{ - if (o->ww_task) { - Py_INCREF(o->ww_task); - return (PyObject*)o->ww_task; - } - Py_RETURN_NONE; -} +/* ----- Task._wakeup implementation */ -static PyGetSetDef TaskWakeupMethWrapper_getsetlist[] = { - {"__self__", (getter)TaskWakeupMethWrapper_get___self__, NULL, NULL}, - {NULL} /* Sentinel */ -}; - -static PyTypeObject TaskWakeupMethWrapper_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "TaskWakeupMethWrapper", - .tp_basicsize = sizeof(TaskWakeupMethWrapper), - .tp_itemsize = 0, - .tp_dealloc = (destructor)TaskWakeupMethWrapper_dealloc, - .tp_call = (ternaryfunc)TaskWakeupMethWrapper_call, - .tp_getattro = PyObject_GenericGetAttr, - .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, - .tp_traverse = (traverseproc)TaskWakeupMethWrapper_traverse, - .tp_clear = (inquiry)TaskWakeupMethWrapper_clear, - .tp_getset = TaskWakeupMethWrapper_getsetlist, +static PyMethodDef TaskWakeupDef = { + "task_wakeup", + (PyCFunction)task_wakeup, + METH_O, + NULL }; -static PyObject * -TaskWakeupMethWrapper_new(TaskObj *task) -{ - TaskWakeupMethWrapper *o; - o = PyObject_GC_New(TaskWakeupMethWrapper, &TaskWakeupMethWrapper_Type); - if (o == NULL) { - return NULL; - } - - Py_INCREF(task); - o->ww_task = task; - - PyObject_GC_Track(o); - return (PyObject*) o; -} - /* ----- Task introspection helpers */ static int @@ -2803,7 +2720,7 @@ task_step_impl(TaskObj *task, PyObject *exc) fut->fut_blocking = 0; /* result.add_done_callback(task._wakeup) */ - wrapper = TaskWakeupMethWrapper_new(task); + wrapper = PyCFunction_New(&TaskWakeupDef, (PyObject *)task); if (wrapper == NULL) { goto fail; } @@ -2884,7 +2801,7 @@ task_step_impl(TaskObj *task, PyObject *exc) goto fail; } - wrapper = TaskWakeupMethWrapper_new(task); + wrapper = PyCFunction_New(&TaskWakeupDef, (PyObject *)task); if (wrapper == NULL) { goto fail; } @@ -3458,9 +3375,6 @@ PyInit__asyncio(void) if (PyType_Ready(&TaskStepMethWrapper_Type) < 0) { return NULL; } - if (PyType_Ready(&TaskWakeupMethWrapper_Type) < 0) { - return NULL; - } if (PyType_Ready(&PyRunningLoopHolder_Type) < 0) { return NULL; } From webhook-mailer at python.org Wed Oct 21 22:06:11 2020 From: webhook-mailer at python.org (methane) Date: Thu, 22 Oct 2020 02:06:11 -0000 Subject: [Python-checkins] bpo-42057: Fix peephole optimizer (GH-22802) Message-ID: https://github.com/python/cpython/commit/07a44d9572c7746568a7fe2fbcd42127fd6d4019 commit: 07a44d9572c7746568a7fe2fbcd42127fd6d4019 branch: 3.9 author: Inada Naoki committer: methane date: 2020-10-22T11:06:07+09:00 summary: bpo-42057: Fix peephole optimizer (GH-22802) files: A Misc/NEWS.d/next/Core and Builtins/2020-10-20-11-36-14.bpo-42057.BI-OoV.rst M Python/peephole.c diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-10-20-11-36-14.bpo-42057.BI-OoV.rst b/Misc/NEWS.d/next/Core and Builtins/2020-10-20-11-36-14.bpo-42057.BI-OoV.rst new file mode 100644 index 0000000000000..95c3a248f9a1a --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2020-10-20-11-36-14.bpo-42057.BI-OoV.rst @@ -0,0 +1,2 @@ +Fix peephole optimizer misoptimize conditional jump + JUMP_IF_NOT_EXC_MATCH +pair. diff --git a/Python/peephole.c b/Python/peephole.c index 84de1abc17547..6954c87b13bfb 100644 --- a/Python/peephole.c +++ b/Python/peephole.c @@ -12,7 +12,7 @@ #define UNCONDITIONAL_JUMP(op) (op==JUMP_ABSOLUTE || op==JUMP_FORWARD) #define CONDITIONAL_JUMP(op) (op==POP_JUMP_IF_FALSE || op==POP_JUMP_IF_TRUE \ - || op==JUMP_IF_FALSE_OR_POP || op==JUMP_IF_TRUE_OR_POP || op==JUMP_IF_NOT_EXC_MATCH) + || op==JUMP_IF_FALSE_OR_POP || op==JUMP_IF_TRUE_OR_POP) #define ABSOLUTE_JUMP(op) (op==JUMP_ABSOLUTE \ || op==POP_JUMP_IF_FALSE || op==POP_JUMP_IF_TRUE \ || op==JUMP_IF_FALSE_OR_POP || op==JUMP_IF_TRUE_OR_POP || op==JUMP_IF_NOT_EXC_MATCH) From webhook-mailer at python.org Wed Oct 21 22:28:44 2020 From: webhook-mailer at python.org (methane) Date: Thu, 22 Oct 2020 02:28:44 -0000 Subject: [Python-checkins] bpo-42057: Add a test case (GH-22878) Message-ID: https://github.com/python/cpython/commit/8f6787d93db1b6022db44b1e1d22460c2b74f60b commit: 8f6787d93db1b6022db44b1e1d22460c2b74f60b branch: 3.9 author: Inada Naoki committer: methane date: 2020-10-22T11:28:36+09:00 summary: bpo-42057: Add a test case (GH-22878) files: M Lib/test/test_peepholer.py diff --git a/Lib/test/test_peepholer.py b/Lib/test/test_peepholer.py index 7913e91e453ca..55543de619718 100644 --- a/Lib/test/test_peepholer.py +++ b/Lib/test/test_peepholer.py @@ -522,6 +522,13 @@ def f(): with self.assertRaises(ValueError): f() + def test_bpo_42057(self): + for i in range(10): + try: + raise Exception + except Exception or Exception: + pass + if __name__ == "__main__": unittest.main() From webhook-mailer at python.org Thu Oct 22 05:44:51 2020 From: webhook-mailer at python.org (miss-islington) Date: Thu, 22 Oct 2020 09:44:51 -0000 Subject: [Python-checkins] _testmultiphase: Fix possible ref leak (GH-22881) Message-ID: https://github.com/python/cpython/commit/8a9463f2d4044506910e26a8ed42e2bc9fc3e0f8 commit: 8a9463f2d4044506910e26a8ed42e2bc9fc3e0f8 branch: master author: Dong-hee Na committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-22T02:44:18-07:00 summary: _testmultiphase: Fix possible ref leak (GH-22881) This is just test code, but sometimes external contributors reference the code snippets from test code. `PyModule_AddObject` should be handled in the proper way. https://docs.python.org/3/c-api/module.html#c.PyModule_AddObject files: M Modules/_testmultiphase.c diff --git a/Modules/_testmultiphase.c b/Modules/_testmultiphase.c index d69ae628fa7a4..8bba077be9bb6 100644 --- a/Modules/_testmultiphase.c +++ b/Modules/_testmultiphase.c @@ -359,6 +359,7 @@ static int execfunc(PyObject *m) goto fail; } if (PyModule_AddObject(m, "Example", temp) != 0) { + Py_DECREF(temp); goto fail; } @@ -369,6 +370,7 @@ static int execfunc(PyObject *m) goto fail; } if (PyModule_AddObject(m, "error", temp) != 0) { + Py_DECREF(temp); goto fail; } @@ -378,6 +380,7 @@ static int execfunc(PyObject *m) goto fail; } if (PyModule_AddObject(m, "Str", temp) != 0) { + Py_DECREF(temp); goto fail; } @@ -807,6 +810,7 @@ meth_state_access_exec(PyObject *m) return -1; } if (PyModule_AddObject(m, "StateAccessType", temp) != 0) { + Py_DECREF(temp); return -1; } From webhook-mailer at python.org Thu Oct 22 06:20:45 2020 From: webhook-mailer at python.org (miss-islington) Date: Thu, 22 Oct 2020 10:20:45 -0000 Subject: [Python-checkins] bpo-1635741: Fix NULL ptr deref in multiprocessing (GH-22880) Message-ID: https://github.com/python/cpython/commit/dde91b1953c0f0d51c4dde056727ff84b7655190 commit: dde91b1953c0f0d51c4dde056727ff84b7655190 branch: master author: Christian Heimes committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-22T03:20:36-07:00 summary: bpo-1635741: Fix NULL ptr deref in multiprocessing (GH-22880) Commit 1d541c25c8019f7a0b80b3e1b437abe171e40b65 introduced a NULL pointer dereference in error path. Signed-off-by: Christian Heimes files: M Modules/_multiprocessing/multiprocessing.c diff --git a/Modules/_multiprocessing/multiprocessing.c b/Modules/_multiprocessing/multiprocessing.c index 25b8dc3967a4f..bec23517fca51 100644 --- a/Modules/_multiprocessing/multiprocessing.c +++ b/Modules/_multiprocessing/multiprocessing.c @@ -207,7 +207,6 @@ multiprocessing_exec(PyObject *module) py_sem_value_max = PyLong_FromLong(SEM_VALUE_MAX); if (py_sem_value_max == NULL) { - Py_DECREF(py_sem_value_max); return -1; } if (PyDict_SetItemString(_PyMp_SemLockType.tp_dict, "SEM_VALUE_MAX", From webhook-mailer at python.org Thu Oct 22 09:16:43 2020 From: webhook-mailer at python.org (larryhastings) Date: Thu, 22 Oct 2020 13:16:43 -0000 Subject: [Python-checkins] Remove 3.5 from Doc version switcher in master. (#22886) Message-ID: https://github.com/python/cpython/commit/283f9a253b4ff4df728558205629b3bb3af6e47f commit: 283f9a253b4ff4df728558205629b3bb3af6e47f branch: master author: larryhastings committer: larryhastings date: 2020-10-22T06:16:21-07:00 summary: Remove 3.5 from Doc version switcher in master. (#22886) files: M Doc/tools/static/switchers.js M Doc/tools/templates/indexsidebar.html diff --git a/Doc/tools/static/switchers.js b/Doc/tools/static/switchers.js index c51f178ce645c..1a1c7d0fa57e2 100644 --- a/Doc/tools/static/switchers.js +++ b/Doc/tools/static/switchers.js @@ -15,7 +15,6 @@ '3.8': '3.8', '3.7': '3.7', '3.6': '3.6', - '3.5': '3.5', '2.7': '2.7', }; diff --git a/Doc/tools/templates/indexsidebar.html b/Doc/tools/templates/indexsidebar.html index 7a40be77aa129..1c1cb5484a4f6 100644 --- a/Doc/tools/templates/indexsidebar.html +++ b/Doc/tools/templates/indexsidebar.html @@ -7,7 +7,6 @@

    {% trans %}Docs by version{% endtrans %}

  • {% trans %}Python 3.8 (stable){% endtrans %}
  • {% trans %}Python 3.7 (stable){% endtrans %}
  • {% trans %}Python 3.6 (security-fixes){% endtrans %}
  • -
  • {% trans %}Python 3.5 (security-fixes){% endtrans %}
  • {% trans %}Python 2.7 (EOL){% endtrans %}
  • {% trans %}All versions{% endtrans %}
  • From webhook-mailer at python.org Thu Oct 22 10:29:37 2020 From: webhook-mailer at python.org (larryhastings) Date: Thu, 22 Oct 2020 14:29:37 -0000 Subject: [Python-checkins] [3.9] Remove 3.5 from Doc version switcher in master. (GH-22886) (#22888) Message-ID: https://github.com/python/cpython/commit/32a9ffdef1a3f31965658bce92d7c87ecb156103 commit: 32a9ffdef1a3f31965658bce92d7c87ecb156103 branch: 3.9 author: larryhastings committer: larryhastings date: 2020-10-22T07:29:26-07:00 summary: [3.9] Remove 3.5 from Doc version switcher in master. (GH-22886) (#22888) (cherry picked from commit 283f9a253b4ff4df728558205629b3bb3af6e47f) files: M Doc/tools/static/switchers.js M Doc/tools/templates/indexsidebar.html diff --git a/Doc/tools/static/switchers.js b/Doc/tools/static/switchers.js index c51f178ce645c..1a1c7d0fa57e2 100644 --- a/Doc/tools/static/switchers.js +++ b/Doc/tools/static/switchers.js @@ -15,7 +15,6 @@ '3.8': '3.8', '3.7': '3.7', '3.6': '3.6', - '3.5': '3.5', '2.7': '2.7', }; diff --git a/Doc/tools/templates/indexsidebar.html b/Doc/tools/templates/indexsidebar.html index 7a40be77aa129..1c1cb5484a4f6 100644 --- a/Doc/tools/templates/indexsidebar.html +++ b/Doc/tools/templates/indexsidebar.html @@ -7,7 +7,6 @@

    {% trans %}Docs by version{% endtrans %}

  • {% trans %}Python 3.8 (stable){% endtrans %}
  • {% trans %}Python 3.7 (stable){% endtrans %}
  • {% trans %}Python 3.6 (security-fixes){% endtrans %}
  • -
  • {% trans %}Python 3.5 (security-fixes){% endtrans %}
  • {% trans %}Python 2.7 (EOL){% endtrans %}
  • {% trans %}All versions{% endtrans %}
  • From webhook-mailer at python.org Thu Oct 22 10:30:03 2020 From: webhook-mailer at python.org (larryhastings) Date: Thu, 22 Oct 2020 14:30:03 -0000 Subject: [Python-checkins] [3.8] Remove 3.5 from Doc version switcher in master. (GH-22886) (#22889) Message-ID: https://github.com/python/cpython/commit/6a5c70ac1f963732db57e9c83d861b522123484c commit: 6a5c70ac1f963732db57e9c83d861b522123484c branch: 3.8 author: larryhastings committer: larryhastings date: 2020-10-22T07:29:54-07:00 summary: [3.8] Remove 3.5 from Doc version switcher in master. (GH-22886) (#22889) (cherry picked from commit 283f9a253b4ff4df728558205629b3bb3af6e47f) files: M Doc/tools/static/switchers.js M Doc/tools/templates/indexsidebar.html diff --git a/Doc/tools/static/switchers.js b/Doc/tools/static/switchers.js index c51f178ce645c..1a1c7d0fa57e2 100644 --- a/Doc/tools/static/switchers.js +++ b/Doc/tools/static/switchers.js @@ -15,7 +15,6 @@ '3.8': '3.8', '3.7': '3.7', '3.6': '3.6', - '3.5': '3.5', '2.7': '2.7', }; diff --git a/Doc/tools/templates/indexsidebar.html b/Doc/tools/templates/indexsidebar.html index 7a40be77aa129..1c1cb5484a4f6 100644 --- a/Doc/tools/templates/indexsidebar.html +++ b/Doc/tools/templates/indexsidebar.html @@ -7,7 +7,6 @@

    {% trans %}Docs by version{% endtrans %}

  • {% trans %}Python 3.8 (stable){% endtrans %}
  • {% trans %}Python 3.7 (stable){% endtrans %}
  • {% trans %}Python 3.6 (security-fixes){% endtrans %}
  • -
  • {% trans %}Python 3.5 (security-fixes){% endtrans %}
  • {% trans %}Python 2.7 (EOL){% endtrans %}
  • {% trans %}All versions{% endtrans %}
  • From webhook-mailer at python.org Thu Oct 22 10:30:23 2020 From: webhook-mailer at python.org (larryhastings) Date: Thu, 22 Oct 2020 14:30:23 -0000 Subject: [Python-checkins] [3.7] Remove 3.5 from Doc version switcher in master. (GH-22886) (#22890) Message-ID: https://github.com/python/cpython/commit/dd2804790dfa116d20e37bc6b4463c07586da76c commit: dd2804790dfa116d20e37bc6b4463c07586da76c branch: 3.7 author: larryhastings committer: larryhastings date: 2020-10-22T07:30:11-07:00 summary: [3.7] Remove 3.5 from Doc version switcher in master. (GH-22886) (#22890) (cherry picked from commit 283f9a253b4ff4df728558205629b3bb3af6e47f) files: M Doc/tools/static/switchers.js M Doc/tools/templates/indexsidebar.html diff --git a/Doc/tools/static/switchers.js b/Doc/tools/static/switchers.js index c51f178ce645c..1a1c7d0fa57e2 100644 --- a/Doc/tools/static/switchers.js +++ b/Doc/tools/static/switchers.js @@ -15,7 +15,6 @@ '3.8': '3.8', '3.7': '3.7', '3.6': '3.6', - '3.5': '3.5', '2.7': '2.7', }; diff --git a/Doc/tools/templates/indexsidebar.html b/Doc/tools/templates/indexsidebar.html index c5924fd5774c5..2ef90f094ddaa 100644 --- a/Doc/tools/templates/indexsidebar.html +++ b/Doc/tools/templates/indexsidebar.html @@ -7,7 +7,6 @@

    {% trans %}Docs by version{% endtrans %}

  • {% trans %}Python 3.8 (stable){% endtrans %}
  • {% trans %}Python 3.7 (stable){% endtrans %}
  • {% trans %}Python 3.6 (security-fixes){% endtrans %}
  • -
  • {% trans %}Python 3.5 (security-fixes){% endtrans %}
  • {% trans %}Python 2.7 (EOL){% endtrans %}
  • {% trans %}All versions{% endtrans %}
  • From webhook-mailer at python.org Thu Oct 22 11:39:23 2020 From: webhook-mailer at python.org (zooba) Date: Thu, 22 Oct 2020 15:39:23 -0000 Subject: [Python-checkins] bpo-25655: Improve Win DLL loading failures doc (GH-22372) Message-ID: https://github.com/python/cpython/commit/b6f2fc90409e291822166d74ce7402e0ef4dba91 commit: b6f2fc90409e291822166d74ce7402e0ef4dba91 branch: master author: Philippe Ombredanne committer: zooba date: 2020-10-22T16:39:18+01:00 summary: bpo-25655: Improve Win DLL loading failures doc (GH-22372) Add documentation to help diagnose CDLL dependent DLL loading errors on windows for OSError with message: "[WinError 126] The specified module could not be found" This error is otherwise difficult to diagnose. files: M Doc/library/ctypes.rst diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index c172d5377636a..bf32d3e549b48 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -1326,6 +1326,21 @@ way is to instantiate one of the following classes: libraries use the standard C calling convention, and are assumed to return :c:type:`int`. + On Windows creating a :class:`CDLL` instance may fail even if the DLL name + exists. When a dependent DLL of the loaded DLL is not found, a + :exc:`OSError` error is raised with the message *"[WinError 126] The + specified module could not be found".* This error message does not contain + the name of the missing DLL because the Windows API does not return this + information making this error hard to diagnose. To resolve this error and + determine which DLL is not found, you need to find the list of dependent + DLLs and determine which one is not found using Windows debugging and + tracing tools. + +.. seealso:: + + `Microsoft DUMPBIN tool `_ + -- A tool to find DLL dependents. + .. class:: OleDLL(name, mode=DEFAULT_MODE, handle=None, use_errno=False, use_last_error=False, winmode=0) From webhook-mailer at python.org Thu Oct 22 11:42:37 2020 From: webhook-mailer at python.org (corona10) Date: Thu, 22 Oct 2020 15:42:37 -0000 Subject: [Python-checkins] bpo-42057: Add regression test to master. (GH-22893) Message-ID: https://github.com/python/cpython/commit/b52432cb8cd7f5f1fc71ea3b7c0ea11573d504f0 commit: b52432cb8cd7f5f1fc71ea3b7c0ea11573d504f0 branch: master author: Mark Shannon committer: corona10 date: 2020-10-23T00:42:26+09:00 summary: bpo-42057: Add regression test to master. (GH-22893) files: M Lib/test/test_peepholer.py diff --git a/Lib/test/test_peepholer.py b/Lib/test/test_peepholer.py index 65047cace56c1..92a82cc54f268 100644 --- a/Lib/test/test_peepholer.py +++ b/Lib/test/test_peepholer.py @@ -522,6 +522,12 @@ def f(): with self.assertRaises(ValueError): f() + def test_bpo_42057(self): + for i in range(10): + try: + raise Exception + except Exception or Exception: + pass if __name__ == "__main__": unittest.main() From webhook-mailer at python.org Thu Oct 22 11:48:59 2020 From: webhook-mailer at python.org (miss-islington) Date: Thu, 22 Oct 2020 15:48:59 -0000 Subject: [Python-checkins] bpo-25655: Improve Win DLL loading failures doc (GH-22372) Message-ID: https://github.com/python/cpython/commit/5d8bc65ba5be5742b3a4cc470dfd990512bdaa93 commit: 5d8bc65ba5be5742b3a4cc470dfd990512bdaa93 branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-22T08:48:51-07:00 summary: bpo-25655: Improve Win DLL loading failures doc (GH-22372) Add documentation to help diagnose CDLL dependent DLL loading errors on windows for OSError with message: "[WinError 126] The specified module could not be found" This error is otherwise difficult to diagnose. (cherry picked from commit b6f2fc90409e291822166d74ce7402e0ef4dba91) Co-authored-by: Philippe Ombredanne files: M Doc/library/ctypes.rst diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index c172d5377636a..bf32d3e549b48 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -1326,6 +1326,21 @@ way is to instantiate one of the following classes: libraries use the standard C calling convention, and are assumed to return :c:type:`int`. + On Windows creating a :class:`CDLL` instance may fail even if the DLL name + exists. When a dependent DLL of the loaded DLL is not found, a + :exc:`OSError` error is raised with the message *"[WinError 126] The + specified module could not be found".* This error message does not contain + the name of the missing DLL because the Windows API does not return this + information making this error hard to diagnose. To resolve this error and + determine which DLL is not found, you need to find the list of dependent + DLLs and determine which one is not found using Windows debugging and + tracing tools. + +.. seealso:: + + `Microsoft DUMPBIN tool `_ + -- A tool to find DLL dependents. + .. class:: OleDLL(name, mode=DEFAULT_MODE, handle=None, use_errno=False, use_last_error=False, winmode=0) From webhook-mailer at python.org Thu Oct 22 11:55:52 2020 From: webhook-mailer at python.org (larryhastings) Date: Thu, 22 Oct 2020 15:55:52 -0000 Subject: [Python-checkins] [3.6] Remove 3.5 from Doc version switcher in master. (GH-22886) (#22891) Message-ID: https://github.com/python/cpython/commit/a75c4c924de102e48faef5538eade764289915ab commit: a75c4c924de102e48faef5538eade764289915ab branch: 3.6 author: larryhastings committer: larryhastings date: 2020-10-22T08:55:36-07:00 summary: [3.6] Remove 3.5 from Doc version switcher in master. (GH-22886) (#22891) (cherry picked from commit 283f9a253b4ff4df728558205629b3bb3af6e47f) files: M Doc/tools/static/switchers.js M Doc/tools/templates/indexsidebar.html diff --git a/Doc/tools/static/switchers.js b/Doc/tools/static/switchers.js index 3b6de6f3d90fd..30801917ed9eb 100644 --- a/Doc/tools/static/switchers.js +++ b/Doc/tools/static/switchers.js @@ -15,7 +15,6 @@ '3.8': '3.8', '3.7': '3.7', '3.6': '3.6', - '3.5': '3.5', '2.7': '2.7', }; diff --git a/Doc/tools/templates/indexsidebar.html b/Doc/tools/templates/indexsidebar.html index c5924fd5774c5..2ef90f094ddaa 100644 --- a/Doc/tools/templates/indexsidebar.html +++ b/Doc/tools/templates/indexsidebar.html @@ -7,7 +7,6 @@

    {% trans %}Docs by version{% endtrans %}

  • {% trans %}Python 3.8 (stable){% endtrans %}
  • {% trans %}Python 3.7 (stable){% endtrans %}
  • {% trans %}Python 3.6 (security-fixes){% endtrans %}
  • -
  • {% trans %}Python 3.5 (security-fixes){% endtrans %}
  • {% trans %}Python 2.7 (EOL){% endtrans %}
  • {% trans %}All versions{% endtrans %}
  • From webhook-mailer at python.org Thu Oct 22 12:02:23 2020 From: webhook-mailer at python.org (miss-islington) Date: Thu, 22 Oct 2020 16:02:23 -0000 Subject: [Python-checkins] bpo-25655: Improve Win DLL loading failures doc (GH-22372) Message-ID: https://github.com/python/cpython/commit/f22f874a66d6ddb32fa74ad4325199db7e4c25fd commit: f22f874a66d6ddb32fa74ad4325199db7e4c25fd branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-22T09:02:13-07:00 summary: bpo-25655: Improve Win DLL loading failures doc (GH-22372) Add documentation to help diagnose CDLL dependent DLL loading errors on windows for OSError with message: "[WinError 126] The specified module could not be found" This error is otherwise difficult to diagnose. (cherry picked from commit b6f2fc90409e291822166d74ce7402e0ef4dba91) Co-authored-by: Philippe Ombredanne files: M Doc/library/ctypes.rst diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index c172d5377636a..bf32d3e549b48 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -1326,6 +1326,21 @@ way is to instantiate one of the following classes: libraries use the standard C calling convention, and are assumed to return :c:type:`int`. + On Windows creating a :class:`CDLL` instance may fail even if the DLL name + exists. When a dependent DLL of the loaded DLL is not found, a + :exc:`OSError` error is raised with the message *"[WinError 126] The + specified module could not be found".* This error message does not contain + the name of the missing DLL because the Windows API does not return this + information making this error hard to diagnose. To resolve this error and + determine which DLL is not found, you need to find the list of dependent + DLLs and determine which one is not found using Windows debugging and + tracing tools. + +.. seealso:: + + `Microsoft DUMPBIN tool `_ + -- A tool to find DLL dependents. + .. class:: OleDLL(name, mode=DEFAULT_MODE, handle=None, use_errno=False, use_last_error=False, winmode=0) From webhook-mailer at python.org Thu Oct 22 12:02:52 2020 From: webhook-mailer at python.org (miss-islington) Date: Thu, 22 Oct 2020 16:02:52 -0000 Subject: [Python-checkins] bpo-42086: Document AST operator nodes acts as a singleton (GH-22896) Message-ID: https://github.com/python/cpython/commit/b37c994e5ac73268abe23c52005b80cdca099793 commit: b37c994e5ac73268abe23c52005b80cdca099793 branch: master author: Batuhan Taskaya committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-22T09:02:43-07:00 summary: bpo-42086: Document AST operator nodes acts as a singleton (GH-22896) Automerge-Triggered-By: GH:gvanrossum files: M Doc/library/ast.rst diff --git a/Doc/library/ast.rst b/Doc/library/ast.rst index f95ee1d5c69c2..b8688ae61a487 100644 --- a/Doc/library/ast.rst +++ b/Doc/library/ast.rst @@ -1503,6 +1503,13 @@ Async and await fields as :class:`For` and :class:`With`, respectively. Only valid in the body of an :class:`AsyncFunctionDef`. +.. note:: + When a string is parsed by :func:`ast.parse`, operator nodes (subclasses + of :class:`ast.operator`, :class:`ast.unaryop`, :class:`ast.cmpop`, + :class:`ast.boolop` and :class:`ast.expr_context`) on the returned tree + will be singletons. Changes to one will be reflected in all other + occurrences of the same value (e.g. :class:`ast.Add`). + :mod:`ast` Helpers ------------------ From webhook-mailer at python.org Thu Oct 22 12:12:02 2020 From: webhook-mailer at python.org (encukou) Date: Thu, 22 Oct 2020 16:12:02 -0000 Subject: [Python-checkins] bpo-38980: Only apply -fno-semantic-interposition if available (GH-22892) Message-ID: https://github.com/python/cpython/commit/c6d7e82d19c091af698d4e4b3623648e259843e3 commit: c6d7e82d19c091af698d4e4b3623648e259843e3 branch: master author: Petr Viktorin committer: encukou date: 2020-10-22T18:11:53+02:00 summary: bpo-38980: Only apply -fno-semantic-interposition if available (GH-22892) files: M aclocal.m4 M configure M configure.ac diff --git a/aclocal.m4 b/aclocal.m4 index b5f9cb0e8da44..99913e7f3b85c 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -12,6 +12,60 @@ # PARTICULAR PURPOSE. m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT]) +# +# DESCRIPTION +# +# Check whether the given FLAG works with the current language's compiler +# or gives an error. (Warnings, however, are ignored) +# +# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on +# success/failure. +# +# If EXTRA-FLAGS is defined, it is added to the current language's default +# flags (e.g. CFLAGS) when the check is done. The check is thus made with +# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to +# force the compiler to issue an error when a bad flag is given. +# +# INPUT gives an alternative input source to AC_COMPILE_IFELSE. +# +# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this +# macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG. +# +# LICENSE +# +# Copyright (c) 2008 Guido U. Draheim +# Copyright (c) 2011 Maarten Bosmans +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 6 + +AC_DEFUN([AX_CHECK_COMPILE_FLAG], +[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF +AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl +AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [ + ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS + _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1" + AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], + [AS_VAR_SET(CACHEVAR,[yes])], + [AS_VAR_SET(CACHEVAR,[no])]) + _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags]) +AS_VAR_IF(CACHEVAR,yes, + [m4_default([$2], :)], + [m4_default([$3], :)]) +AS_VAR_POPDEF([CACHEVAR])dnl +])dnl AX_CHECK_COMPILE_FLAGS + # pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- # serial 11 (pkg-config-0.29.1) diff --git a/configure b/configure index df7118f68d3bb..29f33b543ecc0 100755 --- a/configure +++ b/configure @@ -6484,8 +6484,44 @@ if test "$Py_OPT" = 'true' ; then DEF_MAKE_RULE="build_all" case $CC in *gcc*) + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -fno-semantic-interposition" >&5 +$as_echo_n "checking whether C compiler accepts -fno-semantic-interposition... " >&6; } +if ${ax_cv_check_cflags___fno_semantic_interposition+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -fno-semantic-interposition" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___fno_semantic_interposition=yes +else + ax_cv_check_cflags___fno_semantic_interposition=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___fno_semantic_interposition" >&5 +$as_echo "$ax_cv_check_cflags___fno_semantic_interposition" >&6; } +if test "x$ax_cv_check_cflags___fno_semantic_interposition" = xyes; then : + CFLAGS_NODIST="$CFLAGS_NODIST -fno-semantic-interposition" LDFLAGS_NODIST="$LDFLAGS_NODIST -fno-semantic-interposition" + +else + : +fi + ;; esac diff --git a/configure.ac b/configure.ac index c63c1e88a2302..9698c3c888a6b 100644 --- a/configure.ac +++ b/configure.ac @@ -1320,8 +1320,10 @@ if test "$Py_OPT" = 'true' ; then DEF_MAKE_RULE="build_all" case $CC in *gcc*) + AX_CHECK_COMPILE_FLAG([-fno-semantic-interposition],[ CFLAGS_NODIST="$CFLAGS_NODIST -fno-semantic-interposition" LDFLAGS_NODIST="$LDFLAGS_NODIST -fno-semantic-interposition" + ]) ;; esac From webhook-mailer at python.org Thu Oct 22 14:26:26 2020 From: webhook-mailer at python.org (lysnikolaou) Date: Thu, 22 Oct 2020 18:26:26 -0000 Subject: [Python-checkins] Add CODEOWNERS for the Grammar file (GH-22898) Message-ID: https://github.com/python/cpython/commit/e76b8fc8155fac5dafa6b531d2b679c90c39748d commit: e76b8fc8155fac5dafa6b531d2b679c90c39748d branch: master author: Lysandros Nikolaou committer: lysnikolaou date: 2020-10-22T21:26:14+03:00 summary: Add CODEOWNERS for the Grammar file (GH-22898) files: M .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 31003a8ac247e..5b8662cfe0921 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -76,6 +76,7 @@ Include/pytime.h @pganssle @abalkin /Parser/ @pablogsal @lysnikolaou /Tools/peg_generator/ @pablogsal @lysnikolaou /Lib/test/test_peg_generator/ @pablogsal @lysnikolaou +/Grammar/python.gram @pablogsal @lysnikolaou # SQLite 3 **/*sqlite* @berkerpeksag From webhook-mailer at python.org Thu Oct 22 15:14:44 2020 From: webhook-mailer at python.org (terryjreedy) Date: Thu, 22 Oct 2020 19:14:44 -0000 Subject: [Python-checkins] bpo-33987: IDLE: Use ttk Frame on doc window and statusbar (GH-11433) Message-ID: https://github.com/python/cpython/commit/facb522d44fceaf15de6bc95de1cd680c4621c2a commit: facb522d44fceaf15de6bc95de1cd680c4621c2a branch: master author: Cheryl Sabella committer: terryjreedy date: 2020-10-22T15:14:35-04:00 summary: bpo-33987: IDLE: Use ttk Frame on doc window and statusbar (GH-11433) files: M Lib/idlelib/help.py M Lib/idlelib/statusbar.py diff --git a/Lib/idlelib/help.py b/Lib/idlelib/help.py index 9f63ea0d3990e..f420d40fb9ea4 100644 --- a/Lib/idlelib/help.py +++ b/Lib/idlelib/help.py @@ -28,8 +28,8 @@ from os.path import abspath, dirname, isfile, join from platform import python_version -from tkinter import Toplevel, Frame, Text, Menu -from tkinter.ttk import Menubutton, Scrollbar +from tkinter import Toplevel, Text, Menu +from tkinter.ttk import Frame, Menubutton, Scrollbar, Style from tkinter import font as tkfont from idlelib.config import idleConf @@ -212,7 +212,9 @@ class HelpFrame(Frame): def __init__(self, parent, filename): Frame.__init__(self, parent) self.text = text = HelpText(self, filename) - self['background'] = text['background'] + self.style = Style(parent) + self['style'] = 'helpframe.TFrame' + self.style.configure('helpframe.TFrame', background=text['background']) self.toc = toc = self.toc_menu(text) self.scroll = scroll = Scrollbar(self, command=text.yview) text['yscrollcommand'] = scroll.set diff --git a/Lib/idlelib/statusbar.py b/Lib/idlelib/statusbar.py index c071f898b0f74..ae52a56368c82 100644 --- a/Lib/idlelib/statusbar.py +++ b/Lib/idlelib/statusbar.py @@ -1,4 +1,5 @@ -from tkinter import Frame, Label +from tkinter import Label +from tkinter.ttk import Frame class MultiStatusBar(Frame): @@ -20,7 +21,8 @@ def set_label(self, name, text='', side='left', width=0): def _multistatus_bar(parent): # htest # - from tkinter import Toplevel, Frame, Text, Button + from tkinter import Toplevel, Text + from tkinter.ttk import Frame, Button top = Toplevel(parent) x, y = map(int, parent.geometry().split('+')[1:]) top.geometry("+%d+%d" %(x, y + 175)) From webhook-mailer at python.org Thu Oct 22 15:58:09 2020 From: webhook-mailer at python.org (terryjreedy) Date: Thu, 22 Oct 2020 19:58:09 -0000 Subject: [Python-checkins] bpo-33987: IDLE: Use ttk Frame on doc window and statusbar (GH-11433) (GH-22899) Message-ID: https://github.com/python/cpython/commit/25687bbe0da160ebdd3cd422a01d677ce467e72e commit: 25687bbe0da160ebdd3cd422a01d677ce467e72e branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: terryjreedy date: 2020-10-22T15:58:04-04:00 summary: bpo-33987: IDLE: Use ttk Frame on doc window and statusbar (GH-11433) (GH-22899) (cherry picked from commit facb522d44fceaf15de6bc95de1cd680c4621c2a) Co-authored-by: Cheryl Sabella files: M Lib/idlelib/help.py M Lib/idlelib/statusbar.py diff --git a/Lib/idlelib/help.py b/Lib/idlelib/help.py index 9f63ea0d3990e..f420d40fb9ea4 100644 --- a/Lib/idlelib/help.py +++ b/Lib/idlelib/help.py @@ -28,8 +28,8 @@ from os.path import abspath, dirname, isfile, join from platform import python_version -from tkinter import Toplevel, Frame, Text, Menu -from tkinter.ttk import Menubutton, Scrollbar +from tkinter import Toplevel, Text, Menu +from tkinter.ttk import Frame, Menubutton, Scrollbar, Style from tkinter import font as tkfont from idlelib.config import idleConf @@ -212,7 +212,9 @@ class HelpFrame(Frame): def __init__(self, parent, filename): Frame.__init__(self, parent) self.text = text = HelpText(self, filename) - self['background'] = text['background'] + self.style = Style(parent) + self['style'] = 'helpframe.TFrame' + self.style.configure('helpframe.TFrame', background=text['background']) self.toc = toc = self.toc_menu(text) self.scroll = scroll = Scrollbar(self, command=text.yview) text['yscrollcommand'] = scroll.set diff --git a/Lib/idlelib/statusbar.py b/Lib/idlelib/statusbar.py index c071f898b0f74..ae52a56368c82 100644 --- a/Lib/idlelib/statusbar.py +++ b/Lib/idlelib/statusbar.py @@ -1,4 +1,5 @@ -from tkinter import Frame, Label +from tkinter import Label +from tkinter.ttk import Frame class MultiStatusBar(Frame): @@ -20,7 +21,8 @@ def set_label(self, name, text='', side='left', width=0): def _multistatus_bar(parent): # htest # - from tkinter import Toplevel, Frame, Text, Button + from tkinter import Toplevel, Text + from tkinter.ttk import Frame, Button top = Toplevel(parent) x, y = map(int, parent.geometry().split('+')[1:]) top.geometry("+%d+%d" %(x, y + 175)) From webhook-mailer at python.org Thu Oct 22 15:58:34 2020 From: webhook-mailer at python.org (terryjreedy) Date: Thu, 22 Oct 2020 19:58:34 -0000 Subject: [Python-checkins] bpo-33987: IDLE: Use ttk Frame on doc window and statusbar (GH-11433) (GH-22900) Message-ID: https://github.com/python/cpython/commit/06c9e01c651c35c2e058eca0f7073dd405578f78 commit: 06c9e01c651c35c2e058eca0f7073dd405578f78 branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: terryjreedy date: 2020-10-22T15:58:30-04:00 summary: bpo-33987: IDLE: Use ttk Frame on doc window and statusbar (GH-11433) (GH-22900) (cherry picked from commit facb522d44fceaf15de6bc95de1cd680c4621c2a) Co-authored-by: Cheryl Sabella files: M Lib/idlelib/help.py M Lib/idlelib/statusbar.py diff --git a/Lib/idlelib/help.py b/Lib/idlelib/help.py index 9f63ea0d3990e..f420d40fb9ea4 100644 --- a/Lib/idlelib/help.py +++ b/Lib/idlelib/help.py @@ -28,8 +28,8 @@ from os.path import abspath, dirname, isfile, join from platform import python_version -from tkinter import Toplevel, Frame, Text, Menu -from tkinter.ttk import Menubutton, Scrollbar +from tkinter import Toplevel, Text, Menu +from tkinter.ttk import Frame, Menubutton, Scrollbar, Style from tkinter import font as tkfont from idlelib.config import idleConf @@ -212,7 +212,9 @@ class HelpFrame(Frame): def __init__(self, parent, filename): Frame.__init__(self, parent) self.text = text = HelpText(self, filename) - self['background'] = text['background'] + self.style = Style(parent) + self['style'] = 'helpframe.TFrame' + self.style.configure('helpframe.TFrame', background=text['background']) self.toc = toc = self.toc_menu(text) self.scroll = scroll = Scrollbar(self, command=text.yview) text['yscrollcommand'] = scroll.set diff --git a/Lib/idlelib/statusbar.py b/Lib/idlelib/statusbar.py index c071f898b0f74..ae52a56368c82 100644 --- a/Lib/idlelib/statusbar.py +++ b/Lib/idlelib/statusbar.py @@ -1,4 +1,5 @@ -from tkinter import Frame, Label +from tkinter import Label +from tkinter.ttk import Frame class MultiStatusBar(Frame): @@ -20,7 +21,8 @@ def set_label(self, name, text='', side='left', width=0): def _multistatus_bar(parent): # htest # - from tkinter import Toplevel, Frame, Text, Button + from tkinter import Toplevel, Text + from tkinter.ttk import Frame, Button top = Toplevel(parent) x, y = map(int, parent.geometry().split('+')[1:]) top.geometry("+%d+%d" %(x, y + 175)) From webhook-mailer at python.org Thu Oct 22 19:33:37 2020 From: webhook-mailer at python.org (miss-islington) Date: Thu, 22 Oct 2020 23:33:37 -0000 Subject: [Python-checkins] bpo-38486: Fix dead qmail links in the mailbox docs (GH-22239) Message-ID: https://github.com/python/cpython/commit/ec388cfb4ede56dace2bb78851ff6f38fa2a6abe commit: ec388cfb4ede56dace2bb78851ff6f38fa2a6abe branch: master author: Zackery Spytz committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-22T16:33:28-07:00 summary: bpo-38486: Fix dead qmail links in the mailbox docs (GH-22239) files: M Doc/library/mailbox.rst diff --git a/Doc/library/mailbox.rst b/Doc/library/mailbox.rst index f82a3b200deb7..94d95d10290b0 100644 --- a/Doc/library/mailbox.rst +++ b/Doc/library/mailbox.rst @@ -426,17 +426,14 @@ Supported mailbox formats are Maildir, mbox, MH, Babyl, and MMDF. .. seealso:: - `maildir man page from qmail `_ - The original specification of the format. + `maildir man page from Courier `_ + A specification of the format. Describes a common extension for + supporting folders. `Using maildir format `_ Notes on Maildir by its inventor. Includes an updated name-creation scheme and details on "info" semantics. - `maildir man page from Courier `_ - Another specification of the format. Describes a common extension for supporting - folders. - .. _mailbox-mbox: @@ -485,11 +482,8 @@ Supported mailbox formats are Maildir, mbox, MH, Babyl, and MMDF. .. seealso:: - `mbox man page from qmail `_ - A specification of the format and its variations. - `mbox man page from tin `_ - Another specification of the format, with details on locking. + A specification of the format, with details on locking. `Configuring Netscape Mail on Unix: Why The Content-Length Format is Bad `_ An argument for using the original mbox format rather than a variation. From webhook-mailer at python.org Thu Oct 22 19:42:09 2020 From: webhook-mailer at python.org (miss-islington) Date: Thu, 22 Oct 2020 23:42:09 -0000 Subject: [Python-checkins] [3.8] bpo-38486: Fix dead qmail links in the mailbox docs (GH-22239) (GH-22902) Message-ID: https://github.com/python/cpython/commit/cd894b1094ac472ceb98fe15a28ebeb8301fc079 commit: cd894b1094ac472ceb98fe15a28ebeb8301fc079 branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-22T16:42:01-07:00 summary: [3.8] bpo-38486: Fix dead qmail links in the mailbox docs (GH-22239) (GH-22902) (cherry picked from commit ec388cfb4ede56dace2bb78851ff6f38fa2a6abe) Co-authored-by: Zackery Spytz Automerge-Triggered-By: GH:warsaw files: M Doc/library/mailbox.rst diff --git a/Doc/library/mailbox.rst b/Doc/library/mailbox.rst index f82a3b200deb7..94d95d10290b0 100644 --- a/Doc/library/mailbox.rst +++ b/Doc/library/mailbox.rst @@ -426,17 +426,14 @@ Supported mailbox formats are Maildir, mbox, MH, Babyl, and MMDF. .. seealso:: - `maildir man page from qmail `_ - The original specification of the format. + `maildir man page from Courier `_ + A specification of the format. Describes a common extension for + supporting folders. `Using maildir format `_ Notes on Maildir by its inventor. Includes an updated name-creation scheme and details on "info" semantics. - `maildir man page from Courier `_ - Another specification of the format. Describes a common extension for supporting - folders. - .. _mailbox-mbox: @@ -485,11 +482,8 @@ Supported mailbox formats are Maildir, mbox, MH, Babyl, and MMDF. .. seealso:: - `mbox man page from qmail `_ - A specification of the format and its variations. - `mbox man page from tin `_ - Another specification of the format, with details on locking. + A specification of the format, with details on locking. `Configuring Netscape Mail on Unix: Why The Content-Length Format is Bad `_ An argument for using the original mbox format rather than a variation. From webhook-mailer at python.org Thu Oct 22 19:55:14 2020 From: webhook-mailer at python.org (miss-islington) Date: Thu, 22 Oct 2020 23:55:14 -0000 Subject: [Python-checkins] [3.9] bpo-38486: Fix dead qmail links in the mailbox docs (GH-22239) (GH-22901) Message-ID: https://github.com/python/cpython/commit/69479ecfa86cc2f0e70d5627d52907e82c3f5b9b commit: 69479ecfa86cc2f0e70d5627d52907e82c3f5b9b branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-22T16:55:05-07:00 summary: [3.9] bpo-38486: Fix dead qmail links in the mailbox docs (GH-22239) (GH-22901) (cherry picked from commit ec388cfb4ede56dace2bb78851ff6f38fa2a6abe) Co-authored-by: Zackery Spytz Automerge-Triggered-By: GH:warsaw files: M Doc/library/mailbox.rst diff --git a/Doc/library/mailbox.rst b/Doc/library/mailbox.rst index f82a3b200deb7..94d95d10290b0 100644 --- a/Doc/library/mailbox.rst +++ b/Doc/library/mailbox.rst @@ -426,17 +426,14 @@ Supported mailbox formats are Maildir, mbox, MH, Babyl, and MMDF. .. seealso:: - `maildir man page from qmail `_ - The original specification of the format. + `maildir man page from Courier `_ + A specification of the format. Describes a common extension for + supporting folders. `Using maildir format `_ Notes on Maildir by its inventor. Includes an updated name-creation scheme and details on "info" semantics. - `maildir man page from Courier `_ - Another specification of the format. Describes a common extension for supporting - folders. - .. _mailbox-mbox: @@ -485,11 +482,8 @@ Supported mailbox formats are Maildir, mbox, MH, Babyl, and MMDF. .. seealso:: - `mbox man page from qmail `_ - A specification of the format and its variations. - `mbox man page from tin `_ - Another specification of the format, with details on locking. + A specification of the format, with details on locking. `Configuring Netscape Mail on Unix: Why The Content-Length Format is Bad `_ An argument for using the original mbox format rather than a variation. From webhook-mailer at python.org Thu Oct 22 20:43:01 2020 From: webhook-mailer at python.org (ericsnowcurrently) Date: Fri, 23 Oct 2020 00:43:01 -0000 Subject: [Python-checkins] bpo-36876: Fix the C analyzer tool. (GH-22841) Message-ID: https://github.com/python/cpython/commit/345cd37abe324ad4f60f80e2c3133b8849e54e9b commit: 345cd37abe324ad4f60f80e2c3133b8849e54e9b branch: master author: Eric Snow committer: ericsnowcurrently date: 2020-10-22T18:42:51-06:00 summary: bpo-36876: Fix the C analyzer tool. (GH-22841) The original tool wasn't working right and it was simpler to create a new one, partially re-using some of the old code. At this point the tool runs properly on the master. (Try: ./python Tools/c-analyzer/c-analyzer.py analyze.) It take ~40 seconds on my machine to analyze the full CPython code base. Note that we'll need to iron out some OS-specific stuff (e.g. preprocessor). We're okay though since this tool isn't used yet in our workflow. We will also need to verify the analysis results in detail before activating the check in CI, though I'm pretty sure it's close. https://bugs.python.org/issue36876 files: A Tools/c-analyzer/c-analyzer.py A Tools/c-analyzer/c_analyzer/__main__.py A Tools/c-analyzer/c_analyzer/analyze.py A Tools/c-analyzer/c_analyzer/datafiles.py A Tools/c-analyzer/c_analyzer/info.py A Tools/c-analyzer/c_common/__init__.py A Tools/c-analyzer/c_common/clsutil.py A Tools/c-analyzer/c_common/fsutil.py A Tools/c-analyzer/c_common/info.py A Tools/c-analyzer/c_common/iterutil.py A Tools/c-analyzer/c_common/logging.py A Tools/c-analyzer/c_common/misc.py A Tools/c-analyzer/c_common/scriptutil.py A Tools/c-analyzer/c_common/show.py A Tools/c-analyzer/c_common/strutil.py A Tools/c-analyzer/c_common/tables.py A Tools/c-analyzer/c_parser/__init__.py A Tools/c-analyzer/c_parser/__main__.py A Tools/c-analyzer/c_parser/_state_machine.py A Tools/c-analyzer/c_parser/datafiles.py A Tools/c-analyzer/c_parser/info.py A Tools/c-analyzer/c_parser/parser/__init__.py A Tools/c-analyzer/c_parser/parser/_alt.py A Tools/c-analyzer/c_parser/parser/_common.py A Tools/c-analyzer/c_parser/parser/_compound_decl_body.py A Tools/c-analyzer/c_parser/parser/_delim.py A Tools/c-analyzer/c_parser/parser/_func_body.py A Tools/c-analyzer/c_parser/parser/_global.py A Tools/c-analyzer/c_parser/parser/_info.py A Tools/c-analyzer/c_parser/parser/_regexes.py A Tools/c-analyzer/c_parser/preprocessor/__init__.py A Tools/c-analyzer/c_parser/preprocessor/__main__.py A Tools/c-analyzer/c_parser/preprocessor/common.py A Tools/c-analyzer/c_parser/preprocessor/errors.py A Tools/c-analyzer/c_parser/preprocessor/gcc.py A Tools/c-analyzer/c_parser/preprocessor/pure.py A Tools/c-analyzer/c_parser/source.py A Tools/c-analyzer/cpython/_analyzer.py A Tools/c-analyzer/cpython/_parser.py A Tools/c-analyzer/cpython/ignored.tsv A Tools/c-analyzer/cpython/known.tsv D Lib/test/test_tools/test_c_analyzer/__init__.py D Lib/test/test_tools/test_c_analyzer/__main__.py D Lib/test/test_tools/test_c_analyzer/test_common/__init__.py D Lib/test/test_tools/test_c_analyzer/test_common/test_files.py D Lib/test/test_tools/test_c_analyzer/test_common/test_info.py D Lib/test/test_tools/test_c_analyzer/test_common/test_show.py D Lib/test/test_tools/test_c_analyzer/test_cpython/__init__.py D Lib/test/test_tools/test_c_analyzer/test_cpython/test___main__.py D Lib/test/test_tools/test_c_analyzer/test_cpython/test_functional.py D Lib/test/test_tools/test_c_analyzer/test_cpython/test_supported.py D Lib/test/test_tools/test_c_analyzer/test_parser/__init__.py D Lib/test/test_tools/test_c_analyzer/test_parser/test_declarations.py D Lib/test/test_tools/test_c_analyzer/test_parser/test_preprocessor.py D Lib/test/test_tools/test_c_analyzer/test_symbols/__init__.py D Lib/test/test_tools/test_c_analyzer/test_symbols/test_info.py D Lib/test/test_tools/test_c_analyzer/test_variables/__init__.py D Lib/test/test_tools/test_c_analyzer/test_variables/test_find.py D Lib/test/test_tools/test_c_analyzer/test_variables/test_info.py D Lib/test/test_tools/test_c_analyzer/test_variables/test_known.py D Lib/test/test_tools/test_c_analyzer/util.py D Tools/c-analyzer/c-globals.py D Tools/c-analyzer/c_analyzer/common/__init__.py D Tools/c-analyzer/c_analyzer/common/files.py D Tools/c-analyzer/c_analyzer/common/info.py D Tools/c-analyzer/c_analyzer/common/show.py D Tools/c-analyzer/c_analyzer/common/util.py D Tools/c-analyzer/c_analyzer/parser/__init__.py D Tools/c-analyzer/c_analyzer/parser/declarations.py D Tools/c-analyzer/c_analyzer/parser/find.py D Tools/c-analyzer/c_analyzer/parser/naive.py D Tools/c-analyzer/c_analyzer/parser/preprocessor.py D Tools/c-analyzer/c_analyzer/parser/source.py D Tools/c-analyzer/c_analyzer/symbols/__init__.py D Tools/c-analyzer/c_analyzer/symbols/_nm.py D Tools/c-analyzer/c_analyzer/symbols/find.py D Tools/c-analyzer/c_analyzer/symbols/info.py D Tools/c-analyzer/c_analyzer/variables/__init__.py D Tools/c-analyzer/c_analyzer/variables/find.py D Tools/c-analyzer/c_analyzer/variables/info.py D Tools/c-analyzer/c_analyzer/variables/known.py D Tools/c-analyzer/cpython/README D Tools/c-analyzer/cpython/_generate.py D Tools/c-analyzer/cpython/files.py D Tools/c-analyzer/cpython/find.py D Tools/c-analyzer/cpython/known.py D Tools/c-analyzer/cpython/supported.py D Tools/c-analyzer/ignored-globals.txt D Tools/c-analyzer/ignored.tsv D Tools/c-analyzer/known.tsv M Tools/c-analyzer/README M Tools/c-analyzer/c_analyzer/__init__.py M Tools/c-analyzer/check-c-globals.py M Tools/c-analyzer/cpython/__init__.py M Tools/c-analyzer/cpython/__main__.py diff --git a/Lib/test/test_tools/test_c_analyzer/__init__.py b/Lib/test/test_tools/test_c_analyzer/__init__.py deleted file mode 100644 index d0b4c045104d7..0000000000000 --- a/Lib/test/test_tools/test_c_analyzer/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -import contextlib -import os.path -import test.test_tools -from test.support import load_package_tests - - - at contextlib.contextmanager -def tool_imports_for_tests(): - test.test_tools.skip_if_missing('c-analyzer') - with test.test_tools.imports_under_tool('c-analyzer'): - yield - - -def load_tests(*args): - return load_package_tests(os.path.dirname(__file__), *args) diff --git a/Lib/test/test_tools/test_c_analyzer/__main__.py b/Lib/test/test_tools/test_c_analyzer/__main__.py deleted file mode 100644 index b5b017de8a8a4..0000000000000 --- a/Lib/test/test_tools/test_c_analyzer/__main__.py +++ /dev/null @@ -1,5 +0,0 @@ -from . import load_tests -import unittest - - -unittest.main() diff --git a/Lib/test/test_tools/test_c_analyzer/test_common/__init__.py b/Lib/test/test_tools/test_c_analyzer/test_common/__init__.py deleted file mode 100644 index bc502ef32d291..0000000000000 --- a/Lib/test/test_tools/test_c_analyzer/test_common/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -import os.path -from test.support import load_package_tests - - -def load_tests(*args): - return load_package_tests(os.path.dirname(__file__), *args) diff --git a/Lib/test/test_tools/test_c_analyzer/test_common/test_files.py b/Lib/test/test_tools/test_c_analyzer/test_common/test_files.py deleted file mode 100644 index 0c97d2a0bbf9a..0000000000000 --- a/Lib/test/test_tools/test_c_analyzer/test_common/test_files.py +++ /dev/null @@ -1,470 +0,0 @@ -import os.path -import unittest - -from .. import tool_imports_for_tests -with tool_imports_for_tests(): - from c_analyzer.common.files import ( - iter_files, _walk_tree, glob_tree, - ) - - -def fixpath(filename): - return filename.replace('/', os.path.sep) - - -class IterFilesTests(unittest.TestCase): - - maxDiff = None - - _return_walk = None - - @property - def calls(self): - try: - return self._calls - except AttributeError: - self._calls = [] - return self._calls - - def set_files(self, *filesperroot): - roots = [] - result = [] - for root, files in filesperroot: - root = fixpath(root) - roots.append(root) - result.append([os.path.join(root, fixpath(f)) - for f in files]) - self._return_walk = result - return roots - - def _walk(self, root, *, suffix=None, walk=None): - self.calls.append(('_walk', (root, suffix, walk))) - return iter(self._return_walk.pop(0)) - - def _glob(self, root, *, suffix=None): - self.calls.append(('_glob', (root, suffix))) - return iter(self._return_walk.pop(0)) - - def test_typical(self): - dirnames = self.set_files( - ('spam', ['file1.c', 'file2.c']), - ('eggs', ['ham/file3.h']), - ) - suffixes = ('.c', '.h') - - files = list(iter_files(dirnames, suffixes, - _glob=self._glob, - _walk=self._walk)) - - self.assertEqual(files, [ - fixpath('spam/file1.c'), - fixpath('spam/file2.c'), - fixpath('eggs/ham/file3.h'), - ]) - self.assertEqual(self.calls, [ - ('_walk', ('spam', None, _walk_tree)), - ('_walk', ('eggs', None, _walk_tree)), - ]) - - def test_single_root(self): - self._return_walk = [ - [fixpath('spam/file1.c'), fixpath('spam/file2.c')], - ] - - files = list(iter_files('spam', '.c', - _glob=self._glob, - _walk=self._walk)) - - self.assertEqual(files, [ - fixpath('spam/file1.c'), - fixpath('spam/file2.c'), - ]) - self.assertEqual(self.calls, [ - ('_walk', ('spam', '.c', _walk_tree)), - ]) - - def test_one_root(self): - self._return_walk = [ - [fixpath('spam/file1.c'), fixpath('spam/file2.c')], - ] - - files = list(iter_files(['spam'], '.c', - _glob=self._glob, - _walk=self._walk)) - - self.assertEqual(files, [ - fixpath('spam/file1.c'), - fixpath('spam/file2.c'), - ]) - self.assertEqual(self.calls, [ - ('_walk', ('spam', '.c', _walk_tree)), - ]) - - def test_multiple_roots(self): - dirnames = self.set_files( - ('spam', ['file1.c', 'file2.c']), - ('eggs', ['ham/file3.c']), - ) - - files = list(iter_files(dirnames, '.c', - _glob=self._glob, - _walk=self._walk)) - - self.assertEqual(files, [ - fixpath('spam/file1.c'), - fixpath('spam/file2.c'), - fixpath('eggs/ham/file3.c'), - ]) - self.assertEqual(self.calls, [ - ('_walk', ('spam', '.c', _walk_tree)), - ('_walk', ('eggs', '.c', _walk_tree)), - ]) - - def test_no_roots(self): - files = list(iter_files([], '.c', - _glob=self._glob, - _walk=self._walk)) - - self.assertEqual(files, []) - self.assertEqual(self.calls, []) - - def test_single_suffix(self): - self._return_walk = [ - [fixpath('spam/file1.c'), - fixpath('spam/eggs/file3.c'), - ], - ] - - files = list(iter_files('spam', '.c', - _glob=self._glob, - _walk=self._walk)) - - self.assertEqual(files, [ - fixpath('spam/file1.c'), - fixpath('spam/eggs/file3.c'), - ]) - self.assertEqual(self.calls, [ - ('_walk', ('spam', '.c', _walk_tree)), - ]) - - def test_one_suffix(self): - self._return_walk = [ - [fixpath('spam/file1.c'), - fixpath('spam/file1.h'), - fixpath('spam/file1.o'), - fixpath('spam/eggs/file3.c'), - ], - ] - - files = list(iter_files('spam', ['.c'], - _glob=self._glob, - _walk=self._walk)) - - self.assertEqual(files, [ - fixpath('spam/file1.c'), - fixpath('spam/eggs/file3.c'), - ]) - self.assertEqual(self.calls, [ - ('_walk', ('spam', None, _walk_tree)), - ]) - - def test_multiple_suffixes(self): - self._return_walk = [ - [fixpath('spam/file1.c'), - fixpath('spam/file1.h'), - fixpath('spam/file1.o'), - fixpath('spam/eggs/file3.c'), - ], - ] - - files = list(iter_files('spam', ('.c', '.h'), - _glob=self._glob, - _walk=self._walk)) - - self.assertEqual(files, [ - fixpath('spam/file1.c'), - fixpath('spam/file1.h'), - fixpath('spam/eggs/file3.c'), - ]) - self.assertEqual(self.calls, [ - ('_walk', ('spam', None, _walk_tree)), - ]) - - def test_no_suffix(self): - expected = [fixpath('spam/file1.c'), - fixpath('spam/file1.h'), - fixpath('spam/file1.o'), - fixpath('spam/eggs/file3.c'), - ] - for suffix in (None, '', ()): - with self.subTest(suffix): - self.calls.clear() - self._return_walk = [list(expected)] - - files = list(iter_files('spam', suffix, - _glob=self._glob, - _walk=self._walk)) - - self.assertEqual(files, expected) - self.assertEqual(self.calls, [ - ('_walk', ('spam', suffix, _walk_tree)), - ]) - - def test_relparent(self): - dirnames = self.set_files( - ('/x/y/z/spam', ['file1.c', 'file2.c']), - ('/x/y/z/eggs', ['ham/file3.c']), - ) - - files = list(iter_files(dirnames, '.c', fixpath('/x/y'), - _glob=self._glob, - _walk=self._walk)) - - self.assertEqual(files, [ - fixpath('z/spam/file1.c'), - fixpath('z/spam/file2.c'), - fixpath('z/eggs/ham/file3.c'), - ]) - self.assertEqual(self.calls, [ - ('_walk', (fixpath('/x/y/z/spam'), '.c', _walk_tree)), - ('_walk', (fixpath('/x/y/z/eggs'), '.c', _walk_tree)), - ]) - - def test_glob(self): - dirnames = self.set_files( - ('spam', ['file1.c', 'file2.c']), - ('eggs', ['ham/file3.c']), - ) - - files = list(iter_files(dirnames, '.c', - get_files=glob_tree, - _walk=self._walk, - _glob=self._glob)) - - self.assertEqual(files, [ - fixpath('spam/file1.c'), - fixpath('spam/file2.c'), - fixpath('eggs/ham/file3.c'), - ]) - self.assertEqual(self.calls, [ - ('_glob', ('spam', '.c')), - ('_glob', ('eggs', '.c')), - ]) - - - def test_alt_walk_func(self): - dirnames = self.set_files( - ('spam', ['file1.c', 'file2.c']), - ('eggs', ['ham/file3.c']), - ) - def get_files(root): - return None - - files = list(iter_files(dirnames, '.c', - get_files=get_files, - _walk=self._walk, - _glob=self._glob)) - - self.assertEqual(files, [ - fixpath('spam/file1.c'), - fixpath('spam/file2.c'), - fixpath('eggs/ham/file3.c'), - ]) - self.assertEqual(self.calls, [ - ('_walk', ('spam', '.c', get_files)), - ('_walk', ('eggs', '.c', get_files)), - ]) - - - - - - -# def test_no_dirnames(self): -# dirnames = [] -# filter_by_name = None -# -# files = list(iter_files(dirnames, filter_by_name, -# _walk=self._walk)) -# -# self.assertEqual(files, []) -# self.assertEqual(self.calls, []) -# -# def test_no_filter(self): -# self._return_walk = [ -# [('spam', (), ('file1', 'file2.c', 'file3.h', 'file4.o')), -# ], -# ] -# dirnames = [ -# 'spam', -# ] -# filter_by_name = None -# -# files = list(iter_files(dirnames, filter_by_name, -# _walk=self._walk)) -# -# self.assertEqual(files, [ -# fixpath('spam/file1'), -# fixpath('spam/file2.c'), -# fixpath('spam/file3.h'), -# fixpath('spam/file4.o'), -# ]) -# self.assertEqual(self.calls, [ -# ('_walk', ('spam',)), -# ]) -# -# def test_no_files(self): -# self._return_walk = [ -# [('spam', (), ()), -# ], -# [(fixpath('eggs/ham'), (), ()), -# ], -# ] -# dirnames = [ -# 'spam', -# fixpath('eggs/ham'), -# ] -# filter_by_name = None -# -# files = list(iter_files(dirnames, filter_by_name, -# _walk=self._walk)) -# -# self.assertEqual(files, []) -# self.assertEqual(self.calls, [ -# ('_walk', ('spam',)), -# ('_walk', (fixpath('eggs/ham'),)), -# ]) -# -# def test_tree(self): -# self._return_walk = [ -# [('spam', ('sub1', 'sub2', 'sub3'), ('file1',)), -# (fixpath('spam/sub1'), ('sub1sub1',), ('file2', 'file3')), -# (fixpath('spam/sub1/sub1sub1'), (), ('file4',)), -# (fixpath('spam/sub2'), (), ()), -# (fixpath('spam/sub3'), (), ('file5',)), -# ], -# [(fixpath('eggs/ham'), (), ('file6',)), -# ], -# ] -# dirnames = [ -# 'spam', -# fixpath('eggs/ham'), -# ] -# filter_by_name = None -# -# files = list(iter_files(dirnames, filter_by_name, -# _walk=self._walk)) -# -# self.assertEqual(files, [ -# fixpath('spam/file1'), -# fixpath('spam/sub1/file2'), -# fixpath('spam/sub1/file3'), -# fixpath('spam/sub1/sub1sub1/file4'), -# fixpath('spam/sub3/file5'), -# fixpath('eggs/ham/file6'), -# ]) -# self.assertEqual(self.calls, [ -# ('_walk', ('spam',)), -# ('_walk', (fixpath('eggs/ham'),)), -# ]) -# -# def test_filter_suffixes(self): -# self._return_walk = [ -# [('spam', (), ('file1', 'file2.c', 'file3.h', 'file4.o')), -# ], -# ] -# dirnames = [ -# 'spam', -# ] -# filter_by_name = ('.c', '.h') -# -# files = list(iter_files(dirnames, filter_by_name, -# _walk=self._walk)) -# -# self.assertEqual(files, [ -# fixpath('spam/file2.c'), -# fixpath('spam/file3.h'), -# ]) -# self.assertEqual(self.calls, [ -# ('_walk', ('spam',)), -# ]) -# -# def test_some_filtered(self): -# self._return_walk = [ -# [('spam', (), ('file1', 'file2', 'file3', 'file4')), -# ], -# ] -# dirnames = [ -# 'spam', -# ] -# def filter_by_name(filename, results=[False, True, False, True]): -# self.calls.append(('filter_by_name', (filename,))) -# return results.pop(0) -# -# files = list(iter_files(dirnames, filter_by_name, -# _walk=self._walk)) -# -# self.assertEqual(files, [ -# fixpath('spam/file2'), -# fixpath('spam/file4'), -# ]) -# self.assertEqual(self.calls, [ -# ('_walk', ('spam',)), -# ('filter_by_name', ('file1',)), -# ('filter_by_name', ('file2',)), -# ('filter_by_name', ('file3',)), -# ('filter_by_name', ('file4',)), -# ]) -# -# def test_none_filtered(self): -# self._return_walk = [ -# [('spam', (), ('file1', 'file2', 'file3', 'file4')), -# ], -# ] -# dirnames = [ -# 'spam', -# ] -# def filter_by_name(filename, results=[True, True, True, True]): -# self.calls.append(('filter_by_name', (filename,))) -# return results.pop(0) -# -# files = list(iter_files(dirnames, filter_by_name, -# _walk=self._walk)) -# -# self.assertEqual(files, [ -# fixpath('spam/file1'), -# fixpath('spam/file2'), -# fixpath('spam/file3'), -# fixpath('spam/file4'), -# ]) -# self.assertEqual(self.calls, [ -# ('_walk', ('spam',)), -# ('filter_by_name', ('file1',)), -# ('filter_by_name', ('file2',)), -# ('filter_by_name', ('file3',)), -# ('filter_by_name', ('file4',)), -# ]) -# -# def test_all_filtered(self): -# self._return_walk = [ -# [('spam', (), ('file1', 'file2', 'file3', 'file4')), -# ], -# ] -# dirnames = [ -# 'spam', -# ] -# def filter_by_name(filename, results=[False, False, False, False]): -# self.calls.append(('filter_by_name', (filename,))) -# return results.pop(0) -# -# files = list(iter_files(dirnames, filter_by_name, -# _walk=self._walk)) -# -# self.assertEqual(files, []) -# self.assertEqual(self.calls, [ -# ('_walk', ('spam',)), -# ('filter_by_name', ('file1',)), -# ('filter_by_name', ('file2',)), -# ('filter_by_name', ('file3',)), -# ('filter_by_name', ('file4',)), -# ]) diff --git a/Lib/test/test_tools/test_c_analyzer/test_common/test_info.py b/Lib/test/test_tools/test_c_analyzer/test_common/test_info.py deleted file mode 100644 index 69dbb582c6b68..0000000000000 --- a/Lib/test/test_tools/test_c_analyzer/test_common/test_info.py +++ /dev/null @@ -1,197 +0,0 @@ -import string -import unittest - -from ..util import PseudoStr, StrProxy, Object -from .. import tool_imports_for_tests -with tool_imports_for_tests(): - from c_analyzer.common.info import ( - UNKNOWN, - ID, - ) - - -class IDTests(unittest.TestCase): - - VALID_ARGS = ( - 'x/y/z/spam.c', - 'func', - 'eggs', - ) - VALID_KWARGS = dict(zip(ID._fields, VALID_ARGS)) - VALID_EXPECTED = VALID_ARGS - - def test_from_raw(self): - tests = [ - ('', None), - (None, None), - ('spam', (None, None, 'spam')), - (('spam',), (None, None, 'spam')), - (('x/y/z/spam.c', 'spam'), ('x/y/z/spam.c', None, 'spam')), - (self.VALID_ARGS, self.VALID_EXPECTED), - (self.VALID_KWARGS, self.VALID_EXPECTED), - ] - for raw, expected in tests: - with self.subTest(raw): - id = ID.from_raw(raw) - - self.assertEqual(id, expected) - - def test_minimal(self): - id = ID( - filename=None, - funcname=None, - name='eggs', - ) - - self.assertEqual(id, ( - None, - None, - 'eggs', - )) - - def test_init_typical_global(self): - id = ID( - filename='x/y/z/spam.c', - funcname=None, - name='eggs', - ) - - self.assertEqual(id, ( - 'x/y/z/spam.c', - None, - 'eggs', - )) - - def test_init_typical_local(self): - id = ID( - filename='x/y/z/spam.c', - funcname='func', - name='eggs', - ) - - self.assertEqual(id, ( - 'x/y/z/spam.c', - 'func', - 'eggs', - )) - - def test_init_all_missing(self): - for value in ('', None): - with self.subTest(repr(value)): - id = ID( - filename=value, - funcname=value, - name=value, - ) - - self.assertEqual(id, ( - None, - None, - None, - )) - - def test_init_all_coerced(self): - tests = [ - ('str subclass', - dict( - filename=PseudoStr('x/y/z/spam.c'), - funcname=PseudoStr('func'), - name=PseudoStr('eggs'), - ), - ('x/y/z/spam.c', - 'func', - 'eggs', - )), - ('non-str', - dict( - filename=StrProxy('x/y/z/spam.c'), - funcname=Object(), - name=('a', 'b', 'c'), - ), - ('x/y/z/spam.c', - '', - "('a', 'b', 'c')", - )), - ] - for summary, kwargs, expected in tests: - with self.subTest(summary): - id = ID(**kwargs) - - for field in ID._fields: - value = getattr(id, field) - self.assertIs(type(value), str) - self.assertEqual(tuple(id), expected) - - def test_iterable(self): - id = ID(**self.VALID_KWARGS) - - filename, funcname, name = id - - values = (filename, funcname, name) - for value, expected in zip(values, self.VALID_EXPECTED): - self.assertEqual(value, expected) - - def test_fields(self): - id = ID('a', 'b', 'z') - - self.assertEqual(id.filename, 'a') - self.assertEqual(id.funcname, 'b') - self.assertEqual(id.name, 'z') - - def test_validate_typical(self): - id = ID( - filename='x/y/z/spam.c', - funcname='func', - name='eggs', - ) - - id.validate() # This does not fail. - - def test_validate_missing_field(self): - for field in ID._fields: - with self.subTest(field): - id = ID(**self.VALID_KWARGS) - id = id._replace(**{field: None}) - - if field == 'funcname': - id.validate() # The field can be missing (not set). - id = id._replace(filename=None) - id.validate() # Both fields can be missing (not set). - continue - - with self.assertRaises(TypeError): - id.validate() - - def test_validate_bad_field(self): - badch = tuple(c for c in string.punctuation + string.digits) - notnames = ( - '1a', - 'a.b', - 'a-b', - '&a', - 'a++', - ) + badch - tests = [ - ('filename', ()), # Any non-empty str is okay. - ('funcname', notnames), - ('name', notnames), - ] - seen = set() - for field, invalid in tests: - for value in invalid: - seen.add(value) - with self.subTest(f'{field}={value!r}'): - id = ID(**self.VALID_KWARGS) - id = id._replace(**{field: value}) - - with self.assertRaises(ValueError): - id.validate() - - for field, invalid in tests: - valid = seen - set(invalid) - for value in valid: - with self.subTest(f'{field}={value!r}'): - id = ID(**self.VALID_KWARGS) - id = id._replace(**{field: value}) - - id.validate() # This does not fail. diff --git a/Lib/test/test_tools/test_c_analyzer/test_common/test_show.py b/Lib/test/test_tools/test_c_analyzer/test_common/test_show.py deleted file mode 100644 index 91ca2f3b344dd..0000000000000 --- a/Lib/test/test_tools/test_c_analyzer/test_common/test_show.py +++ /dev/null @@ -1,54 +0,0 @@ -import unittest - -from .. import tool_imports_for_tests -with tool_imports_for_tests(): - from c_analyzer.variables import info - from c_analyzer.common.show import ( - basic, - ) - - -TYPICAL = [ - info.Variable.from_parts('src1/spam.c', None, 'var1', 'static const char *'), - info.Variable.from_parts('src1/spam.c', 'ham', 'initialized', 'static int'), - info.Variable.from_parts('src1/spam.c', None, 'var2', 'static PyObject *'), - info.Variable.from_parts('src1/eggs.c', 'tofu', 'ready', 'static int'), - info.Variable.from_parts('src1/spam.c', None, 'freelist', 'static (PyTupleObject *)[10]'), - info.Variable.from_parts('src1/sub/ham.c', None, 'var1', 'static const char const *'), - info.Variable.from_parts('src2/jam.c', None, 'var1', 'static int'), - info.Variable.from_parts('src2/jam.c', None, 'var2', 'static MyObject *'), - info.Variable.from_parts('Include/spam.h', None, 'data', 'static const int'), - ] - - -class BasicTests(unittest.TestCase): - - maxDiff = None - - def setUp(self): - self.lines = [] - - def print(self, line): - self.lines.append(line) - - def test_typical(self): - basic(TYPICAL, - _print=self.print) - - self.assertEqual(self.lines, [ - 'src1/spam.c:var1 static const char *', - 'src1/spam.c:ham():initialized static int', - 'src1/spam.c:var2 static PyObject *', - 'src1/eggs.c:tofu():ready static int', - 'src1/spam.c:freelist static (PyTupleObject *)[10]', - 'src1/sub/ham.c:var1 static const char const *', - 'src2/jam.c:var1 static int', - 'src2/jam.c:var2 static MyObject *', - 'Include/spam.h:data static const int', - ]) - - def test_no_rows(self): - basic([], - _print=self.print) - - self.assertEqual(self.lines, []) diff --git a/Lib/test/test_tools/test_c_analyzer/test_cpython/__init__.py b/Lib/test/test_tools/test_c_analyzer/test_cpython/__init__.py deleted file mode 100644 index bc502ef32d291..0000000000000 --- a/Lib/test/test_tools/test_c_analyzer/test_cpython/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -import os.path -from test.support import load_package_tests - - -def load_tests(*args): - return load_package_tests(os.path.dirname(__file__), *args) diff --git a/Lib/test/test_tools/test_c_analyzer/test_cpython/test___main__.py b/Lib/test/test_tools/test_c_analyzer/test_cpython/test___main__.py deleted file mode 100644 index 6d69ed7525b59..0000000000000 --- a/Lib/test/test_tools/test_c_analyzer/test_cpython/test___main__.py +++ /dev/null @@ -1,296 +0,0 @@ -import sys -import unittest - -from .. import tool_imports_for_tests -with tool_imports_for_tests(): - from c_analyzer.variables import info - from cpython import SOURCE_DIRS - from cpython.supported import IGNORED_FILE - from cpython.known import DATA_FILE as KNOWN_FILE - from cpython.__main__ import ( - cmd_check, cmd_show, parse_args, main, - ) - - -TYPICAL = [ - (info.Variable.from_parts('src1/spam.c', None, 'var1', 'const char *'), - True, - ), - (info.Variable.from_parts('src1/spam.c', 'ham', 'initialized', 'int'), - True, - ), - (info.Variable.from_parts('src1/spam.c', None, 'var2', 'PyObject *'), - False, - ), - (info.Variable.from_parts('src1/eggs.c', 'tofu', 'ready', 'int'), - True, - ), - (info.Variable.from_parts('src1/spam.c', None, 'freelist', '(PyTupleObject *)[10]'), - False, - ), - (info.Variable.from_parts('src1/sub/ham.c', None, 'var1', 'const char const *'), - True, - ), - (info.Variable.from_parts('src2/jam.c', None, 'var1', 'int'), - True, - ), - (info.Variable.from_parts('src2/jam.c', None, 'var2', 'MyObject *'), - False, - ), - (info.Variable.from_parts('Include/spam.h', None, 'data', 'const int'), - True, - ), - ] - - -class CMDBase(unittest.TestCase): - - maxDiff = None - -# _return_known_from_file = None -# _return_ignored_from_file = None - _return_find = () - - @property - def calls(self): - try: - return self._calls - except AttributeError: - self._calls = [] - return self._calls - -# def _known_from_file(self, *args): -# self.calls.append(('_known_from_file', args)) -# return self._return_known_from_file or {} -# -# def _ignored_from_file(self, *args): -# self.calls.append(('_ignored_from_file', args)) -# return self._return_ignored_from_file or {} - - def _find(self, known, ignored, skip_objects=False): - self.calls.append(('_find', (known, ignored, skip_objects))) - return self._return_find - - def _show(self, *args): - self.calls.append(('_show', args)) - - def _print(self, *args): - self.calls.append(('_print', args)) - - -class CheckTests(CMDBase): - - def test_defaults(self): - self._return_find = [] - - cmd_check('check', - _find=self._find, - _show=self._show, - _print=self._print, - ) - - self.assertEqual( - self.calls[0], - ('_find', (KNOWN_FILE, IGNORED_FILE, False)), - ) - - def test_all_supported(self): - self._return_find = [(v, s) for v, s in TYPICAL if s] - dirs = ['src1', 'src2', 'Include'] - - cmd_check('check', - known='known.tsv', - ignored='ignored.tsv', - _find=self._find, - _show=self._show, - _print=self._print, - ) - - self.assertEqual(self.calls, [ - ('_find', ('known.tsv', 'ignored.tsv', False)), - #('_print', ('okay',)), - ]) - - def test_some_unsupported(self): - self._return_find = TYPICAL - - with self.assertRaises(SystemExit) as cm: - cmd_check('check', - known='known.tsv', - ignored='ignored.tsv', - _find=self._find, - _show=self._show, - _print=self._print, - ) - - unsupported = [v for v, s in TYPICAL if not s] - self.assertEqual(self.calls, [ - ('_find', ('known.tsv', 'ignored.tsv', False)), - ('_print', ('ERROR: found unsupported global variables',)), - ('_print', ()), - ('_show', (sorted(unsupported),)), - ('_print', (' (3 total)',)), - ]) - self.assertEqual(cm.exception.code, 1) - - -class ShowTests(CMDBase): - - def test_defaults(self): - self._return_find = [] - - cmd_show('show', - _find=self._find, - _show=self._show, - _print=self._print, - ) - - self.assertEqual( - self.calls[0], - ('_find', (KNOWN_FILE, IGNORED_FILE, False)), - ) - - def test_typical(self): - self._return_find = TYPICAL - - cmd_show('show', - known='known.tsv', - ignored='ignored.tsv', - _find=self._find, - _show=self._show, - _print=self._print, - ) - - supported = [v for v, s in TYPICAL if s] - unsupported = [v for v, s in TYPICAL if not s] - self.assertEqual(self.calls, [ - ('_find', ('known.tsv', 'ignored.tsv', False)), - ('_print', ('supported:',)), - ('_print', ('----------',)), - ('_show', (sorted(supported),)), - ('_print', (' (6 total)',)), - ('_print', ()), - ('_print', ('unsupported:',)), - ('_print', ('------------',)), - ('_show', (sorted(unsupported),)), - ('_print', (' (3 total)',)), - ]) - - -class ParseArgsTests(unittest.TestCase): - - maxDiff = None - - def test_no_args(self): - self.errmsg = None - def fail(msg): - self.errmsg = msg - sys.exit(msg) - - with self.assertRaises(SystemExit): - parse_args('cg', [], _fail=fail) - - self.assertEqual(self.errmsg, 'missing command') - - def test_check_no_args(self): - cmd, cmdkwargs = parse_args('cg', [ - 'check', - ]) - - self.assertEqual(cmd, 'check') - self.assertEqual(cmdkwargs, { - 'ignored': IGNORED_FILE, - 'known': KNOWN_FILE, - #'dirs': SOURCE_DIRS, - }) - - def test_check_full_args(self): - cmd, cmdkwargs = parse_args('cg', [ - 'check', - '--ignored', 'spam.tsv', - '--known', 'eggs.tsv', - #'dir1', - #'dir2', - #'dir3', - ]) - - self.assertEqual(cmd, 'check') - self.assertEqual(cmdkwargs, { - 'ignored': 'spam.tsv', - 'known': 'eggs.tsv', - #'dirs': ['dir1', 'dir2', 'dir3'] - }) - - def test_show_no_args(self): - cmd, cmdkwargs = parse_args('cg', [ - 'show', - ]) - - self.assertEqual(cmd, 'show') - self.assertEqual(cmdkwargs, { - 'ignored': IGNORED_FILE, - 'known': KNOWN_FILE, - #'dirs': SOURCE_DIRS, - 'skip_objects': False, - }) - - def test_show_full_args(self): - cmd, cmdkwargs = parse_args('cg', [ - 'show', - '--ignored', 'spam.tsv', - '--known', 'eggs.tsv', - #'dir1', - #'dir2', - #'dir3', - ]) - - self.assertEqual(cmd, 'show') - self.assertEqual(cmdkwargs, { - 'ignored': 'spam.tsv', - 'known': 'eggs.tsv', - #'dirs': ['dir1', 'dir2', 'dir3'], - 'skip_objects': False, - }) - - -def new_stub_commands(*names): - calls = [] - def cmdfunc(cmd, **kwargs): - calls.append((cmd, kwargs)) - commands = {name: cmdfunc for name in names} - return commands, calls - - -class MainTests(unittest.TestCase): - - def test_no_command(self): - with self.assertRaises(ValueError): - main(None, {}) - - def test_check(self): - commands, calls = new_stub_commands('check', 'show') - - cmdkwargs = { - 'ignored': 'spam.tsv', - 'known': 'eggs.tsv', - 'dirs': ['dir1', 'dir2', 'dir3'], - } - main('check', cmdkwargs, _COMMANDS=commands) - - self.assertEqual(calls, [ - ('check', cmdkwargs), - ]) - - def test_show(self): - commands, calls = new_stub_commands('check', 'show') - - cmdkwargs = { - 'ignored': 'spam.tsv', - 'known': 'eggs.tsv', - 'dirs': ['dir1', 'dir2', 'dir3'], - } - main('show', cmdkwargs, _COMMANDS=commands) - - self.assertEqual(calls, [ - ('show', cmdkwargs), - ]) diff --git a/Lib/test/test_tools/test_c_analyzer/test_cpython/test_functional.py b/Lib/test/test_tools/test_c_analyzer/test_cpython/test_functional.py deleted file mode 100644 index 927979048448f..0000000000000 --- a/Lib/test/test_tools/test_c_analyzer/test_cpython/test_functional.py +++ /dev/null @@ -1,34 +0,0 @@ -import unittest - -from .. import tool_imports_for_tests -with tool_imports_for_tests(): - pass - - -class SelfCheckTests(unittest.TestCase): - - @unittest.expectedFailure - def test_known(self): - # Make sure known macros & vartypes aren't hiding unknown local types. - # XXX finish! - raise NotImplementedError - - @unittest.expectedFailure - def test_compare_nm_results(self): - # Make sure the "show" results match the statics found by "nm" command. - # XXX Skip if "nm" is not available. - # XXX finish! - raise NotImplementedError - - -class DummySourceTests(unittest.TestCase): - - @unittest.expectedFailure - def test_check(self): - # XXX finish! - raise NotImplementedError - - @unittest.expectedFailure - def test_show(self): - # XXX finish! - raise NotImplementedError diff --git a/Lib/test/test_tools/test_c_analyzer/test_cpython/test_supported.py b/Lib/test/test_tools/test_c_analyzer/test_cpython/test_supported.py deleted file mode 100644 index a244b97e1fc7c..0000000000000 --- a/Lib/test/test_tools/test_c_analyzer/test_cpython/test_supported.py +++ /dev/null @@ -1,98 +0,0 @@ -import re -import textwrap -import unittest - -from .. import tool_imports_for_tests -with tool_imports_for_tests(): - from c_analyzer.common.info import ID - from c_analyzer.variables.info import Variable - from cpython.supported import ( - is_supported, ignored_from_file, - ) - - -class IsSupportedTests(unittest.TestCase): - - @unittest.expectedFailure - def test_supported(self): - statics = [ - Variable('src1/spam.c', None, 'var1', 'const char *'), - Variable('src1/spam.c', None, 'var1', 'int'), - ] - for static in statics: - with self.subTest(static): - result = is_supported(static) - - self.assertTrue(result) - - @unittest.expectedFailure - def test_not_supported(self): - statics = [ - Variable('src1/spam.c', None, 'var1', 'PyObject *'), - Variable('src1/spam.c', None, 'var1', 'PyObject[10]'), - ] - for static in statics: - with self.subTest(static): - result = is_supported(static) - - self.assertFalse(result) - - -class IgnoredFromFileTests(unittest.TestCase): - - maxDiff = None - - _return_read_tsv = () - - @property - def calls(self): - try: - return self._calls - except AttributeError: - self._calls = [] - return self._calls - - def _read_tsv(self, *args): - self.calls.append(('_read_tsv', args)) - return self._return_read_tsv - - def test_typical(self): - lines = textwrap.dedent(''' - filename funcname name kind reason - file1.c - var1 variable ... - file1.c func1 local1 variable | - file1.c - var2 variable ??? - file1.c func2 local2 variable | - file2.c - var1 variable reasons - ''').strip().splitlines() - lines = [re.sub(r'\s{1,8}', '\t', line, 4).replace('|', '') - for line in lines] - self._return_read_tsv = [tuple(v.strip() for v in line.split('\t')) - for line in lines[1:]] - - ignored = ignored_from_file('spam.c', _read_tsv=self._read_tsv) - - self.assertEqual(ignored, { - 'variables': { - ID('file1.c', '', 'var1'): '...', - ID('file1.c', 'func1', 'local1'): '', - ID('file1.c', '', 'var2'): '???', - ID('file1.c', 'func2', 'local2'): '', - ID('file2.c', '', 'var1'): 'reasons', - }, - }) - self.assertEqual(self.calls, [ - ('_read_tsv', ('spam.c', 'filename\tfuncname\tname\tkind\treason')), - ]) - - def test_empty(self): - self._return_read_tsv = [] - - ignored = ignored_from_file('spam.c', _read_tsv=self._read_tsv) - - self.assertEqual(ignored, { - 'variables': {}, - }) - self.assertEqual(self.calls, [ - ('_read_tsv', ('spam.c', 'filename\tfuncname\tname\tkind\treason')), - ]) diff --git a/Lib/test/test_tools/test_c_analyzer/test_parser/__init__.py b/Lib/test/test_tools/test_c_analyzer/test_parser/__init__.py deleted file mode 100644 index bc502ef32d291..0000000000000 --- a/Lib/test/test_tools/test_c_analyzer/test_parser/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -import os.path -from test.support import load_package_tests - - -def load_tests(*args): - return load_package_tests(os.path.dirname(__file__), *args) diff --git a/Lib/test/test_tools/test_c_analyzer/test_parser/test_declarations.py b/Lib/test/test_tools/test_c_analyzer/test_parser/test_declarations.py deleted file mode 100644 index 674fcb1af1c7a..0000000000000 --- a/Lib/test/test_tools/test_c_analyzer/test_parser/test_declarations.py +++ /dev/null @@ -1,795 +0,0 @@ -import textwrap -import unittest - -from .. import tool_imports_for_tests -with tool_imports_for_tests(): - from c_analyzer.parser.declarations import ( - iter_global_declarations, iter_local_statements, - parse_func, _parse_var, parse_compound, - iter_variables, - ) - - -class TestCaseBase(unittest.TestCase): - - maxDiff = None - - @property - def calls(self): - try: - return self._calls - except AttributeError: - self._calls = [] - return self._calls - - -class IterGlobalDeclarationsTests(TestCaseBase): - - def test_functions(self): - tests = [ - (textwrap.dedent(''' - void func1() { - return; - } - '''), - textwrap.dedent(''' - void func1() { - return; - } - ''').strip(), - ), - (textwrap.dedent(''' - static unsigned int * _func1( - const char *arg1, - int *arg2 - long long arg3 - ) - { - return _do_something(arg1, arg2, arg3); - } - '''), - textwrap.dedent(''' - static unsigned int * _func1( const char *arg1, int *arg2 long long arg3 ) { - return _do_something(arg1, arg2, arg3); - } - ''').strip(), - ), - (textwrap.dedent(''' - static PyObject * - _func1(const char *arg1, PyObject *arg2) - { - static int initialized = 0; - if (!initialized) { - initialized = 1; - _init(arg1); - } - - PyObject *result = _do_something(arg1, arg2); - Py_INCREF(result); - return result; - } - '''), - textwrap.dedent(''' - static PyObject * _func1(const char *arg1, PyObject *arg2) { - static int initialized = 0; - if (!initialized) { - initialized = 1; - _init(arg1); - } - PyObject *result = _do_something(arg1, arg2); - Py_INCREF(result); - return result; - } - ''').strip(), - ), - ] - for lines, expected in tests: - body = textwrap.dedent( - expected.partition('{')[2].rpartition('}')[0] - ).strip() - expected = (expected, body) - with self.subTest(lines): - lines = lines.splitlines() - - stmts = list(iter_global_declarations(lines)) - - self.assertEqual(stmts, [expected]) - - @unittest.expectedFailure - def test_declarations(self): - tests = [ - 'int spam;', - 'long long spam;', - 'static const int const *spam;', - 'int spam;', - 'typedef int myint;', - 'typedef PyObject * (*unaryfunc)(PyObject *);', - # typedef struct - # inline struct - # enum - # inline enum - ] - for text in tests: - expected = (text, - ' '.join(l.strip() for l in text.splitlines())) - with self.subTest(lines): - lines = lines.splitlines() - - stmts = list(iter_global_declarations(lines)) - - self.assertEqual(stmts, [expected]) - - @unittest.expectedFailure - def test_declaration_multiple_vars(self): - lines = ['static const int const *spam, *ham=NULL, eggs = 3;'] - - stmts = list(iter_global_declarations(lines)) - - self.assertEqual(stmts, [ - ('static const int const *spam;', None), - ('static const int *ham=NULL;', None), - ('static const int eggs = 3;', None), - ]) - - def test_mixed(self): - lines = textwrap.dedent(''' - int spam; - static const char const *eggs; - - PyObject * start(void) { - static int initialized = 0; - if (initialized) { - initialized = 1; - init(); - } - return _start(); - } - - char* ham; - - static int stop(char *reason) { - ham = reason; - return _stop(); - } - ''').splitlines() - expected = [ - (textwrap.dedent(''' - PyObject * start(void) { - static int initialized = 0; - if (initialized) { - initialized = 1; - init(); - } - return _start(); - } - ''').strip(), - textwrap.dedent(''' - static int initialized = 0; - if (initialized) { - initialized = 1; - init(); - } - return _start(); - ''').strip(), - ), - (textwrap.dedent(''' - static int stop(char *reason) { - ham = reason; - return _stop(); - } - ''').strip(), - textwrap.dedent(''' - ham = reason; - return _stop(); - ''').strip(), - ), - ] - - stmts = list(iter_global_declarations(lines)) - - self.assertEqual(stmts, expected) - #self.assertEqual([stmt for stmt, _ in stmts], - # [stmt for stmt, _ in expected]) - #self.assertEqual([body for _, body in stmts], - # [body for _, body in expected]) - - def test_no_statements(self): - lines = [] - - stmts = list(iter_global_declarations(lines)) - - self.assertEqual(stmts, []) - - def test_bogus(self): - tests = [ - (textwrap.dedent(''' - int spam; - static const char const *eggs; - - PyObject * start(void) { - static int initialized = 0; - if (initialized) { - initialized = 1; - init(); - } - return _start(); - } - - char* ham; - - static int _stop(void) { - // missing closing bracket - - static int stop(char *reason) { - ham = reason; - return _stop(); - } - '''), - [(textwrap.dedent(''' - PyObject * start(void) { - static int initialized = 0; - if (initialized) { - initialized = 1; - init(); - } - return _start(); - } - ''').strip(), - textwrap.dedent(''' - static int initialized = 0; - if (initialized) { - initialized = 1; - init(); - } - return _start(); - ''').strip(), - ), - # Neither "stop()" nor "_stop()" are here. - ], - ), - ] - for lines, expected in tests: - with self.subTest(lines): - lines = lines.splitlines() - - stmts = list(iter_global_declarations(lines)) - - self.assertEqual(stmts, expected) - #self.assertEqual([stmt for stmt, _ in stmts], - # [stmt for stmt, _ in expected]) - #self.assertEqual([body for _, body in stmts], - # [body for _, body in expected]) - - def test_ignore_comments(self): - tests = [ - ('// msg', None), - ('// int stmt;', None), - (' // ... ', None), - ('// /*', None), - ('/* int stmt; */', None), - (""" - /** - * ... - * int stmt; - */ - """, None), - ] - for lines, expected in tests: - with self.subTest(lines): - lines = lines.splitlines() - - stmts = list(iter_global_declarations(lines)) - - self.assertEqual(stmts, [expected] if expected else []) - - -class IterLocalStatementsTests(TestCaseBase): - - def test_vars(self): - tests = [ - # POTS - 'int spam;', - 'unsigned int spam;', - 'char spam;', - 'float spam;', - - # typedefs - 'uint spam;', - 'MyType spam;', - - # complex - 'struct myspam spam;', - 'union choice spam;', - # inline struct - # inline union - # enum? - ] - # pointers - tests.extend([ - # POTS - 'int * spam;', - 'unsigned int * spam;', - 'char *spam;', - 'char const *spam = "spamspamspam...";', - # typedefs - 'MyType *spam;', - # complex - 'struct myspam *spam;', - 'union choice *spam;', - # packed with details - 'const char const *spam;', - # void pointer - 'void *data = NULL;', - # function pointers - 'int (* func)(char *arg1);', - 'char * (* func)(void);', - ]) - # storage class - tests.extend([ - 'static int spam;', - 'extern int spam;', - 'static unsigned int spam;', - 'static struct myspam spam;', - ]) - # type qualifier - tests.extend([ - 'const int spam;', - 'const unsigned int spam;', - 'const struct myspam spam;', - ]) - # combined - tests.extend([ - 'const char *spam = eggs;', - 'static const char const *spam = "spamspamspam...";', - 'extern const char const *spam;', - 'static void *data = NULL;', - 'static int (const * func)(char *arg1) = func1;', - 'static char * (* func)(void);', - ]) - for line in tests: - expected = line - with self.subTest(line): - stmts = list(iter_local_statements([line])) - - self.assertEqual(stmts, [(expected, None)]) - - @unittest.expectedFailure - def test_vars_multiline_var(self): - lines = textwrap.dedent(''' - PyObject * - spam - = NULL; - ''').splitlines() - expected = 'PyObject * spam = NULL;' - - stmts = list(iter_local_statements(lines)) - - self.assertEqual(stmts, [(expected, None)]) - - @unittest.expectedFailure - def test_declaration_multiple_vars(self): - lines = ['static const int const *spam, *ham=NULL, ham2[]={1, 2, 3}, ham3[2]={1, 2}, eggs = 3;'] - - stmts = list(iter_global_declarations(lines)) - - self.assertEqual(stmts, [ - ('static const int const *spam;', None), - ('static const int *ham=NULL;', None), - ('static const int ham[]={1, 2, 3};', None), - ('static const int ham[2]={1, 2};', None), - ('static const int eggs = 3;', None), - ]) - - @unittest.expectedFailure - def test_other_simple(self): - raise NotImplementedError - - @unittest.expectedFailure - def test_compound(self): - raise NotImplementedError - - @unittest.expectedFailure - def test_mixed(self): - raise NotImplementedError - - def test_no_statements(self): - lines = [] - - stmts = list(iter_local_statements(lines)) - - self.assertEqual(stmts, []) - - @unittest.expectedFailure - def test_bogus(self): - raise NotImplementedError - - def test_ignore_comments(self): - tests = [ - ('// msg', None), - ('// int stmt;', None), - (' // ... ', None), - ('// /*', None), - ('/* int stmt; */', None), - (""" - /** - * ... - * int stmt; - */ - """, None), - # mixed with statements - ('int stmt; // ...', ('int stmt;', None)), - ( 'int stmt; /* ... */', ('int stmt;', None)), - ( '/* ... */ int stmt;', ('int stmt;', None)), - ] - for lines, expected in tests: - with self.subTest(lines): - lines = lines.splitlines() - - stmts = list(iter_local_statements(lines)) - - self.assertEqual(stmts, [expected] if expected else []) - - -class ParseFuncTests(TestCaseBase): - - def test_typical(self): - tests = [ - ('PyObject *\nspam(char *a)\n{\nreturn _spam(a);\n}', - 'return _spam(a);', - ('spam', 'PyObject * spam(char *a)'), - ), - ] - for stmt, body, expected in tests: - with self.subTest(stmt): - name, signature = parse_func(stmt, body) - - self.assertEqual((name, signature), expected) - - -class ParseVarTests(TestCaseBase): - - def test_typical(self): - tests = [ - # POTS - ('int spam;', ('spam', 'int')), - ('unsigned int spam;', ('spam', 'unsigned int')), - ('char spam;', ('spam', 'char')), - ('float spam;', ('spam', 'float')), - - # typedefs - ('uint spam;', ('spam', 'uint')), - ('MyType spam;', ('spam', 'MyType')), - - # complex - ('struct myspam spam;', ('spam', 'struct myspam')), - ('union choice spam;', ('spam', 'union choice')), - # inline struct - # inline union - # enum? - ] - # pointers - tests.extend([ - # POTS - ('int * spam;', ('spam', 'int *')), - ('unsigned int * spam;', ('spam', 'unsigned int *')), - ('char *spam;', ('spam', 'char *')), - ('char const *spam = "spamspamspam...";', ('spam', 'char const *')), - # typedefs - ('MyType *spam;', ('spam', 'MyType *')), - # complex - ('struct myspam *spam;', ('spam', 'struct myspam *')), - ('union choice *spam;', ('spam', 'union choice *')), - # packed with details - ('const char const *spam;', ('spam', 'const char const *')), - # void pointer - ('void *data = NULL;', ('data', 'void *')), - # function pointers - ('int (* func)(char *);', ('func', 'int (*)(char *)')), - ('char * (* func)(void);', ('func', 'char * (*)(void)')), - ]) - # storage class - tests.extend([ - ('static int spam;', ('spam', 'static int')), - ('extern int spam;', ('spam', 'extern int')), - ('static unsigned int spam;', ('spam', 'static unsigned int')), - ('static struct myspam spam;', ('spam', 'static struct myspam')), - ]) - # type qualifier - tests.extend([ - ('const int spam;', ('spam', 'const int')), - ('const unsigned int spam;', ('spam', 'const unsigned int')), - ('const struct myspam spam;', ('spam', 'const struct myspam')), - ]) - # combined - tests.extend([ - ('const char *spam = eggs;', ('spam', 'const char *')), - ('static const char const *spam = "spamspamspam...";', - ('spam', 'static const char const *')), - ('extern const char const *spam;', - ('spam', 'extern const char const *')), - ('static void *data = NULL;', ('data', 'static void *')), - ('static int (const * func)(char *) = func1;', - ('func', 'static int (const *)(char *)')), - ('static char * (* func)(void);', - ('func', 'static char * (*)(void)')), - ]) - for stmt, expected in tests: - with self.subTest(stmt): - name, vartype = _parse_var(stmt) - - self.assertEqual((name, vartype), expected) - - - at unittest.skip('not finished') -class ParseCompoundTests(TestCaseBase): - - def test_typical(self): - headers, bodies = parse_compound(stmt, blocks) - ... - - -class IterVariablesTests(TestCaseBase): - - _return_iter_source_lines = None - _return_iter_global = None - _return_iter_local = None - _return_parse_func = None - _return_parse_var = None - _return_parse_compound = None - - def _iter_source_lines(self, filename): - self.calls.append( - ('_iter_source_lines', (filename,))) - return self._return_iter_source_lines.splitlines() - - def _iter_global(self, lines): - self.calls.append( - ('_iter_global', (lines,))) - try: - return self._return_iter_global.pop(0) - except IndexError: - return ('???', None) - - def _iter_local(self, lines): - self.calls.append( - ('_iter_local', (lines,))) - try: - return self._return_iter_local.pop(0) - except IndexError: - return ('???', None) - - def _parse_func(self, stmt, body): - self.calls.append( - ('_parse_func', (stmt, body))) - try: - return self._return_parse_func.pop(0) - except IndexError: - return ('???', '???') - - def _parse_var(self, lines): - self.calls.append( - ('_parse_var', (lines,))) - try: - return self._return_parse_var.pop(0) - except IndexError: - return ('???', '???') - - def _parse_compound(self, stmt, blocks): - self.calls.append( - ('_parse_compound', (stmt, blocks))) - try: - return self._return_parse_compound.pop(0) - except IndexError: - return (['???'], ['???']) - - def test_empty_file(self): - self._return_iter_source_lines = '' - self._return_iter_global = [ - [], - ] - self._return_parse_func = None - self._return_parse_var = None - self._return_parse_compound = None - - srcvars = list(iter_variables('spam.c', - _iter_source_lines=self._iter_source_lines, - _iter_global=self._iter_global, - _iter_local=self._iter_local, - _parse_func=self._parse_func, - _parse_var=self._parse_var, - _parse_compound=self._parse_compound, - )) - - self.assertEqual(srcvars, []) - self.assertEqual(self.calls, [ - ('_iter_source_lines', ('spam.c',)), - ('_iter_global', ([],)), - ]) - - def test_no_statements(self): - content = textwrap.dedent(''' - ... - ''') - self._return_iter_source_lines = content - self._return_iter_global = [ - [], - ] - self._return_parse_func = None - self._return_parse_var = None - self._return_parse_compound = None - - srcvars = list(iter_variables('spam.c', - _iter_source_lines=self._iter_source_lines, - _iter_global=self._iter_global, - _iter_local=self._iter_local, - _parse_func=self._parse_func, - _parse_var=self._parse_var, - _parse_compound=self._parse_compound, - )) - - self.assertEqual(srcvars, []) - self.assertEqual(self.calls, [ - ('_iter_source_lines', ('spam.c',)), - ('_iter_global', (content.splitlines(),)), - ]) - - def test_typical(self): - content = textwrap.dedent(''' - ... - ''') - self._return_iter_source_lines = content - self._return_iter_global = [ - [('', None), # var1 - ('', None), # non-var - ('', None), # var2 - ('', ''), # func1 - ('', None), # var4 - ], - ] - self._return_iter_local = [ - # func1 - [('', None), # var3 - ('', [('
    ', '')]), # if - ('', None), # non-var - ], - # if - [('', None), # var2 ("collision" with global var) - ], - ] - self._return_parse_func = [ - ('func1', ''), - ] - self._return_parse_var = [ - ('var1', ''), - (None, None), - ('var2', ''), - ('var3', ''), - ('var2', ''), - ('var4', ''), - (None, None), - (None, None), - (None, None), - ('var5', ''), - ] - self._return_parse_compound = [ - ([[ - 'if (', - '', - ')', - ], - ], - ['']), - ] - - srcvars = list(iter_variables('spam.c', - _iter_source_lines=self._iter_source_lines, - _iter_global=self._iter_global, - _iter_local=self._iter_local, - _parse_func=self._parse_func, - _parse_var=self._parse_var, - _parse_compound=self._parse_compound, - )) - - self.assertEqual(srcvars, [ - (None, 'var1', ''), - (None, 'var2', ''), - ('func1', 'var3', ''), - ('func1', 'var2', ''), - ('func1', 'var4', ''), - (None, 'var5', ''), - ]) - self.assertEqual(self.calls, [ - ('_iter_source_lines', ('spam.c',)), - ('_iter_global', (content.splitlines(),)), - ('_parse_var', ('',)), - ('_parse_var', ('',)), - ('_parse_var', ('',)), - ('_parse_func', ('', '')), - ('_iter_local', ([''],)), - ('_parse_var', ('',)), - ('_parse_compound', ('', [('
    ', '')])), - ('_parse_var', ('if (',)), - ('_parse_var', ('',)), - ('_parse_var', (')',)), - ('_parse_var', ('',)), - ('_iter_local', ([''],)), - ('_parse_var', ('',)), - ('_parse_var', ('',)), - ]) - - def test_no_locals(self): - content = textwrap.dedent(''' - ... - ''') - self._return_iter_source_lines = content - self._return_iter_global = [ - [('', None), # var1 - ('', None), # non-var - ('', None), # var2 - ('', ''), # func1 - ], - ] - self._return_iter_local = [ - # func1 - [('', None), # non-var - ('', [('
    ', '')]), # if - ('', None), # non-var - ], - # if - [('', None), # non-var - ], - ] - self._return_parse_func = [ - ('func1', ''), - ] - self._return_parse_var = [ - ('var1', ''), - (None, None), - ('var2', ''), - (None, None), - (None, None), - (None, None), - (None, None), - (None, None), - (None, None), - ] - self._return_parse_compound = [ - ([[ - 'if (', - '', - ')', - ], - ], - ['']), - ] - - srcvars = list(iter_variables('spam.c', - _iter_source_lines=self._iter_source_lines, - _iter_global=self._iter_global, - _iter_local=self._iter_local, - _parse_func=self._parse_func, - _parse_var=self._parse_var, - _parse_compound=self._parse_compound, - )) - - self.assertEqual(srcvars, [ - (None, 'var1', ''), - (None, 'var2', ''), - ]) - self.assertEqual(self.calls, [ - ('_iter_source_lines', ('spam.c',)), - ('_iter_global', (content.splitlines(),)), - ('_parse_var', ('',)), - ('_parse_var', ('',)), - ('_parse_var', ('',)), - ('_parse_func', ('', '')), - ('_iter_local', ([''],)), - ('_parse_var', ('',)), - ('_parse_compound', ('', [('
    ', '')])), - ('_parse_var', ('if (',)), - ('_parse_var', ('',)), - ('_parse_var', (')',)), - ('_parse_var', ('',)), - ('_iter_local', ([''],)), - ('_parse_var', ('',)), - ]) diff --git a/Lib/test/test_tools/test_c_analyzer/test_parser/test_preprocessor.py b/Lib/test/test_tools/test_c_analyzer/test_parser/test_preprocessor.py deleted file mode 100644 index b7f950f813976..0000000000000 --- a/Lib/test/test_tools/test_c_analyzer/test_parser/test_preprocessor.py +++ /dev/null @@ -1,1561 +0,0 @@ -import textwrap -import unittest -import sys - -from ..util import wrapped_arg_combos, StrProxy -from .. import tool_imports_for_tests -with tool_imports_for_tests(): - from c_analyzer.parser.preprocessor import ( - iter_lines, - # directives - parse_directive, PreprocessorDirective, - Constant, Macro, IfDirective, Include, OtherDirective, - ) - - -class TestCaseBase(unittest.TestCase): - - maxDiff = None - - def reset(self): - self._calls = [] - self.errors = None - - @property - def calls(self): - try: - return self._calls - except AttributeError: - self._calls = [] - return self._calls - - errors = None - - def try_next_exc(self): - if not self.errors: - return - if exc := self.errors.pop(0): - raise exc - - def check_calls(self, *expected): - self.assertEqual(self.calls, list(expected)) - self.assertEqual(self.errors or [], []) - - -class IterLinesTests(TestCaseBase): - - parsed = None - - def check_calls(self, *expected): - super().check_calls(*expected) - self.assertEqual(self.parsed or [], []) - - def _parse_directive(self, line): - self.calls.append( - ('_parse_directive', line)) - self.try_next_exc() - return self.parsed.pop(0) - - def test_no_lines(self): - lines = [] - - results = list( - iter_lines(lines, _parse_directive=self._parse_directive)) - - self.assertEqual(results, []) - self.check_calls() - - def test_no_directives(self): - lines = textwrap.dedent(''' - - // xyz - typedef enum { - SPAM - EGGS - } kind; - - struct info { - kind kind; - int status; - }; - - typedef struct spam { - struct info info; - } myspam; - - static int spam = 0; - - /** - * ... - */ - static char * - get_name(int arg, - char *default, - ) - { - return default - } - - int check(void) { - return 0; - } - - ''')[1:-1].splitlines() - expected = [(lno, line, None, ()) - for lno, line in enumerate(lines, 1)] - expected[1] = (2, ' ', None, ()) - expected[20] = (21, ' ', None, ()) - del expected[19] - del expected[18] - - results = list( - iter_lines(lines, _parse_directive=self._parse_directive)) - - self.assertEqual(results, expected) - self.check_calls() - - def test_single_directives(self): - tests = [ - ('#include ', Include('')), - ('#define SPAM 1', Constant('SPAM', '1')), - ('#define SPAM() 1', Macro('SPAM', (), '1')), - ('#define SPAM(a, b) a = b;', Macro('SPAM', ('a', 'b'), 'a = b;')), - ('#if defined(SPAM)', IfDirective('if', 'defined(SPAM)')), - ('#ifdef SPAM', IfDirective('ifdef', 'SPAM')), - ('#ifndef SPAM', IfDirective('ifndef', 'SPAM')), - ('#elseif defined(SPAM)', IfDirective('elseif', 'defined(SPAM)')), - ('#else', OtherDirective('else', None)), - ('#endif', OtherDirective('endif', None)), - ('#error ...', OtherDirective('error', '...')), - ('#warning ...', OtherDirective('warning', '...')), - ('#__FILE__ ...', OtherDirective('__FILE__', '...')), - ('#__LINE__ ...', OtherDirective('__LINE__', '...')), - ('#__DATE__ ...', OtherDirective('__DATE__', '...')), - ('#__TIME__ ...', OtherDirective('__TIME__', '...')), - ('#__TIMESTAMP__ ...', OtherDirective('__TIMESTAMP__', '...')), - ] - for line, directive in tests: - with self.subTest(line): - self.reset() - self.parsed = [ - directive, - ] - text = textwrap.dedent(''' - static int spam = 0; - {} - static char buffer[256]; - ''').strip().format(line) - lines = text.strip().splitlines() - - results = list( - iter_lines(lines, _parse_directive=self._parse_directive)) - - self.assertEqual(results, [ - (1, 'static int spam = 0;', None, ()), - (2, line, directive, ()), - ((3, 'static char buffer[256];', None, ('defined(SPAM)',)) - if directive.kind in ('if', 'ifdef', 'elseif') - else (3, 'static char buffer[256];', None, ('! defined(SPAM)',)) - if directive.kind == 'ifndef' - else (3, 'static char buffer[256];', None, ())), - ]) - self.check_calls( - ('_parse_directive', line), - ) - - def test_directive_whitespace(self): - line = ' # define eggs ( a , b ) { a = b ; } ' - directive = Macro('eggs', ('a', 'b'), '{ a = b; }') - self.parsed = [ - directive, - ] - lines = [line] - - results = list( - iter_lines(lines, _parse_directive=self._parse_directive)) - - self.assertEqual(results, [ - (1, line, directive, ()), - ]) - self.check_calls( - ('_parse_directive', '#define eggs ( a , b ) { a = b ; }'), - ) - - @unittest.skipIf(sys.platform == 'win32', 'needs fix under Windows') - def test_split_lines(self): - directive = Macro('eggs', ('a', 'b'), '{ a = b; }') - self.parsed = [ - directive, - ] - text = textwrap.dedent(r''' - static int spam = 0; - #define eggs(a, b) \ - { \ - a = b; \ - } - static char buffer[256]; - ''').strip() - lines = [line + '\n' for line in text.splitlines()] - lines[-1] = lines[-1][:-1] - - results = list( - iter_lines(lines, _parse_directive=self._parse_directive)) - - self.assertEqual(results, [ - (1, 'static int spam = 0;\n', None, ()), - (5, '#define eggs(a, b) { a = b; }\n', directive, ()), - (6, 'static char buffer[256];', None, ()), - ]) - self.check_calls( - ('_parse_directive', '#define eggs(a, b) { a = b; }'), - ) - - def test_nested_conditions(self): - directives = [ - IfDirective('ifdef', 'SPAM'), - IfDirective('if', 'SPAM == 1'), - IfDirective('elseif', 'SPAM == 2'), - OtherDirective('else', None), - OtherDirective('endif', None), - OtherDirective('endif', None), - ] - self.parsed = list(directives) - text = textwrap.dedent(r''' - static int spam = 0; - - #ifdef SPAM - static int start = 0; - # if SPAM == 1 - static char buffer[10]; - # elif SPAM == 2 - static char buffer[100]; - # else - static char buffer[256]; - # endif - static int end = 0; - #endif - - static int eggs = 0; - ''').strip() - lines = [line for line in text.splitlines() if line.strip()] - - results = list( - iter_lines(lines, _parse_directive=self._parse_directive)) - - self.assertEqual(results, [ - (1, 'static int spam = 0;', None, ()), - (2, '#ifdef SPAM', directives[0], ()), - (3, 'static int start = 0;', None, ('defined(SPAM)',)), - (4, '# if SPAM == 1', directives[1], ('defined(SPAM)',)), - (5, 'static char buffer[10];', None, ('defined(SPAM)', 'SPAM == 1')), - (6, '# elif SPAM == 2', directives[2], ('defined(SPAM)', 'SPAM == 1')), - (7, 'static char buffer[100];', None, ('defined(SPAM)', '! (SPAM == 1)', 'SPAM == 2')), - (8, '# else', directives[3], ('defined(SPAM)', '! (SPAM == 1)', 'SPAM == 2')), - (9, 'static char buffer[256];', None, ('defined(SPAM)', '! (SPAM == 1)', '! (SPAM == 2)')), - (10, '# endif', directives[4], ('defined(SPAM)', '! (SPAM == 1)', '! (SPAM == 2)')), - (11, 'static int end = 0;', None, ('defined(SPAM)',)), - (12, '#endif', directives[5], ('defined(SPAM)',)), - (13, 'static int eggs = 0;', None, ()), - ]) - self.check_calls( - ('_parse_directive', '#ifdef SPAM'), - ('_parse_directive', '#if SPAM == 1'), - ('_parse_directive', '#elif SPAM == 2'), - ('_parse_directive', '#else'), - ('_parse_directive', '#endif'), - ('_parse_directive', '#endif'), - ) - - def test_split_blocks(self): - directives = [ - IfDirective('ifdef', 'SPAM'), - OtherDirective('else', None), - OtherDirective('endif', None), - ] - self.parsed = list(directives) - text = textwrap.dedent(r''' - void str_copy(char *buffer, *orig); - - int init(char *name) { - static int initialized = 0; - if (initialized) { - return 0; - } - #ifdef SPAM - static char buffer[10]; - str_copy(buffer, char); - } - - void copy(char *buffer, *orig) { - strncpy(buffer, orig, 9); - buffer[9] = 0; - } - - #else - static char buffer[256]; - str_copy(buffer, char); - } - - void copy(char *buffer, *orig) { - strcpy(buffer, orig); - } - - #endif - ''').strip() - lines = [line for line in text.splitlines() if line.strip()] - - results = list( - iter_lines(lines, _parse_directive=self._parse_directive)) - - self.assertEqual(results, [ - (1, 'void str_copy(char *buffer, *orig);', None, ()), - (2, 'int init(char *name) {', None, ()), - (3, ' static int initialized = 0;', None, ()), - (4, ' if (initialized) {', None, ()), - (5, ' return 0;', None, ()), - (6, ' }', None, ()), - - (7, '#ifdef SPAM', directives[0], ()), - - (8, ' static char buffer[10];', None, ('defined(SPAM)',)), - (9, ' str_copy(buffer, char);', None, ('defined(SPAM)',)), - (10, '}', None, ('defined(SPAM)',)), - (11, 'void copy(char *buffer, *orig) {', None, ('defined(SPAM)',)), - (12, ' strncpy(buffer, orig, 9);', None, ('defined(SPAM)',)), - (13, ' buffer[9] = 0;', None, ('defined(SPAM)',)), - (14, '}', None, ('defined(SPAM)',)), - - (15, '#else', directives[1], ('defined(SPAM)',)), - - (16, ' static char buffer[256];', None, ('! (defined(SPAM))',)), - (17, ' str_copy(buffer, char);', None, ('! (defined(SPAM))',)), - (18, '}', None, ('! (defined(SPAM))',)), - (19, 'void copy(char *buffer, *orig) {', None, ('! (defined(SPAM))',)), - (20, ' strcpy(buffer, orig);', None, ('! (defined(SPAM))',)), - (21, '}', None, ('! (defined(SPAM))',)), - - (22, '#endif', directives[2], ('! (defined(SPAM))',)), - ]) - self.check_calls( - ('_parse_directive', '#ifdef SPAM'), - ('_parse_directive', '#else'), - ('_parse_directive', '#endif'), - ) - - @unittest.skipIf(sys.platform == 'win32', 'needs fix under Windows') - def test_basic(self): - directives = [ - Include(''), - IfDirective('ifdef', 'SPAM'), - IfDirective('if', '! defined(HAM) || !HAM'), - Constant('HAM', '0'), - IfDirective('elseif', 'HAM < 0'), - Constant('HAM', '-1'), - OtherDirective('else', None), - OtherDirective('endif', None), - OtherDirective('endif', None), - IfDirective('if', 'defined(HAM) && (HAM < 0 || ! HAM)'), - OtherDirective('undef', 'HAM'), - OtherDirective('endif', None), - IfDirective('ifndef', 'HAM'), - OtherDirective('endif', None), - ] - self.parsed = list(directives) - text = textwrap.dedent(r''' - #include - print("begin"); - #ifdef SPAM - print("spam"); - #if ! defined(HAM) || !HAM - # DEFINE HAM 0 - #elseif HAM < 0 - # DEFINE HAM -1 - #else - print("ham HAM"); - #endif - #endif - - #if defined(HAM) && \ - (HAM < 0 || ! HAM) - print("ham?"); - #undef HAM - # endif - - #ifndef HAM - print("no ham"); - #endif - print("end"); - ''')[1:-1] - lines = [line + '\n' for line in text.splitlines()] - lines[-1] = lines[-1][:-1] - - results = list( - iter_lines(lines, _parse_directive=self._parse_directive)) - - self.assertEqual(results, [ - (1, '#include \n', Include(''), ()), - (2, 'print("begin");\n', None, ()), - # - (3, '#ifdef SPAM\n', - IfDirective('ifdef', 'SPAM'), - ()), - (4, ' print("spam");\n', - None, - ('defined(SPAM)',)), - (5, ' #if ! defined(HAM) || !HAM\n', - IfDirective('if', '! defined(HAM) || !HAM'), - ('defined(SPAM)',)), - (6, '# DEFINE HAM 0\n', - Constant('HAM', '0'), - ('defined(SPAM)', '! defined(HAM) || !HAM')), - (7, ' #elseif HAM < 0\n', - IfDirective('elseif', 'HAM < 0'), - ('defined(SPAM)', '! defined(HAM) || !HAM')), - (8, '# DEFINE HAM -1\n', - Constant('HAM', '-1'), - ('defined(SPAM)', '! (! defined(HAM) || !HAM)', 'HAM < 0')), - (9, ' #else\n', - OtherDirective('else', None), - ('defined(SPAM)', '! (! defined(HAM) || !HAM)', 'HAM < 0')), - (10, ' print("ham HAM");\n', - None, - ('defined(SPAM)', '! (! defined(HAM) || !HAM)', '! (HAM < 0)')), - (11, ' #endif\n', - OtherDirective('endif', None), - ('defined(SPAM)', '! (! defined(HAM) || !HAM)', '! (HAM < 0)')), - (12, '#endif\n', - OtherDirective('endif', None), - ('defined(SPAM)',)), - # - (13, '\n', None, ()), - # - (15, '#if defined(HAM) && (HAM < 0 || ! HAM)\n', - IfDirective('if', 'defined(HAM) && (HAM < 0 || ! HAM)'), - ()), - (16, ' print("ham?");\n', - None, - ('defined(HAM) && (HAM < 0 || ! HAM)',)), - (17, ' #undef HAM\n', - OtherDirective('undef', 'HAM'), - ('defined(HAM) && (HAM < 0 || ! HAM)',)), - (18, '# endif\n', - OtherDirective('endif', None), - ('defined(HAM) && (HAM < 0 || ! HAM)',)), - # - (19, '\n', None, ()), - # - (20, '#ifndef HAM\n', - IfDirective('ifndef', 'HAM'), - ()), - (21, ' print("no ham");\n', - None, - ('! defined(HAM)',)), - (22, '#endif\n', - OtherDirective('endif', None), - ('! defined(HAM)',)), - # - (23, 'print("end");', None, ()), - ]) - - @unittest.skipIf(sys.platform == 'win32', 'needs fix under Windows') - def test_typical(self): - # We use Include/compile.h from commit 66c4f3f38b86. It has - # a good enough mix of code without being too large. - directives = [ - IfDirective('ifndef', 'Py_COMPILE_H'), - Constant('Py_COMPILE_H', None), - - IfDirective('ifndef', 'Py_LIMITED_API'), - - Include('"code.h"'), - - IfDirective('ifdef', '__cplusplus'), - OtherDirective('endif', None), - - Constant('PyCF_MASK', '(CO_FUTURE_DIVISION | CO_FUTURE_ABSOLUTE_IMPORT | CO_FUTURE_WITH_STATEMENT | CO_FUTURE_PRINT_FUNCTION | CO_FUTURE_UNICODE_LITERALS | CO_FUTURE_BARRY_AS_BDFL | CO_FUTURE_GENERATOR_STOP | CO_FUTURE_ANNOTATIONS)'), - Constant('PyCF_MASK_OBSOLETE', '(CO_NESTED)'), - Constant('PyCF_SOURCE_IS_UTF8', ' 0x0100'), - Constant('PyCF_DONT_IMPLY_DEDENT', '0x0200'), - Constant('PyCF_ONLY_AST', '0x0400'), - Constant('PyCF_IGNORE_COOKIE', '0x0800'), - Constant('PyCF_TYPE_COMMENTS', '0x1000'), - Constant('PyCF_ALLOW_TOP_LEVEL_AWAIT', '0x2000'), - - IfDirective('ifndef', 'Py_LIMITED_API'), - OtherDirective('endif', None), - - Constant('FUTURE_NESTED_SCOPES', '"nested_scopes"'), - Constant('FUTURE_GENERATORS', '"generators"'), - Constant('FUTURE_DIVISION', '"division"'), - Constant('FUTURE_ABSOLUTE_IMPORT', '"absolute_import"'), - Constant('FUTURE_WITH_STATEMENT', '"with_statement"'), - Constant('FUTURE_PRINT_FUNCTION', '"print_function"'), - Constant('FUTURE_UNICODE_LITERALS', '"unicode_literals"'), - Constant('FUTURE_BARRY_AS_BDFL', '"barry_as_FLUFL"'), - Constant('FUTURE_GENERATOR_STOP', '"generator_stop"'), - Constant('FUTURE_ANNOTATIONS', '"annotations"'), - - Macro('PyAST_Compile', ('mod', 's', 'f', 'ar'), 'PyAST_CompileEx(mod, s, f, -1, ar)'), - - Constant('PY_INVALID_STACK_EFFECT', 'INT_MAX'), - - IfDirective('ifdef', '__cplusplus'), - OtherDirective('endif', None), - - OtherDirective('endif', None), # ifndef Py_LIMITED_API - - Constant('Py_single_input', '256'), - Constant('Py_file_input', '257'), - Constant('Py_eval_input', '258'), - Constant('Py_func_type_input', '345'), - - OtherDirective('endif', None), # ifndef Py_COMPILE_H - ] - self.parsed = list(directives) - text = textwrap.dedent(r''' - #ifndef Py_COMPILE_H - #define Py_COMPILE_H - - #ifndef Py_LIMITED_API - #include "code.h" - - #ifdef __cplusplus - extern "C" { - #endif - - /* Public interface */ - struct _node; /* Declare the existence of this type */ - PyAPI_FUNC(PyCodeObject *) PyNode_Compile(struct _node *, const char *); - /* XXX (ncoghlan): Unprefixed type name in a public API! */ - - #define PyCF_MASK (CO_FUTURE_DIVISION | CO_FUTURE_ABSOLUTE_IMPORT | \ - CO_FUTURE_WITH_STATEMENT | CO_FUTURE_PRINT_FUNCTION | \ - CO_FUTURE_UNICODE_LITERALS | CO_FUTURE_BARRY_AS_BDFL | \ - CO_FUTURE_GENERATOR_STOP | CO_FUTURE_ANNOTATIONS) - #define PyCF_MASK_OBSOLETE (CO_NESTED) - #define PyCF_SOURCE_IS_UTF8 0x0100 - #define PyCF_DONT_IMPLY_DEDENT 0x0200 - #define PyCF_ONLY_AST 0x0400 - #define PyCF_IGNORE_COOKIE 0x0800 - #define PyCF_TYPE_COMMENTS 0x1000 - #define PyCF_ALLOW_TOP_LEVEL_AWAIT 0x2000 - - #ifndef Py_LIMITED_API - typedef struct { - int cf_flags; /* bitmask of CO_xxx flags relevant to future */ - int cf_feature_version; /* minor Python version (PyCF_ONLY_AST) */ - } PyCompilerFlags; - #endif - - /* Future feature support */ - - typedef struct { - int ff_features; /* flags set by future statements */ - int ff_lineno; /* line number of last future statement */ - } PyFutureFeatures; - - #define FUTURE_NESTED_SCOPES "nested_scopes" - #define FUTURE_GENERATORS "generators" - #define FUTURE_DIVISION "division" - #define FUTURE_ABSOLUTE_IMPORT "absolute_import" - #define FUTURE_WITH_STATEMENT "with_statement" - #define FUTURE_PRINT_FUNCTION "print_function" - #define FUTURE_UNICODE_LITERALS "unicode_literals" - #define FUTURE_BARRY_AS_BDFL "barry_as_FLUFL" - #define FUTURE_GENERATOR_STOP "generator_stop" - #define FUTURE_ANNOTATIONS "annotations" - - struct _mod; /* Declare the existence of this type */ - #define PyAST_Compile(mod, s, f, ar) PyAST_CompileEx(mod, s, f, -1, ar) - PyAPI_FUNC(PyCodeObject *) PyAST_CompileEx( - struct _mod *mod, - const char *filename, /* decoded from the filesystem encoding */ - PyCompilerFlags *flags, - int optimize, - PyArena *arena); - PyAPI_FUNC(PyCodeObject *) PyAST_CompileObject( - struct _mod *mod, - PyObject *filename, - PyCompilerFlags *flags, - int optimize, - PyArena *arena); - PyAPI_FUNC(PyFutureFeatures *) PyFuture_FromAST( - struct _mod * mod, - const char *filename /* decoded from the filesystem encoding */ - ); - PyAPI_FUNC(PyFutureFeatures *) PyFuture_FromASTObject( - struct _mod * mod, - PyObject *filename - ); - - /* _Py_Mangle is defined in compile.c */ - PyAPI_FUNC(PyObject*) _Py_Mangle(PyObject *p, PyObject *name); - - #define PY_INVALID_STACK_EFFECT INT_MAX - PyAPI_FUNC(int) PyCompile_OpcodeStackEffect(int opcode, int oparg); - PyAPI_FUNC(int) PyCompile_OpcodeStackEffectWithJump(int opcode, int oparg, int jump); - - PyAPI_FUNC(int) _PyAST_Optimize(struct _mod *, PyArena *arena, int optimize); - - #ifdef __cplusplus - } - #endif - - #endif /* !Py_LIMITED_API */ - - /* These definitions must match corresponding definitions in graminit.h. */ - #define Py_single_input 256 - #define Py_file_input 257 - #define Py_eval_input 258 - #define Py_func_type_input 345 - - #endif /* !Py_COMPILE_H */ - ''').strip() - lines = [line + '\n' for line in text.splitlines()] - lines[-1] = lines[-1][:-1] - - results = list( - iter_lines(lines, _parse_directive=self._parse_directive)) - - self.assertEqual(results, [ - (1, '#ifndef Py_COMPILE_H\n', - IfDirective('ifndef', 'Py_COMPILE_H'), - ()), - (2, '#define Py_COMPILE_H\n', - Constant('Py_COMPILE_H', None), - ('! defined(Py_COMPILE_H)',)), - (3, '\n', - None, - ('! defined(Py_COMPILE_H)',)), - (4, '#ifndef Py_LIMITED_API\n', - IfDirective('ifndef', 'Py_LIMITED_API'), - ('! defined(Py_COMPILE_H)',)), - (5, '#include "code.h"\n', - Include('"code.h"'), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (6, '\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (7, '#ifdef __cplusplus\n', - IfDirective('ifdef', '__cplusplus'), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (8, 'extern "C" {\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)', 'defined(__cplusplus)')), - (9, '#endif\n', - OtherDirective('endif', None), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)', 'defined(__cplusplus)')), - (10, '\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (11, ' \n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (12, 'struct _node; \n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (13, 'PyAPI_FUNC(PyCodeObject *) PyNode_Compile(struct _node *, const char *);\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (14, ' \n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (15, '\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (19, '#define PyCF_MASK (CO_FUTURE_DIVISION | CO_FUTURE_ABSOLUTE_IMPORT | CO_FUTURE_WITH_STATEMENT | CO_FUTURE_PRINT_FUNCTION | CO_FUTURE_UNICODE_LITERALS | CO_FUTURE_BARRY_AS_BDFL | CO_FUTURE_GENERATOR_STOP | CO_FUTURE_ANNOTATIONS)\n', - Constant('PyCF_MASK', '(CO_FUTURE_DIVISION | CO_FUTURE_ABSOLUTE_IMPORT | CO_FUTURE_WITH_STATEMENT | CO_FUTURE_PRINT_FUNCTION | CO_FUTURE_UNICODE_LITERALS | CO_FUTURE_BARRY_AS_BDFL | CO_FUTURE_GENERATOR_STOP | CO_FUTURE_ANNOTATIONS)'), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (20, '#define PyCF_MASK_OBSOLETE (CO_NESTED)\n', - Constant('PyCF_MASK_OBSOLETE', '(CO_NESTED)'), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (21, '#define PyCF_SOURCE_IS_UTF8 0x0100\n', - Constant('PyCF_SOURCE_IS_UTF8', ' 0x0100'), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (22, '#define PyCF_DONT_IMPLY_DEDENT 0x0200\n', - Constant('PyCF_DONT_IMPLY_DEDENT', '0x0200'), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (23, '#define PyCF_ONLY_AST 0x0400\n', - Constant('PyCF_ONLY_AST', '0x0400'), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (24, '#define PyCF_IGNORE_COOKIE 0x0800\n', - Constant('PyCF_IGNORE_COOKIE', '0x0800'), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (25, '#define PyCF_TYPE_COMMENTS 0x1000\n', - Constant('PyCF_TYPE_COMMENTS', '0x1000'), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (26, '#define PyCF_ALLOW_TOP_LEVEL_AWAIT 0x2000\n', - Constant('PyCF_ALLOW_TOP_LEVEL_AWAIT', '0x2000'), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (27, '\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (28, '#ifndef Py_LIMITED_API\n', - IfDirective('ifndef', 'Py_LIMITED_API'), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (29, 'typedef struct {\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)', '! defined(Py_LIMITED_API)')), - (30, ' int cf_flags; \n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)', '! defined(Py_LIMITED_API)')), - (31, ' int cf_feature_version; \n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)', '! defined(Py_LIMITED_API)')), - (32, '} PyCompilerFlags;\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)', '! defined(Py_LIMITED_API)')), - (33, '#endif\n', - OtherDirective('endif', None), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)', '! defined(Py_LIMITED_API)')), - (34, '\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (35, ' \n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (36, '\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (37, 'typedef struct {\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (38, ' int ff_features; \n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (39, ' int ff_lineno; \n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (40, '} PyFutureFeatures;\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (41, '\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (42, '#define FUTURE_NESTED_SCOPES "nested_scopes"\n', - Constant('FUTURE_NESTED_SCOPES', '"nested_scopes"'), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (43, '#define FUTURE_GENERATORS "generators"\n', - Constant('FUTURE_GENERATORS', '"generators"'), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (44, '#define FUTURE_DIVISION "division"\n', - Constant('FUTURE_DIVISION', '"division"'), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (45, '#define FUTURE_ABSOLUTE_IMPORT "absolute_import"\n', - Constant('FUTURE_ABSOLUTE_IMPORT', '"absolute_import"'), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (46, '#define FUTURE_WITH_STATEMENT "with_statement"\n', - Constant('FUTURE_WITH_STATEMENT', '"with_statement"'), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (47, '#define FUTURE_PRINT_FUNCTION "print_function"\n', - Constant('FUTURE_PRINT_FUNCTION', '"print_function"'), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (48, '#define FUTURE_UNICODE_LITERALS "unicode_literals"\n', - Constant('FUTURE_UNICODE_LITERALS', '"unicode_literals"'), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (49, '#define FUTURE_BARRY_AS_BDFL "barry_as_FLUFL"\n', - Constant('FUTURE_BARRY_AS_BDFL', '"barry_as_FLUFL"'), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (50, '#define FUTURE_GENERATOR_STOP "generator_stop"\n', - Constant('FUTURE_GENERATOR_STOP', '"generator_stop"'), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (51, '#define FUTURE_ANNOTATIONS "annotations"\n', - Constant('FUTURE_ANNOTATIONS', '"annotations"'), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (52, '\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (53, 'struct _mod; \n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (54, '#define PyAST_Compile(mod, s, f, ar) PyAST_CompileEx(mod, s, f, -1, ar)\n', - Macro('PyAST_Compile', ('mod', 's', 'f', 'ar'), 'PyAST_CompileEx(mod, s, f, -1, ar)'), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (55, 'PyAPI_FUNC(PyCodeObject *) PyAST_CompileEx(\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (56, ' struct _mod *mod,\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (57, ' const char *filename, \n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (58, ' PyCompilerFlags *flags,\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (59, ' int optimize,\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (60, ' PyArena *arena);\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (61, 'PyAPI_FUNC(PyCodeObject *) PyAST_CompileObject(\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (62, ' struct _mod *mod,\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (63, ' PyObject *filename,\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (64, ' PyCompilerFlags *flags,\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (65, ' int optimize,\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (66, ' PyArena *arena);\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (67, 'PyAPI_FUNC(PyFutureFeatures *) PyFuture_FromAST(\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (68, ' struct _mod * mod,\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (69, ' const char *filename \n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (70, ' );\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (71, 'PyAPI_FUNC(PyFutureFeatures *) PyFuture_FromASTObject(\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (72, ' struct _mod * mod,\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (73, ' PyObject *filename\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (74, ' );\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (75, '\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (76, ' \n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (77, 'PyAPI_FUNC(PyObject*) _Py_Mangle(PyObject *p, PyObject *name);\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (78, '\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (79, '#define PY_INVALID_STACK_EFFECT INT_MAX\n', - Constant('PY_INVALID_STACK_EFFECT', 'INT_MAX'), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (80, 'PyAPI_FUNC(int) PyCompile_OpcodeStackEffect(int opcode, int oparg);\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (81, 'PyAPI_FUNC(int) PyCompile_OpcodeStackEffectWithJump(int opcode, int oparg, int jump);\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (82, '\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (83, 'PyAPI_FUNC(int) _PyAST_Optimize(struct _mod *, PyArena *arena, int optimize);\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (84, '\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (85, '#ifdef __cplusplus\n', - IfDirective('ifdef', '__cplusplus'), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (86, '}\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)', 'defined(__cplusplus)')), - (87, '#endif\n', - OtherDirective('endif', None), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)', 'defined(__cplusplus)')), - (88, '\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (89, '#endif \n', - OtherDirective('endif', None), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (90, '\n', - None, - ('! defined(Py_COMPILE_H)',)), - (91, ' \n', - None, - ('! defined(Py_COMPILE_H)',)), - (92, '#define Py_single_input 256\n', - Constant('Py_single_input', '256'), - ('! defined(Py_COMPILE_H)',)), - (93, '#define Py_file_input 257\n', - Constant('Py_file_input', '257'), - ('! defined(Py_COMPILE_H)',)), - (94, '#define Py_eval_input 258\n', - Constant('Py_eval_input', '258'), - ('! defined(Py_COMPILE_H)',)), - (95, '#define Py_func_type_input 345\n', - Constant('Py_func_type_input', '345'), - ('! defined(Py_COMPILE_H)',)), - (96, '\n', - None, - ('! defined(Py_COMPILE_H)',)), - (97, '#endif ', - OtherDirective('endif', None), - ('! defined(Py_COMPILE_H)',)), - ]) - self.check_calls( - ('_parse_directive', '#ifndef Py_COMPILE_H'), - ('_parse_directive', '#define Py_COMPILE_H'), - ('_parse_directive', '#ifndef Py_LIMITED_API'), - ('_parse_directive', '#include "code.h"'), - ('_parse_directive', '#ifdef __cplusplus'), - ('_parse_directive', '#endif'), - ('_parse_directive', '#define PyCF_MASK (CO_FUTURE_DIVISION | CO_FUTURE_ABSOLUTE_IMPORT | CO_FUTURE_WITH_STATEMENT | CO_FUTURE_PRINT_FUNCTION | CO_FUTURE_UNICODE_LITERALS | CO_FUTURE_BARRY_AS_BDFL | CO_FUTURE_GENERATOR_STOP | CO_FUTURE_ANNOTATIONS)'), - ('_parse_directive', '#define PyCF_MASK_OBSOLETE (CO_NESTED)'), - ('_parse_directive', '#define PyCF_SOURCE_IS_UTF8 0x0100'), - ('_parse_directive', '#define PyCF_DONT_IMPLY_DEDENT 0x0200'), - ('_parse_directive', '#define PyCF_ONLY_AST 0x0400'), - ('_parse_directive', '#define PyCF_IGNORE_COOKIE 0x0800'), - ('_parse_directive', '#define PyCF_TYPE_COMMENTS 0x1000'), - ('_parse_directive', '#define PyCF_ALLOW_TOP_LEVEL_AWAIT 0x2000'), - ('_parse_directive', '#ifndef Py_LIMITED_API'), - ('_parse_directive', '#endif'), - ('_parse_directive', '#define FUTURE_NESTED_SCOPES "nested_scopes"'), - ('_parse_directive', '#define FUTURE_GENERATORS "generators"'), - ('_parse_directive', '#define FUTURE_DIVISION "division"'), - ('_parse_directive', '#define FUTURE_ABSOLUTE_IMPORT "absolute_import"'), - ('_parse_directive', '#define FUTURE_WITH_STATEMENT "with_statement"'), - ('_parse_directive', '#define FUTURE_PRINT_FUNCTION "print_function"'), - ('_parse_directive', '#define FUTURE_UNICODE_LITERALS "unicode_literals"'), - ('_parse_directive', '#define FUTURE_BARRY_AS_BDFL "barry_as_FLUFL"'), - ('_parse_directive', '#define FUTURE_GENERATOR_STOP "generator_stop"'), - ('_parse_directive', '#define FUTURE_ANNOTATIONS "annotations"'), - ('_parse_directive', '#define PyAST_Compile(mod, s, f, ar) PyAST_CompileEx(mod, s, f, -1, ar)'), - ('_parse_directive', '#define PY_INVALID_STACK_EFFECT INT_MAX'), - ('_parse_directive', '#ifdef __cplusplus'), - ('_parse_directive', '#endif'), - ('_parse_directive', '#endif'), - ('_parse_directive', '#define Py_single_input 256'), - ('_parse_directive', '#define Py_file_input 257'), - ('_parse_directive', '#define Py_eval_input 258'), - ('_parse_directive', '#define Py_func_type_input 345'), - ('_parse_directive', '#endif'), - ) - - -class ParseDirectiveTests(unittest.TestCase): - - def test_directives(self): - tests = [ - # includes - ('#include "internal/pycore_pystate.h"', Include('"internal/pycore_pystate.h"')), - ('#include ', Include('')), - - # defines - ('#define SPAM int', Constant('SPAM', 'int')), - ('#define SPAM', Constant('SPAM', '')), - ('#define SPAM(x, y) run(x, y)', Macro('SPAM', ('x', 'y'), 'run(x, y)')), - ('#undef SPAM', None), - - # conditionals - ('#if SPAM', IfDirective('if', 'SPAM')), - # XXX complex conditionls - ('#ifdef SPAM', IfDirective('ifdef', 'SPAM')), - ('#ifndef SPAM', IfDirective('ifndef', 'SPAM')), - ('#elseif SPAM', IfDirective('elseif', 'SPAM')), - # XXX complex conditionls - ('#else', OtherDirective('else', '')), - ('#endif', OtherDirective('endif', '')), - - # other - ('#error oops!', None), - ('#warning oops!', None), - ('#pragma ...', None), - ('#__FILE__ ...', None), - ('#__LINE__ ...', None), - ('#__DATE__ ...', None), - ('#__TIME__ ...', None), - ('#__TIMESTAMP__ ...', None), - - # extra whitespace - (' # include ', Include('')), - ('#else ', OtherDirective('else', '')), - ('#endif ', OtherDirective('endif', '')), - ('#define SPAM int ', Constant('SPAM', 'int')), - ('#define SPAM ', Constant('SPAM', '')), - ] - for line, expected in tests: - if expected is None: - kind, _, text = line[1:].partition(' ') - expected = OtherDirective(kind, text) - with self.subTest(line): - directive = parse_directive(line) - - self.assertEqual(directive, expected) - - def test_bad_directives(self): - tests = [ - # valid directives with bad text - '#define 123', - '#else spam', - '#endif spam', - ] - for kind in PreprocessorDirective.KINDS: - # missing leading "#" - tests.append(kind) - if kind in ('else', 'endif'): - continue - # valid directives with missing text - tests.append('#' + kind) - tests.append('#' + kind + ' ') - for line in tests: - with self.subTest(line): - with self.assertRaises(ValueError): - parse_directive(line) - - def test_not_directives(self): - tests = [ - '', - ' ', - 'directive', - 'directive?', - '???', - ] - for line in tests: - with self.subTest(line): - with self.assertRaises(ValueError): - parse_directive(line) - - -class ConstantTests(unittest.TestCase): - - def test_type(self): - directive = Constant('SPAM', '123') - - self.assertIs(type(directive), Constant) - self.assertIsInstance(directive, PreprocessorDirective) - - def test_attrs(self): - d = Constant('SPAM', '123') - kind, name, value = d.kind, d.name, d.value - - self.assertEqual(kind, 'define') - self.assertEqual(name, 'SPAM') - self.assertEqual(value, '123') - - def test_text(self): - tests = [ - (('SPAM', '123'), 'SPAM 123'), - (('SPAM',), 'SPAM'), - ] - for args, expected in tests: - with self.subTest(args): - d = Constant(*args) - text = d.text - - self.assertEqual(text, expected) - - def test_iter(self): - kind, name, value = Constant('SPAM', '123') - - self.assertEqual(kind, 'define') - self.assertEqual(name, 'SPAM') - self.assertEqual(value, '123') - - def test_defaults(self): - kind, name, value = Constant('SPAM') - - self.assertEqual(kind, 'define') - self.assertEqual(name, 'SPAM') - self.assertIs(value, None) - - def test_coerce(self): - tests = [] - # coerced name, value - for args in wrapped_arg_combos('SPAM', '123'): - tests.append((args, ('SPAM', '123'))) - # missing name, value - for name in ('', ' ', None, StrProxy(' '), ()): - for value in ('', ' ', None, StrProxy(' '), ()): - tests.append( - ((name, value), (None, None))) - # whitespace - tests.extend([ - ((' SPAM ', ' 123 '), ('SPAM', '123')), - ]) - - for args, expected in tests: - with self.subTest(args): - d = Constant(*args) - - self.assertEqual(d[1:], expected) - for i, exp in enumerate(expected, start=1): - if exp is not None: - self.assertIs(type(d[i]), str) - - def test_valid(self): - tests = [ - ('SPAM', '123'), - # unusual name - ('_SPAM_', '123'), - ('X_1', '123'), - # unusual value - ('SPAM', None), - ] - for args in tests: - with self.subTest(args): - directive = Constant(*args) - - directive.validate() - - def test_invalid(self): - tests = [ - # invalid name - ((None, '123'), TypeError), - (('_', '123'), ValueError), - (('1', '123'), ValueError), - (('_1_', '123'), ValueError), - # There is no invalid value (including None). - ] - for args, exctype in tests: - with self.subTest(args): - directive = Constant(*args) - - with self.assertRaises(exctype): - directive.validate() - - -class MacroTests(unittest.TestCase): - - def test_type(self): - directive = Macro('SPAM', ('x', 'y'), '123') - - self.assertIs(type(directive), Macro) - self.assertIsInstance(directive, PreprocessorDirective) - - def test_attrs(self): - d = Macro('SPAM', ('x', 'y'), '123') - kind, name, args, body = d.kind, d.name, d.args, d.body - - self.assertEqual(kind, 'define') - self.assertEqual(name, 'SPAM') - self.assertEqual(args, ('x', 'y')) - self.assertEqual(body, '123') - - def test_text(self): - tests = [ - (('SPAM', ('x', 'y'), '123'), 'SPAM(x, y) 123'), - (('SPAM', ('x', 'y'),), 'SPAM(x, y)'), - ] - for args, expected in tests: - with self.subTest(args): - d = Macro(*args) - text = d.text - - self.assertEqual(text, expected) - - def test_iter(self): - kind, name, args, body = Macro('SPAM', ('x', 'y'), '123') - - self.assertEqual(kind, 'define') - self.assertEqual(name, 'SPAM') - self.assertEqual(args, ('x', 'y')) - self.assertEqual(body, '123') - - def test_defaults(self): - kind, name, args, body = Macro('SPAM', ('x', 'y')) - - self.assertEqual(kind, 'define') - self.assertEqual(name, 'SPAM') - self.assertEqual(args, ('x', 'y')) - self.assertIs(body, None) - - def test_coerce(self): - tests = [] - # coerce name and body - for args in wrapped_arg_combos('SPAM', ('x', 'y'), '123'): - tests.append( - (args, ('SPAM', ('x', 'y'), '123'))) - # coerce args - tests.extend([ - (('SPAM', 'x', '123'), - ('SPAM', ('x',), '123')), - (('SPAM', 'x,y', '123'), - ('SPAM', ('x', 'y'), '123')), - ]) - # coerce arg names - for argnames in wrapped_arg_combos('x', 'y'): - tests.append( - (('SPAM', argnames, '123'), - ('SPAM', ('x', 'y'), '123'))) - # missing name, body - for name in ('', ' ', None, StrProxy(' '), ()): - for argnames in (None, ()): - for body in ('', ' ', None, StrProxy(' '), ()): - tests.append( - ((name, argnames, body), - (None, (), None))) - # missing args - tests.extend([ - (('SPAM', None, '123'), - ('SPAM', (), '123')), - (('SPAM', (), '123'), - ('SPAM', (), '123')), - ]) - # missing arg names - for arg in ('', ' ', None, StrProxy(' '), ()): - tests.append( - (('SPAM', (arg,), '123'), - ('SPAM', (None,), '123'))) - tests.extend([ - (('SPAM', ('x', '', 'z'), '123'), - ('SPAM', ('x', None, 'z'), '123')), - ]) - # whitespace - tests.extend([ - ((' SPAM ', (' x ', ' y '), ' 123 '), - ('SPAM', ('x', 'y'), '123')), - (('SPAM', 'x, y', '123'), - ('SPAM', ('x', 'y'), '123')), - ]) - - for args, expected in tests: - with self.subTest(args): - d = Macro(*args) - - self.assertEqual(d[1:], expected) - for i, exp in enumerate(expected, start=1): - if i == 2: - self.assertIs(type(d[i]), tuple) - elif exp is not None: - self.assertIs(type(d[i]), str) - - def test_init_bad_args(self): - tests = [ - ('SPAM', StrProxy('x'), '123'), - ('SPAM', object(), '123'), - ] - for args in tests: - with self.subTest(args): - with self.assertRaises(TypeError): - Macro(*args) - - def test_valid(self): - tests = [ - # unusual name - ('SPAM', ('x', 'y'), 'run(x, y)'), - ('_SPAM_', ('x', 'y'), 'run(x, y)'), - ('X_1', ('x', 'y'), 'run(x, y)'), - # unusual args - ('SPAM', (), 'run(x, y)'), - ('SPAM', ('_x_', 'y_1'), 'run(x, y)'), - ('SPAM', 'x', 'run(x, y)'), - ('SPAM', 'x, y', 'run(x, y)'), - # unusual body - ('SPAM', ('x', 'y'), None), - ] - for args in tests: - with self.subTest(args): - directive = Macro(*args) - - directive.validate() - - def test_invalid(self): - tests = [ - # invalid name - ((None, ('x', 'y'), '123'), TypeError), - (('_', ('x', 'y'), '123'), ValueError), - (('1', ('x', 'y'), '123'), ValueError), - (('_1', ('x', 'y'), '123'), ValueError), - # invalid args - (('SPAM', (None, 'y'), '123'), ValueError), - (('SPAM', ('x', '_'), '123'), ValueError), - (('SPAM', ('x', '1'), '123'), ValueError), - (('SPAM', ('x', '_1_'), '123'), ValueError), - # There is no invalid body (including None). - ] - for args, exctype in tests: - with self.subTest(args): - directive = Macro(*args) - - with self.assertRaises(exctype): - directive.validate() - - -class IfDirectiveTests(unittest.TestCase): - - def test_type(self): - directive = IfDirective('if', '1') - - self.assertIs(type(directive), IfDirective) - self.assertIsInstance(directive, PreprocessorDirective) - - def test_attrs(self): - d = IfDirective('if', '1') - kind, condition = d.kind, d.condition - - self.assertEqual(kind, 'if') - self.assertEqual(condition, '1') - #self.assertEqual(condition, (ArithmeticCondition('1'),)) - - def test_text(self): - tests = [ - (('if', 'defined(SPAM) && 1 || (EGGS > 3 && defined(HAM))'), - 'defined(SPAM) && 1 || (EGGS > 3 && defined(HAM))'), - ] - for kind in IfDirective.KINDS: - tests.append( - ((kind, 'SPAM'), 'SPAM')) - for args, expected in tests: - with self.subTest(args): - d = IfDirective(*args) - text = d.text - - self.assertEqual(text, expected) - - def test_iter(self): - kind, condition = IfDirective('if', '1') - - self.assertEqual(kind, 'if') - self.assertEqual(condition, '1') - #self.assertEqual(condition, (ArithmeticCondition('1'),)) - - #def test_complex_conditions(self): - # ... - - def test_coerce(self): - tests = [] - for kind in IfDirective.KINDS: - if kind == 'ifdef': - cond = 'defined(SPAM)' - elif kind == 'ifndef': - cond = '! defined(SPAM)' - else: - cond = 'SPAM' - for args in wrapped_arg_combos(kind, 'SPAM'): - tests.append((args, (kind, cond))) - tests.extend([ - ((' ' + kind + ' ', ' SPAM '), (kind, cond)), - ]) - for raw in ('', ' ', None, StrProxy(' '), ()): - tests.append(((kind, raw), (kind, None))) - for kind in ('', ' ', None, StrProxy(' '), ()): - tests.append(((kind, 'SPAM'), (None, 'SPAM'))) - for args, expected in tests: - with self.subTest(args): - d = IfDirective(*args) - - self.assertEqual(tuple(d), expected) - for i, exp in enumerate(expected): - if exp is not None: - self.assertIs(type(d[i]), str) - - def test_valid(self): - tests = [] - for kind in IfDirective.KINDS: - tests.extend([ - (kind, 'SPAM'), - (kind, '_SPAM_'), - (kind, 'X_1'), - (kind, '()'), - (kind, '--'), - (kind, '???'), - ]) - for args in tests: - with self.subTest(args): - directive = IfDirective(*args) - - directive.validate() - - def test_invalid(self): - tests = [] - # kind - tests.extend([ - ((None, 'SPAM'), TypeError), - (('_', 'SPAM'), ValueError), - (('-', 'SPAM'), ValueError), - (('spam', 'SPAM'), ValueError), - ]) - for kind in PreprocessorDirective.KINDS: - if kind in IfDirective.KINDS: - continue - tests.append( - ((kind, 'SPAM'), ValueError)) - # condition - for kind in IfDirective.KINDS: - tests.extend([ - ((kind, None), TypeError), - # Any other condition is valid. - ]) - for args, exctype in tests: - with self.subTest(args): - directive = IfDirective(*args) - - with self.assertRaises(exctype): - directive.validate() - - -class IncludeTests(unittest.TestCase): - - def test_type(self): - directive = Include('') - - self.assertIs(type(directive), Include) - self.assertIsInstance(directive, PreprocessorDirective) - - def test_attrs(self): - d = Include('') - kind, file, text = d.kind, d.file, d.text - - self.assertEqual(kind, 'include') - self.assertEqual(file, '') - self.assertEqual(text, '') - - def test_iter(self): - kind, file = Include('') - - self.assertEqual(kind, 'include') - self.assertEqual(file, '') - - def test_coerce(self): - tests = [] - for arg, in wrapped_arg_combos(''): - tests.append((arg, '')) - tests.extend([ - (' ', ''), - ]) - for arg in ('', ' ', None, StrProxy(' '), ()): - tests.append((arg, None )) - for arg, expected in tests: - with self.subTest(arg): - _, file = Include(arg) - - self.assertEqual(file, expected) - if expected is not None: - self.assertIs(type(file), str) - - def test_valid(self): - tests = [ - '', - '"spam.h"', - '"internal/pycore_pystate.h"', - ] - for arg in tests: - with self.subTest(arg): - directive = Include(arg) - - directive.validate() - - def test_invalid(self): - tests = [ - (None, TypeError), - # We currently don't check the file. - ] - for arg, exctype in tests: - with self.subTest(arg): - directive = Include(arg) - - with self.assertRaises(exctype): - directive.validate() - - -class OtherDirectiveTests(unittest.TestCase): - - def test_type(self): - directive = OtherDirective('undef', 'SPAM') - - self.assertIs(type(directive), OtherDirective) - self.assertIsInstance(directive, PreprocessorDirective) - - def test_attrs(self): - d = OtherDirective('undef', 'SPAM') - kind, text = d.kind, d.text - - self.assertEqual(kind, 'undef') - self.assertEqual(text, 'SPAM') - - def test_iter(self): - kind, text = OtherDirective('undef', 'SPAM') - - self.assertEqual(kind, 'undef') - self.assertEqual(text, 'SPAM') - - def test_coerce(self): - tests = [] - for kind in OtherDirective.KINDS: - if kind in ('else', 'endif'): - continue - for args in wrapped_arg_combos(kind, '...'): - tests.append((args, (kind, '...'))) - tests.extend([ - ((' ' + kind + ' ', ' ... '), (kind, '...')), - ]) - for raw in ('', ' ', None, StrProxy(' '), ()): - tests.append(((kind, raw), (kind, None))) - for kind in ('else', 'endif'): - for args in wrapped_arg_combos(kind, None): - tests.append((args, (kind, None))) - tests.extend([ - ((' ' + kind + ' ', None), (kind, None)), - ]) - for kind in ('', ' ', None, StrProxy(' '), ()): - tests.append(((kind, '...'), (None, '...'))) - for args, expected in tests: - with self.subTest(args): - d = OtherDirective(*args) - - self.assertEqual(tuple(d), expected) - for i, exp in enumerate(expected): - if exp is not None: - self.assertIs(type(d[i]), str) - - def test_valid(self): - tests = [] - for kind in OtherDirective.KINDS: - if kind in ('else', 'endif'): - continue - tests.extend([ - (kind, '...'), - (kind, '???'), - (kind, 'SPAM'), - (kind, '1 + 1'), - ]) - for kind in ('else', 'endif'): - tests.append((kind, None)) - for args in tests: - with self.subTest(args): - directive = OtherDirective(*args) - - directive.validate() - - def test_invalid(self): - tests = [] - # kind - tests.extend([ - ((None, '...'), TypeError), - (('_', '...'), ValueError), - (('-', '...'), ValueError), - (('spam', '...'), ValueError), - ]) - for kind in PreprocessorDirective.KINDS: - if kind in OtherDirective.KINDS: - continue - tests.append( - ((kind, None), ValueError)) - # text - for kind in OtherDirective.KINDS: - if kind in ('else', 'endif'): - tests.extend([ - # Any text is invalid. - ((kind, 'SPAM'), ValueError), - ((kind, '...'), ValueError), - ]) - else: - tests.extend([ - ((kind, None), TypeError), - # Any other text is valid. - ]) - for args, exctype in tests: - with self.subTest(args): - directive = OtherDirective(*args) - - with self.assertRaises(exctype): - directive.validate() diff --git a/Lib/test/test_tools/test_c_analyzer/test_symbols/__init__.py b/Lib/test/test_tools/test_c_analyzer/test_symbols/__init__.py deleted file mode 100644 index bc502ef32d291..0000000000000 --- a/Lib/test/test_tools/test_c_analyzer/test_symbols/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -import os.path -from test.support import load_package_tests - - -def load_tests(*args): - return load_package_tests(os.path.dirname(__file__), *args) diff --git a/Lib/test/test_tools/test_c_analyzer/test_symbols/test_info.py b/Lib/test/test_tools/test_c_analyzer/test_symbols/test_info.py deleted file mode 100644 index 1282a89718c82..0000000000000 --- a/Lib/test/test_tools/test_c_analyzer/test_symbols/test_info.py +++ /dev/null @@ -1,192 +0,0 @@ -import string -import unittest - -from ..util import PseudoStr, StrProxy, Object -from .. import tool_imports_for_tests -with tool_imports_for_tests(): - from c_analyzer.common.info import ID - from c_analyzer.symbols.info import Symbol - - -class SymbolTests(unittest.TestCase): - - VALID_ARGS = ( - ID('x/y/z/spam.c', 'func', 'eggs'), - Symbol.KIND.VARIABLE, - False, - ) - VALID_KWARGS = dict(zip(Symbol._fields, VALID_ARGS)) - VALID_EXPECTED = VALID_ARGS - - def test_init_typical_binary_local(self): - id = ID(None, None, 'spam') - symbol = Symbol( - id=id, - kind=Symbol.KIND.VARIABLE, - external=False, - ) - - self.assertEqual(symbol, ( - id, - Symbol.KIND.VARIABLE, - False, - )) - - def test_init_typical_binary_global(self): - id = ID('Python/ceval.c', None, 'spam') - symbol = Symbol( - id=id, - kind=Symbol.KIND.VARIABLE, - external=False, - ) - - self.assertEqual(symbol, ( - id, - Symbol.KIND.VARIABLE, - False, - )) - - def test_init_coercion(self): - tests = [ - ('str subclass', - dict( - id=PseudoStr('eggs'), - kind=PseudoStr('variable'), - external=0, - ), - (ID(None, None, 'eggs'), - Symbol.KIND.VARIABLE, - False, - )), - ('with filename', - dict( - id=('x/y/z/spam.c', 'eggs'), - kind=PseudoStr('variable'), - external=0, - ), - (ID('x/y/z/spam.c', None, 'eggs'), - Symbol.KIND.VARIABLE, - False, - )), - ('non-str 1', - dict( - id=('a', 'b', 'c'), - kind=StrProxy('variable'), - external=0, - ), - (ID('a', 'b', 'c'), - Symbol.KIND.VARIABLE, - False, - )), - ('non-str 2', - dict( - id=('a', 'b', 'c'), - kind=Object(), - external=0, - ), - (ID('a', 'b', 'c'), - '', - False, - )), - ] - for summary, kwargs, expected in tests: - with self.subTest(summary): - symbol = Symbol(**kwargs) - - for field in Symbol._fields: - value = getattr(symbol, field) - if field == 'external': - self.assertIs(type(value), bool) - elif field == 'id': - self.assertIs(type(value), ID) - else: - self.assertIs(type(value), str) - self.assertEqual(tuple(symbol), expected) - - def test_init_all_missing(self): - id = ID(None, None, 'spam') - - symbol = Symbol(id) - - self.assertEqual(symbol, ( - id, - Symbol.KIND.VARIABLE, - None, - )) - - def test_fields(self): - id = ID('z', 'x', 'a') - - symbol = Symbol(id, 'b', False) - - self.assertEqual(symbol.id, id) - self.assertEqual(symbol.kind, 'b') - self.assertIs(symbol.external, False) - - def test___getattr__(self): - id = ID('z', 'x', 'a') - symbol = Symbol(id, 'b', False) - - filename = symbol.filename - funcname = symbol.funcname - name = symbol.name - - self.assertEqual(filename, 'z') - self.assertEqual(funcname, 'x') - self.assertEqual(name, 'a') - - def test_validate_typical(self): - id = ID('z', 'x', 'a') - - symbol = Symbol( - id=id, - kind=Symbol.KIND.VARIABLE, - external=False, - ) - - symbol.validate() # This does not fail. - - def test_validate_missing_field(self): - for field in Symbol._fields: - with self.subTest(field): - symbol = Symbol(**self.VALID_KWARGS) - symbol = symbol._replace(**{field: None}) - - with self.assertRaises(TypeError): - symbol.validate() - - def test_validate_bad_field(self): - badch = tuple(c for c in string.punctuation + string.digits) - notnames = ( - '1a', - 'a.b', - 'a-b', - '&a', - 'a++', - ) + badch - tests = [ - ('id', notnames), - ('kind', ('bogus',)), - ] - seen = set() - for field, invalid in tests: - for value in invalid: - if field != 'kind': - seen.add(value) - with self.subTest(f'{field}={value!r}'): - symbol = Symbol(**self.VALID_KWARGS) - symbol = symbol._replace(**{field: value}) - - with self.assertRaises(ValueError): - symbol.validate() - - for field, invalid in tests: - if field == 'kind': - continue - valid = seen - set(invalid) - for value in valid: - with self.subTest(f'{field}={value!r}'): - symbol = Symbol(**self.VALID_KWARGS) - symbol = symbol._replace(**{field: value}) - - symbol.validate() # This does not fail. diff --git a/Lib/test/test_tools/test_c_analyzer/test_variables/__init__.py b/Lib/test/test_tools/test_c_analyzer/test_variables/__init__.py deleted file mode 100644 index bc502ef32d291..0000000000000 --- a/Lib/test/test_tools/test_c_analyzer/test_variables/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -import os.path -from test.support import load_package_tests - - -def load_tests(*args): - return load_package_tests(os.path.dirname(__file__), *args) diff --git a/Lib/test/test_tools/test_c_analyzer/test_variables/test_find.py b/Lib/test/test_tools/test_c_analyzer/test_variables/test_find.py deleted file mode 100644 index 7a13cf3f5bf56..0000000000000 --- a/Lib/test/test_tools/test_c_analyzer/test_variables/test_find.py +++ /dev/null @@ -1,124 +0,0 @@ -import unittest - -from .. import tool_imports_for_tests -with tool_imports_for_tests(): - from c_analyzer.variables import info - from c_analyzer.variables.find import ( - vars_from_binary, - ) - - -class _Base(unittest.TestCase): - - maxDiff = None - - @property - def calls(self): - try: - return self._calls - except AttributeError: - self._calls = [] - return self._calls - - -class VarsFromBinaryTests(_Base): - - _return_iter_vars = () - _return_get_symbol_resolver = None - - def setUp(self): - super().setUp() - - self.kwargs = dict( - _iter_vars=self._iter_vars, - _get_symbol_resolver=self._get_symbol_resolver, - ) - - def _iter_vars(self, binfile, resolve, handle_id): - self.calls.append(('_iter_vars', (binfile, resolve, handle_id))) - return [(v, v.id) for v in self._return_iter_vars] - - def _get_symbol_resolver(self, known=None, dirnames=(), *, - handle_var, - filenames=None, - check_filename=None, - perfilecache=None, - ): - self.calls.append(('_get_symbol_resolver', - (known, dirnames, handle_var, filenames, - check_filename, perfilecache))) - return self._return_get_symbol_resolver - - def test_typical(self): - resolver = self._return_get_symbol_resolver = object() - variables = self._return_iter_vars = [ - info.Variable.from_parts('dir1/spam.c', None, 'var1', 'int'), - info.Variable.from_parts('dir1/spam.c', None, 'var2', 'static int'), - info.Variable.from_parts('dir1/spam.c', None, 'var3', 'char *'), - info.Variable.from_parts('dir1/spam.c', 'func2', 'var4', 'const char *'), - info.Variable.from_parts('dir1/eggs.c', None, 'var1', 'static int'), - info.Variable.from_parts('dir1/eggs.c', 'func1', 'var2', 'static char *'), - ] - known = object() - filenames = object() - - found = list(vars_from_binary('python', - known=known, - filenames=filenames, - **self.kwargs)) - - self.assertEqual(found, [ - info.Variable.from_parts('dir1/spam.c', None, 'var1', 'int'), - info.Variable.from_parts('dir1/spam.c', None, 'var2', 'static int'), - info.Variable.from_parts('dir1/spam.c', None, 'var3', 'char *'), - info.Variable.from_parts('dir1/spam.c', 'func2', 'var4', 'const char *'), - info.Variable.from_parts('dir1/eggs.c', None, 'var1', 'static int'), - info.Variable.from_parts('dir1/eggs.c', 'func1', 'var2', 'static char *'), - ]) - self.assertEqual(self.calls, [ - ('_get_symbol_resolver', (filenames, known, info.Variable.from_id, None, None, {})), - ('_iter_vars', ('python', resolver, None)), - ]) - -# self._return_iter_symbols = [ -# s_info.Symbol(('dir1/spam.c', None, 'var1'), 'variable', False), -# s_info.Symbol(('dir1/spam.c', None, 'var2'), 'variable', False), -# s_info.Symbol(('dir1/spam.c', None, 'func1'), 'function', False), -# s_info.Symbol(('dir1/spam.c', None, 'func2'), 'function', True), -# s_info.Symbol(('dir1/spam.c', None, 'var3'), 'variable', False), -# s_info.Symbol(('dir1/spam.c', 'func2', 'var4'), 'variable', False), -# s_info.Symbol(('dir1/ham.c', None, 'var1'), 'variable', True), -# s_info.Symbol(('dir1/eggs.c', None, 'var1'), 'variable', False), -# s_info.Symbol(('dir1/eggs.c', None, 'xyz'), 'other', False), -# s_info.Symbol(('dir1/eggs.c', '???', 'var2'), 'variable', False), -# s_info.Symbol(('???', None, 'var_x'), 'variable', False), -# s_info.Symbol(('???', '???', 'var_y'), 'variable', False), -# s_info.Symbol((None, None, '???'), 'other', False), -# ] -# known = object() -# -# vars_from_binary('python', knownvars=known, **this.kwargs) -# found = list(globals_from_symbols(['dir1'], self.iter_symbols)) -# -# self.assertEqual(found, [ -# info.Variable.from_parts('dir1/spam.c', None, 'var1', '???'), -# info.Variable.from_parts('dir1/spam.c', None, 'var2', '???'), -# info.Variable.from_parts('dir1/spam.c', None, 'var3', '???'), -# info.Variable.from_parts('dir1/spam.c', 'func2', 'var4', '???'), -# info.Variable.from_parts('dir1/eggs.c', None, 'var1', '???'), -# ]) -# self.assertEqual(self.calls, [ -# ('iter_symbols', (['dir1'],)), -# ]) -# -# def test_no_symbols(self): -# self._return_iter_symbols = [] -# -# found = list(globals_from_symbols(['dir1'], self.iter_symbols)) -# -# self.assertEqual(found, []) -# self.assertEqual(self.calls, [ -# ('iter_symbols', (['dir1'],)), -# ]) - - # XXX need functional test diff --git a/Lib/test/test_tools/test_c_analyzer/test_variables/test_info.py b/Lib/test/test_tools/test_c_analyzer/test_variables/test_info.py deleted file mode 100644 index d424d8eebb811..0000000000000 --- a/Lib/test/test_tools/test_c_analyzer/test_variables/test_info.py +++ /dev/null @@ -1,244 +0,0 @@ -import string -import unittest - -from ..util import PseudoStr, StrProxy, Object -from .. import tool_imports_for_tests -with tool_imports_for_tests(): - from c_analyzer.common.info import UNKNOWN, ID - from c_analyzer.variables.info import ( - normalize_vartype, Variable - ) - - -class NormalizeVartypeTests(unittest.TestCase): - - def test_basic(self): - tests = [ - (None, None), - ('', ''), - ('int', 'int'), - (PseudoStr('int'), 'int'), - (StrProxy('int'), 'int'), - ] - for vartype, expected in tests: - with self.subTest(vartype): - normalized = normalize_vartype(vartype) - - self.assertEqual(normalized, expected) - - -class VariableTests(unittest.TestCase): - - VALID_ARGS = ( - ('x/y/z/spam.c', 'func', 'eggs'), - 'static', - 'int', - ) - VALID_KWARGS = dict(zip(Variable._fields, VALID_ARGS)) - VALID_EXPECTED = VALID_ARGS - - def test_init_typical_global(self): - for storage in ('static', 'extern', 'implicit'): - with self.subTest(storage): - static = Variable( - id=ID( - filename='x/y/z/spam.c', - funcname=None, - name='eggs', - ), - storage=storage, - vartype='int', - ) - - self.assertEqual(static, ( - ('x/y/z/spam.c', None, 'eggs'), - storage, - 'int', - )) - - def test_init_typical_local(self): - for storage in ('static', 'local'): - with self.subTest(storage): - static = Variable( - id=ID( - filename='x/y/z/spam.c', - funcname='func', - name='eggs', - ), - storage=storage, - vartype='int', - ) - - self.assertEqual(static, ( - ('x/y/z/spam.c', 'func', 'eggs'), - storage, - 'int', - )) - - def test_init_all_missing(self): - for value in ('', None): - with self.subTest(repr(value)): - static = Variable( - id=value, - storage=value, - vartype=value, - ) - - self.assertEqual(static, ( - None, - None, - None, - )) - - def test_init_all_coerced(self): - id = ID('x/y/z/spam.c', 'func', 'spam') - tests = [ - ('str subclass', - dict( - id=( - PseudoStr('x/y/z/spam.c'), - PseudoStr('func'), - PseudoStr('spam'), - ), - storage=PseudoStr('static'), - vartype=PseudoStr('int'), - ), - (id, - 'static', - 'int', - )), - ('non-str 1', - dict( - id=id, - storage=Object(), - vartype=Object(), - ), - (id, - '', - '', - )), - ('non-str 2', - dict( - id=id, - storage=StrProxy('static'), - vartype=StrProxy('variable'), - ), - (id, - 'static', - 'variable', - )), - ('non-str', - dict( - id=id, - storage=('a', 'b', 'c'), - vartype=('x', 'y', 'z'), - ), - (id, - "('a', 'b', 'c')", - "('x', 'y', 'z')", - )), - ] - for summary, kwargs, expected in tests: - with self.subTest(summary): - static = Variable(**kwargs) - - for field in Variable._fields: - value = getattr(static, field) - if field == 'id': - self.assertIs(type(value), ID) - else: - self.assertIs(type(value), str) - self.assertEqual(tuple(static), expected) - - def test_iterable(self): - static = Variable(**self.VALID_KWARGS) - - id, storage, vartype = static - - values = (id, storage, vartype) - for value, expected in zip(values, self.VALID_EXPECTED): - self.assertEqual(value, expected) - - def test_fields(self): - static = Variable(('a', 'b', 'z'), 'x', 'y') - - self.assertEqual(static.id, ('a', 'b', 'z')) - self.assertEqual(static.storage, 'x') - self.assertEqual(static.vartype, 'y') - - def test___getattr__(self): - static = Variable(('a', 'b', 'z'), 'x', 'y') - - self.assertEqual(static.filename, 'a') - self.assertEqual(static.funcname, 'b') - self.assertEqual(static.name, 'z') - - def test_validate_typical(self): - validstorage = ('static', 'extern', 'implicit', 'local') - self.assertEqual(set(validstorage), set(Variable.STORAGE)) - - for storage in validstorage: - with self.subTest(storage): - static = Variable( - id=ID( - filename='x/y/z/spam.c', - funcname='func', - name='eggs', - ), - storage=storage, - vartype='int', - ) - - static.validate() # This does not fail. - - def test_validate_missing_field(self): - for field in Variable._fields: - with self.subTest(field): - static = Variable(**self.VALID_KWARGS) - static = static._replace(**{field: None}) - - with self.assertRaises(TypeError): - static.validate() - for field in ('storage', 'vartype'): - with self.subTest(field): - static = Variable(**self.VALID_KWARGS) - static = static._replace(**{field: UNKNOWN}) - - with self.assertRaises(TypeError): - static.validate() - - def test_validate_bad_field(self): - badch = tuple(c for c in string.punctuation + string.digits) - notnames = ( - '1a', - 'a.b', - 'a-b', - '&a', - 'a++', - ) + badch - tests = [ - ('id', ()), # Any non-empty str is okay. - ('storage', ('external', 'global') + notnames), - ('vartype', ()), # Any non-empty str is okay. - ] - seen = set() - for field, invalid in tests: - for value in invalid: - seen.add(value) - with self.subTest(f'{field}={value!r}'): - static = Variable(**self.VALID_KWARGS) - static = static._replace(**{field: value}) - - with self.assertRaises(ValueError): - static.validate() - - for field, invalid in tests: - if field == 'id': - continue - valid = seen - set(invalid) - for value in valid: - with self.subTest(f'{field}={value!r}'): - static = Variable(**self.VALID_KWARGS) - static = static._replace(**{field: value}) - - static.validate() # This does not fail. diff --git a/Lib/test/test_tools/test_c_analyzer/test_variables/test_known.py b/Lib/test/test_tools/test_c_analyzer/test_variables/test_known.py deleted file mode 100644 index 49ff45c6d1b2c..0000000000000 --- a/Lib/test/test_tools/test_c_analyzer/test_variables/test_known.py +++ /dev/null @@ -1,139 +0,0 @@ -import re -import textwrap -import unittest - -from .. import tool_imports_for_tests -with tool_imports_for_tests(): - from c_analyzer.common.info import ID - from c_analyzer.variables.info import Variable - from c_analyzer.variables.known import ( - read_file, - from_file, - ) - -class _BaseTests(unittest.TestCase): - - maxDiff = None - - @property - def calls(self): - try: - return self._calls - except AttributeError: - self._calls = [] - return self._calls - - -class ReadFileTests(_BaseTests): - - _return_read_tsv = () - - def _read_tsv(self, *args): - self.calls.append(('_read_tsv', args)) - return self._return_read_tsv - - def test_typical(self): - lines = textwrap.dedent(''' - filename funcname name kind declaration - file1.c - var1 variable static int - file1.c func1 local1 variable static int - file1.c - var2 variable int - file1.c func2 local2 variable char * - file2.c - var1 variable char * - ''').strip().splitlines() - lines = [re.sub(r'\s+', '\t', line, 4) for line in lines] - self._return_read_tsv = [tuple(v.strip() for v in line.split('\t')) - for line in lines[1:]] - - known = list(read_file('known.tsv', _read_tsv=self._read_tsv)) - - self.assertEqual(known, [ - ('variable', ID('file1.c', '', 'var1'), 'static int'), - ('variable', ID('file1.c', 'func1', 'local1'), 'static int'), - ('variable', ID('file1.c', '', 'var2'), 'int'), - ('variable', ID('file1.c', 'func2', 'local2'), 'char *'), - ('variable', ID('file2.c', '', 'var1'), 'char *'), - ]) - self.assertEqual(self.calls, [ - ('_read_tsv', - ('known.tsv', 'filename\tfuncname\tname\tkind\tdeclaration')), - ]) - - def test_empty(self): - self._return_read_tsv = [] - - known = list(read_file('known.tsv', _read_tsv=self._read_tsv)) - - self.assertEqual(known, []) - self.assertEqual(self.calls, [ - ('_read_tsv', ('known.tsv', 'filename\tfuncname\tname\tkind\tdeclaration')), - ]) - - -class FromFileTests(_BaseTests): - - _return_read_file = () - _return_handle_var = () - - def _read_file(self, infile): - self.calls.append(('_read_file', (infile,))) - return iter(self._return_read_file) - - def _handle_var(self, varid, decl): - self.calls.append(('_handle_var', (varid, decl))) - var = self._return_handle_var.pop(0) - return var - - def test_typical(self): - expected = [ - Variable.from_parts('file1.c', '', 'var1', 'static int'), - Variable.from_parts('file1.c', 'func1', 'local1', 'static int'), - Variable.from_parts('file1.c', '', 'var2', 'int'), - Variable.from_parts('file1.c', 'func2', 'local2', 'char *'), - Variable.from_parts('file2.c', '', 'var1', 'char *'), - ] - self._return_read_file = [('variable', v.id, v.vartype) - for v in expected] -# ('variable', ID('file1.c', '', 'var1'), 'static int'), -# ('variable', ID('file1.c', 'func1', 'local1'), 'static int'), -# ('variable', ID('file1.c', '', 'var2'), 'int'), -# ('variable', ID('file1.c', 'func2', 'local2'), 'char *'), -# ('variable', ID('file2.c', '', 'var1'), 'char *'), -# ] - self._return_handle_var = list(expected) # a copy - - known = from_file('known.tsv', - handle_var=self._handle_var, - _read_file=self._read_file, - ) - - self.assertEqual(known, { - 'variables': {v.id: v for v in expected}, - }) -# Variable.from_parts('file1.c', '', 'var1', 'static int'), -# Variable.from_parts('file1.c', 'func1', 'local1', 'static int'), -# Variable.from_parts('file1.c', '', 'var2', 'int'), -# Variable.from_parts('file1.c', 'func2', 'local2', 'char *'), -# Variable.from_parts('file2.c', '', 'var1', 'char *'), -# ]}, -# }) - self.assertEqual(self.calls, [ - ('_read_file', ('known.tsv',)), - *[('_handle_var', (v.id, v.vartype)) - for v in expected], - ]) - - def test_empty(self): - self._return_read_file = [] - - known = from_file('known.tsv', - handle_var=self._handle_var, - _read_file=self._read_file, - ) - - self.assertEqual(known, { - 'variables': {}, - }) - self.assertEqual(self.calls, [ - ('_read_file', ('known.tsv',)), - ]) diff --git a/Lib/test/test_tools/test_c_analyzer/util.py b/Lib/test/test_tools/test_c_analyzer/util.py deleted file mode 100644 index ba73b0a4b5fc6..0000000000000 --- a/Lib/test/test_tools/test_c_analyzer/util.py +++ /dev/null @@ -1,60 +0,0 @@ -import itertools - - -class PseudoStr(str): - pass - - -class StrProxy: - def __init__(self, value): - self.value = value - def __str__(self): - return self.value - def __bool__(self): - return bool(self.value) - - -class Object: - def __repr__(self): - return '' - - -def wrapped_arg_combos(*args, - wrappers=(PseudoStr, StrProxy), - skip=(lambda w, i, v: not isinstance(v, str)), - ): - """Yield every possible combination of wrapped items for the given args. - - Effectively, the wrappers are applied to the args according to the - powerset of the args indicies. So the result includes the args - completely unwrapped. - - If "skip" is supplied (default is to skip all non-str values) and - it returns True for a given arg index/value then that arg will - remain unwrapped, - - Only unique results are returned. If an arg was skipped for one - of the combinations then it could end up matching one of the other - combinations. In that case only one of them will be yielded. - """ - if not args: - return - indices = list(range(len(args))) - # The powerset (from recipe in the itertools docs). - combos = itertools.chain.from_iterable(itertools.combinations(indices, r) - for r in range(len(indices)+1)) - seen = set() - for combo in combos: - for wrap in wrappers: - indexes = [] - applied = list(args) - for i in combo: - arg = args[i] - if skip and skip(wrap, i, arg): - continue - indexes.append(i) - applied[i] = wrap(arg) - key = (wrap, tuple(indexes)) - if key not in seen: - yield tuple(applied) - seen.add(key) diff --git a/Tools/c-analyzer/README b/Tools/c-analyzer/README index 8cf20e276d927..86bf1e77e0bfe 100644 --- a/Tools/c-analyzer/README +++ b/Tools/c-analyzer/README @@ -36,6 +36,10 @@ should be run to ensure that no new globals have been added: python3 Tools/c-analyzer/check-c-globals.py +You can also use the more generic tool: + + python3 Tools/c-analyzer/c-analyzer.py + If it reports any globals then they should be resolved. If the globals are runtime state then they should be folded into _PyRuntimeState. Otherwise they should be added to ignored-globals.txt. diff --git a/Tools/c-analyzer/c-analyzer.py b/Tools/c-analyzer/c-analyzer.py new file mode 100644 index 0000000000000..4a5e88cdaf1b0 --- /dev/null +++ b/Tools/c-analyzer/c-analyzer.py @@ -0,0 +1,7 @@ +from cpython.__main__ import parse_args, main, configure_logger + + +cmd, cmd_kwargs, verbosity, traceback_cm = parse_args() +configure_logger(verbosity) +with traceback_cm: + main(cmd, cmd_kwargs) diff --git a/Tools/c-analyzer/c-globals.py b/Tools/c-analyzer/c-globals.py deleted file mode 100644 index b36b791241d53..0000000000000 --- a/Tools/c-analyzer/c-globals.py +++ /dev/null @@ -1,9 +0,0 @@ -# This is a script equivalent of running "python -m test.test_c_globals.cg". - -from cpython.__main__ import parse_args, main - - -# This is effectively copied from cg/__main__.py: -if __name__ == '__main__': - cmd, cmdkwargs = parse_args() - main(cmd, cmdkwargs) diff --git a/Tools/c-analyzer/c_analyzer/__init__.py b/Tools/c-analyzer/c_analyzer/__init__.py index e69de29bb2d1d..4a01cd396f5f5 100644 --- a/Tools/c-analyzer/c_analyzer/__init__.py +++ b/Tools/c-analyzer/c_analyzer/__init__.py @@ -0,0 +1,103 @@ +from c_parser import ( + parse_files as _parse_files, +) +from c_parser.info import ( + KIND, + TypeDeclaration, + filter_by_kind, + collate_by_kind_group, + resolve_parsed, +) +from . import ( + analyze as _analyze, + datafiles as _datafiles, +) +from .info import Analysis + + +def analyze(filenmes, **kwargs): + results = iter_analyis_results(filenames, **kwargs) + return Analysis.from_results(results) + + +def iter_analysis_results(filenmes, *, + known=None, + **kwargs + ): + decls = iter_decls(filenames, **kwargs) + yield from analyze_decls(decls, known) + + +def iter_decls(filenames, *, + kinds=None, + parse_files=_parse_files, + **kwargs + ): + kinds = KIND.DECLS if kinds is None else (KIND.DECLS & set(kinds)) + parse_files = parse_files or _parse_files + + parsed = parse_files(filenames, **kwargs) + parsed = filter_by_kind(parsed, kinds) + for item in parsed: + yield resolve_parsed(item) + + +def analyze_decls(decls, known, *, + analyze_resolved=None, + handle_unresolved=True, + relroot=None, + ): + knowntypes, knowntypespecs = _datafiles.get_known( + known, + handle_unresolved=handle_unresolved, + analyze_resolved=analyze_resolved, + relroot=relroot, + ) + + decls = list(decls) + collated = collate_by_kind_group(decls) + + types = {decl: None for decl in collated['type']} + typespecs = _analyze.get_typespecs(types) + + def analyze_decl(decl): + return _analyze.analyze_decl( + decl, + typespecs, + knowntypespecs, + types, + knowntypes, + analyze_resolved=analyze_resolved, + ) + _analyze.analyze_type_decls(types, analyze_decl, handle_unresolved) + for decl in decls: + if decl in types: + resolved = types[decl] + else: + resolved = analyze_decl(decl) + if resolved and handle_unresolved: + typedeps, _ = resolved + if not isinstance(typedeps, TypeDeclaration): + if not typedeps or None in typedeps: + raise NotImplementedError((decl, resolved)) + + yield decl, resolved + + +####################################### +# checks + +def check_all(analysis, checks, *, failfast=False): + for check in checks or (): + for data, failure in check(analysis): + if failure is None: + continue + + yield data, failure + if failfast: + yield None, None + break + else: + continue + # We failed fast. + break diff --git a/Tools/c-analyzer/c_analyzer/__main__.py b/Tools/c-analyzer/c_analyzer/__main__.py new file mode 100644 index 0000000000000..1fd45b985d9bc --- /dev/null +++ b/Tools/c-analyzer/c_analyzer/__main__.py @@ -0,0 +1,501 @@ +import io +import logging +import os.path +import re +import sys + +from c_common.logging import VERBOSITY, Printer +from c_common.scriptutil import ( + add_verbosity_cli, + add_traceback_cli, + add_sepval_cli, + add_files_cli, + add_commands_cli, + process_args_by_key, + configure_logger, + get_prog, + filter_filenames, + iter_marks, +) +from c_parser.info import KIND, is_type_decl +from . import ( + analyze as _analyze, + check_all as _check_all, + datafiles as _datafiles, +) + + +KINDS = [ + KIND.TYPEDEF, + KIND.STRUCT, + KIND.UNION, + KIND.ENUM, + KIND.FUNCTION, + KIND.VARIABLE, + KIND.STATEMENT, +] + +logger = logging.getLogger(__name__) + + +####################################### +# table helpers + +TABLE_SECTIONS = { + 'types': ( + ['kind', 'name', 'data', 'file'], + is_type_decl, + (lambda v: (v.kind.value, v.filename or '', v.name)), + ), + 'typedefs': 'types', + 'structs': 'types', + 'unions': 'types', + 'enums': 'types', + 'functions': ( + ['name', 'data', 'file'], + (lambda kind: kind is KIND.FUNCTION), + (lambda v: (v.filename or '', v.name)), + ), + 'variables': ( + ['name', 'parent', 'data', 'file'], + (lambda kind: kind is KIND.VARIABLE), + (lambda v: (v.filename or '', str(v.parent) if v.parent else '', v.name)), + ), + 'statements': ( + ['file', 'parent', 'data'], + (lambda kind: kind is KIND.STATEMENT), + (lambda v: (v.filename or '', str(v.parent) if v.parent else '', v.name)), + ), + KIND.TYPEDEF: 'typedefs', + KIND.STRUCT: 'structs', + KIND.UNION: 'unions', + KIND.ENUM: 'enums', + KIND.FUNCTION: 'functions', + KIND.VARIABLE: 'variables', + KIND.STATEMENT: 'statements', +} + + +def _render_table(items, columns, relroot=None): + # XXX improve this + header = '\t'.join(columns) + div = '--------------------' + yield header + yield div + total = 0 + for item in items: + rowdata = item.render_rowdata(columns) + row = [rowdata[c] for c in columns] + if relroot and 'file' in columns: + index = columns.index('file') + row[index] = os.path.relpath(row[index], relroot) + yield '\t'.join(row) + total += 1 + yield div + yield f'total: {total}' + + +def build_section(name, groupitems, *, relroot=None): + info = TABLE_SECTIONS[name] + while type(info) is not tuple: + if name in KINDS: + name = info + info = TABLE_SECTIONS[info] + + columns, match_kind, sortkey = info + items = (v for v in groupitems if match_kind(v.kind)) + items = sorted(items, key=sortkey) + def render(): + yield '' + yield f'{name}:' + yield '' + for line in _render_table(items, columns, relroot): + yield line + return items, render + + +####################################### +# the checks + +CHECKS = { + #'globals': _check_globals, +} + + +def add_checks_cli(parser, checks=None, *, add_flags=None): + default = False + if not checks: + checks = list(CHECKS) + default = True + elif isinstance(checks, str): + checks = [checks] + if (add_flags is None and len(checks) > 1) or default: + add_flags = True + + process_checks = add_sepval_cli(parser, '--check', 'checks', checks) + if add_flags: + for check in checks: + parser.add_argument(f'--{check}', dest='checks', + action='append_const', const=check) + return [ + process_checks, + ] + + +def _get_check_handlers(fmt, printer, verbosity=VERBOSITY): + div = None + def handle_after(): + pass + if not fmt: + div = '' + def handle_failure(failure, data): + data = repr(data) + if verbosity >= 3: + logger.info(f'failure: {failure}') + logger.info(f'data: {data}') + else: + logger.warn(f'failure: {failure} (data: {data})') + elif fmt == 'raw': + def handle_failure(failure, data): + print(f'{failure!r} {data!r}') + elif fmt == 'brief': + def handle_failure(failure, data): + parent = data.parent or '' + funcname = parent if isinstance(parent, str) else parent.name + name = f'({funcname}).{data.name}' if funcname else data.name + failure = failure.split('\t')[0] + print(f'{data.filename}:{name} - {failure}') + elif fmt == 'summary': + def handle_failure(failure, data): + parent = data.parent or '' + funcname = parent if isinstance(parent, str) else parent.name + print(f'{data.filename:35}\t{funcname or "-":35}\t{data.name:40}\t{failure}') + elif fmt == 'full': + div = '' + def handle_failure(failure, data): + name = data.shortkey if data.kind is KIND.VARIABLE else data.name + parent = data.parent or '' + funcname = parent if isinstance(parent, str) else parent.name + known = 'yes' if data.is_known else '*** NO ***' + print(f'{data.kind.value} {name!r} failed ({failure})') + print(f' file: {data.filename}') + print(f' func: {funcname or "-"}') + print(f' name: {data.name}') + print(f' data: ...') + print(f' type unknown: {known}') + else: + if fmt in FORMATS: + raise NotImplementedError(fmt) + raise ValueError(f'unsupported fmt {fmt!r}') + return handle_failure, handle_after, div + + +####################################### +# the formats + +def fmt_raw(analysis): + for item in analysis: + yield from item.render('raw') + + +def fmt_brief(analysis): + # XXX Support sorting. + items = sorted(analysis) + for kind in KINDS: + if kind is KIND.STATEMENT: + continue + for item in items: + if item.kind is not kind: + continue + yield from item.render('brief') + yield f' total: {len(items)}' + + +def fmt_summary(analysis): + # XXX Support sorting and grouping. + items = list(analysis) + total = len(items) + + def section(name): + _, render = build_section(name, items) + yield from render() + + yield from section('types') + yield from section('functions') + yield from section('variables') + yield from section('statements') + + yield '' +# yield f'grand total: {len(supported) + len(unsupported)}' + yield f'grand total: {total}' + + +def fmt_full(analysis): + # XXX Support sorting. + items = sorted(analysis, key=lambda v: v.key) + yield '' + for item in items: + yield from item.render('full') + yield '' + yield f'total: {len(items)}' + + +FORMATS = { + 'raw': fmt_raw, + 'brief': fmt_brief, + 'summary': fmt_summary, + 'full': fmt_full, +} + + +def add_output_cli(parser, *, default='summary'): + parser.add_argument('--format', dest='fmt', default=default, choices=tuple(FORMATS)) + + def process_args(args): + pass + return process_args + + +####################################### +# the commands + +def _cli_check(parser, checks=None, **kwargs): + if isinstance(checks, str): + checks = [checks] + if checks is False: + process_checks = None + elif checks is None: + process_checks = add_checks_cli(parser) + elif len(checks) == 1 and type(checks) is not dict and re.match(r'^<.*>$', checks[0]): + check = checks[0][1:-1] + def process_checks(args): + args.checks = [check] + else: + process_checks = add_checks_cli(parser, checks=checks) + process_output = add_output_cli(parser, default=None) + process_files = add_files_cli(parser, **kwargs) + return [ + process_checks, + process_output, + process_files, + ] + + +def cmd_check(filenames, *, + checks=None, + ignored=None, + fmt=None, + relroot=None, + failfast=False, + iter_filenames=None, + verbosity=VERBOSITY, + _analyze=_analyze, + _CHECKS=CHECKS, + **kwargs + ): + if not checks: + checks = _CHECKS + elif isinstance(checks, str): + checks = [checks] + checks = [_CHECKS[c] if isinstance(c, str) else c + for c in checks] + printer = Printer(verbosity) + (handle_failure, handle_after, div + ) = _get_check_handlers(fmt, printer, verbosity) + + filenames = filter_filenames(filenames, iter_filenames) + + logger.info('analyzing...') + analyzed = _analyze(filenames, **kwargs) + if relroot: + analyzed.fix_filenames(relroot) + + logger.info('checking...') + numfailed = 0 + for data, failure in _check_all(analyzed, checks, failfast=failfast): + if data is None: + printer.info('stopping after one failure') + break + if div is not None and numfailed > 0: + printer.info(div) + numfailed += 1 + handle_failure(failure, data) + handle_after() + + printer.info('-------------------------') + logger.info(f'total failures: {numfailed}') + logger.info('done checking') + + if numfailed > 0: + sys.exit(numfailed) + + +def _cli_analyze(parser, **kwargs): + process_output = add_output_cli(parser) + process_files = add_files_cli(parser, **kwargs) + return [ + process_output, + process_files, + ] + + +# XXX Support filtering by kind. +def cmd_analyze(filenames, *, + fmt=None, + iter_filenames=None, + verbosity=None, + _analyze=_analyze, + formats=FORMATS, + **kwargs + ): + verbosity = verbosity if verbosity is not None else 3 + + try: + do_fmt = formats[fmt] + except KeyError: + raise ValueError(f'unsupported fmt {fmt!r}') + + filenames = filter_filenames(filenames, iter_filenames) + if verbosity == 2: + def iter_filenames(filenames=filenames): + marks = iter_marks() + for filename in filenames: + print(next(marks), end='') + yield filename + filenames = iter_filenames() + elif verbosity > 2: + def iter_filenames(filenames=filenames): + for filename in filenames: + print(f'<{filename}>') + yield filename + filenames = iter_filenames() + + logger.info('analyzing...') + analyzed = _analyze(filenames, **kwargs) + + for line in do_fmt(analyzed): + print(line) + + +def _cli_data(parser, filenames=None, known=None): + ArgumentParser = type(parser) + common = ArgumentParser(add_help=False) + if filenames is None: + common.add_argument('filenames', metavar='FILE', nargs='+') + + subs = parser.add_subparsers(dest='datacmd') + + sub = subs.add_parser('show', parents=[common]) + if known is None: + sub.add_argument('--known', required=True) + + sub = subs.add_parser('dump') + if known is None: + sub.add_argument('--known') + sub.add_argument('--show', action='store_true') + + sub = subs.add_parser('check') + if known is None: + sub.add_argument('--known', required=True) + + return None + + +def cmd_data(datacmd, filenames, known=None, *, + _analyze=_analyze, + formats=FORMATS, + extracolumns=None, + relroot=None, + **kwargs + ): + kwargs.pop('verbosity', None) + usestdout = kwargs.pop('show', None) + if datacmd == 'show': + do_fmt = formats['summary'] + if isinstance(known, str): + known, _ = _datafiles.get_known(known, extracolumns, relroot) + for line in do_fmt(known): + print(line) + elif datacmd == 'dump': + analyzed = _analyze(filenames, **kwargs) + if known is None or usestdout: + outfile = io.StringIO() + _datafiles.write_known(analyzed, outfile, extracolumns, + relroot=relroot) + print(outfile.getvalue()) + else: + _datafiles.write_known(analyzed, known, extracolumns, + relroot=relroot) + elif datacmd == 'check': + raise NotImplementedError(datacmd) + else: + raise ValueError(f'unsupported data command {datacmd!r}') + + +COMMANDS = { + 'check': ( + 'analyze and fail if the given C source/header files have any problems', + [_cli_check], + cmd_check, + ), + 'analyze': ( + 'report on the state of the given C source/header files', + [_cli_analyze], + cmd_analyze, + ), + 'data': ( + 'check/manage local data (e.g. knwon types, ignored vars, caches)', + [_cli_data], + cmd_data, + ), +} + + +####################################### +# the script + +def parse_args(argv=sys.argv[1:], prog=sys.argv[0], *, subset=None): + import argparse + parser = argparse.ArgumentParser( + prog=prog or get_prog(), + ) + + processors = add_commands_cli( + parser, + commands={k: v[1] for k, v in COMMANDS.items()}, + commonspecs=[ + add_verbosity_cli, + add_traceback_cli, + ], + subset=subset, + ) + + args = parser.parse_args(argv) + ns = vars(args) + + cmd = ns.pop('cmd') + + verbosity, traceback_cm = process_args_by_key( + args, + processors[cmd], + ['verbosity', 'traceback_cm'], + ) + # "verbosity" is sent to the commands, so we put it back. + args.verbosity = verbosity + + return cmd, ns, verbosity, traceback_cm + + +def main(cmd, cmd_kwargs): + try: + run_cmd = COMMANDS[cmd][0] + except KeyError: + raise ValueError(f'unsupported cmd {cmd!r}') + run_cmd(**cmd_kwargs) + + +if __name__ == '__main__': + cmd, cmd_kwargs, verbosity, traceback_cm = parse_args() + configure_logger(verbosity) + with traceback_cm: + main(cmd, cmd_kwargs) diff --git a/Tools/c-analyzer/c_analyzer/analyze.py b/Tools/c-analyzer/c_analyzer/analyze.py new file mode 100644 index 0000000000000..d8ae915e42002 --- /dev/null +++ b/Tools/c-analyzer/c_analyzer/analyze.py @@ -0,0 +1,307 @@ +from c_parser.info import ( + KIND, + TypeDeclaration, + POTSType, + FuncPtr, + is_pots, + is_funcptr, +) +from .info import ( + IGNORED, + UNKNOWN, + is_system_type, + SystemType, +) + + +def get_typespecs(typedecls): + typespecs = {} + for decl in typedecls: + if decl.shortkey not in typespecs: + typespecs[decl.shortkey] = [decl] + else: + typespecs[decl.shortkey].append(decl) + return typespecs + + +def analyze_decl(decl, typespecs, knowntypespecs, types, knowntypes, *, + analyze_resolved=None): + resolved = resolve_decl(decl, typespecs, knowntypespecs, types) + if resolved is None: + # The decl is supposed to be skipped or ignored. + return None + if analyze_resolved is None: + return resolved, None + return analyze_resolved(resolved, decl, types, knowntypes) + +# This alias helps us avoid name collisions. +_analyze_decl = analyze_decl + + +def analyze_type_decls(types, analyze_decl, handle_unresolved=True): + unresolved = set(types) + while unresolved: + updated = [] + for decl in unresolved: + resolved = analyze_decl(decl) + if resolved is None: + # The decl should be skipped or ignored. + types[decl] = IGNORED + updated.append(decl) + continue + typedeps, _ = resolved + if typedeps is None: + raise NotImplementedError(decl) + if UNKNOWN in typedeps: + # At least one dependency is unknown, so this decl + # is not resolvable. + types[decl] = UNKNOWN + updated.append(decl) + continue + if None in typedeps: + # XXX + # Handle direct recursive types first. + nonrecursive = 1 + if decl.kind is KIND.STRUCT or decl.kind is KIND.UNION: + nonrecursive = 0 + i = 0 + for member, dep in zip(decl.members, typedeps): + if dep is None: + if member.vartype.typespec != decl.shortkey: + nonrecursive += 1 + else: + typedeps[i] = decl + i += 1 + if nonrecursive: + # We don't have all dependencies resolved yet. + continue + types[decl] = resolved + updated.append(decl) + if updated: + for decl in updated: + unresolved.remove(decl) + else: + # XXX + # Handle indirect recursive types. + ... + # We couldn't resolve the rest. + # Let the caller deal with it! + break + if unresolved and handle_unresolved: + if handle_unresolved is True: + handle_unresolved = _handle_unresolved + handle_unresolved(unresolved, types, analyze_decl) + + +def resolve_decl(decl, typespecs, knowntypespecs, types): + if decl.kind is KIND.ENUM: + typedeps = [] + else: + if decl.kind is KIND.VARIABLE: + vartypes = [decl.vartype] + elif decl.kind is KIND.FUNCTION: + vartypes = [decl.signature.returntype] + elif decl.kind is KIND.TYPEDEF: + vartypes = [decl.vartype] + elif decl.kind is KIND.STRUCT or decl.kind is KIND.UNION: + vartypes = [m.vartype for m in decl.members] + else: + # Skip this one! + return None + + typedeps = [] + for vartype in vartypes: + typespec = vartype.typespec + if is_pots(typespec): + typedecl = POTSType(typespec) + elif is_system_type(typespec): + typedecl = SystemType(typespec) + elif is_funcptr(vartype): + typedecl = FuncPtr(vartype) + else: + typedecl = find_typedecl(decl, typespec, typespecs) + if typedecl is None: + typedecl = find_typedecl(decl, typespec, knowntypespecs) + elif not isinstance(typedecl, TypeDeclaration): + raise NotImplementedError(repr(typedecl)) + if typedecl is None: + # We couldn't find it! + typedecl = UNKNOWN + elif typedecl not in types: + # XXX How can this happen? + typedecl = UNKNOWN + elif types[typedecl] is UNKNOWN: + typedecl = UNKNOWN + elif types[typedecl] is IGNORED: + # We don't care if it didn't resolve. + pass + elif types[typedecl] is None: + # The typedecl for the typespec hasn't been resolved yet. + typedecl = None + typedeps.append(typedecl) + return typedeps + + +def find_typedecl(decl, typespec, typespecs): + specdecls = typespecs.get(typespec) + if not specdecls: + return None + + filename = decl.filename + + if len(specdecls) == 1: + typedecl, = specdecls + if '-' in typespec and typedecl.filename != filename: + # Inlined types are always in the same file. + return None + return typedecl + + # Decide which one to return. + candidates = [] + samefile = None + for typedecl in specdecls: + type_filename = typedecl.filename + if type_filename == filename: + if samefile is not None: + # We expect type names to be unique in a file. + raise NotImplementedError((decl, samefile, typedecl)) + samefile = typedecl + elif filename.endswith('.c') and not type_filename.endswith('.h'): + # If the decl is in a source file then we expect the + # type to be in the same file or in a header file. + continue + candidates.append(typedecl) + if not candidates: + return None + elif len(candidates) == 1: + winner, = candidates + # XXX Check for inline? + elif '-' in typespec: + # Inlined types are always in the same file. + winner = samefile + elif samefile is not None: + # Favor types in the same file. + winner = samefile + else: + # We don't know which to return. + raise NotImplementedError((decl, candidates)) + + return winner + + +############################# +# handling unresolved decls + +class Skipped(TypeDeclaration): + def __init__(self): + _file = _name = _data = _parent = None + super().__init__(_file, _name, _data, _parent, _shortkey='') +_SKIPPED = Skipped() +del Skipped + + +def _handle_unresolved(unresolved, types, analyze_decl): + #raise NotImplementedError(unresolved) + + dump = True + dump = False + if dump: + print() + for decl in types: # Preserve the original order. + if decl not in unresolved: + assert types[decl] is not None, decl + if types[decl] in (UNKNOWN, IGNORED): + unresolved.add(decl) + if dump: + _dump_unresolved(decl, types, analyze_decl) + print() + else: + assert types[decl][0] is not None, (decl, types[decl]) + assert None not in types[decl][0], (decl, types[decl]) + else: + assert types[decl] is None + if dump: + _dump_unresolved(decl, types, analyze_decl) + print() + #raise NotImplementedError + + for decl in unresolved: + types[decl] = ([_SKIPPED], None) + + for decl in types: + assert types[decl] + + +def _dump_unresolved(decl, types, analyze_decl): + if isinstance(decl, str): + typespec = decl + decl, = (d for d in types if d.shortkey == typespec) + elif type(decl) is tuple: + filename, typespec = decl + if '-' in typespec: + found = [d for d in types + if d.shortkey == typespec and d.filename == filename] + #if not found: + # raise NotImplementedError(decl) + decl, = found + else: + found = [d for d in types if d.shortkey == typespec] + if not found: + print(f'*** {typespec} ???') + return + #raise NotImplementedError(decl) + else: + decl, = found + resolved = analyze_decl(decl) + if resolved: + typedeps, _ = resolved or (None, None) + + if decl.kind is KIND.STRUCT or decl.kind is KIND.UNION: + print(f'*** {decl.shortkey} {decl.filename}') + for member, mtype in zip(decl.members, typedeps): + typespec = member.vartype.typespec + if typespec == decl.shortkey: + print(f' ~~~~: {typespec:20} - {member!r}') + continue + status = None + if is_pots(typespec): + mtype = typespec + status = 'okay' + elif is_system_type(typespec): + mtype = typespec + status = 'okay' + elif mtype is None: + if '-' in member.vartype.typespec: + mtype, = [d for d in types + if d.shortkey == member.vartype.typespec + and d.filename == decl.filename] + else: + found = [d for d in types + if d.shortkey == typespec] + if not found: + print(f' ???: {typespec:20}') + continue + mtype, = found + if status is None: + status = 'okay' if types.get(mtype) else 'oops' + if mtype is _SKIPPED: + status = 'okay' + mtype = '' + elif isinstance(mtype, FuncPtr): + status = 'okay' + mtype = str(mtype.vartype) + elif not isinstance(mtype, str): + if hasattr(mtype, 'vartype'): + if is_funcptr(mtype.vartype): + status = 'okay' + mtype = str(mtype).rpartition('(')[0].rstrip() + status = ' okay' if status == 'okay' else f'--> {status}' + print(f' {status}: {typespec:20} - {member!r} ({mtype})') + else: + print(f'*** {decl} ({decl.vartype!r})') + if decl.vartype.typespec.startswith('struct ') or is_funcptr(decl): + _dump_unresolved( + (decl.filename, decl.vartype.typespec), + types, + analyze_decl, + ) diff --git a/Tools/c-analyzer/c_analyzer/common/files.py b/Tools/c-analyzer/c_analyzer/common/files.py deleted file mode 100644 index a8a044757d00b..0000000000000 --- a/Tools/c-analyzer/c_analyzer/common/files.py +++ /dev/null @@ -1,124 +0,0 @@ -import glob -import os -import os.path - -# XXX need tests: -# * walk_tree() -# * glob_tree() -# * iter_files_by_suffix() - - -C_SOURCE_SUFFIXES = ('.c', '.h') - - -def _walk_tree(root, *, - _walk=os.walk, - ): - # A wrapper around os.walk that resolves the filenames. - for parent, _, names in _walk(root): - for name in names: - yield os.path.join(parent, name) - - -def walk_tree(root, *, - suffix=None, - walk=_walk_tree, - ): - """Yield each file in the tree under the given directory name. - - If "suffix" is provided then only files with that suffix will - be included. - """ - if suffix and not isinstance(suffix, str): - raise ValueError('suffix must be a string') - - for filename in walk(root): - if suffix and not filename.endswith(suffix): - continue - yield filename - - -def glob_tree(root, *, - suffix=None, - _glob=glob.iglob, - _escape=glob.escape, - _join=os.path.join, - ): - """Yield each file in the tree under the given directory name. - - If "suffix" is provided then only files with that suffix will - be included. - """ - suffix = suffix or '' - if not isinstance(suffix, str): - raise ValueError('suffix must be a string') - - for filename in _glob(_join(_escape(root), f'*{suffix}')): - yield filename - for filename in _glob(_join(_escape(root), f'**/*{suffix}')): - yield filename - - -def iter_files(root, suffix=None, relparent=None, *, - get_files=None, - _glob=glob_tree, - _walk=walk_tree, - ): - """Yield each file in the tree under the given directory name. - - If "root" is a non-string iterable then do the same for each of - those trees. - - If "suffix" is provided then only files with that suffix will - be included. - - if "relparent" is provided then it is used to resolve each - filename as a relative path. - """ - if get_files is None: - get_files = os.walk - if not isinstance(root, str): - roots = root - for root in roots: - yield from iter_files(root, suffix, relparent, - get_files=get_files, - _glob=_glob, _walk=_walk) - return - - # Use the right "walk" function. - if get_files in (glob.glob, glob.iglob, glob_tree): - get_files = _glob - else: - _files = _walk_tree if get_files in (os.walk, walk_tree) else get_files - get_files = (lambda *a, **k: _walk(*a, walk=_files, **k)) - - # Handle a single suffix. - if suffix and not isinstance(suffix, str): - filenames = get_files(root) - suffix = tuple(suffix) - else: - filenames = get_files(root, suffix=suffix) - suffix = None - - for filename in filenames: - if suffix and not isinstance(suffix, str): # multiple suffixes - if not filename.endswith(suffix): - continue - if relparent: - filename = os.path.relpath(filename, relparent) - yield filename - - -def iter_files_by_suffix(root, suffixes, relparent=None, *, - walk=walk_tree, - _iter_files=iter_files, - ): - """Yield each file in the tree that has the given suffixes. - - Unlike iter_files(), the results are in the original suffix order. - """ - if isinstance(suffixes, str): - suffixes = [suffixes] - # XXX Ignore repeated suffixes? - for suffix in suffixes: - yield from _iter_files(root, suffix, relparent) diff --git a/Tools/c-analyzer/c_analyzer/common/info.py b/Tools/c-analyzer/c_analyzer/common/info.py deleted file mode 100644 index 1a853a42ff2a2..0000000000000 --- a/Tools/c-analyzer/c_analyzer/common/info.py +++ /dev/null @@ -1,138 +0,0 @@ -from collections import namedtuple -import re - -from .util import classonly, _NTBase - -# XXX need tests: -# * ID.match() - - -UNKNOWN = '???' - -# Does not start with digit and contains at least one letter. -NAME_RE = re.compile(r'(?!\d)(?=.*?[A-Za-z])\w+', re.ASCII) - - -class ID(_NTBase, namedtuple('ID', 'filename funcname name')): - """A unique ID for a single symbol or declaration.""" - - __slots__ = () - # XXX Add optional conditions (tuple of strings) field. - #conditions = Slot() - - @classonly - def from_raw(cls, raw): - if not raw: - return None - if isinstance(raw, str): - return cls(None, None, raw) - try: - name, = raw - filename = None - except ValueError: - try: - filename, name = raw - except ValueError: - return super().from_raw(raw) - return cls(filename, None, name) - - def __new__(cls, filename, funcname, name): - self = super().__new__( - cls, - filename=str(filename) if filename else None, - funcname=str(funcname) if funcname else None, - name=str(name) if name else None, - ) - #cls.conditions.set(self, tuple(str(s) if s else None - # for s in conditions or ())) - return self - - def validate(self): - """Fail if the object is invalid (i.e. init with bad data).""" - if not self.name: - raise TypeError('missing name') - if not NAME_RE.fullmatch(self.name): - raise ValueError( - f'name must be an identifier, got {self.name!r}') - - # Symbols from a binary might not have filename/funcname info. - - if self.funcname: - if not self.filename: - raise TypeError('missing filename') - if not NAME_RE.fullmatch(self.funcname) and self.funcname != UNKNOWN: - raise ValueError( - f'name must be an identifier, got {self.funcname!r}') - - # XXX Require the filename (at least UNKONWN)? - # XXX Check the filename? - - @property - def islocal(self): - return self.funcname is not None - - def match(self, other, *, - match_files=(lambda f1, f2: f1 == f2), - ): - """Return True if the two match. - - At least one of the two must be completely valid (no UNKNOWN - anywhere). Otherwise False is returned. The remaining one - *may* have UNKNOWN for both funcname and filename. It must - have a valid name though. - - The caller is responsible for knowing which of the two is valid - (and which to use if both are valid). - """ - # First check the name. - if self.name is None: - return False - if other.name != self.name: - return False - - # Then check the filename. - if self.filename is None: - return False - if other.filename is None: - return False - if self.filename == UNKNOWN: - # "other" must be the valid one. - if other.funcname == UNKNOWN: - return False - elif self.funcname != UNKNOWN: - # XXX Try matching funcname even though we don't - # know the filename? - raise NotImplementedError - else: - return True - elif other.filename == UNKNOWN: - # "self" must be the valid one. - if self.funcname == UNKNOWN: - return False - elif other.funcname != UNKNOWN: - # XXX Try matching funcname even though we don't - # know the filename? - raise NotImplementedError - else: - return True - elif not match_files(self.filename, other.filename): - return False - - # Finally, check the funcname. - if self.funcname == UNKNOWN: - # "other" must be the valid one. - if other.funcname == UNKNOWN: - return False - else: - return other.funcname is not None - elif other.funcname == UNKNOWN: - # "self" must be the valid one. - if self.funcname == UNKNOWN: - return False - else: - return self.funcname is not None - elif self.funcname == other.funcname: - # Both are valid. - return True - - return False diff --git a/Tools/c-analyzer/c_analyzer/common/show.py b/Tools/c-analyzer/c_analyzer/common/show.py deleted file mode 100644 index 5f3cb1c2fb0b5..0000000000000 --- a/Tools/c-analyzer/c_analyzer/common/show.py +++ /dev/null @@ -1,11 +0,0 @@ - -def basic(variables, *, - _print=print): - """Print each row simply.""" - for var in variables: - if var.funcname: - line = f'{var.filename}:{var.funcname}():{var.name}' - else: - line = f'{var.filename}:{var.name}' - line = f'{line:<64} {var.vartype}' - _print(line) diff --git a/Tools/c-analyzer/c_analyzer/datafiles.py b/Tools/c-analyzer/c_analyzer/datafiles.py new file mode 100644 index 0000000000000..0de438cce470f --- /dev/null +++ b/Tools/c-analyzer/c_analyzer/datafiles.py @@ -0,0 +1,109 @@ +import c_common.tables as _tables +import c_parser.info as _info +import c_parser.datafiles as _parser +from . import analyze as _analyze + + +############################# +# "known" decls + +EXTRA_COLUMNS = [ + #'typedecl', +] + + +def analyze_known(known, *, + analyze_resolved=None, + handle_unresolved=True, + ): + knowntypes = knowntypespecs = {} + collated = _info.collate_by_kind_group(known) + types = {decl: None for decl in collated['type']} + typespecs = _analyze.get_typespecs(types) + def analyze_decl(decl): + return _analyze.analyze_decl( + decl, + typespecs, + knowntypespecs, + types, + knowntypes, + analyze_resolved=analyze_resolved, + ) + _analyze.analyze_type_decls(types, analyze_decl, handle_unresolved) + return types, typespecs + + +def get_known(known, extracolumns=None, *, + analyze_resolved=None, + handle_unresolved=True, + relroot=None, + ): + if isinstance(known, str): + known = read_known(known, extracolumns, relroot) + return analyze_known( + known, + handle_unresolved=handle_unresolved, + analyze_resolved=analyze_resolved, + ) + + +def read_known(infile, extracolumns=None, relroot=None): + extracolumns = EXTRA_COLUMNS + ( + list(extracolumns) if extracolumns else [] + ) + known = {} + for decl, extra in _parser.iter_decls_tsv(infile, extracolumns, relroot): + known[decl] = extra + return known + + +def write_known(rows, outfile, extracolumns=None, *, + relroot=None, + backup=True, + ): + extracolumns = EXTRA_COLUMNS + ( + list(extracolumns) if extracolumns else [] + ) + _parser.write_decls_tsv( + rows, + outfile, + extracolumns, + relroot=relroot, + backup=backup, + ) + + +############################# +# ignored vars + +IGNORED_COLUMNS = [ + 'filename', + 'funcname', + 'name', + 'reason', +] +IGNORED_HEADER = '\t'.join(IGNORED_COLUMNS) + + +def read_ignored(infile): + return dict(_iter_ignored(infile)) + + +def _iter_ignored(infile): + for row in _tables.read_table(infile, IGNORED_HEADER, sep='\t'): + *varidinfo, reason = row + varid = _info.DeclID.from_row(varidinfo) + yield varid, reason + + +def write_ignored(variables, outfile): + raise NotImplementedError + reason = '???' + #if not isinstance(varid, DeclID): + # varid = getattr(varid, 'parsed', varid).id + _tables.write_table( + outfile, + IGNORED_HEADER, + sep='\t', + rows=(r.render_rowdata() + (reason,) for r in decls), + ) diff --git a/Tools/c-analyzer/c_analyzer/info.py b/Tools/c-analyzer/c_analyzer/info.py new file mode 100644 index 0000000000000..23d77611a4c3c --- /dev/null +++ b/Tools/c-analyzer/c_analyzer/info.py @@ -0,0 +1,353 @@ +from collections import namedtuple + +from c_common.clsutil import classonly +import c_common.misc as _misc +from c_parser.info import ( + KIND, + HighlevelParsedItem, + Declaration, + TypeDeclaration, + is_type_decl, + is_process_global, +) + + +IGNORED = _misc.Labeled('IGNORED') +UNKNOWN = _misc.Labeled('UNKNOWN') + + +# XXX Use known.tsv for these? +SYSTEM_TYPES = { + 'int8_t', + 'uint8_t', + 'int16_t', + 'uint16_t', + 'int32_t', + 'uint32_t', + 'int64_t', + 'uint64_t', + 'size_t', + 'ssize_t', + 'intptr_t', + 'uintptr_t', + 'wchar_t', + '', + # OS-specific + 'pthread_cond_t', + 'pthread_mutex_t', + 'pthread_key_t', + 'atomic_int', + 'atomic_uintptr_t', + '', + # lib-specific + 'WINDOW', # curses + 'XML_LChar', + 'XML_Size', + 'XML_Parser', + 'enum XML_Error', + 'enum XML_Status', + '', +} + + +def is_system_type(typespec): + return typespec in SYSTEM_TYPES + + +class SystemType(TypeDeclaration): + + def __init__(self, name): + super().__init__(None, name, None, None, _shortkey=name) + + +class Analyzed: + _locked = False + + @classonly + def is_target(cls, raw): + if isinstance(raw, HighlevelParsedItem): + return True + else: + return False + + @classonly + def from_raw(cls, raw, **extra): + if isinstance(raw, cls): + if extra: + # XXX ? + raise NotImplementedError((raw, extra)) + #return cls(raw.item, raw.typedecl, **raw._extra, **extra) + else: + return info + elif cls.is_target(raw): + return cls(raw, **extra) + else: + raise NotImplementedError((raw, extra)) + + @classonly + def from_resolved(cls, item, resolved, **extra): + if isinstance(resolved, TypeDeclaration): + return cls(item, typedecl=resolved, **extra) + else: + typedeps, extra = cls._parse_raw_resolved(item, resolved, extra) + if item.kind is KIND.ENUM: + if typedeps: + raise NotImplementedError((item, resolved, extra)) + elif not typedeps: + raise NotImplementedError((item, resolved, extra)) + return cls(item, typedeps, **extra or {}) + + @classonly + def _parse_raw_resolved(cls, item, resolved, extra_extra): + if resolved in (UNKNOWN, IGNORED): + return resolved, None + try: + typedeps, extra = resolved + except (TypeError, ValueError): + typedeps = extra = None + if extra: + # The resolved data takes precedence. + extra = dict(extra_extra, **extra) + if isinstance(typedeps, TypeDeclaration): + return typedeps, extra + elif typedeps in (None, UNKNOWN): + # It is still effectively unresolved. + return UNKNOWN, extra + elif None in typedeps or UNKNOWN in typedeps: + # It is still effectively unresolved. + return typedeps, extra + elif any(not isinstance(td, TypeDeclaration) for td in typedeps): + raise NotImplementedError((item, typedeps, extra)) + return typedeps, extra + + def __init__(self, item, typedecl=None, **extra): + assert item is not None + self.item = item + if typedecl in (UNKNOWN, IGNORED): + pass + elif item.kind is KIND.STRUCT or item.kind is KIND.UNION: + if isinstance(typedecl, TypeDeclaration): + raise NotImplementedError(item, typedecl) + elif typedecl is None: + typedecl = UNKNOWN + else: + typedecl = [UNKNOWN if d is None else d for d in typedecl] + elif typedecl is None: + typedecl = UNKNOWN + elif typedecl and not isinstance(typedecl, TypeDeclaration): + # All the other decls have a single type decl. + typedecl, = typedecl + if typedecl is None: + typedecl = UNKNOWN + self.typedecl = typedecl + self._extra = extra + self._locked = True + + self._validate() + + def _validate(self): + item = self.item + extra = self._extra + # Check item. + if not isinstance(item, HighlevelParsedItem): + raise ValueError(f'"item" must be a high-level parsed item, got {item!r}') + # Check extra. + for key, value in extra.items(): + if key.startswith('_'): + raise ValueError(f'extra items starting with {"_"!r} not allowed, got {extra!r}') + if hasattr(item, key) and not callable(getattr(item, key)): + raise ValueError(f'extra cannot override item, got {value!r} for key {key!r}') + + def __repr__(self): + kwargs = [ + f'item={self.item!r}', + f'typedecl={self.typedecl!r}', + *(f'{k}={v!r}' for k, v in self._extra.items()) + ] + return f'{type(self).__name__}({", ".join(kwargs)})' + + def __str__(self): + try: + return self._str + except AttributeError: + self._str, = self.render('line') + return self._str + + def __hash__(self): + return hash(self.item) + + def __eq__(self, other): + if isinstance(other, Analyzed): + return self.item == other.item + elif isinstance(other, HighlevelParsedItem): + return self.item == other + elif type(other) is tuple: + return self.item == other + else: + return NotImplemented + + def __gt__(self, other): + if isinstance(other, Analyzed): + return self.item > other.item + elif isinstance(other, HighlevelParsedItem): + return self.item > other + elif type(other) is tuple: + return self.item > other + else: + return NotImplemented + + def __dir__(self): + names = set(super().__dir__()) + names.update(self._extra) + names.remove('_locked') + return sorted(names) + + def __getattr__(self, name): + if name.startswith('_'): + raise AttributeError(name) + # The item takes precedence over the extra data (except if callable). + try: + value = getattr(self.item, name) + if callable(value): + raise AttributeError(name) + except AttributeError: + try: + value = self._extra[name] + except KeyError: + pass + else: + # Speed things up the next time. + self.__dict__[name] = value + return value + raise # re-raise + else: + return value + + def __setattr__(self, name, value): + if self._locked and name != '_str': + raise AttributeError(f'readonly ({name})') + super().__setattr__(name, value) + + def __delattr__(self, name): + if self._locked: + raise AttributeError(f'readonly ({name})') + super().__delattr__(name) + + @property + def decl(self): + if not isinstance(self.item, Declaration): + raise AttributeError('decl') + return self.item + + @property + def signature(self): + # XXX vartype... + ... + + @property + def istype(self): + return is_type_decl(self.item.kind) + + @property + def is_known(self): + if self.typedecl in (UNKNOWN, IGNORED): + return False + elif isinstance(self.typedecl, TypeDeclaration): + return True + else: + return UNKNOWN not in self.typedecl + + def fix_filename(self, relroot): + self.item.fix_filename(relroot) + + def as_rowdata(self, columns=None): + # XXX finsih! + return self.item.as_rowdata(columns) + + def render_rowdata(self, columns=None): + # XXX finsih! + return self.item.render_rowdata(columns) + + def render(self, fmt='line', *, itemonly=False): + if fmt == 'raw': + yield repr(self) + return + rendered = self.item.render(fmt) + if itemonly or not self._extra: + yield from rendered + return + extra = self._render_extra(fmt) + if not extra: + yield from rendered + elif fmt in ('brief', 'line'): + rendered, = rendered + extra, = extra + yield f'{rendered}\t{extra}' + elif fmt == 'summary': + raise NotImplementedError(fmt) + elif fmt == 'full': + yield from rendered + for line in extra: + yield f'\t{line}' + else: + raise NotImplementedError(fmt) + + def _render_extra(self, fmt): + if fmt in ('brief', 'line'): + yield str(self._extra) + else: + raise NotImplementedError(fmt) + + +class Analysis: + + _item_class = Analyzed + + @classonly + def build_item(cls, info, resolved=None, **extra): + if resolved is None: + return cls._item_class.from_raw(info, **extra) + else: + return cls._item_class.from_resolved(info, resolved, **extra) + + @classmethod + def from_results(cls, results): + self = cls() + for info, resolved in results: + self._add_result(info, resolved) + return self + + def __init__(self, items=None): + self._analyzed = {type(self).build_item(item): None + for item in items or ()} + + def __repr__(self): + return f'{type(self).__name__}({list(self._analyzed.keys())})' + + def __iter__(self): + #yield from self.types + #yield from self.functions + #yield from self.variables + yield from self._analyzed + + def __len__(self): + return len(self._analyzed) + + def __getitem__(self, key): + if type(key) is int: + for i, val in enumerate(self._analyzed): + if i == key: + return val + else: + raise IndexError(key) + else: + return self._analyzed[key] + + def fix_filenames(self, relroot): + for item in self._analyzed: + item.fix_filename(relroot) + + def _add_result(self, info, resolved): + analyzed = type(self).build_item(info, resolved) + self._analyzed[analyzed] = None + return analyzed diff --git a/Tools/c-analyzer/c_analyzer/parser/declarations.py b/Tools/c-analyzer/c_analyzer/parser/declarations.py deleted file mode 100644 index f37072cccad86..0000000000000 --- a/Tools/c-analyzer/c_analyzer/parser/declarations.py +++ /dev/null @@ -1,339 +0,0 @@ -import re -import shlex -import subprocess - -from ..common.info import UNKNOWN - -from . import source - - -IDENTIFIER = r'(?:[a-zA-z]|_+[a-zA-Z0-9]\w*)' - -TYPE_QUAL = r'(?:const|volatile)' - -VAR_TYPE_SPEC = r'''(?: - void | - (?: - (?:(?:un)?signed\s+)? - (?: - char | - short | - int | - long | - long\s+int | - long\s+long - ) | - ) | - float | - double | - {IDENTIFIER} | - (?:struct|union)\s+{IDENTIFIER} - )''' - -POINTER = rf'''(?: - (?:\s+const)?\s*[*] - )''' - -#STRUCT = r'''(?: -# (?:struct|(struct\s+%s))\s*[{] -# [^}]* -# [}] -# )''' % (IDENTIFIER) -#UNION = r'''(?: -# (?:union|(union\s+%s))\s*[{] -# [^}]* -# [}] -# )''' % (IDENTIFIER) -#DECL_SPEC = rf'''(?: -# ({VAR_TYPE_SPEC}) | -# ({STRUCT}) | -# ({UNION}) -# )''' - -FUNC_START = rf'''(?: - (?: - (?: - extern | - static | - static\s+inline - )\s+ - )? - #(?:const\s+)? - {VAR_TYPE_SPEC} - )''' -#GLOBAL_VAR_START = rf'''(?: -# (?: -# (?: -# extern | -# static -# )\s+ -# )? -# (?: -# {TYPE_QUAL} -# (?:\s+{TYPE_QUAL})? -# )?\s+ -# {VAR_TYPE_SPEC} -# )''' -GLOBAL_DECL_START_RE = re.compile(rf''' - ^ - (?: - ({FUNC_START}) - ) - ''', re.VERBOSE) - -LOCAL_VAR_START = rf'''(?: - (?: - (?: - register | - static - )\s+ - )? - (?: - (?: - {TYPE_QUAL} - (?:\s+{TYPE_QUAL})? - )\s+ - )? - {VAR_TYPE_SPEC} - {POINTER}? - )''' -LOCAL_STMT_START_RE = re.compile(rf''' - ^ - (?: - ({LOCAL_VAR_START}) - ) - ''', re.VERBOSE) - - -def iter_global_declarations(lines): - """Yield (decl, body) for each global declaration in the given lines. - - For function definitions the header is reduced to one line and - the body is provided as-is. For other compound declarations (e.g. - struct) the entire declaration is reduced to one line and "body" - is None. Likewise for simple declarations (e.g. variables). - - Declarations inside function bodies are ignored, though their text - is provided in the function body. - """ - # XXX Bail out upon bogus syntax. - lines = source.iter_clean_lines(lines) - for line in lines: - if not GLOBAL_DECL_START_RE.match(line): - continue - # We only need functions here, since we only need locals for now. - if line.endswith(';'): - continue - if line.endswith('{') and '(' not in line: - continue - - # Capture the function. - # (assume no func is a one-liner) - decl = line - while '{' not in line: # assume no inline structs, etc. - try: - line = next(lines) - except StopIteration: - return - decl += ' ' + line - - body, end = _extract_block(lines) - if end is None: - return - assert end == '}' - yield (f'{decl}\n{body}\n{end}', body) - - -def iter_local_statements(lines): - """Yield (lines, blocks) for each statement in the given lines. - - For simple statements, "blocks" is None and the statement is reduced - to a single line. For compound statements, "blocks" is a pair of - (header, body) for each block in the statement. The headers are - reduced to a single line each, but the bpdies are provided as-is. - """ - # XXX Bail out upon bogus syntax. - lines = source.iter_clean_lines(lines) - for line in lines: - if not LOCAL_STMT_START_RE.match(line): - continue - - stmt = line - blocks = None - if not line.endswith(';'): - # XXX Support compound & multiline simple statements. - #blocks = [] - continue - - yield (stmt, blocks) - - -def _extract_block(lines): - end = None - depth = 1 - body = [] - for line in lines: - depth += line.count('{') - line.count('}') - if depth == 0: - end = line - break - body.append(line) - return '\n'.join(body), end - - -def parse_func(stmt, body): - """Return (name, signature) for the given function definition.""" - header, _, end = stmt.partition(body) - assert end.strip() == '}' - assert header.strip().endswith('{') - header, _, _= header.rpartition('{') - - signature = ' '.join(header.strip().splitlines()) - - _, _, name = signature.split('(')[0].strip().rpartition(' ') - assert name - - return name, signature - - -#TYPE_SPEC = rf'''(?: -# )''' -#VAR_DECLARATOR = rf'''(?: -# )''' -#VAR_DECL = rf'''(?: -# {TYPE_SPEC}+ -# {VAR_DECLARATOR} -# \s* -# )''' -#VAR_DECLARATION = rf'''(?: -# {VAR_DECL} -# (?: = [^=] [^;]* )? -# ; -# )''' -# -# -#def parse_variable(decl, *, inFunc=False): -# """Return [(name, storage, vartype)] for the given variable declaration.""" -# ... - - -def _parse_var(stmt): - """Return (name, vartype) for the given variable declaration.""" - stmt = stmt.rstrip(';') - m = LOCAL_STMT_START_RE.match(stmt) - assert m - vartype = m.group(0) - name = stmt[len(vartype):].partition('=')[0].strip() - - if name.startswith('('): - name, _, after = name[1:].partition(')') - assert after - name = name.replace('*', '* ') - inside, _, name = name.strip().rpartition(' ') - vartype = f'{vartype} ({inside.strip()}){after}' - else: - name = name.replace('*', '* ') - before, _, name = name.rpartition(' ') - vartype = f'{vartype} {before}' - - vartype = vartype.strip() - while ' ' in vartype: - vartype = vartype.replace(' ', ' ') - - return name, vartype - - -def extract_storage(decl, *, infunc=None): - """Return (storage, vartype) based on the given declaration. - - The default storage is "implicit" (or "local" if infunc is True). - """ - if decl == UNKNOWN: - return decl - if decl.startswith('static '): - return 'static' - #return 'static', decl.partition(' ')[2].strip() - elif decl.startswith('extern '): - return 'extern' - #return 'extern', decl.partition(' ')[2].strip() - elif re.match('.*\b(static|extern)\b', decl): - raise NotImplementedError - elif infunc: - return 'local' - else: - return 'implicit' - - -def parse_compound(stmt, blocks): - """Return (headers, bodies) for the given compound statement.""" - # XXX Identify declarations inside compound statements - # (if/switch/for/while). - raise NotImplementedError - - -def iter_variables(filename, *, - preprocessed=False, - _iter_source_lines=source.iter_lines, - _iter_global=iter_global_declarations, - _iter_local=iter_local_statements, - _parse_func=parse_func, - _parse_var=_parse_var, - _parse_compound=parse_compound, - ): - """Yield (funcname, name, vartype) for every variable in the given file.""" - if preprocessed: - raise NotImplementedError - lines = _iter_source_lines(filename) - for stmt, body in _iter_global(lines): - # At the file top-level we only have to worry about vars & funcs. - if not body: - name, vartype = _parse_var(stmt) - if name: - yield (None, name, vartype) - else: - funcname, _ = _parse_func(stmt, body) - localvars = _iter_locals(body, - _iter_statements=_iter_local, - _parse_var=_parse_var, - _parse_compound=_parse_compound, - ) - for name, vartype in localvars: - yield (funcname, name, vartype) - - -def _iter_locals(lines, *, - _iter_statements=iter_local_statements, - _parse_var=_parse_var, - _parse_compound=parse_compound, - ): - compound = [lines] - while compound: - body = compound.pop(0) - bodylines = body.splitlines() - for stmt, blocks in _iter_statements(bodylines): - if not blocks: - name, vartype = _parse_var(stmt) - if name: - yield (name, vartype) - else: - headers, bodies = _parse_compound(stmt, blocks) - for header in headers: - for line in header: - name, vartype = _parse_var(line) - if name: - yield (name, vartype) - compound.extend(bodies) - - -def iter_all(filename, *, - preprocessed=False, - ): - """Yield a Declaration for each one found. - - If there are duplicates, due to preprocessor conditionals, then - they are checked to make sure they are the same. - """ - # XXX For the moment we cheat. - for funcname, name, decl in iter_variables(filename, - preprocessed=preprocessed): - yield 'variable', funcname, name, decl diff --git a/Tools/c-analyzer/c_analyzer/parser/find.py b/Tools/c-analyzer/c_analyzer/parser/find.py deleted file mode 100644 index 3860d3d459b18..0000000000000 --- a/Tools/c-analyzer/c_analyzer/parser/find.py +++ /dev/null @@ -1,107 +0,0 @@ -from ..common.info import UNKNOWN, ID - -from . import declarations - -# XXX need tests: -# * variables -# * variable -# * variable_from_id - - -def _iter_vars(filenames, preprocessed, *, - handle_id=None, - _iter_decls=declarations.iter_all, - ): - if handle_id is None: - handle_id = ID - - for filename in filenames or (): - for kind, funcname, name, decl in _iter_decls(filename, - preprocessed=preprocessed, - ): - if kind != 'variable': - continue - varid = handle_id(filename, funcname, name) - yield varid, decl - - -# XXX Add a "handle_var" arg like we did for get_resolver()? - -def variables(*filenames, - perfilecache=None, - preprocessed=False, - known=None, # for types - handle_id=None, - _iter_vars=_iter_vars, - ): - """Yield (varid, decl) for each variable found in the given files. - - If "preprocessed" is provided (and not False/None) then it is used - to decide which tool to use to parse the source code after it runs - through the C preprocessor. Otherwise the raw - """ - if len(filenames) == 1 and not (filenames[0], str): - filenames, = filenames - - if perfilecache is None: - yield from _iter_vars(filenames, preprocessed) - else: - # XXX Cache per-file variables (e.g. `{filename: [(varid, decl)]}`). - raise NotImplementedError - - -def variable(name, filenames, *, - local=False, - perfilecache=None, - preprocessed=False, - handle_id=None, - _iter_vars=variables, - ): - """Return (varid, decl) for the first found variable that matches. - - If "local" is True then the first matching local variable in the - file will always be returned. To avoid that, pass perfilecache and - pop each variable from the cache after using it. - """ - for varid, decl in _iter_vars(filenames, - perfilecache=perfilecache, - preprocessed=preprocessed, - ): - if varid.name != name: - continue - if local: - if varid.funcname: - if varid.funcname == UNKNOWN: - raise NotImplementedError - return varid, decl - elif not varid.funcname: - return varid, decl - else: - return None, None # No matching variable was found. - - -def variable_from_id(id, filenames, *, - perfilecache=None, - preprocessed=False, - handle_id=None, - _get_var=variable, - ): - """Return (varid, decl) for the first found variable that matches.""" - local = False - if isinstance(id, str): - name = id - else: - if id.funcname == UNKNOWN: - local = True - elif id.funcname: - raise NotImplementedError - - name = id.name - if id.filename and id.filename != UNKNOWN: - filenames = [id.filename] - return _get_var(name, filenames, - local=local, - perfilecache=perfilecache, - preprocessed=preprocessed, - handle_id=handle_id, - ) diff --git a/Tools/c-analyzer/c_analyzer/parser/naive.py b/Tools/c-analyzer/c_analyzer/parser/naive.py deleted file mode 100644 index 4a4822d84ff54..0000000000000 --- a/Tools/c-analyzer/c_analyzer/parser/naive.py +++ /dev/null @@ -1,179 +0,0 @@ -import re - -from ..common.info import UNKNOWN, ID - -from .preprocessor import _iter_clean_lines - - -_NOT_SET = object() - - -def get_srclines(filename, *, - cache=None, - _open=open, - _iter_lines=_iter_clean_lines, - ): - """Return the file's lines as a list. - - Each line will have trailing whitespace removed (including newline). - - If a cache is given the it is used. - """ - if cache is not None: - try: - return cache[filename] - except KeyError: - pass - - with _open(filename) as srcfile: - srclines = [line - for _, line in _iter_lines(srcfile) - if not line.startswith('#')] - for i, line in enumerate(srclines): - srclines[i] = line.rstrip() - - if cache is not None: - cache[filename] = srclines - return srclines - - -def parse_variable_declaration(srcline): - """Return (name, decl) for the given declaration line.""" - # XXX possible false negatives... - decl, sep, _ = srcline.partition('=') - if not sep: - if not srcline.endswith(';'): - return None, None - decl = decl.strip(';') - decl = decl.strip() - m = re.match(r'.*\b(\w+)\s*(?:\[[^\]]*\])?$', decl) - if not m: - return None, None - name = m.group(1) - return name, decl - - -def parse_variable(srcline, funcname=None): - """Return (varid, decl) for the variable declared on the line (or None).""" - line = srcline.strip() - - # XXX Handle more than just static variables. - if line.startswith('static '): - if '(' in line and '[' not in line: - # a function - return None, None - return parse_variable_declaration(line) - else: - return None, None - - -def iter_variables(filename, *, - srccache=None, - parse_variable=None, - _get_srclines=get_srclines, - _default_parse_variable=parse_variable, - ): - """Yield (varid, decl) for each variable in the given source file.""" - if parse_variable is None: - parse_variable = _default_parse_variable - - indent = '' - prev = '' - funcname = None - for line in _get_srclines(filename, cache=srccache): - # remember current funcname - if funcname: - if line == indent + '}': - funcname = None - continue - else: - if '(' in prev and line == indent + '{': - if not prev.startswith('__attribute__'): - funcname = prev.split('(')[0].split()[-1] - prev = '' - continue - indent = line[:-len(line.lstrip())] - prev = line - - info = parse_variable(line, funcname) - if isinstance(info, list): - for name, _funcname, decl in info: - yield ID(filename, _funcname, name), decl - continue - name, decl = info - - if name is None: - continue - yield ID(filename, funcname, name), decl - - -def _match_varid(variable, name, funcname, ignored=None): - if ignored and variable in ignored: - return False - - if variable.name != name: - return False - - if funcname == UNKNOWN: - if not variable.funcname: - return False - elif variable.funcname != funcname: - return False - - return True - - -def find_variable(filename, funcname, name, *, - ignored=None, - srccache=None, # {filename: lines} - parse_variable=None, - _iter_variables=iter_variables, - ): - """Return the matching variable. - - Return None if the variable is not found. - """ - for varid, decl in _iter_variables(filename, - srccache=srccache, - parse_variable=parse_variable, - ): - if _match_varid(varid, name, funcname, ignored): - return varid, decl - else: - return None - - -def find_variables(varids, filenames=None, *, - srccache=_NOT_SET, - parse_variable=None, - _find_symbol=find_variable, - ): - """Yield (varid, decl) for each ID. - - If the variable is not found then its decl will be UNKNOWN. That - way there will be one resulting variable per given ID. - """ - if srccache is _NOT_SET: - srccache = {} - - used = set() - for varid in varids: - if varid.filename and varid.filename != UNKNOWN: - srcfiles = [varid.filename] - else: - if not filenames: - yield varid, UNKNOWN - continue - srcfiles = filenames - for filename in srcfiles: - varid, decl = _find_varid(filename, varid.funcname, varid.name, - ignored=used, - srccache=srccache, - parse_variable=parse_variable, - ) - if varid: - yield varid, decl - used.add(varid) - break - else: - yield varid, UNKNOWN diff --git a/Tools/c-analyzer/c_analyzer/parser/preprocessor.py b/Tools/c-analyzer/c_analyzer/parser/preprocessor.py deleted file mode 100644 index 41f306e5f8022..0000000000000 --- a/Tools/c-analyzer/c_analyzer/parser/preprocessor.py +++ /dev/null @@ -1,511 +0,0 @@ -from collections import namedtuple -import shlex -import os -import re - -from ..common import util, info - - -CONTINUATION = '\\' + os.linesep - -IDENTIFIER = r'(?:\w*[a-zA-Z]\w*)' -IDENTIFIER_RE = re.compile('^' + IDENTIFIER + '$') - - -def _coerce_str(value): - if not value: - return '' - return str(value).strip() - - -############################# -# directives - -DIRECTIVE_START = r''' - (?: - ^ \s* - [#] \s* - )''' -DIRECTIVE_TEXT = r''' - (?: - (?: \s+ ( .*\S ) )? - \s* $ - )''' -DIRECTIVE = rf''' - (?: - {DIRECTIVE_START} - ( - include | - error | warning | - pragma | - define | undef | - if | ifdef | ifndef | elseif | else | endif | - __FILE__ | __LINE__ | __DATE __ | __TIME__ | __TIMESTAMP__ - ) - {DIRECTIVE_TEXT} - )''' -# (?: -# [^\\\n] | -# \\ [^\n] | -# \\ \n -# )+ -# ) \n -# )''' -DIRECTIVE_RE = re.compile(DIRECTIVE, re.VERBOSE) - -DEFINE = rf''' - (?: - {DIRECTIVE_START} define \s+ - (?: - ( \w*[a-zA-Z]\w* ) - (?: \s* [(] ([^)]*) [)] )? - ) - {DIRECTIVE_TEXT} - )''' -DEFINE_RE = re.compile(DEFINE, re.VERBOSE) - - -def parse_directive(line): - """Return the appropriate directive for the given line.""" - line = line.strip() - if line.startswith('#'): - line = line[1:].lstrip() - line = '#' + line - directive = line - #directive = '#' + line - while ' ' in directive: - directive = directive.replace(' ', ' ') - return _parse_directive(directive) - - -def _parse_directive(line): - m = DEFINE_RE.match(line) - if m: - name, args, text = m.groups() - if args: - args = [a.strip() for a in args.split(',')] - return Macro(name, args, text) - else: - return Constant(name, text) - - m = DIRECTIVE_RE.match(line) - if not m: - raise ValueError(f'unsupported directive {line!r}') - kind, text = m.groups() - if not text: - if kind not in ('else', 'endif'): - raise ValueError(f'missing text in directive {line!r}') - elif kind in ('else', 'endif', 'define'): - raise ValueError(f'unexpected text in directive {line!r}') - if kind == 'include': - directive = Include(text) - elif kind in IfDirective.KINDS: - directive = IfDirective(kind, text) - else: - directive = OtherDirective(kind, text) - directive.validate() - return directive - - -class PreprocessorDirective(util._NTBase): - """The base class for directives.""" - - __slots__ = () - - KINDS = frozenset([ - 'include', - 'pragma', - 'error', 'warning', - 'define', 'undef', - 'if', 'ifdef', 'ifndef', 'elseif', 'else', 'endif', - '__FILE__', '__DATE__', '__LINE__', '__TIME__', '__TIMESTAMP__', - ]) - - @property - def text(self): - return ' '.join(v for v in self[1:] if v and v.strip()) or None - - def validate(self): - """Fail if the object is invalid (i.e. init with bad data).""" - super().validate() - - if not self.kind: - raise TypeError('missing kind') - elif self.kind not in self.KINDS: - raise ValueError - - # text can be anything, including None. - - -class Constant(PreprocessorDirective, - namedtuple('Constant', 'kind name value')): - """A single "constant" directive ("define").""" - - __slots__ = () - - def __new__(cls, name, value=None): - self = super().__new__( - cls, - 'define', - name=_coerce_str(name) or None, - value=_coerce_str(value) or None, - ) - return self - - def validate(self): - """Fail if the object is invalid (i.e. init with bad data).""" - super().validate() - - if not self.name: - raise TypeError('missing name') - elif not IDENTIFIER_RE.match(self.name): - raise ValueError(f'name must be identifier, got {self.name!r}') - - # value can be anything, including None - - -class Macro(PreprocessorDirective, - namedtuple('Macro', 'kind name args body')): - """A single "macro" directive ("define").""" - - __slots__ = () - - def __new__(cls, name, args, body=None): - # "args" must be a string or an iterable of strings (or "empty"). - if isinstance(args, str): - args = [v.strip() for v in args.split(',')] - if args: - args = tuple(_coerce_str(a) or None for a in args) - self = super().__new__( - cls, - kind='define', - name=_coerce_str(name) or None, - args=args if args else (), - body=_coerce_str(body) or None, - ) - return self - - @property - def text(self): - if self.body: - return f'{self.name}({", ".join(self.args)}) {self.body}' - else: - return f'{self.name}({", ".join(self.args)})' - - def validate(self): - """Fail if the object is invalid (i.e. init with bad data).""" - super().validate() - - if not self.name: - raise TypeError('missing name') - elif not IDENTIFIER_RE.match(self.name): - raise ValueError(f'name must be identifier, got {self.name!r}') - - for arg in self.args: - if not arg: - raise ValueError(f'missing arg in {self.args}') - elif not IDENTIFIER_RE.match(arg): - raise ValueError(f'arg must be identifier, got {arg!r}') - - # body can be anything, including None - - -class IfDirective(PreprocessorDirective, - namedtuple('IfDirective', 'kind condition')): - """A single conditional directive (e.g. "if", "ifdef"). - - This only includes directives that actually provide conditions. The - related directives "else" and "endif" are covered by OtherDirective - instead. - """ - - __slots__ = () - - KINDS = frozenset([ - 'if', - 'ifdef', - 'ifndef', - 'elseif', - ]) - - @classmethod - def _condition_from_raw(cls, raw, kind): - #return Condition.from_raw(raw, _kind=kind) - condition = _coerce_str(raw) - if not condition: - return None - - if kind == 'ifdef': - condition = f'defined({condition})' - elif kind == 'ifndef': - condition = f'! defined({condition})' - - return condition - - def __new__(cls, kind, condition): - kind = _coerce_str(kind) - self = super().__new__( - cls, - kind=kind or None, - condition=cls._condition_from_raw(condition, kind), - ) - return self - - @property - def text(self): - if self.kind == 'ifdef': - return self.condition[8:-1] # strip "defined(" - elif self.kind == 'ifndef': - return self.condition[10:-1] # strip "! defined(" - else: - return self.condition - #return str(self.condition) - - def validate(self): - """Fail if the object is invalid (i.e. init with bad data).""" - super().validate() - - if not self.condition: - raise TypeError('missing condition') - #else: - # for cond in self.condition: - # if not cond: - # raise ValueError(f'missing condition in {self.condition}') - # cond.validate() - # if self.kind in ('ifdef', 'ifndef'): - # if len(self.condition) != 1: - # raise ValueError('too many condition') - # if self.kind == 'ifdef': - # if not self.condition[0].startswith('defined '): - # raise ValueError('bad condition') - # else: - # if not self.condition[0].startswith('! defined '): - # raise ValueError('bad condition') - - -class Include(PreprocessorDirective, - namedtuple('Include', 'kind file')): - """A single "include" directive. - - Supported "file" values are either follow the bracket style - () or double quotes ("spam.h"). - """ - - __slots__ = () - - def __new__(cls, file): - self = super().__new__( - cls, - kind='include', - file=_coerce_str(file) or None, - ) - return self - - def validate(self): - """Fail if the object is invalid (i.e. init with bad data).""" - super().validate() - - if not self.file: - raise TypeError('missing file') - - -class OtherDirective(PreprocessorDirective, - namedtuple('OtherDirective', 'kind text')): - """A single directive not covered by another class. - - This includes the "else", "endif", and "undef" directives, which are - otherwise inherently related to the directives covered by the - Constant, Macro, and IfCondition classes. - - Note that all directives must have a text value, except for "else" - and "endif" (which must have no text). - """ - - __slots__ = () - - KINDS = PreprocessorDirective.KINDS - {'include', 'define'} - IfDirective.KINDS - - def __new__(cls, kind, text): - self = super().__new__( - cls, - kind=_coerce_str(kind) or None, - text=_coerce_str(text) or None, - ) - return self - - def validate(self): - """Fail if the object is invalid (i.e. init with bad data).""" - super().validate() - - if self.text: - if self.kind in ('else', 'endif'): - raise ValueError('unexpected text in directive') - elif self.kind not in ('else', 'endif'): - raise TypeError('missing text') - - -############################# -# iterating lines - -def _recompute_conditions(directive, ifstack): - if directive.kind in ('if', 'ifdef', 'ifndef'): - ifstack.append( - ([], directive.condition)) - elif directive.kind == 'elseif': - if ifstack: - negated, active = ifstack.pop() - if active: - negated.append(active) - else: - negated = [] - ifstack.append( - (negated, directive.condition)) - elif directive.kind == 'else': - if ifstack: - negated, active = ifstack.pop() - if active: - negated.append(active) - ifstack.append( - (negated, None)) - elif directive.kind == 'endif': - if ifstack: - ifstack.pop() - - conditions = [] - for negated, active in ifstack: - for condition in negated: - conditions.append(f'! ({condition})') - if active: - conditions.append(active) - return tuple(conditions) - - -def _iter_clean_lines(lines): - lines = iter(enumerate(lines, 1)) - for lno, line in lines: - # Handle line continuations. - while line.endswith(CONTINUATION): - try: - lno, _line = next(lines) - except StopIteration: - break - line = line[:-len(CONTINUATION)] + ' ' + _line - - # Deal with comments. - after = line - line = '' - while True: - # Look for a comment. - before, begin, remainder = after.partition('/*') - if '//' in before: - before, _, _ = before.partition('//') - line += before + ' ' # per the C99 spec - break - line += before - if not begin: - break - line += ' ' # per the C99 spec - - # Go until we find the end of the comment. - _, end, after = remainder.partition('*/') - while not end: - try: - lno, remainder = next(lines) - except StopIteration: - raise Exception('unterminated comment') - _, end, after = remainder.partition('*/') - - yield lno, line - - -def iter_lines(lines, *, - _iter_clean_lines=_iter_clean_lines, - _parse_directive=_parse_directive, - _recompute_conditions=_recompute_conditions, - ): - """Yield (lno, line, directive, active conditions) for each given line. - - This is effectively a subset of the operations taking place in - translation phases 2-4 from the C99 spec (ISO/IEC 9899:TC2); see - section 5.1.1.2. Line continuations are removed and comments - replaced with a single space. (In both cases "lno" will be the last - line involved.) Otherwise each line is returned as-is. - - "lno" is the (1-indexed) line number for the line. - - "directive" will be a PreprocessorDirective or None, depending on - whether or not there is a directive on the line. - - "active conditions" is the set of preprocessor conditions (e.g. - "defined()") under which the current line of code will be included - in compilation. That set is derived from every conditional - directive block (e.g. "if defined()", "ifdef", "else") containing - that line. That includes nested directives. Note that the - current line does not affect the active conditions for iteself. - It only impacts subsequent lines. That applies to directives - that close blocks (e.g. "endif") just as much as conditional - directvies. Also note that "else" and "elseif" directives - update the active conditions (for later lines), rather than - adding to them. - """ - ifstack = [] - conditions = () - for lno, line in _iter_clean_lines(lines): - stripped = line.strip() - if not stripped.startswith('#'): - yield lno, line, None, conditions - continue - - directive = '#' + stripped[1:].lstrip() - while ' ' in directive: - directive = directive.replace(' ', ' ') - directive = _parse_directive(directive) - yield lno, line, directive, conditions - - if directive.kind in ('else', 'endif'): - conditions = _recompute_conditions(directive, ifstack) - elif isinstance(directive, IfDirective): - conditions = _recompute_conditions(directive, ifstack) - - -############################# -# running (platform-specific?) - -def _gcc(filename, *, - _get_argv=(lambda: _get_gcc_argv()), - _run=util.run_cmd, - ): - argv = _get_argv() - argv.extend([ - '-E', filename, - ]) - output = _run(argv) - return output - - -def _get_gcc_argv(*, - _open=open, - _run=util.run_cmd, - ): - with _open('/tmp/print.mk', 'w') as tmpfile: - tmpfile.write('print-%:\n') - #tmpfile.write('\t at echo $* = $($*)\n') - tmpfile.write('\t at echo $($*)\n') - argv = ['/usr/bin/make', - '-f', 'Makefile', - '-f', '/tmp/print.mk', - 'print-CC', - 'print-PY_CORE_CFLAGS', - ] - output = _run(argv) - gcc, cflags = output.strip().splitlines() - argv = shlex.split(gcc.strip()) - cflags = shlex.split(cflags.strip()) - return argv + cflags - - -def run(filename, *, - _gcc=_gcc, - ): - """Return the text of the given file after running the preprocessor.""" - return _gcc(filename) diff --git a/Tools/c-analyzer/c_analyzer/parser/source.py b/Tools/c-analyzer/c_analyzer/parser/source.py deleted file mode 100644 index f8998c8a338b2..0000000000000 --- a/Tools/c-analyzer/c_analyzer/parser/source.py +++ /dev/null @@ -1,34 +0,0 @@ -from . import preprocessor - - -def iter_clean_lines(lines): - incomment = False - for line in lines: - # Deal with comments. - if incomment: - _, sep, line = line.partition('*/') - if sep: - incomment = False - continue - line, _, _ = line.partition('//') - line, sep, remainder = line.partition('/*') - if sep: - _, sep, after = remainder.partition('*/') - if not sep: - incomment = True - continue - line += ' ' + after - - # Ignore blank lines and leading/trailing whitespace. - line = line.strip() - if not line: - continue - - yield line - - -def iter_lines(filename, *, - preprocess=preprocessor.run, - ): - content = preprocess(filename) - return iter(content.splitlines()) diff --git a/Tools/c-analyzer/c_analyzer/symbols/__init__.py b/Tools/c-analyzer/c_analyzer/symbols/__init__.py deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/Tools/c-analyzer/c_analyzer/symbols/_nm.py b/Tools/c-analyzer/c_analyzer/symbols/_nm.py deleted file mode 100644 index f3a75a6d4ba82..0000000000000 --- a/Tools/c-analyzer/c_analyzer/symbols/_nm.py +++ /dev/null @@ -1,117 +0,0 @@ -import os.path -import shutil - -from c_analyzer.common import util, info - -from .info import Symbol - - -# XXX need tests: -# * iter_symbols - -NM_KINDS = { - 'b': Symbol.KIND.VARIABLE, # uninitialized - 'd': Symbol.KIND.VARIABLE, # initialized - #'g': Symbol.KIND.VARIABLE, # uninitialized - #'s': Symbol.KIND.VARIABLE, # initialized - 't': Symbol.KIND.FUNCTION, - } - -SPECIAL_SYMBOLS = { - # binary format (e.g. ELF) - '__bss_start', - '__data_start', - '__dso_handle', - '_DYNAMIC', - '_edata', - '_end', - '__environ@@GLIBC_2.2.5', - '_GLOBAL_OFFSET_TABLE_', - '__JCR_END__', - '__JCR_LIST__', - '__TMC_END__', - } - - -def _is_special_symbol(name): - if name in SPECIAL_SYMBOLS: - return True - if '@@GLIBC' in name: - return True - return False - - -def iter_symbols(binfile, *, - nm=None, - handle_id=None, - _which=shutil.which, - _run=util.run_cmd, - ): - """Yield a Symbol for each relevant entry reported by the "nm" command.""" - if nm is None: - nm = _which('nm') - if not nm: - raise NotImplementedError - if handle_id is None: - handle_id = info.ID - - argv = [nm, - '--line-numbers', - binfile, - ] - try: - output = _run(argv) - except Exception: - if nm is None: - # XXX Use dumpbin.exe /SYMBOLS on Windows. - raise NotImplementedError - raise - for line in output.splitlines(): - (name, kind, external, filename, funcname, - ) = _parse_nm_line(line) - if kind != Symbol.KIND.VARIABLE: - continue - elif _is_special_symbol(name): - continue - yield Symbol( - id=handle_id(filename, funcname, name), - kind=kind, - external=external, - ) - - -def _parse_nm_line(line): - _origline = line - _, _, line = line.partition(' ') # strip off the address - line = line.strip() - - kind, _, line = line.partition(' ') - line = line.strip() - external = kind.isupper() - kind = NM_KINDS.get(kind.lower(), Symbol.KIND.OTHER) - - name, _, filename = line.partition('\t') - name = name.strip() - if filename: - filename = os.path.relpath(filename.partition(':')[0]) - else: - filename = info.UNKNOWN - - name, islocal = _parse_nm_name(name, kind) - funcname = info.UNKNOWN if islocal else None - return name, kind, external, filename, funcname - - -def _parse_nm_name(name, kind): - if kind != Symbol.KIND.VARIABLE: - return name, None - if _is_special_symbol(name): - return name, None - - actual, sep, digits = name.partition('.') - if not sep: - return name, False - - if not digits.isdigit(): - raise Exception(f'got bogus name {name}') - return actual, True diff --git a/Tools/c-analyzer/c_analyzer/symbols/find.py b/Tools/c-analyzer/c_analyzer/symbols/find.py deleted file mode 100644 index 85646523f7a60..0000000000000 --- a/Tools/c-analyzer/c_analyzer/symbols/find.py +++ /dev/null @@ -1,175 +0,0 @@ -import os -import os.path -import shutil - -from ..common import files -from ..common.info import UNKNOWN, ID -from ..parser import find as p_find - -from . import _nm -from .info import Symbol - -# XXX need tests: -# * get_resolver() -# * get_resolver_from_dirs() -# * symbol() -# * symbols() -# * variables() - - -def _resolve_known(symbol, knownvars): - for varid in knownvars: - if symbol.match(varid): - break - else: - return None - return knownvars.pop(varid) - - -def get_resolver(filenames=None, known=None, *, - handle_var, - check_filename=None, - perfilecache=None, - preprocessed=False, - _from_source=p_find.variable_from_id, - ): - """Return a "resolver" func for the given known vars/types and filenames. - - "handle_var" is a callable that takes (ID, decl) and returns a - Variable. Variable.from_id is a suitable callable. - - The returned func takes a single Symbol and returns a corresponding - Variable. If the symbol was located then the variable will be - valid, populated with the corresponding information. Otherwise None - is returned. - """ - knownvars = (known or {}).get('variables') - if knownvars: - knownvars = dict(knownvars) # a copy - if filenames: - if check_filename is None: - filenames = list(filenames) - def check_filename(filename): - return filename in filenames - def resolve(symbol): - # XXX Check "found" instead? - if not check_filename(symbol.filename): - return None - found = _resolve_known(symbol, knownvars) - if found is None: - #return None - varid, decl = _from_source(symbol, filenames, - perfilecache=perfilecache, - preprocessed=preprocessed, - ) - found = handle_var(varid, decl) - return found - else: - def resolve(symbol): - return _resolve_known(symbol, knownvars) - elif filenames: - def resolve(symbol): - varid, decl = _from_source(symbol, filenames, - perfilecache=perfilecache, - preprocessed=preprocessed, - ) - return handle_var(varid, decl) - else: - def resolve(symbol): - return None - return resolve - - -def get_resolver_from_dirs(dirnames, known=None, *, - handle_var, - suffixes=('.c',), - perfilecache=None, - preprocessed=False, - _iter_files=files.iter_files_by_suffix, - _get_resolver=get_resolver, - ): - """Return a "resolver" func for the given known vars/types and filenames. - - "dirnames" should be absolute paths. If not then they will be - resolved relative to CWD. - - See get_resolver(). - """ - dirnames = [d if d.endswith(os.path.sep) else d + os.path.sep - for d in dirnames] - filenames = _iter_files(dirnames, suffixes) - def check_filename(filename): - for dirname in dirnames: - if filename.startswith(dirname): - return True - else: - return False - return _get_resolver(filenames, known, - handle_var=handle_var, - check_filename=check_filename, - perfilecache=perfilecache, - preprocessed=preprocessed, - ) - - -def symbol(symbol, filenames, known=None, *, - perfilecache=None, - preprocessed=False, - handle_id=None, - _get_resolver=get_resolver, - ): - """Return a Variable for the one matching the given symbol. - - "symbol" can be one of several objects: - - * Symbol - use the contained info - * name (str) - look for a global variable with that name - * (filename, name) - look for named global in file - * (filename, funcname, name) - look for named local in file - - A name is always required. If the filename is None, "", or - "UNKNOWN" then all files will be searched. If the funcname is - "" or "UNKNOWN" then only local variables will be searched for. - """ - resolve = _get_resolver(known, filenames, - handle_id=handle_id, - perfilecache=perfilecache, - preprocessed=preprocessed, - ) - return resolve(symbol) - - -def _get_platform_tool(): - if os.name == 'nt': - # XXX Support this. - raise NotImplementedError - elif nm := shutil.which('nm'): - return lambda b, hi: _nm.iter_symbols(b, nm=nm, handle_id=hi) - else: - raise NotImplementedError - - -def symbols(binfile, *, - handle_id=None, - _file_exists=os.path.exists, - _get_platform_tool=_get_platform_tool, - ): - """Yield a Symbol for each one found in the binary.""" - if not _file_exists(binfile): - raise Exception('executable missing (need to build it first?)') - - _iter_symbols = _get_platform_tool() - yield from _iter_symbols(binfile, handle_id) - - -def variables(binfile, *, - resolve, - handle_id=None, - _iter_symbols=symbols, - ): - """Yield (Variable, Symbol) for each found symbol.""" - for symbol in _iter_symbols(binfile, handle_id=handle_id): - if symbol.kind != Symbol.KIND.VARIABLE: - continue - var = resolve(symbol) or None - yield var, symbol diff --git a/Tools/c-analyzer/c_analyzer/symbols/info.py b/Tools/c-analyzer/c_analyzer/symbols/info.py deleted file mode 100644 index 96a251abb7c7f..0000000000000 --- a/Tools/c-analyzer/c_analyzer/symbols/info.py +++ /dev/null @@ -1,51 +0,0 @@ -from collections import namedtuple - -from c_analyzer.common.info import ID -from c_analyzer.common.util import classonly, _NTBase - - -class Symbol(_NTBase, namedtuple('Symbol', 'id kind external')): - """Info for a single compilation symbol.""" - - __slots__ = () - - class KIND: - VARIABLE = 'variable' - FUNCTION = 'function' - OTHER = 'other' - - @classonly - def from_name(cls, name, filename=None, kind=KIND.VARIABLE, external=None): - """Return a new symbol based on the given name.""" - id = ID(filename, None, name) - return cls(id, kind, external) - - def __new__(cls, id, kind=KIND.VARIABLE, external=None): - self = super().__new__( - cls, - id=ID.from_raw(id), - kind=str(kind) if kind else None, - external=bool(external) if external is not None else None, - ) - return self - - def __hash__(self): - return hash(self.id) - - def __getattr__(self, name): - return getattr(self.id, name) - - def validate(self): - """Fail if the object is invalid (i.e. init with bad data).""" - if not self.id: - raise TypeError('missing id') - else: - self.id.validate() - - if not self.kind: - raise TypeError('missing kind') - elif self.kind not in vars(self.KIND).values(): - raise ValueError(f'unsupported kind {self.kind}') - - if self.external is None: - raise TypeError('missing external') diff --git a/Tools/c-analyzer/c_analyzer/variables/__init__.py b/Tools/c-analyzer/c_analyzer/variables/__init__.py deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/Tools/c-analyzer/c_analyzer/variables/find.py b/Tools/c-analyzer/c_analyzer/variables/find.py deleted file mode 100644 index 3fe7284fc00a7..0000000000000 --- a/Tools/c-analyzer/c_analyzer/variables/find.py +++ /dev/null @@ -1,75 +0,0 @@ -from ..common import files -from ..common.info import UNKNOWN -from ..parser import ( - find as p_find, - ) -from ..symbols import ( - info as s_info, - find as s_find, - ) -from .info import Variable - -# XXX need tests: -# * vars_from_source - - -def _remove_cached(cache, var): - if not cache: - return - try: - cached = cache[var.filename] - cached.remove(var) - except (KeyError, IndexError): - pass - - -def vars_from_binary(binfile, *, - known=None, - filenames=None, - handle_id=None, - check_filename=None, - handle_var=Variable.from_id, - _iter_vars=s_find.variables, - _get_symbol_resolver=s_find.get_resolver, - ): - """Yield a Variable for each found Symbol. - - Details are filled in from the given "known" variables and types. - """ - cache = {} - resolve = _get_symbol_resolver(filenames, known, - handle_var=handle_var, - check_filename=check_filename, - perfilecache=cache, - ) - for var, symbol in _iter_vars(binfile, - resolve=resolve, - handle_id=handle_id, - ): - if var is None: - var = Variable(symbol.id, UNKNOWN, UNKNOWN) - yield var - _remove_cached(cache, var) - - -def vars_from_source(filenames, *, - preprocessed=None, - known=None, - handle_id=None, - handle_var=Variable.from_id, - iter_vars=p_find.variables, - ): - """Yield a Variable for each declaration in the raw source code. - - Details are filled in from the given "known" variables and types. - """ - cache = {} - for varid, decl in iter_vars(filenames or (), - perfilecache=cache, - preprocessed=preprocessed, - known=known, - handle_id=handle_id, - ): - var = handle_var(varid, decl) - yield var - _remove_cached(cache, var) diff --git a/Tools/c-analyzer/c_analyzer/variables/info.py b/Tools/c-analyzer/c_analyzer/variables/info.py deleted file mode 100644 index 336a523c7a2db..0000000000000 --- a/Tools/c-analyzer/c_analyzer/variables/info.py +++ /dev/null @@ -1,93 +0,0 @@ -from collections import namedtuple - -from ..common.info import ID, UNKNOWN -from ..common.util import classonly, _NTBase - - -def normalize_vartype(vartype): - """Return the canonical form for a variable type (or func signature).""" - # We allow empty strring through for semantic reasons. - if vartype is None: - return None - - # XXX finish! - # XXX Return (modifiers, type, pointer)? - return str(vartype) - - -# XXX Variable.vartype -> decl (Declaration). - -class Variable(_NTBase, - namedtuple('Variable', 'id storage vartype')): - """Information about a single variable declaration.""" - - __slots__ = () - - STORAGE = ( - 'static', - 'extern', - 'implicit', - 'local', - ) - - @classonly - def from_parts(cls, filename, funcname, name, decl, storage=None): - varid = ID(filename, funcname, name) - if storage is None: - self = cls.from_id(varid, decl) - else: - self = cls(varid, storage, decl) - return self - - @classonly - def from_id(cls, varid, decl): - from ..parser.declarations import extract_storage - storage = extract_storage(decl, infunc=varid.funcname) - return cls(varid, storage, decl) - - def __new__(cls, id, storage, vartype): - self = super().__new__( - cls, - id=ID.from_raw(id), - storage=str(storage) if storage else None, - vartype=normalize_vartype(vartype) if vartype else None, - ) - return self - - def __hash__(self): - return hash(self.id) - - def __getattr__(self, name): - return getattr(self.id, name) - - def _validate_id(self): - if not self.id: - raise TypeError('missing id') - - if not self.filename or self.filename == UNKNOWN: - raise TypeError(f'id missing filename ({self.id})') - - if self.funcname and self.funcname == UNKNOWN: - raise TypeError(f'id missing funcname ({self.id})') - - self.id.validate() - - def validate(self): - """Fail if the object is invalid (i.e. init with bad data).""" - self._validate_id() - - if self.storage is None or self.storage == UNKNOWN: - raise TypeError('missing storage') - elif self.storage not in self.STORAGE: - raise ValueError(f'unsupported storage {self.storage:r}') - - if self.vartype is None or self.vartype == UNKNOWN: - raise TypeError('missing vartype') - - @property - def isglobal(self): - return self.storage != 'local' - - @property - def isconst(self): - return 'const' in self.vartype.split() diff --git a/Tools/c-analyzer/c_analyzer/variables/known.py b/Tools/c-analyzer/c_analyzer/variables/known.py deleted file mode 100644 index aa2934a069e16..0000000000000 --- a/Tools/c-analyzer/c_analyzer/variables/known.py +++ /dev/null @@ -1,91 +0,0 @@ -import csv - -from ..common.info import ID, UNKNOWN -from ..common.util import read_tsv -from .info import Variable - - -# XXX need tests: -# * read_file() -# * look_up_variable() - - -COLUMNS = ('filename', 'funcname', 'name', 'kind', 'declaration') -HEADER = '\t'.join(COLUMNS) - - -def read_file(infile, *, - _read_tsv=read_tsv, - ): - """Yield (kind, id, decl) for each row in the data file. - - The caller is responsible for validating each row. - """ - for row in _read_tsv(infile, HEADER): - filename, funcname, name, kind, declaration = row - if not funcname or funcname == '-': - funcname = None - id = ID(filename, funcname, name) - yield kind, id, declaration - - -def from_file(infile, *, - handle_var=Variable.from_id, - _read_file=read_file, - ): - """Return the info for known declarations in the given file.""" - known = { - 'variables': {}, - #'types': {}, - #'constants': {}, - #'macros': {}, - } - for kind, id, decl in _read_file(infile): - if kind == 'variable': - values = known['variables'] - value = handle_var(id, decl) - else: - raise ValueError(f'unsupported kind in row {row}') - value.validate() - values[id] = value - return known - - -def look_up_variable(varid, knownvars, *, - match_files=(lambda f1, f2: f1 == f2), - ): - """Return the known Variable matching the given ID. - - "knownvars" is a mapping of ID to Variable. - - "match_files" is used to verify if two filenames point to - the same file. - - If no match is found then None is returned. - """ - if not knownvars: - return None - - if varid.funcname == UNKNOWN: - if not varid.filename or varid.filename == UNKNOWN: - for varid in knownvars: - if not varid.funcname: - continue - if varid.name == varid.name: - return knownvars[varid] - else: - return None - else: - for varid in knownvars: - if not varid.funcname: - continue - if not match_files(varid.filename, varid.filename): - continue - if varid.name == varid.name: - return knownvars[varid] - else: - return None - elif not varid.filename or varid.filename == UNKNOWN: - raise NotImplementedError - else: - return knownvars.get(varid.id) diff --git a/Tools/c-analyzer/c_common/__init__.py b/Tools/c-analyzer/c_common/__init__.py new file mode 100644 index 0000000000000..a4c3bb24230f2 --- /dev/null +++ b/Tools/c-analyzer/c_common/__init__.py @@ -0,0 +1,2 @@ + +NOT_SET = object() diff --git a/Tools/c-analyzer/c_analyzer/common/util.py b/Tools/c-analyzer/c_common/clsutil.py similarity index 51% rename from Tools/c-analyzer/c_analyzer/common/util.py rename to Tools/c-analyzer/c_common/clsutil.py index 43d0bb6e66565..aa5f6b9831d4a 100644 --- a/Tools/c-analyzer/c_analyzer/common/util.py +++ b/Tools/c-analyzer/c_common/clsutil.py @@ -1,70 +1,7 @@ -import csv -import subprocess - _NOT_SET = object() -def run_cmd(argv, **kwargs): - proc = subprocess.run( - argv, - #capture_output=True, - #stderr=subprocess.STDOUT, - stdout=subprocess.PIPE, - text=True, - check=True, - **kwargs - ) - return proc.stdout - - -def read_tsv(infile, header, *, - _open=open, - _get_reader=csv.reader, - ): - """Yield each row of the given TSV (tab-separated) file.""" - if isinstance(infile, str): - with _open(infile, newline='') as infile: - yield from read_tsv(infile, header, - _open=_open, - _get_reader=_get_reader, - ) - return - lines = iter(infile) - - # Validate the header. - try: - actualheader = next(lines).strip() - except StopIteration: - actualheader = '' - if actualheader != header: - raise ValueError(f'bad header {actualheader!r}') - - for row in _get_reader(lines, delimiter='\t'): - yield tuple(v.strip() for v in row) - - -def write_tsv(outfile, header, rows, *, - _open=open, - _get_writer=csv.writer, - ): - """Write each of the rows to the given TSV (tab-separated) file.""" - if isinstance(outfile, str): - with _open(outfile, 'w', newline='') as outfile: - return write_tsv(outfile, header, rows, - _open=_open, - _get_writer=_get_writer, - ) - - if isinstance(header, str): - header = header.split('\t') - writer = _get_writer(outfile, delimiter='\t') - writer.writerow(header) - for row in rows: - writer.writerow('' if v is None else str(v) - for v in row) - - class Slot: """A descriptor that provides a slot. @@ -178,66 +115,3 @@ def __get__(self, obj, cls): raise AttributeError(self.name) # called on the class return self.getter(None, cls) - - -class _NTBase: - - __slots__ = () - - @classonly - def from_raw(cls, raw): - if not raw: - return None - elif isinstance(raw, cls): - return raw - elif isinstance(raw, str): - return cls.from_string(raw) - else: - if hasattr(raw, 'items'): - return cls(**raw) - try: - args = tuple(raw) - except TypeError: - pass - else: - return cls(*args) - raise NotImplementedError - - @classonly - def from_string(cls, value): - """Return a new instance based on the given string.""" - raise NotImplementedError - - @classmethod - def _make(cls, iterable): # The default _make() is not subclass-friendly. - return cls.__new__(cls, *iterable) - - # XXX Always validate? - #def __init__(self, *args, **kwargs): - # self.validate() - - # XXX The default __repr__() is not subclass-friendly (where the name changes). - #def __repr__(self): - # _, _, sig = super().__repr__().partition('(') - # return f'{self.__class__.__name__}({sig}' - - # To make sorting work with None: - def __lt__(self, other): - try: - return super().__lt__(other) - except TypeError: - if None in self: - return True - elif None in other: - return False - else: - raise - - def validate(self): - return - - # XXX Always validate? - #def _replace(self, **kwargs): - # obj = super()._replace(**kwargs) - # obj.validate() - # return obj diff --git a/Tools/c-analyzer/c_common/fsutil.py b/Tools/c-analyzer/c_common/fsutil.py new file mode 100644 index 0000000000000..56023f33523b0 --- /dev/null +++ b/Tools/c-analyzer/c_common/fsutil.py @@ -0,0 +1,388 @@ +import fnmatch +import glob +import os +import os.path +import shutil +import stat + +from .iterutil import iter_many + + +C_SOURCE_SUFFIXES = ('.c', '.h') + + +def create_backup(old, backup=None): + if isinstance(old, str): + filename = old + else: + filename = getattr(old, 'name', None) + if not filename: + return None + if not backup or backup is True: + backup = f'{filename}.bak' + try: + shutil.copyfile(filename, backup) + except FileNotFoundError as exc: + if exc.filename != filename: + raise # re-raise + backup = None + return backup + + +################################## +# find files + +def match_glob(filename, pattern): + if fnmatch.fnmatch(filename, pattern): + return True + + # fnmatch doesn't handle ** quite right. It will not match the + # following: + # + # ('x/spam.py', 'x/**/*.py') + # ('spam.py', '**/*.py') + # + # though it *will* match the following: + # + # ('x/y/spam.py', 'x/**/*.py') + # ('x/spam.py', '**/*.py') + + if '**/' not in pattern: + return False + + # We only accommodate the single-"**" case. + return fnmatch.fnmatch(filename, pattern.replace('**/', '', 1)) + + +def iter_filenames(filenames, *, + start=None, + include=None, + exclude=None, + ): + onempty = Exception('no filenames provided') + for filename, solo in iter_many(filenames, onempty): + check, start = _get_check(filename, start, include, exclude) + yield filename, check, solo +# filenames = iter(filenames or ()) +# try: +# first = next(filenames) +# except StopIteration: +# raise Exception('no filenames provided') +# try: +# second = next(filenames) +# except StopIteration: +# check, _ = _get_check(first, start, include, exclude) +# yield first, check, False +# return +# +# check, start = _get_check(first, start, include, exclude) +# yield first, check, True +# check, start = _get_check(second, start, include, exclude) +# yield second, check, True +# for filename in filenames: +# check, start = _get_check(filename, start, include, exclude) +# yield filename, check, True + + +def expand_filenames(filenames): + for filename in filenames: + # XXX Do we need to use glob.escape (a la commit 9355868458, GH-20994)? + if '**/' in filename: + yield from glob.glob(filename.replace('**/', '')) + yield from glob.glob(filename) + + +def _get_check(filename, start, include, exclude): + if start and filename != start: + return (lambda: ''), start + else: + def check(): + if _is_excluded(filename, exclude, include): + return '' + return None + return check, None + + +def _is_excluded(filename, exclude, include): + if include: + for included in include: + if match_glob(filename, included): + return False + return True + elif exclude: + for excluded in exclude: + if match_glob(filename, excluded): + return True + return False + else: + return False + + +def _walk_tree(root, *, + _walk=os.walk, + ): + # A wrapper around os.walk that resolves the filenames. + for parent, _, names in _walk(root): + for name in names: + yield os.path.join(parent, name) + + +def walk_tree(root, *, + suffix=None, + walk=_walk_tree, + ): + """Yield each file in the tree under the given directory name. + + If "suffix" is provided then only files with that suffix will + be included. + """ + if suffix and not isinstance(suffix, str): + raise ValueError('suffix must be a string') + + for filename in walk(root): + if suffix and not filename.endswith(suffix): + continue + yield filename + + +def glob_tree(root, *, + suffix=None, + _glob=glob.iglob, + ): + """Yield each file in the tree under the given directory name. + + If "suffix" is provided then only files with that suffix will + be included. + """ + suffix = suffix or '' + if not isinstance(suffix, str): + raise ValueError('suffix must be a string') + + for filename in _glob(f'{root}/*{suffix}'): + yield filename + for filename in _glob(f'{root}/**/*{suffix}'): + yield filename + + +def iter_files(root, suffix=None, relparent=None, *, + get_files=os.walk, + _glob=glob_tree, + _walk=walk_tree, + ): + """Yield each file in the tree under the given directory name. + + If "root" is a non-string iterable then do the same for each of + those trees. + + If "suffix" is provided then only files with that suffix will + be included. + + if "relparent" is provided then it is used to resolve each + filename as a relative path. + """ + if not isinstance(root, str): + roots = root + for root in roots: + yield from iter_files(root, suffix, relparent, + get_files=get_files, + _glob=_glob, _walk=_walk) + return + + # Use the right "walk" function. + if get_files in (glob.glob, glob.iglob, glob_tree): + get_files = _glob + else: + _files = _walk_tree if get_files in (os.walk, walk_tree) else get_files + get_files = (lambda *a, **k: _walk(*a, walk=_files, **k)) + + # Handle a single suffix. + if suffix and not isinstance(suffix, str): + filenames = get_files(root) + suffix = tuple(suffix) + else: + filenames = get_files(root, suffix=suffix) + suffix = None + + for filename in filenames: + if suffix and not isinstance(suffix, str): # multiple suffixes + if not filename.endswith(suffix): + continue + if relparent: + filename = os.path.relpath(filename, relparent) + yield filename + + +def iter_files_by_suffix(root, suffixes, relparent=None, *, + walk=walk_tree, + _iter_files=iter_files, + ): + """Yield each file in the tree that has the given suffixes. + + Unlike iter_files(), the results are in the original suffix order. + """ + if isinstance(suffixes, str): + suffixes = [suffixes] + # XXX Ignore repeated suffixes? + for suffix in suffixes: + yield from _iter_files(root, suffix, relparent) + + +################################## +# file info + +# XXX posix-only? + +S_IRANY = stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH +S_IWANY = stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH +S_IXANY = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH + + +def is_readable(file, *, user=None, check=False): + filename, st, mode = _get_file_info(file) + if check: + try: + okay = _check_file(filename, S_IRANY) + except NotImplementedError: + okay = NotImplemented + if okay is not NotImplemented: + return okay + # Fall back to checking the mode. + return _check_mode(st, mode, S_IRANY, user) + + +def is_writable(file, *, user=None, check=False): + filename, st, mode = _get_file_info(file) + if check: + try: + okay = _check_file(filename, S_IWANY) + except NotImplementedError: + okay = NotImplemented + if okay is not NotImplemented: + return okay + # Fall back to checking the mode. + return _check_mode(st, mode, S_IWANY, user) + + +def is_executable(file, *, user=None, check=False): + filename, st, mode = _get_file_info(file) + if check: + try: + okay = _check_file(filename, S_IXANY) + except NotImplementedError: + okay = NotImplemented + if okay is not NotImplemented: + return okay + # Fall back to checking the mode. + return _check_mode(st, mode, S_IXANY, user) + + +def _get_file_info(file): + filename = st = mode = None + if isinstance(file, int): + mode = file + elif isinstance(file, os.stat_result): + st = file + else: + if isinstance(file, str): + filename = file + elif hasattr(file, 'name') and os.path.exists(file.name): + filename = file.name + else: + raise NotImplementedError(file) + st = os.stat(filename) + return filename, st, mode or st.st_mode + + +def _check_file(filename, check): + if not isinstance(filename, str): + raise Exception(f'filename required to check file, got {filename}') + if check & S_IRANY: + flags = os.O_RDONLY + elif check & S_IWANY: + flags = os.O_WRONLY + elif check & S_IXANY: + # We can worry about S_IXANY later + return NotImplemented + else: + raise NotImplementedError(check) + + try: + fd = os.open(filename, flags) + except PermissionError: + return False + # We do not ignore other exceptions. + else: + os.close(fd) + return True + + +def _get_user_info(user): + import pwd + username = uid = gid = groups = None + if user is None: + uid = os.geteuid() + #username = os.getlogin() + username = pwd.getpwuid(uid)[0] + gid = os.getgid() + groups = os.getgroups() + else: + if isinstance(user, int): + uid = user + entry = pwd.getpwuid(uid) + username = entry.pw_name + elif isinstance(user, str): + username = user + entry = pwd.getpwnam(username) + uid = entry.pw_uid + else: + raise NotImplementedError(user) + gid = entry.pw_gid + os.getgrouplist(username, gid) + return username, uid, gid, groups + + +def _check_mode(st, mode, check, user): + orig = check + _, uid, gid, groups = _get_user_info(user) + if check & S_IRANY: + check -= S_IRANY + matched = False + if mode & stat.S_IRUSR: + if st.st_uid == uid: + matched = True + if mode & stat.S_IRGRP: + if st.st_uid == gid or st.st_uid in groups: + matched = True + if mode & stat.S_IROTH: + matched = True + if not matched: + return False + if check & S_IWANY: + check -= S_IWANY + matched = False + if mode & stat.S_IWUSR: + if st.st_uid == uid: + matched = True + if mode & stat.S_IWGRP: + if st.st_uid == gid or st.st_uid in groups: + matched = True + if mode & stat.S_IWOTH: + matched = True + if not matched: + return False + if check & S_IXANY: + check -= S_IXANY + matched = False + if mode & stat.S_IXUSR: + if st.st_uid == uid: + matched = True + if mode & stat.S_IXGRP: + if st.st_uid == gid or st.st_uid in groups: + matched = True + if mode & stat.S_IXOTH: + matched = True + if not matched: + return False + if check: + raise NotImplementedError((orig, check)) + return True diff --git a/Tools/c-analyzer/c_analyzer/common/__init__.py b/Tools/c-analyzer/c_common/info.py similarity index 100% rename from Tools/c-analyzer/c_analyzer/common/__init__.py rename to Tools/c-analyzer/c_common/info.py diff --git a/Tools/c-analyzer/c_common/iterutil.py b/Tools/c-analyzer/c_common/iterutil.py new file mode 100644 index 0000000000000..6ded105304e45 --- /dev/null +++ b/Tools/c-analyzer/c_common/iterutil.py @@ -0,0 +1,48 @@ + +_NOT_SET = object() + + +def peek_and_iter(items): + if not items: + return None, None + items = iter(items) + try: + peeked = next(items) + except StopIteration: + return None, None + def chain(): + yield peeked + yield from items + return chain(), peeked + + +def iter_many(items, onempty=None): + if not items: + if onempty is None: + return + if not callable(onempty): + raise onEmpty + items = onempty(items) + yield from iter_many(items, onempty=None) + return + items = iter(items) + try: + first = next(items) + except StopIteration: + if onempty is None: + return + if not callable(onempty): + raise onEmpty + items = onempty(items) + yield from iter_many(items, onempty=None) + else: + try: + second = next(items) + except StopIteration: + yield first, False + return + else: + yield first, True + yield second, True + for item in items: + yield item, True diff --git a/Tools/c-analyzer/c_common/logging.py b/Tools/c-analyzer/c_common/logging.py new file mode 100644 index 0000000000000..12398f7e385fd --- /dev/null +++ b/Tools/c-analyzer/c_common/logging.py @@ -0,0 +1,63 @@ +import logging +import sys + + +VERBOSITY = 3 + + +# The root logger for the whole top-level package: +_logger = logging.getLogger(__name__.rpartition('.')[0]) + + +def configure_logger(logger, verbosity=VERBOSITY, *, + logfile=None, + maxlevel=logging.CRITICAL, + ): + level = max(1, # 0 disables it, so we use the next lowest. + min(maxlevel, + maxlevel - verbosity * 10)) + logger.setLevel(level) + #logger.propagate = False + + if not logger.handlers: + if logfile: + handler = logging.FileHandler(logfile) + else: + handler = logging.StreamHandler(sys.stdout) + handler.setLevel(level) + #handler.setFormatter(logging.Formatter()) + logger.addHandler(handler) + + # In case the provided logger is in a sub-package... + if logger is not _logger: + configure_logger( + _logger, + verbosity, + logfile=logfile, + maxlevel=maxlevel, + ) + + +def hide_emit_errors(): + """Ignore errors while emitting log entries. + + Rather than printing a message desribing the error, we show nothing. + """ + # For now we simply ignore all exceptions. If we wanted to ignore + # specific ones (e.g. BrokenPipeError) then we would need to use + # a Handler subclass with a custom handleError() method. + orig = logging.raiseExceptions + logging.raiseExceptions = False + def restore(): + logging.raiseExceptions = orig + return restore + + +class Printer: + def __init__(self, verbosity=VERBOSITY): + self.verbosity = verbosity + + def info(self, *args, **kwargs): + if self.verbosity < 3: + return + print(*args, **kwargs) diff --git a/Tools/c-analyzer/c_common/misc.py b/Tools/c-analyzer/c_common/misc.py new file mode 100644 index 0000000000000..bfd503ab24adc --- /dev/null +++ b/Tools/c-analyzer/c_common/misc.py @@ -0,0 +1,7 @@ + +class Labeled: + __slots__ = ('_label',) + def __init__(self, label): + self._label = label + def __repr__(self): + return f'<{self._label}>' diff --git a/Tools/c-analyzer/c_common/scriptutil.py b/Tools/c-analyzer/c_common/scriptutil.py new file mode 100644 index 0000000000000..939a85003b296 --- /dev/null +++ b/Tools/c-analyzer/c_common/scriptutil.py @@ -0,0 +1,577 @@ +import argparse +import contextlib +import fnmatch +import logging +import os +import os.path +import shutil +import sys + +from . import fsutil, strutil, iterutil, logging as loggingutil + + +def get_prog(spec=None, *, absolute=False, allowsuffix=True): + if spec is None: + _, spec = _find_script() + # This is more natural for prog than __file__ would be. + filename = sys.argv[0] + elif isinstance(spec, str): + filename = os.path.normpath(spec) + spec = None + else: + filename = spec.origin + if _is_standalone(filename): + # Check if "installed". + if allowsuffix or not filename.endswith('.py'): + basename = os.path.basename(filename) + found = shutil.which(basename) + if found: + script = os.path.abspath(filename) + found = os.path.abspath(found) + if os.path.normcase(script) == os.path.normcase(found): + return basename + # It is only "standalone". + if absolute: + filename = os.path.abspath(filename) + return filename + elif spec is not None: + module = spec.name + if module.endswith('.__main__'): + module = module[:-9] + return f'{sys.executable} -m {module}' + else: + if absolute: + filename = os.path.abspath(filename) + return f'{sys.executable} {filename}' + + +def _find_script(): + frame = sys._getframe(2) + while frame.f_globals['__name__'] != '__main__': + frame = frame.f_back + + # This should match sys.argv[0]. + filename = frame.f_globals['__file__'] + # This will be None if -m wasn't used.. + spec = frame.f_globals['__spec__'] + return filename, spec + + +def is_installed(filename, *, allowsuffix=True): + if not allowsuffix and filename.endswith('.py'): + return False + filename = os.path.abspath(os.path.normalize(filename)) + found = shutil.which(os.path.basename(filename)) + if not found: + return False + if found != filename: + return False + return _is_standalone(filename) + + +def is_standalone(filename): + filename = os.path.abspath(os.path.normalize(filename)) + return _is_standalone(filename) + + +def _is_standalone(filename): + return fsutil.is_executable(filename) + + +################################## +# logging + +VERBOSITY = 3 + +TRACEBACK = os.environ.get('SHOW_TRACEBACK', '').strip() +TRACEBACK = bool(TRACEBACK and TRACEBACK.upper() not in ('0', 'FALSE', 'NO')) + + +logger = logging.getLogger(__name__) + + +def configure_logger(verbosity, logger=None, **kwargs): + if logger is None: + # Configure the root logger. + logger = logging.getLogger() + loggingutil.configure_logger(logger, verbosity, **kwargs) + + +################################## +# selections + +class UnsupportedSelectionError(Exception): + def __init__(self, values, possible): + self.values = tuple(values) + self.possible = tuple(possible) + super().__init__(f'unsupported selections {self.unique}') + + @property + def unique(self): + return tuple(sorted(set(self.values))) + + +def normalize_selection(selected: str, *, possible=None): + if selected in (None, True, False): + return selected + elif isinstance(selected, str): + selected = [selected] + elif not selected: + return () + + unsupported = [] + _selected = set() + for item in selected: + if not item: + continue + for value in item.strip().replace(',', ' ').split(): + if not value: + continue + # XXX Handle subtraction (leading "-"). + if possible and value not in possible and value != 'all': + unsupported.append(value) + _selected.add(value) + if unsupported: + raise UnsupportedSelectionError(unsupported, tuple(possible)) + if 'all' in _selected: + return True + return frozenset(selected) + + +################################## +# CLI parsing helpers + +class CLIArgSpec(tuple): + def __new__(cls, *args, **kwargs): + return super().__new__(cls, (args, kwargs)) + + def __repr__(self): + args, kwargs = self + args = [repr(arg) for arg in args] + for name, value in kwargs.items(): + args.append(f'{name}={value!r}') + return f'{type(self).__name__}({", ".join(args)})' + + def __call__(self, parser, *, _noop=(lambda a: None)): + self.apply(parser) + return _noop + + def apply(self, parser): + args, kwargs = self + parser.add_argument(*args, **kwargs) + + +def apply_cli_argspecs(parser, specs): + processors = [] + for spec in specs: + if callable(spec): + procs = spec(parser) + _add_procs(processors, procs) + else: + args, kwargs = spec + parser.add_argument(args, kwargs) + return processors + + +def _add_procs(flattened, procs): + # XXX Fail on non-empty, non-callable procs? + if not procs: + return + if callable(procs): + flattened.append(procs) + else: + #processors.extend(p for p in procs if callable(p)) + for proc in procs: + _add_procs(flattened, proc) + + +def add_verbosity_cli(parser): + parser.add_argument('-q', '--quiet', action='count', default=0) + parser.add_argument('-v', '--verbose', action='count', default=0) + + def process_args(args): + ns = vars(args) + key = 'verbosity' + if key in ns: + parser.error(f'duplicate arg {key!r}') + ns[key] = max(0, VERBOSITY + ns.pop('verbose') - ns.pop('quiet')) + return key + return process_args + + +def add_traceback_cli(parser): + parser.add_argument('--traceback', '--tb', action='store_true', + default=TRACEBACK) + parser.add_argument('--no-traceback', '--no-tb', dest='traceback', + action='store_const', const=False) + + def process_args(args): + ns = vars(args) + key = 'traceback_cm' + if key in ns: + parser.error(f'duplicate arg {key!r}') + showtb = ns.pop('traceback') + + @contextlib.contextmanager + def traceback_cm(): + restore = loggingutil.hide_emit_errors() + try: + yield + except BrokenPipeError: + # It was piped to "head" or something similar. + pass + except NotImplementedError: + raise # re-raise + except Exception as exc: + if not showtb: + sys.exit(f'ERROR: {exc}') + raise # re-raise + except KeyboardInterrupt: + if not showtb: + sys.exit('\nINTERRUPTED') + raise # re-raise + except BaseException as exc: + if not showtb: + sys.exit(f'{type(exc).__name__}: {exc}') + raise # re-raise + finally: + restore() + ns[key] = traceback_cm() + return key + return process_args + + +def add_sepval_cli(parser, opt, dest, choices, *, sep=',', **kwargs): +# if opt is True: +# parser.add_argument(f'--{dest}', action='append', **kwargs) +# elif isinstance(opt, str) and opt.startswith('-'): +# parser.add_argument(opt, dest=dest, action='append', **kwargs) +# else: +# arg = dest if not opt else opt +# kwargs.setdefault('nargs', '+') +# parser.add_argument(arg, dest=dest, action='append', **kwargs) + if not isinstance(opt, str): + parser.error(f'opt must be a string, got {opt!r}') + elif opt.startswith('-'): + parser.add_argument(opt, dest=dest, action='append', **kwargs) + else: + kwargs.setdefault('nargs', '+') + #kwargs.setdefault('metavar', opt.upper()) + parser.add_argument(opt, dest=dest, action='append', **kwargs) + + def process_args(args): + ns = vars(args) + + # XXX Use normalize_selection()? + if isinstance(ns[dest], str): + ns[dest] = [ns[dest]] + selections = [] + for many in ns[dest] or (): + for value in many.split(sep): + if value not in choices: + parser.error(f'unknown {dest} {value!r}') + selections.append(value) + ns[dest] = selections + return process_args + + +def add_files_cli(parser, *, excluded=None, nargs=None): + process_files = add_file_filtering_cli(parser, excluded=excluded) + parser.add_argument('filenames', nargs=nargs or '+', metavar='FILENAME') + return [ + process_files, + ] + + +def add_file_filtering_cli(parser, *, excluded=None): + parser.add_argument('--start') + parser.add_argument('--include', action='append') + parser.add_argument('--exclude', action='append') + + excluded = tuple(excluded or ()) + + def process_args(args): + ns = vars(args) + key = 'iter_filenames' + if key in ns: + parser.error(f'duplicate arg {key!r}') + + _include = tuple(ns.pop('include') or ()) + _exclude = excluded + tuple(ns.pop('exclude') or ()) + kwargs = dict( + start=ns.pop('start'), + include=tuple(_parse_files(_include)), + exclude=tuple(_parse_files(_exclude)), + # We use the default for "show_header" + ) + ns[key] = (lambda files: fsutil.iter_filenames(files, **kwargs)) + return process_args + + +def _parse_files(filenames): + for filename, _ in strutil.parse_entries(filenames): + yield filename.strip() + + +def add_failure_filtering_cli(parser, pool, *, default=False): + parser.add_argument('--fail', action='append', + metavar=f'"{{all|{"|".join(sorted(pool))}}},..."') + parser.add_argument('--no-fail', dest='fail', action='store_const', const=()) + + def process_args(args): + ns = vars(args) + + fail = ns.pop('fail') + try: + fail = normalize_selection(fail, possible=pool) + except UnsupportedSelectionError as exc: + parser.error(f'invalid --fail values: {", ".join(exc.unique)}') + else: + if fail is None: + fail = default + + if fail is True: + def ignore_exc(_exc): + return False + elif fail is False: + def ignore_exc(_exc): + return True + else: + def ignore_exc(exc): + for err in fail: + if type(exc) == pool[err]: + return False + else: + return True + args.ignore_exc = ignore_exc + return process_args + + +def add_kind_filtering_cli(parser, *, default=None): + parser.add_argument('--kinds', action='append') + + def process_args(args): + ns = vars(args) + + kinds = [] + for kind in ns.pop('kinds') or default or (): + kinds.extend(kind.strip().replace(',', ' ').split()) + + if not kinds: + match_kind = (lambda k: True) + else: + included = set() + excluded = set() + for kind in kinds: + if kind.startswith('-'): + kind = kind[1:] + excluded.add(kind) + if kind in included: + included.remove(kind) + else: + included.add(kind) + if kind in excluded: + excluded.remove(kind) + if excluded: + if included: + ... # XXX fail? + def match_kind(kind, *, _excluded=excluded): + return kind not in _excluded + else: + def match_kind(kind, *, _included=included): + return kind in _included + args.match_kind = match_kind + return process_args + + +COMMON_CLI = [ + add_verbosity_cli, + add_traceback_cli, + #add_dryrun_cli, +] + + +def add_commands_cli(parser, commands, *, commonspecs=COMMON_CLI, subset=None): + arg_processors = {} + if isinstance(subset, str): + cmdname = subset + try: + _, argspecs, _ = commands[cmdname] + except KeyError: + raise ValueError(f'unsupported subset {subset!r}') + parser.set_defaults(cmd=cmdname) + arg_processors[cmdname] = _add_cmd_cli(parser, commonspecs, argspecs) + else: + if subset is None: + cmdnames = subset = list(commands) + elif not subset: + raise NotImplementedError + elif isinstance(subset, set): + cmdnames = [k for k in commands if k in subset] + subset = sorted(subset) + else: + cmdnames = [n for n in subset if n in commands] + if len(cmdnames) < len(subset): + bad = tuple(n for n in subset if n not in commands) + raise ValueError(f'unsupported subset {bad}') + + common = argparse.ArgumentParser(add_help=False) + common_processors = apply_cli_argspecs(common, commonspecs) + subs = parser.add_subparsers(dest='cmd') + for cmdname in cmdnames: + description, argspecs, _ = commands[cmdname] + sub = subs.add_parser( + cmdname, + description=description, + parents=[common], + ) + cmd_processors = _add_cmd_cli(sub, (), argspecs) + arg_processors[cmdname] = common_processors + cmd_processors + return arg_processors + + +def _add_cmd_cli(parser, commonspecs, argspecs): + processors = [] + argspecs = list(commonspecs or ()) + list(argspecs or ()) + for argspec in argspecs: + if callable(argspec): + procs = argspec(parser) + _add_procs(processors, procs) + else: + if not argspec: + raise NotImplementedError + args = list(argspec) + if not isinstance(args[-1], str): + kwargs = args.pop() + if not isinstance(args[0], str): + try: + args, = args + except (TypeError, ValueError): + parser.error(f'invalid cmd args {argspec!r}') + else: + kwargs = {} + parser.add_argument(*args, **kwargs) + # There will be nothing to process. + return processors + + +def _flatten_processors(processors): + for proc in processors: + if proc is None: + continue + if callable(proc): + yield proc + else: + yield from _flatten_processors(proc) + + +def process_args(args, processors, *, keys=None): + processors = _flatten_processors(processors) + ns = vars(args) + extracted = {} + if keys is None: + for process_args in processors: + for key in process_args(args): + extracted[key] = ns.pop(key) + else: + remainder = set(keys) + for process_args in processors: + hanging = process_args(args) + if isinstance(hanging, str): + hanging = [hanging] + for key in hanging or (): + if key not in remainder: + raise NotImplementedError(key) + extracted[key] = ns.pop(key) + remainder.remove(key) + if remainder: + raise NotImplementedError(sorted(remainder)) + return extracted + + +def process_args_by_key(args, processors, keys): + extracted = process_args(args, processors, keys=keys) + return [extracted[key] for key in keys] + + +################################## +# commands + +def set_command(name, add_cli): + """A decorator factory to set CLI info.""" + def decorator(func): + if hasattr(func, '__cli__'): + raise Exception(f'already set') + func.__cli__ = (name, add_cli) + return func + return decorator + + +################################## +# main() helpers + +def filter_filenames(filenames, iter_filenames=None): + for filename, check, _ in _iter_filenames(filenames, iter_filenames): + if (reason := check()): + logger.debug(f'{filename}: {reason}') + continue + yield filename + + +def main_for_filenames(filenames, iter_filenames=None): + for filename, check, show in _iter_filenames(filenames, iter_filenames): + if show: + print() + print('-------------------------------------------') + print(filename) + if (reason := check()): + print(reason) + continue + yield filename + + +def _iter_filenames(filenames, iter_files): + if iter_files is None: + iter_files = fsutil.iter_filenames + yield from iter_files(filenames) + return + + onempty = Exception('no filenames provided') + items = iter_files(filenames) + items, peeked = iterutil.peek_and_iter(items) + if not items: + raise onempty + if isinstance(peeked, str): + check = (lambda: True) + for filename, ismany in iterutil.iter_many(items, onempty): + yield filename, check, ismany + elif len(peeked) == 3: + yield from items + else: + raise NotImplementedError + + +def iter_marks(mark='.', *, group=5, groups=2, lines=10, sep=' '): + mark = mark or '' + sep = f'{mark}{sep}' if sep else mark + end = f'{mark}{os.linesep}' + div = os.linesep + perline = group * groups + perlines = perline * lines + + if perline == 1: + yield end + elif group == 1: + yield sep + + count = 1 + while True: + if count % perline == 0: + yield end + if count % perlines == 0: + yield div + elif count % group == 0: + yield sep + else: + yield mark + count += 1 diff --git a/Tools/c-analyzer/c_analyzer/parser/__init__.py b/Tools/c-analyzer/c_common/show.py similarity index 100% rename from Tools/c-analyzer/c_analyzer/parser/__init__.py rename to Tools/c-analyzer/c_common/show.py diff --git a/Tools/c-analyzer/c_common/strutil.py b/Tools/c-analyzer/c_common/strutil.py new file mode 100644 index 0000000000000..e7535d45bbba2 --- /dev/null +++ b/Tools/c-analyzer/c_common/strutil.py @@ -0,0 +1,42 @@ +import logging + + +logger = logging.getLogger(__name__) + + +def unrepr(value): + raise NotImplementedError + + +def parse_entries(entries, *, ignoresep=None): + for entry in entries: + if ignoresep and ignoresep in entry: + subentries = [entry] + else: + subentries = entry.strip().replace(',', ' ').split() + for item in subentries: + if item.startswith('+'): + filename = item[1:] + try: + infile = open(filename) + except FileNotFoundError: + logger.debug(f'ignored in parse_entries(): +{filename}') + return + with infile: + # We read the entire file here to ensure the file + # gets closed sooner rather than later. Note that + # the file would stay open if this iterator is never + # exchausted. + lines = infile.read().splitlines() + for line in _iter_significant_lines(lines): + yield line, filename + else: + yield item, None + + +def _iter_significant_lines(lines): + for line in lines: + line = line.partition('#')[0] + if not line.strip(): + continue + yield line diff --git a/Tools/c-analyzer/c_common/tables.py b/Tools/c-analyzer/c_common/tables.py new file mode 100644 index 0000000000000..70a230a90b6e8 --- /dev/null +++ b/Tools/c-analyzer/c_common/tables.py @@ -0,0 +1,213 @@ +import csv + +from . import NOT_SET, strutil, fsutil + + +EMPTY = '-' +UNKNOWN = '???' + + +def parse_markers(markers, default=None): + if markers is NOT_SET: + return default + if not markers: + return None + if type(markers) is not str: + return markers + if markers == markers[0] * len(markers): + return [markers] + return list(markers) + + +def fix_row(row, **markers): + if isinstance(row, str): + raise NotImplementedError(row) + empty = parse_markers(markers.pop('empty', ('-',))) + unknown = parse_markers(markers.pop('unknown', ('???',))) + row = (val if val else None for val in row) + if not empty: + if not unknown: + return row + return (UNKNOWN if val in unknown else val for val in row) + elif not unknown: + return (EMPTY if val in empty else val for val in row) + return (EMPTY if val in empty else (UNKNOWN if val in unknown else val) + for val in row) + + +def _fix_read_default(row): + for value in row: + yield value.strip() + + +def _fix_write_default(row, empty=''): + for value in row: + yield empty if value is None else str(value) + + +def _normalize_fix_read(fix): + if fix is None: + fix = '' + if callable(fix): + def fix_row(row): + values = fix(row) + return _fix_read_default(values) + elif isinstance(fix, str): + def fix_row(row): + values = _fix_read_default(row) + return (None if v == fix else v + for v in values) + else: + raise NotImplementedError(fix) + return fix_row + + +def _normalize_fix_write(fix, empty=''): + if fix is None: + fix = empty + if callable(fix): + def fix_row(row): + values = fix(row) + return _fix_write_default(values, empty) + elif isinstance(fix, str): + def fix_row(row): + return _fix_write_default(row, fix) + else: + raise NotImplementedError(fix) + return fix_row + + +def read_table(infile, header, *, + sep='\t', + fix=None, + _open=open, + _get_reader=csv.reader, + ): + """Yield each row of the given ???-separated (e.g. tab) file.""" + if isinstance(infile, str): + with _open(infile, newline='') as infile: + yield from read_table( + infile, + header, + sep=sep, + fix=fix, + _open=_open, + _get_reader=_get_reader, + ) + return + lines = strutil._iter_significant_lines(infile) + + # Validate the header. + if not isinstance(header, str): + header = sep.join(header) + try: + actualheader = next(lines).strip() + except StopIteration: + actualheader = '' + if actualheader != header: + raise ValueError(f'bad header {actualheader!r}') + + fix_row = _normalize_fix_read(fix) + for row in _get_reader(lines, delimiter=sep or '\t'): + yield tuple(fix_row(row)) + + +def write_table(outfile, header, rows, *, + sep='\t', + fix=None, + backup=True, + _open=open, + _get_writer=csv.writer, + ): + """Write each of the rows to the given ???-separated (e.g. tab) file.""" + if backup: + fsutil.create_backup(outfile, backup) + if isinstance(outfile, str): + with _open(outfile, 'w', newline='') as outfile: + return write_table( + outfile, + header, + rows, + sep=sep, + fix=fix, + backup=backup, + _open=_open, + _get_writer=_get_writer, + ) + + if isinstance(header, str): + header = header.split(sep or '\t') + fix_row = _normalize_fix_write(fix) + writer = _get_writer(outfile, delimiter=sep or '\t') + writer.writerow(header) + for row in rows: + writer.writerow( + tuple(fix_row(row)) + ) + + +def parse_table(entries, sep, header=None, rawsep=None, *, + default=NOT_SET, + strict=True, + ): + header, sep = _normalize_table_file_props(header, sep) + if not sep: + raise ValueError('missing "sep"') + + ncols = None + if header: + if strict: + ncols = len(header.split(sep)) + cur_file = None + for line, filename in strutil.parse_entries(entries, ignoresep=sep): + _sep = sep + if filename: + if header and cur_file != filename: + cur_file = filename + # Skip the first line if it's the header. + if line.strip() == header: + continue + else: + # We expected the header. + raise NotImplementedError((header, line)) + elif rawsep and sep not in line: + _sep = rawsep + + row = _parse_row(line, _sep, ncols, default) + if strict and not ncols: + ncols = len(row) + yield row, filename + + +def parse_row(line, sep, *, ncols=None, default=NOT_SET): + if not sep: + raise ValueError('missing "sep"') + return _parse_row(line, sep, ncols, default) + + +def _parse_row(line, sep, ncols, default): + row = tuple(v.strip() for v in line.split(sep)) + if (ncols or 0) > 0: + diff = ncols - len(row) + if diff: + if default is NOT_SET or diff < 0: + raise Exception(f'bad row (expected {ncols} columns, got {row!r})') + row += (default,) * diff + return row + + +def _normalize_table_file_props(header, sep): + if not header: + return None, sep + + if not isinstance(header, str): + if not sep: + raise NotImplementedError(header) + header = sep.join(header) + elif not sep: + for sep in ('\t', ',', ' '): + if sep in header: + break + else: + sep = None + return header, sep diff --git a/Tools/c-analyzer/c_parser/__init__.py b/Tools/c-analyzer/c_parser/__init__.py new file mode 100644 index 0000000000000..39455ddbf1a0c --- /dev/null +++ b/Tools/c-analyzer/c_parser/__init__.py @@ -0,0 +1,46 @@ +from .parser import parse as _parse +from .preprocessor import get_preprocessor as _get_preprocessor + + +def parse_file(filename, *, + match_kind=None, + get_file_preprocessor=None, + ): + if get_file_preprocessor is None: + get_file_preprocessor = _get_preprocessor() + yield from _parse_file(filename, match_kind, get_file_preprocessor) + + +def parse_files(filenames, *, + match_kind=None, + get_file_preprocessor=None, + ): + if get_file_preprocessor is None: + get_file_preprocessor = _get_preprocessor() + for filename in filenames: + yield from _parse_file(filename, match_kind, get_file_preprocessor) + + +def _parse_file(filename, match_kind, get_file_preprocessor): + # Preprocess the file. + preprocess = get_file_preprocessor(filename) + preprocessed = preprocess() + if preprocessed is None: + return + + # Parse the lines. + srclines = ((l.file, l.data) for l in preprocessed if l.kind == 'source') + for item in _parse(srclines): + if match_kind is not None and not match_kind(item.kind): + continue + if not item.filename: + raise NotImplementedError(repr(item)) + yield item + + +def parse_signature(text): + raise NotImplementedError + + +# aliases +from .info import resolve_parsed diff --git a/Tools/c-analyzer/c_parser/__main__.py b/Tools/c-analyzer/c_parser/__main__.py new file mode 100644 index 0000000000000..1752a703f606a --- /dev/null +++ b/Tools/c-analyzer/c_parser/__main__.py @@ -0,0 +1,261 @@ +import logging +import os.path +import sys + +from c_common.scriptutil import ( + CLIArgSpec as Arg, + add_verbosity_cli, + add_traceback_cli, + add_kind_filtering_cli, + add_files_cli, + add_commands_cli, + process_args_by_key, + configure_logger, + get_prog, + main_for_filenames, +) +from .preprocessor import get_preprocessor +from .preprocessor.__main__ import ( + add_common_cli as add_preprocessor_cli, +) +from .info import KIND +from . import parse_file as _iter_parsed + + +logger = logging.getLogger(__name__) + + +def _format_vartype(vartype): + if isinstance(vartype, str): + return vartype + + data = vartype + try: + vartype = data['vartype'] + except KeyError: + storage, typequal, typespec, abstract = vartype.values() + else: + storage = data.get('storage') + if storage: + _, typequal, typespec, abstract = vartype.values() + else: + storage, typequal, typespec, abstract = vartype.values() + + vartype = f'{typespec} {abstract}' + if typequal: + vartype = f'{typequal} {vartype}' + if storage: + vartype = f'{storage} {vartype}' + return vartype + + +def _get_preprocessor(filename, **kwargs): + return get_processor(filename, + log_err=print, + **kwargs + ) + + +####################################### +# the formats + +def fmt_raw(filename, item, *, showfwd=None): + yield str(tuple(item)) + + +def fmt_summary(filename, item, *, showfwd=None): + if item.filename and item.filename != os.path.join('.', filename): + yield f'> {item.filename}' + if showfwd is None: + LINE = ' {lno:>5} {kind:10} {funcname:40} {fwd:1} {name:40} {data}' + else: + LINE = ' {lno:>5} {kind:10} {funcname:40} {name:40} {data}' + lno = kind = funcname = fwd = name = data = '' + MIN_LINE = len(LINE.format(**locals())) + + fileinfo, kind, funcname, name, data = item + lno = fileinfo.lno if fileinfo and fileinfo.lno >= 0 else '' + funcname = funcname or ' --' + name = name or ' --' + isforward = False + if kind is KIND.FUNCTION: + storage, inline, params, returntype, isforward = data.values() + returntype = _format_vartype(returntype) + data = returntype + params + if inline: + data = f'inline {data}' + if storage: + data = f'{storage} {data}' + elif kind is KIND.VARIABLE: + data = _format_vartype(data) + elif kind is KIND.STRUCT or kind is KIND.UNION: + if data is None: + isforward = True + else: + fields = data + data = f'({len(data)}) {{ ' + indent = ',\n' + ' ' * (MIN_LINE + len(data)) + data += ', '.join(f.name for f in fields[:5]) + fields = fields[5:] + while fields: + data = f'{data}{indent}{", ".join(f.name for f in fields[:5])}' + fields = fields[5:] + data += ' }' + elif kind is KIND.ENUM: + if data is None: + isforward = True + else: + names = [d if isinstance(d, str) else d.name + for d in data] + data = f'({len(data)}) {{ ' + indent = ',\n' + ' ' * (MIN_LINE + len(data)) + data += ', '.join(names[:5]) + names = names[5:] + while names: + data = f'{data}{indent}{", ".join(names[:5])}' + names = names[5:] + data += ' }' + elif kind is KIND.TYPEDEF: + data = f'typedef {data}' + elif kind == KIND.STATEMENT: + pass + else: + raise NotImplementedError(item) + if isforward: + fwd = '*' + if not showfwd and showfwd is not None: + return + elif showfwd: + return + kind = kind.value + yield LINE.format(**locals()) + + +def fmt_full(filename, item, *, showfwd=None): + raise NotImplementedError + + +FORMATS = { + 'raw': fmt_raw, + 'summary': fmt_summary, + 'full': fmt_full, +} + + +def add_output_cli(parser): + parser.add_argument('--format', dest='fmt', default='summary', choices=tuple(FORMATS)) + parser.add_argument('--showfwd', action='store_true', default=None) + parser.add_argument('--no-showfwd', dest='showfwd', action='store_false', default=None) + + def process_args(args): + pass + return process_args + + +####################################### +# the commands + +def _cli_parse(parser, excluded=None, **prepr_kwargs): + process_output = add_output_cli(parser) + process_kinds = add_kind_filtering_cli(parser) + process_preprocessor = add_preprocessor_cli(parser, **prepr_kwargs) + process_files = add_files_cli(parser, excluded=excluded) + return [ + process_output, + process_kinds, + process_preprocessor, + process_files, + ] + + +def cmd_parse(filenames, *, + fmt='summary', + showfwd=None, + iter_filenames=None, + **kwargs + ): + if 'get_file_preprocessor' not in kwargs: + kwargs['get_file_preprocessor'] = _get_preprocessor() + try: + do_fmt = FORMATS[fmt] + except KeyError: + raise ValueError(f'unsupported fmt {fmt!r}') + for filename in main_for_filenames(filenames, iter_filenames): + for item in _iter_parsed(filename, **kwargs): + for line in do_fmt(filename, item, showfwd=showfwd): + print(line) + + +def _cli_data(parser): + ... + + return [] + + +def cmd_data(filenames, + **kwargs + ): + # XXX + raise NotImplementedError + + +COMMANDS = { + 'parse': ( + 'parse the given C source & header files', + [_cli_parse], + cmd_parse, + ), + 'data': ( + 'check/manage local data (e.g. excludes, macros)', + [_cli_data], + cmd_data, + ), +} + + +####################################### +# the script + +def parse_args(argv=sys.argv[1:], prog=sys.argv[0], *, subset='parse'): + import argparse + parser = argparse.ArgumentParser( + prog=prog or get_prog, + ) + + processors = add_commands_cli( + parser, + commands={k: v[1] for k, v in COMMANDS.items()}, + commonspecs=[ + add_verbosity_cli, + add_traceback_cli, + ], + subset=subset, + ) + + args = parser.parse_args(argv) + ns = vars(args) + + cmd = ns.pop('cmd') + + verbosity, traceback_cm = process_args_by_key( + args, + processors[cmd], + ['verbosity', 'traceback_cm'], + ) + + return cmd, ns, verbosity, traceback_cm + + +def main(cmd, cmd_kwargs): + try: + run_cmd = COMMANDS[cmd][0] + except KeyError: + raise ValueError(f'unsupported cmd {cmd!r}') + run_cmd(**cmd_kwargs) + + +if __name__ == '__main__': + cmd, cmd_kwargs, verbosity, traceback_cm = parse_args() + configure_logger(verbosity) + with traceback_cm: + main(cmd, cmd_kwargs) diff --git a/Tools/c-analyzer/c_parser/_state_machine.py b/Tools/c-analyzer/c_parser/_state_machine.py new file mode 100644 index 0000000000000..b505b4e3e4724 --- /dev/null +++ b/Tools/c-analyzer/c_parser/_state_machine.py @@ -0,0 +1,244 @@ + +f''' + struct {ANON_IDENTIFIER}; + struct {{ ... }} + struct {IDENTIFIER} {{ ... }} + + union {ANON_IDENTIFIER}; + union {{ ... }} + union {IDENTIFIER} {{ ... }} + + enum {ANON_IDENTIFIER}; + enum {{ ... }} + enum {IDENTIFIER} {{ ... }} + + typedef {VARTYPE} {IDENTIFIER}; + typedef {IDENTIFIER}; + typedef {IDENTIFIER}; + typedef {IDENTIFIER}; +''' + + +def parse(srclines): + if isinstance(srclines, str): # a filename + raise NotImplementedError + + + +# This only handles at most 10 nested levels. +#MATCHED_PARENS = textwrap.dedent(rf''' +# # matched parens +# (?: +# [(] # level 0 +# (?: +# [^()]* +# [(] # level 1 +# (?: +# [^()]* +# [(] # level 2 +# (?: +# [^()]* +# [(] # level 3 +# (?: +# [^()]* +# [(] # level 4 +# (?: +# [^()]* +# [(] # level 5 +# (?: +# [^()]* +# [(] # level 6 +# (?: +# [^()]* +# [(] # level 7 +# (?: +# [^()]* +# [(] # level 8 +# (?: +# [^()]* +# [(] # level 9 +# (?: +# [^()]* +# [(] # level 10 +# [^()]* +# [)] +# )* +# [^()]* +# [)] +# )* +# [^()]* +# [)] +# )* +# [^()]* +# [)] +# )* +# [^()]* +# [)] +# )* +# [^()]* +# [)] +# )* +# [^()]* +# [)] +# )* +# [^()]* +# [)] +# )* +# [^()]* +# [)] +# )* +# [^()]* +# [)] +# )* +# [^()]* +# [)] +# ) +# # end matched parens +# ''') + +''' + # for loop + (?: + \s* \b for + \s* [(] + ( + [^;]* ; + [^;]* ; + .*? + ) #
    + [)] + \s* + (?: + (?: + ( + {_ind(SIMPLE_STMT, 6)} + ) # + ; + ) + | + ( {{ ) # + ) + ) + | + + + + ( + (?: + (?: + (?: + {_ind(SIMPLE_STMT, 6)} + )? + return \b \s* + {_ind(INITIALIZER, 5)} + ) + | + (?: + (?: + {IDENTIFIER} \s* + (?: . | -> ) \s* + )* + {IDENTIFIER} + \s* = \s* + {_ind(INITIALIZER, 5)} + ) + | + (?: + {_ind(SIMPLE_STMT, 5)} + ) + ) + | + # cast compound literal + (?: + (?: + [^'"{{}};]* + {_ind(STRING_LITERAL, 5)} + )* + [^'"{{}};]*? + [^'"{{}};=] + = + \s* [(] [^)]* [)] + \s* {{ [^;]* }} + ) + ) # + + + + # compound statement + (?: + ( + (?: + + # "for" statements are handled separately above. + (?: (?: else \s+ )? if | switch | while ) \s* + {_ind(COMPOUND_HEAD, 5)} + ) + | + (?: else | do ) + # We do not worry about compound statements for labels, + # "case", or "default". + )? #
    + \s* + ( {{ ) # + ) + + + + ( + (?: + [^'"{{}};]* + {_ind(STRING_LITERAL, 5)} + )* + [^'"{{}};]* + # Presumably we will not see "== {{". + [^\s='"{{}};] + )? #
    + + + + ( + \b + (?: + # We don't worry about labels with a compound statement. + (?: + switch \s* [(] [^{{]* [)] + ) + | + (?: + case \b \s* [^:]+ [:] + ) + | + (?: + default \s* [:] + ) + | + (?: + do + ) + | + (?: + while \s* [(] [^{{]* [)] + ) + | + #(?: + # for \s* [(] [^{{]* [)] + # ) + #| + (?: + if \s* [(] + (?: [^{{]* [^)] \s* {{ )* [^{{]* + [)] + ) + | + (?: + else + (?: + \s* + if \s* [(] + (?: [^{{]* [^)] \s* {{ )* [^{{]* + [)] + )? + ) + ) + )? #
    +''' diff --git a/Tools/c-analyzer/c_parser/datafiles.py b/Tools/c-analyzer/c_parser/datafiles.py new file mode 100644 index 0000000000000..5bdb946b1772a --- /dev/null +++ b/Tools/c-analyzer/c_parser/datafiles.py @@ -0,0 +1,150 @@ +import os.path + +import c_common.tables as _tables +import c_parser.info as _info + + +BASE_COLUMNS = [ + 'filename', + 'funcname', + 'name', + 'kind', +] +END_COLUMNS = { + 'parsed': 'data', + 'decls': 'declaration', +} + + +def _get_columns(group, extra=None): + return BASE_COLUMNS + list(extra or ()) + [END_COLUMNS[group]] + #return [ + # *BASE_COLUMNS, + # *extra or (), + # END_COLUMNS[group], + #] + + +############################# +# high-level + +def read_parsed(infile): + # XXX Support other formats than TSV? + columns = _get_columns('parsed') + for row in _tables.read_table(infile, columns, sep='\t', fix='-'): + yield _info.ParsedItem.from_row(row, columns) + + +def write_parsed(items, outfile): + # XXX Support other formats than TSV? + columns = _get_columns('parsed') + rows = (item.as_row(columns) for item in items) + _tables.write_table(outfile, columns, rows, sep='\t', fix='-') + + +def read_decls(infile, fmt=None): + if fmt is None: + fmt = _get_format(infile) + read_all, _ = _get_format_handlers('decls', fmt) + for decl, _ in read_all(infile): + yield decl + + +def write_decls(decls, outfile, fmt=None, *, backup=False): + if fmt is None: + fmt = _get_format(infile) + _, write_all = _get_format_handlers('decls', fmt) + write_all(decls, outfile, backup=backup) + + +############################# +# formats + +def _get_format(file, default='tsv'): + if isinstance(file, str): + filename = file + else: + filename = getattr(file, 'name', '') + _, ext = os.path.splitext(filename) + return ext[1:] if ext else default + + +def _get_format_handlers(group, fmt): + # XXX Use a registry. + if group != 'decls': + raise NotImplementedError(group) + if fmt == 'tsv': + return (_iter_decls_tsv, _write_decls_tsv) + else: + raise NotImplementedError(fmt) + + +# tsv + +def iter_decls_tsv(infile, extracolumns=None, relroot=None): + for info, extra in _iter_decls_tsv(infile, extracolumns, relroot): + decl = _info.Declaration.from_row(info) + yield decl, extra + + +def write_decls_tsv(decls, outfile, extracolumns=None, *, + relroot=None, + **kwargs + ): + # XXX Move the row rendering here. + _write_decls_tsv(rows, outfile, extracolumns, relroot, kwargs) + + +def _iter_decls_tsv(infile, extracolumns=None, relroot=None): + columns = _get_columns('decls', extracolumns) + for row in _tables.read_table(infile, columns, sep='\t'): + if extracolumns: + declinfo = row[:4] + row[-1:] + extra = row[4:-1] + else: + declinfo = row + extra = None + if relroot: + # XXX Use something like tables.fix_row() here. + declinfo = [None if v == '-' else v + for v in declinfo] + declinfo[0] = os.path.join(relroot, declinfo[0]) + yield declinfo, extra + + +def _write_decls_tsv(decls, outfile, extracolumns, relroot,kwargs): + columns = _get_columns('decls', extracolumns) + if extracolumns: + def render_decl(decl): + if type(row) is tuple: + decl, *extra = decl + else: + extra = () + extra += ('???',) * (len(extraColumns) - len(extra)) + *row, declaration = _render_known_row(decl, relroot) + row += extra + (declaration,) + return row + else: + render_decl = _render_known_decl + _tables.write_table( + outfile, + header='\t'.join(columns), + rows=(render_decl(d, relroot) for d in decls), + sep='\t', + **kwargs + ) + + +def _render_known_decl(decl, relroot, *, + # These match BASE_COLUMNS + END_COLUMNS[group]. + _columns = 'filename parent name kind data'.split(), + ): + if not isinstance(decl, _info.Declaration): + # e.g. Analyzed + decl = decl.decl + rowdata = decl.render_rowdata(_columns) + if relroot: + rowdata['filename'] = os.path.relpath(rowdata['filename'], relroot) + return [rowdata[c] or '-' for c in _columns] + # XXX + #return _tables.fix_row(rowdata[c] for c in columns) diff --git a/Tools/c-analyzer/c_parser/info.py b/Tools/c-analyzer/c_parser/info.py new file mode 100644 index 0000000000000..a07ce2e0ccb8d --- /dev/null +++ b/Tools/c-analyzer/c_parser/info.py @@ -0,0 +1,1658 @@ +from collections import namedtuple +import enum +import os.path +import re + +from c_common.clsutil import classonly +import c_common.misc as _misc +import c_common.strutil as _strutil +import c_common.tables as _tables +from .parser._regexes import SIMPLE_TYPE + + +FIXED_TYPE = _misc.Labeled('FIXED_TYPE') + +POTS_REGEX = re.compile(rf'^{SIMPLE_TYPE}$', re.VERBOSE) + + +def is_pots(typespec): + if not typespec: + return None + if type(typespec) is not str: + _, _, _, typespec, _ = get_parsed_vartype(typespec) + return POTS_REGEX.match(typespec) is not None + + +def is_funcptr(vartype): + if not vartype: + return None + _, _, _, _, abstract = get_parsed_vartype(vartype) + return _is_funcptr(abstract) + + +def _is_funcptr(declstr): + if not declstr: + return None + # XXX Support "(*)(". + return '(*)(' in declstr.replace(' ', '') + + +def is_exported_symbol(decl): + _, storage, _, _, _ = get_parsed_vartype(decl) + raise NotImplementedError + + +def is_process_global(vardecl): + kind, storage, _, _, _ = get_parsed_vartype(vardecl) + if kind is not KIND.VARIABLE: + raise NotImplementedError(vardecl) + if 'static' in (storage or ''): + return True + + if hasattr(vardecl, 'parent'): + parent = vardecl.parent + else: + parent = vardecl.get('parent') + return not parent + + +def is_fixed_type(vardecl): + if not vardecl: + return None + _, _, _, typespec, abstract = get_parsed_vartype(vardecl) + if 'typeof' in typespec: + raise NotImplementedError(vardecl) + elif not abstract: + return True + + if '*' not in abstract: + # XXX What about []? + return True + elif _is_funcptr(abstract): + return True + else: + for after in abstract.split('*')[1:]: + if not after.lstrip().startswith('const'): + return False + else: + return True + + +def is_immutable(vardecl): + if not vardecl: + return None + if not is_fixed_type(vardecl): + return False + _, _, typequal, _, _ = get_parsed_vartype(vardecl) + # If there, it can only be "const" or "volatile". + return typequal == 'const' + + +############################# +# kinds + + at enum.unique +class KIND(enum.Enum): + + # XXX Use these in the raw parser code. + TYPEDEF = 'typedef' + STRUCT = 'struct' + UNION = 'union' + ENUM = 'enum' + FUNCTION = 'function' + VARIABLE = 'variable' + STATEMENT = 'statement' + + @classonly + def _from_raw(cls, raw): + if raw is None: + return None + elif isinstance(raw, cls): + return raw + elif type(raw) is str: + # We could use cls[raw] for the upper-case form, + # but there's no need to go to the trouble. + return cls(raw.lower()) + else: + raise NotImplementedError(raw) + + @classonly + def by_priority(cls, group=None): + if group is None: + return cls._ALL_BY_PRIORITY.copy() + elif group == 'type': + return cls._TYPE_DECLS_BY_PRIORITY.copy() + elif group == 'decl': + return cls._ALL_DECLS_BY_PRIORITY.copy() + elif isinstance(group, str): + raise NotImplementedError(group) + else: + # XXX Treat group as a set of kinds & return in priority order? + raise NotImplementedError(group) + + @classonly + def is_type_decl(cls, kind): + if kind in cls.TYPES: + return True + if not isinstance(kind, cls): + raise TypeError(f'expected KIND, got {kind!r}') + return False + + @classonly + def is_decl(cls, kind): + if kind in cls.DECLS: + return True + if not isinstance(kind, cls): + raise TypeError(f'expected KIND, got {kind!r}') + return False + + @classonly + def get_group(cls, kind, *, groups=None): + if not isinstance(kind, cls): + raise TypeError(f'expected KIND, got {kind!r}') + if groups is None: + groups = ['type'] + elif not groups: + groups = () + elif isinstance(groups, str): + group = groups + if group not in cls._GROUPS: + raise ValueError(f'unsupported group {group!r}') + groups = [group] + else: + unsupported = [g for g in groups if g not in cls._GROUPS] + if unsupported: + raise ValueError(f'unsupported groups {", ".join(repr(unsupported))}') + for group in groups: + if kind in cls._GROUPS[group]: + return group + else: + return kind.value + + @classonly + def resolve_group(cls, group): + if isinstance(group, cls): + return {group} + elif isinstance(group, str): + try: + return cls._GROUPS[group].copy() + except KeyError: + raise ValueError(f'unsupported group {group!r}') + else: + resolved = set() + for gr in group: + resolve.update(cls.resolve_group(gr)) + return resolved + #return {*cls.resolve_group(g) for g in group} + + +KIND._TYPE_DECLS_BY_PRIORITY = [ + # These are in preferred order. + KIND.TYPEDEF, + KIND.STRUCT, + KIND.UNION, + KIND.ENUM, +] +KIND._ALL_DECLS_BY_PRIORITY = [ + # These are in preferred order. + *KIND._TYPE_DECLS_BY_PRIORITY, + KIND.FUNCTION, + KIND.VARIABLE, +] +KIND._ALL_BY_PRIORITY = [ + # These are in preferred order. + *KIND._ALL_DECLS_BY_PRIORITY, + KIND.STATEMENT, +] + +KIND.TYPES = frozenset(KIND._TYPE_DECLS_BY_PRIORITY) +KIND.DECLS = frozenset(KIND._ALL_DECLS_BY_PRIORITY) +KIND._GROUPS = { + 'type': KIND.TYPES, + 'decl': KIND.DECLS, +} +KIND._GROUPS.update((k.value, {k}) for k in KIND) + + +# The module-level kind-related helpers (below) deal with .kind: + +def is_type_decl(kind): + # Handle ParsedItem, Declaration, etc.. + kind = getattr(kind, 'kind', kind) + return KIND.is_type_decl(kind) + + +def is_decl(kind): + # Handle ParsedItem, Declaration, etc.. + kind = getattr(kind, 'kind', kind) + return KIND.is_decl(kind) + + +def filter_by_kind(items, kind): + if kind == 'type': + kinds = KIND._TYPE_DECLS + elif kind == 'decl': + kinds = KIND._TYPE_DECLS + try: + okay = kind in KIND + except TypeError: + kinds = set(kind) + else: + kinds = {kind} if okay else set(kind) + for item in items: + if item.kind in kinds: + yield item + + +def collate_by_kind(items): + collated = {kind: [] for kind in KIND} + for item in items: + try: + collated[item.kind].append(item) + except KeyError: + raise ValueError(f'unsupported kind in {item!r}') + return collated + + +def get_kind_group(kind): + # Handle ParsedItem, Declaration, etc.. + kind = getattr(kind, 'kind', kind) + return KIND.get_group(kind) + + +def collate_by_kind_group(items): + collated = {KIND.get_group(k): [] for k in KIND} + for item in items: + group = KIND.get_group(item.kind) + collated[group].append(item) + return collated + + +############################# +# low-level + +class FileInfo(namedtuple('FileInfo', 'filename lno')): + @classmethod + def from_raw(cls, raw): + if isinstance(raw, cls): + return raw + elif isinstance(raw, tuple): + return cls(*raw) + elif not raw: + return None + elif isinstance(raw, str): + return cls(raw, -1) + else: + raise TypeError(f'unsupported "raw": {raw:!r}') + + def __str__(self): + return self.filename + + def fix_filename(self, relroot): + filename = os.path.relpath(self.filename, relroot) + return self._replace(filename=filename) + + +class SourceLine(namedtuple('Line', 'file kind data conditions')): + KINDS = ( + #'directive', # data is ... + 'source', # "data" is the line + #'comment', # "data" is the text, including comment markers + ) + + @property + def filename(self): + return self.file.filename + + @property + def lno(self): + return self.file.lno + + +class DeclID(namedtuple('DeclID', 'filename funcname name')): + """The globally-unique identifier for a declaration.""" + + @classmethod + def from_row(cls, row, **markers): + row = _tables.fix_row(row, **markers) + return cls(*row) + + def __new__(cls, filename, funcname, name): + self = super().__new__( + cls, + filename=str(filename) if filename else None, + funcname=str(funcname) if funcname else None, + name=str(name) if name else None, + ) + self._compare = tuple(v or '' for v in self) + return self + + def __hash__(self): + return super().__hash__() + + def __eq__(self, other): + try: + other = tuple(v or '' for v in other) + except TypeError: + return NotImplemented + return self._compare == other + + def __gt__(self, other): + try: + other = tuple(v or '' for v in other) + except TypeError: + return NotImplemented + return self._compare > other + + +class ParsedItem(namedtuple('ParsedItem', 'file kind parent name data')): + + @classmethod + def from_raw(cls, raw): + if isinstance(raw, cls): + return raw + elif isinstance(raw, tuple): + return cls(*raw) + else: + raise TypeError(f'unsupported "raw": {raw:!r}') + + @classmethod + def from_row(cls, row, columns=None): + if not columns: + colnames = 'filename funcname name kind data'.split() + else: + colnames = list(columns) + for i, column in enumerate(colnames): + if column == 'file': + colnames[i] = 'filename' + elif column == 'funcname': + colnames[i] = 'parent' + if len(row) != len(set(colnames)): + raise NotImplementedError(columns, row) + kwargs = {} + for column, value in zip(colnames, row): + if column == 'filename': + kwargs['file'] = FileInfo.from_raw(value) + elif column == 'kind': + kwargs['kind'] = KIND(value) + elif column in cls._fields: + kwargs[column] = value + else: + raise NotImplementedError(column) + return cls(**kwargs) + + @property + def id(self): + try: + return self._id + except AttributeError: + if self.kind is KIND.STATEMENT: + self._id = None + else: + self._id = DeclID(str(self.file), self.funcname, self.name) + return self._id + + @property + def filename(self): + if not self.file: + return None + return self.file.filename + + @property + def lno(self): + if not self.file: + return -1 + return self.file.lno + + @property + def funcname(self): + if not self.parent: + return None + if type(self.parent) is str: + return self.parent + else: + return self.parent.name + + def as_row(self, columns=None): + if not columns: + columns = self._fields + row = [] + for column in columns: + if column == 'file': + value = self.filename + elif column == 'kind': + value = self.kind.value + elif column == 'data': + value = self._render_data() + else: + value = getattr(self, column) + row.append(value) + return row + + def _render_data(self): + if not self.data: + return None + elif isinstance(self.data, str): + return self.data + else: + # XXX + raise NotImplementedError + + +def _get_vartype(data): + try: + vartype = dict(data['vartype']) + except KeyError: + vartype = dict(data) + storage = data.get('storage') + else: + storage = data.get('storage') or vartype.get('storage') + del vartype['storage'] + return storage, vartype + + +def get_parsed_vartype(decl): + kind = getattr(decl, 'kind', None) + if isinstance(decl, ParsedItem): + storage, vartype = _get_vartype(decl.data) + typequal = vartype['typequal'] + typespec = vartype['typespec'] + abstract = vartype['abstract'] + elif isinstance(decl, dict): + kind = decl.get('kind') + storage, vartype = _get_vartype(decl) + typequal = vartype['typequal'] + typespec = vartype['typespec'] + abstract = vartype['abstract'] + elif isinstance(decl, VarType): + storage = None + typequal, typespec, abstract = decl + elif isinstance(decl, TypeDef): + storage = None + typequal, typespec, abstract = decl.vartype + elif isinstance(decl, Variable): + storage = decl.storage + typequal, typespec, abstract = decl.vartype + elif isinstance(decl, Function): + storage = decl.storage + typequal, typespec, abstract = decl.signature.returntype + elif isinstance(decl, str): + vartype, storage = VarType.from_str(decl) + typequal, typespec, abstract = vartype + else: + raise NotImplementedError(decl) + return kind, storage, typequal, typespec, abstract + + +############################# +# high-level + +class HighlevelParsedItem: + + kind = None + + FIELDS = ('file', 'parent', 'name', 'data') + + @classmethod + def from_parsed(cls, parsed): + if parsed.kind is not cls.kind: + raise TypeError(f'kind mismatch ({parsed.kind.value} != {cls.kind.value})') + data, extra = cls._resolve_data(parsed.data) + self = cls( + cls._resolve_file(parsed), + parsed.name, + data, + cls._resolve_parent(parsed) if parsed.parent else None, + **extra or {} + ) + self._parsed = parsed + return self + + @classmethod + def _resolve_file(cls, parsed): + fileinfo = FileInfo.from_raw(parsed.file) + if not fileinfo: + raise NotImplementedError(parsed) + return fileinfo + + @classmethod + def _resolve_data(cls, data): + return data, None + + @classmethod + def _raw_data(cls, data, extra): + if isinstance(data, str): + return data + else: + raise NotImplementedError(data) + + @classmethod + def _data_as_row(cls, data, extra, colnames): + row = {} + for colname in colnames: + if colname in row: + continue + rendered = cls._render_data_row_item(colname, data, extra) + if rendered is iter(rendered): + rendered, = rendered + row[colname] = rendered + return row + + @classmethod + def _render_data_row_item(cls, colname, data, extra): + if colname == 'data': + return str(data) + else: + return None + + @classmethod + def _render_data_row(cls, fmt, data, extra, colnames): + if fmt != 'row': + raise NotImplementedError + datarow = cls._data_as_row(data, extra, colnames) + unresolved = [c for c, v in datarow.items() if v is None] + if unresolved: + raise NotImplementedError(unresolved) + for colname, value in datarow.items(): + if type(value) != str: + if colname == 'kind': + datarow[colname] = value.value + else: + datarow[colname] = str(value) + return datarow + + @classmethod + def _render_data(cls, fmt, data, extra): + row = cls._render_data_row(fmt, data, extra, ['data']) + yield ' '.join(row.values()) + + @classmethod + def _resolve_parent(cls, parsed, *, _kind=None): + fileinfo = FileInfo(parsed.file.filename, -1) + if isinstance(parsed.parent, str): + if parsed.parent.isidentifier(): + name = parsed.parent + else: + # XXX It could be something like " ". + raise NotImplementedError(repr(parsed.parent)) + parent = ParsedItem(fileinfo, _kind, None, name, None) + elif type(parsed.parent) is tuple: + # XXX It could be something like (kind, name). + raise NotImplementedError(repr(parsed.parent)) + else: + return parsed.parent + Parent = KIND_CLASSES.get(_kind, Declaration) + return Parent.from_parsed(parent) + + @classmethod + def _parse_columns(cls, columns): + colnames = {} # {requested -> actual} + columns = list(columns or cls.FIELDS) + datacolumns = [] + for i, colname in enumerate(columns): + if colname == 'file': + columns[i] = 'filename' + colnames['file'] = 'filename' + elif colname == 'lno': + columns[i] = 'line' + colnames['lno'] = 'line' + elif colname in ('filename', 'line'): + colnames[colname] = colname + elif colname == 'data': + datacolumns.append(colname) + colnames[colname] = None + elif colname in cls.FIELDS or colname == 'kind': + colnames[colname] = colname + else: + datacolumns.append(colname) + colnames[colname] = None + return columns, datacolumns, colnames + + def __init__(self, file, name, data, parent=None, *, + _extra=None, + _shortkey=None, + _key=None, + ): + self.file = file + self.parent = parent or None + self.name = name + self.data = data + self._extra = _extra or {} + self._shortkey = _shortkey + self._key = _key + + def __repr__(self): + args = [f'{n}={getattr(self, n)!r}' + for n in ['file', 'name', 'data', 'parent', *(self._extra or ())]] + return f'{type(self).__name__}({", ".join(args)})' + + def __str__(self): + try: + return self._str + except AttributeError: + self._str = next(self.render()) + return self._str + + def __getattr__(self, name): + try: + return self._extra[name] + except KeyError: + raise AttributeError(name) + + def __hash__(self): + return hash(self._key) + + def __eq__(self, other): + if isinstance(other, HighlevelParsedItem): + return self._key == other._key + elif type(other) is tuple: + return self._key == other + else: + return NotImplemented + + def __gt__(self, other): + if isinstance(other, HighlevelParsedItem): + return self._key > other._key + elif type(other) is tuple: + return self._key > other + else: + return NotImplemented + + @property + def id(self): + return self.parsed.id + + @property + def shortkey(self): + return self._shortkey + + @property + def key(self): + return self._key + + @property + def filename(self): + if not self.file: + return None + return self.file.filename + + @property + def parsed(self): + try: + return self._parsed + except AttributeError: + parent = self.parent + if parent is not None and not isinstance(parent, str): + parent = parent.name + self._parsed = ParsedItem( + self.file, + self.kind, + parent, + self.name, + self._raw_data(), + ) + return self._parsed + + def fix_filename(self, relroot): + if self.file: + self.file = self.file.fix_filename(relroot) + + def as_rowdata(self, columns=None): + columns, datacolumns, colnames = self._parse_columns(columns) + return self._as_row(colnames, datacolumns, self._data_as_row) + + def render_rowdata(self, columns=None): + columns, datacolumns, colnames = self._parse_columns(columns) + def data_as_row(data, ext, cols): + return self._render_data_row('row', data, ext, cols) + rowdata = self._as_row(colnames, datacolumns, data_as_row) + for column, value in rowdata.items(): + colname = colnames.get(column) + if not colname: + continue + if column == 'kind': + value = value.value + else: + if column == 'parent': + if self.parent: + value = f'({self.parent.kind.value} {self.parent.name})' + if not value: + value = '-' + elif type(value) is VarType: + value = repr(str(value)) + else: + value = str(value) + rowdata[column] = value + return rowdata + + def _as_row(self, colnames, datacolumns, data_as_row): + try: + data = data_as_row(self.data, self._extra, datacolumns) + except NotImplementedError: + data = None + row = data or {} + for column, colname in colnames.items(): + if colname == 'filename': + value = self.file.filename if self.file else None + elif colname == 'line': + value = self.file.lno if self.file else None + elif colname is None: + value = getattr(self, column, None) + else: + value = getattr(self, colname, None) + row.setdefault(column, value) + return row + + def render(self, fmt='line'): + fmt = fmt or 'line' + try: + render = _FORMATS[fmt] + except KeyError: + raise TypeError(f'unsupported fmt {fmt!r}') + try: + data = self._render_data(fmt, self.data, self._extra) + except NotImplementedError: + data = '-' + yield from render(self, data) + + +### formats ### + +def _fmt_line(parsed, data=None): + parts = [ + f'<{parsed.kind.value}>', + ] + parent = '' + if parsed.parent: + parent = parsed.parent + if not isinstance(parent, str): + if parent.kind is KIND.FUNCTION: + parent = f'{parent.name}()' + else: + parent = parent.name + name = f'<{parent}>.{parsed.name}' + else: + name = parsed.name + if data is None: + data = parsed.data + elif data is iter(data): + data, = data + parts.extend([ + name, + f'<{data}>' if data else '-', + f'({str(parsed.file or "")})', + ]) + yield '\t'.join(parts) + + +def _fmt_full(parsed, data=None): + if parsed.kind is KIND.VARIABLE and parsed.parent: + prefix = 'local ' + suffix = f' ({parsed.parent.name})' + else: + # XXX Show other prefixes (e.g. global, public) + prefix = suffix = '' + yield f'{prefix}{parsed.kind.value} {parsed.name!r}{suffix}' + for column, info in parsed.render_rowdata().items(): + if column == 'kind': + continue + if column == 'name': + continue + if column == 'parent' and parsed.kind is not KIND.VARIABLE: + continue + if column == 'data': + if parsed.kind in (KIND.STRUCT, KIND.UNION): + column = 'members' + elif parsed.kind is KIND.ENUM: + column = 'enumerators' + elif parsed.kind is KIND.STATEMENT: + column = 'text' + data, = data + else: + column = 'signature' + data, = data + if not data: +# yield f'\t{column}:\t-' + continue + elif isinstance(data, str): + yield f'\t{column}:\t{data!r}' + else: + yield f'\t{column}:' + for line in data: + yield f'\t\t- {line}' + else: + yield f'\t{column}:\t{info}' + + +_FORMATS = { + 'raw': (lambda v, _d: [repr(v)]), + 'brief': _fmt_line, + 'line': _fmt_line, + 'full': _fmt_full, +} + + +### declarations ## + +class Declaration(HighlevelParsedItem): + + @classmethod + def from_row(cls, row, **markers): + fixed = tuple(_tables.fix_row(row, **markers)) + if cls is Declaration: + _, _, _, kind, _ = fixed + sub = KIND_CLASSES.get(KIND(kind)) + if not sub or not issubclass(sub, Declaration): + raise TypeError(f'unsupported kind, got {row!r}') + else: + sub = cls + return sub._from_row(fixed) + + @classmethod + def _from_row(cls, row): + filename, funcname, name, kind, data = row + kind = KIND._from_raw(kind) + if kind is not cls.kind: + raise TypeError(f'expected kind {cls.kind.value!r}, got {row!r}') + fileinfo = FileInfo.from_raw(filename) + if isinstance(data, str): + data, extra = cls._parse_data(data, fmt='row') + if extra: + return cls(fileinfo, name, data, funcname, _extra=extra) + else: + return cls(fileinfo, name, data, funcname) + + @classmethod + def _resolve_parent(cls, parsed, *, _kind=None): + if _kind is None: + raise TypeError(f'{cls.kind.value} declarations do not have parents ({parsed})') + return super()._resolve_parent(parsed, _kind=_kind) + + @classmethod + def _render_data(cls, fmt, data, extra): + if not data: + # XXX There should be some! Forward? + yield '???' + else: + yield from cls._format_data(fmt, data, extra) + + @classmethod + def _render_data_row_item(cls, colname, data, extra): + if colname == 'data': + return cls._format_data('row', data, extra) + else: + return None + + @classmethod + def _format_data(cls, fmt, data, extra): + raise NotImplementedError(fmt) + + @classmethod + def _parse_data(cls, datastr, fmt=None): + """This is the reverse of _render_data.""" + if not datastr or datastr is _tables.UNKNOWN or datastr == '???': + return None, None + elif datastr is _tables.EMPTY or datastr == '-': + # All the kinds have *something* even it is unknown. + raise TypeError('all declarations have data of some sort, got none') + else: + return cls._unformat_data(datastr, fmt) + + @classmethod + def _unformat_data(cls, datastr, fmt=None): + raise NotImplementedError(fmt) + + +class VarType(namedtuple('VarType', 'typequal typespec abstract')): + + @classmethod + def from_str(cls, text): + orig = text + storage, sep, text = text.strip().partition(' ') + if not sep: + text = storage + storage = None + elif storage not in ('auto', 'register', 'static', 'extern'): + text = orig + storage = None + return cls._from_str(text), storage + + @classmethod + def _from_str(cls, text): + orig = text + if text.startswith(('const ', 'volatile ')): + typequal, _, text = text.partition(' ') + else: + typequal = None + + # Extract a series of identifiers/keywords. + m = re.match(r"^ *'?([a-zA-Z_]\w*(?:\s+[a-zA-Z_]\w*)*)\s*(.*?)'?\s*$", text) + if not m: + raise ValueError(f'invalid vartype text {orig!r}') + typespec, abstract = m.groups() + + return cls(typequal, typespec, abstract or None) + + def __str__(self): + parts = [] + if self.qualifier: + parts.append(self.qualifier) + parts.append(self.spec + (self.abstract or '')) + return ' '.join(parts) + + @property + def qualifier(self): + return self.typequal + + @property + def spec(self): + return self.typespec + + +class Variable(Declaration): + kind = KIND.VARIABLE + + @classmethod + def _resolve_parent(cls, parsed): + return super()._resolve_parent(parsed, _kind=KIND.FUNCTION) + + @classmethod + def _resolve_data(cls, data): + if not data: + return None, None + storage, vartype = _get_vartype(data) + return VarType(**vartype), {'storage': storage} + + @classmethod + def _raw_data(self, data, extra): + vartype = data._asdict() + return { + 'storage': extra['storage'], + 'vartype': vartype, + } + + @classmethod + def _format_data(cls, fmt, data, extra): + storage = extra.get('storage') + text = f'{storage} {data}' if storage else str(data) + if fmt in ('line', 'brief'): + yield text + #elif fmt == 'full': + elif fmt == 'row': + yield text + else: + raise NotImplementedError(fmt) + + @classmethod + def _unformat_data(cls, datastr, fmt=None): + if fmt in ('line', 'brief'): + vartype, storage = VarType.from_str(datastr) + return vartype, {'storage': storage} + #elif fmt == 'full': + elif fmt == 'row': + vartype, storage = VarType.from_str(datastr) + return vartype, {'storage': storage} + else: + raise NotImplementedError(fmt) + + def __init__(self, file, name, data, parent=None, storage=None): + super().__init__(file, name, data, parent, + _extra={'storage': storage}, + _shortkey=f'({parent.name}).{name}' if parent else name, + _key=(str(file), + # Tilde comes after all other ascii characters. + f'~{parent or ""}~', + name, + ), + ) + + @property + def vartype(self): + return self.data + + +class Signature(namedtuple('Signature', 'params returntype inline isforward')): + + @classmethod + def from_str(cls, text): + orig = text + storage, sep, text = text.strip().partition(' ') + if not sep: + text = storage + storage = None + elif storage not in ('auto', 'register', 'static', 'extern'): + text = orig + storage = None + return cls._from_str(text), storage + + @classmethod + def _from_str(cls, text): + orig = text + inline, sep, text = text.partition('|') + if not sep: + text = inline + inline = None + + isforward = False + if text.endswith(';'): + text = text[:-1] + isforward = True + elif text.endswith('{}'): + text = text[:-2] + + index = text.rindex('(') + if index < 0: + raise ValueError(f'bad signature text {orig!r}') + params = text[index:] + while params.count('(') <= params.count(')'): + index = text.rindex('(', 0, index) + if index < 0: + raise ValueError(f'bad signature text {orig!r}') + params = text[index:] + text = text[:index] + + returntype = VarType._from_str(text.rstrip()) + + return cls(params, returntype, inline, isforward) + + def __str__(self): + parts = [] + if self.inline: + parts.extend([ + self.inline, + '|', + ]) + parts.extend([ + str(self.returntype), + self.params, + ';' if self.isforward else '{}', + ]) + return ' '.join(parts) + + @property + def returns(self): + return self.returntype + + +class Function(Declaration): + kind = KIND.FUNCTION + + @classmethod + def _resolve_data(cls, data): + if not data: + return None, None + kwargs = dict(data) + returntype = dict(data['returntype']) + del returntype['storage'] + kwargs['returntype'] = VarType(**returntype) + storage = kwargs.pop('storage') + return Signature(**kwargs), {'storage': storage} + + @classmethod + def _raw_data(self, data): + # XXX finsh! + return data + + @classmethod + def _format_data(cls, fmt, data, extra): + storage = extra.get('storage') + text = f'{storage} {data}' if storage else str(data) + if fmt in ('line', 'brief'): + yield text + #elif fmt == 'full': + elif fmt == 'row': + yield text + else: + raise NotImplementedError(fmt) + + @classmethod + def _unformat_data(cls, datastr, fmt=None): + if fmt in ('line', 'brief'): + sig, storage = Signature.from_str(sig) + return sig, {'storage': storage} + #elif fmt == 'full': + elif fmt == 'row': + sig, storage = Signature.from_str(sig) + return sig, {'storage': storage} + else: + raise NotImplementedError(fmt) + + def __init__(self, file, name, data, parent=None, storage=None): + super().__init__(file, name, data, parent, _extra={'storage': storage}) + self._shortkey = f'~{name}~ {self.data}' + self._key = ( + str(file), + self._shortkey, + ) + + @property + def signature(self): + return self.data + + +class TypeDeclaration(Declaration): + + def __init__(self, file, name, data, parent=None, *, _shortkey=None): + if not _shortkey: + _shortkey = f'{self.kind.value} {name}' + super().__init__(file, name, data, parent, + _shortkey=_shortkey, + _key=( + str(file), + _shortkey, + ), + ) + + +class POTSType(TypeDeclaration): + + def __init__(self, name): + _file = _data = _parent = None + super().__init__(_file, name, _data, _parent, _shortkey=name) + + +class FuncPtr(TypeDeclaration): + + def __init__(self, vartype): + _file = _name = _parent = None + data = vartype + self.vartype = vartype + super().__init__(_file, _name, data, _parent, _shortkey=f'<{vartype}>') + + +class TypeDef(TypeDeclaration): + kind = KIND.TYPEDEF + + @classmethod + def _resolve_data(cls, data): + if not data: + raise NotImplementedError(data) + vartype = dict(data) + del vartype['storage'] + return VarType(**vartype), None + + @classmethod + def _raw_data(self, data): + # XXX finish! + return data + + @classmethod + def _format_data(cls, fmt, data, extra): + text = str(data) + if fmt in ('line', 'brief'): + yield text + elif fmt == 'full': + yield text + elif fmt == 'row': + yield text + else: + raise NotImplementedError(fmt) + + @classmethod + def _unformat_data(cls, datastr, fmt=None): + if fmt in ('line', 'brief'): + vartype, _ = VarType.from_str(datastr) + return vartype, None + #elif fmt == 'full': + elif fmt == 'row': + vartype, _ = VarType.from_str(datastr) + return vartype, None + else: + raise NotImplementedError(fmt) + + def __init__(self, file, name, data, parent=None): + super().__init__(file, name, data, parent, _shortkey=name) + + @property + def vartype(self): + return self.data + + +class Member(namedtuple('Member', 'name vartype size')): + + @classmethod + def from_data(cls, raw, index): + name = raw.name if raw.name else index + vartype = size = None + if type(raw.data) is int: + size = raw.data + elif isinstance(raw.data, str): + size = int(raw.data) + elif raw.data: + vartype = dict(raw.data) + del vartype['storage'] + if 'size' in vartype: + size = int(vartype.pop('size')) + vartype = VarType(**vartype) + return cls(name, vartype, size) + + @classmethod + def from_str(cls, text): + name, _, vartype = text.partition(': ') + if name.startswith('#'): + name = int(name[1:]) + if vartype.isdigit(): + size = int(vartype) + vartype = None + else: + vartype, _ = VarType.from_str(vartype) + size = None + return cls(name, vartype, size) + + def __str__(self): + name = self.name if isinstance(self.name, str) else f'#{self.name}' + return f'{name}: {self.vartype or self.size}' + + +class _StructUnion(TypeDeclaration): + + @classmethod + def _resolve_data(cls, data): + if not data: + # XXX There should be some! Forward? + return None, None + return [Member.from_data(v, i) for i, v in enumerate(data)], None + + @classmethod + def _raw_data(self, data): + # XXX finish! + return data + + @classmethod + def _format_data(cls, fmt, data, extra): + if fmt in ('line', 'brief'): + members = ', '.join(f'<{m}>' for m in data) + yield f'[{members}]' + elif fmt == 'full': + for member in data: + yield f'{member}' + elif fmt == 'row': + members = ', '.join(f'<{m}>' for m in data) + yield f'[{members}]' + else: + raise NotImplementedError(fmt) + + @classmethod + def _unformat_data(cls, datastr, fmt=None): + if fmt in ('line', 'brief'): + members = [Member.from_str(m[1:-1]) + for m in datastr[1:-1].split(', ')] + return members, None + #elif fmt == 'full': + elif fmt == 'row': + members = [Member.from_str(m.rstrip('>').lstrip('<')) + for m in datastr[1:-1].split('>, <')] + return members, None + else: + raise NotImplementedError(fmt) + + def __init__(self, file, name, data, parent=None): + super().__init__(file, name, data, parent) + + @property + def members(self): + return self.data + + +class Struct(_StructUnion): + kind = KIND.STRUCT + + +class Union(_StructUnion): + kind = KIND.UNION + + +class Enum(TypeDeclaration): + kind = KIND.ENUM + + @classmethod + def _resolve_data(cls, data): + if not data: + # XXX There should be some! Forward? + return None, None + enumerators = [e if isinstance(e, str) else e.name + for e in data] + return enumerators, None + + @classmethod + def _raw_data(self, data): + # XXX finsih! + return data + + @classmethod + def _format_data(cls, fmt, data, extra): + if fmt in ('line', 'brief'): + yield repr(data) + elif fmt == 'full': + for enumerator in data: + yield f'{enumerator}' + elif fmt == 'row': + # XXX This won't work with CSV... + yield ','.join(data) + else: + raise NotImplementedError(fmt) + + @classmethod + def _unformat_data(cls, datastr, fmt=None): + if fmt in ('line', 'brief'): + return _strutil.unrepr(datastr), None + #elif fmt == 'full': + elif fmt == 'row': + return datastr.split(','), None + else: + raise NotImplementedError(fmt) + + def __init__(self, file, name, data, parent=None): + super().__init__(file, name, data, parent) + + @property + def enumerators(self): + return self.data + + +### statements ### + +class Statement(HighlevelParsedItem): + kind = KIND.STATEMENT + + @classmethod + def _resolve_data(cls, data): + # XXX finsih! + return data, None + + @classmethod + def _raw_data(self, data): + # XXX finsih! + return data + + @classmethod + def _render_data(cls, fmt, data, extra): + # XXX Handle other formats? + return repr(data) + + @classmethod + def _parse_data(self, datastr, fmt=None): + # XXX Handle other formats? + return _strutil.unrepr(datastr), None + + def __init__(self, file, name, data, parent=None): + super().__init__(file, name, data, parent, + _shortkey=data or '', + _key=( + str(file), + file.lno, + # XXX Only one stmt per line? + ), + ) + + @property + def text(self): + return self.data + + +### + +KIND_CLASSES = {cls.kind: cls for cls in [ + Variable, + Function, + TypeDef, + Struct, + Union, + Enum, + Statement, +]} + + +def resolve_parsed(parsed): + if isinstance(parsed, HighlevelParsedItem): + return parsed + try: + cls = KIND_CLASSES[parsed.kind] + except KeyError: + raise ValueError(f'unsupported kind in {parsed!r}') + return cls.from_parsed(parsed) + + +############################# +# composite + +class Declarations: + + @classmethod + def from_decls(cls, decls): + return cls(decls) + + @classmethod + def from_parsed(cls, items): + decls = (resolve_parsed(item) + for item in items + if item.kind is not KIND.STATEMENT) + return cls.from_decls(decls) + + @classmethod + def _resolve_key(cls, raw): + if isinstance(raw, str): + raw = [raw] + elif isinstance(raw, Declaration): + raw = ( + raw.filename if cls._is_public(raw) else None, + # `raw.parent` is always None for types and functions. + raw.parent if raw.kind is KIND.VARIABLE else None, + raw.name, + ) + + extra = None + if len(raw) == 1: + name, = raw + if name: + name = str(name) + if name.endswith(('.c', '.h')): + # This is only legit as a query. + key = (name, None, None) + else: + key = (None, None, name) + else: + key = (None, None, None) + elif len(raw) == 2: + parent, name = raw + name = str(name) + if isinstance(parent, Declaration): + key = (None, parent.name, name) + elif not parent: + key = (None, None, name) + else: + parent = str(parent) + if parent.endswith(('.c', '.h')): + key = (parent, None, name) + else: + key = (None, parent, name) + else: + key, extra = raw[:3], raw[3:] + filename, funcname, name = key + filename = str(filename) if filename else None + if isinstance(funcname, Declaration): + funcname = funcname.name + else: + funcname = str(funcname) if funcname else None + name = str(name) if name else None + key = (filename, funcname, name) + return key, extra + + @classmethod + def _is_public(cls, decl): + # For .c files don't we need info from .h files to make this decision? + # XXX Check for "extern". + # For now we treat all decls a "private" (have filename set). + return False + + def __init__(self, decls): + # (file, func, name) -> decl + # "public": + # * (None, None, name) + # "private", "global": + # * (file, None, name) + # "private", "local": + # * (file, func, name) + if hasattr(decls, 'items'): + self._decls = decls + else: + self._decls = {} + self._extend(decls) + + # XXX always validate? + + def validate(self): + for key, decl in self._decls.items(): + if type(key) is not tuple or len(key) != 3: + raise ValueError(f'expected 3-tuple key, got {key!r} (for decl {decl!r})') + filename, funcname, name = key + if not name: + raise ValueError(f'expected name in key, got {key!r} (for decl {decl!r})') + elif type(name) is not str: + raise ValueError(f'expected name in key to be str, got {key!r} (for decl {decl!r})') + # XXX Check filename type? + # XXX Check funcname type? + + if decl.kind is KIND.STATEMENT: + raise ValueError(f'expected a declaration, got {decl!r}') + + def __repr__(self): + return f'{type(self).__name__}({list(self)})' + + def __len__(self): + return len(self._decls) + + def __iter__(self): + yield from self._decls + + def __getitem__(self, key): + # XXX Be more exact for the 3-tuple case? + if type(key) not in (str, tuple): + raise KeyError(f'unsupported key {key!r}') + resolved, extra = self._resolve_key(key) + if extra: + raise KeyError(f'key must have at most 3 parts, got {key!r}') + if not resolved[2]: + raise ValueError(f'expected name in key, got {key!r}') + try: + return self._decls[resolved] + except KeyError: + if type(key) is tuple and len(key) == 3: + filename, funcname, name = key + else: + filename, funcname, name = resolved + if filename and not filename.endswith(('.c', '.h')): + raise KeyError(f'invalid filename in key {key!r}') + elif funcname and funcname.endswith(('.c', '.h')): + raise KeyError(f'invalid funcname in key {key!r}') + elif name and name.endswith(('.c', '.h')): + raise KeyError(f'invalid name in key {key!r}') + else: + raise # re-raise + + @property + def types(self): + return self._find(kind=KIND.TYPES) + + @property + def functions(self): + return self._find(None, None, None, KIND.FUNCTION) + + @property + def variables(self): + return self._find(None, None, None, KIND.VARIABLE) + + def iter_all(self): + yield from self._decls.values() + + def get(self, key, default=None): + try: + return self[key] + except KeyError: + return default + + #def add_decl(self, decl, key=None): + # decl = _resolve_parsed(decl) + # self._add_decl(decl, key) + + def find(self, *key, **explicit): + if not key: + if not explicit: + return iter(self) + return self._find(**explicit) + + resolved, extra = self._resolve_key(key) + filename, funcname, name = resolved + if not extra: + kind = None + elif len(extra) == 1: + kind, = extra + else: + raise KeyError(f'key must have at most 4 parts, got {key!r}') + + implicit= {} + if filename: + implicit['filename'] = filename + if funcname: + implicit['funcname'] = funcname + if name: + implicit['name'] = name + if kind: + implicit['kind'] = kind + return self._find(**implicit, **explicit) + + def _find(self, filename=None, funcname=None, name=None, kind=None): + for decl in self._decls.values(): + if filename and decl.filename != filename: + continue + if funcname: + if decl.kind is not KIND.VARIABLE: + continue + if decl.parent.name != funcname: + continue + if name and decl.name != name: + continue + if kind: + kinds = KIND.resolve_group(kind) + if decl.kind not in kinds: + continue + yield decl + + def _add_decl(self, decl, key=None): + if key: + if type(key) not in (str, tuple): + raise NotImplementedError((key, decl)) + # Any partial key will be turned into a full key, but that + # same partial key will still match a key lookup. + resolved, _ = self._resolve_key(key) + if not resolved[2]: + raise ValueError(f'expected name in key, got {key!r}') + key = resolved + # XXX Also add with the decl-derived key if not the same? + else: + key, _ = self._resolve_key(decl) + self._decls[key] = decl + + def _extend(self, decls): + decls = iter(decls) + # Check only the first item. + for decl in decls: + if isinstance(decl, Declaration): + self._add_decl(decl) + # Add the rest without checking. + for decl in decls: + self._add_decl(decl) + elif isinstance(decl, HighlevelParsedItem): + raise NotImplementedError(decl) + else: + try: + key, decl = decl + except ValueError: + raise NotImplementedError(decl) + if not isinstance(decl, Declaration): + raise NotImplementedError(decl) + self._add_decl(decl, key) + # Add the rest without checking. + for key, decl in decls: + self._add_decl(decl, key) + # The iterator will be exhausted at this point. diff --git a/Tools/c-analyzer/c_parser/parser/__init__.py b/Tools/c-analyzer/c_parser/parser/__init__.py new file mode 100644 index 0000000000000..7cb34caf09eba --- /dev/null +++ b/Tools/c-analyzer/c_parser/parser/__init__.py @@ -0,0 +1,212 @@ +"""A simple non-validating parser for C99. + +The functions and regex patterns here are not entirely suitable for +validating C syntax. Please rely on a proper compiler for that. +Instead our goal here is merely matching and extracting information from +valid C code. + +Furthermore, the grammar rules for the C syntax (particularly as +described in the K&R book) actually describe a superset, of which the +full C langage is a proper subset. Here are some of the extra +conditions that must be applied when parsing C code: + +* ... + +(see: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf) + +We have taken advantage of the elements of the C grammar that are used +only in a few limited contexts, mostly as delimiters. They allow us to +focus the regex patterns confidently. Here are the relevant tokens and +in which grammar rules they are used: + +separators: +* ";" + + (decl) struct/union: at end of each member decl + + (decl) declaration: at end of each (non-compound) decl + + (stmt) expr stmt: at end of each stmt + + (stmt) for: between exprs in "header" + + (stmt) goto: at end + + (stmt) continue: at end + + (stmt) break: at end + + (stmt) return: at end +* "," + + (decl) struct/union: between member declators + + (decl) param-list: between params + + (decl) enum: between enumerators + + (decl) initializer (compound): between initializers + + (expr) postfix: between func call args + + (expr) expression: between "assignment" exprs +* ":" + + (decl) struct/union: in member declators + + (stmt) label: between label and stmt + + (stmt) case: between expression and stmt + + (stmt) default: between "default" and stmt +* "=" + + (decl) delaration: between decl and initializer + + (decl) enumerator: between identifier and "initializer" + + (expr) assignment: between "var" and expr + +wrappers: +* "(...)" + + (decl) declarator (func ptr): to wrap ptr/name + + (decl) declarator (func ptr): around params + + (decl) declarator: around sub-declarator (for readability) + + (expr) postfix (func call): around args + + (expr) primary: around sub-expr + + (stmt) if: around condition + + (stmt) switch: around source expr + + (stmt) while: around condition + + (stmt) do-while: around condition + + (stmt) for: around "header" +* "{...}" + + (decl) enum: around enumerators + + (decl) func: around body + + (stmt) compound: around stmts +* "[...]" + * (decl) declarator: for arrays + * (expr) postfix: array access + +other: +* "*" + + (decl) declarator: for pointer types + + (expr) unary: for pointer deref + + +To simplify the regular expressions used here, we've takens some +shortcuts and made certain assumptions about the code we are parsing. +Some of these allow us to skip context-sensitive matching (e.g. braces) +or otherwise still match arbitrary C code unambiguously. However, in +some cases there are certain corner cases where the patterns are +ambiguous relative to arbitrary C code. However, they are still +unambiguous in the specific code we are parsing. + +Here are the cases where we've taken shortcuts or made assumptions: + +* there is no overlap syntactically between the local context (func + bodies) and the global context (other than variable decls), so we + do not need to worry about ambiguity due to the overlap: + + the global context has no expressions or statements + + the local context has no function definitions or type decls +* no "inline" type declarations (struct, union, enum) in function + parameters ~(including function pointers)~ +* no "inline" type decls in function return types +* no superflous parentheses in declarators +* var decls in for loops are always "simple" (e.g. no inline types) +* only inline struct/union/enum decls may be anonymouns (without a name) +* no function pointers in function pointer parameters +* for loop "headers" do not have curly braces (e.g. compound init) +* syntactically, variable decls do not overlap with stmts/exprs, except + in the following case: + spam (*eggs) (...) + This could be either a function pointer variable named "eggs" + or a call to a function named "spam", which returns a function + pointer that gets called. The only differentiator is the + syntax used in the "..." part. It will be comma-separated + parameters for the former and comma-separated expressions for + the latter. Thus, if we expect such decls or calls then we must + parse the decl params. +""" + +""" +TODO: +* extract CPython-specific code +* drop include injection (or only add when needed) +* track position instead of slicing "text" +* Parser class instead of the _iter_source() mess +* alt impl using a state machine (& tokenizer or split on delimiters) +""" + +from ..info import ParsedItem +from ._info import SourceInfo + + +def parse(srclines): + if isinstance(srclines, str): # a filename + raise NotImplementedError + + anon_name = anonymous_names() + for result in _parse(srclines, anon_name): + yield ParsedItem.from_raw(result) + + +# XXX Later: Add a separate function to deal with preprocessor directives +# parsed out of raw source. + + +def anonymous_names(): + counter = 1 + def anon_name(prefix='anon-'): + nonlocal counter + name = f'{prefix}{counter}' + counter += 1 + return name + return anon_name + + +############################# +# internal impl + +import logging + + +_logger = logging.getLogger(__name__) + + +def _parse(srclines, anon_name): + from ._global import parse_globals + + source = _iter_source(srclines) + #source = _iter_source(srclines, showtext=True) + for result in parse_globals(source, anon_name): + # XXX Handle blocks here insted of in parse_globals(). + yield result + + +def _iter_source(lines, *, maxtext=20_000, maxlines=700, showtext=False): + filestack = [] + allinfo = {} + # "lines" should be (fileinfo, data), as produced by the preprocessor code. + for fileinfo, line in lines: + if fileinfo.filename in filestack: + while fileinfo.filename != filestack[-1]: + filename = filestack.pop() + del allinfo[filename] + filename = fileinfo.filename + srcinfo = allinfo[filename] + else: + filename = fileinfo.filename + srcinfo = SourceInfo(filename) + filestack.append(filename) + allinfo[filename] = srcinfo + + _logger.debug(f'-> {line}') + srcinfo._add_line(line, fileinfo.lno) + if len(srcinfo.text) > maxtext: + break + if srcinfo.end - srcinfo.start > maxlines: + break + while srcinfo._used(): + yield srcinfo + if showtext: + _logger.debug(f'=> {srcinfo.text}') + else: + if not filestack: + srcinfo = SourceInfo('???') + else: + filename = filestack[-1] + srcinfo = allinfo[filename] + while srcinfo._used(): + yield srcinfo + if showtext: + _logger.debug(f'=> {srcinfo.text}') + yield srcinfo + if showtext: + _logger.debug(f'=> {srcinfo.text}') + if not srcinfo._ready: + return + # At this point either the file ended prematurely + # or there's "too much" text. + filename, lno, text = srcinfo.filename, srcinfo._start, srcinfo.text + if len(text) > 500: + text = text[:500] + '...' + raise Exception(f'unmatched text ({filename} starting at line {lno}):\n{text}') diff --git a/Tools/c-analyzer/c_parser/parser/_alt.py b/Tools/c-analyzer/c_parser/parser/_alt.py new file mode 100644 index 0000000000000..05a9101b4f529 --- /dev/null +++ b/Tools/c-analyzer/c_parser/parser/_alt.py @@ -0,0 +1,6 @@ + +def _parse(srclines, anon_name): + text = ' '.join(l for _, l in srclines) + + from ._delim import parse + yield from parse(text, anon_name) diff --git a/Tools/c-analyzer/c_parser/parser/_common.py b/Tools/c-analyzer/c_parser/parser/_common.py new file mode 100644 index 0000000000000..40c36039f3f47 --- /dev/null +++ b/Tools/c-analyzer/c_parser/parser/_common.py @@ -0,0 +1,115 @@ +import re + +from ._regexes import ( + _ind, + STRING_LITERAL, + VAR_DECL as _VAR_DECL, +) + + +def log_match(group, m): + from . import _logger + _logger.debug(f'matched <{group}> ({m.group(0)})') + + +############################# +# regex utils + +def set_capture_group(pattern, group, *, strict=True): + old = f'(?: # <{group}>' + if strict and f'(?: # <{group}>' not in pattern: + raise ValueError(f'{old!r} not found in pattern') + return pattern.replace(old, f'( # <{group}>', 1) + + +def set_capture_groups(pattern, groups, *, strict=True): + for group in groups: + pattern = set_capture_group(pattern, group, strict=strict) + return pattern + + +############################# +# syntax-related utils + +_PAREN_RE = re.compile(rf''' + (?: + (?: + [^'"()]* + {_ind(STRING_LITERAL, 3)} + )* + [^'"()]* + (?: + ( [(] ) + | + ( [)] ) + ) + ) + ''', re.VERBOSE) + + +def match_paren(text, depth=0): + pos = 0 + while (m := _PAREN_RE.match(text, pos)): + pos = m.end() + _open, _close = m.groups() + if _open: + depth += 1 + else: # _close + depth -= 1 + if depth == 0: + return pos + else: + raise ValueError(f'could not find matching parens for {text!r}') + + +VAR_DECL = set_capture_groups(_VAR_DECL, ( + 'STORAGE', + 'TYPE_QUAL', + 'TYPE_SPEC', + 'DECLARATOR', + 'IDENTIFIER', + 'WRAPPED_IDENTIFIER', + 'FUNC_IDENTIFIER', +)) + + +def parse_var_decl(decl): + m = re.match(VAR_DECL, decl, re.VERBOSE) + (storage, typequal, typespec, declarator, + name, + wrappedname, + funcptrname, + ) = m.groups() + if name: + kind = 'simple' + elif wrappedname: + kind = 'wrapped' + name = wrappedname + elif funcptrname: + kind = 'funcptr' + name = funcptrname + else: + raise NotImplementedError + abstract = declarator.replace(name, '') + vartype = { + 'storage': storage, + 'typequal': typequal, + 'typespec': typespec, + 'abstract': abstract, + } + return (kind, name, vartype) + + +############################# +# parser state utils + +# XXX Drop this or use it! +def iter_results(results): + if not results: + return + if callable(results): + results = results() + + for result, text in results(): + if result: + yield result, text diff --git a/Tools/c-analyzer/c_parser/parser/_compound_decl_body.py b/Tools/c-analyzer/c_parser/parser/_compound_decl_body.py new file mode 100644 index 0000000000000..eb5bc67607bb1 --- /dev/null +++ b/Tools/c-analyzer/c_parser/parser/_compound_decl_body.py @@ -0,0 +1,158 @@ +import re + +from ._regexes import ( + STRUCT_MEMBER_DECL as _STRUCT_MEMBER_DECL, + ENUM_MEMBER_DECL as _ENUM_MEMBER_DECL, +) +from ._common import ( + log_match, + parse_var_decl, + set_capture_groups, +) + + +############################# +# struct / union + +STRUCT_MEMBER_DECL = set_capture_groups(_STRUCT_MEMBER_DECL, ( + 'COMPOUND_TYPE_KIND', + 'COMPOUND_TYPE_NAME', + 'SPECIFIER_QUALIFIER', + 'DECLARATOR', + 'SIZE', + 'ENDING', + 'CLOSE', +)) +STRUCT_MEMBER_RE = re.compile(rf'^ \s* {STRUCT_MEMBER_DECL}', re.VERBOSE) + + +def parse_struct_body(source, anon_name, parent): + done = False + while not done: + done = True + for srcinfo in source: + m = STRUCT_MEMBER_RE.match(srcinfo.text) + if m: + break + else: + # We ran out of lines. + if srcinfo is not None: + srcinfo.done() + return + for item in _parse_struct_next(m, srcinfo, anon_name, parent): + if callable(item): + parse_body = item + yield from parse_body(source) + else: + yield item + done = False + + +def _parse_struct_next(m, srcinfo, anon_name, parent): + (inline_kind, inline_name, + qualspec, declarator, + size, + ending, + close, + ) = m.groups() + remainder = srcinfo.text[m.end():] + + if close: + log_match('compound close', m) + srcinfo.advance(remainder) + + elif inline_kind: + log_match('compound inline', m) + kind = inline_kind + name = inline_name or anon_name('inline-') + # Immediately emit a forward declaration. + yield srcinfo.resolve(kind, name=name, data=None) + + # un-inline the decl. Note that it might not actually be inline. + # We handle the case in the "maybe_inline_actual" branch. + srcinfo.nest( + remainder, + f'{kind} {name}', + ) + def parse_body(source): + _parse_body = DECL_BODY_PARSERS[kind] + + data = [] # members + ident = f'{kind} {name}' + for item in _parse_body(source, anon_name, ident): + if item.kind == 'field': + data.append(item) + else: + yield item + # XXX Should "parent" really be None for inline type decls? + yield srcinfo.resolve(kind, data, name, parent=None) + + srcinfo.resume() + yield parse_body + + else: + # not inline (member) + log_match('compound member', m) + if qualspec: + _, name, data = parse_var_decl(f'{qualspec} {declarator}') + if not name: + name = anon_name('struct-field-') + if size: +# data = (data, size) + data['size'] = int(size) + else: + # This shouldn't happen (we expect each field to have a name). + raise NotImplementedError + name = sized_name or anon_name('struct-field-') + data = int(size) + + yield srcinfo.resolve('field', data, name, parent) # XXX Restart? + if ending == ',': + remainder = rf'{qualspec} {remainder}' + srcinfo.advance(remainder) + + +############################# +# enum + +ENUM_MEMBER_DECL = set_capture_groups(_ENUM_MEMBER_DECL, ( + 'CLOSE', + 'NAME', + 'INIT', + 'ENDING', +)) +ENUM_MEMBER_RE = re.compile(rf'{ENUM_MEMBER_DECL}', re.VERBOSE) + + +def parse_enum_body(source, _anon_name, _parent): + ending = None + while ending != '}': + for srcinfo in source: + m = ENUM_MEMBER_RE.match(srcinfo.text) + if m: + break + else: + # We ran out of lines. + if srcinfo is not None: + srcinfo.done() + return + remainder = srcinfo.text[m.end():] + + (close, + name, init, ending, + ) = m.groups() + if close: + ending = '}' + else: + data = init + yield srcinfo.resolve('field', data, name, _parent) + srcinfo.advance(remainder) + + +############################# + +DECL_BODY_PARSERS = { + 'struct': parse_struct_body, + 'union': parse_struct_body, + 'enum': parse_enum_body, +} diff --git a/Tools/c-analyzer/c_parser/parser/_delim.py b/Tools/c-analyzer/c_parser/parser/_delim.py new file mode 100644 index 0000000000000..51433a629d3a3 --- /dev/null +++ b/Tools/c-analyzer/c_parser/parser/_delim.py @@ -0,0 +1,54 @@ +import re +import textwrap + +from ._regexes import _ind, STRING_LITERAL + + +def parse(text, anon_name): + context = None + data = None + for m in DELIMITER_RE.find_iter(text): + before, opened, closed = m.groups() + delim = opened or closed + + handle_segment = HANDLERS[context][delim] + result, context, data = handle_segment(before, delim, data) + if result: + yield result + + +DELIMITER = textwrap.dedent(rf''' + ( + (?: + [^'"()\[\]{};]* + {_ind(STRING_LITERAL, 3)} + }* + [^'"()\[\]{};]+ + )? # + (?: + ( + [(\[{] + ) # + | + ( + [)\]};] + ) # + )? + ''') +DELIMITER_RE = re.compile(DELIMITER, re.VERBOSE) + +_HANDLERS = { + None: { # global + # opened + '{': ..., + '[': None, + '(': None, + # closed + '}': None, + ']': None, + ')': None, + ';': ..., + }, + '': { + }, +} diff --git a/Tools/c-analyzer/c_parser/parser/_func_body.py b/Tools/c-analyzer/c_parser/parser/_func_body.py new file mode 100644 index 0000000000000..42fd459e111d2 --- /dev/null +++ b/Tools/c-analyzer/c_parser/parser/_func_body.py @@ -0,0 +1,278 @@ +import re + +from ._regexes import ( + LOCAL as _LOCAL, + LOCAL_STATICS as _LOCAL_STATICS, +) +from ._common import ( + log_match, + parse_var_decl, + set_capture_groups, + match_paren, +) +from ._compound_decl_body import DECL_BODY_PARSERS + + +LOCAL = set_capture_groups(_LOCAL, ( + 'EMPTY', + 'INLINE_LEADING', + 'INLINE_PRE', + 'INLINE_KIND', + 'INLINE_NAME', + 'STORAGE', + 'VAR_DECL', + 'VAR_INIT', + 'VAR_ENDING', + 'COMPOUND_BARE', + 'COMPOUND_LABELED', + 'COMPOUND_PAREN', + 'BLOCK_LEADING', + 'BLOCK_OPEN', + 'SIMPLE_STMT', + 'SIMPLE_ENDING', + 'BLOCK_CLOSE', +)) +LOCAL_RE = re.compile(rf'^ \s* {LOCAL}', re.VERBOSE) + + +# Note that parse_function_body() still has trouble with a few files +# in the CPython codebase. + +def parse_function_body(source, name, anon_name): + # XXX + raise NotImplementedError + + +def parse_function_body(name, text, resolve, source, anon_name, parent): + raise NotImplementedError + # For now we do not worry about locals declared in for loop "headers". + depth = 1; + while depth > 0: + m = LOCAL_RE.match(text) + while not m: + text, resolve = continue_text(source, text or '{', resolve) + m = LOCAL_RE.match(text) + text = text[m.end():] + ( + empty, + inline_leading, inline_pre, inline_kind, inline_name, + storage, decl, + var_init, var_ending, + compound_bare, compound_labeled, compound_paren, + block_leading, block_open, + simple_stmt, simple_ending, + block_close, + ) = m.groups() + + if empty: + log_match('', m) + resolve(None, None, None, text) + yield None, text + elif inline_kind: + log_match('', m) + kind = inline_kind + name = inline_name or anon_name('inline-') + data = [] # members + # We must set the internal "text" from _iter_source() to the + # start of the inline compound body, + # Note that this is effectively like a forward reference that + # we do not emit. + resolve(kind, None, name, text, None) + _parse_body = DECL_BODY_PARSERS[kind] + before = [] + ident = f'{kind} {name}' + for member, inline, text in _parse_body(text, resolve, source, anon_name, ident): + if member: + data.append(member) + if inline: + yield from inline + # un-inline the decl. Note that it might not actually be inline. + # We handle the case in the "maybe_inline_actual" branch. + text = f'{inline_leading or ""} {inline_pre or ""} {kind} {name} {text}' + # XXX Should "parent" really be None for inline type decls? + yield resolve(kind, data, name, text, None), text + elif block_close: + log_match('', m) + depth -= 1 + resolve(None, None, None, text) + # XXX This isn't great. Calling resolve() should have + # cleared the closing bracket. However, some code relies + # on the yielded value instead of the resolved one. That + # needs to be fixed. + yield None, text + elif compound_bare: + log_match('', m) + yield resolve('statement', compound_bare, None, text, parent), text + elif compound_labeled: + log_match('', m) + yield resolve('statement', compound_labeled, None, text, parent), text + elif compound_paren: + log_match('', m) + try: + pos = match_paren(text) + except ValueError: + text = f'{compound_paren} {text}' + #resolve(None, None, None, text) + text, resolve = continue_text(source, text, resolve) + yield None, text + else: + head = text[:pos] + text = text[pos:] + if compound_paren == 'for': + # XXX Parse "head" as a compound statement. + stmt1, stmt2, stmt3 = head.split(';', 2) + data = { + 'compound': compound_paren, + 'statements': (stmt1, stmt2, stmt3), + } + else: + data = { + 'compound': compound_paren, + 'statement': head, + } + yield resolve('statement', data, None, text, parent), text + elif block_open: + log_match('', m) + depth += 1 + if block_leading: + # An inline block: the last evaluated expression is used + # in place of the block. + # XXX Combine it with the remainder after the block close. + stmt = f'{block_open}{{}}...;' + yield resolve('statement', stmt, None, text, parent), text + else: + resolve(None, None, None, text) + yield None, text + elif simple_ending: + log_match('', m) + yield resolve('statement', simple_stmt, None, text, parent), text + elif var_ending: + log_match('', m) + kind = 'variable' + _, name, vartype = parse_var_decl(decl) + data = { + 'storage': storage, + 'vartype': vartype, + } + after = () + if var_ending == ',': + # It was a multi-declaration, so queue up the next one. + _, qual, typespec, _ = vartype.values() + text = f'{storage or ""} {qual or ""} {typespec} {text}' + yield resolve(kind, data, name, text, parent), text + if var_init: + _data = f'{name} = {var_init.strip()}' + yield resolve('statement', _data, None, text, parent), text + else: + # This should be unreachable. + raise NotImplementedError + + +############################# +# static local variables + +LOCAL_STATICS = set_capture_groups(_LOCAL_STATICS, ( + 'INLINE_LEADING', + 'INLINE_PRE', + 'INLINE_KIND', + 'INLINE_NAME', + 'STATIC_DECL', + 'STATIC_INIT', + 'STATIC_ENDING', + 'DELIM_LEADING', + 'BLOCK_OPEN', + 'BLOCK_CLOSE', + 'STMT_END', +)) +LOCAL_STATICS_RE = re.compile(rf'^ \s* {LOCAL_STATICS}', re.VERBOSE) + + +def parse_function_statics(source, func, anon_name): + # For now we do not worry about locals declared in for loop "headers". + depth = 1; + while depth > 0: + for srcinfo in source: + m = LOCAL_STATICS_RE.match(srcinfo.text) + if m: + break + else: + # We ran out of lines. + if srcinfo is not None: + srcinfo.done() + return + for item, depth in _parse_next_local_static(m, srcinfo, + anon_name, func, depth): + if callable(item): + parse_body = item + yield from parse_body(source) + elif item is not None: + yield item + + +def _parse_next_local_static(m, srcinfo, anon_name, func, depth): + (inline_leading, inline_pre, inline_kind, inline_name, + static_decl, static_init, static_ending, + _delim_leading, + block_open, + block_close, + stmt_end, + ) = m.groups() + remainder = srcinfo.text[m.end():] + + if inline_kind: + log_match('func inline', m) + kind = inline_kind + name = inline_name or anon_name('inline-') + # Immediately emit a forward declaration. + yield srcinfo.resolve(kind, name=name, data=None), depth + + # un-inline the decl. Note that it might not actually be inline. + # We handle the case in the "maybe_inline_actual" branch. + srcinfo.nest( + remainder, + f'{inline_leading or ""} {inline_pre or ""} {kind} {name}' + ) + def parse_body(source): + _parse_body = DECL_BODY_PARSERS[kind] + + data = [] # members + ident = f'{kind} {name}' + for item in _parse_body(source, anon_name, ident): + if item.kind == 'field': + data.append(item) + else: + yield item + # XXX Should "parent" really be None for inline type decls? + yield srcinfo.resolve(kind, data, name, parent=None) + + srcinfo.resume() + yield parse_body, depth + + elif static_decl: + log_match('local variable', m) + _, name, data = parse_var_decl(static_decl) + + yield srcinfo.resolve('variable', data, name, parent=func), depth + + if static_init: + srcinfo.advance(f'{name} {static_init} {remainder}') + elif static_ending == ',': + # It was a multi-declaration, so queue up the next one. + _, qual, typespec, _ = data.values() + srcinfo.advance(f'static {qual or ""} {typespec} {remainder}') + else: + srcinfo.advance('') + + else: + log_match('func other', m) + if block_open: + depth += 1 + elif block_close: + depth -= 1 + elif stmt_end: + pass + else: + # This should be unreachable. + raise NotImplementedError + srcinfo.advance(remainder) + yield None, depth diff --git a/Tools/c-analyzer/c_parser/parser/_global.py b/Tools/c-analyzer/c_parser/parser/_global.py new file mode 100644 index 0000000000000..35947c1299813 --- /dev/null +++ b/Tools/c-analyzer/c_parser/parser/_global.py @@ -0,0 +1,179 @@ +import re + +from ._regexes import ( + GLOBAL as _GLOBAL, +) +from ._common import ( + log_match, + parse_var_decl, + set_capture_groups, +) +from ._compound_decl_body import DECL_BODY_PARSERS +#from ._func_body import parse_function_body +from ._func_body import parse_function_statics as parse_function_body + + +GLOBAL = set_capture_groups(_GLOBAL, ( + 'EMPTY', + 'COMPOUND_LEADING', + 'COMPOUND_KIND', + 'COMPOUND_NAME', + 'FORWARD_KIND', + 'FORWARD_NAME', + 'MAYBE_INLINE_ACTUAL', + 'TYPEDEF_DECL', + 'TYPEDEF_FUNC_PARAMS', + 'VAR_STORAGE', + 'FUNC_INLINE', + 'VAR_DECL', + 'FUNC_PARAMS', + 'FUNC_DELIM', + 'FUNC_LEGACY_PARAMS', + 'VAR_INIT', + 'VAR_ENDING', +)) +GLOBAL_RE = re.compile(rf'^ \s* {GLOBAL}', re.VERBOSE) + + +def parse_globals(source, anon_name): + for srcinfo in source: + m = GLOBAL_RE.match(srcinfo.text) + if not m: + # We need more text. + continue + for item in _parse_next(m, srcinfo, anon_name): + if callable(item): + parse_body = item + yield from parse_body(source) + else: + yield item + else: + # We ran out of lines. + if srcinfo is not None: + srcinfo.done() + return + + +def _parse_next(m, srcinfo, anon_name): + ( + empty, + # compound type decl (maybe inline) + compound_leading, compound_kind, compound_name, + forward_kind, forward_name, maybe_inline_actual, + # typedef + typedef_decl, typedef_func_params, + # vars and funcs + storage, func_inline, decl, + func_params, func_delim, func_legacy_params, + var_init, var_ending, + ) = m.groups() + remainder = srcinfo.text[m.end():] + + if empty: + log_match('global empty', m) + srcinfo.advance(remainder) + + elif maybe_inline_actual: + log_match('maybe_inline_actual', m) + # Ignore forward declarations. + # XXX Maybe return them too (with an "isforward" flag)? + if not maybe_inline_actual.strip().endswith(';'): + remainder = maybe_inline_actual + remainder + yield srcinfo.resolve(forward_kind, None, forward_name) + if maybe_inline_actual.strip().endswith('='): + # We use a dummy prefix for a fake typedef. + # XXX Ideally this case would not be caught by MAYBE_INLINE_ACTUAL. + _, name, data = parse_var_decl(f'{forward_kind} {forward_name} fake_typedef_{forward_name}') + yield srcinfo.resolve('typedef', data, name, parent=None) + remainder = f'{name} {remainder}' + srcinfo.advance(remainder) + + elif compound_kind: + kind = compound_kind + name = compound_name or anon_name('inline-') + # Immediately emit a forward declaration. + yield srcinfo.resolve(kind, name=name, data=None) + + # un-inline the decl. Note that it might not actually be inline. + # We handle the case in the "maybe_inline_actual" branch. + srcinfo.nest( + remainder, + f'{compound_leading or ""} {compound_kind} {name}', + ) + def parse_body(source): + _parse_body = DECL_BODY_PARSERS[compound_kind] + + data = [] # members + ident = f'{kind} {name}' + for item in _parse_body(source, anon_name, ident): + if item.kind == 'field': + data.append(item) + else: + yield item + # XXX Should "parent" really be None for inline type decls? + yield srcinfo.resolve(kind, data, name, parent=None) + + srcinfo.resume() + yield parse_body + + elif typedef_decl: + log_match('typedef', m) + kind = 'typedef' + _, name, data = parse_var_decl(typedef_decl) + if typedef_func_params: + return_type = data + # This matches the data for func declarations. + data = { + 'storage': None, + 'inline': None, + 'params': f'({typedef_func_params})', + 'returntype': return_type, + 'isforward': True, + } + yield srcinfo.resolve(kind, data, name, parent=None) + srcinfo.advance(remainder) + + elif func_delim or func_legacy_params: + log_match('function', m) + kind = 'function' + _, name, return_type = parse_var_decl(decl) + func_params = func_params or func_legacy_params + data = { + 'storage': storage, + 'inline': func_inline, + 'params': f'({func_params})', + 'returntype': return_type, + 'isforward': func_delim == ';', + } + + yield srcinfo.resolve(kind, data, name, parent=None) + srcinfo.advance(remainder) + + if func_delim == '{' or func_legacy_params: + def parse_body(source): + yield from parse_function_body(source, name, anon_name) + yield parse_body + + elif var_ending: + log_match('global variable', m) + kind = 'variable' + _, name, vartype = parse_var_decl(decl) + data = { + 'storage': storage, + 'vartype': vartype, + } + yield srcinfo.resolve(kind, data, name, parent=None) + + if var_ending == ',': + # It was a multi-declaration, so queue up the next one. + _, qual, typespec, _ = vartype.values() + remainder = f'{storage or ""} {qual or ""} {typespec} {remainder}' + srcinfo.advance(remainder) + + if var_init: + _data = f'{name} = {var_init.strip()}' + yield srcinfo.resolve('statement', _data, name=None) + + else: + # This should be unreachable. + raise NotImplementedError diff --git a/Tools/c-analyzer/c_parser/parser/_info.py b/Tools/c-analyzer/c_parser/parser/_info.py new file mode 100644 index 0000000000000..2dcd5e5e760b7 --- /dev/null +++ b/Tools/c-analyzer/c_parser/parser/_info.py @@ -0,0 +1,168 @@ +from ..info import KIND, ParsedItem, FileInfo + + +class TextInfo: + + def __init__(self, text, start=None, end=None): + # immutable: + if not start: + start = 1 + self.start = start + + # mutable: + lines = text.splitlines() or [''] + self.text = text.strip() + if not end: + end = start + len(lines) - 1 + self.end = end + self.line = lines[-1] + + def __repr__(self): + args = (f'{a}={getattr(self, a)!r}' + for a in ['text', 'start', 'end']) + return f'{type(self).__name__}({", ".join(args)})' + + def add_line(self, line, lno=None): + if lno is None: + lno = self.end + 1 + else: + if isinstance(lno, FileInfo): + fileinfo = lno + if fileinfo.filename != self.filename: + raise NotImplementedError((fileinfo, self.filename)) + lno = fileinfo.lno + # XXX + #if lno < self.end: + # raise NotImplementedError((lno, self.end)) + line = line.lstrip() + self.text += ' ' + line + self.line = line + self.end = lno + + +class SourceInfo: + + _ready = False + + def __init__(self, filename, _current=None): + # immutable: + self.filename = filename + # mutable: + if isinstance(_current, str): + _current = TextInfo(_current) + self._current = _current + start = -1 + self._start = _current.start if _current else -1 + self._nested = [] + self._set_ready() + + def __repr__(self): + args = (f'{a}={getattr(self, a)!r}' + for a in ['filename', '_current']) + return f'{type(self).__name__}({", ".join(args)})' + + @property + def start(self): + if self._current is None: + return self._start + return self._current.start + + @property + def end(self): + if self._current is None: + return self._start + return self._current.end + + @property + def text(self): + if self._current is None: + return '' + return self._current.text + + def nest(self, text, before, start=None): + if self._current is None: + raise Exception('nesting requires active source text') + current = self._current + current.text = before + self._nested.append(current) + self._replace(text, start) + + def resume(self, remainder=None): + if not self._nested: + raise Exception('no nested text to resume') + if self._current is None: + raise Exception('un-nesting requires active source text') + if remainder is None: + remainder = self._current.text + self._clear() + self._current = self._nested.pop() + self._current.text += ' ' + remainder + self._set_ready() + + def advance(self, remainder, start=None): + if self._current is None: + raise Exception('advancing requires active source text') + if remainder.strip(): + self._replace(remainder, start, fixnested=True) + else: + if self._nested: + self._replace('', start, fixnested=True) + #raise Exception('cannot advance while nesting') + else: + self._clear(start) + + def resolve(self, kind, data, name, parent=None): + # "field" isn't a top-level kind, so we leave it as-is. + if kind and kind != 'field': + kind = KIND._from_raw(kind) + fileinfo = FileInfo(self.filename, self._start) + return ParsedItem(fileinfo, kind, parent, name, data) + + def done(self): + self._set_ready() + + def _set_ready(self): + if self._current is None: + self._ready = False + else: + self._ready = self._current.text.strip() != '' + + def _used(self): + ready = self._ready + self._ready = False + return ready + + def _clear(self, start=None): + old = self._current + if self._current is not None: + # XXX Fail if self._current wasn't used up? + if start is None: + start = self._current.end + self._current = None + if start is not None: + self._start = start + self._set_ready() + return old + + def _replace(self, text, start=None, *, fixnested=False): + end = self._current.end + old = self._clear(start) + self._current = TextInfo(text, self._start, end) + if fixnested and self._nested and self._nested[-1] is old: + self._nested[-1] = self._current + self._set_ready() + + def _add_line(self, line, lno=None): + if not line.strip(): + # We don't worry about multi-line string literals. + return + if self._current is None: + self._start = lno + self._current = TextInfo(line, lno) + else: + # XXX + #if lno < self._current.end: + # # A circular include? + # raise NotImplementedError((lno, self)) + self._current.add_line(line, lno) + self._ready = True diff --git a/Tools/c-analyzer/c_parser/parser/_regexes.py b/Tools/c-analyzer/c_parser/parser/_regexes.py new file mode 100644 index 0000000000000..e9bc31d335a7d --- /dev/null +++ b/Tools/c-analyzer/c_parser/parser/_regexes.py @@ -0,0 +1,796 @@ +# Regular expression patterns for C syntax. +# +# None of these patterns has any capturing. However, a number of them +# have capturing markers compatible with utils.set_capture_groups(). + +import textwrap + + +def _ind(text, level=1, edges='both'): + indent = ' ' * level + text = textwrap.indent(text, indent) + if edges == 'pre' or edges == 'both': + text = '\n' + indent + text.lstrip() + if edges == 'post' or edges == 'both': + text = text.rstrip() + '\n' + ' ' * (level - 1) + return text + + +####################################### +# general + +HEX = r'(?: [0-9a-zA-Z] )' + +STRING_LITERAL = textwrap.dedent(rf''' + (?: + # character literal + (?: + ['] [^'] ['] + | + ['] \\ . ['] + | + ['] \\x{HEX}{HEX} ['] + | + ['] \\0\d\d ['] + | + (?: + ['] \\o[01]\d\d ['] + | + ['] \\o2[0-4]\d ['] + | + ['] \\o25[0-5] ['] + ) + ) + | + # string literal + (?: + ["] (?: [^"\\]* \\ . )* [^"\\]* ["] + ) + # end string literal + ) + ''') + +_KEYWORD = textwrap.dedent(r''' + (?: + \b + (?: + auto | + extern | + register | + static | + typedef | + + const | + volatile | + + signed | + unsigned | + char | + short | + int | + long | + float | + double | + void | + + struct | + union | + enum | + + goto | + return | + sizeof | + break | + continue | + if | + else | + for | + do | + while | + switch | + case | + default | + entry + ) + \b + ) + ''') +KEYWORD = rf''' + # keyword + {_KEYWORD} + # end keyword + ''' +_KEYWORD = ''.join(_KEYWORD.split()) + +IDENTIFIER = r'(?: [a-zA-Z_][a-zA-Z0-9_]* )' +# We use a negative lookahead to filter out keywords. +STRICT_IDENTIFIER = rf'(?: (?! {_KEYWORD} ) \b {IDENTIFIER} \b )' +ANON_IDENTIFIER = rf'(?: (?! {_KEYWORD} ) \b {IDENTIFIER} (?: - \d+ )? \b )' + + +####################################### +# types + +SIMPLE_TYPE = textwrap.dedent(rf''' + # simple type + (?: + \b + (?: + void + | + (?: signed | unsigned ) # implies int + | + (?: + (?: (?: signed | unsigned ) \s+ )? + (?: (?: long | short ) \s+ )? + (?: char | short | int | long | float | double ) + ) + ) + \b + ) + # end simple type + ''') + +COMPOUND_TYPE_KIND = r'(?: \b (?: struct | union | enum ) \b )' + + +####################################### +# variable declarations + +STORAGE_CLASS = r'(?: \b (?: auto | register | static | extern ) \b )' +TYPE_QUALIFIER = r'(?: \b (?: const | volatile ) \b )' +PTR_QUALIFIER = rf'(?: [*] (?: \s* {TYPE_QUALIFIER} )? )' + +TYPE_SPEC = textwrap.dedent(rf''' + # type spec + (?: + {_ind(SIMPLE_TYPE, 2)} + | + (?: + [_]*typeof[_]* + \s* [(] + (?: \s* [*&] )* + \s* {STRICT_IDENTIFIER} + \s* [)] + ) + | + # reference to a compound type + (?: + {COMPOUND_TYPE_KIND} + (?: \s* {ANON_IDENTIFIER} )? + ) + | + # reference to a typedef + {STRICT_IDENTIFIER} + ) + # end type spec + ''') + +DECLARATOR = textwrap.dedent(rf''' + # declarator (possibly abstract) + (?: + (?: {PTR_QUALIFIER} \s* )* + (?: + (?: + (?: # + {STRICT_IDENTIFIER} + ) + (?: \s* \[ (?: \s* [^\]]+ \s* )? [\]] )* # arrays + ) + | + (?: + [(] \s* + (?: # + {STRICT_IDENTIFIER} + ) + (?: \s* \[ (?: \s* [^\]]+ \s* )? [\]] )* # arrays + \s* [)] + ) + | + # func ptr + (?: + [(] (?: \s* {PTR_QUALIFIER} )? \s* + (?: # + {STRICT_IDENTIFIER} + ) + (?: \s* \[ (?: \s* [^\]]+ \s* )? [\]] )* # arrays + \s* [)] + # We allow for a single level of paren nesting in parameters. + \s* [(] (?: [^()]* [(] [^)]* [)] )* [^)]* [)] + ) + ) + ) + # end declarator + ''') + +VAR_DECL = textwrap.dedent(rf''' + # var decl (and typedef and func return type) + (?: + (?: + (?: # + {STORAGE_CLASS} + ) + \s* + )? + (?: + (?: # + {TYPE_QUALIFIER} + ) + \s* + )? + (?: + (?: # + {_ind(TYPE_SPEC, 4)} + ) + ) + \s* + (?: + (?: # + {_ind(DECLARATOR, 4)} + ) + ) + ) + # end var decl + ''') + +INITIALIZER = textwrap.dedent(rf''' + # initializer + (?: + (?: + [(] + # no nested parens (e.g. func ptr) + [^)]* + [)] + \s* + )? + (?: + # a string literal + (?: + (?: {_ind(STRING_LITERAL, 4)} \s* )* + {_ind(STRING_LITERAL, 4)} + ) + | + + # a simple initializer + (?: + (?: + [^'",;{{]* + {_ind(STRING_LITERAL, 4)} + )* + [^'",;{{]* + ) + | + + # a struct/array literal + (?: + # We only expect compound initializers with + # single-variable declarations. + {{ + (?: + [^'";]*? + {_ind(STRING_LITERAL, 5)} + )* + [^'";]*? + }} + (?= \s* ; ) # Note this lookahead. + ) + ) + ) + # end initializer + ''') + + +####################################### +# compound type declarations + +STRUCT_MEMBER_DECL = textwrap.dedent(rf''' + (?: + # inline compound type decl + (?: + (?: # + {COMPOUND_TYPE_KIND} + ) + (?: + \s+ + (?: # + {STRICT_IDENTIFIER} + ) + )? + \s* {{ + ) + | + (?: + # typed member + (?: + # Technically it doesn't have to have a type... + (?: # + (?: {TYPE_QUALIFIER} \s* )? + {_ind(TYPE_SPEC, 5)} + ) + (?: + # If it doesn't have a declarator then it will have + # a size and vice versa. + \s* + (?: # + {_ind(DECLARATOR, 6)} + ) + )? + ) + + # sized member + (?: + \s* [:] \s* + (?: # + \d+ + ) + )? + \s* + (?: # + [,;] + ) + ) + | + (?: + \s* + (?: # + }} + ) + ) + ) + ''') + +ENUM_MEMBER_DECL = textwrap.dedent(rf''' + (?: + (?: + \s* + (?: # + }} + ) + ) + | + (?: + \s* + (?: # + {IDENTIFIER} + ) + (?: + \s* = \s* + (?: # + {_ind(STRING_LITERAL, 4)} + | + [^'",}}]+ + ) + )? + \s* + (?: # + , | }} + ) + ) + ) + ''') + + +####################################### +# statements + +SIMPLE_STMT_BODY = textwrap.dedent(rf''' + # simple statement body + (?: + (?: + [^'"{{}};]* + {_ind(STRING_LITERAL, 3)} + )* + [^'"{{}};]* + #(?= [;{{] ) # Note this lookahead. + ) + # end simple statement body + ''') +SIMPLE_STMT = textwrap.dedent(rf''' + # simple statement + (?: + (?: # + # stmt-inline "initializer" + (?: + return \b + (?: + \s* + {_ind(INITIALIZER, 5)} + )? + ) + | + # variable assignment + (?: + (?: [*] \s* )? + (?: + {STRICT_IDENTIFIER} \s* + (?: . | -> ) \s* + )* + {STRICT_IDENTIFIER} + (?: \s* \[ \s* \d+ \s* \] )? + \s* = \s* + {_ind(INITIALIZER, 4)} + ) + | + # catchall return statement + (?: + return \b + (?: + (?: + [^'";]* + {_ind(STRING_LITERAL, 6)} + )* + \s* [^'";]* + )? + ) + | + # simple statement + (?: + {_ind(SIMPLE_STMT_BODY, 4)} + ) + ) + \s* + (?: # + ; + ) + ) + # end simple statement + ''') +COMPOUND_STMT = textwrap.dedent(rf''' + # compound statement + (?: + \b + (?: + (?: + (?: # + else | do + ) + \b + ) + | + (?: + (?: # + (?: + case \b + (?: + [^'":]* + {_ind(STRING_LITERAL, 7)} + )* + \s* [^'":]* + ) + | + default + | + {STRICT_IDENTIFIER} + ) + \s* [:] + ) + | + (?: + (?: # + for | while | if | switch + ) + \s* (?= [(] ) # Note this lookahead. + ) + ) + \s* + ) + # end compound statement + ''') + + +####################################### +# function bodies + +LOCAL = textwrap.dedent(rf''' + (?: + # an empty statement + (?: # + ; + ) + | + # inline type decl + (?: + (?: + (?: # + [^;{{}}]+? + ) + \s* + )? + (?: # + (?: {STORAGE_CLASS} \s* )? + (?: {TYPE_QUALIFIER} \s* )? + )? # + (?: # + {COMPOUND_TYPE_KIND} + ) + (?: + \s+ + (?: # + {STRICT_IDENTIFIER} + ) + )? + \s* {{ + ) + | + # var decl + (?: + (?: # + {STORAGE_CLASS} + )? # + (?: + \s* + (?: # + {_ind(VAR_DECL, 5)} + ) + ) + (?: + (?: + # initializer + # We expect only basic initializers. + \s* = \s* + (?: # + {_ind(INITIALIZER, 6)} + ) + )? + (?: + \s* + (?: # + [,;] + ) + ) + ) + ) + | + {_ind(COMPOUND_STMT, 2)} + | + # start-of-block + (?: + (?: # + (?: + [^'"{{}};]* + {_ind(STRING_LITERAL, 5)} + )* + [^'"{{}};]* + # Presumably we will not see "== {{". + [^\s='"{{}});] + \s* + )? # + (?: # + {{ + ) + ) + | + {_ind(SIMPLE_STMT, 2)} + | + # end-of-block + (?: # + }} + ) + ) + ''') + +LOCAL_STATICS = textwrap.dedent(rf''' + (?: + # inline type decl + (?: + (?: + (?: # + [^;{{}}]+? + ) + \s* + )? + (?: # + (?: {STORAGE_CLASS} \s* )? + (?: {TYPE_QUALIFIER} \s* )? + )? + (?: # + {COMPOUND_TYPE_KIND} + ) + (?: + \s+ + (?: # + {STRICT_IDENTIFIER} + ) + )? + \s* {{ + ) + | + # var decl + (?: + # We only look for static variables. + (?: # + static \b + (?: \s* {TYPE_QUALIFIER} )? + \s* {_ind(TYPE_SPEC, 4)} + \s* {_ind(DECLARATOR, 4)} + ) + \s* + (?: + (?: # + = \s* + {_ind(INITIALIZER, 4)} + \s* + [,;{{] + ) + | + (?: # + [,;] + ) + ) + ) + | + # everything else + (?: + (?: # + (?: + [^'"{{}};]* + {_ind(STRING_LITERAL, 4)} + )* + \s* [^'"{{}};]* + ) + (?: + (?: # + {{ + ) + | + (?: # + }} + ) + | + (?: # + ; + ) + ) + ) + ) + ''') + + +####################################### +# global declarations + +GLOBAL = textwrap.dedent(rf''' + (?: + # an empty statement + (?: # + ; + ) + | + + # compound type decl (maybe inline) + (?: + (?: + (?: # + [^;{{}}]+? + ) + \s* + )? + (?: # + {COMPOUND_TYPE_KIND} + ) + (?: + \s+ + (?: # + {STRICT_IDENTIFIER} + ) + )? + \s* {{ + ) + | + # bogus inline decl artifact + # This simplifies resolving the relative syntactic ambiguity of + # inline structs. + (?: + (?: # + {COMPOUND_TYPE_KIND} + ) + \s* + (?: # + {ANON_IDENTIFIER} + ) + (?: # + [^=,;({{[*\]]* + [=,;({{] + ) + ) + | + + # typedef + (?: + \b typedef \b \s* + (?: # + {_ind(VAR_DECL, 4)} + ) + (?: + # We expect no inline type definitions in the parameters. + \s* [(] \s* + (?: # + [^{{;]* + ) + \s* [)] + )? + \s* ; + ) + | + + # func decl/definition & var decls + # XXX dedicated pattern for funcs (more restricted)? + (?: + (?: + (?: # + {STORAGE_CLASS} + ) + \s* + )? + (?: + (?: # + \b inline \b + ) + \s* + )? + (?: # + {_ind(VAR_DECL, 4)} + ) + (?: + # func decl / definition + (?: + (?: + # We expect no inline type definitions in the parameters. + \s* [(] \s* + (?: # + [^{{;]* + ) + \s* [)] \s* + (?: # + [{{;] + ) + ) + | + (?: + # This is some old-school syntax! + \s* [(] \s* + # We throw away the bare names: + {STRICT_IDENTIFIER} + (?: \s* , \s* {STRICT_IDENTIFIER} )* + \s* [)] \s* + + # We keep the trailing param declarations: + (?: # + # There's at least one! + (?: {TYPE_QUALIFIER} \s* )? + {_ind(TYPE_SPEC, 7)} + \s* + {_ind(DECLARATOR, 7)} + \s* ; + (?: + \s* + (?: {TYPE_QUALIFIER} \s* )? + {_ind(TYPE_SPEC, 8)} + \s* + {_ind(DECLARATOR, 8)} + \s* ; + )* + ) + \s* {{ + ) + ) + | + # var / typedef + (?: + (?: + # initializer + # We expect only basic initializers. + \s* = \s* + (?: # + {_ind(INITIALIZER, 6)} + ) + )? + \s* + (?: # + [,;] + ) + ) + ) + ) + ) + ''') diff --git a/Tools/c-analyzer/c_parser/preprocessor/__init__.py b/Tools/c-analyzer/c_parser/preprocessor/__init__.py new file mode 100644 index 0000000000000..f206f694db5a8 --- /dev/null +++ b/Tools/c-analyzer/c_parser/preprocessor/__init__.py @@ -0,0 +1,190 @@ +import contextlib +import distutils.ccompiler +import logging +import os.path + +from c_common.fsutil import match_glob as _match_glob +from c_common.tables import parse_table as _parse_table +from ..source import ( + resolve as _resolve_source, + good_file as _good_file, +) +from . import errors as _errors +from . import ( + pure as _pure, + gcc as _gcc, +) + + +logger = logging.getLogger(__name__) + + +# Supprted "source": +# * filename (string) +# * lines (iterable) +# * text (string) +# Supported return values: +# * iterator of SourceLine +# * sequence of SourceLine +# * text (string) +# * something that combines all those +# XXX Add the missing support from above. +# XXX Add more low-level functions to handle permutations? + +def preprocess(source, *, + incldirs=None, + macros=None, + samefiles=None, + filename=None, + tool=True, + ): + """... + + CWD should be the project root and "source" should be relative. + """ + if tool: + logger.debug(f'CWD: {os.getcwd()!r}') + logger.debug(f'incldirs: {incldirs!r}') + logger.debug(f'macros: {macros!r}') + logger.debug(f'samefiles: {samefiles!r}') + _preprocess = _get_preprocessor(tool) + with _good_file(source, filename) as source: + return _preprocess(source, incldirs, macros, samefiles) or () + else: + source, filename = _resolve_source(source, filename) + # We ignore "includes", "macros", etc. + return _pure.preprocess(source, filename) + + # if _run() returns just the lines: +# text = _run(source) +# lines = [line + os.linesep for line in text.splitlines()] +# lines[-1] = lines[-1].splitlines()[0] +# +# conditions = None +# for lno, line in enumerate(lines, 1): +# kind = 'source' +# directive = None +# data = line +# yield lno, kind, data, conditions + + +def get_preprocessor(*, + file_macros=None, + file_incldirs=None, + file_same=None, + ignore_exc=False, + log_err=None, + ): + _preprocess = preprocess + if file_macros: + file_macros = tuple(_parse_macros(file_macros)) + if file_incldirs: + file_incldirs = tuple(_parse_incldirs(file_incldirs)) + if file_same: + file_same = tuple(file_same) + if not callable(ignore_exc): + ignore_exc = (lambda exc, _ig=ignore_exc: _ig) + + def get_file_preprocessor(filename): + filename = filename.strip() + if file_macros: + macros = list(_resolve_file_values(filename, file_macros)) + if file_incldirs: + incldirs = [v for v, in _resolve_file_values(filename, file_incldirs)] + + def preprocess(**kwargs): + if file_macros and 'macros' not in kwargs: + kwargs['macros'] = macros + if file_incldirs and 'incldirs' not in kwargs: + kwargs['incldirs'] = [v for v, in _resolve_file_values(filename, file_incldirs)] + if file_same and 'file_same' not in kwargs: + kwargs['samefiles'] = file_same + kwargs.setdefault('filename', filename) + with handling_errors(ignore_exc, log_err=log_err): + return _preprocess(filename, **kwargs) + return preprocess + return get_file_preprocessor + + +def _resolve_file_values(filename, file_values): + # We expect the filename and all patterns to be absolute paths. + for pattern, *value in file_values or (): + if _match_glob(filename, pattern): + yield value + + +def _parse_macros(macros): + for row, srcfile in _parse_table(macros, '\t', 'glob\tname\tvalue', rawsep='=', default=None): + yield row + + +def _parse_incldirs(incldirs): + for row, srcfile in _parse_table(incldirs, '\t', 'glob\tdirname', default=None): + glob, dirname = row + if dirname is None: + # Match all files. + dirname = glob + row = ('*', dirname.strip()) + yield row + + + at contextlib.contextmanager +def handling_errors(ignore_exc=None, *, log_err=None): + try: + yield + except _errors.OSMismatchError as exc: + if not ignore_exc(exc): + raise # re-raise + if log_err is not None: + log_err(f'') + return None + except _errors.MissingDependenciesError as exc: + if not ignore_exc(exc): + raise # re-raise + if log_err is not None: + log_err(f'4} {line.kind:10} | {text}') + + filenames = main_for_filenames(filenames, iter_filenames) + for filename in filenames: + lines = _iter_preprocessed(filename, **kwargs) + show_file(filename, lines) + + +def _cli_data(parser): + ... + + return None + + +def cmd_data(filenames, + **kwargs + ): + # XXX + raise NotImplementedError + + +COMMANDS = { + 'preprocess': ( + 'preprocess the given C source & header files', + [_cli_preprocess], + cmd_preprocess, + ), + 'data': ( + 'check/manage local data (e.g. excludes, macros)', + [_cli_data], + cmd_data, + ), +} + + +####################################### +# the script + +def parse_args(argv=sys.argv[1:], prog=sys.argv[0], *, + subset='preprocess', + excluded=None, + **prepr_kwargs + ): + import argparse + parser = argparse.ArgumentParser( + prog=prog or get_prog(), + ) + + processors = add_commands_cli( + parser, + commands={k: v[1] for k, v in COMMANDS.items()}, + commonspecs=[ + add_verbosity_cli, + add_traceback_cli, + ], + subset=subset, + ) + + args = parser.parse_args(argv) + ns = vars(args) + + cmd = ns.pop('cmd') + + verbosity, traceback_cm = process_args_by_key( + args, + processors[cmd], + ['verbosity', 'traceback_cm'], + ) + + return cmd, ns, verbosity, traceback_cm + + +def main(cmd, cmd_kwargs): + try: + run_cmd = COMMANDS[cmd][0] + except KeyError: + raise ValueError(f'unsupported cmd {cmd!r}') + run_cmd(**cmd_kwargs) + + +if __name__ == '__main__': + cmd, cmd_kwargs, verbosity, traceback_cm = parse_args() + configure_logger(verbosity) + with traceback_cm: + main(cmd, cmd_kwargs) diff --git a/Tools/c-analyzer/c_parser/preprocessor/common.py b/Tools/c-analyzer/c_parser/preprocessor/common.py new file mode 100644 index 0000000000000..63681025c63d4 --- /dev/null +++ b/Tools/c-analyzer/c_parser/preprocessor/common.py @@ -0,0 +1,173 @@ +import contextlib +import distutils.ccompiler +import logging +import shlex +import subprocess +import sys + +from ..info import FileInfo, SourceLine +from .errors import ( + PreprocessorFailure, + ErrorDirectiveError, + MissingDependenciesError, + OSMismatchError, +) + + +logger = logging.getLogger(__name__) + + +# XXX Add aggregate "source" class(es)? +# * expose all lines as single text string +# * expose all lines as sequence +# * iterate all lines + + +def run_cmd(argv, *, + #capture_output=True, + stdout=subprocess.PIPE, + #stderr=subprocess.STDOUT, + stderr=subprocess.PIPE, + text=True, + check=True, + **kwargs + ): + if isinstance(stderr, str) and stderr.lower() == 'stdout': + stderr = subprocess.STDOUT + + kw = dict(locals()) + kw.pop('argv') + kw.pop('kwargs') + kwargs.update(kw) + + proc = subprocess.run(argv, **kwargs) + return proc.stdout + + +def preprocess(tool, filename, **kwargs): + argv = _build_argv(tool, filename, **kwargs) + logger.debug(' '.join(shlex.quote(v) for v in argv)) + + # Make sure the OS is supported for this file. + if (_expected := is_os_mismatch(filename)): + error = None + raise OSMismatchError(filename, _expected, argv, error, TOOL) + + # Run the command. + with converted_error(tool, argv, filename): + # We use subprocess directly here, instead of calling the + # distutil compiler object's preprocess() method, since that + # one writes to stdout/stderr and it's simpler to do it directly + # through subprocess. + return run_cmd(argv) + + +def _build_argv( + tool, + filename, + incldirs=None, + macros=None, + preargs=None, + postargs=None, + executable=None, + compiler=None, +): + compiler = distutils.ccompiler.new_compiler( + compiler=compiler or tool, + ) + if executable: + compiler.set_executable('preprocessor', executable) + + argv = None + def _spawn(_argv): + nonlocal argv + argv = _argv + compiler.spawn = _spawn + compiler.preprocess( + filename, + macros=[tuple(v) for v in macros or ()], + include_dirs=incldirs or (), + extra_preargs=preargs or (), + extra_postargs=postargs or (), + ) + return argv + + + at contextlib.contextmanager +def converted_error(tool, argv, filename): + try: + yield + except subprocess.CalledProcessError as exc: + convert_error( + tool, + argv, + filename, + exc.stderr, + exc.returncode, + ) + + +def convert_error(tool, argv, filename, stderr, rc): + error = (stderr.splitlines()[0], rc) + if (_expected := is_os_mismatch(filename, stderr)): + logger.debug(stderr.strip()) + raise OSMismatchError(filename, _expected, argv, error, tool) + elif (_missing := is_missing_dep(stderr)): + logger.debug(stderr.strip()) + raise MissingDependenciesError(filename, (_missing,), argv, error, tool) + elif '#error' in stderr: + # XXX Ignore incompatible files. + error = (stderr.splitlines()[1], rc) + logger.debug(stderr.strip()) + raise ErrorDirectiveError(filename, argv, error, tool) + else: + # Try one more time, with stderr written to the terminal. + try: + output = run_cmd(argv, stderr=None) + except subprocess.CalledProcessError: + raise PreprocessorFailure(filename, argv, error, tool) + + +def is_os_mismatch(filename, errtext=None): + # See: https://docs.python.org/3/library/sys.html#sys.platform + actual = sys.platform + if actual == 'unknown': + raise NotImplementedError + + if errtext is not None: + if (missing := is_missing_dep(errtext)): + matching = get_matching_oses(missing, filename) + if actual not in matching: + return matching + return False + + +def get_matching_oses(missing, filename): + # OSX + if 'darwin' in filename or 'osx' in filename: + return ('darwin',) + elif missing == 'SystemConfiguration/SystemConfiguration.h': + return ('darwin',) + + # Windows + elif missing in ('windows.h', 'winsock2.h'): + return ('win32',) + + # other + elif missing == 'sys/ldr.h': + return ('aix',) + elif missing == 'dl.h': + # XXX The existence of Python/dynload_dl.c implies others... + # Note that hpux isn't actual supported any more. + return ('hpux', '???') + + # unrecognized + else: + return () + + +def is_missing_dep(errtext): + if 'No such file or directory' in errtext: + missing = errtext.split(': No such file or directory')[0].split()[-1] + return missing + return False diff --git a/Tools/c-analyzer/c_parser/preprocessor/errors.py b/Tools/c-analyzer/c_parser/preprocessor/errors.py new file mode 100644 index 0000000000000..9b66801d630a6 --- /dev/null +++ b/Tools/c-analyzer/c_parser/preprocessor/errors.py @@ -0,0 +1,110 @@ +import sys + + +OS = sys.platform + + +def _as_tuple(items): + if isinstance(items, str): + return tuple(items.strip().replace(',', ' ').split()) + elif items: + return tuple(items) + else: + return () + + +class PreprocessorError(Exception): + """Something preprocessor-related went wrong.""" + + @classmethod + def _msg(cls, filename, reason, **ignored): + msg = 'failure while preprocessing' + if reason: + msg = f'{msg} ({reason})' + return msg + + def __init__(self, filename, preprocessor=None, reason=None): + if isinstance(reason, str): + reason = reason.strip() + + self.filename = filename + self.preprocessor = preprocessor or None + self.reason = str(reason) if reason else None + + msg = self._msg(**vars(self)) + msg = f'({filename}) {msg}' + if preprocessor: + msg = f'[{preprocessor}] {msg}' + super().__init__(msg) + + +class PreprocessorFailure(PreprocessorError): + """The preprocessor command failed.""" + + @classmethod + def _msg(cls, error, **ignored): + msg = 'preprocessor command failed' + if error: + msg = f'{msg} {error}' + return msg + + def __init__(self, filename, argv, error=None, preprocessor=None): + exitcode = -1 + if isinstance(error, tuple): + if len(error) == 2: + error, exitcode = error + else: + error = str(error) + if isinstance(error, str): + error = error.strip() + + self.argv = _as_tuple(argv) or None + self.error = error if error else None + self.exitcode = exitcode + + reason = str(self.error) + super().__init__(filename, preprocessor, reason) + + +class ErrorDirectiveError(PreprocessorFailure): + """The file hit a #error directive.""" + + @classmethod + def _msg(cls, error, **ignored): + return f'#error directive hit ({error})' + + def __init__(self, filename, argv, error, *args, **kwargs): + super().__init__(filename, argv, error, *args, **kwargs) + + +class MissingDependenciesError(PreprocessorFailure): + """The preprocessor did not have access to all the target's dependencies.""" + + @classmethod + def _msg(cls, missing, **ignored): + msg = 'preprocessing failed due to missing dependencies' + if missing: + msg = f'{msg} ({", ".join(missing)})' + return msg + + def __init__(self, filename, missing=None, *args, **kwargs): + self.missing = _as_tuple(missing) or None + + super().__init__(filename, *args, **kwargs) + + +class OSMismatchError(MissingDependenciesError): + """The target is not compatible with the host OS.""" + + @classmethod + def _msg(cls, expected, **ignored): + return f'OS is {OS} but expected {expected or "???"}' + + def __init__(self, filename, expected=None, *args, **kwargs): + if isinstance(expected, str): + expected = expected.strip() + + self.actual = OS + self.expected = expected if expected else None + + super().__init__(filename, None, *args, **kwargs) diff --git a/Tools/c-analyzer/c_parser/preprocessor/gcc.py b/Tools/c-analyzer/c_parser/preprocessor/gcc.py new file mode 100644 index 0000000000000..bb404a487b735 --- /dev/null +++ b/Tools/c-analyzer/c_parser/preprocessor/gcc.py @@ -0,0 +1,123 @@ +import os.path +import re + +from . import common as _common + + +TOOL = 'gcc' + +# https://gcc.gnu.org/onlinedocs/cpp/Preprocessor-Output.html +LINE_MARKER_RE = re.compile(r'^# (\d+) "([^"]+)"(?: [1234])*$') +PREPROC_DIRECTIVE_RE = re.compile(r'^\s*#\s*(\w+)\b.*') +COMPILER_DIRECTIVE_RE = re.compile(r''' + ^ + (.*?) # + (__\w+__) # + \s* + [(] [(] + ( + [^()]* + (?: + [(] + [^()]* + [)] + [^()]* + )* + ) # + ( [)] [)] )? # +''', re.VERBOSE) + +POST_ARGS = ( + '-pthread', + '-std=c99', + #'-g', + #'-Og', + #'-Wno-unused-result', + #'-Wsign-compare', + #'-Wall', + #'-Wextra', + '-E', +) + + +def preprocess(filename, incldirs=None, macros=None, samefiles=None): + text = _common.preprocess( + TOOL, + filename, + incldirs=incldirs, + macros=macros, + #preargs=PRE_ARGS, + postargs=POST_ARGS, + executable=['gcc'], + compiler='unix', + ) + return _iter_lines(text, filename, samefiles) + + +def _iter_lines(text, filename, samefiles, *, raw=False): + lines = iter(text.splitlines()) + + # Build the lines and filter out directives. + partial = 0 # depth + origfile = None + for line in lines: + m = LINE_MARKER_RE.match(line) + if m: + lno, origfile = m.groups() + lno = int(lno) + elif _filter_orig_file(origfile, filename, samefiles): + if (m := PREPROC_DIRECTIVE_RE.match(line)): + name, = m.groups() + if name != 'pragma': + raise Exception(line) + else: + if not raw: + line, partial = _strip_directives(line, partial=partial) + yield _common.SourceLine( + _common.FileInfo(filename, lno), + 'source', + line or '', + None, + ) + lno += 1 + + +def _strip_directives(line, partial=0): + # We assume there are no string literals with parens in directive bodies. + while partial > 0: + if not (m := re.match(r'[^{}]*([()])', line)): + return None, partial + delim, = m.groups() + partial += 1 if delim == '(' else -1 # opened/closed + line = line[m.end():] + + line = re.sub(r'__extension__', '', line) + + while (m := COMPILER_DIRECTIVE_RE.match(line)): + before, _, _, closed = m.groups() + if closed: + line = f'{before} {line[m.end():]}' + else: + after, partial = _strip_directives(line[m.end():], 2) + line = f'{before} {after or ""}' + if partial: + break + + return line, partial + + +def _filter_orig_file(origfile, current, samefiles): + if origfile == current: + return True + if origfile == '': + return True + if os.path.isabs(origfile): + return False + + for filename in samefiles or (): + if filename.endswith(os.path.sep): + filename += os.path.basename(current) + if origfile == filename: + return True + + return False diff --git a/Tools/c-analyzer/c_parser/preprocessor/pure.py b/Tools/c-analyzer/c_parser/preprocessor/pure.py new file mode 100644 index 0000000000000..e971389b1888d --- /dev/null +++ b/Tools/c-analyzer/c_parser/preprocessor/pure.py @@ -0,0 +1,23 @@ +from ..source import ( + opened as _open_source, +) +from . import common as _common + + +def preprocess(lines, filename=None): + if isinstance(lines, str): + with _open_source(lines, filename) as (lines, filename): + yield from preprocess(lines, filename) + return + + # XXX actually preprocess... + for lno, line in enumerate(lines, 1): + kind = 'source' + data = line + conditions = None + yield _common.SourceLine( + _common.FileInfo(filename, lno), + kind, + data, + conditions, + ) diff --git a/Tools/c-analyzer/c_parser/source.py b/Tools/c-analyzer/c_parser/source.py new file mode 100644 index 0000000000000..30a09eeb56a1f --- /dev/null +++ b/Tools/c-analyzer/c_parser/source.py @@ -0,0 +1,64 @@ +import contextlib +import os.path + + +def resolve(source, filename): + if _looks_like_filename(source): + return _resolve_filename(source, filename) + + if isinstance(source, str): + source = source.splitlines() + + # At this point "source" is not a str. + if not filename: + filename = None + elif not isinstance(filename, str): + raise TypeError(f'filename should be str (or None), got {filename!r}') + else: + filename, _ = _resolve_filename(filename) + return source, filename + + + at contextlib.contextmanager +def good_file(filename, alt=None): + if not _looks_like_filename(filename): + raise ValueError(f'expected a filename, got {filename}') + filename, _ = _resolve_filename(filename, alt) + try: + yield filename + except Exception: + if not os.path.exists(filename): + raise FileNotFoundError(f'file not found: {filename}') + raise # re-raise + + +def _looks_like_filename(value): + if not isinstance(value, str): + return False + return value.endswith(('.c', '.h')) + + +def _resolve_filename(filename, alt=None): + if os.path.isabs(filename): + ... +# raise NotImplementedError + else: + filename = os.path.join('.', filename) + + if not alt: + alt = filename + elif os.path.abspath(filename) == os.path.abspath(alt): + alt = filename + else: + raise ValueError(f'mismatch: {filename} != {alt}') + return filename, alt + + + at contextlib.contextmanager +def opened(source, filename=None): + source, filename = resolve(source, filename) + if isinstance(source, str): + with open(source) as srcfile: + yield srcfile, filename + else: + yield source, filename diff --git a/Tools/c-analyzer/check-c-globals.py b/Tools/c-analyzer/check-c-globals.py index 1371f92742327..3fe2bdcae1460 100644 --- a/Tools/c-analyzer/check-c-globals.py +++ b/Tools/c-analyzer/check-c-globals.py @@ -1,448 +1,35 @@ +from cpython.__main__ import main, configure_logger -from collections import namedtuple -import glob -import os.path -import re -import shutil -import sys -import subprocess - - -VERBOSITY = 2 - -C_GLOBALS_DIR = os.path.abspath(os.path.dirname(__file__)) -TOOLS_DIR = os.path.dirname(C_GLOBALS_DIR) -ROOT_DIR = os.path.dirname(TOOLS_DIR) -GLOBALS_FILE = os.path.join(C_GLOBALS_DIR, 'ignored-globals.txt') - -SOURCE_DIRS = ['Include', 'Objects', 'Modules', 'Parser', 'Python'] - -CAPI_REGEX = re.compile(r'^ *PyAPI_DATA\([^)]*\) \W*(_?Py\w+(?:, \w+)*\w).*;.*$') - - -IGNORED_VARS = { - '_DYNAMIC', - '_GLOBAL_OFFSET_TABLE_', - '__JCR_LIST__', - '__JCR_END__', - '__TMC_END__', - '__bss_start', - '__data_start', - '__dso_handle', - '_edata', - '_end', - } - - -def find_capi_vars(root): - capi_vars = {} - for dirname in SOURCE_DIRS: - for filename in glob.glob(os.path.join( - glob.escape(os.path.join(ROOT_DIR, dirname)), - '**/*.[hc]'), - recursive=True): - with open(filename) as file: - for name in _find_capi_vars(file): - if name in capi_vars: - assert not filename.endswith('.c') - assert capi_vars[name].endswith('.c') - capi_vars[name] = filename - return capi_vars - - -def _find_capi_vars(lines): - for line in lines: - if not line.startswith('PyAPI_DATA'): - continue - assert '{' not in line - match = CAPI_REGEX.match(line) - assert match - names, = match.groups() - for name in names.split(', '): - yield name - - -def _read_global_names(filename): - # These variables are shared between all interpreters in the process. - with open(filename) as file: - return {line.partition('#')[0].strip() - for line in file - if line.strip() and not line.startswith('#')} - - -def _is_global_var(name, globalnames): - if _is_autogen_var(name): - return True - if _is_type_var(name): - return True - if _is_module(name): - return True - if _is_exception(name): - return True - if _is_compiler(name): - return True - return name in globalnames - - -def _is_autogen_var(name): - return ( - name.startswith('PyId_') or - '.' in name or - # Objects/typeobject.c - name.startswith('op_id.') or - name.startswith('rop_id.') or - # Python/graminit.c - name.startswith('arcs_') or - name.startswith('states_') - ) - - -def _is_type_var(name): - if name.endswith(('Type', '_Type', '_type')): # XXX Always a static type? - return True - if name.endswith('_desc'): # for structseq types - return True - return ( - name.startswith('doc_') or - name.endswith(('_doc', '__doc__', '_docstring')) or - name.endswith('_methods') or - name.endswith('_fields') or - name.endswith(('_memberlist', '_members')) or - name.endswith('_slots') or - name.endswith(('_getset', '_getsets', '_getsetlist')) or - name.endswith('_as_mapping') or - name.endswith('_as_number') or - name.endswith('_as_sequence') or - name.endswith('_as_buffer') or - name.endswith('_as_async') - ) - - -def _is_module(name): - if name.endswith(('_functions', 'Methods', '_Methods')): - return True - if name == 'module_def': - return True - if name == 'initialized': - return True - return name.endswith(('module', '_Module')) - - -def _is_exception(name): - # Other vars are enumerated in globals-core.txt. - if not name.startswith(('PyExc_', '_PyExc_')): - return False - return name.endswith(('Error', 'Warning')) - - -def _is_compiler(name): - return ( - # Python/Python-ast.c - name.endswith('_type') or - name.endswith('_singleton') or - name.endswith('_attributes') - ) - - -class Var(namedtuple('Var', 'name kind scope capi filename')): - - @classmethod - def parse_nm(cls, line, expected, ignored, capi_vars, globalnames): - _, _, line = line.partition(' ') # strip off the address - line = line.strip() - kind, _, line = line.partition(' ') - if kind in ignored or (): - return None - elif kind not in expected or (): - raise RuntimeError('unsupported NM type {!r}'.format(kind)) - - name, _, filename = line.partition('\t') - name = name.strip() - if _is_autogen_var(name): - return None - if _is_global_var(name, globalnames): - scope = 'global' - else: - scope = None - capi = (name in capi_vars or ()) - if filename: - filename = os.path.relpath(filename.partition(':')[0]) - return cls(name, kind, scope, capi, filename or '~???~') - - @property - def external(self): - return self.kind.isupper() - - -def find_vars(root, globals_filename=GLOBALS_FILE): - python = os.path.join(root, 'python') - if not os.path.exists(python): - raise RuntimeError('python binary missing (need to build it first?)') - capi_vars = find_capi_vars(root) - globalnames = _read_global_names(globals_filename) - - nm = shutil.which('nm') - if nm is None: - # XXX Use dumpbin.exe /SYMBOLS on Windows. - raise NotImplementedError - else: - yield from (var - for var in _find_var_symbols(python, nm, capi_vars, - globalnames) - if var.name not in IGNORED_VARS) - - -NM_FUNCS = set('Tt') -NM_PUBLIC_VARS = set('BD') -NM_PRIVATE_VARS = set('bd') -NM_VARS = NM_PUBLIC_VARS | NM_PRIVATE_VARS -NM_DATA = set('Rr') -NM_OTHER = set('ACGgiINpSsuUVvWw-?') -NM_IGNORED = NM_FUNCS | NM_DATA | NM_OTHER - - -def _find_var_symbols(python, nm, capi_vars, globalnames): - args = [nm, - '--line-numbers', - python] - out = subprocess.check_output(args) - for line in out.decode('utf-8').splitlines(): - var = Var.parse_nm(line, NM_VARS, NM_IGNORED, capi_vars, globalnames) - if var is None: - continue - yield var - - -####################################### - -class Filter(namedtuple('Filter', 'name op value action')): - - @classmethod - def parse(cls, raw): - action = '+' - if raw.startswith(('+', '-')): - action = raw[0] - raw = raw[1:] - # XXX Support < and >? - name, op, value = raw.partition('=') - return cls(name, op, value, action) - - def check(self, var): - value = getattr(var, self.name, None) - if not self.op: - matched = bool(value) - elif self.op == '=': - matched = (value == self.value) - else: - raise NotImplementedError - - if self.action == '+': - return matched - elif self.action == '-': - return not matched - else: - raise NotImplementedError - - -def filter_var(var, filters): - for filter in filters: - if not filter.check(var): - return False - return True - - -def make_sort_key(spec): - columns = [(col.strip('_'), '_' if col.startswith('_') else '') - for col in spec] - def sort_key(var): - return tuple(getattr(var, col).lstrip(prefix) - for col, prefix in columns) - return sort_key - - -def make_groups(allvars, spec): - group = spec - groups = {} - for var in allvars: - value = getattr(var, group) - key = '{}: {}'.format(group, value) - try: - groupvars = groups[key] - except KeyError: - groupvars = groups[key] = [] - groupvars.append(var) - return groups - - -def format_groups(groups, columns, fmts, widths): - for group in sorted(groups): - groupvars = groups[group] - yield '', 0 - yield ' # {}'.format(group), 0 - yield from format_vars(groupvars, columns, fmts, widths) - - -def format_vars(allvars, columns, fmts, widths): - fmt = ' '.join(fmts[col] for col in columns) - fmt = ' ' + fmt.replace(' ', ' ') + ' ' # for div margin - header = fmt.replace(':', ':^').format(*(col.upper() for col in columns)) - yield header, 0 - div = ' '.join('-'*(widths[col]+2) for col in columns) - yield div, 0 - for var in allvars: - values = (getattr(var, col) for col in columns) - row = fmt.format(*('X' if val is True else val or '' - for val in values)) - yield row, 1 - yield div, 0 - - -####################################### - -COLUMNS = 'name,external,capi,scope,filename' -COLUMN_NAMES = COLUMNS.split(',') - -COLUMN_WIDTHS = {col: len(col) - for col in COLUMN_NAMES} -COLUMN_WIDTHS.update({ - 'name': 50, - 'scope': 7, - 'filename': 40, - }) -COLUMN_FORMATS = {col: '{:%s}' % width - for col, width in COLUMN_WIDTHS.items()} -for col in COLUMN_FORMATS: - if COLUMN_WIDTHS[col] == len(col): - COLUMN_FORMATS[col] = COLUMN_FORMATS[col].replace(':', ':^') - - -def _parse_filters_arg(raw, error): - filters = [] - for value in raw.split(','): - value=value.strip() - if not value: - continue - try: - filter = Filter.parse(value) - if filter.name not in COLUMN_NAMES: - raise Exception('unsupported column {!r}'.format(filter.name)) - except Exception as e: - error('bad filter {!r}: {}'.format(raw, e)) - filters.append(filter) - return filters - - -def _parse_columns_arg(raw, error): - columns = raw.split(',') - for column in columns: - if column not in COLUMN_NAMES: - error('unsupported column {!r}'.format(column)) - return columns - - -def _parse_sort_arg(raw, error): - sort = raw.split(',') - for column in sort: - if column.lstrip('_') not in COLUMN_NAMES: - error('unsupported column {!r}'.format(column)) - return sort - - -def _parse_group_arg(raw, error): - if not raw: - return raw - group = raw - if group not in COLUMN_NAMES: - error('unsupported column {!r}'.format(group)) - if group != 'filename': - error('unsupported group {!r}'.format(group)) - return group - - -def parse_args(argv=None): - if argv is None: - argv = sys.argv[1:] +def parse_args(): import argparse + from c_common.scriptutil import ( + add_verbosity_cli, + add_traceback_cli, + process_args_by_key, + ) + from cpython.__main__ import _cli_check parser = argparse.ArgumentParser() + processors = [ + add_verbosity_cli(parser), + add_traceback_cli(parser), + _cli_check(parser, checks=''), + ] - parser.add_argument('-v', '--verbose', action='count', default=0) - parser.add_argument('-q', '--quiet', action='count', default=0) - - parser.add_argument('--filters', default='-scope', - help='[[-][=]] ...') - - parser.add_argument('--columns', default=COLUMNS, - help='a comma-separated list of columns to show') - parser.add_argument('--sort', default='filename,_name', - help='a comma-separated list of columns to sort') - parser.add_argument('--group', - help='group by the given column name (- to not group)') - - parser.add_argument('--rc-on-match', dest='rc', type=int) - - parser.add_argument('filename', nargs='?', default=GLOBALS_FILE) - - args = parser.parse_args(argv) - - verbose = vars(args).pop('verbose', 0) - quiet = vars(args).pop('quiet', 0) - args.verbosity = max(0, VERBOSITY + verbose - quiet) - - if args.sort.startswith('filename') and not args.group: - args.group = 'filename' - - if args.rc is None: - if '-scope=core' in args.filters or 'core' not in args.filters: - args.rc = 0 - else: - args.rc = 1 - - args.filters = _parse_filters_arg(args.filters, parser.error) - args.columns = _parse_columns_arg(args.columns, parser.error) - args.sort = _parse_sort_arg(args.sort, parser.error) - args.group = _parse_group_arg(args.group, parser.error) - - return args - - -def main(root=ROOT_DIR, filename=GLOBALS_FILE, - filters=None, columns=COLUMN_NAMES, sort=None, group=None, - verbosity=VERBOSITY, rc=1): - - log = lambda msg: ... - if verbosity >= 2: - log = lambda msg: print(msg) - - allvars = (var - for var in find_vars(root, filename) - if filter_var(var, filters)) - if sort: - allvars = sorted(allvars, key=make_sort_key(sort)) - - if group: - try: - columns.remove(group) - except ValueError: - pass - grouped = make_groups(allvars, group) - lines = format_groups(grouped, columns, COLUMN_FORMATS, COLUMN_WIDTHS) - else: - lines = format_vars(allvars, columns, COLUMN_FORMATS, COLUMN_WIDTHS) + args = parser.parse_args() + ns = vars(args) - total = 0 - for line, count in lines: - total += count - log(line) - log('\ntotal: {}'.format(total)) + cmd = 'check' + verbosity, traceback_cm = process_args_by_key( + args, + processors, + ['verbosity', 'traceback_cm'], + ) - if total and rc: - print('ERROR: found unsafe globals', file=sys.stderr) - return rc - return 0 + return cmd, ns, verbosity, traceback_cm -if __name__ == '__main__': - args = parse_args() - sys.exit( - main(**vars(args))) +(cmd, cmd_kwargs, verbosity, traceback_cm) = parse_args() +configure_logger(verbosity) +with traceback_cm: + main(cmd, cmd_kwargs) diff --git a/Tools/c-analyzer/cpython/README b/Tools/c-analyzer/cpython/README deleted file mode 100644 index 772b8be27008b..0000000000000 --- a/Tools/c-analyzer/cpython/README +++ /dev/null @@ -1,72 +0,0 @@ -####################################### -# C Globals and CPython Runtime State. - -CPython's C code makes extensive use of global variables (whether static -globals or static locals). Each such variable falls into one of several -categories: - -* strictly const data -* used exclusively in main or in the REPL -* process-global state (e.g. managing process-level resources - like signals and file descriptors) -* Python "global" runtime state -* per-interpreter runtime state - -The last one can be a problem as soon as anyone creates a second -interpreter (AKA "subinterpreter") in a process. It is definitely a -problem under subinterpreters if they are no longer sharing the GIL, -since the GIL protects us from a lot of race conditions. Keep in mind -that ultimately *all* objects (PyObject) should be treated as -per-interpreter state. This includes "static types", freelists, -_PyIdentifier, and singletons. Take that in for a second. It has -significant implications on where we use static variables! - -Be aware that module-global state (stored in C statics) is a kind of -per-interpreter state. There have been efforts across many years, and -still going, to provide extension module authors mechanisms to store -that state safely (see PEPs 3121, 489, etc.). - -(Note that there has been discussion around support for running multiple -Python runtimes in the same process. That would ends up with the same -problems, relative to static variables, that subinterpreters have.) - -Historically we have been bad at keeping per-interpreter state out of -static variables, mostly because until recently subinterpreters were -not widely used nor even factored in to solutions. However, the -feature is growing in popularity and use in the community. - -Mandate: "Eliminate use of static variables for per-interpreter state." - -The "c-statics.py" script in this directory, along with its accompanying -data files, are part of the effort to resolve existing problems with -our use of static variables and to prevent future problems. - -#------------------------- -## statics for actually-global state (and runtime state consolidation) - -In general, holding any kind of state in static variables -increases maintenance burden and increases the complexity of code (e.g. -we use TSS to identify the active thread state). So it is a good idea -to avoid using statics for state even if for the "global" runtime or -for process-global state. - -Relative to maintenance burden, one problem is where the runtime -state is spread throughout the codebase in dozens of individual -globals. Unlike the other globals, the runtime state represents a set -of values that are constantly shifting in a complex way. When they are -spread out it's harder to get a clear picture of what the runtime -involves. Furthermore, when they are spread out it complicates efforts -that change the runtime. - -Consequently, the globals for Python's runtime state have been -consolidated under a single top-level _PyRuntime global. No new globals -should be added for runtime state. Instead, they should be added to -_PyRuntimeState or one of its sub-structs. The tools in this directory -are run as part of the test suite to ensure that no new globals have -been added. The script can be run manually as well: - - ./python Lib/test/test_c_statics/c-statics.py check - -If it reports any globals then they should be resolved. If the globals -are runtime state then they should be folded into _PyRuntimeState. -Otherwise they should be marked as ignored. diff --git a/Tools/c-analyzer/cpython/__init__.py b/Tools/c-analyzer/cpython/__init__.py index ae45b424e3cc8..d0b3eff3c4b86 100644 --- a/Tools/c-analyzer/cpython/__init__.py +++ b/Tools/c-analyzer/cpython/__init__.py @@ -1,29 +1,20 @@ import os.path -import sys -TOOL_ROOT = os.path.abspath( +TOOL_ROOT = os.path.normcase( + os.path.abspath( os.path.dirname( # c-analyzer/ - os.path.dirname(__file__))) # cpython/ -DATA_DIR = TOOL_ROOT + os.path.dirname(__file__)))) # cpython/ REPO_ROOT = ( os.path.dirname( # .. os.path.dirname(TOOL_ROOT))) # Tools/ INCLUDE_DIRS = [os.path.join(REPO_ROOT, name) for name in [ - 'Include', - ]] + 'Include', +]] SOURCE_DIRS = [os.path.join(REPO_ROOT, name) for name in [ - 'Python', - 'Parser', - 'Objects', - 'Modules', - ]] - -#PYTHON = os.path.join(REPO_ROOT, 'python') -PYTHON = sys.executable - - -# Clean up the namespace. -del sys -del os + 'Python', + 'Parser', + 'Objects', + 'Modules', +]] diff --git a/Tools/c-analyzer/cpython/__main__.py b/Tools/c-analyzer/cpython/__main__.py index 6b0f9bcb9687f..23a3de06f639c 100644 --- a/Tools/c-analyzer/cpython/__main__.py +++ b/Tools/c-analyzer/cpython/__main__.py @@ -1,212 +1,280 @@ -import argparse -import re +import logging import sys -from c_analyzer.common import show -from c_analyzer.common.info import UNKNOWN +from c_common.fsutil import expand_filenames, iter_files_by_suffix +from c_common.scriptutil import ( + add_verbosity_cli, + add_traceback_cli, + add_commands_cli, + add_kind_filtering_cli, + add_files_cli, + process_args_by_key, + configure_logger, + get_prog, +) +from c_parser.info import KIND +import c_parser.__main__ as c_parser +import c_analyzer.__main__ as c_analyzer +import c_analyzer as _c_analyzer +from c_analyzer.info import UNKNOWN +from . import _analyzer, _parser, REPO_ROOT + + +logger = logging.getLogger(__name__) + + +def _resolve_filenames(filenames): + if filenames: + resolved = (_parser.resolve_filename(f) for f in filenames) + else: + resolved = _parser.iter_filenames() + return resolved + + +def fmt_summary(analysis): + # XXX Support sorting and grouping. + supported = [] + unsupported = [] + for item in analysis: + if item.supported: + supported.append(item) + else: + unsupported.append(item) + total = 0 + + def section(name, groupitems): + nonlocal total + items, render = c_analyzer.build_section(name, groupitems, + relroot=REPO_ROOT) + yield from render() + total += len(items) + + yield '' + yield '====================' + yield 'supported' + yield '====================' + + yield from section('types', supported) + yield from section('variables', supported) + + yield '' + yield '====================' + yield 'unsupported' + yield '====================' + + yield from section('types', unsupported) + yield from section('variables', unsupported) + + yield '' + yield f'grand total: {total}' + -from . import SOURCE_DIRS -from .find import supported_vars -from .known import ( - from_file as known_from_file, - DATA_FILE as KNOWN_FILE, +####################################### +# the checks + +CHECKS = dict(c_analyzer.CHECKS, **{ + 'globals': _analyzer.check_globals, +}) + +####################################### +# the commands + +FILES_KWARGS = dict(excluded=_parser.EXCLUDED, nargs='*') + + +def _cli_parse(parser): + process_output = c_parser.add_output_cli(parser) + process_kind = add_kind_filtering_cli(parser) + process_preprocessor = c_parser.add_preprocessor_cli( + parser, + get_preprocessor=_parser.get_preprocessor, + ) + process_files = add_files_cli(parser, **FILES_KWARGS) + return [ + process_output, + process_kind, + process_preprocessor, + process_files, + ] + + +def cmd_parse(filenames=None, **kwargs): + filenames = _resolve_filenames(filenames) + if 'get_file_preprocessor' not in kwargs: + kwargs['get_file_preprocessor'] = _parser.get_preprocessor() + c_parser.cmd_parse(filenames, **kwargs) + + +def _cli_check(parser, **kwargs): + return c_analyzer._cli_check(parser, CHECKS, **kwargs, **FILES_KWARGS) + + +def cmd_check(filenames=None, **kwargs): + filenames = _resolve_filenames(filenames) + kwargs['get_file_preprocessor'] = _parser.get_preprocessor(log_err=print) + c_analyzer.cmd_check( + filenames, + relroot=REPO_ROOT, + _analyze=_analyzer.analyze, + _CHECKS=CHECKS, + **kwargs ) -from .supported import IGNORED_FILE - - -def _check_results(unknown, knownvars, used): - def _match_unused_global(variable): - found = [] - for varid in knownvars: - if varid in used: - continue - if varid.funcname is not None: - continue - if varid.name != variable.name: - continue - if variable.filename and variable.filename != UNKNOWN: - if variable.filename == varid.filename: - found.append(varid) - else: - found.append(varid) - return found - - badknown = set() - for variable in sorted(unknown): - msg = None - if variable.funcname != UNKNOWN: - msg = f'could not find global symbol {variable.id}' - elif m := _match_unused_global(variable): - assert isinstance(m, list) - badknown.update(m) - elif variable.name in ('completed', 'id'): # XXX Figure out where these variables are. - unknown.remove(variable) - else: - msg = f'could not find local symbol {variable.id}' - if msg: - #raise Exception(msg) - print(msg) - if badknown: - print('---') - print(f'{len(badknown)} globals in known.tsv, but may actually be local:') - for varid in sorted(badknown): - print(f'{varid.filename:30} {varid.name}') - unused = sorted(varid - for varid in set(knownvars) - used - if varid.name != 'id') # XXX Figure out where these variables are. - if unused: - print('---') - print(f'did not use {len(unused)} known vars:') - for varid in unused: - print(f'{varid.filename:30} {varid.funcname or "-":20} {varid.name}') - raise Exception('not all known symbols used') - if unknown: - print('---') - raise Exception('could not find all symbols') - - -# XXX Move this check to its own command. -def cmd_check_cache(cmd, *, - known=KNOWN_FILE, - ignored=IGNORED_FILE, - _known_from_file=known_from_file, - _find=supported_vars, - ): - known = _known_from_file(known) - - used = set() - unknown = set() - for var, supported in _find(known=known, ignored=ignored): - if supported is None: - unknown.add(var) - continue - used.add(var.id) - _check_results(unknown, known['variables'], used) - - -def cmd_check(cmd, *, - known=KNOWN_FILE, - ignored=IGNORED_FILE, - _find=supported_vars, - _show=show.basic, - _print=print, - ): - """ - Fail if there are unsupported globals variables. - - In the failure case, the list of unsupported variables - will be printed out. - """ - unsupported = [] - for var, supported in _find(known=known, ignored=ignored): - if not supported: - unsupported.append(var) - - if not unsupported: - #_print('okay') - return - - _print('ERROR: found unsupported global variables') - _print() - _show(sorted(unsupported)) - _print(f' ({len(unsupported)} total)') - sys.exit(1) - - -def cmd_show(cmd, *, - known=KNOWN_FILE, - ignored=IGNORED_FILE, - skip_objects=False, - _find=supported_vars, - _show=show.basic, - _print=print, - ): - """ - Print out the list of found global variables. - - The variables will be distinguished as "supported" or "unsupported". - """ - allsupported = [] - allunsupported = [] - for found, supported in _find(known=known, - ignored=ignored, - skip_objects=skip_objects, - ): - if supported is None: - continue - (allsupported if supported else allunsupported - ).append(found) - - _print('supported:') - _print('----------') - _show(sorted(allsupported)) - _print(f' ({len(allsupported)} total)') - _print() - _print('unsupported:') - _print('------------') - _show(sorted(allunsupported)) - _print(f' ({len(allunsupported)} total)') - - -############################# -# the script -COMMANDS = { - 'check': cmd_check, - 'show': cmd_show, - } - -PROG = sys.argv[0] -PROG = 'c-globals.py' - - -def parse_args(prog=PROG, argv=sys.argv[1:], *, _fail=None): - common = argparse.ArgumentParser(add_help=False) - common.add_argument('--ignored', metavar='FILE', - default=IGNORED_FILE, - help='path to file that lists ignored vars') - common.add_argument('--known', metavar='FILE', - default=KNOWN_FILE, - help='path to file that lists known types') - #common.add_argument('dirs', metavar='DIR', nargs='*', - # default=SOURCE_DIRS, - # help='a directory to check') - parser = argparse.ArgumentParser( - prog=prog, +def cmd_analyze(filenames=None, **kwargs): + formats = dict(c_analyzer.FORMATS) + formats['summary'] = fmt_summary + filenames = _resolve_filenames(filenames) + kwargs['get_file_preprocessor'] = _parser.get_preprocessor(log_err=print) + c_analyzer.cmd_analyze( + filenames, + _analyze=_analyzer.analyze, + formats=formats, + **kwargs + ) + + +def _cli_data(parser): + filenames = False + known = True + return c_analyzer._cli_data(parser, filenames, known) + + +def cmd_data(datacmd, **kwargs): + formats = dict(c_analyzer.FORMATS) + formats['summary'] = fmt_summary + filenames = (file + for file in _resolve_filenames(None) + if file not in _parser.EXCLUDED) + kwargs['get_file_preprocessor'] = _parser.get_preprocessor(log_err=print) + if datacmd == 'show': + types = _analyzer.read_known() + results = [] + for decl, info in types.items(): + if info is UNKNOWN: + if decl.kind in (KIND.STRUCT, KIND.UNION): + extra = {'unsupported': ['type unknown'] * len(decl.members)} + else: + extra = {'unsupported': ['type unknown']} + info = (info, extra) + results.append((decl, info)) + if decl.shortkey == 'struct _object': + tempinfo = info + known = _analyzer.Analysis.from_results(results) + analyze = None + elif datacmd == 'dump': + known = _analyzer.KNOWN_FILE + def analyze(files, **kwargs): + decls = [] + for decl in _analyzer.iter_decls(files, **kwargs): + if not KIND.is_type_decl(decl.kind): + continue + if not decl.filename.endswith('.h'): + if decl.shortkey not in _analyzer.KNOWN_IN_DOT_C: + continue + decls.append(decl) + results = _c_analyzer.analyze_decls( + decls, + known={}, + analyze_resolved=_analyzer.analyze_resolved, ) - subs = parser.add_subparsers(dest='cmd') + return _analyzer.Analysis.from_results(results) + else: + known = _analyzer.read_known() + def analyze(files, **kwargs): + return _analyzer.iter_decls(files, **kwargs) + extracolumns = None + c_analyzer.cmd_data( + datacmd, + filenames, + known, + _analyze=analyze, + formats=formats, + extracolumns=extracolumns, + relroot=REPO_ROOT, + **kwargs + ) - check = subs.add_parser('check', parents=[common]) - show = subs.add_parser('show', parents=[common]) - show.add_argument('--skip-objects', action='store_true') +# We do not define any other cmd_*() handlers here, +# favoring those defined elsewhere. - if _fail is None: - def _fail(msg): - parser.error(msg) +COMMANDS = { + 'check': ( + 'analyze and fail if the CPython source code has any problems', + [_cli_check], + cmd_check, + ), + 'analyze': ( + 'report on the state of the CPython source code', + [(lambda p: c_analyzer._cli_analyze(p, **FILES_KWARGS))], + cmd_analyze, + ), + 'parse': ( + 'parse the CPython source files', + [_cli_parse], + cmd_parse, + ), + 'data': ( + 'check/manage local data (e.g. knwon types, ignored vars, caches)', + [_cli_data], + cmd_data, + ), +} + + +####################################### +# the script + +def parse_args(argv=sys.argv[1:], prog=None, *, subset=None): + import argparse + parser = argparse.ArgumentParser( + prog=prog or get_prog(), + ) + +# if subset == 'check' or subset == ['check']: +# if checks is not None: +# commands = dict(COMMANDS) +# commands['check'] = list(commands['check']) +# cli = commands['check'][1][0] +# commands['check'][1][0] = (lambda p: cli(p, checks=checks)) + processors = add_commands_cli( + parser, + commands=COMMANDS, + commonspecs=[ + add_verbosity_cli, + add_traceback_cli, + ], + subset=subset, + ) - # Now parse the args. args = parser.parse_args(argv) ns = vars(args) cmd = ns.pop('cmd') - if not cmd: - _fail('missing command') - return cmd, ns + verbosity, traceback_cm = process_args_by_key( + args, + processors[cmd], + ['verbosity', 'traceback_cm'], + ) + if cmd != 'parse': + # "verbosity" is sent to the commands, so we put it back. + args.verbosity = verbosity + + return cmd, ns, verbosity, traceback_cm -def main(cmd, cmdkwargs=None, *, _COMMANDS=COMMANDS): +def main(cmd, cmd_kwargs): try: - cmdfunc = _COMMANDS[cmd] + run_cmd = COMMANDS[cmd][-1] except KeyError: - raise ValueError( - f'unsupported cmd {cmd!r}' if cmd else 'missing cmd') - - cmdfunc(cmd, **cmdkwargs or {}) + raise ValueError(f'unsupported cmd {cmd!r}') + run_cmd(**cmd_kwargs) if __name__ == '__main__': - cmd, cmdkwargs = parse_args() - main(cmd, cmdkwargs) + cmd, cmd_kwargs, verbosity, traceback_cm = parse_args() + configure_logger(verbosity) + with traceback_cm: + main(cmd, cmd_kwargs) diff --git a/Tools/c-analyzer/cpython/_analyzer.py b/Tools/c-analyzer/cpython/_analyzer.py new file mode 100644 index 0000000000000..98f8888651e57 --- /dev/null +++ b/Tools/c-analyzer/cpython/_analyzer.py @@ -0,0 +1,348 @@ +import os.path +import re + +from c_common.clsutil import classonly +from c_parser.info import ( + KIND, + DeclID, + Declaration, + TypeDeclaration, + TypeDef, + Struct, + Member, + FIXED_TYPE, + is_type_decl, + is_pots, + is_funcptr, + is_process_global, + is_fixed_type, + is_immutable, +) +import c_analyzer as _c_analyzer +import c_analyzer.info as _info +import c_analyzer.datafiles as _datafiles +from . import _parser, REPO_ROOT + + +_DATA_DIR = os.path.dirname(__file__) +KNOWN_FILE = os.path.join(_DATA_DIR, 'known.tsv') +IGNORED_FILE = os.path.join(_DATA_DIR, 'ignored.tsv') +KNOWN_IN_DOT_C = { + 'struct _odictobject': False, + 'PyTupleObject': False, + 'struct _typeobject': False, + 'struct _arena': True, # ??? + 'struct _frame': False, + 'struct _ts': True, # ??? + 'struct PyCodeObject': False, + 'struct _is': True, # ??? + 'PyWideStringList': True, # ??? + # recursive + 'struct _dictkeysobject': False, +} +# These are loaded from the respective .tsv files upon first use. +_KNOWN = { + # {(file, ID) | ID => info | bool} + #'PyWideStringList': True, +} +#_KNOWN = {(Struct(None, typeid.partition(' ')[-1], None) +# if typeid.startswith('struct ') +# else TypeDef(None, typeid, None) +# ): ([], {'unsupported': None if supported else True}) +# for typeid, supported in _KNOWN_IN_DOT_C.items()} +_IGNORED = { + # {ID => reason} +} + +KINDS = frozenset((*KIND.TYPES, KIND.VARIABLE)) + + +def read_known(): + if not _KNOWN: + # Cache a copy the first time. + extracols = None # XXX + #extracols = ['unsupported'] + known = _datafiles.read_known(KNOWN_FILE, extracols, REPO_ROOT) + # For now we ignore known.values() (i.e. "extra"). + types, _ = _datafiles.analyze_known( + known, + analyze_resolved=analyze_resolved, + ) + _KNOWN.update(types) + return _KNOWN.copy() + + +def write_known(): + raise NotImplementedError + datafiles.write_known(decls, IGNORED_FILE, ['unsupported'], relroot=REPO_ROOT) + + +def read_ignored(): + if not _IGNORED: + _IGNORED.update(_datafiles.read_ignored(IGNORED_FILE)) + return dict(_IGNORED) + + +def write_ignored(): + raise NotImplementedError + datafiles.write_ignored(variables, IGNORED_FILE) + + +def analyze(filenames, *, + skip_objects=False, + **kwargs + ): + if skip_objects: + # XXX Set up a filter. + raise NotImplementedError + + known = read_known() + + decls = iter_decls(filenames) + results = _c_analyzer.analyze_decls( + decls, + known, + analyze_resolved=analyze_resolved, + ) + analysis = Analysis.from_results(results) + + return analysis + + +def iter_decls(filenames, **kwargs): + decls = _c_analyzer.iter_decls( + filenames, + # We ignore functions (and statements). + kinds=KINDS, + parse_files=_parser.parse_files, + **kwargs + ) + for decl in decls: + if not decl.data: + # Ignore forward declarations. + continue + yield decl + + +def analyze_resolved(resolved, decl, types, knowntypes, extra=None): + if decl.kind not in KINDS: + # Skip it! + return None + + typedeps = resolved + if typedeps is _info.UNKNOWN: + if decl.kind in (KIND.STRUCT, KIND.UNION): + typedeps = [typedeps] * len(decl.members) + else: + typedeps = [typedeps] + #assert isinstance(typedeps, (list, TypeDeclaration)), typedeps + + if extra is None: + extra = {} + elif 'unsupported' in extra: + raise NotImplementedError((decl, extra)) + + unsupported = _check_unsupported(decl, typedeps, types, knowntypes) + extra['unsupported'] = unsupported + + return typedeps, extra + + +def _check_unsupported(decl, typedeps, types, knowntypes): + if typedeps is None: + raise NotImplementedError(decl) + + if decl.kind in (KIND.STRUCT, KIND.UNION): + return _check_members(decl, typedeps, types, knowntypes) + elif decl.kind is KIND.ENUM: + if typedeps: + raise NotImplementedError((decl, typedeps)) + return None + else: + return _check_typedep(decl, typedeps, types, knowntypes) + + +def _check_members(decl, typedeps, types, knowntypes): + if isinstance(typedeps, TypeDeclaration): + raise NotImplementedError((decl, typedeps)) + + #members = decl.members or () # A forward decl has no members. + members = decl.members + if not members: + # A forward decl has no members, but that shouldn't surface here.. + raise NotImplementedError(decl) + if len(members) != len(typedeps): + raise NotImplementedError((decl, typedeps)) + + unsupported = [] + for member, typedecl in zip(members, typedeps): + checked = _check_typedep(member, typedecl, types, knowntypes) + unsupported.append(checked) + if any(None if v is FIXED_TYPE else v for v in unsupported): + return unsupported + elif FIXED_TYPE in unsupported: + return FIXED_TYPE + else: + return None + + +def _check_typedep(decl, typedecl, types, knowntypes): + if not isinstance(typedecl, TypeDeclaration): + if hasattr(type(typedecl), '__len__'): + if len(typedecl) == 1: + typedecl, = typedecl + if typedecl is None: + # XXX Fail? + return 'typespec (missing)' + elif typedecl is _info.UNKNOWN: + # XXX Is this right? + return 'typespec (unknown)' + elif not isinstance(typedecl, TypeDeclaration): + raise NotImplementedError((decl, typedecl)) + + if isinstance(decl, Member): + return _check_vartype(decl, typedecl, types, knowntypes) + elif not isinstance(decl, Declaration): + raise NotImplementedError(decl) + elif decl.kind is KIND.TYPEDEF: + return _check_vartype(decl, typedecl, types, knowntypes) + elif decl.kind is KIND.VARIABLE: + if not is_process_global(decl): + return None + checked = _check_vartype(decl, typedecl, types, knowntypes) + return 'mutable' if checked is FIXED_TYPE else checked + else: + raise NotImplementedError(decl) + + +def _check_vartype(decl, typedecl, types, knowntypes): + """Return failure reason.""" + checked = _check_typespec(decl, typedecl, types, knowntypes) + if checked: + return checked + if is_immutable(decl.vartype): + return None + if is_fixed_type(decl.vartype): + return FIXED_TYPE + return 'mutable' + + +def _check_typespec(decl, typedecl, types, knowntypes): + typespec = decl.vartype.typespec + if typedecl is not None: + found = types.get(typedecl) + if found is None: + found = knowntypes.get(typedecl) + + if found is not None: + _, extra = found + if extra is None: + # XXX Under what circumstances does this happen? + extra = {} + unsupported = extra.get('unsupported') + if unsupported is FIXED_TYPE: + unsupported = None + return 'typespec' if unsupported else None + # Fall back to default known types. + if is_pots(typespec): + return None + elif _info.is_system_type(typespec): + return None + elif is_funcptr(decl.vartype): + return None + return 'typespec' + + +class Analyzed(_info.Analyzed): + + @classonly + def is_target(cls, raw): + if not super().is_target(raw): + return False + if raw.kind not in KINDS: + return False + return True + + #@classonly + #def _parse_raw_result(cls, result, extra): + # typedecl, extra = super()._parse_raw_result(result, extra) + # if typedecl is None: + # return None, extra + # raise NotImplementedError + + def __init__(self, item, typedecl=None, *, unsupported=None, **extra): + if 'unsupported' in extra: + raise NotImplementedError((item, typedecl, unsupported, extra)) + if not unsupported: + unsupported = None + elif isinstance(unsupported, (str, TypeDeclaration)): + unsupported = (unsupported,) + elif unsupported is not FIXED_TYPE: + unsupported = tuple(unsupported) + self.unsupported = unsupported + extra['unsupported'] = self.unsupported # ...for __repr__(), etc. + if self.unsupported is None: + #self.supported = None + self.supported = True + elif self.unsupported is FIXED_TYPE: + if item.kind is KIND.VARIABLE: + raise NotImplementedError(item, typedecl, unsupported) + self.supported = True + else: + self.supported = not self.unsupported + super().__init__(item, typedecl, **extra) + + def render(self, fmt='line', *, itemonly=False): + if fmt == 'raw': + yield repr(self) + return + rendered = super().render(fmt, itemonly=itemonly) + # XXX ??? + #if itemonly: + # yield from rendered + supported = self._supported + if fmt in ('line', 'brief'): + rendered, = rendered + parts = [ + '+' if supported else '-' if supported is False else '', + rendered, + ] + yield '\t'.join(parts) + elif fmt == 'summary': + raise NotImplementedError(fmt) + elif fmt == 'full': + yield from rendered + if supported: + yield f'\tsupported:\t{supported}' + else: + raise NotImplementedError(fmt) + + +class Analysis(_info.Analysis): + _item_class = Analyzed + + @classonly + def build_item(cls, info, result=None): + if not isinstance(info, Declaration) or info.kind not in KINDS: + raise NotImplementedError((info, result)) + return super().build_item(info, result) + + +def check_globals(analysis): + # yield (data, failure) + ignored = read_ignored() + for item in analysis: + if item.kind != KIND.VARIABLE: + continue + if item.supported: + continue + if item.id in ignored: + continue + reason = item.unsupported + if not reason: + reason = '???' + elif not isinstance(reason, str): + if len(reason) == 1: + reason, = reason + reason = f'({reason})' + yield item, f'not supported {reason:20}\t{item.storage or ""} {item.vartype}' diff --git a/Tools/c-analyzer/cpython/_generate.py b/Tools/c-analyzer/cpython/_generate.py deleted file mode 100644 index 3456604b81470..0000000000000 --- a/Tools/c-analyzer/cpython/_generate.py +++ /dev/null @@ -1,326 +0,0 @@ -# The code here consists of hacks for pre-populating the known.tsv file. - -from c_analyzer.parser.preprocessor import _iter_clean_lines -from c_analyzer.parser.naive import ( - iter_variables, parse_variable_declaration, find_variables, - ) -from c_analyzer.common.known import HEADER as KNOWN_HEADER -from c_analyzer.common.info import UNKNOWN, ID -from c_analyzer.variables import Variable -from c_analyzer.util import write_tsv - -from . import SOURCE_DIRS, REPO_ROOT -from .known import DATA_FILE as KNOWN_FILE -from .files import iter_cpython_files - - -POTS = ('char ', 'wchar_t ', 'int ', 'Py_ssize_t ') -POTS += tuple('const ' + v for v in POTS) -STRUCTS = ('PyTypeObject', 'PyObject', 'PyMethodDef', 'PyModuleDef', 'grammar') - - -def _parse_global(line, funcname=None): - line = line.strip() - if line.startswith('static '): - if '(' in line and '[' not in line and ' = ' not in line: - return None, None - name, decl = parse_variable_declaration(line) - elif line.startswith(('Py_LOCAL(', 'Py_LOCAL_INLINE(')): - name, decl = parse_variable_declaration(line) - elif line.startswith('_Py_static_string('): - decl = line.strip(';').strip() - name = line.split('(')[1].split(',')[0].strip() - elif line.startswith('_Py_IDENTIFIER('): - decl = line.strip(';').strip() - name = 'PyId_' + line.split('(')[1].split(')')[0].strip() - elif funcname: - return None, None - - # global-only - elif line.startswith('PyAPI_DATA('): # only in .h files - name, decl = parse_variable_declaration(line) - elif line.startswith('extern '): # only in .h files - name, decl = parse_variable_declaration(line) - elif line.startswith('PyDoc_VAR('): - decl = line.strip(';').strip() - name = line.split('(')[1].split(')')[0].strip() - elif line.startswith(POTS): # implied static - if '(' in line and '[' not in line and ' = ' not in line: - return None, None - name, decl = parse_variable_declaration(line) - elif line.startswith(STRUCTS) and line.endswith(' = {'): # implied static - name, decl = parse_variable_declaration(line) - elif line.startswith(STRUCTS) and line.endswith(' = NULL;'): # implied static - name, decl = parse_variable_declaration(line) - elif line.startswith('struct '): - if not line.endswith(' = {'): - return None, None - if not line.partition(' ')[2].startswith(STRUCTS): - return None, None - # implied static - name, decl = parse_variable_declaration(line) - - # file-specific - elif line.startswith(('SLOT1BINFULL(', 'SLOT1BIN(')): - # Objects/typeobject.c - funcname = line.split('(')[1].split(',')[0] - return [ - ('op_id', funcname, '_Py_static_string(op_id, OPSTR)'), - ('rop_id', funcname, '_Py_static_string(op_id, OPSTR)'), - ] - elif line.startswith('WRAP_METHOD('): - # Objects/weakrefobject.c - funcname, name = (v.strip() for v in line.split('(')[1].split(')')[0].split(',')) - return [ - ('PyId_' + name, funcname, f'_Py_IDENTIFIER({name})'), - ] - - else: - return None, None - return name, decl - - -def _pop_cached(varcache, filename, funcname, name, *, - _iter_variables=iter_variables, - ): - # Look for the file. - try: - cached = varcache[filename] - except KeyError: - cached = varcache[filename] = {} - for variable in _iter_variables(filename, - parse_variable=_parse_global, - ): - variable._isglobal = True - cached[variable.id] = variable - for var in cached: - print(' ', var) - - # Look for the variable. - if funcname == UNKNOWN: - for varid in cached: - if varid.name == name: - break - else: - return None - return cached.pop(varid) - else: - return cached.pop((filename, funcname, name), None) - - -def find_matching_variable(varid, varcache, allfilenames, *, - _pop_cached=_pop_cached, - ): - if varid.filename and varid.filename != UNKNOWN: - filenames = [varid.filename] - else: - filenames = allfilenames - for filename in filenames: - variable = _pop_cached(varcache, filename, varid.funcname, varid.name) - if variable is not None: - return variable - else: - if varid.filename and varid.filename != UNKNOWN and varid.funcname is None: - for filename in allfilenames: - if not filename.endswith('.h'): - continue - variable = _pop_cached(varcache, filename, None, varid.name) - if variable is not None: - return variable - return None - - -MULTILINE = { - # Python/Python-ast.c - 'Load_singleton': 'PyObject *', - 'Store_singleton': 'PyObject *', - 'Del_singleton': 'PyObject *', - 'AugLoad_singleton': 'PyObject *', - 'AugStore_singleton': 'PyObject *', - 'Param_singleton': 'PyObject *', - 'And_singleton': 'PyObject *', - 'Or_singleton': 'PyObject *', - 'Add_singleton': 'static PyObject *', - 'Sub_singleton': 'static PyObject *', - 'Mult_singleton': 'static PyObject *', - 'MatMult_singleton': 'static PyObject *', - 'Div_singleton': 'static PyObject *', - 'Mod_singleton': 'static PyObject *', - 'Pow_singleton': 'static PyObject *', - 'LShift_singleton': 'static PyObject *', - 'RShift_singleton': 'static PyObject *', - 'BitOr_singleton': 'static PyObject *', - 'BitXor_singleton': 'static PyObject *', - 'BitAnd_singleton': 'static PyObject *', - 'FloorDiv_singleton': 'static PyObject *', - 'Invert_singleton': 'static PyObject *', - 'Not_singleton': 'static PyObject *', - 'UAdd_singleton': 'static PyObject *', - 'USub_singleton': 'static PyObject *', - 'Eq_singleton': 'static PyObject *', - 'NotEq_singleton': 'static PyObject *', - 'Lt_singleton': 'static PyObject *', - 'LtE_singleton': 'static PyObject *', - 'Gt_singleton': 'static PyObject *', - 'GtE_singleton': 'static PyObject *', - 'Is_singleton': 'static PyObject *', - 'IsNot_singleton': 'static PyObject *', - 'In_singleton': 'static PyObject *', - 'NotIn_singleton': 'static PyObject *', - # Python/symtable.c - 'top': 'static identifier ', - 'lambda': 'static identifier ', - 'genexpr': 'static identifier ', - 'listcomp': 'static identifier ', - 'setcomp': 'static identifier ', - 'dictcomp': 'static identifier ', - '__class__': 'static identifier ', - # Python/compile.c - '__doc__': 'static PyObject *', - '__annotations__': 'static PyObject *', - # Objects/floatobject.c - 'double_format': 'static float_format_type ', - 'float_format': 'static float_format_type ', - 'detected_double_format': 'static float_format_type ', - 'detected_float_format': 'static float_format_type ', - # Python/dtoa.c - 'private_mem': 'static double private_mem[PRIVATE_mem]', - 'pmem_next': 'static double *', - # Modules/_weakref.c - 'weakref_functions': 'static PyMethodDef ', -} -INLINE = { - # Modules/_tracemalloc.c - 'allocators': 'static struct { PyMemAllocatorEx mem; PyMemAllocatorEx raw; PyMemAllocatorEx obj; } ', - # Modules/faulthandler.c - 'fatal_error': 'static struct { int enabled; PyObject *file; int fd; int all_threads; PyInterpreterState *interp; void *exc_handler; } ', - 'thread': 'static struct { PyObject *file; int fd; PY_TIMEOUT_T timeout_us; int repeat; PyInterpreterState *interp; int exit; char *header; size_t header_len; PyThread_type_lock cancel_event; PyThread_type_lock running; } ', - # Modules/signalmodule.c - 'Handlers': 'static volatile struct { _Py_atomic_int tripped; PyObject *func; } Handlers[NSIG]', - 'wakeup': 'static volatile struct { SOCKET_T fd; int warn_on_full_buffer; int use_send; } ', - # Python/dynload_shlib.c - 'handles': 'static struct { dev_t dev; ino_t ino; void *handle; } handles[128]', - # Objects/obmalloc.c - '_PyMem_Debug': 'static struct { debug_alloc_api_t raw; debug_alloc_api_t mem; debug_alloc_api_t obj; } ', - # Python/bootstrap_hash.c - 'urandom_cache': 'static struct { int fd; dev_t st_dev; ino_t st_ino; } ', - } -FUNC = { - # Objects/object.c - '_Py_abstract_hack': 'Py_ssize_t (*_Py_abstract_hack)(PyObject *)', - # Parser/myreadline.c - 'PyOS_InputHook': 'int (*PyOS_InputHook)(void)', - # Python/pylifecycle.c - '_PyOS_mystrnicmp_hack': 'int (*_PyOS_mystrnicmp_hack)(const char *, const char *, Py_ssize_t)', - # Parser/myreadline.c - 'PyOS_ReadlineFunctionPointer': 'char *(*PyOS_ReadlineFunctionPointer)(FILE *, FILE *, const char *)', - } -IMPLIED = { - # Objects/boolobject.c - '_Py_FalseStruct': 'static struct _longobject ', - '_Py_TrueStruct': 'static struct _longobject ', - # Modules/config.c - '_PyImport_Inittab': 'struct _inittab _PyImport_Inittab[]', - } -GLOBALS = {} -GLOBALS.update(MULTILINE) -GLOBALS.update(INLINE) -GLOBALS.update(FUNC) -GLOBALS.update(IMPLIED) - -LOCALS = { - 'buildinfo': ('Modules/getbuildinfo.c', - 'Py_GetBuildInfo', - 'static char buildinfo[50 + sizeof(GITVERSION) + ((sizeof(GITTAG) > sizeof(GITBRANCH)) ? sizeof(GITTAG) : sizeof(GITBRANCH))]'), - 'methods': ('Python/codecs.c', - '_PyCodecRegistry_Init', - 'static struct { char *name; PyMethodDef def; } methods[]'), - } - - -def _known(symbol): - if symbol.funcname: - if symbol.funcname != UNKNOWN or symbol.filename != UNKNOWN: - raise KeyError(symbol.name) - filename, funcname, decl = LOCALS[symbol.name] - varid = ID(filename, funcname, symbol.name) - elif not symbol.filename or symbol.filename == UNKNOWN: - raise KeyError(symbol.name) - else: - varid = symbol.id - try: - decl = GLOBALS[symbol.name] - except KeyError: - - if symbol.name.endswith('_methods'): - decl = 'static PyMethodDef ' - elif symbol.filename == 'Objects/exceptions.c' and symbol.name.startswith(('PyExc_', '_PyExc_')): - decl = 'static PyTypeObject ' - else: - raise - if symbol.name not in decl: - decl = decl + symbol.name - return Variable(varid, 'static', decl) - - -def known_row(varid, decl): - return ( - varid.filename, - varid.funcname or '-', - varid.name, - 'variable', - decl, - ) - - -def known_rows(symbols, *, - cached=True, - _get_filenames=iter_cpython_files, - _find_match=find_matching_variable, - _find_symbols=find_variables, - _as_known=known_row, - ): - filenames = list(_get_filenames()) - cache = {} - if cached: - for symbol in symbols: - try: - found = _known(symbol) - except KeyError: - found = _find_match(symbol, cache, filenames) - if found is None: - found = Variable(symbol.id, UNKNOWN, UNKNOWN) - yield _as_known(found.id, found.vartype) - else: - raise NotImplementedError # XXX incorporate KNOWN - for variable in _find_symbols(symbols, filenames, - srccache=cache, - parse_variable=_parse_global, - ): - #variable = variable._replace( - # filename=os.path.relpath(variable.filename, REPO_ROOT)) - if variable.funcname == UNKNOWN: - print(variable) - if variable.vartype== UNKNOWN: - print(variable) - yield _as_known(variable.id, variable.vartype) - - -def generate(symbols, filename=None, *, - _generate_rows=known_rows, - _write_tsv=write_tsv, - ): - if not filename: - filename = KNOWN_FILE + '.new' - - rows = _generate_rows(symbols) - _write_tsv(filename, KNOWN_HEADER, rows) - - -if __name__ == '__main__': - from c_symbols import binary - symbols = binary.iter_symbols( - binary.PYTHON, - find_local_symbol=None, - ) - generate(symbols) diff --git a/Tools/c-analyzer/cpython/_parser.py b/Tools/c-analyzer/cpython/_parser.py new file mode 100644 index 0000000000000..35fa296251e2e --- /dev/null +++ b/Tools/c-analyzer/cpython/_parser.py @@ -0,0 +1,308 @@ +import os.path +import re + +from c_common.fsutil import expand_filenames, iter_files_by_suffix +from c_parser.preprocessor import ( + get_preprocessor as _get_preprocessor, +) +from c_parser import ( + parse_file as _parse_file, + parse_files as _parse_files, +) +from . import REPO_ROOT, INCLUDE_DIRS, SOURCE_DIRS + + +GLOB_ALL = '**/*' + + +def clean_lines(text): + """Clear out comments, blank lines, and leading/trailing whitespace.""" + lines = (line.strip() for line in text.splitlines()) + lines = (line.partition('#')[0].rstrip() + for line in lines + if line and not line.startswith('#')) + glob_all = f'{GLOB_ALL} ' + lines = (re.sub(r'^[*] ', glob_all, line) for line in lines) + lines = (os.path.join(REPO_ROOT, line) for line in lines) + return list(lines) + + +''' + at begin=sh@ +./python ../c-parser/cpython.py + --exclude '+../c-parser/EXCLUDED' + --macros '+../c-parser/MACROS' + --incldirs '+../c-parser/INCL_DIRS' + --same './Include/cpython/' + Include/*.h + Include/internal/*.h + Modules/**/*.c + Objects/**/*.c + Parser/**/*.c + Python/**/*.c + at end=sh@ +''' + +GLOBS = [ + 'Include/*.h', + 'Include/internal/*.h', + 'Modules/**/*.c', + 'Objects/**/*.c', + 'Parser/**/*.c', + 'Python/**/*.c', +] + +EXCLUDED = clean_lines(''' +# @begin=conf@ + +# Rather than fixing for this one, we manually make sure it's okay. +Modules/_sha3/kcp/KeccakP-1600-opt64.c + +# OSX +#Modules/_ctypes/darwin/*.c +#Modules/_ctypes/libffi_osx/*.c +Modules/_scproxy.c # SystemConfiguration/SystemConfiguration.h + +# Windows +Modules/_winapi.c # windows.h +Modules/overlapped.c # winsock.h +Python/dynload_win.c # windows.h + +# other OS-dependent +Python/dynload_dl.c # dl.h +Python/dynload_hpux.c # dl.h +Python/dynload_aix.c # sys/ldr.h + +# @end=conf@ +''') + +# XXX Fix the parser. +EXCLUDED += clean_lines(''' +# The tool should be able to parse these... + +Modules/_dbmmodule.c +Modules/cjkcodecs/_codecs_*.c +Modules/expat/xmlrole.c +Modules/expat/xmlparse.c +Python/initconfig.c +''') + +INCL_DIRS = clean_lines(''' +# @begin=tsv@ + +glob dirname +* . +* ./Include +* ./Include/internal + +Modules/_tkinter.c /usr/include/tcl8.6 +Modules/tkappinit.c /usr/include/tcl +Modules/_decimal/**/*.c Modules/_decimal/libmpdec + +# @end=tsv@ +''')[1:] + +MACROS = clean_lines(''' +# @begin=tsv@ + +glob name value + +Include/internal/*.h Py_BUILD_CORE 1 +Python/**/*.c Py_BUILD_CORE 1 +Parser/**/*.c Py_BUILD_CORE 1 +Objects/**/*.c Py_BUILD_CORE 1 + +Modules/faulthandler.c Py_BUILD_CORE 1 +Modules/_functoolsmodule.c Py_BUILD_CORE 1 +Modules/gcmodule.c Py_BUILD_CORE 1 +Modules/getpath.c Py_BUILD_CORE 1 +Modules/_io/*.c Py_BUILD_CORE 1 +Modules/itertoolsmodule.c Py_BUILD_CORE 1 +Modules/_localemodule.c Py_BUILD_CORE 1 +Modules/main.c Py_BUILD_CORE 1 +Modules/posixmodule.c Py_BUILD_CORE 1 +Modules/signalmodule.c Py_BUILD_CORE 1 +Modules/_threadmodule.c Py_BUILD_CORE 1 +Modules/_tracemalloc.c Py_BUILD_CORE 1 +Modules/_asynciomodule.c Py_BUILD_CORE 1 +Modules/mathmodule.c Py_BUILD_CORE 1 +Modules/cmathmodule.c Py_BUILD_CORE 1 +Modules/_weakref.c Py_BUILD_CORE 1 +Modules/sha256module.c Py_BUILD_CORE 1 +Modules/sha512module.c Py_BUILD_CORE 1 +Modules/_datetimemodule.c Py_BUILD_CORE 1 +Modules/_ctypes/cfield.c Py_BUILD_CORE 1 +Modules/_heapqmodule.c Py_BUILD_CORE 1 +Modules/_posixsubprocess.c Py_BUILD_CORE 1 + +Modules/_json.c Py_BUILD_CORE_BUILTIN 1 +Modules/_pickle.c Py_BUILD_CORE_BUILTIN 1 +Modules/_testinternalcapi.c Py_BUILD_CORE_BUILTIN 1 + +Include/cpython/abstract.h Py_CPYTHON_ABSTRACTOBJECT_H 1 +Include/cpython/bytearrayobject.h Py_CPYTHON_BYTEARRAYOBJECT_H 1 +Include/cpython/bytesobject.h Py_CPYTHON_BYTESOBJECT_H 1 +Include/cpython/ceval.h Py_CPYTHON_CEVAL_H 1 +Include/cpython/code.h Py_CPYTHON_CODE_H 1 +Include/cpython/dictobject.h Py_CPYTHON_DICTOBJECT_H 1 +Include/cpython/fileobject.h Py_CPYTHON_FILEOBJECT_H 1 +Include/cpython/fileutils.h Py_CPYTHON_FILEUTILS_H 1 +Include/cpython/frameobject.h Py_CPYTHON_FRAMEOBJECT_H 1 +Include/cpython/import.h Py_CPYTHON_IMPORT_H 1 +Include/cpython/interpreteridobject.h Py_CPYTHON_INTERPRETERIDOBJECT_H 1 +Include/cpython/listobject.h Py_CPYTHON_LISTOBJECT_H 1 +Include/cpython/methodobject.h Py_CPYTHON_METHODOBJECT_H 1 +Include/cpython/object.h Py_CPYTHON_OBJECT_H 1 +Include/cpython/objimpl.h Py_CPYTHON_OBJIMPL_H 1 +Include/cpython/pyerrors.h Py_CPYTHON_ERRORS_H 1 +Include/cpython/pylifecycle.h Py_CPYTHON_PYLIFECYCLE_H 1 +Include/cpython/pymem.h Py_CPYTHON_PYMEM_H 1 +Include/cpython/pystate.h Py_CPYTHON_PYSTATE_H 1 +Include/cpython/sysmodule.h Py_CPYTHON_SYSMODULE_H 1 +Include/cpython/traceback.h Py_CPYTHON_TRACEBACK_H 1 +Include/cpython/tupleobject.h Py_CPYTHON_TUPLEOBJECT_H 1 +Include/cpython/unicodeobject.h Py_CPYTHON_UNICODEOBJECT_H 1 + +# implied include of pyport.h +Include/**/*.h PyAPI_DATA(RTYPE) extern RTYPE +Include/**/*.h PyAPI_FUNC(RTYPE) RTYPE +Include/**/*.h Py_DEPRECATED(VER) /* */ +Include/**/*.h _Py_NO_RETURN /* */ +Include/**/*.h PYLONG_BITS_IN_DIGIT 30 +Modules/**/*.c PyMODINIT_FUNC PyObject* +Objects/unicodeobject.c PyMODINIT_FUNC PyObject* +Python/marshal.c PyMODINIT_FUNC PyObject* +Python/_warnings.c PyMODINIT_FUNC PyObject* +Python/Python-ast.c PyMODINIT_FUNC PyObject* +Python/import.c PyMODINIT_FUNC PyObject* +Modules/_testcapimodule.c PyAPI_FUNC(RTYPE) RTYPE +Python/getargs.c PyAPI_FUNC(RTYPE) RTYPE + +# implied include of exports.h +#Modules/_io/bytesio.c Py_EXPORTED_SYMBOL /* */ + +# implied include of object.h +Include/**/*.h PyObject_HEAD PyObject ob_base; +Include/**/*.h PyObject_VAR_HEAD PyVarObject ob_base; + +# implied include of pyconfig.h +Include/**/*.h SIZEOF_WCHAR_T 4 + +# implied include of +Include/**/*.h _POSIX_THREADS 1 + +# from Makefile +Modules/getpath.c PYTHONPATH 1 +Modules/getpath.c PREFIX ... +Modules/getpath.c EXEC_PREFIX ... +Modules/getpath.c VERSION ... +Modules/getpath.c VPATH ... + +# from Modules/_sha3/sha3module.c +Modules/_sha3/kcp/KeccakP-1600-inplace32BI.c PLATFORM_BYTE_ORDER 4321 # force big-endian +Modules/_sha3/kcp/*.c KeccakOpt 64 +Modules/_sha3/kcp/*.c KeccakP200_excluded 1 +Modules/_sha3/kcp/*.c KeccakP400_excluded 1 +Modules/_sha3/kcp/*.c KeccakP800_excluded 1 + +# See: setup.py +Modules/_decimal/**/*.c CONFIG_64 1 +Modules/_decimal/**/*.c ASM 1 +Modules/expat/xmlparse.c HAVE_EXPAT_CONFIG_H 1 +Modules/expat/xmlparse.c XML_POOR_ENTROPY 1 +Modules/_dbmmodule.c HAVE_GDBM_DASH_NDBM_H 1 + +# @end=tsv@ +''')[1:] + +# -pthread +# -Wno-unused-result +# -Wsign-compare +# -g +# -Og +# -Wall +# -std=c99 +# -Wextra +# -Wno-unused-result -Wno-unused-parameter +# -Wno-missing-field-initializers +# -Werror=implicit-function-declaration + +SAME = [ + './Include/cpython/', +] + + +def resolve_filename(filename): + orig = filename + filename = os.path.normcase(os.path.normpath(filename)) + if os.path.isabs(filename): + if os.path.relpath(filename, REPO_ROOT).startswith('.'): + raise Exception(f'{orig!r} is outside the repo ({REPO_ROOT})') + return filename + else: + return os.path.join(REPO_ROOT, filename) + + +def iter_filenames(*, search=False): + if search: + yield from iter_files_by_suffix(INCLUDE_DIRS, ('.h',)) + yield from iter_files_by_suffix(SOURCE_DIRS, ('.c',)) + else: + globs = (os.path.join(REPO_ROOT, file) for file in GLOBS) + yield from expand_filenames(globs) + + +def get_preprocessor(*, + file_macros=None, + file_incldirs=None, + file_same=None, + **kwargs + ): + macros = tuple(MACROS) + if file_macros: + macros += tuple(file_macros) + incldirs = tuple(INCL_DIRS) + if file_incldirs: + incldirs += tuple(file_incldirs) + return _get_preprocessor( + file_macros=macros, + file_incldirs=incldirs, + file_same=file_same, + **kwargs + ) + + +def parse_file(filename, *, + match_kind=None, + ignore_exc=None, + log_err=None, + ): + get_file_preprocessor = get_preprocessor( + ignore_exc=ignore_exc, + log_err=log_err, + ) + yield from _parse_file( + filename, + match_kind=match_kind, + get_file_preprocessor=get_file_preprocessor, + ) + + +def parse_files(filenames=None, *, + match_kind=None, + ignore_exc=None, + log_err=None, + get_file_preprocessor=None, + **file_kwargs + ): + if get_file_preprocessor is None: + get_file_preprocessor = get_preprocessor( + ignore_exc=ignore_exc, + log_err=log_err, + ) + yield from _parse_files( + filenames, + match_kind=match_kind, + get_file_preprocessor=get_file_preprocessor, + **file_kwargs + ) diff --git a/Tools/c-analyzer/cpython/files.py b/Tools/c-analyzer/cpython/files.py deleted file mode 100644 index 543097af7bcd5..0000000000000 --- a/Tools/c-analyzer/cpython/files.py +++ /dev/null @@ -1,29 +0,0 @@ -from c_analyzer.common.files import ( - C_SOURCE_SUFFIXES, walk_tree, iter_files_by_suffix, - ) - -from . import SOURCE_DIRS, REPO_ROOT - -# XXX need tests: -# * iter_files() - - -def iter_files(*, - walk=walk_tree, - _files=iter_files_by_suffix, - ): - """Yield each file in the tree for each of the given directory names.""" - excludedtrees = [ - os.path.join('Include', 'cpython', ''), - ] - def is_excluded(filename): - for root in excludedtrees: - if filename.startswith(root): - return True - return False - for filename in _files(SOURCE_DIRS, C_SOURCE_SUFFIXES, REPO_ROOT, - walk=walk, - ): - if is_excluded(filename): - continue - yield filename diff --git a/Tools/c-analyzer/cpython/find.py b/Tools/c-analyzer/cpython/find.py deleted file mode 100644 index a7bc0b477b839..0000000000000 --- a/Tools/c-analyzer/cpython/find.py +++ /dev/null @@ -1,101 +0,0 @@ -import os.path - -from c_analyzer.common import files -from c_analyzer.common.info import UNKNOWN, ID -from c_analyzer.variables import find as _common - -from . import SOURCE_DIRS, PYTHON, REPO_ROOT -from .known import ( - from_file as known_from_file, - DATA_FILE as KNOWN_FILE, - ) -from .supported import ( - ignored_from_file, IGNORED_FILE, is_supported, _is_object, - ) - -# XXX need tests: -# * vars_from_binary() -# * vars_from_source() -# * supported_vars() - - -def _handle_id(filename, funcname, name, *, - _relpath=os.path.relpath, - ): - filename = _relpath(filename, REPO_ROOT) - return ID(filename, funcname, name) - - -def vars_from_binary(*, - known=KNOWN_FILE, - _known_from_file=known_from_file, - _iter_files=files.iter_files_by_suffix, - _iter_vars=_common.vars_from_binary, - ): - """Yield a Variable for each found Symbol. - - Details are filled in from the given "known" variables and types. - """ - if isinstance(known, str): - known = _known_from_file(known) - dirnames = SOURCE_DIRS - suffixes = ('.c',) - filenames = _iter_files(dirnames, suffixes) - # XXX For now we only use known variables (no source lookup). - filenames = None - yield from _iter_vars(PYTHON, - known=known, - filenames=filenames, - handle_id=_handle_id, - check_filename=(lambda n: True), - ) - - -def vars_from_source(*, - preprocessed=None, - known=KNOWN_FILE, - _known_from_file=known_from_file, - _iter_files=files.iter_files_by_suffix, - _iter_vars=_common.vars_from_source, - ): - """Yield a Variable for each declaration in the raw source code. - - Details are filled in from the given "known" variables and types. - """ - if isinstance(known, str): - known = _known_from_file(known) - dirnames = SOURCE_DIRS - suffixes = ('.c',) - filenames = _iter_files(dirnames, suffixes) - yield from _iter_vars(filenames, - preprocessed=preprocessed, - known=known, - handle_id=_handle_id, - ) - - -def supported_vars(*, - known=KNOWN_FILE, - ignored=IGNORED_FILE, - skip_objects=False, - _known_from_file=known_from_file, - _ignored_from_file=ignored_from_file, - _iter_vars=vars_from_binary, - _is_supported=is_supported, - ): - """Yield (var, is supported) for each found variable.""" - if isinstance(known, str): - known = _known_from_file(known) - if isinstance(ignored, str): - ignored = _ignored_from_file(ignored) - - for var in _iter_vars(known=known): - if not var.isglobal: - continue - elif var.vartype == UNKNOWN: - yield var, None - # XXX Support proper filters instead. - elif skip_objects and _is_object(found.vartype): - continue - else: - yield var, _is_supported(var, ignored, known) diff --git a/Tools/c-analyzer/cpython/ignored.tsv b/Tools/c-analyzer/cpython/ignored.tsv new file mode 100644 index 0000000000000..2c456db063e42 --- /dev/null +++ b/Tools/c-analyzer/cpython/ignored.tsv @@ -0,0 +1,2 @@ +filename funcname name reason +#??? - somevar ??? diff --git a/Tools/c-analyzer/cpython/known.py b/Tools/c-analyzer/cpython/known.py deleted file mode 100644 index c3cc2c06026ce..0000000000000 --- a/Tools/c-analyzer/cpython/known.py +++ /dev/null @@ -1,66 +0,0 @@ -import csv -import os.path - -from c_analyzer.parser.declarations import extract_storage -from c_analyzer.variables import known as _common -from c_analyzer.variables.info import Variable - -from . import DATA_DIR - - -# XXX need tests: -# * from_file() -# * look_up_variable() - - -DATA_FILE = os.path.join(DATA_DIR, 'known.tsv') - - -def _get_storage(decl, infunc): - # statics - if decl.startswith(('Py_LOCAL(', 'Py_LOCAL_INLINE(')): - return 'static' - if decl.startswith(('_Py_IDENTIFIER(', '_Py_static_string(')): - return 'static' - if decl.startswith('PyDoc_VAR('): - return 'static' - if decl.startswith(('SLOT1BINFULL(', 'SLOT1BIN(')): - return 'static' - if decl.startswith('WRAP_METHOD('): - return 'static' - # public extern - if decl.startswith('PyAPI_DATA('): - return 'extern' - # Fall back to the normal handler. - return extract_storage(decl, infunc=infunc) - - -def _handle_var(varid, decl): -# if varid.name == 'id' and decl == UNKNOWN: -# # None of these are variables. -# decl = 'int id'; - storage = _get_storage(decl, varid.funcname) - return Variable(varid, storage, decl) - - -def from_file(infile=DATA_FILE, *, - _from_file=_common.from_file, - _handle_var=_handle_var, - ): - """Return the info for known declarations in the given file.""" - return _from_file(infile, handle_var=_handle_var) - - -def look_up_variable(varid, knownvars, *, - _lookup=_common.look_up_variable, - ): - """Return the known variable matching the given ID. - - "knownvars" is a mapping of ID to Variable. - - "match_files" is used to verify if two filenames point to - the same file. - - If no match is found then None is returned. - """ - return _lookup(varid, knownvars) diff --git a/Tools/c-analyzer/cpython/known.tsv b/Tools/c-analyzer/cpython/known.tsv new file mode 100644 index 0000000000000..a48ef02dc6f6f --- /dev/null +++ b/Tools/c-analyzer/cpython/known.tsv @@ -0,0 +1,3 @@ +filename funcname name kind declaration +#filename funcname name kind is_supported declaration +#??? - PyWideStringList typedef ??? diff --git a/Tools/c-analyzer/cpython/supported.py b/Tools/c-analyzer/cpython/supported.py deleted file mode 100644 index 18786eefd8ded..0000000000000 --- a/Tools/c-analyzer/cpython/supported.py +++ /dev/null @@ -1,398 +0,0 @@ -import os.path -import re - -from c_analyzer.common.info import ID -from c_analyzer.common.util import read_tsv, write_tsv - -from . import DATA_DIR - -# XXX need tests: -# * generate / script - - -IGNORED_FILE = os.path.join(DATA_DIR, 'ignored.tsv') - -IGNORED_COLUMNS = ('filename', 'funcname', 'name', 'kind', 'reason') -IGNORED_HEADER = '\t'.join(IGNORED_COLUMNS) - -# XXX Move these to ignored.tsv. -IGNORED = { - # global - 'PyImport_FrozenModules': 'process-global', - 'M___hello__': 'process-global', - 'inittab_copy': 'process-global', - 'PyHash_Func': 'process-global', - '_Py_HashSecret_Initialized': 'process-global', - '_TARGET_LOCALES': 'process-global', - - # startup (only changed before/during) - '_PyRuntime': 'runtime startup', - 'runtime_initialized': 'runtime startup', - 'static_arg_parsers': 'runtime startup', - 'orig_argv': 'runtime startup', - 'opt_ptr': 'runtime startup', - '_preinit_warnoptions': 'runtime startup', - '_Py_StandardStreamEncoding': 'runtime startup', - 'Py_FileSystemDefaultEncoding': 'runtime startup', - '_Py_StandardStreamErrors': 'runtime startup', - 'Py_FileSystemDefaultEncodeErrors': 'runtime startup', - 'Py_BytesWarningFlag': 'runtime startup', - 'Py_DebugFlag': 'runtime startup', - 'Py_DontWriteBytecodeFlag': 'runtime startup', - 'Py_FrozenFlag': 'runtime startup', - 'Py_HashRandomizationFlag': 'runtime startup', - 'Py_IgnoreEnvironmentFlag': 'runtime startup', - 'Py_InspectFlag': 'runtime startup', - 'Py_InteractiveFlag': 'runtime startup', - 'Py_IsolatedFlag': 'runtime startup', - 'Py_NoSiteFlag': 'runtime startup', - 'Py_NoUserSiteDirectory': 'runtime startup', - 'Py_OptimizeFlag': 'runtime startup', - 'Py_QuietFlag': 'runtime startup', - 'Py_UTF8Mode': 'runtime startup', - 'Py_UnbufferedStdioFlag': 'runtime startup', - 'Py_VerboseFlag': 'runtime startup', - '_Py_path_config': 'runtime startup', - '_PyOS_optarg': 'runtime startup', - '_PyOS_opterr': 'runtime startup', - '_PyOS_optind': 'runtime startup', - '_Py_HashSecret': 'runtime startup', - - # REPL - '_PyOS_ReadlineLock': 'repl', - '_PyOS_ReadlineTState': 'repl', - - # effectively const - 'tracemalloc_empty_traceback': 'const', - '_empty_bitmap_node': 'const', - 'posix_constants_pathconf': 'const', - 'posix_constants_confstr': 'const', - 'posix_constants_sysconf': 'const', - '_PySys_ImplCacheTag': 'const', - '_PySys_ImplName': 'const', - 'PyImport_Inittab': 'const', - '_PyImport_DynLoadFiletab': 'const', - '_PyParser_Grammar': 'const', - 'Py_hexdigits': 'const', - '_PyImport_Inittab': 'const', - '_PyByteArray_empty_string': 'const', - '_PyLong_DigitValue': 'const', - '_Py_SwappedOp': 'const', - 'PyStructSequence_UnnamedField': 'const', - - # signals are main-thread only - 'faulthandler_handlers': 'signals are main-thread only', - 'user_signals': 'signals are main-thread only', - 'wakeup': 'signals are main-thread only', - - # hacks - '_PySet_Dummy': 'only used as a placeholder', - } - -BENIGN = 'races here are benign and unlikely' - - -def is_supported(variable, ignored=None, known=None, *, - _ignored=(lambda *a, **k: _is_ignored(*a, **k)), - _vartype_okay=(lambda *a, **k: _is_vartype_okay(*a, **k)), - ): - """Return True if the given global variable is okay in CPython.""" - if _ignored(variable, - ignored and ignored.get('variables')): - return True - elif _vartype_okay(variable.vartype, - ignored.get('types')): - return True - else: - return False - - -def _is_ignored(variable, ignoredvars=None, *, - _IGNORED=IGNORED, - ): - """Return the reason if the variable is a supported global. - - Return None if the variable is not a supported global. - """ - if ignoredvars and (reason := ignoredvars.get(variable.id)): - return reason - - if variable.funcname is None: - if reason := _IGNORED.get(variable.name): - return reason - - # compiler - if variable.filename == 'Python/graminit.c': - if variable.vartype.startswith('static state '): - return 'compiler' - if variable.filename == 'Python/symtable.c': - if variable.vartype.startswith('static identifier '): - return 'compiler' - if variable.filename == 'Python/Python-ast.c': - # These should be const. - if variable.name.endswith('_field'): - return 'compiler' - if variable.name.endswith('_attribute'): - return 'compiler' - - # other - if variable.filename == 'Python/dtoa.c': - # guarded by lock? - if variable.name in ('p5s', 'freelist'): - return 'dtoa is thread-safe?' - if variable.name in ('private_mem', 'pmem_next'): - return 'dtoa is thread-safe?' - if variable.filename == 'Python/thread.c': - # Threads do not become an issue until after these have been set - # and these never get changed after that. - if variable.name in ('initialized', 'thread_debug'): - return 'thread-safe' - if variable.filename == 'Python/getversion.c': - if variable.name == 'version': - # Races are benign here, as well as unlikely. - return BENIGN - if variable.filename == 'Python/fileutils.c': - if variable.name == 'force_ascii': - return BENIGN - if variable.name == 'ioctl_works': - return BENIGN - if variable.name == '_Py_open_cloexec_works': - return BENIGN - if variable.filename == 'Python/codecs.c': - if variable.name == 'ucnhash_CAPI': - return BENIGN - if variable.filename == 'Python/bootstrap_hash.c': - if variable.name == 'getrandom_works': - return BENIGN - if variable.filename == 'Objects/unicodeobject.c': - if variable.name == 'ucnhash_CAPI': - return BENIGN - if variable.name == 'bloom_linebreak': - # *mostly* benign - return BENIGN - if variable.filename == 'Modules/getbuildinfo.c': - if variable.name == 'buildinfo': - # The static is used for pre-allocation. - return BENIGN - if variable.filename == 'Modules/posixmodule.c': - if variable.name == 'ticks_per_second': - return BENIGN - if variable.name == 'dup3_works': - return BENIGN - if variable.filename == 'Modules/timemodule.c': - if variable.name == 'ticks_per_second': - return BENIGN - if variable.filename == 'Objects/longobject.c': - if variable.name == 'log_base_BASE': - return BENIGN - if variable.name == 'convwidth_base': - return BENIGN - if variable.name == 'convmultmax_base': - return BENIGN - - return None - - -def _is_vartype_okay(vartype, ignoredtypes=None): - if _is_object(vartype): - return None - - if vartype.startswith('static const '): - return 'const' - if vartype.startswith('const '): - return 'const' - - # components for TypeObject definitions - for name in ('PyMethodDef', 'PyGetSetDef', 'PyMemberDef'): - if name in vartype: - return 'const' - for name in ('PyNumberMethods', 'PySequenceMethods', 'PyMappingMethods', - 'PyBufferProcs', 'PyAsyncMethods'): - if name in vartype: - return 'const' - for name in ('slotdef', 'newfunc'): - if name in vartype: - return 'const' - - # structseq - for name in ('PyStructSequence_Desc', 'PyStructSequence_Field'): - if name in vartype: - return 'const' - - # other definiitions - if 'PyModuleDef' in vartype: - return 'const' - - # thread-safe - if '_Py_atomic_int' in vartype: - return 'thread-safe' - if 'pthread_condattr_t' in vartype: - return 'thread-safe' - - # startup - if '_Py_PreInitEntry' in vartype: - return 'startup' - - # global -# if 'PyMemAllocatorEx' in vartype: -# return True - - # others -# if 'PyThread_type_lock' in vartype: -# return True - - # XXX ??? - # _Py_tss_t - # _Py_hashtable_t - # stack_t - # _PyUnicode_Name_CAPI - - # functions - if '(' in vartype and '[' not in vartype: - return 'function pointer' - - # XXX finish! - # * allow const values? - #raise NotImplementedError - return None - - -PYOBJECT_RE = re.compile(r''' - ^ - ( - # must start with "static " - static \s+ - ( - identifier - ) - \b - ) | - ( - # may start with "static " - ( static \s+ )? - ( - .* - ( - PyObject | - PyTypeObject | - _? Py \w+ Object | - _PyArg_Parser | - _Py_Identifier | - traceback_t | - PyAsyncGenASend | - _PyAsyncGenWrappedValue | - PyContext | - method_cache_entry - ) - \b - ) | - ( - ( - _Py_IDENTIFIER | - _Py_static_string - ) - [(] - ) - ) - ''', re.VERBOSE) - - -def _is_object(vartype): - if 'PyDictKeysObject' in vartype: - return False - if PYOBJECT_RE.match(vartype): - return True - if vartype.endswith((' _Py_FalseStruct', ' _Py_TrueStruct')): - return True - - # XXX Add more? - - #for part in vartype.split(): - # # XXX const is automatic True? - # if part == 'PyObject' or part.startswith('PyObject['): - # return True - return False - - -def ignored_from_file(infile, *, - _read_tsv=read_tsv, - ): - """Yield a Variable for each ignored var in the file.""" - ignored = { - 'variables': {}, - #'types': {}, - #'constants': {}, - #'macros': {}, - } - for row in _read_tsv(infile, IGNORED_HEADER): - filename, funcname, name, kind, reason = row - if not funcname or funcname == '-': - funcname = None - id = ID(filename, funcname, name) - if kind == 'variable': - values = ignored['variables'] - else: - raise ValueError(f'unsupported kind in row {row}') - values[id] = reason - return ignored - - -################################## -# generate - -def _get_row(varid, reason): - return ( - varid.filename, - varid.funcname or '-', - varid.name, - 'variable', - str(reason), - ) - - -def _get_rows(variables, ignored=None, *, - _as_row=_get_row, - _is_ignored=_is_ignored, - _vartype_okay=_is_vartype_okay, - ): - count = 0 - for variable in variables: - reason = _is_ignored(variable, - ignored and ignored.get('variables'), - ) - if not reason: - reason = _vartype_okay(variable.vartype, - ignored and ignored.get('types')) - if not reason: - continue - - print(' ', variable, repr(reason)) - yield _as_row(variable.id, reason) - count += 1 - print(f'total: {count}') - - -def _generate_ignored_file(variables, filename=None, *, - _generate_rows=_get_rows, - _write_tsv=write_tsv, - ): - if not filename: - filename = IGNORED_FILE + '.new' - rows = _generate_rows(variables) - _write_tsv(filename, IGNORED_HEADER, rows) - - -if __name__ == '__main__': - from cpython import SOURCE_DIRS - from cpython.known import ( - from_file as known_from_file, - DATA_FILE as KNOWN_FILE, - ) - # XXX This is wrong! - from . import find - known = known_from_file(KNOWN_FILE) - knownvars = (known or {}).get('variables') - variables = find.globals_from_binary(knownvars=knownvars, - dirnames=SOURCE_DIRS) - - _generate_ignored_file(variables) diff --git a/Tools/c-analyzer/ignored-globals.txt b/Tools/c-analyzer/ignored-globals.txt deleted file mode 100644 index ce6d1d805147b..0000000000000 --- a/Tools/c-analyzer/ignored-globals.txt +++ /dev/null @@ -1,492 +0,0 @@ -# All variables declared here are shared between all interpreters -# in a single process. That means that they must not be changed -# unless that change should apply to all interpreters. -# -# See check-c-globals.py. -# -# Many generic names are handled via the script: -# -# * most exceptions and all warnings handled via _is_exception() -# * for builtin modules, generic names are handled via _is_module() -# * generic names for static types handled via _is_type_var() -# * AST vars handled via _is_compiler() - - -####################################### -# main - -# Modules/getpath.c -exec_prefix -module_search_path -prefix -progpath - -# Modules/main.c -orig_argc -orig_argv - -# Python/getopt.c -opt_ptr -_PyOS_optarg -_PyOS_opterr -_PyOS_optind - - -####################################### -# REPL - -# Parser/myreadline.c -PyOS_InputHook -PyOS_ReadlineFunctionPointer -_PyOS_ReadlineLock -_PyOS_ReadlineTState - - -####################################### -# state - -# Python/dtoa.c -p5s -pmem_next # very slight race -private_mem # very slight race - -# Python/import.c -# For the moment the import lock stays global. Ultimately there should -# be a global lock for extension modules and a per-interpreter lock. -import_lock -import_lock_level -import_lock_thread - -# Python/pylifecycle.c -_PyRuntime - - -#--------------------------------- -# module globals (PyObject) - -# Modules/_functoolsmodule.c -kwd_mark - -# Modules/_localemodule.c -Error - -# Modules/_threadmodule.c -ThreadError - -# Modules/_tracemalloc.c -unknown_filename - -# Modules/gcmodule.c -gc_str - -# Modules/posixmodule.c -billion -posix_putenv_garbage - -# Modules/signalmodule.c -DefaultHandler -IgnoreHandler -IntHandler -ItimerError - -# Modules/zipimport.c -ZipImportError -zip_directory_cache - - -#--------------------------------- -# module globals (other) - -# Modules/_tracemalloc.c -allocators -tables_lock -tracemalloc_config -tracemalloc_empty_traceback -tracemalloc_filenames -tracemalloc_peak_traced_memory -tracemalloc_reentrant_key -tracemalloc_traceback -tracemalloc_tracebacks -tracemalloc_traced_memory -tracemalloc_traces - -# Modules/faulthandler.c -fatal_error -faulthandler_handlers -old_stack -stack -thread -user_signals - -# Modules/posixmodule.c -posix_constants_confstr -posix_constants_pathconf -posix_constants_sysconf -structseq_new -ticks_per_second - -# Modules/signalmodule.c -Handlers # main thread only -is_tripped # main thread only -main_pid -main_thread -old_siginthandler -wakeup_fd # main thread only - -# Modules/zipimport.c -zip_searchorder - -# Python/bltinmodule.c -Py_FileSystemDefaultEncodeErrors -Py_FileSystemDefaultEncoding -Py_HasFileSystemDefaultEncoding - -# Python/sysmodule.c -_PySys_ImplCacheTag -_PySys_ImplName - - -#--------------------------------- -# freelists - -# Modules/_collectionsmodule.c -freeblocks -numfreeblocks - -# Objects/classobject.c -free_list -numfree - -# Objects/dictobject.c -free_list -keys_free_list -numfree -numfreekeys - -# Objects/exceptions.c -memerrors_freelist -memerrors_numfree - -# Objects/floatobject.c -free_list -numfree - -# Objects/frameobject.c -free_list -numfree - -# Objects/genobject.c -ag_asend_freelist -ag_asend_freelist_free -ag_value_freelist -ag_value_freelist_free - -# Objects/listobject.c -free_list -numfree - -# Objects/methodobject.c -free_list -numfree - -# Objects/sliceobject.c -slice_cache # slight race - -# Objects/tupleobject.c -free_list -numfree - -# Python/dtoa.c -freelist # very slight race - - -#--------------------------------- -# caches (PyObject) - -# Objects/typeobject.c -method_cache # only for static types -next_version_tag # only for static types - -# Python/dynload_shlib.c -handles # slight race during import -nhandles # slight race during import - -# Python/import.c -extensions # slight race on init during import - - -#--------------------------------- -# caches (other) - -# Python/bootstrap_hash.c -urandom_cache - -# Python/modsupport.c -_Py_PackageContext # Slight race during import! Move to PyThreadState? - - -#--------------------------------- -# counters - -# Objects/bytesobject.c -null_strings -one_strings - -# Objects/dictobject.c -pydict_global_version - -# Objects/moduleobject.c -max_module_number # slight race during import - - -####################################### -# constants - -#--------------------------------- -# singletons - -# Objects/boolobject.c -_Py_FalseStruct -_Py_TrueStruct - -# Objects/object.c -_Py_NoneStruct -_Py_NotImplementedStruct - -# Objects/sliceobject.c -_Py_EllipsisObject - - -#--------------------------------- -# constants (other) - -# Modules/config.c -_PyImport_Inittab - -# Objects/bytearrayobject.c -_PyByteArray_empty_string - -# Objects/dictobject.c -empty_keys_struct -empty_values - -# Objects/floatobject.c -detected_double_format -detected_float_format -double_format -float_format - -# Objects/longobject.c -_PyLong_DigitValue - -# Objects/object.c -_Py_SwappedOp - -# Objects/obmalloc.c -_PyMem_Debug - -# Objects/setobject.c -_dummy_struct - -# Objects/structseq.c -PyStructSequence_UnnamedField - -# Objects/typeobject.c -name_op -slotdefs # almost -slotdefs_initialized # almost -subtype_getsets_dict_only -subtype_getsets_full -subtype_getsets_weakref_only -tp_new_methoddef - -# Objects/unicodeobject.c -bloom_linebreak -static_strings # slight race - -# Parser/tokenizer.c -_PyParser_TokenNames - -# Python/Python-ast.c -alias_fields - -# Python/codecs.c -Py_hexdigits -ucnhash_CAPI # slight performance-only race - -# Python/dynload_shlib.c -_PyImport_DynLoadFiletab - -# Python/fileutils.c -_Py_open_cloexec_works -force_ascii - -# Python/frozen.c -M___hello__ -PyImport_FrozenModules - -# Python/graminit.c -_PyParser_Grammar -dfas -labels - -# Python/import.c -PyImport_Inittab - -# Python/pylifecycle.c -_TARGET_LOCALES - - -#--------------------------------- -# initialized (PyObject) - -# Objects/bytesobject.c -characters -nullstring - -# Objects/exceptions.c -PyExc_RecursionErrorInst -errnomap - -# Objects/longobject.c -_PyLong_One -_PyLong_Zero -small_ints - -# Objects/setobject.c -emptyfrozenset - -# Objects/unicodeobject.c -interned # slight race on init in PyUnicode_InternInPlace() -unicode_empty -unicode_latin1 - - -#--------------------------------- -# initialized (other) - -# Python/getargs.c -static_arg_parsers - -# Python/pyhash.c -PyHash_Func -_Py_HashSecret -_Py_HashSecret_Initialized - -# Python/pylifecycle.c -_Py_StandardStreamEncoding -_Py_StandardStreamErrors -default_home -env_home -progname -Py_BytesWarningFlag -Py_DebugFlag -Py_DontWriteBytecodeFlag -Py_FrozenFlag -Py_HashRandomizationFlag -Py_IgnoreEnvironmentFlag -Py_InspectFlag -Py_InteractiveFlag -Py_IsolatedFlag -Py_NoSiteFlag -Py_NoUserSiteDirectory -Py_OptimizeFlag -Py_QuietFlag -Py_UnbufferedStdioFlag -Py_VerboseFlag - - -#--------------------------------- -# types - -# Modules/_threadmodule.c -Locktype -RLocktype -localdummytype -localtype - -# Objects/exceptions.c -PyExc_BaseException -PyExc_Exception -PyExc_GeneratorExit -PyExc_KeyboardInterrupt -PyExc_StopAsyncIteration -PyExc_StopIteration -PyExc_SystemExit -_PyExc_BaseException -_PyExc_Exception -_PyExc_GeneratorExit -_PyExc_KeyboardInterrupt -_PyExc_StopAsyncIteration -_PyExc_StopIteration -_PyExc_SystemExit - -# Objects/structseq.c -_struct_sequence_template - - -#--------------------------------- -# interned strings/bytes - -# Modules/_io/_iomodule.c -_PyIO_empty_bytes -_PyIO_empty_str -_PyIO_str_close -_PyIO_str_closed -_PyIO_str_decode -_PyIO_str_encode -_PyIO_str_fileno -_PyIO_str_flush -_PyIO_str_getstate -_PyIO_str_isatty -_PyIO_str_newlines -_PyIO_str_nl -_PyIO_str_read -_PyIO_str_read1 -_PyIO_str_readable -_PyIO_str_readall -_PyIO_str_readinto -_PyIO_str_readline -_PyIO_str_reset -_PyIO_str_seek -_PyIO_str_seekable -_PyIO_str_setstate -_PyIO_str_tell -_PyIO_str_truncate -_PyIO_str_writable -_PyIO_str_write - -# Modules/_threadmodule.c -str_dict - -# Objects/boolobject.c -false_str -true_str - -# Objects/listobject.c -indexerr - -# Python/symtable.c -__class__ -dictcomp -genexpr -lambda -listcomp -setcomp -top - -# Python/sysmodule.c -whatstrings - - -####################################### -# hacks - -# Objects/object.c -_Py_abstract_hack - -# Objects/setobject.c -_PySet_Dummy - -# Python/pylifecycle.c -_PyOS_mystrnicmp_hack diff --git a/Tools/c-analyzer/ignored.tsv b/Tools/c-analyzer/ignored.tsv deleted file mode 100644 index a0e0e503da6ab..0000000000000 --- a/Tools/c-analyzer/ignored.tsv +++ /dev/null @@ -1 +0,0 @@ -filename funcname name kind reason diff --git a/Tools/c-analyzer/known.tsv b/Tools/c-analyzer/known.tsv deleted file mode 100644 index f8c12a3944d9b..0000000000000 --- a/Tools/c-analyzer/known.tsv +++ /dev/null @@ -1,1927 +0,0 @@ -filename funcname name kind declaration -Modules/_abc.c - _abc_data_type variable static PyTypeObject _abc_data_type -Modules/_abc.c - abc_invalidation_counter variable static unsigned long long abc_invalidation_counter -Modules/_abc.c - _abcmodule variable static struct PyModuleDef _abcmodule -Python/import.c import_find_and_load accumulated variable static _PyTime_t accumulated -Modules/itertoolsmodule.c - accumulate_methods variable static PyMethodDef accumulate_methods -Modules/itertoolsmodule.c - accumulate_type variable static PyTypeObject accumulate_type -Python/Python-ast.c - Add_singleton variable static PyObject *Add_singleton -Python/Python-ast.c - Add_type variable static PyTypeObject *Add_type -Objects/genobject.c - ag_asend_freelist variable static PyAsyncGenASend *ag_asend_freelist[_PyAsyncGen_MAXFREELIST] -Objects/genobject.c - ag_asend_freelist_free variable static int ag_asend_freelist_free -Objects/genobject.c - ag_value_freelist variable static _PyAsyncGenWrappedValue *ag_value_freelist[_PyAsyncGen_MAXFREELIST] -Objects/genobject.c - ag_value_freelist_free variable static int ag_value_freelist_free -Python/Python-ast.c - alias_fields variable static const char *alias_fields[] -Python/Python-ast.c - alias_type variable static PyTypeObject *alias_type -Modules/_tracemalloc.c - allocators variable static struct { PyMemAllocatorEx mem; PyMemAllocatorEx raw; PyMemAllocatorEx obj; } allocators -Python/Python-ast.c - And_singleton variable static PyObject *And_singleton -Python/Python-ast.c - And_type variable static PyTypeObject *And_type -Python/Python-ast.c - AnnAssign_fields variable static const char *AnnAssign_fields[] -Python/Python-ast.c - AnnAssign_type variable static PyTypeObject *AnnAssign_type -Python/compile.c - __annotations__ variable static PyObject *__annotations__ -Objects/obmalloc.c - arenas variable static struct arena_object* arenas -Python/Python-ast.c - arg_attributes variable static const char *arg_attributes[] -Python/Python-ast.c - arg_fields variable static const char *arg_fields[] -Python/Python-ast.c - arg_type variable static PyTypeObject *arg_type -Python/Python-ast.c - arguments_fields variable static const char *arguments_fields[] -Python/Python-ast.c - arguments_type variable static PyTypeObject *arguments_type -Python/Python-ast.c - Assert_fields variable static const char *Assert_fields[] -Python/compile.c compiler_assert assertion_error variable static PyObject *assertion_error -Python/Python-ast.c - Assert_type variable static PyTypeObject *Assert_type -Python/Python-ast.c - Assign_fields variable static const char *Assign_fields[] -Python/Python-ast.c - Assign_type variable static PyTypeObject *Assign_type -Python/Python-ast.c - _astmodule variable static struct PyModuleDef _astmodule -Python/Python-ast.c - AST_type variable static PyTypeObject AST_type -Python/Python-ast.c - ast_type_getsets variable static PyGetSetDef ast_type_getsets[] -Python/Python-ast.c - ast_type_methods variable static PyMethodDef ast_type_methods -Python/Python-ast.c - AsyncFor_fields variable static const char *AsyncFor_fields[] -Python/Python-ast.c - AsyncFor_type variable static PyTypeObject *AsyncFor_type -Python/Python-ast.c - AsyncFunctionDef_fields variable static const char *AsyncFunctionDef_fields[] -Python/Python-ast.c - AsyncFunctionDef_type variable static PyTypeObject *AsyncFunctionDef_type -Objects/genobject.c - async_gen_as_async variable static PyAsyncMethods async_gen_as_async -Objects/genobject.c - async_gen_asend_as_async variable static PyAsyncMethods async_gen_asend_as_async -Objects/genobject.c - async_gen_asend_methods variable static PyMethodDef async_gen_asend_methods -Objects/genobject.c - async_gen_athrow_as_async variable static PyAsyncMethods async_gen_athrow_as_async -Objects/genobject.c - async_gen_athrow_methods variable static PyMethodDef async_gen_athrow_methods -Objects/genobject.c - async_gen_getsetlist variable static PyGetSetDef async_gen_getsetlist[] -Python/sysmodule.c - asyncgen_hooks_desc variable static PyStructSequence_Desc asyncgen_hooks_desc -Python/sysmodule.c - asyncgen_hooks_fields variable static PyStructSequence_Field asyncgen_hooks_fields[] -Python/sysmodule.c - AsyncGenHooksType variable static PyTypeObject AsyncGenHooksType -Objects/genobject.c - async_gen_memberlist variable static PyMemberDef async_gen_memberlist[] -Objects/genobject.c - async_gen_methods variable static PyMethodDef async_gen_methods -Python/Python-ast.c - AsyncWith_fields variable static const char *AsyncWith_fields[] -Python/Python-ast.c - AsyncWith_type variable static PyTypeObject *AsyncWith_type -Modules/atexitmodule.c - atexit_methods variable static PyMethodDef atexit_methods -Modules/atexitmodule.c - atexitmodule variable static struct PyModuleDef atexitmodule -Modules/atexitmodule.c - atexit_slots variable static PyModuleDef_Slot atexit_slots[] -Modules/_operator.c - attrgetter_methods variable static PyMethodDef attrgetter_methods -Modules/_operator.c - attrgetter_type variable static PyTypeObject attrgetter_type -Python/Python-ast.c - Attribute_fields variable static const char *Attribute_fields[] -Python/Python-ast.c - Attribute_type variable static PyTypeObject *Attribute_type -Python/Python-ast.c - AugAssign_fields variable static const char *AugAssign_fields[] -Python/Python-ast.c - AugAssign_type variable static PyTypeObject *AugAssign_type -Python/Python-ast.c - AugLoad_singleton variable static PyObject *AugLoad_singleton -Python/Python-ast.c - AugLoad_type variable static PyTypeObject *AugLoad_type -Python/Python-ast.c - AugStore_singleton variable static PyObject *AugStore_singleton -Python/Python-ast.c - AugStore_type variable static PyTypeObject *AugStore_type -Python/Python-ast.c - Await_fields variable static const char *Await_fields[] -Python/Python-ast.c - Await_type variable static PyTypeObject *Await_type -Objects/exceptions.c - BaseException_getset variable static PyGetSetDef BaseException_getset[] -Objects/exceptions.c - BaseException_members variable static struct PyMemberDef BaseException_members[] -Objects/exceptions.c - BaseException_methods variable static PyMethodDef BaseException_methods -Modules/posixmodule.c - billion variable static PyObject *billion -Python/Python-ast.c - BinOp_fields variable static const char *BinOp_fields[] -Python/Python-ast.c - BinOp_type variable static PyTypeObject *BinOp_type -Python/Python-ast.c - BitAnd_singleton variable static PyObject *BitAnd_singleton -Python/Python-ast.c - BitAnd_type variable static PyTypeObject *BitAnd_type -Python/Python-ast.c - BitOr_singleton variable static PyObject *BitOr_singleton -Python/Python-ast.c - BitOr_type variable static PyTypeObject *BitOr_type -Python/Python-ast.c - BitXor_singleton variable static PyObject *BitXor_singleton -Python/Python-ast.c - BitXor_type variable static PyTypeObject *BitXor_type -Objects/unicodeobject.c - bloom_linebreak variable static BLOOM_MASK bloom_linebreak -Objects/boolobject.c - bool_as_number variable static PyNumberMethods bool_as_number -Python/Python-ast.c - BoolOp_fields variable static const char *BoolOp_fields[] -Python/Python-ast.c - boolop_type variable static PyTypeObject *boolop_type -Python/Python-ast.c - BoolOp_type variable static PyTypeObject *BoolOp_type -Python/_warnings.c is_internal_frame bootstrap_string variable static PyObject *bootstrap_string -Python/Python-ast.c - Break_type variable static PyTypeObject *Break_type -Modules/_io/bufferedio.c - bufferediobase_methods variable static PyMethodDef bufferediobase_methods -Modules/_io/bufferedio.c - bufferedrandom_getset variable static PyGetSetDef bufferedrandom_getset[] -Modules/_io/bufferedio.c - bufferedrandom_members variable static PyMemberDef bufferedrandom_members[] -Modules/_io/bufferedio.c - bufferedrandom_methods variable static PyMethodDef bufferedrandom_methods -Modules/_io/bufferedio.c - bufferedreader_getset variable static PyGetSetDef bufferedreader_getset[] -Modules/_io/bufferedio.c - bufferedreader_members variable static PyMemberDef bufferedreader_members[] -Modules/_io/bufferedio.c - bufferedreader_methods variable static PyMethodDef bufferedreader_methods -Modules/_io/bufferedio.c - bufferedrwpair_getset variable static PyGetSetDef bufferedrwpair_getset[] -Modules/_io/bufferedio.c - bufferedrwpair_methods variable static PyMethodDef bufferedrwpair_methods -Modules/_io/bufferedio.c - bufferedwriter_getset variable static PyGetSetDef bufferedwriter_getset[] -Modules/_io/bufferedio.c - bufferedwriter_members variable static PyMemberDef bufferedwriter_members[] -Modules/_io/bufferedio.c - bufferedwriter_methods variable static PyMethodDef bufferedwriter_methods -Modules/getbuildinfo.c Py_GetBuildInfo buildinfo variable static char buildinfo[50 + sizeof(GITVERSION) + ((sizeof(GITTAG) > sizeof(GITBRANCH)) ? sizeof(GITTAG) : sizeof(GITBRANCH))] -Python/bltinmodule.c - builtin_methods variable static PyMethodDef builtin_methods -Python/bltinmodule.c - builtinsmodule variable static struct PyModuleDef builtinsmodule -Python/import.c PyImport_Import builtins_str variable static PyObject *builtins_str -Python/ceval.c make_pending_calls busy variable static int busy -Objects/bytearrayobject.c - bytearray_as_buffer variable static PyBufferProcs bytearray_as_buffer -Objects/bytearrayobject.c - bytearray_as_mapping variable static PyMappingMethods bytearray_as_mapping -Objects/bytearrayobject.c - bytearray_as_number variable static PyNumberMethods bytearray_as_number -Objects/bytearrayobject.c - bytearray_as_sequence variable static PySequenceMethods bytearray_as_sequence -Objects/bytearrayobject.c - bytearrayiter_methods variable static PyMethodDef bytearrayiter_methods -Objects/bytearrayobject.c - bytearray_methods variable static PyMethodDef bytearray_methods -Objects/bytesobject.c - bytes_as_buffer variable static PyBufferProcs bytes_as_buffer -Objects/bytesobject.c - bytes_as_mapping variable static PyMappingMethods bytes_as_mapping -Objects/bytesobject.c - bytes_as_number variable static PyNumberMethods bytes_as_number -Objects/bytesobject.c - bytes_as_sequence variable static PySequenceMethods bytes_as_sequence -Modules/_io/bytesio.c - bytesiobuf_as_buffer variable static PyBufferProcs bytesiobuf_as_buffer -Modules/_io/bytesio.c - bytesio_getsetlist variable static PyGetSetDef bytesio_getsetlist[] -Modules/_io/bytesio.c - bytesio_methods variable static PyMethodDef bytesio_methods -Objects/bytesobject.c - bytes_methods variable static PyMethodDef bytes_methods -Python/thread_pthread.h init_condattr ca variable static pthread_condattr_t ca -Python/Python-ast.c - Call_fields variable static const char *Call_fields[] -Objects/iterobject.c - calliter_methods variable static PyMethodDef calliter_methods -Python/Python-ast.c - Call_type variable static PyTypeObject *Call_type -Objects/cellobject.c - cell_getsetlist variable static PyGetSetDef cell_getsetlist[] -Modules/itertoolsmodule.c - chain_methods variable static PyMethodDef chain_methods -Modules/itertoolsmodule.c - chain_type variable static PyTypeObject chain_type -Objects/bytesobject.c - characters variable static PyBytesObject *characters[UCHAR_MAX + 1] -Python/symtable.c - __class__ variable static identifier __class__ -Python/Python-ast.c - ClassDef_fields variable static const char *ClassDef_fields[] -Python/Python-ast.c - ClassDef_type variable static PyTypeObject *ClassDef_type -Objects/funcobject.c - cm_getsetlist variable static PyGetSetDef cm_getsetlist[] -Objects/funcobject.c - cm_memberlist variable static PyMemberDef cm_memberlist[] -Python/Python-ast.c - cmpop_type variable static PyTypeObject *cmpop_type -Modules/_codecsmodule.c - _codecs_functions variable static PyMethodDef _codecs_functions[] -Modules/_codecsmodule.c - codecsmodule variable static struct PyModuleDef codecsmodule -Objects/codeobject.c - code_memberlist variable static PyMemberDef code_memberlist[] -Objects/codeobject.c - code_methods variable static PyMethodDef code_methods -Modules/_collectionsmodule.c - _collectionsmodule variable static struct PyModuleDef _collectionsmodule -Modules/itertoolsmodule.c - combinations_methods variable static PyMethodDef combinations_methods -Modules/itertoolsmodule.c - combinations_type variable static PyTypeObject combinations_type -Objects/typeobject.c object_new comma_id variable _Py_static_string(comma_id, "", "") -Python/Python-ast.c - Compare_fields variable static const char *Compare_fields[] -Python/Python-ast.c - Compare_type variable static PyTypeObject *Compare_type -Objects/complexobject.c - complex_as_number variable static PyNumberMethods complex_as_number -Objects/complexobject.c - complex_members variable static PyMemberDef complex_members[] -Objects/complexobject.c - complex_methods variable static PyMethodDef complex_methods -Python/Python-ast.c - comprehension_fields variable static const char *comprehension_fields[] -Python/Python-ast.c - comprehension_type variable static PyTypeObject *comprehension_type -Modules/itertoolsmodule.c - compress_methods variable static PyMethodDef compress_methods -Modules/itertoolsmodule.c - compress_type variable static PyTypeObject compress_type -Python/thread_pthread.h - condattr_monotonic variable static pthread_condattr_t *condattr_monotonic -Python/Python-ast.c - Constant_fields variable static const char *Constant_fields[] -Python/Python-ast.c - Constant_type variable static PyTypeObject *Constant_type -Python/Python-ast.c - Continue_type variable static PyTypeObject *Continue_type -Objects/longobject.c PyLong_FromString convmultmax_base variable static twodigits convmultmax_base[37] -Objects/longobject.c PyLong_FromString convwidth_base variable static int convwidth_base[37] -Objects/genobject.c - coro_as_async variable static PyAsyncMethods coro_as_async -Objects/genobject.c - coro_getsetlist variable static PyGetSetDef coro_getsetlist[] -Objects/genobject.c - coro_memberlist variable static PyMemberDef coro_memberlist[] -Objects/genobject.c - coro_methods variable static PyMethodDef coro_methods -Objects/genobject.c - coro_wrapper_methods variable static PyMethodDef coro_wrapper_methods -Modules/itertoolsmodule.c - count_methods variable static PyMethodDef count_methods -Modules/itertoolsmodule.c - count_type variable static PyTypeObject count_type -Python/context.c - ctx_freelist variable static PyContext *ctx_freelist -Python/context.c - ctx_freelist_len variable static int ctx_freelist_len -Modules/itertoolsmodule.c - cwr_methods variable static PyMethodDef cwr_methods -Modules/itertoolsmodule.c - cwr_type variable static PyTypeObject cwr_type -Modules/itertoolsmodule.c - cycle_methods variable static PyMethodDef cycle_methods -Modules/itertoolsmodule.c - cycle_type variable static PyTypeObject cycle_type -Objects/obmalloc.c new_arena debug_stats variable static int debug_stats -Modules/signalmodule.c - DefaultHandler variable static PyObject *DefaultHandler -Modules/_collectionsmodule.c - defdict_members variable static PyMemberDef defdict_members[] -Modules/_collectionsmodule.c - defdict_methods variable static PyMethodDef defdict_methods -Modules/_collectionsmodule.c - defdict_type variable static PyTypeObject defdict_type -Python/Python-ast.c - Delete_fields variable static const char *Delete_fields[] -Python/Python-ast.c - Delete_type variable static PyTypeObject *Delete_type -Python/Python-ast.c - Del_singleton variable static PyObject *Del_singleton -Python/Python-ast.c - Del_type variable static PyTypeObject *Del_type -Modules/_collectionsmodule.c - deque_as_number variable static PyNumberMethods deque_as_number -Modules/_collectionsmodule.c - deque_as_sequence variable static PySequenceMethods deque_as_sequence -Modules/_collectionsmodule.c - deque_getset variable static PyGetSetDef deque_getset[] -Modules/_collectionsmodule.c - dequeiter_methods variable static PyMethodDef dequeiter_methods -Modules/_collectionsmodule.c - dequeiter_type variable static PyTypeObject dequeiter_type -Modules/_collectionsmodule.c - deque_methods variable static PyMethodDef deque_methods -Modules/_collectionsmodule.c - dequereviter_type variable static PyTypeObject dequereviter_type -Modules/_collectionsmodule.c - deque_type variable static PyTypeObject deque_type -Objects/descrobject.c - descr_members variable static PyMemberDef descr_members[] -Objects/descrobject.c - descr_methods variable static PyMethodDef descr_methods -Modules/_abc.c - _destroy_def variable static PyMethodDef _destroy_def -Objects/floatobject.c - detected_double_format variable static float_format_type detected_double_format -Objects/floatobject.c - detected_float_format variable static float_format_type detected_float_format -Objects/dictobject.c - dict_as_mapping variable static PyMappingMethods dict_as_mapping -Objects/dictobject.c - dict_as_sequence variable static PySequenceMethods dict_as_sequence -Python/symtable.c - dictcomp variable static identifier dictcomp -Python/Python-ast.c - DictComp_fields variable static const char *DictComp_fields[] -Python/Python-ast.c - DictComp_type variable static PyTypeObject *DictComp_type -Python/Python-ast.c - Dict_fields variable static const char *Dict_fields[] -Objects/dictobject.c - dictitems_as_sequence variable static PySequenceMethods dictitems_as_sequence -Objects/dictobject.c - dictitems_methods variable static PyMethodDef dictitems_methods -Objects/dictobject.c - dictiter_methods variable static PyMethodDef dictiter_methods -Objects/dictobject.c - dictkeys_as_sequence variable static PySequenceMethods dictkeys_as_sequence -Objects/dictobject.c - dictkeys_methods variable static PyMethodDef dictkeys_methods -Python/Python-ast.c - Dict_type variable static PyTypeObject *Dict_type -Objects/dictobject.c - dictvalues_as_sequence variable static PySequenceMethods dictvalues_as_sequence -Objects/dictobject.c - dictvalues_methods variable static PyMethodDef dictvalues_methods -Objects/dictobject.c - dictviews_as_number variable static PyNumberMethods dictviews_as_number -Modules/posixmodule.c - DirEntry_members variable static PyMemberDef DirEntry_members[] -Modules/posixmodule.c - DirEntry_methods variable static PyMethodDef DirEntry_methods -Modules/posixmodule.c - DirEntryType variable static PyTypeObject DirEntryType -Python/Python-ast.c - Div_singleton variable static PyObject *Div_singleton -Python/Python-ast.c - Div_type variable static PyTypeObject *Div_type -Python/compile.c - __doc__ variable static PyObject *__doc__ -Objects/classobject.c method_get_doc docstr variable static PyObject *docstr -Objects/classobject.c instancemethod_get_doc docstr variable static PyObject *docstr -Python/compile.c compiler_set_qualname dot variable _Py_static_string(dot, ""."") -Python/compile.c compiler_set_qualname dot_locals variable _Py_static_string(dot_locals, ""."") -Objects/floatobject.c - double_format variable static float_format_type double_format -Modules/itertoolsmodule.c - dropwhile_methods variable static PyMethodDef dropwhile_methods -Modules/itertoolsmodule.c - dropwhile_type variable static PyTypeObject dropwhile_type -Objects/setobject.c - _dummy_struct variable static PyObject _dummy_struct -Modules/posixmodule.c os_dup2_impl dup3_works variable static int dup3_works -Modules/_io/bufferedio.c _PyIO_trap_eintr eintr_int variable static PyObject *eintr_int -Objects/sliceobject.c - ellipsis_methods variable static PyMethodDef ellipsis_methods -Python/hamt.c - _empty_bitmap_node variable static PyHamtNode_Bitmap *_empty_bitmap_node -Objects/setobject.c - emptyfrozenset variable static PyObject *emptyfrozenset -Python/hamt.c - _empty_hamt variable static PyHamtObject *_empty_hamt -Objects/dictobject.c - empty_keys_struct variable static PyDictKeysObject empty_keys_struct -Objects/codeobject.c PyCode_NewEmpty emptystring variable static PyObject *emptystring -Python/compile.c compiler_from_import empty_string variable static PyObject *empty_string -Objects/dictobject.c - empty_values variable static PyObject *empty_values[1] -Objects/unicodeobject.c - encoding_map_methods variable static PyMethodDef encoding_map_methods -Objects/unicodeobject.c - EncodingMapType variable static PyTypeObject EncodingMapType -Objects/enumobject.c - enum_methods variable static PyMethodDef enum_methods -Python/Python-ast.c - Eq_singleton variable static PyObject *Eq_singleton -Python/Python-ast.c - Eq_type variable static PyTypeObject *Eq_type -Objects/exceptions.c - errnomap variable static PyObject *errnomap -Modules/errnomodule.c - errno_methods variable static PyMethodDef errno_methods -Modules/errnomodule.c - errnomodule variable static struct PyModuleDef errnomodule -Modules/_localemodule.c - Error variable static PyObject *Error -Python/Python-ast.c - excepthandler_attributes variable static const char *excepthandler_attributes[] -Python/Python-ast.c - ExceptHandler_fields variable static const char *ExceptHandler_fields[] -Python/Python-ast.c - excepthandler_type variable static PyTypeObject *excepthandler_type -Python/Python-ast.c - ExceptHandler_type variable static PyTypeObject *ExceptHandler_type -Modules/_threadmodule.c - ExceptHookArgs_desc variable static PyStructSequence_Desc ExceptHookArgs_desc -Modules/_threadmodule.c - ExceptHookArgs_fields variable static PyStructSequence_Field ExceptHookArgs_fields[] -Modules/_threadmodule.c - ExceptHookArgsType variable static PyTypeObject ExceptHookArgsType -Objects/exceptions.c _check_for_legacy_statements exec_prefix variable static PyObject *exec_prefix -Python/Python-ast.c - expr_attributes variable static const char *expr_attributes[] -Python/Python-ast.c - expr_context_type variable static PyTypeObject *expr_context_type -Python/Python-ast.c - Expression_fields variable static const char *Expression_fields[] -Python/Python-ast.c - Expression_type variable static PyTypeObject *Expression_type -Python/Python-ast.c - Expr_fields variable static const char *Expr_fields[] -Python/Python-ast.c - expr_type variable static PyTypeObject *expr_type -Python/Python-ast.c - Expr_type variable static PyTypeObject *Expr_type -Python/import.c - extensions variable static PyObject *extensions -Python/Python-ast.c - ExtSlice_fields variable static const char *ExtSlice_fields[] -Python/Python-ast.c - ExtSlice_type variable static PyTypeObject *ExtSlice_type -Objects/boolobject.c - false_str variable static PyObject *false_str -Modules/faulthandler.c - fatal_error variable static struct { int enabled; PyObject *file; int fd; int all_threads; PyInterpreterState *interp; void *exc_handler; } fatal_error -Modules/faulthandler.c - faulthandler_handlers variable static fault_handler_t faulthandler_handlers[] -Objects/stringlib/unicode_format.h - fieldnameiter_methods variable static PyMethodDef fieldnameiter_methods -Modules/_io/fileio.c - fileio_getsetlist variable static PyGetSetDef fileio_getsetlist[] -Modules/_io/fileio.c - fileio_members variable static PyMemberDef fileio_members[] -Modules/_io/fileio.c - fileio_methods variable static PyMethodDef fileio_methods -Modules/itertoolsmodule.c - filterfalse_methods variable static PyMethodDef filterfalse_methods -Modules/itertoolsmodule.c - filterfalse_type variable static PyTypeObject filterfalse_type -Python/bltinmodule.c - filter_methods variable static PyMethodDef filter_methods -Python/sysmodule.c - flags_desc variable static PyStructSequence_Desc flags_desc -Python/sysmodule.c - flags_fields variable static PyStructSequence_Field flags_fields[] -Python/sysmodule.c - FlagsType variable static PyTypeObject FlagsType -Objects/floatobject.c - float_as_number variable static PyNumberMethods float_as_number -Objects/floatobject.c - float_format variable static float_format_type -Objects/floatobject.c - float_getset variable static PyGetSetDef float_getset[] -Objects/floatobject.c - floatinfo_desc variable static PyStructSequence_Desc floatinfo_desc -Objects/floatobject.c - floatinfo_fields variable static PyStructSequence_Field floatinfo_fields[] -Objects/floatobject.c - FloatInfoType variable static PyTypeObject FloatInfoType -Objects/floatobject.c - float_methods variable static PyMethodDef float_methods -Python/Python-ast.c - FloorDiv_singleton variable static PyObject *FloorDiv_singleton -Python/Python-ast.c - FloorDiv_type variable static PyTypeObject *FloorDiv_type -Python/fileutils.c - force_ascii variable static int force_ascii -Python/Python-ast.c - For_fields variable static const char *For_fields[] -Python/Python-ast.c - FormattedValue_fields variable static const char *FormattedValue_fields[] -Python/Python-ast.c - FormattedValue_type variable static PyTypeObject *FormattedValue_type -Objects/stringlib/unicode_format.h - formatteriter_methods variable static PyMethodDef formatteriter_methods -Python/Python-ast.c - For_type variable static PyTypeObject *For_type -Objects/frameobject.c - frame_getsetlist variable static PyGetSetDef frame_getsetlist[] -Objects/frameobject.c - frame_memberlist variable static PyMemberDef frame_memberlist[] -Objects/frameobject.c - frame_methods variable static PyMethodDef frame_methods -Modules/_collectionsmodule.c - freeblocks variable static block *freeblocks[MAXFREEBLOCKS] -Python/dtoa.c - freelist variable static Bigint *freelist[Kmax+1] -Objects/floatobject.c - free_list variable static PyFloatObject *free_list -Objects/frameobject.c - free_list variable static PyFrameObject *free_list -Objects/listobject.c - free_list variable static PyListObject *free_list[PyList_MAXFREELIST] -Objects/dictobject.c - free_list variable static PyDictObject *free_list[PyDict_MAXFREELIST] -Objects/methodobject.c - free_list variable static PyCFunctionObject *free_list -Objects/tupleobject.c - free_list variable static PyTupleObject *free_list[PyTuple_MAXSAVESIZE] -Objects/classobject.c - free_list variable static PyMethodObject *free_list -Objects/setobject.c - frozenset_as_number variable static PyNumberMethods frozenset_as_number -Objects/setobject.c - frozenset_methods variable static PyMethodDef frozenset_methods -Objects/funcobject.c - func_getsetlist variable static PyGetSetDef func_getsetlist[] -Objects/funcobject.c - func_memberlist variable static PyMemberDef func_memberlist[] -Python/Python-ast.c - FunctionDef_fields variable static const char *FunctionDef_fields[] -Python/Python-ast.c - FunctionDef_type variable static PyTypeObject *FunctionDef_type -Modules/_sre.c - _functions variable static PyMethodDef _functions[] -Python/Python-ast.c - FunctionType_fields variable static const char *FunctionType_fields[] -Python/Python-ast.c - FunctionType_type variable static PyTypeObject *FunctionType_type -Modules/_functoolsmodule.c - _functoolsmodule variable static struct PyModuleDef _functoolsmodule -Modules/gcmodule.c - GcMethods variable static PyMethodDef GcMethods[] -Modules/gcmodule.c - gcmodule variable static struct PyModuleDef gcmodule -Modules/gcmodule.c - gc_str variable static PyObject *gc_str -Python/Python-ast.c - GeneratorExp_fields variable static const char *GeneratorExp_fields[] -Python/Python-ast.c - GeneratorExp_type variable static PyTypeObject *GeneratorExp_type -Python/symtable.c - genexpr variable static identifier genexpr -Objects/genobject.c - gen_getsetlist variable static PyGetSetDef gen_getsetlist[] -Objects/genobject.c - gen_memberlist variable static PyMemberDef gen_memberlist[] -Objects/genobject.c - gen_methods variable static PyMethodDef gen_methods -Python/bootstrap_hash.c py_getrandom getrandom_works variable static int getrandom_works -Objects/descrobject.c - getset_getset variable static PyGetSetDef getset_getset[] -Python/Python-ast.c - Global_fields variable static const char *Global_fields[] -Python/Python-ast.c - Global_type variable static PyTypeObject *Global_type -Modules/itertoolsmodule.c - groupby_methods variable static PyMethodDef groupby_methods -Modules/itertoolsmodule.c - groupby_type variable static PyTypeObject groupby_type -Modules/itertoolsmodule.c - _grouper_methods variable static PyMethodDef _grouper_methods -Modules/itertoolsmodule.c - _grouper_type variable static PyTypeObject _grouper_type -Python/Python-ast.c - GtE_singleton variable static PyObject *GtE_singleton -Python/Python-ast.c - GtE_type variable static PyTypeObject *GtE_type -Python/Python-ast.c - Gt_singleton variable static PyObject *Gt_singleton -Python/Python-ast.c - Gt_type variable static PyTypeObject *Gt_type -Modules/signalmodule.c - Handlers variable static volatile struct { _Py_atomic_int tripped; PyObject *func; } Handlers[NSIG] -Python/dynload_shlib.c - handles variable static struct { dev_t dev; ino_t ino; void *handle; } handles[128] -Python/sysmodule.c - hash_info_desc variable static PyStructSequence_Desc hash_info_desc -Python/sysmodule.c - hash_info_fields variable static PyStructSequence_Field hash_info_fields[] -Python/sysmodule.c - Hash_InfoType variable static PyTypeObject Hash_InfoType -Python/import.c import_find_and_load header variable static int header -Python/Python-ast.c - IfExp_fields variable static const char *IfExp_fields[] -Python/Python-ast.c - IfExp_type variable static PyTypeObject *IfExp_type -Python/Python-ast.c - If_fields variable static const char *If_fields[] -Python/Python-ast.c - If_type variable static PyTypeObject *If_type -Modules/signalmodule.c - IgnoreHandler variable static PyObject *IgnoreHandler -Python/import.c - imp_methods variable static PyMethodDef imp_methods -Python/import.c - impmodule variable static struct PyModuleDef impmodule -Objects/exceptions.c - ImportError_members variable static PyMemberDef ImportError_members[] -Objects/exceptions.c - ImportError_methods variable static PyMethodDef ImportError_methods -Python/Python-ast.c - Import_fields variable static const char *Import_fields[] -Python/Python-ast.c - ImportFrom_fields variable static const char *ImportFrom_fields[] -Python/Python-ast.c - ImportFrom_type variable static PyTypeObject *ImportFrom_type -Python/import.c import_find_and_load import_level variable static int import_level -Python/_warnings.c is_internal_frame importlib_string variable static PyObject *importlib_string -Python/import.c - import_lock variable static PyThread_type_lock import_lock -Python/import.c - import_lock_level variable static int import_lock_level -Python/import.c - import_lock_thread variable static unsigned long import_lock_thread -Python/import.c PyImport_Import import_str variable static PyObject *import_str -Python/Python-ast.c - Import_type variable static PyTypeObject *Import_type -Modules/_io/textio.c - incrementalnewlinedecoder_getset variable static PyGetSetDef incrementalnewlinedecoder_getset[] -Modules/_io/textio.c - incrementalnewlinedecoder_methods variable static PyMethodDef incrementalnewlinedecoder_methods -Objects/listobject.c - indexerr variable static PyObject *indexerr -Python/Python-ast.c - Index_fields variable static const char *Index_fields[] -Python/Python-ast.c - Index_type variable static PyTypeObject *Index_type -Python/thread.c - initialized variable static int initialized -Modules/posixmodule.c - initialized variable static int initialized -Modules/pwdmodule.c - initialized variable static int initialized -Modules/signalmodule.c - initialized variable static int initialized -Modules/timemodule.c - initialized variable static int initialized -Python/Python-ast.c init_types initialized variable static int initialized -Objects/listobject.c PyList_New initialized variable static int initialized -Python/import.c - inittab_copy variable static struct _inittab *inittab_copy -Python/Python-ast.c - In_singleton variable static PyObject *In_singleton -Objects/classobject.c - instancemethod_getset variable static PyGetSetDef instancemethod_getset[] -Objects/classobject.c - instancemethod_memberlist variable static PyMemberDef instancemethod_memberlist[] -Python/Python-ast.c - Interactive_fields variable static const char *Interactive_fields[] -Python/Python-ast.c - Interactive_type variable static PyTypeObject *Interactive_type -Objects/unicodeobject.c - interned variable static PyObject *interned -Objects/interpreteridobject.c - interpid_as_number variable static PyNumberMethods interpid_as_number -Modules/signalmodule.c - IntHandler variable static PyObject *IntHandler -Objects/longobject.c - int_info_desc variable static PyStructSequence_Desc int_info_desc -Objects/longobject.c - int_info_fields variable static PyStructSequence_Field int_info_fields[] -Objects/longobject.c - Int_InfoType variable static PyTypeObject Int_InfoType -Python/Python-ast.c - In_type variable static PyTypeObject *In_type -Python/Python-ast.c - Invert_singleton variable static PyObject *Invert_singleton -Python/Python-ast.c - Invert_type variable static PyTypeObject *Invert_type -Modules/_io/iobase.c - iobase_getset variable static PyGetSetDef iobase_getset[] -Modules/_io/iobase.c - iobase_methods variable static PyMethodDef iobase_methods -Python/fileutils.c set_inheritable ioctl_works variable static int ioctl_works -Modules/itertoolsmodule.c - islice_methods variable static PyMethodDef islice_methods -Modules/itertoolsmodule.c - islice_type variable static PyTypeObject islice_type -Python/Python-ast.c - IsNot_singleton variable static PyObject *IsNot_singleton -Python/Python-ast.c - IsNot_type variable static PyTypeObject *IsNot_type -Python/Python-ast.c - Is_singleton variable static PyObject *Is_singleton -Modules/signalmodule.c - is_tripped variable static _Py_atomic_int is_tripped -Python/Python-ast.c - Is_type variable static PyTypeObject *Is_type -Modules/_operator.c - itemgetter_methods variable static PyMethodDef itemgetter_methods -Modules/_operator.c - itemgetter_type variable static PyTypeObject itemgetter_type -Modules/itertoolsmodule.c - itertoolsmodule variable static struct PyModuleDef itertoolsmodule -Modules/signalmodule.c - ItimerError variable static PyObject *ItimerError -Python/Python-ast.c - JoinedStr_fields variable static const char *JoinedStr_fields[] -Python/Python-ast.c - JoinedStr_type variable static PyTypeObject *JoinedStr_type -Modules/_functoolsmodule.c - keyobject_members variable static PyMemberDef keyobject_members[] -Modules/_functoolsmodule.c - keyobject_type variable static PyTypeObject keyobject_type -Objects/dictobject.c - keys_free_list variable static PyDictKeysObject *keys_free_list[PyDict_MAXFREELIST] -Python/Python-ast.c - keyword_fields variable static const char *keyword_fields[] -Python/sysmodule.c sys_set_asyncgen_hooks keywords variable static const char *keywords[] -Modules/_bisectmodule.c bisect_right keywords variable static const char *keywords[] -Modules/_bisectmodule.c insort_right keywords variable static const char *keywords[] -Python/Python-ast.c - keyword_type variable static PyTypeObject *keyword_type -Modules/_functoolsmodule.c keyobject_call kwargs variable static const char *kwargs[] -Modules/_functoolsmodule.c functools_cmp_to_key kwargs variable static const char *kwargs[] -Modules/itertoolsmodule.c repeat_new kwargs variable static const char *kwargs[] -Python/_warnings.c warnings_warn_explicit kwd_list variable static const char *kwd_list[] -Modules/_functoolsmodule.c - kwd_mark variable static PyObject *kwd_mark -Python/bltinmodule.c builtin___import__ kwlist variable static const char *kwlist[] -Python/bltinmodule.c min_max kwlist variable static const char *kwlist[] -Python/context.c contextvar_tp_new kwlist variable static const char *kwlist[] -Python/sysmodule.c sys_getsizeof kwlist variable static const char *kwlist[] -Objects/bytearrayobject.c bytearray_init kwlist variable static const char *kwlist[] -Objects/bytesobject.c bytes_new kwlist variable static const char *kwlist[] -Objects/exceptions.c ImportError_init kwlist variable static const char *kwlist[] -Objects/interpreteridobject.c interpid_new kwlist variable static const char *kwlist[] -Objects/memoryobject.c memory_new kwlist variable static const char *kwlist[] -Objects/memoryobject.c memory_cast kwlist variable static const char *kwlist[] -Objects/memoryobject.c memory_tobytes kwlist variable static const char *kwlist[] -Objects/odictobject.c odict_pop kwlist variable static const char *kwlist[] -Objects/unicodeobject.c unicode_new kwlist variable static const char *kwlist[] -Objects/weakrefobject.c weakref_call kwlist variable static const char *kwlist[] -Modules/_elementtree.c element_setstate_from_Python kwlist variable static const char *kwlist[] -Modules/_json.c scanner_call kwlist variable static const char *kwlist[] -Modules/_json.c scanner_new kwlist variable static const char *kwlist[] -Modules/_json.c encoder_new kwlist variable static const char *kwlist[] -Modules/_json.c encoder_call kwlist variable static const char *kwlist[] -Python/symtable.c - lambda variable static identifier lambda -Python/Python-ast.c - Lambda_fields variable static const char *Lambda_fields[] -Python/Python-ast.c - Lambda_type variable static PyTypeObject *Lambda_type -Objects/listobject.c - list_as_mapping variable static PyMappingMethods list_as_mapping -Objects/listobject.c - list_as_sequence variable static PySequenceMethods list_as_sequence -Python/symtable.c - listcomp variable static identifier listcomp -Python/Python-ast.c - ListComp_fields variable static const char *ListComp_fields[] -Python/Python-ast.c - ListComp_type variable static PyTypeObject *ListComp_type -Python/Python-ast.c - List_fields variable static const char *List_fields[] -Objects/listobject.c - listiter_methods variable static PyMethodDef listiter_methods -Objects/listobject.c - list_methods variable static PyMethodDef list_methods -Objects/listobject.c - listreviter_methods variable static PyMethodDef listreviter_methods -Python/Python-ast.c - List_type variable static PyTypeObject *List_type -Python/ceval.c - lltrace variable static int lltrace -Python/Python-ast.c - Load_singleton variable static PyObject *Load_singleton -Python/Python-ast.c - Load_type variable static PyTypeObject *Load_type -Modules/_threadmodule.c - localdummytype variable static PyTypeObject localdummytype -Modules/_localemodule.c - _localemodule variable static struct PyModuleDef _localemodule -Modules/_threadmodule.c - localtype variable static PyTypeObject localtype -Modules/_threadmodule.c - lock_methods variable static PyMethodDef lock_methods -Modules/_threadmodule.c - Locktype variable static PyTypeObject Locktype -Objects/longobject.c PyLong_FromString log_base_BASE variable static double log_base_BASE[37] -Objects/longobject.c - long_as_number variable static PyNumberMethods long_as_number -Objects/longobject.c - long_getset variable static PyGetSetDef long_getset[] -Objects/longobject.c - long_methods variable static PyMethodDef long_methods -Objects/rangeobject.c - longrangeiter_methods variable static PyMethodDef longrangeiter_methods -Modules/_functoolsmodule.c - lru_cache_getsetlist variable static PyGetSetDef lru_cache_getsetlist[] -Modules/_functoolsmodule.c - lru_cache_methods variable static PyMethodDef lru_cache_methods -Modules/_functoolsmodule.c - lru_cache_type variable static PyTypeObject lru_cache_type -Modules/_functoolsmodule.c - lru_list_elem_type variable static PyTypeObject lru_list_elem_type -Python/Python-ast.c - LShift_singleton variable static PyObject *LShift_singleton -Python/Python-ast.c - LShift_type variable static PyTypeObject *LShift_type -Python/Python-ast.c - LtE_singleton variable static PyObject *LtE_singleton -Python/Python-ast.c - LtE_type variable static PyTypeObject *LtE_type -Python/Python-ast.c - Lt_singleton variable static PyObject *Lt_singleton -Python/Python-ast.c - Lt_type variable static PyTypeObject *Lt_type -Python/bltinmodule.c - map_methods variable static PyMethodDef map_methods -Objects/descrobject.c - mappingproxy_as_mapping variable static PyMappingMethods mappingproxy_as_mapping -Objects/descrobject.c - mappingproxy_as_sequence variable static PySequenceMethods mappingproxy_as_sequence -Objects/descrobject.c - mappingproxy_methods variable static PyMethodDef mappingproxy_methods -Objects/dictobject.c - mapp_methods variable static PyMethodDef mapp_methods -Python/marshal.c - marshal_methods variable static PyMethodDef marshal_methods -Python/marshal.c - marshalmodule variable static struct PyModuleDef marshalmodule -Modules/_sre.c - match_as_mapping variable static PyMappingMethods match_as_mapping -Modules/_sre.c - match_getset variable static PyGetSetDef match_getset[] -Modules/_sre.c - match_members variable static PyMemberDef match_members[] -Modules/_sre.c - match_methods variable static PyMethodDef match_methods -Modules/_sre.c - Match_Type variable static PyTypeObject Match_Type -Python/Python-ast.c - MatMult_singleton variable static PyObject *MatMult_singleton -Python/Python-ast.c - MatMult_type variable static PyTypeObject *MatMult_type -Objects/obmalloc.c - maxarenas variable static uint maxarenas -Objects/moduleobject.c - max_module_number variable static Py_ssize_t max_module_number -Objects/descrobject.c - member_getset variable static PyGetSetDef member_getset[] -Objects/exceptions.c - memerrors_freelist variable static PyBaseExceptionObject *memerrors_freelist -Objects/exceptions.c - memerrors_numfree variable static int memerrors_numfree -Objects/memoryobject.c - memory_as_buffer variable static PyBufferProcs memory_as_buffer -Objects/memoryobject.c - memory_as_mapping variable static PyMappingMethods memory_as_mapping -Objects/memoryobject.c - memory_as_sequence variable static PySequenceMethods memory_as_sequence -Objects/memoryobject.c - memory_getsetlist variable static PyGetSetDef memory_getsetlist[] -Objects/memoryobject.c - memory_methods variable static PyMethodDef memory_methods -Objects/methodobject.c - meth_getsets variable static PyGetSetDef meth_getsets [] -Objects/methodobject.c - meth_members variable static PyMemberDef meth_members[] -Objects/methodobject.c - meth_methods variable static PyMethodDef meth_methods -Objects/typeobject.c - method_cache variable static struct method_cache_entry method_cache[1 << MCACHE_SIZE_EXP] -Modules/_operator.c - methodcaller_methods variable static PyMethodDef methodcaller_methods -Modules/_operator.c - methodcaller_type variable static PyTypeObject methodcaller_type -Objects/classobject.c - method_getset variable static PyGetSetDef method_getset[] -Objects/descrobject.c - method_getset variable static PyGetSetDef method_getset[] -Objects/classobject.c - method_memberlist variable static PyMemberDef method_memberlist[] -Objects/classobject.c - method_methods variable static PyMethodDef method_methods -Python/codecs.c _PyCodecRegistry_Init methods variable static struct { char *name; PyMethodDef def; } methods[] -Python/frozen.c - M___hello__ variable static unsigned char M___hello__[] -Python/Python-ast.c - Mod_singleton variable static PyObject *Mod_singleton -Python/Python-ast.c - mod_type variable static PyTypeObject *mod_type -Python/Python-ast.c - Mod_type variable static PyTypeObject *Mod_type -Modules/faulthandler.c - module_def variable static struct PyModuleDef module_def -Modules/_tracemalloc.c - module_def variable static struct PyModuleDef module_def -Python/Python-ast.c - Module_fields variable static const char *Module_fields[] -Modules/_collectionsmodule.c - module_functions variable static struct PyMethodDef module_functions[] -Modules/_abc.c - module_functions variable static struct PyMethodDef module_functions[] -Objects/moduleobject.c - module_members variable static PyMemberDef module_members[] -Objects/moduleobject.c - module_methods variable static PyMethodDef module_methods -Modules/_functoolsmodule.c - module_methods variable static PyMethodDef module_methods -Modules/itertoolsmodule.c - module_methods variable static PyMethodDef module_methods -Modules/_io/_iomodule.c - module_methods variable static PyMethodDef module_methods -Modules/faulthandler.c - module_methods variable static PyMethodDef module_methods -Modules/_tracemalloc.c - module_methods variable static PyMethodDef module_methods -Python/Python-ast.c - Module_type variable static PyTypeObject *Module_type -Python/Python-ast.c - Mult_singleton variable static PyObject *Mult_singleton -Python/Python-ast.c - Mult_type variable static PyTypeObject *Mult_type -Objects/funcobject.c PyFunction_NewWithQualName __name__ variable static PyObject *__name__ -Python/compile.c compiler_lambda name variable static identifier name -Python/compile.c compiler_genexp name variable static identifier name -Python/compile.c compiler_listcomp name variable static identifier name -Python/compile.c compiler_setcomp name variable static identifier name -Python/compile.c compiler_dictcomp name variable static identifier name -Python/Python-ast.c - NamedExpr_fields variable static const char *NamedExpr_fields[] -Python/Python-ast.c - NamedExpr_type variable static PyTypeObject *NamedExpr_type -Python/Python-ast.c - Name_fields variable static const char *Name_fields[] -Objects/typeobject.c - name_op variable static _Py_Identifier name_op[] -Objects/namespaceobject.c - namespace_members variable static PyMemberDef namespace_members[] -Objects/namespaceobject.c - namespace_methods variable static PyMethodDef namespace_methods -Python/Python-ast.c - Name_type variable static PyTypeObject *Name_type -Objects/obmalloc.c - narenas_currently_allocated variable static size_t narenas_currently_allocated -Objects/obmalloc.c - narenas_highwater variable static size_t narenas_highwater -Python/sysmodule.c sys_displayhook newline variable static PyObject *newline -Objects/typeobject.c - next_version_tag variable static unsigned int next_version_tag -Objects/obmalloc.c - nfp2lasta variable static struct arena_object* nfp2lasta[MAX_POOLS_IN_ARENA + 1] -Python/dynload_shlib.c - nhandles variable static int nhandles -Objects/object.c - none_as_number variable static PyNumberMethods none_as_number -Python/Python-ast.c - Nonlocal_fields variable static const char *Nonlocal_fields[] -Python/Python-ast.c - Nonlocal_type variable static PyTypeObject *Nonlocal_type -Python/Python-ast.c - NotEq_singleton variable static PyObject *NotEq_singleton -Python/Python-ast.c - NotEq_type variable static PyTypeObject *NotEq_type -Objects/object.c - notimplemented_methods variable static PyMethodDef notimplemented_methods -Python/Python-ast.c - NotIn_singleton variable static PyObject *NotIn_singleton -Python/Python-ast.c - NotIn_type variable static PyTypeObject *NotIn_type -Python/Python-ast.c - Not_singleton variable static PyObject *Not_singleton -Python/Python-ast.c - Not_type variable static PyTypeObject *Not_type -Objects/obmalloc.c - ntimes_arena_allocated variable static size_t ntimes_arena_allocated -Objects/bytesobject.c - nullstring variable static PyBytesObject *nullstring -Objects/codeobject.c PyCode_NewEmpty nulltuple variable static PyObject *nulltuple -Objects/floatobject.c - numfree variable static int numfree -Objects/frameobject.c - numfree variable static int numfree -Objects/listobject.c - numfree variable static int numfree -Objects/dictobject.c - numfree variable static int numfree -Objects/methodobject.c - numfree variable static int numfree -Objects/tupleobject.c - numfree variable static int numfree[PyTuple_MAXSAVESIZE] -Objects/classobject.c - numfree variable static int numfree -Modules/_collectionsmodule.c - numfreeblocks variable static Py_ssize_t numfreeblocks -Objects/dictobject.c - numfreekeys variable static int numfreekeys -Objects/typeobject.c - object_getsets variable static PyGetSetDef object_getsets[] -Objects/typeobject.c - object_methods variable static PyMethodDef object_methods -Objects/typeobject.c object___reduce_ex___impl objreduce variable static PyObject *objreduce -Objects/odictobject.c - odict_as_mapping variable static PyMappingMethods odict_as_mapping -Objects/odictobject.c - odict_getset variable static PyGetSetDef odict_getset[] -Objects/odictobject.c - odictitems_methods variable static PyMethodDef odictitems_methods -Objects/odictobject.c - odictiter_methods variable static PyMethodDef odictiter_methods -Objects/odictobject.c - odictkeys_methods variable static PyMethodDef odictkeys_methods -Objects/odictobject.c - odict_methods variable static PyMethodDef odict_methods -Objects/odictobject.c - odictvalues_methods variable static PyMethodDef odictvalues_methods -Modules/faulthandler.c - old_stack variable static stack_t old_stack -Modules/_operator.c - operator_methods variable static PyMethodDef operator_methods -Modules/_operator.c - operatormodule variable static struct PyModuleDef operatormodule -Python/Python-ast.c - operator_type variable static PyTypeObject *operator_type -Objects/typeobject.c slot_nb_add op_id variable _Py_static_string(op_id, OPSTR) -Objects/typeobject.c slot_nb_subtract op_id variable _Py_static_string(op_id, OPSTR) -Objects/typeobject.c slot_nb_multiply op_id variable _Py_static_string(op_id, OPSTR) -Objects/typeobject.c slot_nb_matrix_multiply op_id variable _Py_static_string(op_id, OPSTR) -Objects/typeobject.c slot_nb_remainder op_id variable _Py_static_string(op_id, OPSTR) -Objects/typeobject.c slot_nb_divmod op_id variable _Py_static_string(op_id, OPSTR) -Objects/typeobject.c slot_nb_power_binary op_id variable _Py_static_string(op_id, OPSTR) -Objects/typeobject.c slot_nb_lshift op_id variable _Py_static_string(op_id, OPSTR) -Objects/typeobject.c slot_nb_rshift op_id variable _Py_static_string(op_id, OPSTR) -Objects/typeobject.c slot_nb_and op_id variable _Py_static_string(op_id, OPSTR) -Objects/typeobject.c slot_nb_xor op_id variable _Py_static_string(op_id, OPSTR) -Objects/typeobject.c slot_nb_or op_id variable _Py_static_string(op_id, OPSTR) -Objects/typeobject.c slot_nb_floor_divide op_id variable _Py_static_string(op_id, OPSTR) -Objects/typeobject.c slot_nb_true_divide op_id variable _Py_static_string(op_id, OPSTR) -Python/getopt.c - opt_ptr variable static const wchar_t *opt_ptr -Python/initconfig.c - orig_argv variable static PyWideStringList orig_argv -Python/Python-ast.c - Or_singleton variable static PyObject *Or_singleton -Python/Python-ast.c - Or_type variable static PyTypeObject *Or_type -Objects/exceptions.c - OSError_getset variable static PyGetSetDef OSError_getset[] -Objects/exceptions.c - OSError_members variable static PyMemberDef OSError_members[] -Objects/exceptions.c - OSError_methods variable static PyMethodDef OSError_methods -Python/dtoa.c - p5s variable static Bigint *p5s -Python/Python-ast.c - Param_singleton variable static PyObject *Param_singleton -Python/Python-ast.c - Param_type variable static PyTypeObject *Param_type -Python/bltinmodule.c builtin_print _parser variable static struct _PyArg_Parser _parser -Python/clinic/_warnings.c.h warnings_warn _parser variable static _PyArg_Parser _parser -Python/clinic/bltinmodule.c.h builtin_compile _parser variable static _PyArg_Parser _parser -Python/clinic/bltinmodule.c.h builtin_round _parser variable static _PyArg_Parser _parser -Python/clinic/bltinmodule.c.h builtin_sum _parser variable static _PyArg_Parser _parser -Python/clinic/import.c.h _imp_source_hash _parser variable static _PyArg_Parser _parser -Python/clinic/sysmodule.c.h sys_addaudithook _parser variable static _PyArg_Parser _parser -Python/clinic/sysmodule.c.h sys_set_coroutine_origin_tracking_depth _parser variable static _PyArg_Parser _parser -Python/clinic/traceback.c.h tb_new _parser variable static _PyArg_Parser _parser -Objects/clinic/bytearrayobject.c.h bytearray_translate _parser variable static _PyArg_Parser _parser -Objects/clinic/bytearrayobject.c.h bytearray_split _parser variable static _PyArg_Parser _parser -Objects/clinic/bytearrayobject.c.h bytearray_rsplit _parser variable static _PyArg_Parser _parser -Objects/clinic/bytearrayobject.c.h bytearray_decode _parser variable static _PyArg_Parser _parser -Objects/clinic/bytearrayobject.c.h bytearray_splitlines _parser variable static _PyArg_Parser _parser -Objects/clinic/bytearrayobject.c.h bytearray_hex _parser variable static _PyArg_Parser _parser -Objects/clinic/bytesobject.c.h bytes_split _parser variable static _PyArg_Parser _parser -Objects/clinic/bytesobject.c.h bytes_rsplit _parser variable static _PyArg_Parser _parser -Objects/clinic/bytesobject.c.h bytes_translate _parser variable static _PyArg_Parser _parser -Objects/clinic/bytesobject.c.h bytes_decode _parser variable static _PyArg_Parser _parser -Objects/clinic/bytesobject.c.h bytes_splitlines _parser variable static _PyArg_Parser _parser -Objects/clinic/bytesobject.c.h bytes_hex _parser variable static _PyArg_Parser _parser -Objects/clinic/codeobject.c.h code_replace _parser variable static _PyArg_Parser _parser -Objects/clinic/complexobject.c.h complex_new _parser variable static _PyArg_Parser _parser -Objects/clinic/descrobject.c.h mappingproxy_new _parser variable static _PyArg_Parser _parser -Objects/clinic/descrobject.c.h property_init _parser variable static _PyArg_Parser _parser -Objects/clinic/enumobject.c.h enum_new _parser variable static _PyArg_Parser _parser -Objects/clinic/funcobject.c.h func_new _parser variable static _PyArg_Parser _parser -Objects/clinic/listobject.c.h list_sort _parser variable static _PyArg_Parser _parser -Objects/clinic/longobject.c.h long_new _parser variable static _PyArg_Parser _parser -Objects/clinic/longobject.c.h int_to_bytes _parser variable static _PyArg_Parser _parser -Objects/clinic/longobject.c.h int_from_bytes _parser variable static _PyArg_Parser _parser -Objects/clinic/memoryobject.c.h memoryview_hex _parser variable static _PyArg_Parser _parser -Objects/clinic/moduleobject.c.h module___init__ _parser variable static _PyArg_Parser _parser -Objects/clinic/odictobject.c.h OrderedDict_fromkeys _parser variable static _PyArg_Parser _parser -Objects/clinic/odictobject.c.h OrderedDict_setdefault _parser variable static _PyArg_Parser _parser -Objects/clinic/odictobject.c.h OrderedDict_popitem _parser variable static _PyArg_Parser _parser -Objects/clinic/odictobject.c.h OrderedDict_move_to_end _parser variable static _PyArg_Parser _parser -Objects/clinic/structseq.c.h structseq_new _parser variable static _PyArg_Parser _parser -Objects/clinic/unicodeobject.c.h unicode_encode _parser variable static _PyArg_Parser _parser -Objects/clinic/unicodeobject.c.h unicode_expandtabs _parser variable static _PyArg_Parser _parser -Objects/clinic/unicodeobject.c.h unicode_split _parser variable static _PyArg_Parser _parser -Objects/clinic/unicodeobject.c.h unicode_rsplit _parser variable static _PyArg_Parser _parser -Objects/clinic/unicodeobject.c.h unicode_splitlines _parser variable static _PyArg_Parser _parser -Objects/stringlib/clinic/transmogrify.h.h stringlib_expandtabs _parser variable static _PyArg_Parser _parser -Modules/_blake2/clinic/blake2b_impl.c.h py_blake2b_new _parser variable static _PyArg_Parser _parser -Modules/_blake2/clinic/blake2s_impl.c.h py_blake2s_new _parser variable static _PyArg_Parser _parser -Modules/_io/clinic/_iomodule.c.h _io_open _parser variable static _PyArg_Parser _parser -Modules/_io/clinic/_iomodule.c.h _io_open_code _parser variable static _PyArg_Parser _parser -Modules/_io/clinic/bufferedio.c.h _io_BufferedReader___init__ _parser variable static _PyArg_Parser _parser -Modules/_io/clinic/bufferedio.c.h _io_BufferedWriter___init__ _parser variable static _PyArg_Parser _parser -Modules/_io/clinic/bufferedio.c.h _io_BufferedRandom___init__ _parser variable static _PyArg_Parser _parser -Modules/_io/clinic/bytesio.c.h _io_BytesIO___init__ _parser variable static _PyArg_Parser _parser -Modules/_io/clinic/fileio.c.h _io_FileIO___init__ _parser variable static _PyArg_Parser _parser -Modules/_io/clinic/stringio.c.h _io_StringIO___init__ _parser variable static _PyArg_Parser _parser -Modules/_io/clinic/textio.c.h _io_IncrementalNewlineDecoder___init__ _parser variable static _PyArg_Parser _parser -Modules/_io/clinic/textio.c.h _io_IncrementalNewlineDecoder_decode _parser variable static _PyArg_Parser _parser -Modules/_io/clinic/textio.c.h _io_TextIOWrapper___init__ _parser variable static _PyArg_Parser _parser -Modules/_io/clinic/textio.c.h _io_TextIOWrapper_reconfigure _parser variable static _PyArg_Parser _parser -Modules/_io/clinic/winconsoleio.c.h _io__WindowsConsoleIO___init__ _parser variable static _PyArg_Parser _parser -Modules/_multiprocessing/clinic/posixshmem.c.h _posixshmem_shm_open _parser variable static _PyArg_Parser _parser -Modules/_multiprocessing/clinic/posixshmem.c.h _posixshmem_shm_unlink _parser variable static _PyArg_Parser _parser -Modules/cjkcodecs/clinic/multibytecodec.c.h _multibytecodec_MultibyteCodec_encode _parser variable static _PyArg_Parser _parser -Modules/cjkcodecs/clinic/multibytecodec.c.h _multibytecodec_MultibyteCodec_decode _parser variable static _PyArg_Parser _parser -Modules/cjkcodecs/clinic/multibytecodec.c.h _multibytecodec_MultibyteIncrementalEncoder_encode _parser variable static _PyArg_Parser _parser -Modules/cjkcodecs/clinic/multibytecodec.c.h _multibytecodec_MultibyteIncrementalDecoder_decode _parser variable static _PyArg_Parser _parser -Modules/clinic/_asynciomodule.c.h _asyncio_Future___init__ _parser variable static _PyArg_Parser _parser -Modules/clinic/_asynciomodule.c.h _asyncio_Future_add_done_callback _parser variable static _PyArg_Parser _parser -Modules/clinic/_asynciomodule.c.h _asyncio_Task___init__ _parser variable static _PyArg_Parser _parser -Modules/clinic/_asynciomodule.c.h _asyncio_Task_current_task _parser variable static _PyArg_Parser _parser -Modules/clinic/_asynciomodule.c.h _asyncio_Task_all_tasks _parser variable static _PyArg_Parser _parser -Modules/clinic/_asynciomodule.c.h _asyncio_Task_get_stack _parser variable static _PyArg_Parser _parser -Modules/clinic/_asynciomodule.c.h _asyncio_Task_print_stack _parser variable static _PyArg_Parser _parser -Modules/clinic/_asynciomodule.c.h _asyncio__register_task _parser variable static _PyArg_Parser _parser -Modules/clinic/_asynciomodule.c.h _asyncio__unregister_task _parser variable static _PyArg_Parser _parser -Modules/clinic/_asynciomodule.c.h _asyncio__enter_task _parser variable static _PyArg_Parser _parser -Modules/clinic/_asynciomodule.c.h _asyncio__leave_task _parser variable static _PyArg_Parser _parser -Modules/clinic/_bz2module.c.h _bz2_BZ2Decompressor_decompress _parser variable static _PyArg_Parser _parser -Modules/clinic/_codecsmodule.c.h _codecs_encode _parser variable static _PyArg_Parser _parser -Modules/clinic/_codecsmodule.c.h _codecs_decode _parser variable static _PyArg_Parser _parser -Modules/clinic/_cursesmodule.c.h _curses_setupterm _parser variable static _PyArg_Parser _parser -Modules/clinic/_datetimemodule.c.h datetime_datetime_now _parser variable static _PyArg_Parser _parser -Modules/clinic/_elementtree.c.h _elementtree_Element_find _parser variable static _PyArg_Parser _parser -Modules/clinic/_elementtree.c.h _elementtree_Element_findtext _parser variable static _PyArg_Parser _parser -Modules/clinic/_elementtree.c.h _elementtree_Element_findall _parser variable static _PyArg_Parser _parser -Modules/clinic/_elementtree.c.h _elementtree_Element_iterfind _parser variable static _PyArg_Parser _parser -Modules/clinic/_elementtree.c.h _elementtree_Element_get _parser variable static _PyArg_Parser _parser -Modules/clinic/_elementtree.c.h _elementtree_Element_iter _parser variable static _PyArg_Parser _parser -Modules/clinic/_elementtree.c.h _elementtree_Element_getiterator _parser variable static _PyArg_Parser _parser -Modules/clinic/_elementtree.c.h _elementtree_TreeBuilder___init__ _parser variable static _PyArg_Parser _parser -Modules/clinic/_elementtree.c.h _elementtree_XMLParser___init__ _parser variable static _PyArg_Parser _parser -Modules/clinic/_hashopenssl.c.h EVP_new _parser variable static _PyArg_Parser _parser -Modules/clinic/_hashopenssl.c.h pbkdf2_hmac _parser variable static _PyArg_Parser _parser -Modules/clinic/_hashopenssl.c.h _hashlib_scrypt _parser variable static _PyArg_Parser _parser -Modules/clinic/_hashopenssl.c.h _hashlib_hmac_digest _parser variable static _PyArg_Parser _parser -Modules/clinic/_lzmamodule.c.h _lzma_LZMADecompressor_decompress _parser variable static _PyArg_Parser _parser -Modules/clinic/_lzmamodule.c.h _lzma_LZMADecompressor___init__ _parser variable static _PyArg_Parser _parser -Modules/clinic/_opcode.c.h _opcode_stack_effect _parser variable static _PyArg_Parser _parser -Modules/clinic/_pickle.c.h _pickle_Pickler___init__ _parser variable static _PyArg_Parser _parser -Modules/clinic/_pickle.c.h _pickle_Unpickler___init__ _parser variable static _PyArg_Parser _parser -Modules/clinic/_pickle.c.h _pickle_dump _parser variable static _PyArg_Parser _parser -Modules/clinic/_pickle.c.h _pickle_dumps _parser variable static _PyArg_Parser _parser -Modules/clinic/_pickle.c.h _pickle_load _parser variable static _PyArg_Parser _parser -Modules/clinic/_pickle.c.h _pickle_loads _parser variable static _PyArg_Parser _parser -Modules/clinic/_queuemodule.c.h _queue_SimpleQueue_put _parser variable static _PyArg_Parser _parser -Modules/clinic/_queuemodule.c.h _queue_SimpleQueue_put_nowait _parser variable static _PyArg_Parser _parser -Modules/clinic/_queuemodule.c.h _queue_SimpleQueue_get _parser variable static _PyArg_Parser _parser -Modules/clinic/_sre.c.h _sre_SRE_Pattern_match _parser variable static _PyArg_Parser _parser -Modules/clinic/_sre.c.h _sre_SRE_Pattern_fullmatch _parser variable static _PyArg_Parser _parser -Modules/clinic/_sre.c.h _sre_SRE_Pattern_search _parser variable static _PyArg_Parser _parser -Modules/clinic/_sre.c.h _sre_SRE_Pattern_findall _parser variable static _PyArg_Parser _parser -Modules/clinic/_sre.c.h _sre_SRE_Pattern_finditer _parser variable static _PyArg_Parser _parser -Modules/clinic/_sre.c.h _sre_SRE_Pattern_scanner _parser variable static _PyArg_Parser _parser -Modules/clinic/_sre.c.h _sre_SRE_Pattern_split _parser variable static _PyArg_Parser _parser -Modules/clinic/_sre.c.h _sre_SRE_Pattern_sub _parser variable static _PyArg_Parser _parser -Modules/clinic/_sre.c.h _sre_SRE_Pattern_subn _parser variable static _PyArg_Parser _parser -Modules/clinic/_sre.c.h _sre_compile _parser variable static _PyArg_Parser _parser -Modules/clinic/_sre.c.h _sre_SRE_Match_expand _parser variable static _PyArg_Parser _parser -Modules/clinic/_sre.c.h _sre_SRE_Match_groups _parser variable static _PyArg_Parser _parser -Modules/clinic/_sre.c.h _sre_SRE_Match_groupdict _parser variable static _PyArg_Parser _parser -Modules/clinic/_ssl.c.h _ssl__SSLSocket_get_channel_binding _parser variable static _PyArg_Parser _parser -Modules/clinic/_ssl.c.h _ssl__SSLContext_load_cert_chain _parser variable static _PyArg_Parser _parser -Modules/clinic/_ssl.c.h _ssl__SSLContext_load_verify_locations _parser variable static _PyArg_Parser _parser -Modules/clinic/_ssl.c.h _ssl__SSLContext__wrap_socket _parser variable static _PyArg_Parser _parser -Modules/clinic/_ssl.c.h _ssl__SSLContext__wrap_bio _parser variable static _PyArg_Parser _parser -Modules/clinic/_ssl.c.h _ssl__SSLContext_get_ca_certs _parser variable static _PyArg_Parser _parser -Modules/clinic/_ssl.c.h _ssl_txt2obj _parser variable static _PyArg_Parser _parser -Modules/clinic/_ssl.c.h _ssl_enum_certificates _parser variable static _PyArg_Parser _parser -Modules/clinic/_ssl.c.h _ssl_enum_crls _parser variable static _PyArg_Parser _parser -Modules/clinic/_struct.c.h Struct___init__ _parser variable static _PyArg_Parser _parser -Modules/clinic/_struct.c.h Struct_unpack_from _parser variable static _PyArg_Parser _parser -Modules/clinic/_struct.c.h unpack_from _parser variable static _PyArg_Parser _parser -Modules/clinic/_winapi.c.h _winapi_ConnectNamedPipe _parser variable static _PyArg_Parser _parser -Modules/clinic/_winapi.c.h _winapi_ReadFile _parser variable static _PyArg_Parser _parser -Modules/clinic/_winapi.c.h _winapi_WriteFile _parser variable static _PyArg_Parser _parser -Modules/clinic/_winapi.c.h _winapi_GetFileType _parser variable static _PyArg_Parser _parser -Modules/clinic/binascii.c.h binascii_b2a_uu _parser variable static _PyArg_Parser _parser -Modules/clinic/binascii.c.h binascii_b2a_base64 _parser variable static _PyArg_Parser _parser -Modules/clinic/binascii.c.h binascii_b2a_hex _parser variable static _PyArg_Parser _parser -Modules/clinic/binascii.c.h binascii_hexlify _parser variable static _PyArg_Parser _parser -Modules/clinic/binascii.c.h binascii_a2b_qp _parser variable static _PyArg_Parser _parser -Modules/clinic/binascii.c.h binascii_b2a_qp _parser variable static _PyArg_Parser _parser -Modules/clinic/cmathmodule.c.h cmath_isclose _parser variable static _PyArg_Parser _parser -Modules/clinic/gcmodule.c.h gc_collect _parser variable static _PyArg_Parser _parser -Modules/clinic/gcmodule.c.h gc_get_objects _parser variable static _PyArg_Parser _parser -Modules/clinic/grpmodule.c.h grp_getgrgid _parser variable static _PyArg_Parser _parser -Modules/clinic/grpmodule.c.h grp_getgrnam _parser variable static _PyArg_Parser _parser -Modules/_functoolsmodule.c - partial_getsetlist variable static PyGetSetDef partial_getsetlist[] -Modules/_functoolsmodule.c - partial_memberlist variable static PyMemberDef partial_memberlist[] -Modules/_functoolsmodule.c - partial_methods variable static PyMethodDef partial_methods -Modules/_functoolsmodule.c - partial_type variable static PyTypeObject partial_type -Python/Python-ast.c - Pass_type variable static PyTypeObject *Pass_type -Modules/_sre.c - pattern_getset variable static PyGetSetDef pattern_getset[] -Modules/_sre.c - pattern_members variable static PyMemberDef pattern_members[] -Modules/_sre.c - pattern_methods variable static PyMethodDef pattern_methods -Modules/_sre.c - Pattern_Type variable static PyTypeObject Pattern_Type -Modules/itertoolsmodule.c - permuations_methods variable static PyMethodDef permuations_methods -Modules/itertoolsmodule.c - permutations_type variable static PyTypeObject permutations_type -Objects/picklebufobject.c - picklebuf_as_buffer variable static PyBufferProcs picklebuf_as_buffer -Objects/picklebufobject.c - picklebuf_methods variable static PyMethodDef picklebuf_methods -Python/dtoa.c - pmem_next variable static double *pmem_next -Objects/typeobject.c resolve_slotdups pname variable static PyObject *pname -Modules/posixmodule.c - posix_constants_confstr variable static struct constdef posix_constants_confstr[] -Modules/posixmodule.c - posix_constants_pathconf variable static struct constdef posix_constants_pathconf[] -Modules/posixmodule.c - posix_constants_sysconf variable static struct constdef posix_constants_sysconf[] -Modules/posixmodule.c - posix_methods variable static PyMethodDef posix_methods -Modules/posixmodule.c - posixmodule variable static struct PyModuleDef posixmodule -Modules/posixmodule.c - posix_putenv_garbage variable static PyObject *posix_putenv_garbage -Python/Python-ast.c - Pow_singleton variable static PyObject *Pow_singleton -Python/Python-ast.c - Pow_type variable static PyTypeObject *Pow_type -Python/sysmodule.c - _preinit_warnoptions variable static _Py_PreInitEntry _preinit_warnoptions -Python/sysmodule.c - _preinit_xoptions variable static _Py_PreInitEntry _preinit_xoptions -Objects/exceptions.c _check_for_legacy_statements print_prefix variable static PyObject *print_prefix -Python/dtoa.c - private_mem variable static double private_mem[PRIVATE_mem] -Modules/itertoolsmodule.c - product_methods variable static PyMethodDef product_methods -Modules/itertoolsmodule.c - product_type variable static PyTypeObject product_type -Objects/descrobject.c - property_getsetlist variable static PyGetSetDef property_getsetlist[] -Objects/descrobject.c - property_members variable static PyMemberDef property_members[] -Objects/descrobject.c - property_methods variable static PyMethodDef property_methods -Objects/weakrefobject.c - proxy_as_mapping variable static PyMappingMethods proxy_as_mapping -Objects/weakrefobject.c - proxy_as_number variable static PyNumberMethods proxy_as_number -Objects/weakrefobject.c - proxy_as_sequence variable static PySequenceMethods proxy_as_sequence -Objects/weakrefobject.c - proxy_methods variable static PyMethodDef proxy_methods -Objects/typeobject.c resolve_slotdups ptrs variable static slotdef *ptrs[MAX_EQUIV] -Modules/pwdmodule.c - pwd_methods variable static PyMethodDef pwd_methods -Modules/pwdmodule.c - pwdmodule variable static struct PyModuleDef pwdmodule -Objects/obmalloc.c - _Py_AllocatedBlocks variable static Py_ssize_t _Py_AllocatedBlocks -Objects/genobject.c - _PyAsyncGenASend_Type variable PyTypeObject _PyAsyncGenASend_Type -Objects/genobject.c - _PyAsyncGenAThrow_Type variable PyTypeObject _PyAsyncGenAThrow_Type -Objects/genobject.c - PyAsyncGen_Type variable PyTypeObject PyAsyncGen_Type -Objects/genobject.c - _PyAsyncGenWrappedValue_Type variable PyTypeObject _PyAsyncGenWrappedValue_Type -Objects/typeobject.c - PyBaseObject_Type variable PyTypeObject PyBaseObject_Type -Modules/_blake2/blake2b_impl.c - PyBlake2_BLAKE2bType variable PyTypeObject PyBlake2_BLAKE2bType -Modules/_blake2/blake2s_impl.c - PyBlake2_BLAKE2sType variable PyTypeObject PyBlake2_BLAKE2sType -Objects/boolobject.c - PyBool_Type variable PyTypeObject PyBool_Type -Modules/_io/bufferedio.c - PyBufferedIOBase_Type variable PyTypeObject PyBufferedIOBase_Type -Modules/_io/bufferedio.c - PyBufferedRandom_Type variable PyTypeObject PyBufferedRandom_Type -Modules/_io/bufferedio.c - PyBufferedReader_Type variable PyTypeObject PyBufferedReader_Type -Modules/_io/bufferedio.c - PyBufferedRWPair_Type variable PyTypeObject PyBufferedRWPair_Type -Modules/_io/bufferedio.c - PyBufferedWriter_Type variable PyTypeObject PyBufferedWriter_Type -Objects/bytearrayobject.c - _PyByteArray_empty_string variable char _PyByteArray_empty_string[] -Objects/bytearrayobject.c - PyByteArrayIter_Type variable PyTypeObject PyByteArrayIter_Type -Objects/bytearrayobject.c - PyByteArray_Type variable PyTypeObject PyByteArray_Type -Modules/_io/bytesio.c - _PyBytesIOBuffer_Type variable PyTypeObject _PyBytesIOBuffer_Type -Modules/_io/bytesio.c - PyBytesIO_Type variable PyTypeObject PyBytesIO_Type -Objects/bytesobject.c - PyBytesIter_Type variable PyTypeObject PyBytesIter_Type -Objects/bytesobject.c - PyBytes_Type variable PyTypeObject PyBytes_Type -Python/initconfig.c - Py_BytesWarningFlag variable int Py_BytesWarningFlag -Objects/iterobject.c - PyCallIter_Type variable PyTypeObject PyCallIter_Type -Objects/capsule.c - PyCapsule_Type variable PyTypeObject PyCapsule_Type -Objects/cellobject.c - PyCell_Type variable PyTypeObject PyCell_Type -Objects/methodobject.c - PyCFunction_Type variable PyTypeObject PyCFunction_Type -Objects/descrobject.c - PyClassMethodDescr_Type variable PyTypeObject PyClassMethodDescr_Type -Objects/funcobject.c - PyClassMethod_Type variable PyTypeObject PyClassMethod_Type -Objects/codeobject.c - PyCode_Type variable PyTypeObject PyCode_Type -Objects/complexobject.c - PyComplex_Type variable PyTypeObject PyComplex_Type -Python/context.c - PyContext_as_mapping variable static PyMappingMethods PyContext_as_mapping -Python/context.c - PyContext_as_sequence variable static PySequenceMethods PyContext_as_sequence -Python/context.c - PyContext_methods variable static PyMethodDef PyContext_methods -Python/context.c - PyContextTokenMissing_Type variable PyTypeObject PyContextTokenMissing_Type -Python/context.c - PyContextToken_Type variable PyTypeObject PyContextToken_Type -Python/context.c - PyContextTokenType_getsetlist variable static PyGetSetDef PyContextTokenType_getsetlist[] -Python/context.c - PyContext_Type variable PyTypeObject PyContext_Type -Python/context.c - PyContextVar_members variable static PyMemberDef PyContextVar_members[] -Python/context.c - PyContextVar_methods variable static PyMethodDef PyContextVar_methods -Python/context.c - PyContextVar_Type variable PyTypeObject PyContextVar_Type -Objects/genobject.c - PyCoro_Type variable PyTypeObject PyCoro_Type -Objects/genobject.c - _PyCoroWrapper_Type variable PyTypeObject _PyCoroWrapper_Type -Python/initconfig.c - Py_DebugFlag variable int Py_DebugFlag -Objects/dictobject.c - pydict_global_version variable static uint64_t pydict_global_version -Objects/dictobject.c - PyDictItems_Type variable PyTypeObject PyDictItems_Type -Objects/dictobject.c - PyDictIterItem_Type variable PyTypeObject PyDictIterItem_Type -Objects/dictobject.c - PyDictIterKey_Type variable PyTypeObject PyDictIterKey_Type -Objects/dictobject.c - PyDictIterValue_Type variable PyTypeObject PyDictIterValue_Type -Objects/dictobject.c - PyDictKeys_Type variable PyTypeObject PyDictKeys_Type -Objects/descrobject.c - PyDictProxy_Type variable PyTypeObject PyDictProxy_Type -Objects/dictobject.c - PyDictRevIterItem_Type variable PyTypeObject PyDictRevIterItem_Type -Objects/dictobject.c - PyDictRevIterKey_Type variable PyTypeObject PyDictRevIterKey_Type -Objects/dictobject.c - PyDictRevIterValue_Type variable PyTypeObject PyDictRevIterValue_Type -Objects/dictobject.c - PyDict_Type variable PyTypeObject PyDict_Type -Objects/dictobject.c - PyDictValues_Type variable PyTypeObject PyDictValues_Type -Python/initconfig.c - Py_DontWriteBytecodeFlag variable int Py_DontWriteBytecodeFlag -Objects/sliceobject.c - _Py_EllipsisObject variable PyObject _Py_EllipsisObject -Objects/sliceobject.c - PyEllipsis_Type variable PyTypeObject PyEllipsis_Type -Objects/enumobject.c - PyEnum_Type variable PyTypeObject PyEnum_Type -Objects/exceptions.c - _PyExc_ArithmeticError variable static PyTypeObject _PyExc_ArithmeticError -Objects/exceptions.c - PyExc_ArithmeticError variable static PyTypeObject PyExc_ArithmeticError -Objects/exceptions.c - _PyExc_AssertionError variable static PyTypeObject _PyExc_AssertionError -Objects/exceptions.c - PyExc_AssertionError variable static PyTypeObject PyExc_AssertionError -Objects/exceptions.c - _PyExc_AttributeError variable static PyTypeObject _PyExc_AttributeError -Objects/exceptions.c - PyExc_AttributeError variable static PyTypeObject PyExc_AttributeError -Objects/exceptions.c - _PyExc_BaseException variable static PyTypeObject _PyExc_BaseException -Objects/exceptions.c - PyExc_BaseException variable static PyTypeObject PyExc_BaseException -Objects/exceptions.c - _PyExc_BlockingIOError variable static PyTypeObject _PyExc_BlockingIOError -Objects/exceptions.c - PyExc_BlockingIOError variable static PyTypeObject PyExc_BlockingIOError -Objects/exceptions.c - _PyExc_BrokenPipeError variable static PyTypeObject _PyExc_BrokenPipeError -Objects/exceptions.c - PyExc_BrokenPipeError variable static PyTypeObject PyExc_BrokenPipeError -Objects/exceptions.c - _PyExc_BufferError variable static PyTypeObject _PyExc_BufferError -Objects/exceptions.c - PyExc_BufferError variable static PyTypeObject PyExc_BufferError -Objects/exceptions.c - _PyExc_BytesWarning variable static PyTypeObject _PyExc_BytesWarning -Objects/exceptions.c - PyExc_BytesWarning variable static PyTypeObject PyExc_BytesWarning -Objects/exceptions.c - _PyExc_ChildProcessError variable static PyTypeObject _PyExc_ChildProcessError -Objects/exceptions.c - PyExc_ChildProcessError variable static PyTypeObject PyExc_ChildProcessError -Objects/exceptions.c - _PyExc_ConnectionAbortedError variable static PyTypeObject _PyExc_ConnectionAbortedError -Objects/exceptions.c - PyExc_ConnectionAbortedError variable static PyTypeObject PyExc_ConnectionAbortedError -Objects/exceptions.c - _PyExc_ConnectionError variable static PyTypeObject _PyExc_ConnectionError -Objects/exceptions.c - PyExc_ConnectionError variable static PyTypeObject PyExc_ConnectionError -Objects/exceptions.c - _PyExc_ConnectionRefusedError variable static PyTypeObject _PyExc_ConnectionRefusedError -Objects/exceptions.c - PyExc_ConnectionRefusedError variable static PyTypeObject PyExc_ConnectionRefusedError -Objects/exceptions.c - _PyExc_ConnectionResetError variable static PyTypeObject _PyExc_ConnectionResetError -Objects/exceptions.c - PyExc_ConnectionResetError variable static PyTypeObject PyExc_ConnectionResetError -Objects/exceptions.c - _PyExc_DeprecationWarning variable static PyTypeObject _PyExc_DeprecationWarning -Objects/exceptions.c - PyExc_DeprecationWarning variable static PyTypeObject PyExc_DeprecationWarning -Objects/exceptions.c - PyExc_EnvironmentError variable static PyTypeObject PyExc_EnvironmentError -Objects/exceptions.c - _PyExc_EOFError variable static PyTypeObject _PyExc_EOFError -Objects/exceptions.c - PyExc_EOFError variable static PyTypeObject PyExc_EOFError -Objects/exceptions.c - _PyExc_Exception variable static PyTypeObject _PyExc_Exception -Objects/exceptions.c - PyExc_Exception variable static PyTypeObject PyExc_Exception -Objects/exceptions.c - _PyExc_FileExistsError variable static PyTypeObject _PyExc_FileExistsError -Objects/exceptions.c - PyExc_FileExistsError variable static PyTypeObject PyExc_FileExistsError -Objects/exceptions.c - _PyExc_FileNotFoundError variable static PyTypeObject _PyExc_FileNotFoundError -Objects/exceptions.c - PyExc_FileNotFoundError variable static PyTypeObject PyExc_FileNotFoundError -Objects/exceptions.c - _PyExc_FloatingPointError variable static PyTypeObject _PyExc_FloatingPointError -Objects/exceptions.c - PyExc_FloatingPointError variable static PyTypeObject PyExc_FloatingPointError -Objects/exceptions.c - _PyExc_FutureWarning variable static PyTypeObject _PyExc_FutureWarning -Objects/exceptions.c - PyExc_FutureWarning variable static PyTypeObject PyExc_FutureWarning -Objects/exceptions.c - _PyExc_GeneratorExit variable static PyTypeObject _PyExc_GeneratorExit -Objects/exceptions.c - PyExc_GeneratorExit variable static PyTypeObject PyExc_GeneratorExit -Objects/exceptions.c - _PyExc_ImportError variable static PyTypeObject _PyExc_ImportError -Objects/exceptions.c - PyExc_ImportError variable static PyTypeObject PyExc_ImportError -Objects/exceptions.c - _PyExc_ImportWarning variable static PyTypeObject _PyExc_ImportWarning -Objects/exceptions.c - PyExc_ImportWarning variable static PyTypeObject PyExc_ImportWarning -Objects/exceptions.c - _PyExc_IndentationError variable static PyTypeObject _PyExc_IndentationError -Objects/exceptions.c - PyExc_IndentationError variable static PyTypeObject PyExc_IndentationError -Objects/exceptions.c - _PyExc_IndexError variable static PyTypeObject _PyExc_IndexError -Objects/exceptions.c - PyExc_IndexError variable static PyTypeObject PyExc_IndexError -Objects/exceptions.c - _PyExc_InterruptedError variable static PyTypeObject _PyExc_InterruptedError -Objects/exceptions.c - PyExc_InterruptedError variable static PyTypeObject PyExc_InterruptedError -Objects/exceptions.c - PyExc_IOError variable static PyTypeObject PyExc_IOError -Objects/exceptions.c - _PyExc_IsADirectoryError variable static PyTypeObject _PyExc_IsADirectoryError -Objects/exceptions.c - PyExc_IsADirectoryError variable static PyTypeObject PyExc_IsADirectoryError -Objects/exceptions.c - _PyExc_KeyboardInterrupt variable static PyTypeObject _PyExc_KeyboardInterrupt -Objects/exceptions.c - PyExc_KeyboardInterrupt variable static PyTypeObject PyExc_KeyboardInterrupt -Objects/exceptions.c - _PyExc_KeyError variable static PyTypeObject _PyExc_KeyError -Objects/exceptions.c - PyExc_KeyError variable static PyTypeObject PyExc_KeyError -Objects/exceptions.c - _PyExc_LookupError variable static PyTypeObject _PyExc_LookupError -Objects/exceptions.c - PyExc_LookupError variable static PyTypeObject PyExc_LookupError -Objects/exceptions.c - _PyExc_MemoryError variable static PyTypeObject _PyExc_MemoryError -Objects/exceptions.c - PyExc_MemoryError variable static PyTypeObject PyExc_MemoryError -Objects/exceptions.c - _PyExc_ModuleNotFoundError variable static PyTypeObject _PyExc_ModuleNotFoundError -Objects/exceptions.c - PyExc_ModuleNotFoundError variable static PyTypeObject PyExc_ModuleNotFoundError -Objects/exceptions.c - _PyExc_NameError variable static PyTypeObject _PyExc_NameError -Objects/exceptions.c - PyExc_NameError variable static PyTypeObject PyExc_NameError -Objects/exceptions.c - _PyExc_NotADirectoryError variable static PyTypeObject _PyExc_NotADirectoryError -Objects/exceptions.c - PyExc_NotADirectoryError variable static PyTypeObject PyExc_NotADirectoryError -Objects/exceptions.c - _PyExc_NotImplementedError variable static PyTypeObject _PyExc_NotImplementedError -Objects/exceptions.c - PyExc_NotImplementedError variable static PyTypeObject PyExc_NotImplementedError -Objects/exceptions.c - _PyExc_OSError variable static PyTypeObject _PyExc_OSError -Objects/exceptions.c - PyExc_OSError variable static PyTypeObject PyExc_OSError -Objects/exceptions.c - _PyExc_OverflowError variable static PyTypeObject _PyExc_OverflowError -Objects/exceptions.c - PyExc_OverflowError variable static PyTypeObject PyExc_OverflowError -Objects/exceptions.c - _PyExc_PendingDeprecationWarning variable static PyTypeObject _PyExc_PendingDeprecationWarning -Objects/exceptions.c - PyExc_PendingDeprecationWarning variable static PyTypeObject PyExc_PendingDeprecationWarning -Objects/exceptions.c - _PyExc_PermissionError variable static PyTypeObject _PyExc_PermissionError -Objects/exceptions.c - PyExc_PermissionError variable static PyTypeObject PyExc_PermissionError -Objects/exceptions.c - _PyExc_ProcessLookupError variable static PyTypeObject _PyExc_ProcessLookupError -Objects/exceptions.c - PyExc_ProcessLookupError variable static PyTypeObject PyExc_ProcessLookupError -Objects/exceptions.c - _PyExc_RecursionError variable static PyTypeObject _PyExc_RecursionError -Objects/exceptions.c - PyExc_RecursionError variable static PyTypeObject PyExc_RecursionError -Objects/exceptions.c - _PyExc_ReferenceError variable static PyTypeObject _PyExc_ReferenceError -Objects/exceptions.c - PyExc_ReferenceError variable static PyTypeObject PyExc_ReferenceError -Objects/exceptions.c - _PyExc_ResourceWarning variable static PyTypeObject _PyExc_ResourceWarning -Objects/exceptions.c - PyExc_ResourceWarning variable static PyTypeObject PyExc_ResourceWarning -Objects/exceptions.c - _PyExc_RuntimeError variable static PyTypeObject _PyExc_RuntimeError -Objects/exceptions.c - PyExc_RuntimeError variable static PyTypeObject PyExc_RuntimeError -Objects/exceptions.c - _PyExc_RuntimeWarning variable static PyTypeObject _PyExc_RuntimeWarning -Objects/exceptions.c - PyExc_RuntimeWarning variable static PyTypeObject PyExc_RuntimeWarning -Objects/exceptions.c - _PyExc_StopAsyncIteration variable static PyTypeObject _PyExc_StopAsyncIteration -Objects/exceptions.c - PyExc_StopAsyncIteration variable static PyTypeObject PyExc_StopAsyncIteration -Objects/exceptions.c - _PyExc_StopIteration variable static PyTypeObject _PyExc_StopIteration -Objects/exceptions.c - PyExc_StopIteration variable static PyTypeObject PyExc_StopIteration -Objects/exceptions.c - _PyExc_SyntaxError variable static PyTypeObject _PyExc_SyntaxError -Objects/exceptions.c - PyExc_SyntaxError variable static PyTypeObject PyExc_SyntaxError -Objects/exceptions.c - _PyExc_SyntaxWarning variable static PyTypeObject _PyExc_SyntaxWarning -Objects/exceptions.c - PyExc_SyntaxWarning variable static PyTypeObject PyExc_SyntaxWarning -Objects/exceptions.c - _PyExc_SystemError variable static PyTypeObject _PyExc_SystemError -Objects/exceptions.c - PyExc_SystemError variable static PyTypeObject PyExc_SystemError -Objects/exceptions.c - _PyExc_SystemExit variable static PyTypeObject _PyExc_SystemExit -Objects/exceptions.c - PyExc_SystemExit variable static PyTypeObject PyExc_SystemExit -Objects/exceptions.c - _PyExc_TabError variable static PyTypeObject _PyExc_TabError -Objects/exceptions.c - PyExc_TabError variable static PyTypeObject PyExc_TabError -Objects/exceptions.c - _PyExc_TargetScopeError variable static PyTypeObject _PyExc_TargetScopeError -Objects/exceptions.c - PyExc_TargetScopeError variable static PyTypeObject PyExc_TargetScopeError -Objects/exceptions.c - _PyExc_TimeoutError variable static PyTypeObject _PyExc_TimeoutError -Objects/exceptions.c - PyExc_TimeoutError variable static PyTypeObject PyExc_TimeoutError -Objects/exceptions.c - _PyExc_TypeError variable static PyTypeObject _PyExc_TypeError -Objects/exceptions.c - PyExc_TypeError variable static PyTypeObject PyExc_TypeError -Objects/exceptions.c - _PyExc_UnboundLocalError variable static PyTypeObject _PyExc_UnboundLocalError -Objects/exceptions.c - PyExc_UnboundLocalError variable static PyTypeObject PyExc_UnboundLocalError -Objects/exceptions.c - _PyExc_UnicodeDecodeError variable static PyTypeObject _PyExc_UnicodeDecodeError -Objects/exceptions.c - PyExc_UnicodeDecodeError variable static PyTypeObject PyExc_UnicodeDecodeError -Objects/exceptions.c - _PyExc_UnicodeEncodeError variable static PyTypeObject _PyExc_UnicodeEncodeError -Objects/exceptions.c - PyExc_UnicodeEncodeError variable static PyTypeObject PyExc_UnicodeEncodeError -Objects/exceptions.c - _PyExc_UnicodeError variable static PyTypeObject _PyExc_UnicodeError -Objects/exceptions.c - PyExc_UnicodeError variable static PyTypeObject PyExc_UnicodeError -Objects/exceptions.c - _PyExc_UnicodeTranslateError variable static PyTypeObject _PyExc_UnicodeTranslateError -Objects/exceptions.c - PyExc_UnicodeTranslateError variable static PyTypeObject PyExc_UnicodeTranslateError -Objects/exceptions.c - _PyExc_UnicodeWarning variable static PyTypeObject _PyExc_UnicodeWarning -Objects/exceptions.c - PyExc_UnicodeWarning variable static PyTypeObject PyExc_UnicodeWarning -Objects/exceptions.c - _PyExc_UserWarning variable static PyTypeObject _PyExc_UserWarning -Objects/exceptions.c - PyExc_UserWarning variable static PyTypeObject PyExc_UserWarning -Objects/exceptions.c - _PyExc_ValueError variable static PyTypeObject _PyExc_ValueError -Objects/exceptions.c - PyExc_ValueError variable static PyTypeObject PyExc_ValueError -Objects/exceptions.c - _PyExc_Warning variable static PyTypeObject _PyExc_Warning -Objects/exceptions.c - PyExc_Warning variable static PyTypeObject PyExc_Warning -Objects/exceptions.c - _PyExc_ZeroDivisionError variable static PyTypeObject _PyExc_ZeroDivisionError -Objects/exceptions.c - PyExc_ZeroDivisionError variable static PyTypeObject PyExc_ZeroDivisionError -Objects/boolobject.c - _Py_FalseStruct variable static struct _longobject _Py_FalseStruct -Objects/tupleobject.c - _Py_fast_tuple_allocs variable Py_ssize_t _Py_fast_tuple_allocs -Objects/stringlib/unicode_format.h - PyFieldNameIter_Type variable static PyTypeObject PyFieldNameIter_Type -Modules/_io/fileio.c - PyFileIO_Type variable PyTypeObject PyFileIO_Type -Python/preconfig.c - Py_FileSystemDefaultEncodeErrors variable const char *Py_FileSystemDefaultEncodeErrors -Python/preconfig.c - Py_FileSystemDefaultEncoding variable const char * Py_FileSystemDefaultEncoding -Python/bltinmodule.c - PyFilter_Type variable PyTypeObject PyFilter_Type -Objects/floatobject.c - PyFloat_Type variable PyTypeObject PyFloat_Type -Objects/stringlib/unicode_format.h - PyFormatterIter_Type variable static PyTypeObject PyFormatterIter_Type -Objects/frameobject.c - PyFrame_Type variable PyTypeObject PyFrame_Type -Python/initconfig.c - Py_FrozenFlag variable int Py_FrozenFlag -Objects/setobject.c - PyFrozenSet_Type variable PyTypeObject PyFrozenSet_Type -Objects/funcobject.c - PyFunction_Type variable PyTypeObject PyFunction_Type -Objects/genobject.c - PyGen_Type variable PyTypeObject PyGen_Type -Objects/descrobject.c - PyGetSetDescr_Type variable PyTypeObject PyGetSetDescr_Type -Python/hamt.c - _PyHamt_ArrayNode_Type variable PyTypeObject _PyHamt_ArrayNode_Type -Python/hamt.c - PyHamt_as_mapping variable static PyMappingMethods PyHamt_as_mapping -Python/hamt.c - PyHamt_as_sequence variable static PySequenceMethods PyHamt_as_sequence -Python/hamt.c - _PyHamt_BitmapNode_Type variable PyTypeObject _PyHamt_BitmapNode_Type -Python/hamt.c - _PyHamt_CollisionNode_Type variable PyTypeObject _PyHamt_CollisionNode_Type -Python/hamt.c - _PyHamtItems_Type variable PyTypeObject _PyHamtItems_Type -Python/hamt.c - PyHamtIterator_as_mapping variable static PyMappingMethods PyHamtIterator_as_mapping -Python/hamt.c - _PyHamtKeys_Type variable PyTypeObject _PyHamtKeys_Type -Python/hamt.c - PyHamt_methods variable static PyMethodDef PyHamt_methods -Python/hamt.c - _PyHamt_Type variable PyTypeObject _PyHamt_Type -Python/hamt.c - _PyHamtValues_Type variable PyTypeObject _PyHamtValues_Type -Python/preconfig.c - _Py_HasFileSystemDefaultEncodeErrors variable const(int) _Py_HasFileSystemDefaultEncodeErrors -Python/preconfig.c - Py_HasFileSystemDefaultEncoding variable const(int) Py_HasFileSystemDefaultEncoding -Python/pyhash.c - PyHash_Func variable static PyHash_FuncDef PyHash_Func -Python/initconfig.c - Py_HashRandomizationFlag variable int Py_HashRandomizationFlag -Python/pyhash.c - _Py_HashSecret variable _Py_HashSecret_t _Py_HashSecret -Python/bootstrap_hash.c - _Py_HashSecret_Initialized variable static int _Py_HashSecret_Initialized -Python/codecs.c - Py_hexdigits variable const char * Py_hexdigits -Python/sysmodule.c - PyId__ variable _Py_IDENTIFIER(_) -Modules/_abc.c - PyId__abc_impl variable _Py_IDENTIFIER(_abc_impl) -Objects/typeobject.c - PyId___abstractmethods__ variable _Py_IDENTIFIER(__abstractmethods__) -Modules/_abc.c - PyId___abstractmethods__ variable _Py_IDENTIFIER(__abstractmethods__) -Python/ceval.c _PyEval_EvalFrameDefault PyId___aenter__ variable _Py_IDENTIFIER(__aenter__) -Python/ceval.c _PyEval_EvalFrameDefault PyId___aexit__ variable _Py_IDENTIFIER(__aexit__) -Objects/typeobject.c slot_am_aiter PyId___aiter__ variable _Py_IDENTIFIER(__aiter__) -Python/ceval.c import_all_from PyId___all__ variable _Py_IDENTIFIER(__all__) -Objects/typeobject.c slot_am_anext PyId___anext__ variable _Py_IDENTIFIER(__anext__) -Python/Python-ast.c - PyId_annotation variable _Py_IDENTIFIER(annotation) -Python/ceval.c _PyEval_EvalFrameDefault PyId___annotations__ variable _Py_IDENTIFIER(__annotations__) -Python/Python-ast.c - PyId_arg variable _Py_IDENTIFIER(arg) -Python/Python-ast.c - PyId_args variable _Py_IDENTIFIER(args) -Python/Python-ast.c - PyId_argtypes variable _Py_IDENTIFIER(argtypes) -Python/Python-ast.c - PyId_asname variable _Py_IDENTIFIER(asname) -Python/Python-ast.c make_type PyId__ast variable _Py_IDENTIFIER(_ast) -Python/Python-ast.c - PyId_attr variable _Py_IDENTIFIER(attr) -Python/Python-ast.c - PyId__attributes variable _Py_IDENTIFIER(_attributes) -Objects/typeobject.c slot_am_await PyId___await__ variable _Py_IDENTIFIER(__await__) -Python/Python-ast.c - PyId_bases variable _Py_IDENTIFIER(bases) -Modules/_abc.c - PyId___bases__ variable _Py_IDENTIFIER(__bases__) -Objects/abstract.c abstract_get_bases PyId___bases__ variable _Py_IDENTIFIER(__bases__) -Objects/typeobject.c merge_class_dict PyId___bases__ variable _Py_IDENTIFIER(__bases__) -Objects/longobject.c - PyId_big variable _Py_IDENTIFIER(big) -Modules/_io/_iomodule.c _io_open_impl PyId__blksize variable _Py_IDENTIFIER(_blksize) -Python/Python-ast.c - PyId_body variable _Py_IDENTIFIER(body) -Objects/typeobject.c slot_nb_bool PyId___bool__ variable _Py_IDENTIFIER(__bool__) -Python/sysmodule.c - PyId_buffer variable _Py_IDENTIFIER(buffer) -Python/ceval.c _PyEval_EvalFrameDefault PyId___build_class__ variable _Py_IDENTIFIER(__build_class__) -Objects/typeobject.c - PyId_builtins variable _Py_IDENTIFIER(builtins) -Python/errors.c - PyId_builtins variable _Py_IDENTIFIER(builtins) -Python/pythonrun.c - PyId_builtins variable _Py_IDENTIFIER(builtins) -Python/sysmodule.c - PyId_builtins variable _Py_IDENTIFIER(builtins) -Objects/frameobject.c - PyId___builtins__ variable _Py_IDENTIFIER(__builtins__) -Python/bltinmodule.c - PyId___builtins__ variable _Py_IDENTIFIER(__builtins__) -Python/import.c module_dict_for_exec PyId___builtins__ variable _Py_IDENTIFIER(__builtins__) -Objects/object.c - PyId___bytes__ variable _Py_IDENTIFIER(__bytes__) -Objects/bytesobject.c format_obj PyId___bytes__ variable _Py_IDENTIFIER(__bytes__) -Objects/bytesobject.c bytes_new PyId___bytes__ variable _Py_IDENTIFIER(__bytes__) -Objects/weakrefobject.c proxy_bytes PyId___bytes__ variable _Py_IDENTIFIER(__bytes__) -Objects/typeobject.c slot_tp_call PyId___call__ variable _Py_IDENTIFIER(__call__) -Python/Python-ast.c - PyId_cause variable _Py_IDENTIFIER(cause) -Objects/typeobject.c - PyId___class__ variable _Py_IDENTIFIER(__class__) -Modules/_abc.c - PyId___class__ variable _Py_IDENTIFIER(__class__) -Python/compile.c compiler_enter_scope PyId___class__ variable _Py_IDENTIFIER(__class__) -Objects/abstract.c recursive_isinstance PyId___class__ variable _Py_IDENTIFIER(__class__) -Objects/typeobject.c type_new PyId___classcell__ variable _Py_IDENTIFIER(__classcell__) -Objects/typeobject.c - PyId___class_getitem__ variable _Py_IDENTIFIER(__class_getitem__) -Objects/abstract.c PyObject_GetItem PyId___class_getitem__ variable _Py_IDENTIFIER(__class_getitem__) -Python/import.c PyImport_Cleanup PyId_clear variable _Py_IDENTIFIER(clear) -Python/traceback.c - PyId_close variable _Py_IDENTIFIER(close) -Modules/_io/bufferedio.c - PyId_close variable _Py_IDENTIFIER(close) -Modules/_io/textio.c - PyId_close variable _Py_IDENTIFIER(close) -Objects/genobject.c gen_close_iter PyId_close variable _Py_IDENTIFIER(close) -Modules/_dbmmodule.c dbm__exit__ PyId_close variable _Py_IDENTIFIER(close) -Modules/_gdbmmodule.c dbm__exit__ PyId_close variable _Py_IDENTIFIER(close) -Python/pythonrun.c _Py_HandleSystemExit PyId_code variable _Py_IDENTIFIER(code) -Python/Python-ast.c - PyId_col_offset variable _Py_IDENTIFIER(col_offset) -Python/Python-ast.c - PyId_comparators variable _Py_IDENTIFIER(comparators) -Objects/complexobject.c try_complex_special_method PyId___complex__ variable _Py_IDENTIFIER(__complex__) -Objects/typeobject.c slot_sq_contains PyId___contains__ variable _Py_IDENTIFIER(__contains__) -Python/Python-ast.c - PyId_context_expr variable _Py_IDENTIFIER(context_expr) -Python/Python-ast.c - PyId_conversion variable _Py_IDENTIFIER(conversion) -Modules/itertoolsmodule.c itertools_tee_impl PyId___copy__ variable _Py_IDENTIFIER(__copy__) -Objects/descrobject.c mappingproxy_copy PyId_copy variable _Py_IDENTIFIER(copy) -Objects/typeobject.c import_copyreg PyId_copyreg variable _Py_IDENTIFIER(copyreg) -Python/Python-ast.c - PyId_ctx variable _Py_IDENTIFIER(ctx) -Modules/_io/bufferedio.c - PyId__dealloc_warn variable _Py_IDENTIFIER(_dealloc_warn) -Modules/_io/textio.c - PyId__dealloc_warn variable _Py_IDENTIFIER(_dealloc_warn) -Modules/_io/textio.c - PyId_decode variable _Py_IDENTIFIER(decode) -Python/Python-ast.c - PyId_decorator_list variable _Py_IDENTIFIER(decorator_list) -Python/_warnings.c get_default_action PyId_defaultaction variable _Py_IDENTIFIER(defaultaction) -Python/Python-ast.c - PyId_defaults variable _Py_IDENTIFIER(defaults) -Objects/typeobject.c slot_tp_finalize PyId___del__ variable _Py_IDENTIFIER(__del__) -Objects/typeobject.c slot_tp_setattro PyId___delattr__ variable _Py_IDENTIFIER(__delattr__) -Objects/typeobject.c slot_tp_descr_set PyId___delete__ variable _Py_IDENTIFIER(__delete__) -Objects/typeobject.c - PyId___delitem__ variable _Py_IDENTIFIER(__delitem__) -Objects/typeobject.c - PyId___dict__ variable _Py_IDENTIFIER(__dict__) -Modules/_abc.c - PyId___dict__ variable _Py_IDENTIFIER(__dict__) -Python/bltinmodule.c - PyId___dict__ variable _Py_IDENTIFIER(__dict__) -Python/Python-ast.c ast_type_reduce PyId___dict__ variable _Py_IDENTIFIER(__dict__) -Python/ceval.c import_all_from PyId___dict__ variable _Py_IDENTIFIER(__dict__) -Objects/bytearrayobject.c _common_reduce PyId___dict__ variable _Py_IDENTIFIER(__dict__) -Objects/moduleobject.c module_dir PyId___dict__ variable _Py_IDENTIFIER(__dict__) -Objects/odictobject.c odict_reduce PyId___dict__ variable _Py_IDENTIFIER(__dict__) -Objects/setobject.c set_reduce PyId___dict__ variable _Py_IDENTIFIER(__dict__) -Modules/_collectionsmodule.c deque_reduce PyId___dict__ variable _Py_IDENTIFIER(__dict__) -Objects/dictobject.c dictviews_sub PyId_difference_update variable _Py_IDENTIFIER(difference_update) -Python/Python-ast.c - PyId_dims variable _Py_IDENTIFIER(dims) -Objects/object.c - PyId___dir__ variable _Py_IDENTIFIER(__dir__) -Objects/moduleobject.c module_dir PyId___dir__ variable _Py_IDENTIFIER(__dir__) -Python/ceval.c _PyEval_EvalFrameDefault PyId_displayhook variable _Py_IDENTIFIER(displayhook) -Objects/typeobject.c - PyId___doc__ variable _Py_IDENTIFIER(__doc__) -Objects/descrobject.c property_init_impl PyId___doc__ variable _Py_IDENTIFIER(__doc__) -Objects/moduleobject.c module_init_dict PyId___doc__ variable _Py_IDENTIFIER(__doc__) -Objects/moduleobject.c PyModule_SetDocString PyId___doc__ variable _Py_IDENTIFIER(__doc__) -Python/Python-ast.c - PyId_elt variable _Py_IDENTIFIER(elt) -Python/Python-ast.c - PyId_elts variable _Py_IDENTIFIER(elts) -Modules/faulthandler.c - PyId_enable variable _Py_IDENTIFIER(enable) -Python/sysmodule.c - PyId_encoding variable _Py_IDENTIFIER(encoding) -Python/bltinmodule.c - PyId_encoding variable _Py_IDENTIFIER(encoding) -Python/pythonrun.c PyRun_InteractiveOneObjectEx PyId_encoding variable _Py_IDENTIFIER(encoding) -Python/Python-ast.c - PyId_end_col_offset variable _Py_IDENTIFIER(end_col_offset) -Python/Python-ast.c - PyId_end_lineno variable _Py_IDENTIFIER(end_lineno) -Python/ceval.c _PyEval_EvalFrameDefault PyId___enter__ variable _Py_IDENTIFIER(__enter__) -Objects/typeobject.c overrides_hash PyId___eq__ variable _Py_IDENTIFIER(__eq__) -Python/bltinmodule.c - PyId_errors variable _Py_IDENTIFIER(errors) -Python/Python-ast.c - PyId_exc variable _Py_IDENTIFIER(exc) -Python/pythonrun.c - PyId_excepthook variable _Py_IDENTIFIER(excepthook) -Python/ceval.c _PyEval_EvalFrameDefault PyId___exit__ variable _Py_IDENTIFIER(__exit__) -Modules/_pickle.c do_append PyId_extend variable _Py_IDENTIFIER(extend) -Python/Python-ast.c - PyId__fields variable _Py_IDENTIFIER(_fields) -Objects/moduleobject.c PyModule_GetFilenameObject PyId___file__ variable _Py_IDENTIFIER(__file__) -Python/errors.c PyErr_SyntaxLocationObject PyId_filename variable _Py_IDENTIFIER(filename) -Python/pythonrun.c parse_syntax_error PyId_filename variable _Py_IDENTIFIER(filename) -Modules/_io/textio.c - PyId_fileno variable _Py_IDENTIFIER(fileno) -Modules/faulthandler.c - PyId_fileno variable _Py_IDENTIFIER(fileno) -Python/bltinmodule.c - PyId_fileno variable _Py_IDENTIFIER(fileno) -Objects/fileobject.c PyObject_AsFileDescriptor PyId_fileno variable _Py_IDENTIFIER(fileno) -Modules/itertoolsmodule.c zip_longest_new PyId_fillvalue variable _Py_IDENTIFIER(fillvalue) -Python/_warnings.c get_filter PyId_filters variable _Py_IDENTIFIER(filters) -Python/Python-ast.c - PyId_finalbody variable _Py_IDENTIFIER(finalbody) -Modules/_io/iobase.c iobase_finalize PyId__finalizing variable _Py_IDENTIFIER(_finalizing) -Python/import.c import_find_and_load PyId__find_and_load variable _Py_IDENTIFIER(_find_and_load) -Python/import.c PyImport_ExecCodeModuleObject PyId__fix_up_module variable _Py_IDENTIFIER(_fix_up_module) -Python/errors.c - PyId_flush variable _Py_IDENTIFIER(flush) -Python/pylifecycle.c - PyId_flush variable _Py_IDENTIFIER(flush) -Python/pythonrun.c - PyId_flush variable _Py_IDENTIFIER(flush) -Modules/_threadmodule.c - PyId_flush variable _Py_IDENTIFIER(flush) -Modules/_io/bufferedio.c - PyId_flush variable _Py_IDENTIFIER(flush) -Modules/_io/textio.c - PyId_flush variable _Py_IDENTIFIER(flush) -Modules/faulthandler.c - PyId_flush variable _Py_IDENTIFIER(flush) -Python/bltinmodule.c - PyId_flush variable _Py_IDENTIFIER(flush) -Objects/abstract.c PyObject_Format PyId___format__ variable _Py_IDENTIFIER(__format__) -Python/Python-ast.c - PyId_format_spec variable _Py_IDENTIFIER(format_spec) -Modules/posixmodule.c path_converter PyId___fspath__ variable _Py_IDENTIFIER(__fspath__) -Modules/posixmodule.c PyOS_FSPath PyId___fspath__ variable _Py_IDENTIFIER(__fspath__) -Python/Python-ast.c - PyId_func variable _Py_IDENTIFIER(func) -Python/Python-ast.c - PyId_generators variable _Py_IDENTIFIER(generators) -Objects/descrobject.c mappingproxy_get PyId_get variable _Py_IDENTIFIER(get) -Modules/_collectionsmodule.c _count_elements PyId_get variable _Py_IDENTIFIER(get) -Objects/typeobject.c slot_tp_descr_get PyId___get__ variable _Py_IDENTIFIER(__get__) -Objects/classobject.c method_reduce PyId_getattr variable _Py_IDENTIFIER(getattr) -Objects/descrobject.c descr_reduce PyId_getattr variable _Py_IDENTIFIER(getattr) -Objects/descrobject.c wrapper_reduce PyId_getattr variable _Py_IDENTIFIER(getattr) -Objects/moduleobject.c module_getattro PyId___getattr__ variable _Py_IDENTIFIER(__getattr__) -Objects/methodobject.c meth_reduce PyId_getattr variable _Py_IDENTIFIER(getattr) -Objects/typeobject.c slot_tp_getattr_hook PyId___getattr__ variable _Py_IDENTIFIER(__getattr__) -Objects/typeobject.c - PyId___getattribute__ variable _Py_IDENTIFIER(__getattribute__) -Objects/typeobject.c - PyId___getitem__ variable _Py_IDENTIFIER(__getitem__) -Objects/typeobject.c _PyObject_GetNewArguments PyId___getnewargs__ variable _Py_IDENTIFIER(__getnewargs__) -Objects/typeobject.c _PyObject_GetNewArguments PyId___getnewargs_ex__ variable _Py_IDENTIFIER(__getnewargs_ex__) -Modules/_io/textio.c - PyId_getpreferredencoding variable _Py_IDENTIFIER(getpreferredencoding) -Python/_warnings.c get_source_line PyId_get_source variable _Py_IDENTIFIER(get_source) -Python/import.c PyImport_ExecCodeModuleWithPathnames PyId__get_sourcefile variable _Py_IDENTIFIER(_get_sourcefile) -Objects/typeobject.c _PyObject_GetState PyId___getstate__ variable _Py_IDENTIFIER(__getstate__) -Python/import.c PyImport_ImportModuleLevelObject PyId__handle_fromlist variable _Py_IDENTIFIER(_handle_fromlist) -Python/Python-ast.c - PyId_handlers variable _Py_IDENTIFIER(handlers) -Objects/typeobject.c - PyId___hash__ variable _Py_IDENTIFIER(__hash__) -Python/Python-ast.c - PyId_id variable _Py_IDENTIFIER(id) -Python/Python-ast.c - PyId_ifs variable _Py_IDENTIFIER(ifs) -Python/import.c PyImport_ReloadModule PyId_imp variable _Py_IDENTIFIER(imp) -Python/ceval.c import_name PyId___import__ variable _Py_IDENTIFIER(__import__) -Objects/typeobject.c slot_nb_index PyId___index__ variable _Py_IDENTIFIER(__index__) -Objects/typeobject.c slot_tp_init PyId___init__ variable _Py_IDENTIFIER(__init__) -Objects/moduleobject.c _PyModuleSpec_IsInitializing PyId__initializing variable _Py_IDENTIFIER(_initializing) -Objects/typeobject.c - PyId___init_subclass__ variable _Py_IDENTIFIER(__init_subclass__) -Objects/abstract.c PyObject_IsInstance PyId___instancecheck__ variable _Py_IDENTIFIER(__instancecheck__) -Objects/dictobject.c _PyDictView_Intersect PyId_intersection_update variable _Py_IDENTIFIER(intersection_update) -Modules/_io/iobase.c - PyId___IOBase_closed variable _Py_IDENTIFIER(__IOBase_closed) -Objects/typeobject.c slot_nb_inplace_power PyId___ipow__ variable _Py_IDENTIFIER(__ipow__) -Objects/object.c - PyId___isabstractmethod__ variable _Py_IDENTIFIER(__isabstractmethod__) -Python/Python-ast.c - PyId_is_async variable _Py_IDENTIFIER(is_async) -Modules/_io/bufferedio.c - PyId_isatty variable _Py_IDENTIFIER(isatty) -Modules/_io/textio.c - PyId_isatty variable _Py_IDENTIFIER(isatty) -Python/pylifecycle.c create_stdio PyId_isatty variable _Py_IDENTIFIER(isatty) -Modules/_io/_iomodule.c _io_open_impl PyId_isatty variable _Py_IDENTIFIER(isatty) -Python/codecs.c _PyCodec_LookupTextEncoding PyId__is_text_encoding variable _Py_IDENTIFIER(_is_text_encoding) -Python/Python-ast.c - PyId_items variable _Py_IDENTIFIER(items) -Objects/abstract.c PyMapping_Items PyId_items variable _Py_IDENTIFIER(items) -Objects/descrobject.c mappingproxy_items PyId_items variable _Py_IDENTIFIER(items) -Objects/odictobject.c odict_reduce PyId_items variable _Py_IDENTIFIER(items) -Objects/odictobject.c odict_repr PyId_items variable _Py_IDENTIFIER(items) -Objects/odictobject.c mutablemapping_update PyId_items variable _Py_IDENTIFIER(items) -Objects/typeobject.c _PyObject_GetItemsIter PyId_items variable _Py_IDENTIFIER(items) -Modules/_collectionsmodule.c defdict_reduce PyId_items variable _Py_IDENTIFIER(items) -Python/Python-ast.c - PyId_iter variable _Py_IDENTIFIER(iter) -Objects/bytearrayobject.c bytearrayiter_reduce PyId_iter variable _Py_IDENTIFIER(iter) -Objects/bytesobject.c striter_reduce PyId_iter variable _Py_IDENTIFIER(iter) -Objects/dictobject.c dictiter_reduce PyId_iter variable _Py_IDENTIFIER(iter) -Objects/iterobject.c iter_reduce PyId_iter variable _Py_IDENTIFIER(iter) -Objects/iterobject.c calliter_reduce PyId_iter variable _Py_IDENTIFIER(iter) -Objects/listobject.c listiter_reduce_general PyId_iter variable _Py_IDENTIFIER(iter) -Objects/odictobject.c odictiter_reduce PyId_iter variable _Py_IDENTIFIER(iter) -Objects/rangeobject.c rangeiter_reduce PyId_iter variable _Py_IDENTIFIER(iter) -Objects/rangeobject.c longrangeiter_reduce PyId_iter variable _Py_IDENTIFIER(iter) -Objects/setobject.c setiter_reduce PyId_iter variable _Py_IDENTIFIER(iter) -Objects/tupleobject.c tupleiter_reduce PyId_iter variable _Py_IDENTIFIER(iter) -Objects/unicodeobject.c unicodeiter_reduce PyId_iter variable _Py_IDENTIFIER(iter) -Objects/typeobject.c slot_tp_iter PyId___iter__ variable _Py_IDENTIFIER(__iter__) -Modules/arraymodule.c array_arrayiterator___reduce___impl PyId_iter variable _Py_IDENTIFIER(iter) -Python/Python-ast.c - PyId_key variable _Py_IDENTIFIER(key) -Python/Python-ast.c - PyId_keys variable _Py_IDENTIFIER(keys) -Objects/abstract.c PyMapping_Keys PyId_keys variable _Py_IDENTIFIER(keys) -Objects/descrobject.c mappingproxy_keys PyId_keys variable _Py_IDENTIFIER(keys) -Objects/dictobject.c dict_update_common PyId_keys variable _Py_IDENTIFIER(keys) -Objects/odictobject.c mutablemapping_update PyId_keys variable _Py_IDENTIFIER(keys) -Python/Python-ast.c - PyId_keywords variable _Py_IDENTIFIER(keywords) -Python/Python-ast.c - PyId_kind variable _Py_IDENTIFIER(kind) -Python/Python-ast.c - PyId_kwarg variable _Py_IDENTIFIER(kwarg) -Python/Python-ast.c - PyId_kw_defaults variable _Py_IDENTIFIER(kw_defaults) -Python/Python-ast.c - PyId_kwonlyargs variable _Py_IDENTIFIER(kwonlyargs) -Python/pythonrun.c - PyId_last_traceback variable _Py_IDENTIFIER(last_traceback) -Python/pythonrun.c - PyId_last_type variable _Py_IDENTIFIER(last_type) -Python/pythonrun.c - PyId_last_value variable _Py_IDENTIFIER(last_value) -Python/Python-ast.c - PyId_left variable _Py_IDENTIFIER(left) -Objects/typeobject.c - PyId___len__ variable _Py_IDENTIFIER(__len__) -Objects/abstract.c PyObject_LengthHint PyId___length_hint__ variable _Py_IDENTIFIER(__length_hint__) -Python/Python-ast.c - PyId_level variable _Py_IDENTIFIER(level) -Python/Python-ast.c - PyId_lineno variable _Py_IDENTIFIER(lineno) -Python/errors.c PyErr_SyntaxLocationObject PyId_lineno variable _Py_IDENTIFIER(lineno) -Python/pythonrun.c parse_syntax_error PyId_lineno variable _Py_IDENTIFIER(lineno) -Objects/longobject.c - PyId_little variable _Py_IDENTIFIER(little) -Python/_warnings.c get_source_line PyId___loader__ variable _Py_IDENTIFIER(__loader__) -Objects/moduleobject.c module_init_dict PyId___loader__ variable _Py_IDENTIFIER(__loader__) -Python/import.c PyImport_ImportModuleLevelObject PyId__lock_unlock_module variable _Py_IDENTIFIER(_lock_unlock_module) -Python/Python-ast.c - PyId_lower variable _Py_IDENTIFIER(lower) -Python/ceval.c _PyEval_EvalFrameDefault PyId___ltrace__ variable _Py_IDENTIFIER(__ltrace__) -Python/pythonrun.c PyRun_InteractiveOneObjectEx PyId___main__ variable _Py_IDENTIFIER(__main__) -Python/_warnings.c check_matched PyId_match variable _Py_IDENTIFIER(match) -Python/bltinmodule.c - PyId_metaclass variable _Py_IDENTIFIER(metaclass) -Objects/dictobject.c dict_subscript PyId___missing__ variable _Py_IDENTIFIER(__missing__) -Modules/_io/bufferedio.c - PyId_mode variable _Py_IDENTIFIER(mode) -Modules/_io/textio.c - PyId_mode variable _Py_IDENTIFIER(mode) -Python/pylifecycle.c create_stdio PyId_mode variable _Py_IDENTIFIER(mode) -Modules/_io/_iomodule.c _io_open_impl PyId_mode variable _Py_IDENTIFIER(mode) -Python/Python-ast.c - PyId_module variable _Py_IDENTIFIER(module) -Objects/typeobject.c - PyId___module__ variable _Py_IDENTIFIER(__module__) -Python/Python-ast.c make_type PyId___module__ variable _Py_IDENTIFIER(__module__) -Python/errors.c PyErr_NewException PyId___module__ variable _Py_IDENTIFIER(__module__) -Python/errors.c PyErr_NewException PyId___module__ variable _Py_IDENTIFIER(__module__) -Python/pythonrun.c print_exception PyId___module__ variable _Py_IDENTIFIER(__module__) -Modules/_pickle.c whichmodule PyId___module__ variable _Py_IDENTIFIER(__module__) -Objects/typeobject.c type_mro_modified PyId_mro variable _Py_IDENTIFIER(mro) -Objects/typeobject.c mro_invoke PyId_mro variable _Py_IDENTIFIER(mro) -Python/bltinmodule.c - PyId___mro_entries__ variable _Py_IDENTIFIER(__mro_entries__) -Objects/typeobject.c type_new PyId___mro_entries__ variable _Py_IDENTIFIER(__mro_entries__) -Python/Python-ast.c - PyId_msg variable _Py_IDENTIFIER(msg) -Python/errors.c PyErr_SyntaxLocationObject PyId_msg variable _Py_IDENTIFIER(msg) -Python/pythonrun.c parse_syntax_error PyId_msg variable _Py_IDENTIFIER(msg) -Python/pylifecycle.c - PyId_name variable _Py_IDENTIFIER(name) -Modules/_io/fileio.c - PyId_name variable _Py_IDENTIFIER(name) -Modules/_io/bufferedio.c - PyId_name variable _Py_IDENTIFIER(name) -Modules/_io/textio.c - PyId_name variable _Py_IDENTIFIER(name) -Python/Python-ast.c - PyId_name variable _Py_IDENTIFIER(name) -Objects/exceptions.c ImportError_getstate PyId_name variable _Py_IDENTIFIER(name) -Objects/typeobject.c - PyId___name__ variable _Py_IDENTIFIER(__name__) -Objects/classobject.c - PyId___name__ variable _Py_IDENTIFIER(__name__) -Python/_warnings.c setup_context PyId___name__ variable _Py_IDENTIFIER(__name__) -Python/_warnings.c get_source_line PyId___name__ variable _Py_IDENTIFIER(__name__) -Python/_warnings.c show_warning PyId___name__ variable _Py_IDENTIFIER(__name__) -Python/ceval.c import_from PyId___name__ variable _Py_IDENTIFIER(__name__) -Python/ceval.c import_all_from PyId___name__ variable _Py_IDENTIFIER(__name__) -Python/import.c resolve_name PyId___name__ variable _Py_IDENTIFIER(__name__) -Objects/moduleobject.c module_init_dict PyId___name__ variable _Py_IDENTIFIER(__name__) -Objects/moduleobject.c PyModule_GetNameObject PyId___name__ variable _Py_IDENTIFIER(__name__) -Objects/moduleobject.c module_getattro PyId___name__ variable _Py_IDENTIFIER(__name__) -Objects/weakrefobject.c weakref_repr PyId___name__ variable _Py_IDENTIFIER(__name__) -Modules/_pickle.c save_global PyId___name__ variable _Py_IDENTIFIER(__name__) -Modules/_pickle.c save_reduce PyId___name__ variable _Py_IDENTIFIER(__name__) -Python/Python-ast.c - PyId_names variable _Py_IDENTIFIER(names) -Objects/typeobject.c - PyId___new__ variable _Py_IDENTIFIER(__new__) -Objects/typeobject.c reduce_newobj PyId___newobj__ variable _Py_IDENTIFIER(__newobj__) -Objects/typeobject.c reduce_newobj PyId___newobj_ex__ variable _Py_IDENTIFIER(__newobj_ex__) -Objects/typeobject.c slot_tp_iternext PyId___next__ variable _Py_IDENTIFIER(__next__) -Objects/structseq.c - PyId_n_fields variable _Py_IDENTIFIER(n_fields) -Python/ast.c new_identifier PyId_NFKC variable _Py_IDENTIFIER(NFKC) -Objects/structseq.c - PyId_n_sequence_fields variable _Py_IDENTIFIER(n_sequence_fields) -Objects/structseq.c - PyId_n_unnamed_fields variable _Py_IDENTIFIER(n_unnamed_fields) -Python/errors.c PyErr_SyntaxLocationObject PyId_offset variable _Py_IDENTIFIER(offset) -Python/pythonrun.c parse_syntax_error PyId_offset variable _Py_IDENTIFIER(offset) -Python/_warnings.c get_once_registry PyId_onceregistry variable _Py_IDENTIFIER(onceregistry) -Python/Python-ast.c - PyId_op variable _Py_IDENTIFIER(op) -Python/traceback.c - PyId_open variable _Py_IDENTIFIER(open) -Python/pylifecycle.c create_stdio PyId_open variable _Py_IDENTIFIER(open) -Parser/tokenizer.c fp_setreadl PyId_open variable _Py_IDENTIFIER(open) -Objects/fileobject.c PyFile_FromFd PyId_open variable _Py_IDENTIFIER(open) -Objects/fileobject.c PyFile_OpenCodeObject PyId_open variable _Py_IDENTIFIER(open) -Python/Python-ast.c - PyId_operand variable _Py_IDENTIFIER(operand) -Python/Python-ast.c - PyId_ops variable _Py_IDENTIFIER(ops) -Python/Python-ast.c - PyId_optional_vars variable _Py_IDENTIFIER(optional_vars) -Python/Python-ast.c - PyId_orelse variable _Py_IDENTIFIER(orelse) -Python/import.c resolve_name PyId___package__ variable _Py_IDENTIFIER(__package__) -Objects/moduleobject.c module_init_dict PyId___package__ variable _Py_IDENTIFIER(__package__) -Python/import.c resolve_name PyId_parent variable _Py_IDENTIFIER(parent) -Modules/_operator.c methodcaller_reduce PyId_partial variable _Py_IDENTIFIER(partial) -Python/sysmodule.c - PyId_path variable _Py_IDENTIFIER(path) -Python/traceback.c - PyId_path variable _Py_IDENTIFIER(path) -Objects/exceptions.c ImportError_getstate PyId_path variable _Py_IDENTIFIER(path) -Modules/main.c pymain_sys_path_add_path0 PyId_path variable _Py_IDENTIFIER(path) -Python/import.c resolve_name PyId___path__ variable _Py_IDENTIFIER(__path__) -Python/import.c PyImport_ImportModuleLevelObject PyId___path__ variable _Py_IDENTIFIER(__path__) -Modules/_io/bufferedio.c - PyId_peek variable _Py_IDENTIFIER(peek) -Python/Python-ast.c - PyId_posonlyargs variable _Py_IDENTIFIER(posonlyargs) -Objects/typeobject.c slot_nb_power PyId___pow__ variable _Py_IDENTIFIER(__pow__) -Python/bltinmodule.c - PyId___prepare__ variable _Py_IDENTIFIER(__prepare__) -Python/errors.c PyErr_SyntaxLocationObject PyId_print_file_and_line variable _Py_IDENTIFIER(print_file_and_line) -Python/pythonrun.c print_exception PyId_print_file_and_line variable _Py_IDENTIFIER(print_file_and_line) -Python/pythonrun.c - PyId_ps1 variable _Py_IDENTIFIER(ps1) -Python/pythonrun.c - PyId_ps2 variable _Py_IDENTIFIER(ps2) -Objects/object.c - PyId_Py_Repr variable _Py_IDENTIFIER(Py_Repr) -Objects/classobject.c - PyId___qualname__ variable _Py_IDENTIFIER(__qualname__) -Objects/descrobject.c calculate_qualname PyId___qualname__ variable _Py_IDENTIFIER(__qualname__) -Objects/methodobject.c meth_get__qualname__ PyId___qualname__ variable _Py_IDENTIFIER(__qualname__) -Objects/typeobject.c type_new PyId___qualname__ variable _Py_IDENTIFIER(__qualname__) -Modules/_io/textio.c - PyId_raw variable _Py_IDENTIFIER(raw) -Python/pylifecycle.c create_stdio PyId_raw variable _Py_IDENTIFIER(raw) -Modules/_io/iobase.c - PyId_read variable _Py_IDENTIFIER(read) -Modules/_io/bufferedio.c - PyId_read variable _Py_IDENTIFIER(read) -Modules/_io/textio.c - PyId_read variable _Py_IDENTIFIER(read) -Modules/_io/bufferedio.c - PyId_read1 variable _Py_IDENTIFIER(read1) -Python/marshal.c marshal_load PyId_read variable _Py_IDENTIFIER(read) -Modules/_io/bufferedio.c - PyId_readable variable _Py_IDENTIFIER(readable) -Modules/_io/textio.c - PyId_readable variable _Py_IDENTIFIER(readable) -Modules/_io/iobase.c _io__RawIOBase_read_impl PyId_readall variable _Py_IDENTIFIER(readall) -Modules/_io/bufferedio.c - PyId_readinto variable _Py_IDENTIFIER(readinto) -Modules/_io/bufferedio.c - PyId_readinto1 variable _Py_IDENTIFIER(readinto1) -Python/marshal.c r_string PyId_readinto variable _Py_IDENTIFIER(readinto) -Parser/tokenizer.c fp_setreadl PyId_readline variable _Py_IDENTIFIER(readline) -Objects/fileobject.c PyFile_GetLine PyId_readline variable _Py_IDENTIFIER(readline) -Objects/typeobject.c object___reduce_ex___impl PyId___reduce__ variable _Py_IDENTIFIER(__reduce__) -Python/import.c PyImport_ReloadModule PyId_reload variable _Py_IDENTIFIER(reload) -Modules/_io/textio.c - PyId_replace variable _Py_IDENTIFIER(replace) -Python/importdl.c get_encoded_name PyId_replace variable _Py_IDENTIFIER(replace) -Objects/typeobject.c slot_tp_repr PyId___repr__ variable _Py_IDENTIFIER(__repr__) -Modules/_io/textio.c - PyId_reset variable _Py_IDENTIFIER(reset) -Python/Python-ast.c - PyId_returns variable _Py_IDENTIFIER(returns) -Objects/enumobject.c reversed_new_impl PyId___reversed__ variable _Py_IDENTIFIER(__reversed__) -Objects/listobject.c listiter_reduce_general PyId_reversed variable _Py_IDENTIFIER(reversed) -Python/Python-ast.c - PyId_right variable _Py_IDENTIFIER(right) -Python/bltinmodule.c - PyId___round__ variable _Py_IDENTIFIER(__round__) -Modules/_io/textio.c - PyId_seek variable _Py_IDENTIFIER(seek) -Modules/_io/iobase.c _io__IOBase_tell_impl PyId_seek variable _Py_IDENTIFIER(seek) -Modules/_io/textio.c - PyId_seekable variable _Py_IDENTIFIER(seekable) -Python/ceval.c _PyEval_EvalFrameDefault PyId_send variable _Py_IDENTIFIER(send) -Objects/typeobject.c slot_tp_descr_set PyId___set__ variable _Py_IDENTIFIER(__set__) -Objects/typeobject.c slot_tp_setattro PyId___setattr__ variable _Py_IDENTIFIER(__setattr__) -Objects/typeobject.c - PyId___setitem__ variable _Py_IDENTIFIER(__setitem__) -Modules/_collectionsmodule.c _count_elements PyId___setitem__ variable _Py_IDENTIFIER(__setitem__) -Objects/typeobject.c - PyId___set_name__ variable _Py_IDENTIFIER(__set_name__) -Modules/_io/textio.c - PyId_setstate variable _Py_IDENTIFIER(setstate) -Modules/_pickle.c load_build PyId___setstate__ variable _Py_IDENTIFIER(__setstate__) -Python/_warnings.c call_show_warning PyId__showwarnmsg variable _Py_IDENTIFIER(_showwarnmsg) -Python/pylifecycle.c wait_for_thread_shutdown PyId__shutdown variable _Py_IDENTIFIER(_shutdown) -Python/Python-ast.c - PyId_simple variable _Py_IDENTIFIER(simple) -Python/sysmodule.c - PyId___sizeof__ variable _Py_IDENTIFIER(__sizeof__) -Python/Python-ast.c - PyId_slice variable _Py_IDENTIFIER(slice) -Objects/typeobject.c _PyType_GetSlotNames PyId___slotnames__ variable _Py_IDENTIFIER(__slotnames__) -Objects/typeobject.c _PyType_GetSlotNames PyId__slotnames variable _Py_IDENTIFIER(_slotnames) -Objects/typeobject.c type_new PyId___slots__ variable _Py_IDENTIFIER(__slots__) -Python/bltinmodule.c - PyId_sort variable _Py_IDENTIFIER(sort) -Python/import.c resolve_name PyId___spec__ variable _Py_IDENTIFIER(__spec__) -Python/import.c PyImport_ImportModuleLevelObject PyId___spec__ variable _Py_IDENTIFIER(__spec__) -Objects/moduleobject.c module_init_dict PyId___spec__ variable _Py_IDENTIFIER(__spec__) -Objects/moduleobject.c module_getattro PyId___spec__ variable _Py_IDENTIFIER(__spec__) -Python/_warnings.c - PyId_stderr variable _Py_IDENTIFIER(stderr) -Python/errors.c - PyId_stderr variable _Py_IDENTIFIER(stderr) -Python/pylifecycle.c - PyId_stderr variable _Py_IDENTIFIER(stderr) -Python/pythonrun.c - PyId_stderr variable _Py_IDENTIFIER(stderr) -Python/sysmodule.c - PyId_stderr variable _Py_IDENTIFIER(stderr) -Modules/_threadmodule.c - PyId_stderr variable _Py_IDENTIFIER(stderr) -Modules/faulthandler.c - PyId_stderr variable _Py_IDENTIFIER(stderr) -Python/bltinmodule.c - PyId_stderr variable _Py_IDENTIFIER(stderr) -Python/pylifecycle.c - PyId_stdin variable _Py_IDENTIFIER(stdin) -Python/pythonrun.c - PyId_stdin variable _Py_IDENTIFIER(stdin) -Python/bltinmodule.c - PyId_stdin variable _Py_IDENTIFIER(stdin) -Python/pylifecycle.c - PyId_stdout variable _Py_IDENTIFIER(stdout) -Python/pythonrun.c - PyId_stdout variable _Py_IDENTIFIER(stdout) -Python/sysmodule.c - PyId_stdout variable _Py_IDENTIFIER(stdout) -Python/bltinmodule.c - PyId_stdout variable _Py_IDENTIFIER(stdout) -Python/Python-ast.c - PyId_step variable _Py_IDENTIFIER(step) -Modules/posixmodule.c DirEntry_test_mode PyId_st_mode variable _Py_IDENTIFIER(st_mode) -Modules/_io/textio.c - PyId_strict variable _Py_IDENTIFIER(strict) -Python/pythonrun.c - PyId_string variable _Py_static_string(PyId_string, """") -Modules/timemodule.c time_strptime PyId__strptime_time variable _Py_IDENTIFIER(_strptime_time) -Modules/posixmodule.c wait_helper PyId_struct_rusage variable _Py_IDENTIFIER(struct_rusage) -Modules/_abc.c - PyId___subclasscheck__ variable _Py_IDENTIFIER(__subclasscheck__) -Objects/abstract.c PyObject_IsSubclass PyId___subclasscheck__ variable _Py_IDENTIFIER(__subclasscheck__) -Modules/_abc.c - PyId___subclasshook__ variable _Py_IDENTIFIER(__subclasshook__) -Objects/dictobject.c dictviews_xor PyId_symmetric_difference_update variable _Py_IDENTIFIER(symmetric_difference_update) -Python/Python-ast.c - PyId_tag variable _Py_IDENTIFIER(tag) -Python/Python-ast.c - PyId_target variable _Py_IDENTIFIER(target) -Python/Python-ast.c - PyId_targets variable _Py_IDENTIFIER(targets) -Modules/_io/textio.c - PyId_tell variable _Py_IDENTIFIER(tell) -Python/Python-ast.c - PyId_test variable _Py_IDENTIFIER(test) -Python/errors.c PyErr_SyntaxLocationObject PyId_text variable _Py_IDENTIFIER(text) -Python/pythonrun.c parse_syntax_error PyId_text variable _Py_IDENTIFIER(text) -Python/traceback.c - PyId_TextIOWrapper variable _Py_IDENTIFIER(TextIOWrapper) -Python/pylifecycle.c create_stdio PyId_TextIOWrapper variable _Py_IDENTIFIER(TextIOWrapper) -Python/pylifecycle.c - PyId_threading variable _Py_IDENTIFIER(threading) -Objects/genobject.c _gen_throw PyId_throw variable _Py_IDENTIFIER(throw) -Objects/abstract.c PyNumber_Long PyId___trunc__ variable _Py_IDENTIFIER(__trunc__) -Python/Python-ast.c - PyId_type variable _Py_IDENTIFIER(type) -Python/Python-ast.c - PyId_type_comment variable _Py_IDENTIFIER(type_comment) -Python/Python-ast.c - PyId_type_ignores variable _Py_IDENTIFIER(type_ignores) -Python/errors.c _PyErr_WriteUnraisableMsg PyId_unraisablehook variable _Py_IDENTIFIER(unraisablehook) -Objects/dictobject.c dictviews_or PyId_update variable _Py_IDENTIFIER(update) -Python/Python-ast.c - PyId_upper variable _Py_IDENTIFIER(upper) -Python/Python-ast.c - PyId_value variable _Py_IDENTIFIER(value) -Python/Python-ast.c - PyId_values variable _Py_IDENTIFIER(values) -Objects/abstract.c PyMapping_Values PyId_values variable _Py_IDENTIFIER(values) -Objects/descrobject.c mappingproxy_values PyId_values variable _Py_IDENTIFIER(values) -Python/Python-ast.c - PyId_vararg variable _Py_IDENTIFIER(vararg) -Python/_warnings.c already_warned PyId_version variable _Py_IDENTIFIER(version) -Python/_warnings.c call_show_warning PyId_WarningMessage variable _Py_IDENTIFIER(WarningMessage) -Python/_warnings.c setup_context PyId___warningregistry__ variable _Py_IDENTIFIER(__warningregistry__) -Python/_warnings.c get_warnings_attr PyId_warnings variable _Py_IDENTIFIER(warnings) -Python/sysmodule.c - PyId_warnoptions variable _Py_IDENTIFIER(warnoptions) -Python/_warnings.c _PyErr_WarnUnawaitedCoroutine PyId__warn_unawaited_coroutine variable _Py_IDENTIFIER(_warn_unawaited_coroutine) -Modules/_io/bufferedio.c - PyId_writable variable _Py_IDENTIFIER(writable) -Modules/_io/textio.c - PyId_writable variable _Py_IDENTIFIER(writable) -Python/sysmodule.c - PyId_write variable _Py_IDENTIFIER(write) -Modules/_io/bufferedio.c - PyId_write variable _Py_IDENTIFIER(write) -Python/marshal.c marshal_dump_impl PyId_write variable _Py_IDENTIFIER(write) -Objects/fileobject.c PyFile_WriteObject PyId_write variable _Py_IDENTIFIER(write) -Python/sysmodule.c - PyId__xoptions variable _Py_IDENTIFIER(_xoptions) -Python/import.c _PyImportZip_Init PyId_zipimporter variable _Py_IDENTIFIER(zipimporter) -Python/initconfig.c - Py_IgnoreEnvironmentFlag variable int Py_IgnoreEnvironmentFlag -Python/dynload_shlib.c - _PyImport_DynLoadFiletab variable const char *_PyImport_DynLoadFiletab[] -Python/frozen.c - PyImport_FrozenModules variable const struct _frozen * PyImport_FrozenModules -Modules/config.c - _PyImport_Inittab variable struct _inittab _PyImport_Inittab[] -Python/import.c - PyImport_Inittab variable struct _inittab * PyImport_Inittab -Modules/_io/textio.c - PyIncrementalNewlineDecoder_Type variable PyTypeObject PyIncrementalNewlineDecoder_Type -Python/initconfig.c - Py_InspectFlag variable int Py_InspectFlag -Objects/classobject.c - PyInstanceMethod_Type variable PyTypeObject PyInstanceMethod_Type -Python/initconfig.c - Py_InteractiveFlag variable int Py_InteractiveFlag -Objects/interpreteridobject.c - _PyInterpreterID_Type variable PyTypeObject _PyInterpreterID_Type -Modules/_io/iobase.c - PyIOBase_Type variable PyTypeObject PyIOBase_Type -Modules/_io/_iomodule.c - _PyIO_empty_bytes variable PyObject *_PyIO_empty_bytes -Modules/_io/_iomodule.c - _PyIO_empty_str variable PyObject *_PyIO_empty_str -Modules/_io/_iomodule.c - _PyIO_Module variable struct PyModuleDef _PyIO_Module -Modules/_io/_iomodule.c - _PyIO_str_close variable PyObject *_PyIO_str_close -Modules/_io/_iomodule.c - _PyIO_str_closed variable PyObject *_PyIO_str_closed -Modules/_io/_iomodule.c - _PyIO_str_decode variable PyObject *_PyIO_str_decode -Modules/_io/_iomodule.c - _PyIO_str_encode variable PyObject *_PyIO_str_encode -Modules/_io/_iomodule.c - _PyIO_str_fileno variable PyObject *_PyIO_str_fileno -Modules/_io/_iomodule.c - _PyIO_str_flush variable PyObject *_PyIO_str_flush -Modules/_io/_iomodule.c - _PyIO_str_getstate variable PyObject *_PyIO_str_getstate -Modules/_io/_iomodule.c - _PyIO_str_isatty variable PyObject *_PyIO_str_isatty -Modules/_io/_iomodule.c - _PyIO_str_newlines variable PyObject *_PyIO_str_newlines -Modules/_io/_iomodule.c - _PyIO_str_nl variable PyObject *_PyIO_str_nl -Modules/_io/_iomodule.c - _PyIO_str_peek variable PyObject *_PyIO_str_peek -Modules/_io/_iomodule.c - _PyIO_str_read variable PyObject *_PyIO_str_read -Modules/_io/_iomodule.c - _PyIO_str_read1 variable PyObject *_PyIO_str_read1 -Modules/_io/_iomodule.c - _PyIO_str_readable variable PyObject *_PyIO_str_readable -Modules/_io/_iomodule.c - _PyIO_str_readall variable PyObject *_PyIO_str_readall -Modules/_io/_iomodule.c - _PyIO_str_readinto variable PyObject *_PyIO_str_readinto -Modules/_io/_iomodule.c - _PyIO_str_readline variable PyObject *_PyIO_str_readline -Modules/_io/_iomodule.c - _PyIO_str_reset variable PyObject *_PyIO_str_reset -Modules/_io/_iomodule.c - _PyIO_str_seek variable PyObject *_PyIO_str_seek -Modules/_io/_iomodule.c - _PyIO_str_seekable variable PyObject *_PyIO_str_seekable -Modules/_io/_iomodule.c - _PyIO_str_setstate variable PyObject *_PyIO_str_setstate -Modules/_io/_iomodule.c - _PyIO_str_tell variable PyObject *_PyIO_str_tell -Modules/_io/_iomodule.c - _PyIO_str_truncate variable PyObject *_PyIO_str_truncate -Modules/_io/_iomodule.c - _PyIO_str_writable variable PyObject *_PyIO_str_writable -Modules/_io/_iomodule.c - _PyIO_str_write variable PyObject *_PyIO_str_write -Python/initconfig.c - Py_IsolatedFlag variable int Py_IsolatedFlag -Objects/listobject.c - PyListIter_Type variable PyTypeObject PyListIter_Type -Objects/listobject.c - PyListRevIter_Type variable PyTypeObject PyListRevIter_Type -Objects/listobject.c - PyList_Type variable PyTypeObject PyList_Type -Modules/_localemodule.c - PyLocale_Methods variable static struct PyMethodDef PyLocale_Methods[] -Objects/longobject.c - _PyLong_DigitValue variable unsigned char _PyLong_DigitValue[256] -Objects/longobject.c - _PyLong_One variable PyObject *_PyLong_One -Objects/rangeobject.c - PyLongRangeIter_Type variable PyTypeObject PyLongRangeIter_Type -Objects/longobject.c - PyLong_Type variable PyTypeObject PyLong_Type -Objects/longobject.c - _PyLong_Zero variable PyObject *_PyLong_Zero -Objects/memoryobject.c - _PyManagedBuffer_Type variable PyTypeObject _PyManagedBuffer_Type -Python/bltinmodule.c - PyMap_Type variable PyTypeObject PyMap_Type -Objects/obmalloc.c - _PyMem variable static PyMemAllocatorEx _PyMem -Objects/descrobject.c - PyMemberDescr_Type variable PyTypeObject PyMemberDescr_Type -Objects/obmalloc.c - _PyMem_Debug variable static struct { debug_alloc_api_t raw; debug_alloc_api_t mem; debug_alloc_api_t obj; } _PyMem_Debug -Objects/memoryobject.c - PyMemoryView_Type variable PyTypeObject PyMemoryView_Type -Objects/obmalloc.c - _PyMem_Raw variable static PyMemAllocatorEx _PyMem_Raw -Objects/descrobject.c - PyMethodDescr_Type variable PyTypeObject PyMethodDescr_Type -Objects/classobject.c - PyMethod_Type variable PyTypeObject PyMethod_Type -Objects/descrobject.c - _PyMethodWrapper_Type variable PyTypeObject _PyMethodWrapper_Type -Objects/moduleobject.c - PyModuleDef_Type variable PyTypeObject PyModuleDef_Type -Objects/moduleobject.c - PyModule_Type variable PyTypeObject PyModule_Type -Objects/namespaceobject.c - _PyNamespace_Type variable PyTypeObject _PyNamespace_Type -Objects/object.c - _Py_NoneStruct variable PyObject _Py_NoneStruct -Objects/object.c - _PyNone_Type variable PyTypeObject _PyNone_Type -Python/initconfig.c - Py_NoSiteFlag variable int Py_NoSiteFlag -Objects/object.c - _Py_NotImplementedStruct variable PyObject _Py_NotImplementedStruct -Objects/object.c - _PyNotImplemented_Type variable PyTypeObject _PyNotImplemented_Type -Python/initconfig.c - Py_NoUserSiteDirectory variable int Py_NoUserSiteDirectory -Objects/bytesobject.c - _Py_null_strings variable Py_ssize_t _Py_null_strings -Objects/obmalloc.c - _PyObject variable static PyMemAllocatorEx _PyObject -Objects/obmalloc.c - _PyObject_Arena variable static PyObjectArenaAllocator _PyObject_Arena -Objects/odictobject.c - PyODictItems_Type variable PyTypeObject PyODictItems_Type -Objects/odictobject.c - PyODictIter_Type variable PyTypeObject PyODictIter_Type -Objects/odictobject.c - PyODictKeys_Type variable PyTypeObject PyODictKeys_Type -Objects/odictobject.c - PyODict_Type variable PyTypeObject PyODict_Type -Objects/odictobject.c - PyODictValues_Type variable PyTypeObject PyODictValues_Type -Python/fileutils.c - _Py_open_cloexec_works variable int _Py_open_cloexec_works -Objects/bytesobject.c - _Py_one_strings variable Py_ssize_t _Py_one_strings -Python/initconfig.c - Py_OptimizeFlag variable int Py_OptimizeFlag -Parser/myreadline.c - PyOS_InputHook variable int (*PyOS_InputHook)(void) -Python/pylifecycle.c - _PyOS_mystrnicmp_hack variable int (*_PyOS_mystrnicmp_hack)(const char *, const char *, Py_ssize_t) -Python/getopt.c - _PyOS_optarg variable const wchar_t *_PyOS_optarg -Python/getopt.c - _PyOS_opterr variable int _PyOS_opterr -Python/getopt.c - _PyOS_optind variable Py_ssize_t _PyOS_optind -Parser/myreadline.c - PyOS_ReadlineFunctionPointer variable char *(*PyOS_ReadlineFunctionPointer)(FILE *, FILE *, const char *) -Parser/myreadline.c - _PyOS_ReadlineLock variable static PyThread_type_lock _PyOS_ReadlineLock -Parser/myreadline.c - _PyOS_ReadlineTState variable PyThreadState* _PyOS_ReadlineTState -Python/modsupport.c - _Py_PackageContext variable const char *_Py_PackageContext -Python/graminit.c - _PyParser_Grammar variable grammar _PyParser_Grammar -Python/pathconfig.c - _Py_path_config variable _PyPathConfig _Py_path_config -Objects/picklebufobject.c - PyPickleBuffer_Type variable PyTypeObject PyPickleBuffer_Type -Objects/descrobject.c - PyProperty_Type variable PyTypeObject PyProperty_Type -Python/initconfig.c - Py_QuietFlag variable int Py_QuietFlag -Objects.longobject.c - _Py_quick_int_allocs variable Py_ssize_t _Py_quick_int_allocs -Objects.longobject.c - _Py_quick_new_int_allocs variable Py_ssize_t _Py_quick_new_int_allocs -Objects/rangeobject.c - PyRangeIter_Type variable PyTypeObject PyRangeIter_Type -Objects/rangeobject.c - PyRange_Type variable PyTypeObject PyRange_Type -Modules/_io/iobase.c - PyRawIOBase_Type variable PyTypeObject PyRawIOBase_Type -Objects/object.c - _Py_RefTotal variable Py_ssize_t _Py_RefTotal -Objects/enumobject.c - PyReversed_Type variable PyTypeObject PyReversed_Type -Python/pylifecycle.c - _PyRuntime variable _PyRuntimeState _PyRuntime -Objects/iterobject.c - PySeqIter_Type variable PyTypeObject PySeqIter_Type -Objects/setobject.c - _PySet_Dummy variable PyObject * _PySet_Dummy -Objects/setobject.c - _PySetDummy_Type variable static PyTypeObject _PySetDummy_Type -Objects/setobject.c - PySetIter_Type variable PyTypeObject PySetIter_Type -Objects/setobject.c - PySet_Type variable PyTypeObject PySet_Type -Objects/sliceobject.c - PySlice_Type variable PyTypeObject PySlice_Type -Python/initconfig.c - _Py_StandardStreamEncoding variable static char *_Py_StandardStreamEncoding -Python/initconfig.c - _Py_StandardStreamErrors variable static char *_Py_StandardStreamErrors -Objects/funcobject.c - PyStaticMethod_Type variable PyTypeObject PyStaticMethod_Type -Objects/fileobject.c - PyStdPrinter_Type variable PyTypeObject PyStdPrinter_Type -Python/symtable.c - PySTEntry_Type variable PyTypeObject PySTEntry_Type -Modules/_io/stringio.c - PyStringIO_Type variable PyTypeObject PyStringIO_Type -Objects/structseq.c - PyStructSequence_UnnamedField variable char *PyStructSequence_UnnamedField -Objects/typeobject.c - PySuper_Type variable PyTypeObject PySuper_Type -Objects/object.c - _Py_SwappedOp variable int _Py_SwappedOp[] -Python/sysmodule.c - _PySys_ImplCacheTag variable const char *_PySys_ImplCacheTag -Python/sysmodule.c - _PySys_ImplName variable const char *_PySys_ImplName -Modules/_io/textio.c - PyTextIOBase_Type variable PyTypeObject PyTextIOBase_Type -Modules/_io/textio.c - PyTextIOWrapper_Type variable PyTypeObject PyTextIOWrapper_Type -Python/traceback.c - PyTraceBack_Type variable PyTypeObject PyTraceBack_Type -Objects/obmalloc.c - _Py_tracemalloc_config variable struct _PyTraceMalloc_Config _Py_tracemalloc_config -Objects/boolobject.c - _Py_TrueStruct variable static struct _longobject _Py_TrueStruct -Objects/tupleobject.c - PyTupleIter_Type variable PyTypeObject PyTupleIter_Type -Objects/tupleobject.c - PyTuple_Type variable PyTypeObject PyTuple_Type -Objects/tupleobject.c - _Py_tuple_zero_allocs variable Py_ssize_t _Py_tuple_zero_allocs -Objects/typeobject.c - PyType_Type variable PyTypeObject PyType_Type -Python/initconfig.c - Py_UnbufferedStdioFlag variable int Py_UnbufferedStdioFlag -Python/pylifecycle.c - _Py_UnhandledKeyboardInterrupt variable int _Py_UnhandledKeyboardInterrupt -Objects/unicodeobject.c - PyUnicodeIter_Type variable PyTypeObject PyUnicodeIter_Type -Objects/unicodeobject.c - PyUnicode_Type variable PyTypeObject PyUnicode_Type -Python/initconfig.c - Py_UTF8Mode variable int Py_UTF8Mode -Python/initconfig.c - Py_VerboseFlag variable int Py_VerboseFlag -Objects/weakrefobject.c - _PyWeakref_CallableProxyType variable PyTypeObject _PyWeakref_CallableProxyType -Objects/weakrefobject.c - _PyWeakref_ProxyType variable PyTypeObject _PyWeakref_ProxyType -Objects/weakrefobject.c - _PyWeakref_RefType variable PyTypeObject _PyWeakref_RefType -Objects/weakrefobject.c - _PyWeakref_RefType variable PyTypeObject _PyWeakref_RefType -Objects/descrobject.c - PyWrapperDescr_Type variable PyTypeObject PyWrapperDescr_Type -Python/bltinmodule.c - PyZip_Type variable PyTypeObject PyZip_Type -Python/Python-ast.c - Raise_fields variable static const char *Raise_fields[] -Python/Python-ast.c - Raise_type variable static PyTypeObject *Raise_type -Objects/rangeobject.c - range_as_mapping variable static PyMappingMethods range_as_mapping -Objects/rangeobject.c - range_as_number variable static PyNumberMethods range_as_number -Objects/rangeobject.c - range_as_sequence variable static PySequenceMethods range_as_sequence -Objects/rangeobject.c - rangeiter_methods variable static PyMethodDef rangeiter_methods -Objects/rangeobject.c - range_members variable static PyMemberDef range_members[] -Objects/rangeobject.c - range_methods variable static PyMethodDef range_methods -Modules/_io/iobase.c - rawiobase_methods variable static PyMethodDef rawiobase_methods -Python/pylifecycle.c fatal_error reentrant variable static int reentrant -Modules/faulthandler.c faulthandler_dump_traceback reentrant variable static volatile int reentrant -Modules/itertoolsmodule.c - repeat_methods variable static PyMethodDef repeat_methods -Modules/itertoolsmodule.c - repeat_type variable static PyTypeObject repeat_type -Python/Python-ast.c - Return_fields variable static const char *Return_fields[] -Python/compile.c compiler_visit_annotations return_str variable static identifier return_str -Python/Python-ast.c - Return_type variable static PyTypeObject *Return_type -Objects/enumobject.c - reversediter_methods variable static PyMethodDef reversediter_methods -Modules/_threadmodule.c - rlock_methods variable static PyMethodDef rlock_methods -Modules/_threadmodule.c - RLocktype variable static PyTypeObject RLocktype -Objects/typeobject.c slot_nb_add rop_id variable _Py_static_string(op_id, OPSTR) -Objects/typeobject.c slot_nb_subtract rop_id variable _Py_static_string(op_id, OPSTR) -Objects/typeobject.c slot_nb_multiply rop_id variable _Py_static_string(op_id, OPSTR) -Objects/typeobject.c slot_nb_matrix_multiply rop_id variable _Py_static_string(op_id, OPSTR) -Objects/typeobject.c slot_nb_remainder rop_id variable _Py_static_string(op_id, OPSTR) -Objects/typeobject.c slot_nb_divmod rop_id variable _Py_static_string(op_id, OPSTR) -Objects/typeobject.c slot_nb_power_binary rop_id variable _Py_static_string(op_id, OPSTR) -Objects/typeobject.c slot_nb_lshift rop_id variable _Py_static_string(op_id, OPSTR) -Objects/typeobject.c slot_nb_rshift rop_id variable _Py_static_string(op_id, OPSTR) -Objects/typeobject.c slot_nb_and rop_id variable _Py_static_string(op_id, OPSTR) -Objects/typeobject.c slot_nb_xor rop_id variable _Py_static_string(op_id, OPSTR) -Objects/typeobject.c slot_nb_or rop_id variable _Py_static_string(op_id, OPSTR) -Objects/typeobject.c slot_nb_floor_divide rop_id variable _Py_static_string(op_id, OPSTR) -Objects/typeobject.c slot_nb_true_divide rop_id variable _Py_static_string(op_id, OPSTR) -Python/Python-ast.c - RShift_singleton variable static PyObject *RShift_singleton -Python/Python-ast.c - RShift_type variable static PyTypeObject *RShift_type -Python/pylifecycle.c - runtime_initialized variable static int runtime_initialized -Modules/posixmodule.c - ScandirIterator_methods variable static PyMethodDef ScandirIterator_methods -Modules/posixmodule.c - ScandirIteratorType variable static PyTypeObject ScandirIteratorType -Modules/_sre.c - scanner_members variable static PyMemberDef scanner_members[] -Modules/_sre.c - scanner_methods variable static PyMethodDef scanner_methods -Modules/_sre.c - Scanner_Type variable static PyTypeObject Scanner_Type -Modules/posixmodule.c - sched_param_desc variable static PyStructSequence_Desc sched_param_desc -Modules/posixmodule.c - sched_param_fields variable static PyStructSequence_Field sched_param_fields[] -Modules/posixmodule.c - SchedParamType variable static PyTypeObject* SchedParamType -Objects/iterobject.c - seqiter_methods variable static PyMethodDef seqiter_methods -Objects/setobject.c - set_as_number variable static PyNumberMethods set_as_number -Objects/setobject.c - set_as_sequence variable static PySequenceMethods set_as_sequence -Python/symtable.c - setcomp variable static identifier setcomp -Python/Python-ast.c - SetComp_fields variable static const char *SetComp_fields[] -Python/Python-ast.c - SetComp_type variable static PyTypeObject *SetComp_type -Python/Python-ast.c - Set_fields variable static const char *Set_fields[] -Objects/setobject.c - setiter_methods variable static PyMethodDef setiter_methods -Objects/setobject.c - set_methods variable static PyMethodDef set_methods -Python/Python-ast.c - Set_type variable static PyTypeObject *Set_type -Modules/signalmodule.c - SiginfoType variable static PyTypeObject SiginfoType -Modules/signalmodule.c - signal_methods variable static PyMethodDef signal_methods -Modules/signalmodule.c - signalmodule variable static struct PyModuleDef signalmodule -Python/import.c PyImport_Import silly_list variable static PyObject *silly_list -Objects/sliceobject.c - slice_cache variable static PySliceObject *slice_cache -Python/Python-ast.c - Slice_fields variable static const char *Slice_fields[] -Objects/sliceobject.c - slice_members variable static PyMemberDef slice_members[] -Objects/sliceobject.c - slice_methods variable static PyMethodDef slice_methods -Python/Python-ast.c - slice_type variable static PyTypeObject *slice_type -Python/Python-ast.c - Slice_type variable static PyTypeObject *Slice_type -Objects/typeobject.c - slotdefs variable static slotdef slotdefs[] -Objects/typeobject.c - slotdefs_initialized variable static int slotdefs_initialized -Objects/longobject.c - small_ints variable static PyLongObject small_ints[NSMALLNEGINTS + NSMALLPOSINTS] -Objects/funcobject.c - sm_getsetlist variable static PyGetSetDef sm_getsetlist[] -Objects/funcobject.c - sm_memberlist variable static PyMemberDef sm_memberlist[] -Modules/xxsubtype.c - spamdict_members variable static PyMemberDef spamdict_members[] -Modules/xxsubtype.c - spamdict_methods variable static PyMethodDef spamdict_methods -Modules/xxsubtype.c - spamdict_type variable static PyTypeObject spamdict_type -Modules/xxsubtype.c - spamlist_getsets variable static PyGetSetDef spamlist_getsets[] -Modules/xxsubtype.c - spamlist_methods variable static PyMethodDef spamlist_methods -Modules/xxsubtype.c - spamlist_type variable static PyTypeObject spamlist_type -Modules/_sre.c - sremodule variable static struct PyModuleDef sremodule -Modules/faulthandler.c - stack variable static stack_t stack -Modules/itertoolsmodule.c - starmap_methods variable static PyMethodDef starmap_methods -Modules/itertoolsmodule.c - starmap_type variable static PyTypeObject starmap_type -Python/Python-ast.c - Starred_fields variable static const char *Starred_fields[] -Python/Python-ast.c - Starred_type variable static PyTypeObject *Starred_type -Python/graminit.c - states_0 variable static state states_0[3] -Python/graminit.c - states_1 variable static state states_1[2] -Python/graminit.c - states_10 variable static state states_10[4] -Python/graminit.c - states_11 variable static state states_11[34] -Python/graminit.c - states_12 variable static state states_12[2] -Python/graminit.c - states_13 variable static state states_13[2] -Python/graminit.c - states_14 variable static state states_14[4] -Python/graminit.c - states_15 variable static state states_15[2] -Python/graminit.c - states_16 variable static state states_16[6] -Python/graminit.c - states_17 variable static state states_17[5] -Python/graminit.c - states_18 variable static state states_18[3] -Python/graminit.c - states_19 variable static state states_19[2] -Python/graminit.c - states_2 variable static state states_2[3] -Python/graminit.c - states_20 variable static state states_20[3] -Python/graminit.c - states_21 variable static state states_21[2] -Python/graminit.c - states_22 variable static state states_22[2] -Python/graminit.c - states_23 variable static state states_23[2] -Python/graminit.c - states_24 variable static state states_24[2] -Python/graminit.c - states_25 variable static state states_25[3] -Python/graminit.c - states_26 variable static state states_26[2] -Python/graminit.c - states_27 variable static state states_27[5] -Python/graminit.c - states_28 variable static state states_28[2] -Python/graminit.c - states_29 variable static state states_29[3] -Python/graminit.c - states_3 variable static state states_3[7] -Python/graminit.c - states_30 variable static state states_30[8] -Python/graminit.c - states_31 variable static state states_31[4] -Python/graminit.c - states_32 variable static state states_32[4] -Python/graminit.c - states_33 variable static state states_33[3] -Python/graminit.c - states_34 variable static state states_34[2] -Python/graminit.c - states_35 variable static state states_35[2] -Python/graminit.c - states_36 variable static state states_36[3] -Python/graminit.c - states_37 variable static state states_37[3] -Python/graminit.c - states_38 variable static state states_38[5] -Python/graminit.c - states_39 variable static state states_39[2] -Python/graminit.c - states_4 variable static state states_4[2] -Python/graminit.c - states_40 variable static state states_40[3] -Python/graminit.c - states_41 variable static state states_41[8] -Python/graminit.c - states_42 variable static state states_42[8] -Python/graminit.c - states_43 variable static state states_43[11] -Python/graminit.c - states_44 variable static state states_44[13] -Python/graminit.c - states_45 variable static state states_45[6] -Python/graminit.c - states_46 variable static state states_46[4] -Python/graminit.c - states_47 variable static state states_47[5] -Python/graminit.c - states_48 variable static state states_48[5] -Python/graminit.c - states_49 variable static state states_49[4] -Python/graminit.c - states_5 variable static state states_5[3] -Python/graminit.c - states_50 variable static state states_50[6] -Python/graminit.c - states_51 variable static state states_51[2] -Python/graminit.c - states_52 variable static state states_52[5] -Python/graminit.c - states_53 variable static state states_53[5] -Python/graminit.c - states_54 variable static state states_54[2] -Python/graminit.c - states_55 variable static state states_55[2] -Python/graminit.c - states_56 variable static state states_56[3] -Python/graminit.c - states_57 variable static state states_57[2] -Python/graminit.c - states_58 variable static state states_58[4] -Python/graminit.c - states_59 variable static state states_59[3] -Python/graminit.c - states_6 variable static state states_6[3] -Python/graminit.c - states_60 variable static state states_60[2] -Python/graminit.c - states_61 variable static state states_61[2] -Python/graminit.c - states_62 variable static state states_62[2] -Python/graminit.c - states_63 variable static state states_63[2] -Python/graminit.c - states_64 variable static state states_64[2] -Python/graminit.c - states_65 variable static state states_65[2] -Python/graminit.c - states_66 variable static state states_66[3] -Python/graminit.c - states_67 variable static state states_67[4] -Python/graminit.c - states_68 variable static state states_68[3] -Python/graminit.c - states_69 variable static state states_69[9] -Python/graminit.c - states_7 variable static state states_7[9] -Python/graminit.c - states_70 variable static state states_70[5] -Python/graminit.c - states_71 variable static state states_71[7] -Python/graminit.c - states_72 variable static state states_72[3] -Python/graminit.c - states_73 variable static state states_73[5] -Python/graminit.c - states_74 variable static state states_74[3] -Python/graminit.c - states_75 variable static state states_75[3] -Python/graminit.c - states_76 variable static state states_76[3] -Python/graminit.c - states_77 variable static state states_77[14] -Python/graminit.c - states_78 variable static state states_78[8] -Python/graminit.c - states_79 variable static state states_79[3] -Python/graminit.c - states_8 variable static state states_8[4] -Python/graminit.c - states_80 variable static state states_80[4] -Python/graminit.c - states_81 variable static state states_81[2] -Python/graminit.c - states_82 variable static state states_82[6] -Python/graminit.c - states_83 variable static state states_83[3] -Python/graminit.c - states_84 variable static state states_84[4] -Python/graminit.c - states_85 variable static state states_85[2] -Python/graminit.c - states_86 variable static state states_86[3] -Python/graminit.c - states_87 variable static state states_87[3] -Python/graminit.c - states_88 variable static state states_88[7] -Python/graminit.c - states_89 variable static state states_89[3] -Python/graminit.c - states_9 variable static state states_9[42] -Python/graminit.c - states_90 variable static state states_90[6] -Python/graminit.c - states_91 variable static state states_91[11] -Python/getargs.c - static_arg_parsers variable static struct _PyArg_Parser *static_arg_parsers -Objects/unicodeobject.c - static_strings variable static _Py_Identifier *static_strings -Modules/_stat.c - stat_methods variable static PyMethodDef stat_methods -Modules/_stat.c - statmodule variable static struct PyModuleDef statmodule -Modules/posixmodule.c - stat_result_desc variable static PyStructSequence_Desc stat_result_desc -Modules/posixmodule.c - stat_result_fields variable static PyStructSequence_Field stat_result_fields[] -Modules/posixmodule.c - StatResultType variable static PyTypeObject* StatResultType -Modules/posixmodule.c - statvfs_result_desc variable static PyStructSequence_Desc statvfs_result_desc -Modules/posixmodule.c - statvfs_result_fields variable static PyStructSequence_Field statvfs_result_fields[] -Modules/posixmodule.c - StatVFSResultType variable static PyTypeObject* StatVFSResultType -Objects/fileobject.c - stdprinter_getsetlist variable static PyGetSetDef stdprinter_getsetlist[] -Objects/fileobject.c - stdprinter_methods variable static PyMethodDef stdprinter_methods -Python/symtable.c - ste_memberlist variable static PyMemberDef ste_memberlist[] -Python/Python-ast.c - stmt_attributes variable static const char *stmt_attributes[] -Python/Python-ast.c - stmt_type variable static PyTypeObject *stmt_type -Objects/exceptions.c - StopIteration_members variable static PyMemberDef StopIteration_members[] -Python/Python-ast.c - Store_singleton variable static PyObject *Store_singleton -Python/Python-ast.c - Store_type variable static PyTypeObject *Store_type -Python/ast_unparse.c - _str_close_br variable static PyObject *_str_close_br -Python/ast_unparse.c - _str_dbl_close_br variable static PyObject *_str_dbl_close_br -Python/ast_unparse.c - _str_dbl_open_br variable static PyObject *_str_dbl_open_br -Modules/_threadmodule.c - str_dict variable static PyObject *str_dict -Modules/_io/stringio.c - stringio_getset variable static PyGetSetDef stringio_getset[] -Modules/_io/stringio.c - stringio_methods variable static PyMethodDef stringio_methods -Objects/unicodeobject.c - _string_methods variable static PyMethodDef _string_methods -Objects/unicodeobject.c - _string_module variable static struct PyModuleDef _string_module -Objects/bytesobject.c - striter_methods variable static PyMethodDef striter_methods -Python/ast_unparse.c - _str_open_br variable static PyObject *_str_open_br -Modules/pwdmodule.c - StructPwdType variable static PyTypeObject StructPwdType -Modules/pwdmodule.c - struct_pwd_type_desc variable static PyStructSequence_Desc struct_pwd_type_desc -Modules/pwdmodule.c - struct_pwd_type_fields variable static PyStructSequence_Field struct_pwd_type_fields[] -Modules/posixmodule.c wait_helper struct_rusage variable static PyObject *struct_rusage -Objects/structseq.c - structseq_methods variable static PyMethodDef structseq_methods -Modules/posixmodule.c - structseq_new variable static newfunc structseq_new -Modules/signalmodule.c - struct_siginfo_desc variable static PyStructSequence_Desc struct_siginfo_desc -Modules/signalmodule.c - struct_siginfo_fields variable static PyStructSequence_Field struct_siginfo_fields[] -Modules/timemodule.c - StructTimeType variable static PyTypeObject StructTimeType -Modules/timemodule.c - struct_time_type_desc variable static PyStructSequence_Desc struct_time_type_desc -Modules/timemodule.c - struct_time_type_fields variable static PyStructSequence_Field struct_time_type_fields[] -Python/Python-ast.c - Subscript_fields variable static const char *Subscript_fields[] -Python/Python-ast.c - Subscript_type variable static PyTypeObject *Subscript_type -Python/Python-ast.c - Sub_singleton variable static PyObject *Sub_singleton -Python/Python-ast.c - Sub_type variable static PyTypeObject *Sub_type -Objects/typeobject.c - subtype_getsets_dict_only variable static PyGetSetDef subtype_getsets_dict_only[] -Objects/typeobject.c - subtype_getsets_full variable static PyGetSetDef subtype_getsets_full[] -Objects/typeobject.c - subtype_getsets_weakref_only variable static PyGetSetDef subtype_getsets_weakref_only[] -Python/Python-ast.c - Suite_fields variable static const char *Suite_fields[] -Python/Python-ast.c - Suite_type variable static PyTypeObject *Suite_type -Objects/typeobject.c - super_members variable static PyMemberDef super_members[] -Modules/symtablemodule.c - symtable_methods variable static PyMethodDef symtable_methods -Modules/symtablemodule.c - symtablemodule variable static struct PyModuleDef symtablemodule -Objects/exceptions.c - SyntaxError_members variable static PyMemberDef SyntaxError_members[] -Python/sysmodule.c - sys_methods variable static PyMethodDef sys_methods -Python/sysmodule.c - sysmodule variable static struct PyModuleDef sysmodule -Objects/exceptions.c - SystemExit_members variable static PyMemberDef SystemExit_members[] -Modules/_tracemalloc.c - tables_lock variable static PyThread_type_lock tables_lock -Modules/itertoolsmodule.c - takewhile_reduce_methods variable static PyMethodDef takewhile_reduce_methods -Modules/itertoolsmodule.c - takewhile_type variable static PyTypeObject takewhile_type -Python/pylifecycle.c - _TARGET_LOCALES variable static _LocaleCoercionTarget _TARGET_LOCALES[] -Python/traceback.c - tb_getsetters variable static PyGetSetDef tb_getsetters[] -Python/traceback.c - tb_memberlist variable static PyMemberDef tb_memberlist[] -Python/traceback.c - tb_methods variable static PyMethodDef tb_methods -Modules/itertoolsmodule.c - teedataobject_methods variable static PyMethodDef teedataobject_methods -Modules/itertoolsmodule.c - teedataobject_type variable static PyTypeObject teedataobject_type -Modules/itertoolsmodule.c - tee_methods variable static PyMethodDef tee_methods -Modules/itertoolsmodule.c - tee_type variable static PyTypeObject tee_type -Modules/posixmodule.c - TerminalSize_desc variable static PyStructSequence_Desc TerminalSize_desc -Modules/posixmodule.c - TerminalSize_fields variable static PyStructSequence_Field TerminalSize_fields[] -Modules/posixmodule.c - TerminalSizeType variable static PyTypeObject* TerminalSizeType -Modules/_io/textio.c - textiobase_getset variable static PyGetSetDef textiobase_getset[] -Modules/_io/textio.c - textiobase_methods variable static PyMethodDef textiobase_methods -Modules/_io/textio.c - textiowrapper_getset variable static PyGetSetDef textiowrapper_getset[] -Modules/_io/textio.c - textiowrapper_members variable static PyMemberDef textiowrapper_members[] -Modules/_io/textio.c - textiowrapper_methods variable static PyMethodDef textiowrapper_methods -Modules/faulthandler.c - thread variable static struct { PyObject *file; int fd; PY_TIMEOUT_T timeout_us; int repeat; PyInterpreterState *interp; int exit; char *header; size_t header_len; PyThread_type_lock cancel_event; PyThread_type_lock running; } thread -Python/thread.c - thread_debug variable static int thread_debug -Modules/_threadmodule.c - ThreadError variable static PyObject *ThreadError -Python/thread.c - threadinfo_desc variable static PyStructSequence_Desc threadinfo_desc -Python/thread.c - threadinfo_fields variable static PyStructSequence_Field threadinfo_fields[] -Python/thread.c - ThreadInfoType variable static PyTypeObject ThreadInfoType -Modules/_threadmodule.c - thread_methods variable static PyMethodDef thread_methods -Modules/_threadmodule.c - threadmodule variable static struct PyModuleDef threadmodule -Modules/posixmodule.c - ticks_per_second variable static long ticks_per_second -Modules/timemodule.c _PyTime_GetProcessTimeWithInfo ticks_per_second variable static long ticks_per_second -Modules/timemodule.c - time_methods variable static PyMethodDef time_methods -Modules/timemodule.c - timemodule variable static struct PyModuleDef timemodule -Modules/posixmodule.c - times_result_desc variable static PyStructSequence_Desc times_result_desc -Modules/posixmodule.c - times_result_fields variable static PyStructSequence_Field times_result_fields[] -Modules/posixmodule.c - TimesResultType variable static PyTypeObject* TimesResultType -Python/context.c - _token_missing variable static PyObject *_token_missing -Python/symtable.c - top variable static identifier top -Objects/typeobject.c - tp_new_methoddef variable static struct PyMethodDef tp_new_methoddef[] -Modules/_tracemalloc.c - tracemalloc_empty_traceback variable static traceback_t tracemalloc_empty_traceback -Modules/_tracemalloc.c - tracemalloc_filenames variable static _Py_hashtable_t *tracemalloc_filenames -Modules/_tracemalloc.c - tracemalloc_peak_traced_memory variable static size_t tracemalloc_peak_traced_memory -Modules/_tracemalloc.c - tracemalloc_reentrant_key variable static Py_tss_t tracemalloc_reentrant_key -Modules/_tracemalloc.c - tracemalloc_traceback variable static traceback_t *tracemalloc_traceback -Modules/_tracemalloc.c - tracemalloc_tracebacks variable static _Py_hashtable_t *tracemalloc_tracebacks -Modules/_tracemalloc.c - tracemalloc_traced_memory variable static size_t tracemalloc_traced_memory -Modules/_tracemalloc.c - tracemalloc_traces variable static _Py_hashtable_t *tracemalloc_traces -Objects/boolobject.c - true_str variable static PyObject *true_str -Python/Python-ast.c - Try_fields variable static const char *Try_fields[] -Python/Python-ast.c - Try_type variable static PyTypeObject *Try_type -Objects/tupleobject.c - tuple_as_mapping variable static PyMappingMethods tuple_as_mapping -Objects/tupleobject.c - tuple_as_sequence variable static PySequenceMethods tuple_as_sequence -Python/Python-ast.c - Tuple_fields variable static const char *Tuple_fields[] -Modules/_collectionsmodule.c - tuplegetter_members variable static PyMemberDef tuplegetter_members[] -Modules/_collectionsmodule.c - tuplegetter_methods variable static PyMethodDef tuplegetter_methods -Modules/_collectionsmodule.c - tuplegetter_type variable static PyTypeObject tuplegetter_type -Objects/tupleobject.c - tupleiter_methods variable static PyMethodDef tupleiter_methods -Objects/tupleobject.c - tuple_methods variable static PyMethodDef tuple_methods -Python/Python-ast.c - Tuple_type variable static PyTypeObject *Tuple_type -Objects/typeobject.c - type_getsets variable static PyGetSetDef type_getsets[] -Python/Python-ast.c - TypeIgnore_fields variable static const char *TypeIgnore_fields[] -Python/Python-ast.c - type_ignore_type variable static PyTypeObject *type_ignore_type -Python/Python-ast.c - TypeIgnore_type variable static PyTypeObject *TypeIgnore_type -Objects/typeobject.c - type_members variable static PyMemberDef type_members[] -Objects/typeobject.c - type_methods variable static PyMethodDef type_methods -Python/Python-ast.c - UAdd_singleton variable static PyObject *UAdd_singleton -Python/Python-ast.c - UAdd_type variable static PyTypeObject *UAdd_type -Objects/unicodeobject.c - ucnhash_CAPI variable static _PyUnicode_Name_CAPI *ucnhash_CAPI -Python/codecs.c - ucnhash_CAPI variable static _PyUnicode_Name_CAPI *ucnhash_CAPI -Python/ast.c - u_kind variable static PyObject *u_kind -Modules/posixmodule.c - uname_result_desc variable static PyStructSequence_Desc uname_result_desc -Modules/posixmodule.c - uname_result_fields variable static PyStructSequence_Field uname_result_fields[] -Modules/posixmodule.c - UnameResultType variable static PyTypeObject* UnameResultType -Python/Python-ast.c - UnaryOp_fields variable static const char *UnaryOp_fields[] -Python/Python-ast.c - unaryop_type variable static PyTypeObject *unaryop_type -Python/Python-ast.c - UnaryOp_type variable static PyTypeObject *UnaryOp_type -Objects/unicodeobject.c - unicode_as_mapping variable static PyMappingMethods unicode_as_mapping -Objects/unicodeobject.c - unicode_as_number variable static PyNumberMethods unicode_as_number -Objects/unicodeobject.c - unicode_as_sequence variable static PySequenceMethods unicode_as_sequence -Objects/unicodeobject.c - unicode_empty variable static PyObject *unicode_empty -Objects/exceptions.c - UnicodeError_members variable static PyMemberDef UnicodeError_members[] -Objects/unicodeobject.c - unicodeiter_methods variable static PyMethodDef unicodeiter_methods -Objects/unicodeobject.c - unicode_latin1 variable static PyObject *unicode_latin1[256] -Objects/unicodeobject.c - unicode_methods variable static PyMethodDef unicode_methods -Modules/_tracemalloc.c - unknown_filename variable static PyObject *unknown_filename -Python/errors.c - UnraisableHookArgs_desc variable static PyStructSequence_Desc UnraisableHookArgs_desc -Python/errors.c - UnraisableHookArgs_fields variable static PyStructSequence_Field UnraisableHookArgs_fields[] -Python/errors.c - UnraisableHookArgsType variable static PyTypeObject UnraisableHookArgsType -Objects/obmalloc.c - unused_arena_objects variable static struct arena_object* unused_arena_objects -Python/bootstrap_hash.c - urandom_cache variable static struct { int fd; dev_t st_dev; ino_t st_ino; } urandom_cache -Objects/obmalloc.c - usable_arenas variable static struct arena_object* usable_arenas -Objects/obmalloc.c - usedpools variable static poolp usedpools[2 * ((NB_SMALL_SIZE_CLASSES + 7) / 8) * 8] -Modules/faulthandler.c - user_signals variable static user_signal_t *user_signals -Python/Python-ast.c - USub_singleton variable static PyObject *USub_singleton -Python/Python-ast.c - USub_type variable static PyTypeObject *USub_type -Python/getversion.c Py_GetVersion version variable static char version[250] -Python/sysmodule.c - version_info_desc variable static PyStructSequence_Desc version_info_desc -Python/sysmodule.c - version_info_fields variable static PyStructSequence_Field version_info_fields[] -Python/sysmodule.c - VersionInfoType variable static PyTypeObject VersionInfoType -Modules/posixmodule.c - waitid_result_desc variable static PyStructSequence_Desc waitid_result_desc -Modules/posixmodule.c - waitid_result_fields variable static PyStructSequence_Field waitid_result_fields[] -Modules/posixmodule.c - WaitidResultType variable static PyTypeObject* WaitidResultType -Modules/signalmodule.c - wakeup variable static volatile struct { SOCKET_T fd; int warn_on_full_buffer; int use_send; } wakeup -Python/_warnings.c - warnings_functions variable static PyMethodDef warnings_functions[] -Python/_warnings.c - warningsmodule variable static struct PyModuleDef warningsmodule -Modules/_weakref.c - weakref_functions variable static PyMethodDef weakref_functions -Objects/weakrefobject.c - weakref_members variable static PyMemberDef weakref_members[] -Modules/_weakref.c - weakrefmodule variable static struct PyModuleDef weakrefmodule -Python/sysmodule.c - whatstrings variable static PyObject *whatstrings[8] -Python/Python-ast.c - While_fields variable static const char *While_fields[] -Python/Python-ast.c - While_type variable static PyTypeObject *While_type -Python/Python-ast.c - With_fields variable static const char *With_fields[] -Python/Python-ast.c - withitem_fields variable static const char *withitem_fields[] -Python/Python-ast.c - withitem_type variable static PyTypeObject *withitem_type -Python/Python-ast.c - With_type variable static PyTypeObject *With_type -Objects/descrobject.c - wrapperdescr_getset variable static PyGetSetDef wrapperdescr_getset[] -Objects/descrobject.c - wrapper_getsets variable static PyGetSetDef wrapper_getsets[] -Objects/descrobject.c - wrapper_members variable static PyMemberDef wrapper_members[] -Objects/descrobject.c - wrapper_methods variable static PyMethodDef wrapper_methods -Modules/_threadmodule.c local_new wr_callback_def variable static PyMethodDef wr_callback_def -Modules/xxsubtype.c - xxsubtype_functions variable static PyMethodDef xxsubtype_functions[] -Modules/xxsubtype.c - xxsubtypemodule variable static struct PyModuleDef xxsubtypemodule -Modules/xxsubtype.c - xxsubtype_slots variable static struct PyModuleDef_Slot xxsubtype_slots[] -Python/Python-ast.c - Yield_fields variable static const char *Yield_fields[] -Python/Python-ast.c - YieldFrom_fields variable static const char *YieldFrom_fields[] -Python/Python-ast.c - YieldFrom_type variable static PyTypeObject *YieldFrom_type -Python/Python-ast.c - Yield_type variable static PyTypeObject *Yield_type -Modules/itertoolsmodule.c - zip_longest_methods variable static PyMethodDef zip_longest_methods -Modules/itertoolsmodule.c - ziplongest_type variable static PyTypeObject ziplongest_type -Python/bltinmodule.c - zip_methods variable static PyMethodDef zip_methods From webhook-mailer at python.org Fri Oct 23 06:08:36 2020 From: webhook-mailer at python.org (zooba) Date: Fri, 23 Oct 2020 10:08:36 -0000 Subject: [Python-checkins] bpo-40592: shutil.which will not return None anymore if ; is the last char in PATHEXT (GH-20088) Message-ID: https://github.com/python/cpython/commit/da6f098188c9825f10ae60db8987056b3a54c2e8 commit: da6f098188c9825f10ae60db8987056b3a54c2e8 branch: master author: Christopher Marchfelder committer: zooba date: 2020-10-23T11:08:24+01:00 summary: bpo-40592: shutil.which will not return None anymore if ; is the last char in PATHEXT (GH-20088) shutil.which will not return None anymore for empty str in PATHEXT Empty PATHEXT will now be defaulted to _WIN_DEFAULT_PATHEXT files: A Misc/NEWS.d/next/Library/2020-05-14-16-01-34.bpo-40592.Cmk855.rst M Lib/shutil.py M Lib/test/test_shutil.py diff --git a/Lib/shutil.py b/Lib/shutil.py index a4ce2c0290bc9..223e9a8a70506 100644 --- a/Lib/shutil.py +++ b/Lib/shutil.py @@ -53,6 +53,9 @@ _USE_CP_SENDFILE = hasattr(os, "sendfile") and sys.platform.startswith("linux") _HAS_FCOPYFILE = posix and hasattr(posix, "_fcopyfile") # macOS +# CMD defaults in Windows 10 +_WIN_DEFAULT_PATHEXT = ".COM;.EXE;.BAT;.CMD;.VBS;.JS;.WS;.MSC" + __all__ = ["copyfileobj", "copyfile", "copymode", "copystat", "copy", "copy2", "copytree", "move", "rmtree", "Error", "SpecialFileError", "ExecError", "make_archive", "get_archive_formats", @@ -1415,7 +1418,9 @@ def which(cmd, mode=os.F_OK | os.X_OK, path=None): path.insert(0, curdir) # PATHEXT is necessary to check on Windows. - pathext = os.environ.get("PATHEXT", "").split(os.pathsep) + pathext_source = os.getenv("PATHEXT") or _WIN_DEFAULT_PATHEXT + pathext = [ext for ext in pathext_source.split(os.pathsep) if ext] + if use_bytes: pathext = [os.fsencode(ext) for ext in pathext] # See if the given file matches any of the expected path extensions. diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py index 06ca50af14337..890f2c7d406c2 100644 --- a/Lib/test/test_shutil.py +++ b/Lib/test/test_shutil.py @@ -1849,6 +1849,23 @@ def test_pathext(self): rv = shutil.which(program, path=self.temp_dir) self.assertEqual(rv, temp_filexyz.name) + # Issue 40592: See https://bugs.python.org/issue40592 + @unittest.skipUnless(sys.platform == "win32", 'test specific to Windows') + def test_pathext_with_empty_str(self): + ext = ".xyz" + temp_filexyz = tempfile.NamedTemporaryFile(dir=self.temp_dir, + prefix="Tmp2", suffix=ext) + self.addCleanup(temp_filexyz.close) + + # strip path and extension + program = os.path.basename(temp_filexyz.name) + program = os.path.splitext(program)[0] + + with os_helper.EnvironmentVarGuard() as env: + env['PATHEXT'] = f"{ext};" # note the ; + rv = shutil.which(program, path=self.temp_dir) + self.assertEqual(rv, temp_filexyz.name) + class TestWhichBytes(TestWhich): def setUp(self): diff --git a/Misc/NEWS.d/next/Library/2020-05-14-16-01-34.bpo-40592.Cmk855.rst b/Misc/NEWS.d/next/Library/2020-05-14-16-01-34.bpo-40592.Cmk855.rst new file mode 100644 index 0000000000000..3211a1bc345fa --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-05-14-16-01-34.bpo-40592.Cmk855.rst @@ -0,0 +1 @@ +:func:`shutil.which` now ignores empty entries in :envvar:`PATHEXT` instead of treating them as a match. From webhook-mailer at python.org Fri Oct 23 08:06:04 2020 From: webhook-mailer at python.org (markshannon) Date: Fri, 23 Oct 2020 12:06:04 -0000 Subject: [Python-checkins] Add Mark Shannon to CODEOWNERS. (#22914) Message-ID: https://github.com/python/cpython/commit/780151479f6f417838fc9e797864cf0b807604a9 commit: 780151479f6f417838fc9e797864cf0b807604a9 branch: master author: Mark Shannon committer: markshannon date: 2020-10-23T13:05:48+01:00 summary: Add Mark Shannon to CODEOWNERS. (#22914) files: M .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 5b8662cfe0921..8c76f54af5504 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -9,10 +9,16 @@ # Core **/*context* @1st1 -**/*genobject* @1st1 +**/*genobject* @1st1 @markshannon **/*hamt* @1st1 Objects/set* @rhettinger -Objects/dict* @methane +Objects/dict* @methane @markshannon +Objects/type* @markshannon +Objects/codeobject.c @markshannon +Objects/frameobject.c @markshannon +Objects/call.c @markshannon +Python/ceval.c @markshannon +Python/compile.c @markshannon # Hashing **/*hashlib* @python/crypto-team @tiran From webhook-mailer at python.org Fri Oct 23 11:24:07 2020 From: webhook-mailer at python.org (Mariatta) Date: Fri, 23 Oct 2020 15:24:07 -0000 Subject: [Python-checkins] Add GitHub sponsor info for Python (GH-22887) Message-ID: https://github.com/python/cpython/commit/7c4065d305228aa406675224d631f81964d12855 commit: 7c4065d305228aa406675224d631f81964d12855 branch: master author: Mariatta committer: Mariatta date: 2020-10-23T08:23:58-07:00 summary: Add GitHub sponsor info for Python (GH-22887) Sponsor Python on GitHub files: M .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 36c4e2771843e..b08459313accf 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1 +1,2 @@ custom: https://www.python.org/psf/donations/python-dev/ +github: [python] From webhook-mailer at python.org Fri Oct 23 15:55:48 2020 From: webhook-mailer at python.org (rhettinger) Date: Fri, 23 Oct 2020 19:55:48 -0000 Subject: [Python-checkins] Create a primer section for the descriptor howto guide (GH-22906) Message-ID: https://github.com/python/cpython/commit/8d3d7314d44d762a6fb42d079f57b6b5273473d6 commit: 8d3d7314d44d762a6fb42d079f57b6b5273473d6 branch: master author: Raymond Hettinger committer: rhettinger date: 2020-10-23T12:55:39-07:00 summary: Create a primer section for the descriptor howto guide (GH-22906) files: M Doc/glossary.rst M Doc/howto/descriptor.rst M Doc/tools/susp-ignored.csv diff --git a/Doc/glossary.rst b/Doc/glossary.rst index 4f0654b3254e4..32aa12a200f63 100644 --- a/Doc/glossary.rst +++ b/Doc/glossary.rst @@ -301,7 +301,8 @@ Glossary including functions, methods, properties, class methods, static methods, and reference to super classes. - For more information about descriptors' methods, see :ref:`descriptors`. + For more information about descriptors' methods, see :ref:`descriptors` + or the :ref:`Descriptor How To Guide `. dictionary An associative array, where arbitrary keys are mapped to values. The diff --git a/Doc/howto/descriptor.rst b/Doc/howto/descriptor.rst index b792b6c6ab77f..4a53b9e615692 100644 --- a/Doc/howto/descriptor.rst +++ b/Doc/howto/descriptor.rst @@ -1,3 +1,5 @@ +.. _descriptorhowto: + ====================== Descriptor HowTo Guide ====================== @@ -7,6 +9,415 @@ Descriptor HowTo Guide .. Contents:: + +:term:`Descriptors ` let objects customize attribute lookup, +storage, and deletion. + +This HowTo guide has three major sections: + +1) The "primer" gives a basic overview, moving gently from simple examples, + adding one feature at a time. It is a great place to start. + +2) The second section shows a complete, practical descriptor example. If you + already know the basics, start there. + +3) The third section provides a more technical tutorial that goes into the + detailed mechanics of how descriptors work. Most people don't need this + level of detail. + + +Primer +^^^^^^ + +In this primer, we start with most basic possible example and then we'll add +new capabilities one by one. + + +Simple example: A descriptor that returns a constant +---------------------------------------------------- + +The :class:`Ten` class is a descriptor that always returns the constant ``10``:: + + + class Ten: + def __get__(self, obj, objtype=None): + return 10 + +To use the descriptor, it must be stored as a class variable in another class:: + + class A: + x = 5 # Regular class attribute + y = Ten() # Descriptor + +An interactive session shows the difference between normal attribute lookup +and descriptor lookup:: + + >>> a = A() # Make an instance of class A + >>> a.x # Normal attribute lookup + 5 + >>> a.y # Descriptor lookup + 10 + +In the ``a.x`` attribute lookup, the dot operator finds the value ``5`` stored +in the class dictionary. In the ``a.y`` descriptor lookup, the dot operator +calls the descriptor's :meth:`__get__()` method. That method returns ``10``. +Note that the value ``10`` is not stored in either the class dictionary or the +instance dictionary. Instead, the value ``10`` is computed on demand. + +This example shows how a simple descriptor works, but it isn't very useful. +For retrieving constants, normal attribute lookup would be better. + +In the next section, we'll create something more useful, a dynamic lookup. + + +Dynamic lookups +--------------- + +Interesting descriptors typically run computations instead of doing lookups:: + + + import os + + class DirectorySize: + + def __get__(self, obj, objtype=None): + return len(os.listdir(obj.dirname)) + + class Directory: + + size = DirectorySize() # Descriptor + + def __init__(self, dirname): + self.dirname = dirname # Regular instance attribute + +An interactive session shows that the lookup is dynamic ? it computes +different, updated answers each time:: + + >>> g = Directory('games') + >>> s = Directory('songs') + >>> g.size # The games directory has three files + 3 + >>> os.system('touch games/newfile') # Add a fourth file to the directory + 0 + >>> g.size + 4 + >>> s.size # The songs directory has twenty files + 20 + +Besides showing how descriptors can run computations, this example also +reveals the purpose of the parameters to :meth:`__get__`. The *self* +parameter is *size*, an instance of *DirectorySize*. The *obj* parameter is +either *g* or *s*, an instance of *Directory*. It is *obj* parameter that +lets the :meth:`__get__` method learn the target directory. The *objtype* +parameter is the class *Directory*. + + +Managed attributes +------------------ + +A popular use for descriptors is managing access to instance data. The +descriptor is assigned to a public attribute in the class dictionary while the +actual data is stored as a private attribute in the instance dictionary. The +descriptor's :meth:`__get__` and :meth:`__set__` methods are triggered when +the public attribute is accessed. + +In the following example, *age* is the public attribute and *_age* is the +private attribute. When the public attribute is accessed, the descriptor logs +the lookup or update:: + + import logging + + logging.basicConfig(level=logging.INFO) + + class LoggedAgeAccess: + + def __get__(self, obj, objtype=None): + value = obj._age + logging.info('Accessing %r giving %r', 'age', value) + return value + + def __set__(self, obj, value): + logging.info('Updating %r to %r', 'age', value) + obj._age = value + + class Person: + + age = LoggedAgeAccess() # Descriptor + + def __init__(self, name, age): + self.name = name # Regular instance attribute + self.age = age # Calls the descriptor + + def birthday(self): + self.age += 1 # Calls both __get__() and __set__() + + +An interactive session shows that all access to the managed attribute *age* is +logged, but that the regular attribute *name* is not logged:: + + >>> mary = Person('Mary M', 30) # The initial age update is logged + INFO:root:Updating 'age' to 30 + >>> dave = Person('David D', 40) + INFO:root:Updating 'age' to 40 + + >>> vars(mary) # The actual data is in a private attribute + {'name': 'Mary M', '_age': 30} + >>> vars(dave) + {'name': 'David D', '_age': 40} + + >>> mary.age # Access the data and log the lookup + INFO:root:Accessing 'age' giving 30 + 30 + >>> mary.birthday() # Updates are logged as well + INFO:root:Accessing 'age' giving 30 + INFO:root:Updating 'age' to 31 + + >>> dave.name # Regular attribute lookup isn't logged + 'David D' + >>> dave.age # Only the managed attribute is logged + INFO:root:Accessing 'age' giving 40 + 40 + +One major issue with this example is the private name *_age* is hardwired in +the *LoggedAgeAccess* class. That means that each instance can only have one +logged attribute and that its name is unchangeable. In the next example, +we'll fix that problem. + + +Customized Names +---------------- + +When a class uses descriptors, it can inform each descriptor about what +variable name was used. + +In this example, the :class:`Person` class has two descriptor instances, +*name* and *age*. When the :class:`Person` class is defined, it makes a +callback to :meth:`__set_name__` in *LoggedAccess* so that the field names can +be recorded, giving each descriptor its own *public_name* and *private_name*:: + + import logging + + logging.basicConfig(level=logging.INFO) + + class LoggedAccess: + + def __set_name__(self, owner, name): + self.public_name = name + self.private_name = f'_{name}' + + def __get__(self, obj, objtype=None): + value = getattr(obj, self.private_name) + logging.info('Accessing %r giving %r', self.public_name, value) + return value + + def __set__(self, obj, value): + logging.info('Updating %r to %r', self.public_name, value) + setattr(obj, self.private_name, value) + + class Person: + + name = LoggedAccess() # First descriptor + age = LoggedAccess() # Second descriptor + + def __init__(self, name, age): + self.name = name # Calls the first descriptor + self.age = age # Calls the second descriptor + + def birthday(self): + self.age += 1 + +An interactive session shows that the :class:`Person` class has called +:meth:`__set_name__` so that the field names would be recorded. Here +we call :func:`vars` to lookup the descriptor without triggering it:: + + >>> vars(vars(Person)['name']) + {'public_name': 'name', 'private_name': '_name'} + >>> vars(vars(Person)['age']) + {'public_name': 'age', 'private_name': '_age'} + +The new class now logs access to both *name* and *age*:: + + >>> pete = Person('Peter P', 10) + INFO:root:Updating 'name' to 'Peter P' + INFO:root:Updating 'age' to 10 + >>> kate = Person('Catherine C', 20) + INFO:root:Updating 'name' to 'Catherine C' + INFO:root:Updating 'age' to 20 + +The two *Person* instances contain only the private names:: + + >>> vars(pete) + {'_name': 'Peter P', '_age': 10} + >>> vars(kate) + {'_name': 'Catherine C', '_age': 20} + + +Closing thoughts +---------------- + +A :term:`descriptor` is what we call any object that defines :meth:`__get__`, +:meth:`__set__`, or :meth:`__delete__`. + +Descriptors get invoked by the dot operator during attribute lookup. If a +descriptor is accessed indirectly with ``vars(some_class)[descriptor_name]``, +the descriptor instance is returned without invoking it. + +Descriptors only work when used as class variables. When put in instances, +they have no effect. + +The main motivation for descriptors is to provide a hook allowing objects +stored in class variables to control what happens during dotted lookup. + +Traditionally, the calling class controls what happens during lookup. +Descriptors invert that relationship and allow the data being looked-up to +have a say in the matter. + +Descriptors are used throughout the language. It is how functions turn into +bound methods. Common tools like :func:`classmethod`, :func:`staticmethod`, +:func:`property`, and :func:`functools.cached_property` are all implemented as +descriptors. + + +Complete Practical Example +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +In this example, we create a practical and powerful tool for locating +notoriously hard to find data corruption bugs. + + +Validator class +--------------- + +A validator is a descriptor for managed attribute access. Prior to storing +any data, it verifies that the new value meets various type and range +restrictions. If those restrictions aren't met, it raises an exception to +prevents data corruption at its source. + +This :class:`Validator` class is both an :term:`abstract base class` and a +managed attribute descriptor:: + + from abc import ABC, abstractmethod + + class Validator(ABC): + + def __set_name__(self, owner, name): + self.private_name = f'_{name}' + + def __get__(self, obj, objtype=None): + return getattr(obj, self.private_name) + + def __set__(self, obj, value): + self.validate(value) + setattr(obj, self.private_name, value) + + @abstractmethod + def validate(self, value): + pass + +Custom validators need to subclass from :class:`Validator` and supply a +:meth:`validate` method to test various restrictions as needed. + + +Custom validators +----------------- + +Here are three practical data validation utilities: + +1) :class:`OneOf` verifies that a value is one of a restricted set of options. + +2) :class:`Number` verifies that a value is either an :class:`int` or + :class:`float`. Optionally, it verifies that a value is between a given + minimum or maximum. + +3) :class:`String` verifies that a value is a :class:`str`. Optionally, it + validates a given minimum or maximum length. Optionally, it can test for + another predicate as well. + +:: + + class OneOf(Validator): + + def __init__(self, *options): + self.options = set(options) + + def validate(self, value): + if value not in self.options: + raise ValueError(f'Expected {value!r} to be one of {self.options!r}') + + class Number(Validator): + + def __init__(self, minvalue=None, maxvalue=None): + self.minvalue = minvalue + self.maxvalue = maxvalue + + def validate(self, value): + if not isinstance(value, (int, float)): + raise TypeError(f'Expected {value!r} to be an int or float') + if self.minvalue is not None and value < self.minvalue: + raise ValueError( + f'Expected {value!r} to be at least {self.minvalue!r}' + ) + if self.maxvalue is not None and value > self.maxvalue: + raise ValueError( + f'Expected {value!r} to be no more than {self.maxvalue!r}' + ) + + class String(Validator): + + def __init__(self, minsize=None, maxsize=None, predicate=None): + self.minsize = minsize + self.maxsize = maxsize + self.predicate = predicate + + def validate(self, value): + if not isinstance(value, str): + raise TypeError(f'Expected {value!r} to be an str') + if self.minsize is not None and len(value) < self.minsize: + raise ValueError( + f'Expected {value!r} to be no smaller than {self.minsize!r}' + ) + if self.maxsize is not None and len(value) > self.maxsize: + raise ValueError( + f'Expected {value!r} to be no bigger than {self.maxsize!r}' + ) + if self.predicate is not None and not self.predicate(value): + raise ValueError( + f'Expected {self.predicate} to be true for {value!r}' + ) + + +Practical use +------------- + +Here's how the data validators can be used in a real class:: + + class Component: + + name = String(minsize=3, maxsize=10, predicate=str.isupper) + kind = OneOf('plastic', 'metal') + quantity = Number(minvalue=0) + + def __init__(self, name, kind, quantity): + self.name = name + self.kind = kind + self.quantity = quantity + +The descriptors prevent invalid instances from being created:: + + Component('WIDGET', 'metal', 5) # Allowed. + Component('Widget', 'metal', 5) # Blocked: 'Widget' is not all uppercase + Component('WIDGET', 'metle', 5) # Blocked: 'metle' is misspelled + Component('WIDGET', 'metal', -5) # Blocked: -5 is negative + Component('WIDGET', 'metal', 'V') # Blocked: 'V' isn't a number + + +Technical Tutorial +^^^^^^^^^^^^^^^^^^ + +What follows is a more technical tutorial for the mechanics and details of how +descriptors work. + + Abstract -------- @@ -39,10 +450,10 @@ Where this occurs in the precedence chain depends on which descriptor methods were defined. Descriptors are a powerful, general purpose protocol. They are the mechanism -behind properties, methods, static methods, class methods, and :func:`super()`. -They are used throughout Python itself to implement the new style classes -introduced in version 2.2. Descriptors simplify the underlying C-code and offer -a flexible set of new tools for everyday Python programs. +behind properties, methods, static methods, class methods, and +:func:`super()`. They are used throughout Python itself. Descriptors +simplify the underlying C code and offer a flexible set of new tools for +everyday Python programs. Descriptor Protocol @@ -132,11 +543,29 @@ The implementation details are in :c:func:`super_getattro()` in The details above show that the mechanism for descriptors is embedded in the :meth:`__getattribute__()` methods for :class:`object`, :class:`type`, and :func:`super`. Classes inherit this machinery when they derive from -:class:`object` or if they have a meta-class providing similar functionality. +:class:`object` or if they have a metaclass providing similar functionality. Likewise, classes can turn-off descriptor invocation by overriding :meth:`__getattribute__()`. +Automatic Name Notification +--------------------------- + +Sometimes it is desirable for a descriptor to know what class variable name it +was assigned to. When a new class is created, the :class:`type` metaclass +scans the dictionary of the new class. If any of the entries are descriptors +and if they define :meth:`__set_name__`, that method is called with two +arguments. The *owner* is the class where the descriptor is used, the *name* +is class variable the descriptor was assigned to. + +The implementation details are in :c:func:`type_new()` and +:c:func:`set_names()` in :source:`Objects/typeobject.c`. + +Since the update logic is in :meth:`type.__new__`, notifications only take +place at the time of class creation. If descriptors are added to the class +afterwards, :meth:`__set_name__` will need to be called manually. + + Descriptor Example ------------------ @@ -154,7 +583,7 @@ descriptor is useful for monitoring just a few chosen attributes:: self.val = initval self.name = name - def __get__(self, obj, objtype): + def __get__(self, obj, objtype=None): print('Retrieving', self.name) return self.val @@ -162,11 +591,11 @@ descriptor is useful for monitoring just a few chosen attributes:: print('Updating', self.name) self.val = val - >>> class MyClass: - ... x = RevealAccess(10, 'var "x"') - ... y = 5 - ... - >>> m = MyClass() + class B: + x = RevealAccess(10, 'var "x"') + y = 5 + + >>> m = B() >>> m.x Retrieving var "x" 10 @@ -251,12 +680,13 @@ affect existing client code accessing the attribute directly. The solution is to wrap access to the value attribute in a property data descriptor:: class Cell: - . . . - def getvalue(self): + ... + + @property + def value(self): "Recalculate the cell before returning value" self.recalc() return self._value - value = property(getvalue) Functions and Methods @@ -278,42 +708,48 @@ non-data descriptors which return bound methods when they are invoked from an object. In pure Python, it works like this:: class Function: - . . . + ... + def __get__(self, obj, objtype=None): "Simulate func_descr_get() in Objects/funcobject.c" if obj is None: return self return types.MethodType(self, obj) -Running the interpreter shows how the function descriptor works in practice:: +Running the following in class in the interpreter shows how the function +descriptor works in practice:: - >>> class D: - ... def f(self, x): - ... return x - ... - >>> d = D() + class D: + def f(self, x): + return x + +Access through the class dictionary does not invoke :meth:`__get__`. Instead, +it just returns the underlying function object:: - # Access through the class dictionary does not invoke __get__. - # It just returns the underlying function object. >>> D.__dict__['f'] - # Dotted access from a class calls __get__() which just returns - # the underlying function unchanged. +Dotted access from a class calls :meth:`__get__` which just returns the +underlying function unchanged:: + >>> D.f - # The function has a __qualname__ attribute to support introspection +The function has a :term:`qualified name` attribute to support introspection:: + >>> D.f.__qualname__ 'D.f' - # Dotted access from an instance calls __get__() which returns the - # function wrapped in a bound method object +Dotted access from an instance calls :meth:`__get__` which returns a bound +method object:: + + >>> d = D() >>> d.f > - # Internally, the bound method stores the underlying function and - # the bound instance. +Internally, the bound method stores the underlying function and the bound +instance:: + >>> d.f.__func__ >>> d.f.__self__ @@ -328,20 +764,20 @@ patterns of binding functions into methods. To recap, functions have a :meth:`__get__` method so that they can be converted to a method when accessed as attributes. The non-data descriptor transforms an -``obj.f(*args)`` call into ``f(obj, *args)``. Calling ``klass.f(*args)`` +``obj.f(*args)`` call into ``f(obj, *args)``. Calling ``cls.f(*args)`` becomes ``f(*args)``. This chart summarizes the binding and its two most useful variants: +-----------------+----------------------+------------------+ | Transformation | Called from an | Called from a | - | | Object | Class | + | | object | class | +=================+======================+==================+ | function | f(obj, \*args) | f(\*args) | +-----------------+----------------------+------------------+ | staticmethod | f(\*args) | f(\*args) | +-----------------+----------------------+------------------+ - | classmethod | f(type(obj), \*args) | f(klass, \*args) | + | classmethod | f(type(obj), \*args) | f(cls, \*args) | +-----------------+----------------------+------------------+ Static methods return the underlying function without changes. Calling either @@ -365,11 +801,11 @@ It can be called either from an object or the class: ``s.erf(1.5) --> .9332`` o Since staticmethods return the underlying function with no changes, the example calls are unexciting:: - >>> class E: - ... def f(x): - ... print(x) - ... f = staticmethod(f) - ... + class E: + @staticmethod + def f(x): + print(x) + >>> E.f(3) 3 >>> E().f(3) @@ -391,32 +827,33 @@ Unlike static methods, class methods prepend the class reference to the argument list before calling the function. This format is the same for whether the caller is an object or a class:: - >>> class E: - ... def f(klass, x): - ... return klass.__name__, x - ... f = classmethod(f) - ... - >>> print(E.f(3)) - ('E', 3) - >>> print(E().f(3)) - ('E', 3) + class F: + @classmethod + def f(cls, x): + return cls.__name__, x + + >>> print(F.f(3)) + ('F', 3) + >>> print(F().f(3)) + ('F', 3) This behavior is useful whenever the function only needs to have a class -reference and does not care about any underlying data. One use for classmethods -is to create alternate class constructors. In Python 2.3, the classmethod +reference and does not care about any underlying data. One use for +classmethods is to create alternate class constructors. The classmethod :func:`dict.fromkeys` creates a new dictionary from a list of keys. The pure Python equivalent is:: class Dict: - . . . - def fromkeys(klass, iterable, value=None): + ... + + @classmethod + def fromkeys(cls, iterable, value=None): "Emulate dict_fromkeys() in Objects/dictobject.c" - d = klass() + d = cls() for key in iterable: d[key] = value return d - fromkeys = classmethod(fromkeys) Now a new dictionary of unique keys can be constructed like this:: @@ -432,10 +869,9 @@ Using the non-data descriptor protocol, a pure Python version of def __init__(self, f): self.f = f - def __get__(self, obj, klass=None): - if klass is None: - klass = type(obj) + def __get__(self, obj, cls=None): + if cls is None: + cls = type(obj) def newfunc(*args): - return self.f(klass, *args) + return self.f(cls, *args) return newfunc - diff --git a/Doc/tools/susp-ignored.csv b/Doc/tools/susp-ignored.csv index 99d6decc4ece1..b15fd32b357f0 100644 --- a/Doc/tools/susp-ignored.csv +++ b/Doc/tools/susp-ignored.csv @@ -23,6 +23,9 @@ howto/curses,,:blue,"2:green, 3:yellow, 4:blue, 5:magenta, 6:cyan, and 7:white. howto/curses,,:magenta,"2:green, 3:yellow, 4:blue, 5:magenta, 6:cyan, and 7:white. The" howto/curses,,:cyan,"2:green, 3:yellow, 4:blue, 5:magenta, 6:cyan, and 7:white. The" howto/curses,,:white,"2:green, 3:yellow, 4:blue, 5:magenta, 6:cyan, and 7:white. The" +howto/descriptor,,:root,"INFO:root" +howto/descriptor,,:Updating,"root:Updating" +howto/descriptor,,:Accessing,"root:Accessing" howto/instrumentation,,::,python$target:::function-entry howto/instrumentation,,:function,python$target:::function-entry howto/instrumentation,,::,python$target:::function-return From webhook-mailer at python.org Fri Oct 23 16:49:42 2020 From: webhook-mailer at python.org (rhettinger) Date: Fri, 23 Oct 2020 20:49:42 -0000 Subject: [Python-checkins] Create a primer section for the descriptor howto guide (GH-22906) (GH0-22918) Message-ID: https://github.com/python/cpython/commit/f8d96b98a48c97d0da8714e5e18675412774a6ef commit: f8d96b98a48c97d0da8714e5e18675412774a6ef branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: rhettinger date: 2020-10-23T13:49:32-07:00 summary: Create a primer section for the descriptor howto guide (GH-22906) (GH0-22918) files: M Doc/glossary.rst M Doc/howto/descriptor.rst M Doc/tools/susp-ignored.csv diff --git a/Doc/glossary.rst b/Doc/glossary.rst index 9fdbdb1a83f28..189d49ee0d627 100644 --- a/Doc/glossary.rst +++ b/Doc/glossary.rst @@ -301,7 +301,8 @@ Glossary including functions, methods, properties, class methods, static methods, and reference to super classes. - For more information about descriptors' methods, see :ref:`descriptors`. + For more information about descriptors' methods, see :ref:`descriptors` + or the :ref:`Descriptor How To Guide `. dictionary An associative array, where arbitrary keys are mapped to values. The diff --git a/Doc/howto/descriptor.rst b/Doc/howto/descriptor.rst index b792b6c6ab77f..4a53b9e615692 100644 --- a/Doc/howto/descriptor.rst +++ b/Doc/howto/descriptor.rst @@ -1,3 +1,5 @@ +.. _descriptorhowto: + ====================== Descriptor HowTo Guide ====================== @@ -7,6 +9,415 @@ Descriptor HowTo Guide .. Contents:: + +:term:`Descriptors ` let objects customize attribute lookup, +storage, and deletion. + +This HowTo guide has three major sections: + +1) The "primer" gives a basic overview, moving gently from simple examples, + adding one feature at a time. It is a great place to start. + +2) The second section shows a complete, practical descriptor example. If you + already know the basics, start there. + +3) The third section provides a more technical tutorial that goes into the + detailed mechanics of how descriptors work. Most people don't need this + level of detail. + + +Primer +^^^^^^ + +In this primer, we start with most basic possible example and then we'll add +new capabilities one by one. + + +Simple example: A descriptor that returns a constant +---------------------------------------------------- + +The :class:`Ten` class is a descriptor that always returns the constant ``10``:: + + + class Ten: + def __get__(self, obj, objtype=None): + return 10 + +To use the descriptor, it must be stored as a class variable in another class:: + + class A: + x = 5 # Regular class attribute + y = Ten() # Descriptor + +An interactive session shows the difference between normal attribute lookup +and descriptor lookup:: + + >>> a = A() # Make an instance of class A + >>> a.x # Normal attribute lookup + 5 + >>> a.y # Descriptor lookup + 10 + +In the ``a.x`` attribute lookup, the dot operator finds the value ``5`` stored +in the class dictionary. In the ``a.y`` descriptor lookup, the dot operator +calls the descriptor's :meth:`__get__()` method. That method returns ``10``. +Note that the value ``10`` is not stored in either the class dictionary or the +instance dictionary. Instead, the value ``10`` is computed on demand. + +This example shows how a simple descriptor works, but it isn't very useful. +For retrieving constants, normal attribute lookup would be better. + +In the next section, we'll create something more useful, a dynamic lookup. + + +Dynamic lookups +--------------- + +Interesting descriptors typically run computations instead of doing lookups:: + + + import os + + class DirectorySize: + + def __get__(self, obj, objtype=None): + return len(os.listdir(obj.dirname)) + + class Directory: + + size = DirectorySize() # Descriptor + + def __init__(self, dirname): + self.dirname = dirname # Regular instance attribute + +An interactive session shows that the lookup is dynamic ? it computes +different, updated answers each time:: + + >>> g = Directory('games') + >>> s = Directory('songs') + >>> g.size # The games directory has three files + 3 + >>> os.system('touch games/newfile') # Add a fourth file to the directory + 0 + >>> g.size + 4 + >>> s.size # The songs directory has twenty files + 20 + +Besides showing how descriptors can run computations, this example also +reveals the purpose of the parameters to :meth:`__get__`. The *self* +parameter is *size*, an instance of *DirectorySize*. The *obj* parameter is +either *g* or *s*, an instance of *Directory*. It is *obj* parameter that +lets the :meth:`__get__` method learn the target directory. The *objtype* +parameter is the class *Directory*. + + +Managed attributes +------------------ + +A popular use for descriptors is managing access to instance data. The +descriptor is assigned to a public attribute in the class dictionary while the +actual data is stored as a private attribute in the instance dictionary. The +descriptor's :meth:`__get__` and :meth:`__set__` methods are triggered when +the public attribute is accessed. + +In the following example, *age* is the public attribute and *_age* is the +private attribute. When the public attribute is accessed, the descriptor logs +the lookup or update:: + + import logging + + logging.basicConfig(level=logging.INFO) + + class LoggedAgeAccess: + + def __get__(self, obj, objtype=None): + value = obj._age + logging.info('Accessing %r giving %r', 'age', value) + return value + + def __set__(self, obj, value): + logging.info('Updating %r to %r', 'age', value) + obj._age = value + + class Person: + + age = LoggedAgeAccess() # Descriptor + + def __init__(self, name, age): + self.name = name # Regular instance attribute + self.age = age # Calls the descriptor + + def birthday(self): + self.age += 1 # Calls both __get__() and __set__() + + +An interactive session shows that all access to the managed attribute *age* is +logged, but that the regular attribute *name* is not logged:: + + >>> mary = Person('Mary M', 30) # The initial age update is logged + INFO:root:Updating 'age' to 30 + >>> dave = Person('David D', 40) + INFO:root:Updating 'age' to 40 + + >>> vars(mary) # The actual data is in a private attribute + {'name': 'Mary M', '_age': 30} + >>> vars(dave) + {'name': 'David D', '_age': 40} + + >>> mary.age # Access the data and log the lookup + INFO:root:Accessing 'age' giving 30 + 30 + >>> mary.birthday() # Updates are logged as well + INFO:root:Accessing 'age' giving 30 + INFO:root:Updating 'age' to 31 + + >>> dave.name # Regular attribute lookup isn't logged + 'David D' + >>> dave.age # Only the managed attribute is logged + INFO:root:Accessing 'age' giving 40 + 40 + +One major issue with this example is the private name *_age* is hardwired in +the *LoggedAgeAccess* class. That means that each instance can only have one +logged attribute and that its name is unchangeable. In the next example, +we'll fix that problem. + + +Customized Names +---------------- + +When a class uses descriptors, it can inform each descriptor about what +variable name was used. + +In this example, the :class:`Person` class has two descriptor instances, +*name* and *age*. When the :class:`Person` class is defined, it makes a +callback to :meth:`__set_name__` in *LoggedAccess* so that the field names can +be recorded, giving each descriptor its own *public_name* and *private_name*:: + + import logging + + logging.basicConfig(level=logging.INFO) + + class LoggedAccess: + + def __set_name__(self, owner, name): + self.public_name = name + self.private_name = f'_{name}' + + def __get__(self, obj, objtype=None): + value = getattr(obj, self.private_name) + logging.info('Accessing %r giving %r', self.public_name, value) + return value + + def __set__(self, obj, value): + logging.info('Updating %r to %r', self.public_name, value) + setattr(obj, self.private_name, value) + + class Person: + + name = LoggedAccess() # First descriptor + age = LoggedAccess() # Second descriptor + + def __init__(self, name, age): + self.name = name # Calls the first descriptor + self.age = age # Calls the second descriptor + + def birthday(self): + self.age += 1 + +An interactive session shows that the :class:`Person` class has called +:meth:`__set_name__` so that the field names would be recorded. Here +we call :func:`vars` to lookup the descriptor without triggering it:: + + >>> vars(vars(Person)['name']) + {'public_name': 'name', 'private_name': '_name'} + >>> vars(vars(Person)['age']) + {'public_name': 'age', 'private_name': '_age'} + +The new class now logs access to both *name* and *age*:: + + >>> pete = Person('Peter P', 10) + INFO:root:Updating 'name' to 'Peter P' + INFO:root:Updating 'age' to 10 + >>> kate = Person('Catherine C', 20) + INFO:root:Updating 'name' to 'Catherine C' + INFO:root:Updating 'age' to 20 + +The two *Person* instances contain only the private names:: + + >>> vars(pete) + {'_name': 'Peter P', '_age': 10} + >>> vars(kate) + {'_name': 'Catherine C', '_age': 20} + + +Closing thoughts +---------------- + +A :term:`descriptor` is what we call any object that defines :meth:`__get__`, +:meth:`__set__`, or :meth:`__delete__`. + +Descriptors get invoked by the dot operator during attribute lookup. If a +descriptor is accessed indirectly with ``vars(some_class)[descriptor_name]``, +the descriptor instance is returned without invoking it. + +Descriptors only work when used as class variables. When put in instances, +they have no effect. + +The main motivation for descriptors is to provide a hook allowing objects +stored in class variables to control what happens during dotted lookup. + +Traditionally, the calling class controls what happens during lookup. +Descriptors invert that relationship and allow the data being looked-up to +have a say in the matter. + +Descriptors are used throughout the language. It is how functions turn into +bound methods. Common tools like :func:`classmethod`, :func:`staticmethod`, +:func:`property`, and :func:`functools.cached_property` are all implemented as +descriptors. + + +Complete Practical Example +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +In this example, we create a practical and powerful tool for locating +notoriously hard to find data corruption bugs. + + +Validator class +--------------- + +A validator is a descriptor for managed attribute access. Prior to storing +any data, it verifies that the new value meets various type and range +restrictions. If those restrictions aren't met, it raises an exception to +prevents data corruption at its source. + +This :class:`Validator` class is both an :term:`abstract base class` and a +managed attribute descriptor:: + + from abc import ABC, abstractmethod + + class Validator(ABC): + + def __set_name__(self, owner, name): + self.private_name = f'_{name}' + + def __get__(self, obj, objtype=None): + return getattr(obj, self.private_name) + + def __set__(self, obj, value): + self.validate(value) + setattr(obj, self.private_name, value) + + @abstractmethod + def validate(self, value): + pass + +Custom validators need to subclass from :class:`Validator` and supply a +:meth:`validate` method to test various restrictions as needed. + + +Custom validators +----------------- + +Here are three practical data validation utilities: + +1) :class:`OneOf` verifies that a value is one of a restricted set of options. + +2) :class:`Number` verifies that a value is either an :class:`int` or + :class:`float`. Optionally, it verifies that a value is between a given + minimum or maximum. + +3) :class:`String` verifies that a value is a :class:`str`. Optionally, it + validates a given minimum or maximum length. Optionally, it can test for + another predicate as well. + +:: + + class OneOf(Validator): + + def __init__(self, *options): + self.options = set(options) + + def validate(self, value): + if value not in self.options: + raise ValueError(f'Expected {value!r} to be one of {self.options!r}') + + class Number(Validator): + + def __init__(self, minvalue=None, maxvalue=None): + self.minvalue = minvalue + self.maxvalue = maxvalue + + def validate(self, value): + if not isinstance(value, (int, float)): + raise TypeError(f'Expected {value!r} to be an int or float') + if self.minvalue is not None and value < self.minvalue: + raise ValueError( + f'Expected {value!r} to be at least {self.minvalue!r}' + ) + if self.maxvalue is not None and value > self.maxvalue: + raise ValueError( + f'Expected {value!r} to be no more than {self.maxvalue!r}' + ) + + class String(Validator): + + def __init__(self, minsize=None, maxsize=None, predicate=None): + self.minsize = minsize + self.maxsize = maxsize + self.predicate = predicate + + def validate(self, value): + if not isinstance(value, str): + raise TypeError(f'Expected {value!r} to be an str') + if self.minsize is not None and len(value) < self.minsize: + raise ValueError( + f'Expected {value!r} to be no smaller than {self.minsize!r}' + ) + if self.maxsize is not None and len(value) > self.maxsize: + raise ValueError( + f'Expected {value!r} to be no bigger than {self.maxsize!r}' + ) + if self.predicate is not None and not self.predicate(value): + raise ValueError( + f'Expected {self.predicate} to be true for {value!r}' + ) + + +Practical use +------------- + +Here's how the data validators can be used in a real class:: + + class Component: + + name = String(minsize=3, maxsize=10, predicate=str.isupper) + kind = OneOf('plastic', 'metal') + quantity = Number(minvalue=0) + + def __init__(self, name, kind, quantity): + self.name = name + self.kind = kind + self.quantity = quantity + +The descriptors prevent invalid instances from being created:: + + Component('WIDGET', 'metal', 5) # Allowed. + Component('Widget', 'metal', 5) # Blocked: 'Widget' is not all uppercase + Component('WIDGET', 'metle', 5) # Blocked: 'metle' is misspelled + Component('WIDGET', 'metal', -5) # Blocked: -5 is negative + Component('WIDGET', 'metal', 'V') # Blocked: 'V' isn't a number + + +Technical Tutorial +^^^^^^^^^^^^^^^^^^ + +What follows is a more technical tutorial for the mechanics and details of how +descriptors work. + + Abstract -------- @@ -39,10 +450,10 @@ Where this occurs in the precedence chain depends on which descriptor methods were defined. Descriptors are a powerful, general purpose protocol. They are the mechanism -behind properties, methods, static methods, class methods, and :func:`super()`. -They are used throughout Python itself to implement the new style classes -introduced in version 2.2. Descriptors simplify the underlying C-code and offer -a flexible set of new tools for everyday Python programs. +behind properties, methods, static methods, class methods, and +:func:`super()`. They are used throughout Python itself. Descriptors +simplify the underlying C code and offer a flexible set of new tools for +everyday Python programs. Descriptor Protocol @@ -132,11 +543,29 @@ The implementation details are in :c:func:`super_getattro()` in The details above show that the mechanism for descriptors is embedded in the :meth:`__getattribute__()` methods for :class:`object`, :class:`type`, and :func:`super`. Classes inherit this machinery when they derive from -:class:`object` or if they have a meta-class providing similar functionality. +:class:`object` or if they have a metaclass providing similar functionality. Likewise, classes can turn-off descriptor invocation by overriding :meth:`__getattribute__()`. +Automatic Name Notification +--------------------------- + +Sometimes it is desirable for a descriptor to know what class variable name it +was assigned to. When a new class is created, the :class:`type` metaclass +scans the dictionary of the new class. If any of the entries are descriptors +and if they define :meth:`__set_name__`, that method is called with two +arguments. The *owner* is the class where the descriptor is used, the *name* +is class variable the descriptor was assigned to. + +The implementation details are in :c:func:`type_new()` and +:c:func:`set_names()` in :source:`Objects/typeobject.c`. + +Since the update logic is in :meth:`type.__new__`, notifications only take +place at the time of class creation. If descriptors are added to the class +afterwards, :meth:`__set_name__` will need to be called manually. + + Descriptor Example ------------------ @@ -154,7 +583,7 @@ descriptor is useful for monitoring just a few chosen attributes:: self.val = initval self.name = name - def __get__(self, obj, objtype): + def __get__(self, obj, objtype=None): print('Retrieving', self.name) return self.val @@ -162,11 +591,11 @@ descriptor is useful for monitoring just a few chosen attributes:: print('Updating', self.name) self.val = val - >>> class MyClass: - ... x = RevealAccess(10, 'var "x"') - ... y = 5 - ... - >>> m = MyClass() + class B: + x = RevealAccess(10, 'var "x"') + y = 5 + + >>> m = B() >>> m.x Retrieving var "x" 10 @@ -251,12 +680,13 @@ affect existing client code accessing the attribute directly. The solution is to wrap access to the value attribute in a property data descriptor:: class Cell: - . . . - def getvalue(self): + ... + + @property + def value(self): "Recalculate the cell before returning value" self.recalc() return self._value - value = property(getvalue) Functions and Methods @@ -278,42 +708,48 @@ non-data descriptors which return bound methods when they are invoked from an object. In pure Python, it works like this:: class Function: - . . . + ... + def __get__(self, obj, objtype=None): "Simulate func_descr_get() in Objects/funcobject.c" if obj is None: return self return types.MethodType(self, obj) -Running the interpreter shows how the function descriptor works in practice:: +Running the following in class in the interpreter shows how the function +descriptor works in practice:: - >>> class D: - ... def f(self, x): - ... return x - ... - >>> d = D() + class D: + def f(self, x): + return x + +Access through the class dictionary does not invoke :meth:`__get__`. Instead, +it just returns the underlying function object:: - # Access through the class dictionary does not invoke __get__. - # It just returns the underlying function object. >>> D.__dict__['f'] - # Dotted access from a class calls __get__() which just returns - # the underlying function unchanged. +Dotted access from a class calls :meth:`__get__` which just returns the +underlying function unchanged:: + >>> D.f - # The function has a __qualname__ attribute to support introspection +The function has a :term:`qualified name` attribute to support introspection:: + >>> D.f.__qualname__ 'D.f' - # Dotted access from an instance calls __get__() which returns the - # function wrapped in a bound method object +Dotted access from an instance calls :meth:`__get__` which returns a bound +method object:: + + >>> d = D() >>> d.f > - # Internally, the bound method stores the underlying function and - # the bound instance. +Internally, the bound method stores the underlying function and the bound +instance:: + >>> d.f.__func__ >>> d.f.__self__ @@ -328,20 +764,20 @@ patterns of binding functions into methods. To recap, functions have a :meth:`__get__` method so that they can be converted to a method when accessed as attributes. The non-data descriptor transforms an -``obj.f(*args)`` call into ``f(obj, *args)``. Calling ``klass.f(*args)`` +``obj.f(*args)`` call into ``f(obj, *args)``. Calling ``cls.f(*args)`` becomes ``f(*args)``. This chart summarizes the binding and its two most useful variants: +-----------------+----------------------+------------------+ | Transformation | Called from an | Called from a | - | | Object | Class | + | | object | class | +=================+======================+==================+ | function | f(obj, \*args) | f(\*args) | +-----------------+----------------------+------------------+ | staticmethod | f(\*args) | f(\*args) | +-----------------+----------------------+------------------+ - | classmethod | f(type(obj), \*args) | f(klass, \*args) | + | classmethod | f(type(obj), \*args) | f(cls, \*args) | +-----------------+----------------------+------------------+ Static methods return the underlying function without changes. Calling either @@ -365,11 +801,11 @@ It can be called either from an object or the class: ``s.erf(1.5) --> .9332`` o Since staticmethods return the underlying function with no changes, the example calls are unexciting:: - >>> class E: - ... def f(x): - ... print(x) - ... f = staticmethod(f) - ... + class E: + @staticmethod + def f(x): + print(x) + >>> E.f(3) 3 >>> E().f(3) @@ -391,32 +827,33 @@ Unlike static methods, class methods prepend the class reference to the argument list before calling the function. This format is the same for whether the caller is an object or a class:: - >>> class E: - ... def f(klass, x): - ... return klass.__name__, x - ... f = classmethod(f) - ... - >>> print(E.f(3)) - ('E', 3) - >>> print(E().f(3)) - ('E', 3) + class F: + @classmethod + def f(cls, x): + return cls.__name__, x + + >>> print(F.f(3)) + ('F', 3) + >>> print(F().f(3)) + ('F', 3) This behavior is useful whenever the function only needs to have a class -reference and does not care about any underlying data. One use for classmethods -is to create alternate class constructors. In Python 2.3, the classmethod +reference and does not care about any underlying data. One use for +classmethods is to create alternate class constructors. The classmethod :func:`dict.fromkeys` creates a new dictionary from a list of keys. The pure Python equivalent is:: class Dict: - . . . - def fromkeys(klass, iterable, value=None): + ... + + @classmethod + def fromkeys(cls, iterable, value=None): "Emulate dict_fromkeys() in Objects/dictobject.c" - d = klass() + d = cls() for key in iterable: d[key] = value return d - fromkeys = classmethod(fromkeys) Now a new dictionary of unique keys can be constructed like this:: @@ -432,10 +869,9 @@ Using the non-data descriptor protocol, a pure Python version of def __init__(self, f): self.f = f - def __get__(self, obj, klass=None): - if klass is None: - klass = type(obj) + def __get__(self, obj, cls=None): + if cls is None: + cls = type(obj) def newfunc(*args): - return self.f(klass, *args) + return self.f(cls, *args) return newfunc - diff --git a/Doc/tools/susp-ignored.csv b/Doc/tools/susp-ignored.csv index e03c68fa98a0b..80e77be45f95b 100644 --- a/Doc/tools/susp-ignored.csv +++ b/Doc/tools/susp-ignored.csv @@ -23,6 +23,9 @@ howto/curses,,:blue,"2:green, 3:yellow, 4:blue, 5:magenta, 6:cyan, and 7:white. howto/curses,,:magenta,"2:green, 3:yellow, 4:blue, 5:magenta, 6:cyan, and 7:white. The" howto/curses,,:cyan,"2:green, 3:yellow, 4:blue, 5:magenta, 6:cyan, and 7:white. The" howto/curses,,:white,"2:green, 3:yellow, 4:blue, 5:magenta, 6:cyan, and 7:white. The" +howto/descriptor,,:root,"INFO:root" +howto/descriptor,,:Updating,"root:Updating" +howto/descriptor,,:Accessing,"root:Accessing" howto/instrumentation,,::,python$target:::function-entry howto/instrumentation,,:function,python$target:::function-entry howto/instrumentation,,::,python$target:::function-return From webhook-mailer at python.org Fri Oct 23 17:06:22 2020 From: webhook-mailer at python.org (miss-islington) Date: Fri, 23 Oct 2020 21:06:22 -0000 Subject: [Python-checkins] Allow dependabot to check GitHub actions monthly (GH-22787) Message-ID: https://github.com/python/cpython/commit/697109bbf3929402b415304dddc386edc54b1b69 commit: 697109bbf3929402b415304dddc386edc54b1b69 branch: master author: John Losito committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-23T14:06:13-07:00 summary: Allow dependabot to check GitHub actions monthly (GH-22787) Let Dependabot update GitHub Actions dependency once a month. Here's reference to the dependabot configs. https://docs.github.com/en/free-pro-team at latest/github/administering-a-repository/keeping-your-actions-up-to-date-with-github-dependabot files: A .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000000..8ac6b8c4984dc --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "monthly" From webhook-mailer at python.org Fri Oct 23 17:29:58 2020 From: webhook-mailer at python.org (miss-islington) Date: Fri, 23 Oct 2020 21:29:58 -0000 Subject: [Python-checkins] build(deps): bump actions/cache from v1 to v2.1.2 (GH-22919) Message-ID: https://github.com/python/cpython/commit/cd0edbc716d64ccd437d97af1eaa3b7a6f0f6de5 commit: cd0edbc716d64ccd437d97af1eaa3b7a6f0f6de5 branch: master author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-23T14:29:41-07:00 summary: build(deps): bump actions/cache from v1 to v2.1.2 (GH-22919) Bumps [actions/cache](https://github.com/actions/cache) from v1 to v2.1.2.
    Release notes

    Sourced from actions/cache's releases.

    v2.1.2

    • Adds input to limit the chunk upload size, useful for self-hosted runners with slower upload speeds
    • No-op when executing on GHES
    Commits

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
    Dependabot commands and options
    You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    Automerge-Triggered-By: GH:Mariatta files: M .github/workflows/build.yml M .github/workflows/coverage.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index df68fe271de7a..d00c227182f61 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -106,7 +106,7 @@ jobs: run: sudo ./.github/workflows/posix-deps-apt.sh - name: 'Restore OpenSSL build' id: cache-openssl - uses: actions/cache at v1 + uses: actions/cache at v2.1.2 with: path: ./multissl/openssl/${{ env.OPENSSL_VER }} key: ${{ runner.os }}-multissl-openssl-${{ env.OPENSSL_VER }} diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 035348e8a6932..ed71a012395d4 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -32,7 +32,7 @@ jobs: run: sudo ./.github/workflows/posix-deps-apt.sh - name: 'Restore OpenSSL build' id: cache-openssl - uses: actions/cache at v1 + uses: actions/cache at v2.1.2 with: path: ./multissl/openssl/${{ env.OPENSSL_VER }} key: ${{ runner.os }}-multissl-openssl-${{ env.OPENSSL_VER }} From webhook-mailer at python.org Fri Oct 23 17:32:23 2020 From: webhook-mailer at python.org (miss-islington) Date: Fri, 23 Oct 2020 21:32:23 -0000 Subject: [Python-checkins] build(deps): bump actions/upload-artifact from v1 to v2.2.0 (GH-22920) Message-ID: https://github.com/python/cpython/commit/a3c4ceffe61df89cc9ce5c437cae0276521dc83b commit: a3c4ceffe61df89cc9ce5c437cae0276521dc83b branch: master author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-23T14:32:12-07:00 summary: build(deps): bump actions/upload-artifact from v1 to v2.2.0 (GH-22920) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from v1 to v2.2.0.
    Release notes

    Sourced from actions/upload-artifact's releases.

    v2.2.0

    • Support for artifact retention
    Commits

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
    Dependabot commands and options
    You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    Automerge-Triggered-By: GH:Mariatta files: M .github/workflows/doc.yml diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index 88c9cbd797ab2..7c6012eed4f2d 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -38,7 +38,7 @@ jobs: - name: 'Build documentation' run: xvfb-run make -C Doc/ PYTHON=../python SPHINXOPTS="-q -W --keep-going -j4" doctest suspicious html - name: 'Upload' - uses: actions/upload-artifact at v1 + uses: actions/upload-artifact at v2.2.0 with: name: doc-html path: Doc/build/html From webhook-mailer at python.org Fri Oct 23 17:38:08 2020 From: webhook-mailer at python.org (miss-islington) Date: Fri, 23 Oct 2020 21:38:08 -0000 Subject: [Python-checkins] [3.8] bpo-40592: shutil.which will not return None anymore if ; is the last char in PATHEXT (GH-20088) (GH-22913) Message-ID: https://github.com/python/cpython/commit/8b4842b7a830299870067bba59f53529b13647a2 commit: 8b4842b7a830299870067bba59f53529b13647a2 branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-23T14:37:58-07:00 summary: [3.8] bpo-40592: shutil.which will not return None anymore if ; is the last char in PATHEXT (GH-20088) (GH-22913) shutil.which will not return None anymore for empty str in PATHEXT Empty PATHEXT will now be defaulted to _WIN_DEFAULT_PATHEXT (cherry picked from commit da6f098188c9825f10ae60db8987056b3a54c2e8) Co-authored-by: Christopher Marchfelder files: A Misc/NEWS.d/next/Library/2020-05-14-16-01-34.bpo-40592.Cmk855.rst M Lib/shutil.py M Lib/test/test_shutil.py diff --git a/Lib/shutil.py b/Lib/shutil.py index 1f05d80f32a8f..9d151493e7667 100644 --- a/Lib/shutil.py +++ b/Lib/shutil.py @@ -53,6 +53,9 @@ _USE_CP_SENDFILE = hasattr(os, "sendfile") and sys.platform.startswith("linux") _HAS_FCOPYFILE = posix and hasattr(posix, "_fcopyfile") # macOS +# CMD defaults in Windows 10 +_WIN_DEFAULT_PATHEXT = ".COM;.EXE;.BAT;.CMD;.VBS;.JS;.WS;.MSC" + __all__ = ["copyfileobj", "copyfile", "copymode", "copystat", "copy", "copy2", "copytree", "move", "rmtree", "Error", "SpecialFileError", "ExecError", "make_archive", "get_archive_formats", @@ -1400,7 +1403,9 @@ def which(cmd, mode=os.F_OK | os.X_OK, path=None): path.insert(0, curdir) # PATHEXT is necessary to check on Windows. - pathext = os.environ.get("PATHEXT", "").split(os.pathsep) + pathext_source = os.getenv("PATHEXT") or _WIN_DEFAULT_PATHEXT + pathext = [ext for ext in pathext_source.split(os.pathsep) if ext] + if use_bytes: pathext = [os.fsencode(ext) for ext in pathext] # See if the given file matches any of the expected path extensions. diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py index bcb7e498e198e..77306170d9fc1 100644 --- a/Lib/test/test_shutil.py +++ b/Lib/test/test_shutil.py @@ -1830,6 +1830,23 @@ def test_pathext(self): rv = shutil.which(program, path=self.temp_dir) self.assertEqual(rv, temp_filexyz.name) + # Issue 40592: See https://bugs.python.org/issue40592 + @unittest.skipUnless(sys.platform == "win32", 'test specific to Windows') + def test_pathext_with_empty_str(self): + ext = ".xyz" + temp_filexyz = tempfile.NamedTemporaryFile(dir=self.temp_dir, + prefix="Tmp2", suffix=ext) + self.addCleanup(temp_filexyz.close) + + # strip path and extension + program = os.path.basename(temp_filexyz.name) + program = os.path.splitext(program)[0] + + with support.EnvironmentVarGuard() as env: + env['PATHEXT'] = f"{ext};" # note the ; + rv = shutil.which(program, path=self.temp_dir) + self.assertEqual(rv, temp_filexyz.name) + class TestWhichBytes(TestWhich): def setUp(self): diff --git a/Misc/NEWS.d/next/Library/2020-05-14-16-01-34.bpo-40592.Cmk855.rst b/Misc/NEWS.d/next/Library/2020-05-14-16-01-34.bpo-40592.Cmk855.rst new file mode 100644 index 0000000000000..3211a1bc345fa --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-05-14-16-01-34.bpo-40592.Cmk855.rst @@ -0,0 +1 @@ +:func:`shutil.which` now ignores empty entries in :envvar:`PATHEXT` instead of treating them as a match. From webhook-mailer at python.org Fri Oct 23 17:38:10 2020 From: webhook-mailer at python.org (miss-islington) Date: Fri, 23 Oct 2020 21:38:10 -0000 Subject: [Python-checkins] [3.9] bpo-40592: shutil.which will not return None anymore if ; is the last char in PATHEXT (GH-20088) (GH-22912) Message-ID: https://github.com/python/cpython/commit/c437fe39cf5318f27f3381a983fbf320f65064bc commit: c437fe39cf5318f27f3381a983fbf320f65064bc branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-23T14:38:05-07:00 summary: [3.9] bpo-40592: shutil.which will not return None anymore if ; is the last char in PATHEXT (GH-20088) (GH-22912) shutil.which will not return None anymore for empty str in PATHEXT Empty PATHEXT will now be defaulted to _WIN_DEFAULT_PATHEXT (cherry picked from commit da6f098188c9825f10ae60db8987056b3a54c2e8) Co-authored-by: Christopher Marchfelder files: A Misc/NEWS.d/next/Library/2020-05-14-16-01-34.bpo-40592.Cmk855.rst M Lib/shutil.py M Lib/test/test_shutil.py diff --git a/Lib/shutil.py b/Lib/shutil.py index a4ce2c0290bc9..223e9a8a70506 100644 --- a/Lib/shutil.py +++ b/Lib/shutil.py @@ -53,6 +53,9 @@ _USE_CP_SENDFILE = hasattr(os, "sendfile") and sys.platform.startswith("linux") _HAS_FCOPYFILE = posix and hasattr(posix, "_fcopyfile") # macOS +# CMD defaults in Windows 10 +_WIN_DEFAULT_PATHEXT = ".COM;.EXE;.BAT;.CMD;.VBS;.JS;.WS;.MSC" + __all__ = ["copyfileobj", "copyfile", "copymode", "copystat", "copy", "copy2", "copytree", "move", "rmtree", "Error", "SpecialFileError", "ExecError", "make_archive", "get_archive_formats", @@ -1415,7 +1418,9 @@ def which(cmd, mode=os.F_OK | os.X_OK, path=None): path.insert(0, curdir) # PATHEXT is necessary to check on Windows. - pathext = os.environ.get("PATHEXT", "").split(os.pathsep) + pathext_source = os.getenv("PATHEXT") or _WIN_DEFAULT_PATHEXT + pathext = [ext for ext in pathext_source.split(os.pathsep) if ext] + if use_bytes: pathext = [os.fsencode(ext) for ext in pathext] # See if the given file matches any of the expected path extensions. diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py index e56b337083c8f..e19af64be0e1a 100644 --- a/Lib/test/test_shutil.py +++ b/Lib/test/test_shutil.py @@ -1848,6 +1848,23 @@ def test_pathext(self): rv = shutil.which(program, path=self.temp_dir) self.assertEqual(rv, temp_filexyz.name) + # Issue 40592: See https://bugs.python.org/issue40592 + @unittest.skipUnless(sys.platform == "win32", 'test specific to Windows') + def test_pathext_with_empty_str(self): + ext = ".xyz" + temp_filexyz = tempfile.NamedTemporaryFile(dir=self.temp_dir, + prefix="Tmp2", suffix=ext) + self.addCleanup(temp_filexyz.close) + + # strip path and extension + program = os.path.basename(temp_filexyz.name) + program = os.path.splitext(program)[0] + + with support.EnvironmentVarGuard() as env: + env['PATHEXT'] = f"{ext};" # note the ; + rv = shutil.which(program, path=self.temp_dir) + self.assertEqual(rv, temp_filexyz.name) + class TestWhichBytes(TestWhich): def setUp(self): diff --git a/Misc/NEWS.d/next/Library/2020-05-14-16-01-34.bpo-40592.Cmk855.rst b/Misc/NEWS.d/next/Library/2020-05-14-16-01-34.bpo-40592.Cmk855.rst new file mode 100644 index 0000000000000..3211a1bc345fa --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-05-14-16-01-34.bpo-40592.Cmk855.rst @@ -0,0 +1 @@ +:func:`shutil.which` now ignores empty entries in :envvar:`PATHEXT` instead of treating them as a match. From webhook-mailer at python.org Fri Oct 23 17:48:24 2020 From: webhook-mailer at python.org (miss-islington) Date: Fri, 23 Oct 2020 21:48:24 -0000 Subject: [Python-checkins] build(deps): bump actions/cache from v1 to v2.1.2 (GH-22919) Message-ID: https://github.com/python/cpython/commit/11ab4fcbcefcd9dd3af509d25f3f408d2fc01eec commit: 11ab4fcbcefcd9dd3af509d25f3f408d2fc01eec branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-23T14:48:15-07:00 summary: build(deps): bump actions/cache from v1 to v2.1.2 (GH-22919) Bumps [actions/cache](https://github.com/actions/cache) from v1 to v2.1.2.
    Release notes

    Sourced from actions/cache's releases.

    v2.1.2

    • Adds input to limit the chunk upload size, useful for self-hosted runners with slower upload speeds
    • No-op when executing on GHES
    Commits

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: GH- (dependabot-automerge-start) [//]: GH- (dependabot-automerge-end) ---
    Dependabot commands and options
    You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    Automerge-Triggered-By: GH:Mariatta (cherry picked from commit cd0edbc716d64ccd437d97af1eaa3b7a6f0f6de5) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> files: M .github/workflows/build.yml M .github/workflows/coverage.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2cd2fbb686336..9e63b68576303 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -104,7 +104,7 @@ jobs: run: sudo ./.github/workflows/posix-deps-apt.sh - name: 'Restore OpenSSL build' id: cache-openssl - uses: actions/cache at v1 + uses: actions/cache at v2.1.2 with: path: ./multissl/openssl/${{ env.OPENSSL_VER }} key: ${{ runner.os }}-multissl-openssl-${{ env.OPENSSL_VER }} diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 4f46cbf1100e9..d66c02b51b15e 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -32,7 +32,7 @@ jobs: run: sudo ./.github/workflows/posix-deps-apt.sh - name: 'Restore OpenSSL build' id: cache-openssl - uses: actions/cache at v1 + uses: actions/cache at v2.1.2 with: path: ./multissl/openssl/${{ env.OPENSSL_VER }} key: ${{ runner.os }}-multissl-openssl-${{ env.OPENSSL_VER }} From webhook-mailer at python.org Fri Oct 23 17:49:19 2020 From: webhook-mailer at python.org (miss-islington) Date: Fri, 23 Oct 2020 21:49:19 -0000 Subject: [Python-checkins] build(deps): bump actions/cache from v1 to v2.1.2 (GH-22919) Message-ID: https://github.com/python/cpython/commit/8d6477fbde6a7b29debdcac35978c70b669960a4 commit: 8d6477fbde6a7b29debdcac35978c70b669960a4 branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-23T14:49:15-07:00 summary: build(deps): bump actions/cache from v1 to v2.1.2 (GH-22919) Bumps [actions/cache](https://github.com/actions/cache) from v1 to v2.1.2.
    Release notes

    Sourced from actions/cache's releases.

    v2.1.2

    • Adds input to limit the chunk upload size, useful for self-hosted runners with slower upload speeds
    • No-op when executing on GHES
    Commits

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: GH- (dependabot-automerge-start) [//]: GH- (dependabot-automerge-end) ---
    Dependabot commands and options
    You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    Automerge-Triggered-By: GH:Mariatta (cherry picked from commit cd0edbc716d64ccd437d97af1eaa3b7a6f0f6de5) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> files: M .github/workflows/build.yml M .github/workflows/coverage.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1d0cf0b5ec352..e4e5a3ad63d3b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -97,7 +97,7 @@ jobs: run: sudo ./.github/workflows/posix-deps-apt.sh - name: 'Restore OpenSSL build' id: cache-openssl - uses: actions/cache at v1 + uses: actions/cache at v2.1.2 with: path: ./multissl/openssl/${{ env.OPENSSL_VER }} key: ${{ runner.os }}-multissl-openssl-${{ env.OPENSSL_VER }} diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index b5668f85dd7ab..ec8c733e316c6 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -30,7 +30,7 @@ jobs: run: sudo ./.github/workflows/posix-deps-apt.sh - name: 'Restore OpenSSL build' id: cache-openssl - uses: actions/cache at v1 + uses: actions/cache at v2.1.2 with: path: ./multissl/openssl/${{ env.OPENSSL_VER }} key: ${{ runner.os }}-multissl-openssl-${{ env.OPENSSL_VER }} From webhook-mailer at python.org Fri Oct 23 17:51:09 2020 From: webhook-mailer at python.org (miss-islington) Date: Fri, 23 Oct 2020 21:51:09 -0000 Subject: [Python-checkins] build(deps): bump actions/upload-artifact from v1 to v2.2.0 (GH-22920) Message-ID: https://github.com/python/cpython/commit/750a8a9568742917b1f62f30ed3c54e26088a5c2 commit: 750a8a9568742917b1f62f30ed3c54e26088a5c2 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-23T14:51:04-07:00 summary: build(deps): bump actions/upload-artifact from v1 to v2.2.0 (GH-22920) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from v1 to v2.2.0.
    Release notes

    Sourced from actions/upload-artifact's releases.

    v2.2.0

    • Support for artifact retention
    Commits

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: GH- (dependabot-automerge-start) [//]: GH- (dependabot-automerge-end) ---
    Dependabot commands and options
    You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    Automerge-Triggered-By: GH:Mariatta (cherry picked from commit a3c4ceffe61df89cc9ce5c437cae0276521dc83b) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> files: M .github/workflows/doc.yml diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index d481ea279d796..455ad02f7ed3e 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -36,7 +36,7 @@ jobs: - name: 'Build documentation' run: xvfb-run make -C Doc/ PYTHON=../python SPHINXOPTS="-q -W -j4" doctest suspicious html - name: 'Upload' - uses: actions/upload-artifact at v1 + uses: actions/upload-artifact at v2.2.0 with: name: doc-html path: Doc/build/html From webhook-mailer at python.org Fri Oct 23 17:53:43 2020 From: webhook-mailer at python.org (miss-islington) Date: Fri, 23 Oct 2020 21:53:43 -0000 Subject: [Python-checkins] build(deps): bump actions/upload-artifact from v1 to v2.2.0 (GH-22920) Message-ID: https://github.com/python/cpython/commit/82bfe36432bdfe3a183d1721445ef30c0a7f3a34 commit: 82bfe36432bdfe3a183d1721445ef30c0a7f3a34 branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-23T14:53:39-07:00 summary: build(deps): bump actions/upload-artifact from v1 to v2.2.0 (GH-22920) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from v1 to v2.2.0.
    Release notes

    Sourced from actions/upload-artifact's releases.

    v2.2.0

    • Support for artifact retention
    Commits

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: GH- (dependabot-automerge-start) [//]: GH- (dependabot-automerge-end) ---
    Dependabot commands and options
    You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    Automerge-Triggered-By: GH:Mariatta (cherry picked from commit a3c4ceffe61df89cc9ce5c437cae0276521dc83b) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> files: M .github/workflows/doc.yml diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index e7d99583e6909..ff041210a3dea 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -34,7 +34,7 @@ jobs: - name: 'Build documentation' run: xvfb-run make -C Doc/ PYTHON=../python SPHINXOPTS="-q -W -j4" doctest suspicious html - name: 'Upload' - uses: actions/upload-artifact at v1 + uses: actions/upload-artifact at v2.2.0 with: name: doc-html path: Doc/build/html From webhook-mailer at python.org Fri Oct 23 18:49:04 2020 From: webhook-mailer at python.org (orsenthil) Date: Fri, 23 Oct 2020 22:49:04 -0000 Subject: [Python-checkins] bpo-38976: Add support for HTTP Only flag in MozillaCookieJar (#17471) Message-ID: https://github.com/python/cpython/commit/16ee68da6e12bb2d79751b32cc37523fe4f4bb48 commit: 16ee68da6e12bb2d79751b32cc37523fe4f4bb48 branch: master author: Jacob Neil Taylor committer: orsenthil date: 2020-10-23T15:48:55-07:00 summary: bpo-38976: Add support for HTTP Only flag in MozillaCookieJar (#17471) Add support for HTTP Only flag in MozillaCookieJar Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> files: A Misc/NEWS.d/next/Library/2019-12-05-05-22-49.bpo-38976.5MG7Uu.rst M Lib/http/cookiejar.py M Lib/test/test_http_cookiejar.py diff --git a/Lib/http/cookiejar.py b/Lib/http/cookiejar.py index 47ed5c3d64ab7..eaa76c26b9c59 100644 --- a/Lib/http/cookiejar.py +++ b/Lib/http/cookiejar.py @@ -50,10 +50,18 @@ def _debug(*args): logger = logging.getLogger("http.cookiejar") return logger.debug(*args) - +HTTPONLY_ATTR = "HTTPOnly" +HTTPONLY_PREFIX = "#HttpOnly_" DEFAULT_HTTP_PORT = str(http.client.HTTP_PORT) +NETSCAPE_MAGIC_RGX = re.compile("#( Netscape)? HTTP Cookie File") MISSING_FILENAME_TEXT = ("a filename was not supplied (nor was the CookieJar " "instance initialised with one)") +NETSCAPE_HEADER_TEXT = """\ +# Netscape HTTP Cookie File +# http://curl.haxx.se/rfc/cookie_spec.html +# This is a generated file! Do not edit. + +""" def _warn_unhandled_exception(): # There are a few catch-all except: statements in this module, for @@ -2004,19 +2012,11 @@ class MozillaCookieJar(FileCookieJar): header by default (Mozilla can cope with that). """ - magic_re = re.compile("#( Netscape)? HTTP Cookie File") - header = """\ -# Netscape HTTP Cookie File -# http://curl.haxx.se/rfc/cookie_spec.html -# This is a generated file! Do not edit. - -""" def _really_load(self, f, filename, ignore_discard, ignore_expires): now = time.time() - magic = f.readline() - if not self.magic_re.search(magic): + if not NETSCAPE_MAGIC_RGX.match(f.readline()): raise LoadError( "%r does not look like a Netscape format cookies file" % filename) @@ -2024,8 +2024,17 @@ def _really_load(self, f, filename, ignore_discard, ignore_expires): try: while 1: line = f.readline() + rest = {} + if line == "": break + # httponly is a cookie flag as defined in rfc6265 + # when encoded in a netscape cookie file, + # the line is prepended with "#HttpOnly_" + if line.startswith(HTTPONLY_PREFIX): + rest[HTTPONLY_ATTR] = "" + line = line[len(HTTPONLY_PREFIX):] + # last field may be absent, so keep any trailing tab if line.endswith("\n"): line = line[:-1] @@ -2063,7 +2072,7 @@ def _really_load(self, f, filename, ignore_discard, ignore_expires): discard, None, None, - {}) + rest) if not ignore_discard and c.discard: continue if not ignore_expires and c.is_expired(now): @@ -2083,16 +2092,17 @@ def save(self, filename=None, ignore_discard=False, ignore_expires=False): else: raise ValueError(MISSING_FILENAME_TEXT) with open(filename, "w") as f: - f.write(self.header) + f.write(NETSCAPE_HEADER_TEXT) now = time.time() for cookie in self: + domain = cookie.domain if not ignore_discard and cookie.discard: continue if not ignore_expires and cookie.is_expired(now): continue if cookie.secure: secure = "TRUE" else: secure = "FALSE" - if cookie.domain.startswith("."): initial_dot = "TRUE" + if domain.startswith("."): initial_dot = "TRUE" else: initial_dot = "FALSE" if cookie.expires is not None: expires = str(cookie.expires) @@ -2107,7 +2117,9 @@ def save(self, filename=None, ignore_discard=False, ignore_expires=False): else: name = cookie.name value = cookie.value + if cookie.has_nonstandard_attr(HTTPONLY_ATTR): + domain = HTTPONLY_PREFIX + domain f.write( - "\t".join([cookie.domain, initial_dot, cookie.path, + "\t".join([domain, initial_dot, cookie.path, secure, expires, name, value])+ "\n") diff --git a/Lib/test/test_http_cookiejar.py b/Lib/test/test_http_cookiejar.py index 99d038fa15c1e..fdf15efde12ef 100644 --- a/Lib/test/test_http_cookiejar.py +++ b/Lib/test/test_http_cookiejar.py @@ -1773,6 +1773,10 @@ def test_mozilla(self): interact_netscape(c, "http://www.foo.com/", "fooc=bar; Domain=www.foo.com; %s" % expires) + for cookie in c: + if cookie.name == "foo1": + cookie.set_nonstandard_attr("HTTPOnly", "") + def save_and_restore(cj, ignore_discard): try: cj.save(ignore_discard=ignore_discard) @@ -1787,6 +1791,7 @@ def save_and_restore(cj, ignore_discard): new_c = save_and_restore(c, True) self.assertEqual(len(new_c), 6) # none discarded self.assertIn("name='foo1', value='bar'", repr(new_c)) + self.assertIn("rest={'HTTPOnly': ''}", repr(new_c)) new_c = save_and_restore(c, False) self.assertEqual(len(new_c), 4) # 2 of them discarded on save diff --git a/Misc/NEWS.d/next/Library/2019-12-05-05-22-49.bpo-38976.5MG7Uu.rst b/Misc/NEWS.d/next/Library/2019-12-05-05-22-49.bpo-38976.5MG7Uu.rst new file mode 100644 index 0000000000000..7a48943a6c6cc --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-12-05-05-22-49.bpo-38976.5MG7Uu.rst @@ -0,0 +1,4 @@ +The :mod:`http.cookiejar` module now supports the parsing of cookies in CURL-style cookiejar files through MozillaCookieJar +on all platforms. Previously, such cookie entries would be silently ignored when loading a cookiejar with such entries. + +Additionally, the HTTP Only attribute is persisted in the object, and will be correctly written to file if the MozillaCookieJar object is subsequently dumped. \ No newline at end of file From webhook-mailer at python.org Fri Oct 23 20:11:14 2020 From: webhook-mailer at python.org (gvanrossum) Date: Sat, 24 Oct 2020 00:11:14 -0000 Subject: [Python-checkins] bpo-41950: Typo in Python 3.9 what's new page (GH-22573) Message-ID: https://github.com/python/cpython/commit/dc33f798139016961fba33b37c792c690399b2b6 commit: dc33f798139016961fba33b37c792c690399b2b6 branch: 3.9 author: Weiliang Li committer: gvanrossum date: 2020-10-23T17:11:10-07:00 summary: bpo-41950: Typo in Python 3.9 what's new page (GH-22573) files: M Doc/whatsnew/3.9.rst diff --git a/Doc/whatsnew/3.9.rst b/Doc/whatsnew/3.9.rst index da0718561537b..1a37f16ea2b09 100644 --- a/Doc/whatsnew/3.9.rst +++ b/Doc/whatsnew/3.9.rst @@ -1177,7 +1177,7 @@ CPython bytecode changes * ``COMPARE_OP`` for rich comparisons * ``IS_OP`` for 'is' and 'is not' tests - * ``CONTAINS_OP`` for 'in' and 'is not' tests + * ``CONTAINS_OP`` for 'in' and 'not in' tests * ``JUMP_IF_NOT_EXC_MATCH`` for checking exceptions in 'try-except' statements. From webhook-mailer at python.org Fri Oct 23 20:47:10 2020 From: webhook-mailer at python.org (gpshead) Date: Sat, 24 Oct 2020 00:47:10 -0000 Subject: [Python-checkins] bpo-35823: subprocess: Use vfork() instead of fork() on Linux when safe (GH-11671) Message-ID: https://github.com/python/cpython/commit/976da903a746a5455998e9ca45fbc4d3ad3479d8 commit: 976da903a746a5455998e9ca45fbc4d3ad3479d8 branch: master author: Alexey Izbyshev committer: gpshead date: 2020-10-23T17:47:01-07:00 summary: bpo-35823: subprocess: Use vfork() instead of fork() on Linux when safe (GH-11671) * bpo-35823: subprocess: Use vfork() instead of fork() on Linux when safe When used to run a new executable image, fork() is not a good choice for process creation, especially if the parent has a large working set: fork() needs to copy page tables, which is slow, and may fail on systems where overcommit is disabled, despite that the child is not going to touch most of its address space. Currently, subprocess is capable of using posix_spawn() instead, which normally provides much better performance. However, posix_spawn() does not support many of child setup operations exposed by subprocess.Popen(). Most notably, it's not possible to express `close_fds=True`, which happens to be the default, via posix_spawn(). As a result, most users can't benefit from faster process creation, at least not without changing their code. However, Linux provides vfork() system call, which creates a new process without copying the address space of the parent, and which is actually used by C libraries to efficiently implement posix_spawn(). Due to sharing of the address space and even the stack with the parent, extreme care is required to use vfork(). At least the following restrictions must hold: * No signal handlers must execute in the child process. Otherwise, they might clobber memory shared with the parent, potentially confusing it. * Any library function called after vfork() in the child must be async-signal-safe (as for fork()), but it must also not interact with any library state in a way that might break due to address space sharing and/or lack of any preparations performed by libraries on normal fork(). POSIX.1 permits to call only execve() and _exit(), and later revisions remove vfork() specification entirely. In practice, however, almost all operations needed by subprocess.Popen() can be safely implemented on Linux. * Due to sharing of the stack with the parent, the child must be careful not to clobber local variables that are alive across vfork() call. Compilers are normally aware of this and take extra care with vfork() (and setjmp(), which has a similar problem). * In case the parent is privileged, special attention must be paid to vfork() use, because sharing an address space across different privilege domains is insecure[1]. This patch adds support for using vfork() instead of fork() on Linux when it's possible to do safely given the above. In particular: * vfork() is not used if credential switch is requested. The reverse case (simple subprocess.Popen() but another application thread switches credentials concurrently) is not possible for pure-Python apps because subprocess.Popen() and functions like os.setuid() are mutually excluded via GIL. We might also consider to add a way to opt-out of vfork() (and posix_spawn() on platforms where it might be implemented via vfork()) in a future PR. * vfork() is not used if `preexec_fn != None`. With this change, subprocess will still use posix_spawn() if possible, but will fallback to vfork() on Linux in most cases, and, failing that, to fork(). [1] https://ewontfix.com/7 Co-authored-by: Gregory P. Smith [Google LLC] files: A Misc/NEWS.d/next/Library/2020-10-16-07-45-35.bpo-35823.SNQo56.rst M Modules/_posixsubprocess.c M configure M configure.ac M pyconfig.h.in diff --git a/Misc/NEWS.d/next/Library/2020-10-16-07-45-35.bpo-35823.SNQo56.rst b/Misc/NEWS.d/next/Library/2020-10-16-07-45-35.bpo-35823.SNQo56.rst new file mode 100644 index 0000000000000..cd428d36960c9 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-10-16-07-45-35.bpo-35823.SNQo56.rst @@ -0,0 +1,2 @@ +Use ``vfork()`` instead of ``fork()`` for :func:`subprocess.Popen` on Linux +to improve performance in cases where it is deemed safe. diff --git a/Modules/_posixsubprocess.c b/Modules/_posixsubprocess.c index d08c47980e9c6..ed498572a828b 100644 --- a/Modules/_posixsubprocess.c +++ b/Modules/_posixsubprocess.c @@ -36,6 +36,12 @@ # define SYS_getdents64 __NR_getdents64 #endif +#if defined(__linux__) && defined(HAVE_VFORK) && defined(HAVE_SIGNAL_H) && \ + defined(HAVE_PTHREAD_SIGMASK) && !defined(HAVE_BROKEN_PTHREAD_SIGMASK) +# include +# define VFORK_USABLE 1 +#endif + #if defined(__sun) && defined(__SVR4) /* readdir64 is used to work around Solaris 9 bug 6395699. */ # define readdir readdir64 @@ -407,9 +413,53 @@ _close_open_fds_maybe_unsafe(long start_fd, PyObject* py_fds_to_keep) #endif /* else NOT (defined(__linux__) && defined(HAVE_SYS_SYSCALL_H)) */ +#ifdef VFORK_USABLE +/* Reset dispositions for all signals to SIG_DFL except for ignored + * signals. This way we ensure that no signal handlers can run + * after we unblock signals in a child created by vfork(). + */ +static void +reset_signal_handlers(const sigset_t *child_sigmask) +{ + struct sigaction sa_dfl = {.sa_handler = SIG_DFL}; + for (int sig = 1; sig < _NSIG; sig++) { + /* Dispositions for SIGKILL and SIGSTOP can't be changed. */ + if (sig == SIGKILL || sig == SIGSTOP) { + continue; + } + + /* There is no need to reset the disposition of signals that will + * remain blocked across execve() since the kernel will do it. */ + if (sigismember(child_sigmask, sig) == 1) { + continue; + } + + struct sigaction sa; + /* C libraries usually return EINVAL for signals used + * internally (e.g. for thread cancellation), so simply + * skip errors here. */ + if (sigaction(sig, NULL, &sa) == -1) { + continue; + } + + /* void *h works as these fields are both pointer types already. */ + void *h = (sa.sa_flags & SA_SIGINFO ? (void *)sa.sa_sigaction : + (void *)sa.sa_handler); + if (h == SIG_IGN || h == SIG_DFL) { + continue; + } + + /* This call can't reasonably fail, but if it does, terminating + * the child seems to be too harsh, so ignore errors. */ + (void) sigaction(sig, &sa_dfl, NULL); + } +} +#endif /* VFORK_USABLE */ + + /* - * This function is code executed in the child process immediately after fork - * to set things up and call exec(). + * This function is code executed in the child process immediately after + * (v)fork to set things up and call exec(). * * All of the code in this function must only use async-signal-safe functions, * listed at `man 7 signal` or @@ -417,8 +467,28 @@ _close_open_fds_maybe_unsafe(long start_fd, PyObject* py_fds_to_keep) * * This restriction is documented at * http://www.opengroup.org/onlinepubs/009695399/functions/fork.html. + * + * If this function is called after vfork(), even more care must be taken. + * The lack of preparations that C libraries normally take on fork(), + * as well as sharing the address space with the parent, might make even + * async-signal-safe functions vfork-unsafe. In particular, on Linux, + * set*id() and setgroups() library functions must not be called, since + * they have to interact with the library-level thread list and send + * library-internal signals to implement per-process credentials semantics + * required by POSIX but not supported natively on Linux. Another reason to + * avoid this family of functions is that sharing an address space between + * processes running with different privileges is inherently insecure. + * See bpo-35823 for further discussion and references. + * + * In some C libraries, setrlimit() has the same thread list/signalling + * behavior since resource limits were per-thread attributes before + * Linux 2.6.10. Musl, as of 1.2.1, is known to have this issue + * (https://www.openwall.com/lists/musl/2020/10/15/6). + * + * If vfork-unsafe functionality is desired after vfork(), consider using + * syscall() to obtain it. */ -static void +_Py_NO_INLINE static void child_exec(char *const exec_array[], char *const argv[], char *const envp[], @@ -432,6 +502,7 @@ child_exec(char *const exec_array[], int call_setgid, gid_t gid, int call_setgroups, size_t groups_size, const gid_t *groups, int call_setuid, uid_t uid, int child_umask, + const void *child_sigmask, PyObject *py_fds_to_keep, PyObject *preexec_fn, PyObject *preexec_fn_args_tuple) @@ -507,6 +578,13 @@ child_exec(char *const exec_array[], if (restore_signals) _Py_RestoreSignals(); +#ifdef VFORK_USABLE + if (child_sigmask) { + reset_signal_handlers(child_sigmask); + POSIX_CALL(pthread_sigmask(SIG_SETMASK, child_sigmask, NULL)); + } +#endif + #ifdef HAVE_SETSID if (call_setsid) POSIX_CALL(setsid()); @@ -599,6 +677,81 @@ child_exec(char *const exec_array[], } +/* The main purpose of this wrapper function is to isolate vfork() from both + * subprocess_fork_exec() and child_exec(). A child process created via + * vfork() executes on the same stack as the parent process while the latter is + * suspended, so this function should not be inlined to avoid compiler bugs + * that might clobber data needed by the parent later. Additionally, + * child_exec() should not be inlined to avoid spurious -Wclobber warnings from + * GCC (see bpo-35823). + */ +_Py_NO_INLINE static pid_t +do_fork_exec(char *const exec_array[], + char *const argv[], + char *const envp[], + const char *cwd, + int p2cread, int p2cwrite, + int c2pread, int c2pwrite, + int errread, int errwrite, + int errpipe_read, int errpipe_write, + int close_fds, int restore_signals, + int call_setsid, + int call_setgid, gid_t gid, + int call_setgroups, size_t groups_size, const gid_t *groups, + int call_setuid, uid_t uid, int child_umask, + const void *child_sigmask, + PyObject *py_fds_to_keep, + PyObject *preexec_fn, + PyObject *preexec_fn_args_tuple) +{ + + pid_t pid; + +#ifdef VFORK_USABLE + if (child_sigmask) { + /* These are checked by our caller; verify them in debug builds. */ + assert(!call_setsid); + assert(!call_setuid); + assert(!call_setgid); + assert(!call_setgroups); + assert(preexec_fn == Py_None); + + pid = vfork(); + } else +#endif + { + pid = fork(); + } + + if (pid != 0) { + return pid; + } + + /* Child process. + * See the comment above child_exec() for restrictions imposed on + * the code below. + */ + + if (preexec_fn != Py_None) { + /* We'll be calling back into Python later so we need to do this. + * This call may not be async-signal-safe but neither is calling + * back into Python. The user asked us to use hope as a strategy + * to avoid deadlock... */ + PyOS_AfterFork_Child(); + } + + child_exec(exec_array, argv, envp, cwd, + p2cread, p2cwrite, c2pread, c2pwrite, + errread, errwrite, errpipe_read, errpipe_write, + close_fds, restore_signals, call_setsid, + call_setgid, gid, call_setgroups, groups_size, groups, + call_setuid, uid, child_umask, child_sigmask, + py_fds_to_keep, preexec_fn, preexec_fn_args_tuple); + _exit(255); + return 0; /* Dead code to avoid a potential compiler warning. */ +} + + static PyObject * subprocess_fork_exec(PyObject* self, PyObject *args) { @@ -836,39 +989,56 @@ subprocess_fork_exec(PyObject* self, PyObject *args) need_after_fork = 1; } - pid = fork(); - if (pid == 0) { - /* Child process */ - /* - * Code from here to _exit() must only use async-signal-safe functions, - * listed at `man 7 signal` or - * http://www.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html. + /* NOTE: When old_sigmask is non-NULL, do_fork_exec() may use vfork(). */ + const void *old_sigmask = NULL; +#ifdef VFORK_USABLE + /* Use vfork() only if it's safe. See the comment above child_exec(). */ + sigset_t old_sigs; + if (preexec_fn == Py_None && + !call_setuid && !call_setgid && !call_setgroups && !call_setsid) { + /* Block all signals to ensure that no signal handlers are run in the + * child process while it shares memory with us. Note that signals + * used internally by C libraries won't be blocked by + * pthread_sigmask(), but signal handlers installed by C libraries + * normally service only signals originating from *within the process*, + * so it should be sufficient to consider any library function that + * might send such a signal to be vfork-unsafe and do not call it in + * the child. */ + sigset_t all_sigs; + sigfillset(&all_sigs); + pthread_sigmask(SIG_BLOCK, &all_sigs, &old_sigs); + old_sigmask = &old_sigs; + } +#endif - if (preexec_fn != Py_None) { - /* We'll be calling back into Python later so we need to do this. - * This call may not be async-signal-safe but neither is calling - * back into Python. The user asked us to use hope as a strategy - * to avoid deadlock... */ - PyOS_AfterFork_Child(); - } + pid = do_fork_exec(exec_array, argv, envp, cwd, + p2cread, p2cwrite, c2pread, c2pwrite, + errread, errwrite, errpipe_read, errpipe_write, + close_fds, restore_signals, call_setsid, + call_setgid, gid, call_setgroups, num_groups, groups, + call_setuid, uid, child_umask, old_sigmask, + py_fds_to_keep, preexec_fn, preexec_fn_args_tuple); - child_exec(exec_array, argv, envp, cwd, - p2cread, p2cwrite, c2pread, c2pwrite, - errread, errwrite, errpipe_read, errpipe_write, - close_fds, restore_signals, call_setsid, - call_setgid, gid, call_setgroups, num_groups, groups, - call_setuid, uid, child_umask, - py_fds_to_keep, preexec_fn, preexec_fn_args_tuple); - _exit(255); - return NULL; /* Dead code to avoid a potential compiler warning. */ - } /* Parent (original) process */ if (pid == -1) { /* Capture errno for the exception. */ saved_errno = errno; } +#ifdef VFORK_USABLE + if (old_sigmask) { + /* vfork() semantics guarantees that the parent is blocked + * until the child performs _exit() or execve(), so it is safe + * to unblock signals once we're here. + * Note that in environments where vfork() is implemented as fork(), + * such as QEMU user-mode emulation, the parent won't be blocked, + * but it won't share the address space with the child, + * so it's still safe to unblock the signals. */ + pthread_sigmask(SIG_SETMASK, old_sigmask, NULL); + } +#endif + Py_XDECREF(cwd_obj2); if (need_after_fork) diff --git a/configure b/configure index 29f33b543ecc0..bc87485bf516d 100755 --- a/configure +++ b/configure @@ -11732,7 +11732,7 @@ for ac_func in alarm accept4 setitimer getitimer bind_textdomain_codeset chown \ sigaction sigaltstack sigfillset siginterrupt sigpending sigrelse \ sigtimedwait sigwait sigwaitinfo snprintf strftime strlcpy strsignal symlinkat sync \ sysconf tcgetpgrp tcsetpgrp tempnam timegm times tmpfile tmpnam tmpnam_r \ - truncate uname unlinkat utimensat utimes waitid waitpid wait3 wait4 \ + truncate uname unlinkat utimensat utimes vfork waitid waitpid wait3 wait4 \ wcscoll wcsftime wcsxfrm wmemcmp writev _getpty rtpSpawn do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` diff --git a/configure.ac b/configure.ac index 9698c3c888a6b..49ed09a3a009b 100644 --- a/configure.ac +++ b/configure.ac @@ -3690,7 +3690,7 @@ AC_CHECK_FUNCS(alarm accept4 setitimer getitimer bind_textdomain_codeset chown \ sigaction sigaltstack sigfillset siginterrupt sigpending sigrelse \ sigtimedwait sigwait sigwaitinfo snprintf strftime strlcpy strsignal symlinkat sync \ sysconf tcgetpgrp tcsetpgrp tempnam timegm times tmpfile tmpnam tmpnam_r \ - truncate uname unlinkat utimensat utimes waitid waitpid wait3 wait4 \ + truncate uname unlinkat utimensat utimes vfork waitid waitpid wait3 wait4 \ wcscoll wcsftime wcsxfrm wmemcmp writev _getpty rtpSpawn) # Force lchmod off for Linux. Linux disallows changing the mode of symbolic diff --git a/pyconfig.h.in b/pyconfig.h.in index 298cb4fa12f80..af8a3d6d9c370 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -1301,6 +1301,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_UUID_UUID_H +/* Define to 1 if you have the `vfork' function. */ +#undef HAVE_VFORK + /* Define to 1 if you have the `wait3' function. */ #undef HAVE_WAIT3 From webhook-mailer at python.org Fri Oct 23 21:11:00 2020 From: webhook-mailer at python.org (miss-islington) Date: Sat, 24 Oct 2020 01:11:00 -0000 Subject: [Python-checkins] Mention in "What's New" that the import system is starting to be cleaned up (GH-22931) Message-ID: https://github.com/python/cpython/commit/04523c5bd60569468cd8971dfaeec99e51c353fa commit: 04523c5bd60569468cd8971dfaeec99e51c353fa branch: master author: Brett Cannon committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-23T18:10:54-07:00 summary: Mention in "What's New" that the import system is starting to be cleaned up (GH-22931) Automerge-Triggered-By: GH:brettcannon files: M Doc/whatsnew/3.10.rst diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index e464be6eb7e95..6206c949cc59b 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -267,6 +267,23 @@ Optimizations Deprecated ========== +* Starting in this release, there will be a concerted effort to begin + cleaning up old import semantics that were kept for Python 2.7 + compatibility. Specifically, + :meth:`~importlib.abc.PathEntryFinder.find_loader`/:meth:`~importlib.abc.Finder.find_module` + (superseded by :meth:`~importlib.abc.Finder.find_spec`), + :meth:`~importlib.abc.Loader.load_module` + (superseded by :meth:`~importlib.abc.Loader.exec_module`), + :meth:`~importlib.abc.Loader.module_repr` (which the import system + takes care of for you), the ``__package__`` attribute + (superseded by ``__spec__.parent``), the ``__loader__`` attribute + (superseded by ``__spec__.loader``), and the ``__cached__`` attribute + (superseded by ``__spec__.cached``) will slowly be removed (as well + as other classes and methods in :mod:`importlib`). + :exc:`ImportWarning` and/or :exc:`DeprecationWarning` will be raised + as appropriate to help identify code which needs updating during + this transition. + Removed ======= From webhook-mailer at python.org Fri Oct 23 21:37:33 2020 From: webhook-mailer at python.org (rhettinger) Date: Sat, 24 Oct 2020 01:37:33 -0000 Subject: [Python-checkins] bpo-19072: Update descriptor howto for decorator chaining (GH-22934) Message-ID: https://github.com/python/cpython/commit/8e5b0fdce337ef0a1f4f38b31a8c6b66c56b16d2 commit: 8e5b0fdce337ef0a1f4f38b31a8c6b66c56b16d2 branch: master author: Raymond Hettinger committer: rhettinger date: 2020-10-23T18:37:27-07:00 summary: bpo-19072: Update descriptor howto for decorator chaining (GH-22934) files: M Doc/howto/descriptor.rst diff --git a/Doc/howto/descriptor.rst b/Doc/howto/descriptor.rst index 4a53b9e615692..4e9fad30d31c4 100644 --- a/Doc/howto/descriptor.rst +++ b/Doc/howto/descriptor.rst @@ -872,6 +872,16 @@ Using the non-data descriptor protocol, a pure Python version of def __get__(self, obj, cls=None): if cls is None: cls = type(obj) - def newfunc(*args): - return self.f(cls, *args) - return newfunc + if hasattr(obj, '__get__'): + return self.f.__get__(cls) + return types.MethodType(self.f, cls) + +The code path for ``hasattr(obj, '__get__')`` was added in Python 3.9 and +makes it possible for :func:`classmethod` to support chained decorators. +For example, a classmethod and property could be chained together:: + + class G: + @classmethod + @property + def __doc__(cls): + return f'A doc for {cls.__name__!r}' From webhook-mailer at python.org Fri Oct 23 22:01:43 2020 From: webhook-mailer at python.org (rhettinger) Date: Sat, 24 Oct 2020 02:01:43 -0000 Subject: [Python-checkins] bpo-19072: Update descriptor howto for decorator chaining (GH-22934) (GH-22935) Message-ID: https://github.com/python/cpython/commit/c17f63fae57dc02e78bd6931b8fb6c6c2f9d4d81 commit: c17f63fae57dc02e78bd6931b8fb6c6c2f9d4d81 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: rhettinger date: 2020-10-23T19:01:35-07:00 summary: bpo-19072: Update descriptor howto for decorator chaining (GH-22934) (GH-22935) files: M Doc/howto/descriptor.rst diff --git a/Doc/howto/descriptor.rst b/Doc/howto/descriptor.rst index 4a53b9e615692..4e9fad30d31c4 100644 --- a/Doc/howto/descriptor.rst +++ b/Doc/howto/descriptor.rst @@ -872,6 +872,16 @@ Using the non-data descriptor protocol, a pure Python version of def __get__(self, obj, cls=None): if cls is None: cls = type(obj) - def newfunc(*args): - return self.f(cls, *args) - return newfunc + if hasattr(obj, '__get__'): + return self.f.__get__(cls) + return types.MethodType(self.f, cls) + +The code path for ``hasattr(obj, '__get__')`` was added in Python 3.9 and +makes it possible for :func:`classmethod` to support chained decorators. +For example, a classmethod and property could be chained together:: + + class G: + @classmethod + @property + def __doc__(cls): + return f'A doc for {cls.__name__!r}' From webhook-mailer at python.org Fri Oct 23 23:59:42 2020 From: webhook-mailer at python.org (gvanrossum) Date: Sat, 24 Oct 2020 03:59:42 -0000 Subject: [Python-checkins] bpo-42139: Update What's New 3.9 for master (#22936) Message-ID: https://github.com/python/cpython/commit/805ef73ad0ac4a77f3764dd17dfc959d562ce627 commit: 805ef73ad0ac4a77f3764dd17dfc959d562ce627 branch: master author: Terry Jan Reedy committer: gvanrossum date: 2020-10-23T20:59:33-07:00 summary: bpo-42139: Update What's New 3.9 for master (#22936) files: M Doc/whatsnew/3.9.rst diff --git a/Doc/whatsnew/3.9.rst b/Doc/whatsnew/3.9.rst index 89017981f6d49..1a37f16ea2b09 100644 --- a/Doc/whatsnew/3.9.rst +++ b/Doc/whatsnew/3.9.rst @@ -4,22 +4,21 @@ :Release: |release| :Date: |today| +:Editor: ?ukasz Langa .. Rules for maintenance: - * Anyone can add text to this document. Do not spend very much time - on the wording of your changes, because your text will probably - get rewritten to some degree. + * Anyone can add text to this document. Your text might get + rewritten to some degree. * The maintainer will go through Misc/NEWS periodically and add changes; it's therefore more important to add your changes to Misc/NEWS than to this file. * This is not a complete list of every single change; completeness - is the purpose of Misc/NEWS. Some changes I consider too small + is the purpose of Misc/NEWS. Some changes will be too small or esoteric to include. If such a change is added to the text, - I'll just remove it. (This is another reason you shouldn't spend - too much time on writing your addition.) + it might get removed during final editing. * If you want to draw your new text to the attention of the maintainer, add 'XXX' to the beginning of the paragraph or @@ -46,14 +45,13 @@ when researching a change. This article explains the new features in Python 3.9, compared to 3.8. +Python 3.9 was released on October 5th, 2020. For full details, see the :ref:`changelog `. -.. note:: +.. seealso:: - Prerelease users should be aware that this document is currently in draft - form. It will be updated substantially as Python 3.9 moves towards release, - so it's worth checking back even after reading earlier versions. + :pep:`596` - Python 3.9 Release Schedule Summary -- Release highlights @@ -62,27 +60,70 @@ Summary -- Release highlights .. This section singles out the most important changes in Python 3.9. Brevity is key. +New syntax features: + +* :pep:`584`, union operators added to ``dict``; +* :pep:`585`, type hinting generics in standard collections; +* :pep:`614`, relaxed grammar restrictions on decorators. + +New built-in features: + +* :pep:`616`, string methods to remove prefixes and suffixes. + +New features in the standard library: + +* :pep:`593`, flexible function and variable annotations; +* :func:`os.pidfd_open` added that allows process management without races + and signals. + +Interpreter improvements: + +* :pep:`573`, fast access to module state from methods of C extension + types; +* :pep:`617`, CPython now uses a new parser based on PEG; +* a number of Python builtins (range, tuple, set, frozenset, list, dict) are + now sped up using :pep:`590` vectorcall; +* garbage collection does not block on resurrected objects; +* a number of Python modules (:mod:`_abc`, :mod:`audioop`, :mod:`_bz2`, + :mod:`_codecs`, :mod:`_contextvars`, :mod:`_crypt`, :mod:`_functools`, + :mod:`_json`, :mod:`_locale`, :mod:`math`, :mod:`operator`, :mod:`resource`, + :mod:`time`, :mod:`_weakref`) now use multiphase initialization as defined + by PEP 489; +* a number of standard library modules (:mod:`audioop`, :mod:`ast`, :mod:`grp`, + :mod:`_hashlib`, :mod:`pwd`, :mod:`_posixsubprocess`, :mod:`random`, + :mod:`select`, :mod:`struct`, :mod:`termios`, :mod:`zlib`) are now using + the stable ABI defined by PEP 384. -.. PEP-sized items next. +New library modules: + +* :pep:`615`, the IANA Time Zone Database is now present in the standard + library in the :mod:`zoneinfo` module; +* an implementation of a topological sort of a graph is now provided in + the new :mod:`graphlib` module. + +Release process changes: + +* :pep:`602`, CPython adopts an annual release cycle. You should check for DeprecationWarning in your code ==================================================== -When Python 2.7 was still supported, many functions were kept for backward -compatibility with Python 2.7. With the end of Python 2.7 support, these -backward compatibility layers have been removed, or will be removed soon. -Most of them emitted a :exc:`DeprecationWarning` warning for several years. For -example, using ``collections.Mapping`` instead of ``collections.abc.Mapping`` -emits a :exc:`DeprecationWarning` since Python 3.3, released in 2012. +When Python 2.7 was still supported, a lot of functionality in Python 3 +was kept for backward compatibility with Python 2.7. With the end of Python +2 support, these backward compatibility layers have been removed, or will +be removed soon. Most of them emitted a :exc:`DeprecationWarning` warning for +several years. For example, using ``collections.Mapping`` instead of +``collections.abc.Mapping`` emits a :exc:`DeprecationWarning` since Python +3.3, released in 2012. Test your application with the :option:`-W` ``default`` command-line option to see :exc:`DeprecationWarning` and :exc:`PendingDeprecationWarning`, or even with :option:`-W` ``error`` to treat them as errors. :ref:`Warnings Filter ` can be used to ignore warnings from third-party code. -It has been decided to keep a few backward compatibility layers for one last -release, to give more time to Python projects maintainers to organize the +Python 3.9 is the last version providing those Python 2 backward compatibility +layers, to give more time to Python projects maintainers to organize the removal of the Python 2 support and add support for Python 3.9. Aliases to :ref:`Abstract Base Classes ` in @@ -94,6 +135,9 @@ More generally, try to run your tests in the :ref:`Python Development Mode ` which helps to prepare your code to make it compatible with the next Python version. +Note: a number of pre-existing deprecatations were removed in this version +of Python as well. Consult the :ref:`removed-in-python-39` section. + New Features ============ @@ -102,11 +146,23 @@ Dictionary Merge & Update Operators ----------------------------------- Merge (``|``) and update (``|=``) operators have been added to the built-in -:class:`dict` class. See :pep:`584` for a full description. +:class:`dict` class. Those complement the existing ``dict.update`` and +``{**d1, **d2}`` methods of merging dictionaries. + +Example:: + + >>> x = {"key1": "value1 from x", "key2": "value2 from x"} + >>> y = {"key2": "value2 from y", "key3": "value3 from y"} + >>> x | y + {'key1': 'value1 from x', 'key2': 'value2 from y', 'key3': 'value3 from y'} + >>> y | x + {'key2': 'value2 from x', 'key3': 'value3 from y', 'key1': 'value1 from x'} + +See :pep:`584` for a full description. (Contributed by Brandt Bucher in :issue:`36144`.) -PEP 616: New removeprefix() and removesuffix() string methods -------------------------------------------------------------- +New String Methods to Remove Prefixes and Suffixes +-------------------------------------------------- :meth:`str.removeprefix(prefix)` and :meth:`str.removesuffix(suffix)` have been added @@ -115,8 +171,8 @@ to easily remove an unneeded prefix or a suffix from a string. Corresponding added. See :pep:`616` for a full description. (Contributed by Dennis Sweeney in :issue:`39939`.) -PEP 585: Builtin Generic Types ------------------------------- +Type Hinting Generics in Standard Collections +--------------------------------------------- In type annotations you can now use built-in collection types such as ``list`` and ``dict`` as generic types instead of importing the @@ -135,8 +191,8 @@ Example: See :pep:`585` for more details. (Contributed by Guido van Rossum, Ethan Smith, and Batuhan Ta?kaya in :issue:`39481`.) -PEP 617: New Parser -------------------- +New Parser +---------- Python 3.9 uses a new parser, based on `PEG `_ instead @@ -167,7 +223,6 @@ Other Language Changes its top-level package. (Contributed by Ngalim Siregar in :issue:`37444`.) - * Python now gets the absolute path of the script filename specified on the command line (ex: ``python3 script.py``): the ``__file__`` attribute of the :mod:`__main__` module became an absolute path, rather than a relative @@ -201,6 +256,17 @@ Other Language Changes for the correspondent concrete type (``list`` in this case). (Contributed by Serhiy Storchaka in :issue:`40257`.) +* Parallel running of :meth:`~agen.aclose` / :meth:`~agen.asend` / + :meth:`~agen.athrow` is now prohibited, and ``ag_running`` now reflects + the actual running status of the async generator. + (Contributed by Yury Selivanov in :issue:`30773`.) + +* Unexpected errors in calling the ``__iter__`` method are no longer masked by + ``TypeError`` in the :keyword:`in` operator and functions + :func:`~operator.contains`, :func:`~operator.indexOf` and + :func:`~operator.countOf` of the :mod:`operator` module. + (Contributed by Serhiy Storchaka in :issue:`40824`.) + New Modules =========== @@ -248,9 +314,10 @@ PyPI and maintained by the CPython core team. graphlib --------- -Add the :mod:`graphlib` that contains the :class:`graphlib.TopologicalSorter` class -to offer functionality to perform topological sorting of graphs. (Contributed by Pablo -Galindo, Tim Peters and Larry Hastings in :issue:`17005`.) +A new module, :mod:`graphlib`, was added that contains the +:class:`graphlib.TopologicalSorter` class to offer functionality to perform +topological sorting of graphs. (Contributed by Pablo Galindo, Tim Peters and +Larry Hastings in :issue:`17005`.) Improved Modules @@ -296,6 +363,15 @@ loop, and essentially works as a high-level version of :meth:`~asyncio.loop.run_in_executor` that can directly take keyword arguments. (Contributed by Kyle Stanley and Yury Selivanov in :issue:`32309`.) +When cancelling the task due to a timeout, :meth:`asyncio.wait_for` will now +wait until the cancellation is complete also in the case when *timeout* is +<= 0, like it does with positive timeouts. +(Contributed by Elvis Pranskevichus in :issue:`32751`.) + +:mod:`asyncio` now raises :exc:`TyperError` when calling incompatible +methods with an :class:`ssl.SSLSocket` socket. +(Contributed by Ido Michael in :issue:`37404`.) + compileall ---------- @@ -328,7 +404,7 @@ startup overhead and reduces the amount of lost CPU time to idle workers. curses ------ -Add :func:`curses.get_escdelay`, :func:`curses.set_escdelay`, +Added :func:`curses.get_escdelay`, :func:`curses.set_escdelay`, :func:`curses.get_tabsize`, and :func:`curses.set_tabsize` functions. (Contributed by Anthony Sottile in :issue:`38312`.) @@ -375,12 +451,17 @@ finalized by the garbage collector. (Contributed by Pablo Galindo in hashlib ------- +The :mod:`hashlib` module can now use SHA3 hashes and SHAKE XOF from OpenSSL +when available. +(Contributed by Christian Heimes in :issue:`37630`.) + Builtin hash modules can now be disabled with ``./configure --without-builtin-hashlib-hashes`` or selectively enabled with e.g. ``./configure --with-builtin-hashlib-hashes=sha3,blake2`` to force use of OpenSSL based implementation. (Contributed by Christian Heimes in :issue:`40479`) + http ---- @@ -390,13 +471,13 @@ HTTP status codes ``103 EARLY_HINTS``, ``418 IM_A_TEAPOT`` and ``425 TOO_EARLY`` IDLE and idlelib ---------------- -Add option to toggle cursor blink off. (Contributed by Zackery Spytz +Added option to toggle cursor blink off. (Contributed by Zackery Spytz in :issue:`4603`.) Escape key now closes IDLE completion windows. (Contributed by Johnny Najera in :issue:`38944`.) -Add keywords to module name completion list. (Contributed by Terry J. +Added keywords to module name completion list. (Contributed by Terry J. Reedy in :issue:`37765`.) The changes above have been backported to 3.8 maintenance releases. @@ -426,6 +507,17 @@ now raises :exc:`ImportError` instead of :exc:`ValueError` for invalid relative import attempts. (Contributed by Ngalim Siregar in :issue:`37444`.) +Import loaders which publish immutable module objects can now publish +immutable packages in addition to individual modules. +(Contributed by Dino Viehland in :issue:`39336`.) + +Added :func:`importlib.resources.files` function with support for +subdirectories in package data, matching backport in ``importlib_resources`` +version 1.5. +(Contributed by Jason R. Coombs in :issue:`39791`.) + +Refreshed ``importlib.metadata`` from ``importlib_metadata`` version 1.6.1. + inspect ------- @@ -448,15 +540,15 @@ Expanded the :func:`math.gcd` function to handle multiple arguments. Formerly, it only supported two arguments. (Contributed by Serhiy Storchaka in :issue:`39648`.) -Add :func:`math.lcm`: return the least common multiple of specified arguments. +Added :func:`math.lcm`: return the least common multiple of specified arguments. (Contributed by Mark Dickinson, Ananthakrishnan and Serhiy Storchaka in :issue:`39479` and :issue:`39648`.) -Add :func:`math.nextafter`: return the next floating-point value after *x* +Added :func:`math.nextafter`: return the next floating-point value after *x* towards *y*. (Contributed by Victor Stinner in :issue:`39288`.) -Add :func:`math.ulp`: return the value of the least significant bit +Added :func:`math.ulp`: return the value of the least significant bit of a float. (Contributed by Victor Stinner in :issue:`39310`.) @@ -492,7 +584,7 @@ The :func:`os.putenv` and :func:`os.unsetenv` functions are now always available. (Contributed by Victor Stinner in :issue:`39395`.) -Add :func:`os.waitstatus_to_exitcode` function: +Added :func:`os.waitstatus_to_exitcode` function: convert a wait status to an exit code. (Contributed by Victor Stinner in :issue:`40094`.) @@ -503,6 +595,12 @@ Added :meth:`pathlib.Path.readlink()` which acts similarly to :func:`os.readlink`. (Contributed by Girts Folkmanis in :issue:`30618`) +pdb +--- + +On Windows now :class:`~pdb.Pdb` supports ``~/.pdbrc``. +(Contributed by Tim Hopper and Dan Lidral-Porter in :issue:`20523`.) + poplib ------ @@ -526,7 +624,7 @@ method etc, but for any object that has its own ``__doc__`` attribute. random ------ -Add a new :attr:`random.Random.randbytes` method: generate random bytes. +Added a new :attr:`random.Random.randbytes` method: generate random bytes. (Contributed by Victor Stinner in :issue:`40286`.) signal @@ -555,6 +653,11 @@ constant on Linux 4.1 and greater. The socket module now supports the :data:`~socket.CAN_J1939` protocol on platforms that support it. (Contributed by Karl Ding in :issue:`40291`.) +The socket module now has the :func:`socket.send_fds` and +:func:`socket.recv.fds` methods. (Contributed by Joannah Nanjekye, Shinya +Okano and Victor Stinner in :issue:`28724`.) + + time ---- @@ -566,7 +669,7 @@ which has nanosecond resolution, rather than sys --- -Add a new :attr:`sys.platlibdir` attribute: name of the platform-specific +Added a new :attr:`sys.platlibdir` attribute: name of the platform-specific library directory. It is used to build the path of standard library and the paths of installed extension modules. It is equal to ``"lib"`` on most platforms. On Fedora and SuSE, it is equal to ``"lib64"`` on 64-bit platforms. @@ -630,7 +733,7 @@ Optimizations (Contributed by Serhiy Storchaka in :issue:`32856`.) -* Optimize signal handling in multithreaded applications. If a thread different +* Optimized signal handling in multithreaded applications. If a thread different than the main thread gets a signal, the bytecode evaluation loop is no longer interrupted at each bytecode instruction to check for pending signals which cannot be handled. Only the main thread of the main interpreter can handle @@ -640,10 +743,36 @@ Optimizations until the main thread handles signals. (Contributed by Victor Stinner in :issue:`40010`.) -* Optimize the :mod:`subprocess` module on FreeBSD using ``closefrom()``. +* Optimized the :mod:`subprocess` module on FreeBSD using ``closefrom()``. (Contributed by Ed Maste, Conrad Meyer, Kyle Evans, Kubilay Kocak and Victor Stinner in :issue:`38061`.) +* :c:func:`PyLong_FromDouble` is now up to 1.87x faster for values that + fit into :c:type:`long`. + (Contributed by Sergey Fedoseev in :issue:`37986`.) + +* A number of Python builtins (:class:`range`, :class:`tuple`, :class:`set`, + :class:`frozenset`, :class:`list`, :class:`dict`) are now sped up by using + :pep:`590` vectorcall protocol. + (Contributed by Dong-hee Na, Mark Shannon, Jeroen Demeyer and Petr Viktorin in :issue:`37207`.) + +* Optimized :func:`~set.difference_update` for the case when the other set + is much larger than the base set. + (Suggested by Evgeny Kapun with code contributed by Michele Orr? in :issue:`8425`.) + +* Python's small object allocator (``obmalloc.c``) now allows (no more than) + one empty arena to remain available for immediate reuse, without returning + it to the OS. This prevents thrashing in simple loops where an arena could + be created and destroyed anew on each iteration. + (Contributed by Tim Peters in :issue:`37257`.) + +* :term:`floor division` of float operation now has a better performance. Also + the message of :exc:`ZeroDivisionError` for this operation is updated. + (Contributed by Dong-hee Na in :issue:`39434`.) + +* Decoding short ASCII strings with UTF-8 and ascii codecs is now about + 15% faster. (Contributed by Inada Naoki in :issue:`37348`.) + Here's a summary of performance improvements from Python 3.4 through Python 3.9: .. code-block:: none @@ -699,10 +828,6 @@ in nanoseconds. The benchmarks were measured on an running the macOS 64-bit builds found at `python.org `_. -* A number of Python builtins (:class:`range`, :class:`tuple`, :class:`set`, :class:`frozenset`, :class:`list`, :class:`dict`) - are now sped up by using :pep:`590` vectorcall protocol. - (Contributed by Dong-hee Na, Mark Shannon, Jeroen Demeyer and Petr Viktorin in :issue:`37207`.) - Deprecated ========== @@ -784,6 +909,9 @@ Deprecated * Passing ``None`` as the first argument to the :func:`shlex.split` function has been deprecated. (Contributed by Zackery Spytz in :issue:`33262`.) +* :func:`smtpd.MailmanProxy` is now deprecated as it is unusable without + an external module, ``mailman``. (Contributed by Samuel Colvin in :issue:`35800`.) + * The :mod:`lib2to3` module now emits a :exc:`PendingDeprecationWarning`. Python 3.9 switched to a PEG parser (see :pep:`617`), and Python 3.10 may include new language syntax that is not parsable by lib2to3's LL(1) parser. @@ -798,6 +926,8 @@ Deprecated .. _LibCST: https://libcst.readthedocs.io/ .. _parso: https://parso.readthedocs.io/ +.. _removed-in-python-39: + Removed ======= @@ -967,6 +1097,19 @@ Changes in the Python API of ``wchar_t`` since Python 3.3. (Contributed by Inada Naoki in :issue:`34538`.) +* The :func:`logging.getLogger` API now returns the root logger when passed + the name ``'root'``, whereas previously it returned a non-root logger named + ``'root'``. This could affect cases where user code explicitly wants a + non-root logger named ``'root'``, or instantiates a logger using + ``logging.getLogger(__name__)`` in some top-level module called ``'root.py'``. + (Contributed by Vinay Sajip in :issue:`37742`.) + +* Division handling of :class:`~pathlib.PurePath` now returns ``NotImplemented`` + instead of raising a :exc:`TypeError` when passed something other than an + instance of ``str`` or :class:`~pathlib.PurePath`. This allows creating + compatible classes that don't inherit from those mentioned types. + (Contributed by Roger Aiudi in :issue:`34775`). + Changes in the C API -------------------- @@ -1017,6 +1160,11 @@ Changes in the C API (See :issue:`35810` and :issue:`40217` for more information.) +* The functions ``PyEval_CallObject``, ``PyEval_CallFunction``, + ``PyEval_CallMethod`` and ``PyEval_CallObjectWithKeywords`` are deprecated. + Use :c:func:`PyObject_Call` and its variants instead. + (See more details in :issue:`29548`.) + CPython bytecode changes ------------------------ @@ -1025,11 +1173,21 @@ CPython bytecode changes correctly if the :exc:`AssertionError` exception was being shadowed. (Contributed by Zackery Spytz in :issue:`34880`.) +* The :opcode:`COMPARE_OP` opcode was split into four distinct instructions: + + * ``COMPARE_OP`` for rich comparisons + * ``IS_OP`` for 'is' and 'is not' tests + * ``CONTAINS_OP`` for 'in' and 'not in' tests + * ``JUMP_IF_NOT_EXC_MATCH`` for checking exceptions in 'try-except' + statements. + + (Contributed by Mark Shannon in :issue:`39156`.) + Build Changes ============= -* Add ``--with-platlibdir`` option to the ``configure`` script: name of the +* Added ``--with-platlibdir`` option to the ``configure`` script: name of the platform-specific library directory, stored in the new :attr:`sys.platlibdir` attribute. See :attr:`sys.platlibdir` attribute for more information. (Contributed by Jan Mat?jek, Mat?j Cepl, Charalampos Stratakis @@ -1042,6 +1200,34 @@ Build Changes functions are now required to build Python. (Contributed by Victor Stinner in :issue:`39395`.) +* On non-Windows platforms, creating ``bdist_wininst`` installers is now + officially unsupported. (See :issue:`10945` for more details.) + +* When building Python on macOS from source, ``_tkinter`` now links with + non-system Tcl and Tk frameworks if they are installed in + ``/Library/Frameworks``, as had been the case on older releases + of macOS. If a macOS SDK is explicitly configured, by using + ``--enable-universalsdk=`` or ``-isysroot``, only the SDK itself is + searched. The default behavior can still be overridden with + ``--with-tcltk-includes`` and ``--with-tcltk-libs``. + (Contributed by Ned Deily in :issue:`34956`.) + +* Python can now be built for Windows 10 ARM64. + (Contributed by Steve Dower in :issue:`33125`.) + +* Some individual tests are now skipped when ``--pgo`` is used. The tests + in question increased the PGO task time significantly and likely + didn't help improve optimization of the final executable. This + speeds up the task by a factor of about 15x. Running the full unit test + suite is slow. This change may result in a slightly less optimized build + since not as many code branches will be executed. If you are willing to + wait for the much slower build, the old behavior can be restored using + ``./configure [..] PROFILE_TASK="-m test --pgo-extended"``. We make no + guarantees as to which PGO task set produces a faster build. Users who care + should run their own relevant benchmarks as results can depend on the + environment, workload, and compiler tool chain. + (See :issue:`36044` and :issue:`37707` for more details.) + C API Changes ============= @@ -1049,29 +1235,29 @@ C API Changes New Features ------------ -* :pep:`573`: Add :c:func:`PyType_FromModuleAndSpec` to associate +* :pep:`573`: Added :c:func:`PyType_FromModuleAndSpec` to associate a module with a class; :c:func:`PyType_GetModule` and :c:func:`PyType_GetModuleState` to retrieve the module and its state; and :c:data:`PyCMethod` and :c:data:`METH_METHOD` to allow a method to access the class it was defined in. (Contributed by Marcel Plch and Petr Viktorin in :issue:`38787`.) -* Add :c:func:`PyFrame_GetCode` function: get a frame code. - Add :c:func:`PyFrame_GetBack` function: get the frame next outer frame. +* Added :c:func:`PyFrame_GetCode` function: get a frame code. + Added :c:func:`PyFrame_GetBack` function: get the frame next outer frame. (Contributed by Victor Stinner in :issue:`40421`.) -* Add :c:func:`PyFrame_GetLineNumber` to the limited C API. +* Added :c:func:`PyFrame_GetLineNumber` to the limited C API. (Contributed by Victor Stinner in :issue:`40421`.) -* Add :c:func:`PyThreadState_GetInterpreter` and +* Added :c:func:`PyThreadState_GetInterpreter` and :c:func:`PyInterpreterState_Get` functions to get the interpreter. - Add :c:func:`PyThreadState_GetFrame` function to get the current frame of a + Added :c:func:`PyThreadState_GetFrame` function to get the current frame of a Python thread state. - Add :c:func:`PyThreadState_GetID` function: get the unique identifier of a + Added :c:func:`PyThreadState_GetID` function: get the unique identifier of a Python thread state. (Contributed by Victor Stinner in :issue:`39947`.) -* Add a new public :c:func:`PyObject_CallNoArgs` function to the C API, which +* Added a new public :c:func:`PyObject_CallNoArgs` function to the C API, which calls a callable Python object without any arguments. It is the most efficient way to call a callable Python object without any argument. (Contributed by Victor Stinner in :issue:`37194`.) @@ -1093,11 +1279,15 @@ New Features to a module. (Contributed by Dong-hee Na in :issue:`40024`.) -* Add the functions :c:func:`PyObject_GC_IsTracked` and +* Added the functions :c:func:`PyObject_GC_IsTracked` and :c:func:`PyObject_GC_IsFinalized` to the public API to allow to query if Python objects are being currently tracked or have been already finalized by - the garbage collector respectively. (Contributed by Pablo Galindo in - :issue:`40241`.) + the garbage collector respectively. + (Contributed by Pablo Galindo Salgado in :issue:`40241`.) + +* Added :c:func:`_PyObject_FunctionStr` to get a user-friendly string + representation of a function-like object. + (Patch by Jeroen Demeyer in :issue:`37645`.) Porting to Python 3.9 @@ -1144,10 +1334,44 @@ Porting to Python 3.9 Python 3.3. (Contributed by Inada Naoki in :issue:`36346`.) +* The :c:func:`Py_FatalError` function is replaced with a macro which logs + automatically the name of the current function, unless the + ``Py_LIMITED_API`` macro is defined. + (Contributed by Victor Stinner in :issue:`39882`.) + +* The vectorcall protocol now requires that the caller passes only strings as + keyword names. (See :issue:`37540` for more information.) + +* Implementation details of a number of macros and functions are now hidden: + + * :c:func:`PyObject_IS_GC` macro was converted to a function. + + * The :c:func:`PyObject_NEW` macro becomes an alias to the + :c:func:`PyObject_New` macro, and the :c:func:`PyObject_NEW_VAR` macro + becomes an alias to the :c:func:`PyObject_NewVar` macro. They no longer + access directly the :c:member:`PyTypeObject.tp_basicsize` member. + + * :c:func:`PyType_HasFeature` now always calls :c:func:`PyType_GetFlags`. + Previously, it accessed directly the :c:member:`PyTypeObject.tp_flags` + member when the limited C API was not used. + + * :c:func:`PyObject_GET_WEAKREFS_LISTPTR` macro was converted to a function: + the macro accessed directly the :c:member:`PyTypeObject.tp_weaklistoffset` + member. + + * :c:func:`PyObject_CheckBuffer` macro was converted to a function: the macro + accessed directly the :c:member:`PyTypeObject.tp_as_buffer` member. + + * :c:func:`PyIndex_Check` is now always declared as an opaque function to hide + implementation details: removed the ``PyIndex_Check()`` macro. The macro accessed + directly the :c:member:`PyTypeObject.tp_as_number` member. + + (See :issue:`40170` for more details.) + Removed ------- -* Exclude ``PyFPE_START_PROTECT()`` and ``PyFPE_END_PROTECT()`` macros of +* Excluded ``PyFPE_START_PROTECT()`` and ``PyFPE_END_PROTECT()`` macros of ``pyfpe.h`` from the limited C API. (Contributed by Victor Stinner in :issue:`38835`.) @@ -1158,7 +1382,7 @@ Removed * Changes in the limited C API (if ``Py_LIMITED_API`` macro is defined): - * Exclude the following functions from the limited C API: + * Excluded the following functions from the limited C API: * ``PyThreadState_DeleteCurrent()`` (Contributed by Joannah Nanjekye in :issue:`37878`.) @@ -1175,7 +1399,7 @@ Removed * ``Py_TRASHCAN_SAFE_BEGIN`` * ``Py_TRASHCAN_SAFE_END`` - * Move following functions and definitions to the internal C API: + * Moved following functions and definitions to the internal C API: * ``_PyDebug_PrintTotalRefs()`` * ``_Py_PrintReferences()`` @@ -1185,12 +1409,12 @@ Removed (Contributed by Victor Stinner in :issue:`38644` and :issue:`39542`.) -* Remove ``_PyRuntime.getframe`` hook and remove ``_PyThreadState_GetFrame`` +* Removed ``_PyRuntime.getframe`` hook and removed ``_PyThreadState_GetFrame`` macro which was an alias to ``_PyRuntime.getframe``. They were only exposed - by the internal C API. Remove also ``PyThreadFrameGetter`` type. + by the internal C API. Removed also ``PyThreadFrameGetter`` type. (Contributed by Victor Stinner in :issue:`39946`.) -* Remove the following functions from the C API. Call :c:func:`PyGC_Collect` +* Removed the following functions from the C API. Call :c:func:`PyGC_Collect` explicitly to clear all free lists. (Contributed by Inada Naoki and Victor Stinner in :issue:`37340`, :issue:`38896` and :issue:`40428`.) @@ -1209,10 +1433,20 @@ Removed * ``PyUnicode_ClearFreeList()``: the Unicode free list has been removed in Python 3.3. -* Remove ``_PyUnicode_ClearStaticStrings()`` function. +* Removed ``_PyUnicode_ClearStaticStrings()`` function. (Contributed by Victor Stinner in :issue:`39465`.) -* Remove ``Py_UNICODE_MATCH``. It has been deprecated by :pep:`393`, and +* Removed ``Py_UNICODE_MATCH``. It has been deprecated by :pep:`393`, and broken since Python 3.3. The :c:func:`PyUnicode_Tailmatch` function can be used instead. (Contributed by Inada Naoki in :issue:`36346`.) + +* Cleaned header files of interfaces defined but with no implementation. + The public API symbols being removed are: + ``_PyBytes_InsertThousandsGroupingLocale``, + ``_PyBytes_InsertThousandsGrouping``, ``_Py_InitializeFromArgs``, + ``_Py_InitializeFromWideArgs``, ``_PyFloat_Repr``, ``_PyFloat_Digits``, + ``_PyFloat_DigitsInit``, ``PyFrame_ExtendStack``, ``_PyAIterWrapper_Type``, + ``PyNullImporter_Type``, ``PyCmpWrapper_Type``, ``PySortWrapper_Type``, + ``PyNoArgsFunction``. + (Contributed by Pablo Galindo Salgado in :issue:`39372`.) From webhook-mailer at python.org Sat Oct 24 10:13:20 2020 From: webhook-mailer at python.org (Mariatta) Date: Sat, 24 Oct 2020 14:13:20 -0000 Subject: [Python-checkins] Automatically add skip news and skip issue label to dependabot PRs (GH-22927) Message-ID: https://github.com/python/cpython/commit/2455cb5dbec06dbd1c4486fbdbaaade51546428b commit: 2455cb5dbec06dbd1c4486fbdbaaade51546428b branch: master author: Mariatta committer: Mariatta date: 2020-10-24T07:12:55-07:00 summary: Automatically add skip news and skip issue label to dependabot PRs (GH-22927) Closes https://github.com/python/bedevere/issues/287 files: M .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 8ac6b8c4984dc..e90677b9f775a 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,3 +4,6 @@ updates: directory: "/" schedule: interval: "monthly" + labels: + - "skip issue" + - "skip news" From webhook-mailer at python.org Sat Oct 24 10:29:21 2020 From: webhook-mailer at python.org (tirkarthi) Date: Sat, 24 Oct 2020 14:29:21 -0000 Subject: [Python-checkins] [doc] Fix link to abc.ABCMeta.register in Glossary (GH-22932) Message-ID: https://github.com/python/cpython/commit/e01e442125bbc98e6dab66f38ecc6c45f69e6587 commit: e01e442125bbc98e6dab66f38ecc6c45f69e6587 branch: master author: Andre Delfino committer: tirkarthi date: 2020-10-24T19:59:17+05:30 summary: [doc] Fix link to abc.ABCMeta.register in Glossary (GH-22932) files: M Doc/glossary.rst diff --git a/Doc/glossary.rst b/Doc/glossary.rst index 32aa12a200f63..847500e556056 100644 --- a/Doc/glossary.rst +++ b/Doc/glossary.rst @@ -1031,7 +1031,7 @@ Glossary :meth:`index`, :meth:`__contains__`, and :meth:`__reversed__`. Types that implement this expanded interface can be registered explicitly using - :func:`~abc.register`. + :func:`~abc.ABCMeta.register`. set comprehension A compact way to process all or part of the elements in an iterable and From webhook-mailer at python.org Sat Oct 24 13:47:45 2020 From: webhook-mailer at python.org (gpshead) Date: Sat, 24 Oct 2020 17:47:45 -0000 Subject: [Python-checkins] bpo-35823: subprocess: Fix handling of pthread_sigmask() errors (GH-22944) Message-ID: https://github.com/python/cpython/commit/473db47747bb8bc986d88ad81799bcbd88153ac5 commit: 473db47747bb8bc986d88ad81799bcbd88153ac5 branch: master author: Alexey Izbyshev committer: gpshead date: 2020-10-24T10:47:38-07:00 summary: bpo-35823: subprocess: Fix handling of pthread_sigmask() errors (GH-22944) Using POSIX_CALL() is incorrect since pthread_sigmask() returns the error number instead of setting errno. Also handle failure of the first call to pthread_sigmask() in the parent process, and explain why we don't handle failure of the second call in a comment. files: M Modules/_posixsubprocess.c diff --git a/Modules/_posixsubprocess.c b/Modules/_posixsubprocess.c index ed498572a828b..b7cba30ee7618 100644 --- a/Modules/_posixsubprocess.c +++ b/Modules/_posixsubprocess.c @@ -581,7 +581,9 @@ child_exec(char *const exec_array[], #ifdef VFORK_USABLE if (child_sigmask) { reset_signal_handlers(child_sigmask); - POSIX_CALL(pthread_sigmask(SIG_SETMASK, child_sigmask, NULL)); + if ((errno = pthread_sigmask(SIG_SETMASK, child_sigmask, NULL))) { + goto error; + } } #endif @@ -1007,7 +1009,11 @@ subprocess_fork_exec(PyObject* self, PyObject *args) */ sigset_t all_sigs; sigfillset(&all_sigs); - pthread_sigmask(SIG_BLOCK, &all_sigs, &old_sigs); + if ((saved_errno = pthread_sigmask(SIG_BLOCK, &all_sigs, &old_sigs))) { + errno = saved_errno; + PyErr_SetFromErrno(PyExc_OSError); + goto cleanup; + } old_sigmask = &old_sigs; } #endif @@ -1034,8 +1040,13 @@ subprocess_fork_exec(PyObject* self, PyObject *args) * Note that in environments where vfork() is implemented as fork(), * such as QEMU user-mode emulation, the parent won't be blocked, * but it won't share the address space with the child, - * so it's still safe to unblock the signals. */ - pthread_sigmask(SIG_SETMASK, old_sigmask, NULL); + * so it's still safe to unblock the signals. + * + * We don't handle errors here because this call can't fail + * if valid arguments are given, and because there is no good + * way for the caller to deal with a failure to restore + * the thread signal mask. */ + (void) pthread_sigmask(SIG_SETMASK, old_sigmask, NULL); } #endif From webhook-mailer at python.org Sat Oct 24 14:14:27 2020 From: webhook-mailer at python.org (serhiy-storchaka) Date: Sat, 24 Oct 2020 18:14:27 -0000 Subject: [Python-checkins] bpo-41052: Fix pickling heap types implemented in C with protocols 0 and 1 (GH-22870) Message-ID: https://github.com/python/cpython/commit/8cd1dbae32d9303caac3a473d3332f17bc98c921 commit: 8cd1dbae32d9303caac3a473d3332f17bc98c921 branch: master author: Serhiy Storchaka committer: serhiy-storchaka date: 2020-10-24T21:14:23+03:00 summary: bpo-41052: Fix pickling heap types implemented in C with protocols 0 and 1 (GH-22870) files: A Misc/NEWS.d/next/Library/2020-10-21-23-45-02.bpo-41052.3N7J2J.rst M Lib/copyreg.py M Lib/test/pickletester.py M Modules/_bz2module.c M Modules/_lzmamodule.c M Modules/_randommodule.c M Modules/clinic/_bz2module.c.h M Modules/clinic/_lzmamodule.c.h M Modules/clinic/_randommodule.c.h diff --git a/Lib/copyreg.py b/Lib/copyreg.py index dfc463c49a389..7ab8c128eb044 100644 --- a/Lib/copyreg.py +++ b/Lib/copyreg.py @@ -48,6 +48,7 @@ def _reconstructor(cls, base, state): return obj _HEAPTYPE = 1<<9 +_new_type = type(int.__new__) # Python code for object.__reduce_ex__ for protocols 0 and 1 @@ -57,6 +58,9 @@ def _reduce_ex(self, proto): for base in cls.__mro__: if hasattr(base, '__flags__') and not base.__flags__ & _HEAPTYPE: break + new = base.__new__ + if isinstance(new, _new_type) and new.__self__ is base: + break else: base = object # not really reachable if base is object: diff --git a/Lib/test/pickletester.py b/Lib/test/pickletester.py index fb972a3ba5e9b..ae288f5d01250 100644 --- a/Lib/test/pickletester.py +++ b/Lib/test/pickletester.py @@ -1969,6 +1969,17 @@ def test_newobj_proxies(self): self.assertEqual(B(x), B(y), detail) self.assertEqual(x.__dict__, y.__dict__, detail) + def test_newobj_overridden_new(self): + # Test that Python class with C implemented __new__ is pickleable + for proto in protocols: + x = MyIntWithNew2(1) + x.foo = 42 + s = self.dumps(x, proto) + y = self.loads(s) + self.assertIs(type(y), MyIntWithNew2) + self.assertEqual(int(y), 1) + self.assertEqual(y.foo, 42) + def test_newobj_not_class(self): # Issue 24552 global SimpleNewObj @@ -3089,6 +3100,13 @@ class MyFrozenSet(frozenset): MyStr, MyUnicode, MyTuple, MyList, MyDict, MySet, MyFrozenSet] +class MyIntWithNew(int): + def __new__(cls, value): + raise AssertionError + +class MyIntWithNew2(MyIntWithNew): + __new__ = int.__new__ + class SlotList(MyList): __slots__ = ["foo"] diff --git a/Misc/NEWS.d/next/Library/2020-10-21-23-45-02.bpo-41052.3N7J2J.rst b/Misc/NEWS.d/next/Library/2020-10-21-23-45-02.bpo-41052.3N7J2J.rst new file mode 100644 index 0000000000000..528e90ed13493 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-10-21-23-45-02.bpo-41052.3N7J2J.rst @@ -0,0 +1,2 @@ +Pickling heap types implemented in C with protocols 0 and 1 raises now an +error instead of producing incorrect data. diff --git a/Modules/_bz2module.c b/Modules/_bz2module.c index effb0de2e6536..bfcdac692461f 100644 --- a/Modules/_bz2module.c +++ b/Modules/_bz2module.c @@ -272,21 +272,6 @@ _bz2_BZ2Compressor_flush_impl(BZ2Compressor *self) return result; } -/*[clinic input] -_bz2.BZ2Compressor.__reduce__ - -[clinic start generated code]*/ - -static PyObject * -_bz2_BZ2Compressor___reduce___impl(BZ2Compressor *self) -/*[clinic end generated code: output=d13db66ae043e141 input=e09bccef0e6731b2]*/ -{ - PyErr_Format(PyExc_TypeError, - "cannot pickle %s object", - Py_TYPE(self)->tp_name); - return NULL; -} - static void* BZ2_Malloc(void* ctx, int items, int size) { @@ -399,7 +384,6 @@ BZ2Compressor_traverse(BZ2Compressor *self, visitproc visit, void *arg) static PyMethodDef BZ2Compressor_methods[] = { _BZ2_BZ2COMPRESSOR_COMPRESS_METHODDEF _BZ2_BZ2COMPRESSOR_FLUSH_METHODDEF - _BZ2_BZ2COMPRESSOR___REDUCE___METHODDEF {NULL} }; @@ -642,21 +626,6 @@ _bz2_BZ2Decompressor_decompress_impl(BZ2Decompressor *self, Py_buffer *data, return result; } -/*[clinic input] -_bz2.BZ2Decompressor.__reduce__ - -[clinic start generated code]*/ - -static PyObject * -_bz2_BZ2Decompressor___reduce___impl(BZ2Decompressor *self) -/*[clinic end generated code: output=f6a40650813f482e input=8db9175a609fdd43]*/ -{ - PyErr_Format(PyExc_TypeError, - "cannot pickle %s object", - Py_TYPE(self)->tp_name); - return NULL; -} - /* Argument Clinic is not used since the Argument Clinic always want to check the type which would be wrong here */ static int @@ -746,7 +715,6 @@ BZ2Decompressor_traverse(BZ2Decompressor *self, visitproc visit, void *arg) static PyMethodDef BZ2Decompressor_methods[] = { _BZ2_BZ2DECOMPRESSOR_DECOMPRESS_METHODDEF - _BZ2_BZ2DECOMPRESSOR___REDUCE___METHODDEF {NULL} }; diff --git a/Modules/_lzmamodule.c b/Modules/_lzmamodule.c index 24e1d6c2884ce..b01f630009813 100644 --- a/Modules/_lzmamodule.c +++ b/Modules/_lzmamodule.c @@ -825,24 +825,9 @@ Compressor_dealloc(Compressor *self) Py_DECREF(tp); } -/*[clinic input] -_lzma.LZMACompressor.__reduce__ -[clinic start generated code]*/ - -static PyObject * -_lzma_LZMACompressor___reduce___impl(Compressor *self) -/*[clinic end generated code: output=b49a0538d1cad752 input=6be52aba16b513c1]*/ -{ - PyErr_Format(PyExc_TypeError, - "cannot pickle %s object", - Py_TYPE(self)->tp_name); - return NULL; -} - static PyMethodDef Compressor_methods[] = { _LZMA_LZMACOMPRESSOR_COMPRESS_METHODDEF _LZMA_LZMACOMPRESSOR_FLUSH_METHODDEF - _LZMA_LZMACOMPRESSOR___REDUCE___METHODDEF {NULL} }; @@ -1309,23 +1294,8 @@ Decompressor_traverse(Decompressor *self, visitproc visit, void *arg) return 0; } -/*[clinic input] -_lzma.LZMADecompressor.__reduce__ -[clinic start generated code]*/ - -static PyObject * -_lzma_LZMADecompressor___reduce___impl(Decompressor *self) -/*[clinic end generated code: output=2611fff0104a9c30 input=b9882e030aecd9a5]*/ -{ - PyErr_Format(PyExc_TypeError, - "cannot pickle %s object", - Py_TYPE(self)->tp_name); - return NULL; -} - static PyMethodDef Decompressor_methods[] = { _LZMA_LZMADECOMPRESSOR_DECOMPRESS_METHODDEF - _LZMA_LZMADECOMPRESSOR___REDUCE___METHODDEF {NULL} }; diff --git a/Modules/_randommodule.c b/Modules/_randommodule.c index b8bc0449c1b1b..d33870808622f 100644 --- a/Modules/_randommodule.c +++ b/Modules/_randommodule.c @@ -536,29 +536,12 @@ random_new(PyTypeObject *type, PyObject *args, PyObject *kwds) } -/*[clinic input] - -_random.Random.__reduce__ - -[clinic start generated code]*/ - -static PyObject * -_random_Random___reduce___impl(RandomObject *self) -/*[clinic end generated code: output=ddea0dcdb60ffd6d input=bd38ec35fd157e0f]*/ -{ - PyErr_Format(PyExc_TypeError, - "cannot pickle %s object", - Py_TYPE(self)->tp_name); - return NULL; -} - static PyMethodDef random_methods[] = { _RANDOM_RANDOM_RANDOM_METHODDEF _RANDOM_RANDOM_SEED_METHODDEF _RANDOM_RANDOM_GETSTATE_METHODDEF _RANDOM_RANDOM_SETSTATE_METHODDEF _RANDOM_RANDOM_GETRANDBITS_METHODDEF - _RANDOM_RANDOM___REDUCE___METHODDEF {NULL, NULL} /* sentinel */ }; diff --git a/Modules/clinic/_bz2module.c.h b/Modules/clinic/_bz2module.c.h index ff67d34155dfd..71ad0b135718a 100644 --- a/Modules/clinic/_bz2module.c.h +++ b/Modules/clinic/_bz2module.c.h @@ -65,23 +65,6 @@ _bz2_BZ2Compressor_flush(BZ2Compressor *self, PyObject *Py_UNUSED(ignored)) return _bz2_BZ2Compressor_flush_impl(self); } -PyDoc_STRVAR(_bz2_BZ2Compressor___reduce____doc__, -"__reduce__($self, /)\n" -"--\n" -"\n"); - -#define _BZ2_BZ2COMPRESSOR___REDUCE___METHODDEF \ - {"__reduce__", (PyCFunction)_bz2_BZ2Compressor___reduce__, METH_NOARGS, _bz2_BZ2Compressor___reduce____doc__}, - -static PyObject * -_bz2_BZ2Compressor___reduce___impl(BZ2Compressor *self); - -static PyObject * -_bz2_BZ2Compressor___reduce__(BZ2Compressor *self, PyObject *Py_UNUSED(ignored)) -{ - return _bz2_BZ2Compressor___reduce___impl(self); -} - PyDoc_STRVAR(_bz2_BZ2Decompressor_decompress__doc__, "decompress($self, /, data, max_length=-1)\n" "--\n" @@ -156,21 +139,4 @@ _bz2_BZ2Decompressor_decompress(BZ2Decompressor *self, PyObject *const *args, Py return return_value; } - -PyDoc_STRVAR(_bz2_BZ2Decompressor___reduce____doc__, -"__reduce__($self, /)\n" -"--\n" -"\n"); - -#define _BZ2_BZ2DECOMPRESSOR___REDUCE___METHODDEF \ - {"__reduce__", (PyCFunction)_bz2_BZ2Decompressor___reduce__, METH_NOARGS, _bz2_BZ2Decompressor___reduce____doc__}, - -static PyObject * -_bz2_BZ2Decompressor___reduce___impl(BZ2Decompressor *self); - -static PyObject * -_bz2_BZ2Decompressor___reduce__(BZ2Decompressor *self, PyObject *Py_UNUSED(ignored)) -{ - return _bz2_BZ2Decompressor___reduce___impl(self); -} -/*[clinic end generated code: output=001f31fdacb4cb01 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=ed10705d7a9fd598 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_lzmamodule.c.h b/Modules/clinic/_lzmamodule.c.h index e15cc0c7e743b..526031ade3b96 100644 --- a/Modules/clinic/_lzmamodule.c.h +++ b/Modules/clinic/_lzmamodule.c.h @@ -65,23 +65,6 @@ _lzma_LZMACompressor_flush(Compressor *self, PyObject *Py_UNUSED(ignored)) return _lzma_LZMACompressor_flush_impl(self); } -PyDoc_STRVAR(_lzma_LZMACompressor___reduce____doc__, -"__reduce__($self, /)\n" -"--\n" -"\n"); - -#define _LZMA_LZMACOMPRESSOR___REDUCE___METHODDEF \ - {"__reduce__", (PyCFunction)_lzma_LZMACompressor___reduce__, METH_NOARGS, _lzma_LZMACompressor___reduce____doc__}, - -static PyObject * -_lzma_LZMACompressor___reduce___impl(Compressor *self); - -static PyObject * -_lzma_LZMACompressor___reduce__(Compressor *self, PyObject *Py_UNUSED(ignored)) -{ - return _lzma_LZMACompressor___reduce___impl(self); -} - PyDoc_STRVAR(_lzma_LZMADecompressor_decompress__doc__, "decompress($self, /, data, max_length=-1)\n" "--\n" @@ -228,23 +211,6 @@ _lzma_LZMADecompressor___init__(PyObject *self, PyObject *args, PyObject *kwargs return return_value; } -PyDoc_STRVAR(_lzma_LZMADecompressor___reduce____doc__, -"__reduce__($self, /)\n" -"--\n" -"\n"); - -#define _LZMA_LZMADECOMPRESSOR___REDUCE___METHODDEF \ - {"__reduce__", (PyCFunction)_lzma_LZMADecompressor___reduce__, METH_NOARGS, _lzma_LZMADecompressor___reduce____doc__}, - -static PyObject * -_lzma_LZMADecompressor___reduce___impl(Decompressor *self); - -static PyObject * -_lzma_LZMADecompressor___reduce__(Decompressor *self, PyObject *Py_UNUSED(ignored)) -{ - return _lzma_LZMADecompressor___reduce___impl(self); -} - PyDoc_STRVAR(_lzma_is_check_supported__doc__, "is_check_supported($module, check_id, /)\n" "--\n" @@ -320,4 +286,4 @@ _lzma__decode_filter_properties(PyObject *module, PyObject *const *args, Py_ssiz return return_value; } -/*[clinic end generated code: output=d89b6159e98544be input=a9049054013a1b77]*/ +/*[clinic end generated code: output=867b9e334053b679 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_randommodule.c.h b/Modules/clinic/_randommodule.c.h index 3322a370288c3..b3cd435b6f204 100644 --- a/Modules/clinic/_randommodule.c.h +++ b/Modules/clinic/_randommodule.c.h @@ -109,21 +109,4 @@ _random_Random_getrandbits(RandomObject *self, PyObject *arg) exit: return return_value; } - -PyDoc_STRVAR(_random_Random___reduce____doc__, -"__reduce__($self, /)\n" -"--\n" -"\n"); - -#define _RANDOM_RANDOM___REDUCE___METHODDEF \ - {"__reduce__", (PyCFunction)_random_Random___reduce__, METH_NOARGS, _random_Random___reduce____doc__}, - -static PyObject * -_random_Random___reduce___impl(RandomObject *self); - -static PyObject * -_random_Random___reduce__(RandomObject *self, PyObject *Py_UNUSED(ignored)) -{ - return _random_Random___reduce___impl(self); -} -/*[clinic end generated code: output=450f0961c2c92389 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=cc8a23b2757dc6ba input=a9049054013a1b77]*/ From webhook-mailer at python.org Sat Oct 24 15:07:44 2020 From: webhook-mailer at python.org (gpshead) Date: Sat, 24 Oct 2020 19:07:44 -0000 Subject: [Python-checkins] bpo-35823: Allow setsid() after vfork() on Linux. (GH-22945) Message-ID: https://github.com/python/cpython/commit/be3c3a0e468237430ad7d19a33c60d306199a7f2 commit: be3c3a0e468237430ad7d19a33c60d306199a7f2 branch: master author: Gregory P. Smith committer: gpshead date: 2020-10-24T12:07:35-07:00 summary: bpo-35823: Allow setsid() after vfork() on Linux. (GH-22945) It should just be a syscall updating a couple of fields in the kernel side process info. Confirming, in glibc is appears to be a shim for the setsid syscall (based on not finding any code implementing anything special for it) and in uclibc (*much* easier to read) it is clearly just a setsid syscall shim. A breadcrumb _suggesting_ that it is not allowed on Darwin/macOS comes from a commit in emacs: https://lists.gnu.org/archive/html/bug-gnu-emacs/2017-04/msg00297.html but I don't have a way to verify if that is true or not. As we are not supporting vfork on macOS today I just left a note in a comment. files: M Modules/_posixsubprocess.c diff --git a/Modules/_posixsubprocess.c b/Modules/_posixsubprocess.c index b7cba30ee7618..8baea314f4e40 100644 --- a/Modules/_posixsubprocess.c +++ b/Modules/_posixsubprocess.c @@ -38,6 +38,8 @@ #if defined(__linux__) && defined(HAVE_VFORK) && defined(HAVE_SIGNAL_H) && \ defined(HAVE_PTHREAD_SIGMASK) && !defined(HAVE_BROKEN_PTHREAD_SIGMASK) +/* If this is ever expanded to non-Linux platforms, verify what calls are + * allowed after vfork(). Ex: setsid() may be disallowed on macOS? */ # include # define VFORK_USABLE 1 #endif @@ -712,7 +714,6 @@ do_fork_exec(char *const exec_array[], #ifdef VFORK_USABLE if (child_sigmask) { /* These are checked by our caller; verify them in debug builds. */ - assert(!call_setsid); assert(!call_setuid); assert(!call_setgid); assert(!call_setgroups); @@ -997,7 +998,7 @@ subprocess_fork_exec(PyObject* self, PyObject *args) /* Use vfork() only if it's safe. See the comment above child_exec(). */ sigset_t old_sigs; if (preexec_fn == Py_None && - !call_setuid && !call_setgid && !call_setgroups && !call_setsid) { + !call_setuid && !call_setgid && !call_setgroups) { /* Block all signals to ensure that no signal handlers are run in the * child process while it shares memory with us. Note that signals * used internally by C libraries won't be blocked by From webhook-mailer at python.org Sat Oct 24 19:32:43 2020 From: webhook-mailer at python.org (terryjreedy) Date: Sat, 24 Oct 2020 23:32:43 -0000 Subject: [Python-checkins] bpo-33987: Use ttk Label on IDLE statusbar (GH-22941) Message-ID: https://github.com/python/cpython/commit/e53e54425d9b7b9b7b082817da104d60bb25e3a2 commit: e53e54425d9b7b9b7b082817da104d60bb25e3a2 branch: master author: Mark Roseman committer: terryjreedy date: 2020-10-24T19:32:34-04:00 summary: bpo-33987: Use ttk Label on IDLE statusbar (GH-22941) files: M Lib/idlelib/statusbar.py diff --git a/Lib/idlelib/statusbar.py b/Lib/idlelib/statusbar.py index ae52a56368c82..755fafb0ac643 100644 --- a/Lib/idlelib/statusbar.py +++ b/Lib/idlelib/statusbar.py @@ -1,5 +1,4 @@ -from tkinter import Label -from tkinter.ttk import Frame +from tkinter.ttk import Label, Frame class MultiStatusBar(Frame): From webhook-mailer at python.org Sat Oct 24 19:45:08 2020 From: webhook-mailer at python.org (terryjreedy) Date: Sat, 24 Oct 2020 23:45:08 -0000 Subject: [Python-checkins] bpo-33987: Use master ttk Frame for IDLE config dialog (GH-22943) Message-ID: https://github.com/python/cpython/commit/c579ad14d3b5bb9a45d7b9cc708eaf0bf4884c50 commit: c579ad14d3b5bb9a45d7b9cc708eaf0bf4884c50 branch: master author: Mark Roseman committer: terryjreedy date: 2020-10-24T19:45:00-04:00 summary: bpo-33987: Use master ttk Frame for IDLE config dialog (GH-22943) files: M Lib/idlelib/configdialog.py diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index 82596498d3461..a84e1c5668f99 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -67,7 +67,6 @@ def __init__(self, parent, title='', *, _htest=False, _utest=False): if not _utest: self.withdraw() - self.configure(borderwidth=5) self.title(title or 'IDLE Preferences') x = parent.winfo_rootx() + 20 y = parent.winfo_rooty() + (30 if not _htest else 150) @@ -97,6 +96,7 @@ def create_widgets(self): """Create and place widgets for tabbed dialog. Widgets Bound to self: + frame: encloses all other widgets note: Notebook highpage: HighPage fontpage: FontPage @@ -109,7 +109,9 @@ def create_widgets(self): load_configs: Load pages except for extensions. activate_config_changes: Tell editors to reload. """ - self.note = note = Notebook(self) + self.frame = frame = Frame(self, padding="5px") + self.frame.grid(sticky="nwes") + self.note = note = Notebook(frame) self.highpage = HighPage(note) self.fontpage = FontPage(note, self.highpage) self.keyspage = KeysPage(note) @@ -148,7 +150,7 @@ def create_action_buttons(self): padding_args = {} else: padding_args = {'padding': (6, 3)} - outer = Frame(self, padding=2) + outer = Frame(self.frame, padding=2) buttons_frame = Frame(outer, padding=2) self.buttons = {} for txt, cmd in ( @@ -687,7 +689,7 @@ class HighPage(Frame): def __init__(self, master): super().__init__(master) - self.cd = master.master + self.cd = master.winfo_toplevel() self.style = Style(master) self.create_page_highlight() self.load_theme_cfg() @@ -1346,7 +1348,7 @@ class KeysPage(Frame): def __init__(self, master): super().__init__(master) - self.cd = master.master + self.cd = master.winfo_toplevel() self.create_page_keys() self.load_key_cfg() From webhook-mailer at python.org Sat Oct 24 19:51:08 2020 From: webhook-mailer at python.org (miss-islington) Date: Sat, 24 Oct 2020 23:51:08 -0000 Subject: [Python-checkins] bpo-33987: Use ttk Label on IDLE statusbar (GH-22941) Message-ID: https://github.com/python/cpython/commit/c1b620eecc2ca0f9ae9d5ee2d973e823db295ad2 commit: c1b620eecc2ca0f9ae9d5ee2d973e823db295ad2 branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-24T16:51:03-07:00 summary: bpo-33987: Use ttk Label on IDLE statusbar (GH-22941) (cherry picked from commit e53e54425d9b7b9b7b082817da104d60bb25e3a2) Co-authored-by: Mark Roseman files: M Lib/idlelib/statusbar.py diff --git a/Lib/idlelib/statusbar.py b/Lib/idlelib/statusbar.py index ae52a56368c82..755fafb0ac643 100644 --- a/Lib/idlelib/statusbar.py +++ b/Lib/idlelib/statusbar.py @@ -1,5 +1,4 @@ -from tkinter import Label -from tkinter.ttk import Frame +from tkinter.ttk import Label, Frame class MultiStatusBar(Frame): From webhook-mailer at python.org Sat Oct 24 19:53:38 2020 From: webhook-mailer at python.org (miss-islington) Date: Sat, 24 Oct 2020 23:53:38 -0000 Subject: [Python-checkins] bpo-33987: Use ttk Label on IDLE statusbar (GH-22941) Message-ID: https://github.com/python/cpython/commit/4efd2defb899c5c45334814234c842f8f513879e commit: 4efd2defb899c5c45334814234c842f8f513879e branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-24T16:53:34-07:00 summary: bpo-33987: Use ttk Label on IDLE statusbar (GH-22941) (cherry picked from commit e53e54425d9b7b9b7b082817da104d60bb25e3a2) Co-authored-by: Mark Roseman files: M Lib/idlelib/statusbar.py diff --git a/Lib/idlelib/statusbar.py b/Lib/idlelib/statusbar.py index ae52a56368c82..755fafb0ac643 100644 --- a/Lib/idlelib/statusbar.py +++ b/Lib/idlelib/statusbar.py @@ -1,5 +1,4 @@ -from tkinter import Label -from tkinter.ttk import Frame +from tkinter.ttk import Label, Frame class MultiStatusBar(Frame): From webhook-mailer at python.org Sat Oct 24 20:03:41 2020 From: webhook-mailer at python.org (miss-islington) Date: Sun, 25 Oct 2020 00:03:41 -0000 Subject: [Python-checkins] bpo-33987: Use master ttk Frame for IDLE config dialog (GH-22943) Message-ID: https://github.com/python/cpython/commit/253c8eb40ec5a87aa539f913fcd83d1759069981 commit: 253c8eb40ec5a87aa539f913fcd83d1759069981 branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-24T17:03:34-07:00 summary: bpo-33987: Use master ttk Frame for IDLE config dialog (GH-22943) (cherry picked from commit c579ad14d3b5bb9a45d7b9cc708eaf0bf4884c50) Co-authored-by: Mark Roseman files: M Lib/idlelib/configdialog.py diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index 82596498d3461..a84e1c5668f99 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -67,7 +67,6 @@ def __init__(self, parent, title='', *, _htest=False, _utest=False): if not _utest: self.withdraw() - self.configure(borderwidth=5) self.title(title or 'IDLE Preferences') x = parent.winfo_rootx() + 20 y = parent.winfo_rooty() + (30 if not _htest else 150) @@ -97,6 +96,7 @@ def create_widgets(self): """Create and place widgets for tabbed dialog. Widgets Bound to self: + frame: encloses all other widgets note: Notebook highpage: HighPage fontpage: FontPage @@ -109,7 +109,9 @@ def create_widgets(self): load_configs: Load pages except for extensions. activate_config_changes: Tell editors to reload. """ - self.note = note = Notebook(self) + self.frame = frame = Frame(self, padding="5px") + self.frame.grid(sticky="nwes") + self.note = note = Notebook(frame) self.highpage = HighPage(note) self.fontpage = FontPage(note, self.highpage) self.keyspage = KeysPage(note) @@ -148,7 +150,7 @@ def create_action_buttons(self): padding_args = {} else: padding_args = {'padding': (6, 3)} - outer = Frame(self, padding=2) + outer = Frame(self.frame, padding=2) buttons_frame = Frame(outer, padding=2) self.buttons = {} for txt, cmd in ( @@ -687,7 +689,7 @@ class HighPage(Frame): def __init__(self, master): super().__init__(master) - self.cd = master.master + self.cd = master.winfo_toplevel() self.style = Style(master) self.create_page_highlight() self.load_theme_cfg() @@ -1346,7 +1348,7 @@ class KeysPage(Frame): def __init__(self, master): super().__init__(master) - self.cd = master.master + self.cd = master.winfo_toplevel() self.create_page_keys() self.load_key_cfg() From webhook-mailer at python.org Sat Oct 24 20:32:40 2020 From: webhook-mailer at python.org (miss-islington) Date: Sun, 25 Oct 2020 00:32:40 -0000 Subject: [Python-checkins] [doc] Fix link to abc.ABCMeta.register in Glossary (GH-22932) Message-ID: https://github.com/python/cpython/commit/eac21a048bb6f33b20224a5d6896c34d1d6abc8e commit: eac21a048bb6f33b20224a5d6896c34d1d6abc8e branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-24T17:32:36-07:00 summary: [doc] Fix link to abc.ABCMeta.register in Glossary (GH-22932) (cherry picked from commit e01e442125bbc98e6dab66f38ecc6c45f69e6587) Co-authored-by: Andre Delfino files: M Doc/glossary.rst diff --git a/Doc/glossary.rst b/Doc/glossary.rst index 7be755e411310..632ed3f4301c0 100644 --- a/Doc/glossary.rst +++ b/Doc/glossary.rst @@ -1024,7 +1024,7 @@ Glossary :meth:`index`, :meth:`__contains__`, and :meth:`__reversed__`. Types that implement this expanded interface can be registered explicitly using - :func:`~abc.register`. + :func:`~abc.ABCMeta.register`. single dispatch A form of :term:`generic function` dispatch where the implementation is From webhook-mailer at python.org Sat Oct 24 20:33:06 2020 From: webhook-mailer at python.org (miss-islington) Date: Sun, 25 Oct 2020 00:33:06 -0000 Subject: [Python-checkins] [doc] Fix link to abc.ABCMeta.register in Glossary (GH-22932) Message-ID: https://github.com/python/cpython/commit/c2c44774893770da6ef4709828d398d73beffc56 commit: c2c44774893770da6ef4709828d398d73beffc56 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-24T17:33:01-07:00 summary: [doc] Fix link to abc.ABCMeta.register in Glossary (GH-22932) (cherry picked from commit e01e442125bbc98e6dab66f38ecc6c45f69e6587) Co-authored-by: Andre Delfino files: M Doc/glossary.rst diff --git a/Doc/glossary.rst b/Doc/glossary.rst index 189d49ee0d627..778fd40438157 100644 --- a/Doc/glossary.rst +++ b/Doc/glossary.rst @@ -1025,7 +1025,7 @@ Glossary :meth:`index`, :meth:`__contains__`, and :meth:`__reversed__`. Types that implement this expanded interface can be registered explicitly using - :func:`~abc.register`. + :func:`~abc.ABCMeta.register`. single dispatch A form of :term:`generic function` dispatch where the implementation is From webhook-mailer at python.org Sat Oct 24 21:08:26 2020 From: webhook-mailer at python.org (miss-islington) Date: Sun, 25 Oct 2020 01:08:26 -0000 Subject: [Python-checkins] bpo-33987: Use master ttk Frame for IDLE config dialog (GH-22943) Message-ID: https://github.com/python/cpython/commit/2d493893c7aa9af633c4ebeb56ecce42e3a82e9f commit: 2d493893c7aa9af633c4ebeb56ecce42e3a82e9f branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-24T18:08:17-07:00 summary: bpo-33987: Use master ttk Frame for IDLE config dialog (GH-22943) (cherry picked from commit c579ad14d3b5bb9a45d7b9cc708eaf0bf4884c50) Co-authored-by: Mark Roseman files: M Lib/idlelib/configdialog.py diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index 82596498d3461..a84e1c5668f99 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -67,7 +67,6 @@ def __init__(self, parent, title='', *, _htest=False, _utest=False): if not _utest: self.withdraw() - self.configure(borderwidth=5) self.title(title or 'IDLE Preferences') x = parent.winfo_rootx() + 20 y = parent.winfo_rooty() + (30 if not _htest else 150) @@ -97,6 +96,7 @@ def create_widgets(self): """Create and place widgets for tabbed dialog. Widgets Bound to self: + frame: encloses all other widgets note: Notebook highpage: HighPage fontpage: FontPage @@ -109,7 +109,9 @@ def create_widgets(self): load_configs: Load pages except for extensions. activate_config_changes: Tell editors to reload. """ - self.note = note = Notebook(self) + self.frame = frame = Frame(self, padding="5px") + self.frame.grid(sticky="nwes") + self.note = note = Notebook(frame) self.highpage = HighPage(note) self.fontpage = FontPage(note, self.highpage) self.keyspage = KeysPage(note) @@ -148,7 +150,7 @@ def create_action_buttons(self): padding_args = {} else: padding_args = {'padding': (6, 3)} - outer = Frame(self, padding=2) + outer = Frame(self.frame, padding=2) buttons_frame = Frame(outer, padding=2) self.buttons = {} for txt, cmd in ( @@ -687,7 +689,7 @@ class HighPage(Frame): def __init__(self, master): super().__init__(master) - self.cd = master.master + self.cd = master.winfo_toplevel() self.style = Style(master) self.create_page_highlight() self.load_theme_cfg() @@ -1346,7 +1348,7 @@ class KeysPage(Frame): def __init__(self, master): super().__init__(master) - self.cd = master.master + self.cd = master.winfo_toplevel() self.create_page_keys() self.load_key_cfg() From webhook-mailer at python.org Sat Oct 24 21:17:25 2020 From: webhook-mailer at python.org (rhettinger) Date: Sun, 25 Oct 2020 01:17:25 -0000 Subject: [Python-checkins] bpo-42127: Document effect of cached_property on key-sharing dictionaries (GH-22930) Message-ID: https://github.com/python/cpython/commit/48be6b1ef7a6201e13c87a317361cdb60bd5faa8 commit: 48be6b1ef7a6201e13c87a317361cdb60bd5faa8 branch: master author: Raymond Hettinger committer: rhettinger date: 2020-10-24T18:17:17-07:00 summary: bpo-42127: Document effect of cached_property on key-sharing dictionaries (GH-22930) files: M Doc/library/functools.rst diff --git a/Doc/library/functools.rst b/Doc/library/functools.rst index 186cb4c381dee..75c9d41b43acd 100644 --- a/Doc/library/functools.rst +++ b/Doc/library/functools.rst @@ -73,16 +73,31 @@ The :mod:`functools` module defines the following functions: def variance(self): return statistics.variance(self._data) - .. versionadded:: 3.8 + Note, this decorator interferes with the operation of :pep:`412` + key-sharing dictionaries. This means that instance dictionaries + can take more space than usual. - .. note:: + Also, this decorator requires that the ``__dict__`` attribute on each instance + be a mutable mapping. This means it will not work with some types, such as + metaclasses (since the ``__dict__`` attributes on type instances are + read-only proxies for the class namespace), and those that specify + ``__slots__`` without including ``__dict__`` as one of the defined slots + (as such classes don't provide a ``__dict__`` attribute at all). + + If a mutable mapping is not available or if space-efficient key sharing + is desired, an effect similar to :func:`cached_property` can be achieved + by a stacking :func:`property` on top of :func:`cache`:: - This decorator requires that the ``__dict__`` attribute on each instance - be a mutable mapping. This means it will not work with some types, such as - metaclasses (since the ``__dict__`` attributes on type instances are - read-only proxies for the class namespace), and those that specify - ``__slots__`` without including ``__dict__`` as one of the defined slots - (as such classes don't provide a ``__dict__`` attribute at all). + class DataSet: + def __init__(self, sequence_of_numbers): + self._data = sequence_of_numbers + + @property + @cache + def stdev(self): + return statistics.stdev(self._data) + + .. versionadded:: 3.8 .. function:: cmp_to_key(func) @@ -658,4 +673,4 @@ callable, weak referencable, and can have attributes. There are some important differences. For instance, the :attr:`~definition.__name__` and :attr:`__doc__` attributes are not created automatically. Also, :class:`partial` objects defined in classes behave like static methods and do not transform into bound methods -during instance attribute look-up. \ No newline at end of file +during instance attribute look-up. From webhook-mailer at python.org Sat Oct 24 22:00:23 2020 From: webhook-mailer at python.org (rhettinger) Date: Sun, 25 Oct 2020 02:00:23 -0000 Subject: [Python-checkins] bpo-42127: Document effect of cached_property on key-sharing dictionaries (GH-22930) (GH-22955) Message-ID: https://github.com/python/cpython/commit/427cb0aa78813b89a3f100073bf7d70a53510f57 commit: 427cb0aa78813b89a3f100073bf7d70a53510f57 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: rhettinger date: 2020-10-24T19:00:14-07:00 summary: bpo-42127: Document effect of cached_property on key-sharing dictionaries (GH-22930) (GH-22955) files: M Doc/library/functools.rst diff --git a/Doc/library/functools.rst b/Doc/library/functools.rst index 14aa184e2cd14..4869b67cb9416 100644 --- a/Doc/library/functools.rst +++ b/Doc/library/functools.rst @@ -73,16 +73,31 @@ The :mod:`functools` module defines the following functions: def variance(self): return statistics.variance(self._data) - .. versionadded:: 3.8 + Note, this decorator interferes with the operation of :pep:`412` + key-sharing dictionaries. This means that instance dictionaries + can take more space than usual. - .. note:: + Also, this decorator requires that the ``__dict__`` attribute on each instance + be a mutable mapping. This means it will not work with some types, such as + metaclasses (since the ``__dict__`` attributes on type instances are + read-only proxies for the class namespace), and those that specify + ``__slots__`` without including ``__dict__`` as one of the defined slots + (as such classes don't provide a ``__dict__`` attribute at all). + + If a mutable mapping is not available or if space-efficient key sharing + is desired, an effect similar to :func:`cached_property` can be achieved + by a stacking :func:`property` on top of :func:`cache`:: - This decorator requires that the ``__dict__`` attribute on each instance - be a mutable mapping. This means it will not work with some types, such as - metaclasses (since the ``__dict__`` attributes on type instances are - read-only proxies for the class namespace), and those that specify - ``__slots__`` without including ``__dict__`` as one of the defined slots - (as such classes don't provide a ``__dict__`` attribute at all). + class DataSet: + def __init__(self, sequence_of_numbers): + self._data = sequence_of_numbers + + @property + @cache + def stdev(self): + return statistics.stdev(self._data) + + .. versionadded:: 3.8 .. function:: cmp_to_key(func) @@ -651,4 +666,4 @@ callable, weak referencable, and can have attributes. There are some important differences. For instance, the :attr:`~definition.__name__` and :attr:`__doc__` attributes are not created automatically. Also, :class:`partial` objects defined in classes behave like static methods and do not transform into bound methods -during instance attribute look-up. \ No newline at end of file +during instance attribute look-up. From webhook-mailer at python.org Sat Oct 24 22:53:25 2020 From: webhook-mailer at python.org (miss-islington) Date: Sun, 25 Oct 2020 02:53:25 -0000 Subject: [Python-checkins] bpo-29981: Add examples and update index for set, dict, and generator comprehensions'(GH-20272) Message-ID: https://github.com/python/cpython/commit/60bef61f717dde915058b03159b2c2e97d765858 commit: 60bef61f717dde915058b03159b2c2e97d765858 branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-24T19:53:18-07:00 summary: bpo-29981: Add examples and update index for set, dict, and generator comprehensions'(GH-20272) Co-authored-by: R?mi Lapeyre (cherry picked from commit 2d55aa9e37c9c84f4f6a8135d0326da0bcd8f38b) Co-authored-by: Florian Dahlitz files: M Doc/glossary.rst M Doc/library/stdtypes.rst M Doc/reference/expressions.rst M Misc/ACKS diff --git a/Doc/glossary.rst b/Doc/glossary.rst index 632ed3f4301c0..d94fc4469c50a 100644 --- a/Doc/glossary.rst +++ b/Doc/glossary.rst @@ -308,6 +308,12 @@ Glossary keys can be any object with :meth:`__hash__` and :meth:`__eq__` methods. Called a hash in Perl. + dictionary comprehension + A compact way to process all or part of the elements in an iterable and + return a dictionary with the results. ``results = {n: n ** 2 for n in + range(10)}`` generates a dictionary containing key ``n`` mapped to + value ``n ** 2``. See :ref:`comprehensions`. + dictionary view The objects returned from :meth:`dict.keys`, :meth:`dict.values`, and :meth:`dict.items` are called dictionary views. They provide a dynamic @@ -1026,6 +1032,12 @@ Glossary interface can be registered explicitly using :func:`~abc.ABCMeta.register`. + set comprehension + A compact way to process all or part of the elements in an iterable and + return a set with the results. ``results = {c for c in 'abracadabra' if + c not in 'abc'}`` generates the set of strings ``{'r', 'd'}``. See + :ref:`comprehensions`. + single dispatch A form of :term:`generic function` dispatch where the implementation is chosen based on the type of a single argument. diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index ccd2f99ccb34f..78c7cc76d7e7b 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -3994,6 +3994,12 @@ The constructors for both classes work the same: objects. If *iterable* is not specified, a new empty set is returned. + Sets can be created by several means: + + * Use a comma-separated list of elements within braces: ``{'jack', 'sjoerd'}`` + * Use a set comprehension: ``{c for c in 'abracadabra' if c not in 'abc'}`` + * Use the type constructor: ``set()``, ``set('foobar')``, ``set(['a', 'b', 'foo'])`` + Instances of :class:`set` and :class:`frozenset` provide the following operations: @@ -4186,6 +4192,14 @@ pairs within braces, for example: ``{'jack': 4098, 'sjoerd': 4127}`` or ``{4098: Return a new dictionary initialized from an optional positional argument and a possibly empty set of keyword arguments. + Dictionaries can be created by several means: + + * Use a comma-separated list of ``key: value`` pairs within braces: + ``{'jack': 4098, 'sjoerd': 4127}`` or ``{4098: 'jack', 4127: 'sjoerd'}`` + * Use a dict comprehension: ``{}``, ``{x: x ** 2 for x in range(10)}`` + * Use the type constructor: ``dict()``, + ``dict([('foo', 100), ('bar', 200)])``, ``dict(foo=100, bar=200)`` + If no positional argument is given, an empty dictionary is created. If a positional argument is given and it is a mapping object, a dictionary is created with the same key-value pairs as the mapping object. Otherwise, diff --git a/Doc/reference/expressions.rst b/Doc/reference/expressions.rst index b68c29860cf33..81dd6fc860355 100644 --- a/Doc/reference/expressions.rst +++ b/Doc/reference/expressions.rst @@ -162,6 +162,8 @@ ambiguities and allow common typos to pass uncaught. Displays for lists, sets and dictionaries ----------------------------------------- +.. index:: single: comprehensions + For constructing a list, a set or a dictionary Python provides special syntax called "displays", each of them in two flavors: @@ -260,6 +262,7 @@ Set displays .. index:: pair: set; display + pair: set; comprehensions object: set single: {} (curly brackets); set expression single: , (comma); expression list @@ -287,6 +290,7 @@ Dictionary displays .. index:: pair: dictionary; display + pair: dictionary; comprehensions key, datum, key/datum pair object: dictionary single: {} (curly brackets); dictionary expression diff --git a/Misc/ACKS b/Misc/ACKS index b0246402c354d..d44c794e425e0 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -368,6 +368,7 @@ Brian Curtin Hakan Celik Jason Curtis Paul Dagnelie +Florian Dahlitz Lisandro Dalcin Darren Dale Andrew Dalke From webhook-mailer at python.org Sat Oct 24 22:53:45 2020 From: webhook-mailer at python.org (miss-islington) Date: Sun, 25 Oct 2020 02:53:45 -0000 Subject: [Python-checkins] bpo-29981: Add examples and update index for set, dict, and generator comprehensions'(GH-20272) Message-ID: https://github.com/python/cpython/commit/2cccc29eaf20694006c03af0f4740abdf8ae5579 commit: 2cccc29eaf20694006c03af0f4740abdf8ae5579 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-24T19:53:41-07:00 summary: bpo-29981: Add examples and update index for set, dict, and generator comprehensions'(GH-20272) Co-authored-by: R?mi Lapeyre (cherry picked from commit 2d55aa9e37c9c84f4f6a8135d0326da0bcd8f38b) Co-authored-by: Florian Dahlitz files: M Doc/glossary.rst M Doc/library/stdtypes.rst M Doc/reference/expressions.rst M Misc/ACKS diff --git a/Doc/glossary.rst b/Doc/glossary.rst index 778fd40438157..847500e556056 100644 --- a/Doc/glossary.rst +++ b/Doc/glossary.rst @@ -309,6 +309,12 @@ Glossary keys can be any object with :meth:`__hash__` and :meth:`__eq__` methods. Called a hash in Perl. + dictionary comprehension + A compact way to process all or part of the elements in an iterable and + return a dictionary with the results. ``results = {n: n ** 2 for n in + range(10)}`` generates a dictionary containing key ``n`` mapped to + value ``n ** 2``. See :ref:`comprehensions`. + dictionary view The objects returned from :meth:`dict.keys`, :meth:`dict.values`, and :meth:`dict.items` are called dictionary views. They provide a dynamic @@ -1027,6 +1033,12 @@ Glossary interface can be registered explicitly using :func:`~abc.ABCMeta.register`. + set comprehension + A compact way to process all or part of the elements in an iterable and + return a set with the results. ``results = {c for c in 'abracadabra' if + c not in 'abc'}`` generates the set of strings ``{'r', 'd'}``. See + :ref:`comprehensions`. + single dispatch A form of :term:`generic function` dispatch where the implementation is chosen based on the type of a single argument. diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index c3f5c04aac7d9..1de48e13e271f 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -4119,6 +4119,12 @@ The constructors for both classes work the same: objects. If *iterable* is not specified, a new empty set is returned. + Sets can be created by several means: + + * Use a comma-separated list of elements within braces: ``{'jack', 'sjoerd'}`` + * Use a set comprehension: ``{c for c in 'abracadabra' if c not in 'abc'}`` + * Use the type constructor: ``set()``, ``set('foobar')``, ``set(['a', 'b', 'foo'])`` + Instances of :class:`set` and :class:`frozenset` provide the following operations: @@ -4311,6 +4317,14 @@ pairs within braces, for example: ``{'jack': 4098, 'sjoerd': 4127}`` or ``{4098: Return a new dictionary initialized from an optional positional argument and a possibly empty set of keyword arguments. + Dictionaries can be created by several means: + + * Use a comma-separated list of ``key: value`` pairs within braces: + ``{'jack': 4098, 'sjoerd': 4127}`` or ``{4098: 'jack', 4127: 'sjoerd'}`` + * Use a dict comprehension: ``{}``, ``{x: x ** 2 for x in range(10)}`` + * Use the type constructor: ``dict()``, + ``dict([('foo', 100), ('bar', 200)])``, ``dict(foo=100, bar=200)`` + If no positional argument is given, an empty dictionary is created. If a positional argument is given and it is a mapping object, a dictionary is created with the same key-value pairs as the mapping object. Otherwise, diff --git a/Doc/reference/expressions.rst b/Doc/reference/expressions.rst index f1f19c3f02d39..938a9732f5a25 100644 --- a/Doc/reference/expressions.rst +++ b/Doc/reference/expressions.rst @@ -162,6 +162,8 @@ ambiguities and allow common typos to pass uncaught. Displays for lists, sets and dictionaries ----------------------------------------- +.. index:: single: comprehensions + For constructing a list, a set or a dictionary Python provides special syntax called "displays", each of them in two flavors: @@ -260,6 +262,7 @@ Set displays .. index:: pair: set; display + pair: set; comprehensions object: set single: {} (curly brackets); set expression single: , (comma); expression list @@ -287,6 +290,7 @@ Dictionary displays .. index:: pair: dictionary; display + pair: dictionary; comprehensions key, datum, key/datum pair object: dictionary single: {} (curly brackets); dictionary expression diff --git a/Misc/ACKS b/Misc/ACKS index f06fad7926d46..9ad9dffe22aea 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -379,6 +379,7 @@ Brian Curtin Jason Curtis Hakan Celik Paul Dagnelie +Florian Dahlitz Lisandro Dalcin Darren Dale Andrew Dalke From webhook-mailer at python.org Sat Oct 24 23:14:07 2020 From: webhook-mailer at python.org (terryjreedy) Date: Sun, 25 Oct 2020 03:14:07 -0000 Subject: [Python-checkins] bpo-33987: Add master ttk Frame to IDLE search dialogs (GH-22942) Message-ID: https://github.com/python/cpython/commit/5df6c99cb450fe2f30be681dbf68cd1d34d3bbe4 commit: 5df6c99cb450fe2f30be681dbf68cd1d34d3bbe4 branch: master author: Mark Roseman committer: terryjreedy date: 2020-10-24T23:14:02-04:00 summary: bpo-33987: Add master ttk Frame to IDLE search dialogs (GH-22942) files: A Misc/NEWS.d/next/IDLE/2020-10-24-21-27-37.bpo-33987.fIh9JL.rst M Lib/idlelib/NEWS.txt M Lib/idlelib/idle_test/test_searchbase.py M Lib/idlelib/searchbase.py diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt index 754034200a1f6..3ece623b3aa18 100644 --- a/Lib/idlelib/NEWS.txt +++ b/Lib/idlelib/NEWS.txt @@ -3,6 +3,9 @@ Released on 2021-10-04? ====================================== +bpo-33987: Mostly finish using ttk widgets, mainly for editor, +settings, and searches. Some patches by Mark Roseman. + bpo-41775: Make 'IDLE Shell' the shell title. bpo-35764: Rewrite the Calltips doc section. diff --git a/Lib/idlelib/idle_test/test_searchbase.py b/Lib/idlelib/idle_test/test_searchbase.py index aee0c4c69929a..8c9c410ebaf47 100644 --- a/Lib/idlelib/idle_test/test_searchbase.py +++ b/Lib/idlelib/idle_test/test_searchbase.py @@ -76,7 +76,7 @@ def test_create_widgets(self): def test_make_entry(self): equal = self.assertEqual self.dialog.row = 0 - self.dialog.top = self.root + self.dialog.frame = Frame(self.root) entry, label = self.dialog.make_entry("Test:", 'hello') equal(label['text'], 'Test:') @@ -89,7 +89,7 @@ def test_make_entry(self): equal(self.dialog.row, 1) def test_create_entries(self): - self.dialog.top = self.root + self.dialog.frame = Frame(self.root) self.dialog.row = 0 self.engine.setpat('hello') self.dialog.create_entries() @@ -97,7 +97,7 @@ def test_create_entries(self): def test_make_frame(self): self.dialog.row = 0 - self.dialog.top = self.root + self.dialog.frame = Frame(self.root) frame, label = self.dialog.make_frame() self.assertEqual(label, '') self.assertEqual(str(type(frame)), "") @@ -108,7 +108,7 @@ def test_make_frame(self): self.assertEqual(label['text'], 'testlabel') def btn_test_setup(self, meth): - self.dialog.top = self.root + self.dialog.frame = Frame(self.root) self.dialog.row = 0 return meth() @@ -140,13 +140,13 @@ def test_create_other_buttons(self): self.assertEqual(var.get(), state) def test_make_button(self): - self.dialog.top = self.root - self.dialog.buttonframe = Frame(self.dialog.top) + self.dialog.frame = Frame(self.root) + self.dialog.buttonframe = Frame(self.dialog.frame) btn = self.dialog.make_button('Test', self.dialog.close) self.assertEqual(btn['text'], 'Test') def test_create_command_buttons(self): - self.dialog.top = self.root + self.dialog.frame = Frame(self.root) self.dialog.create_command_buttons() # Look for close button command in buttonframe closebuttoncommand = '' diff --git a/Lib/idlelib/searchbase.py b/Lib/idlelib/searchbase.py index 6fba0b8e583f2..fbef87aa2d3d0 100644 --- a/Lib/idlelib/searchbase.py +++ b/Lib/idlelib/searchbase.py @@ -33,6 +33,7 @@ def __init__(self, root, engine): '''Initialize root, engine, and top attributes. top (level widget): set in create_widgets() called from open(). + frame: container for all widgets in dialog. text (Text searched): set in open(), only used in subclasses(). ent (ry): created in make_entry() called from create_entry(). row (of grid): 0 in create_widgets(), +1 in make_entry/frame(). @@ -83,10 +84,14 @@ def create_widgets(self): top.wm_title(self.title) top.wm_iconname(self.icon) self.top = top + self.frame = Frame(top, padding="5px") + self.frame.grid(sticky="nwes") + top.grid_columnconfigure(0, weight=100) + top.grid_rowconfigure(0, weight=100) self.row = 0 - self.top.grid_columnconfigure(0, pad=2, weight=0) - self.top.grid_columnconfigure(1, pad=2, minsize=100, weight=100) + self.frame.grid_columnconfigure(0, pad=2, weight=0) + self.frame.grid_columnconfigure(1, pad=2, minsize=100, weight=100) self.create_entries() # row 0 (and maybe 1), cols 0, 1 self.create_option_buttons() # next row, cols 0, 1 @@ -99,9 +104,9 @@ def make_entry(self, label_text, var): entry - gridded labeled Entry for text entry. label - Label widget, returned for testing. ''' - label = Label(self.top, text=label_text) + label = Label(self.frame, text=label_text) label.grid(row=self.row, column=0, sticky="nw") - entry = Entry(self.top, textvariable=var, exportselection=0) + entry = Entry(self.frame, textvariable=var, exportselection=0) entry.grid(row=self.row, column=1, sticky="nwe") self.row = self.row + 1 return entry, label @@ -117,11 +122,11 @@ def make_frame(self,labeltext=None): label - Label widget, returned for testing. ''' if labeltext: - label = Label(self.top, text=labeltext) + label = Label(self.frame, text=labeltext) label.grid(row=self.row, column=0, sticky="nw") else: label = '' - frame = Frame(self.top) + frame = Frame(self.frame) frame.grid(row=self.row, column=1, columnspan=1, sticky="nwe") self.row = self.row + 1 return frame, label @@ -171,7 +176,7 @@ def make_button(self, label, command, isdef=0): def create_command_buttons(self): "Place buttons in vertical command frame gridded on right." - f = self.buttonframe = Frame(self.top) + f = self.buttonframe = Frame(self.frame) f.grid(row=0,column=2,padx=2,pady=2,ipadx=2,ipady=2) b = self.make_button("Close", self.close) diff --git a/Misc/NEWS.d/next/IDLE/2020-10-24-21-27-37.bpo-33987.fIh9JL.rst b/Misc/NEWS.d/next/IDLE/2020-10-24-21-27-37.bpo-33987.fIh9JL.rst new file mode 100644 index 0000000000000..1e67edc03c658 --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2020-10-24-21-27-37.bpo-33987.fIh9JL.rst @@ -0,0 +1,3 @@ +Mostly finish using ttk widgets, mainly for editor, settings, +and searches. Some patches by Mark Roseman. + From webhook-mailer at python.org Sat Oct 24 23:34:47 2020 From: webhook-mailer at python.org (rhettinger) Date: Sun, 25 Oct 2020 03:34:47 -0000 Subject: [Python-checkins] Second round of updates to the descriptor howto guide (GH-22946) Message-ID: https://github.com/python/cpython/commit/4a9c6379380defd37b5483607d0d804db18f7812 commit: 4a9c6379380defd37b5483607d0d804db18f7812 branch: master author: Raymond Hettinger committer: rhettinger date: 2020-10-24T20:34:39-07:00 summary: Second round of updates to the descriptor howto guide (GH-22946) files: M Doc/howto/descriptor.rst diff --git a/Doc/howto/descriptor.rst b/Doc/howto/descriptor.rst index 4e9fad30d31c4..bed4078e3a3a9 100644 --- a/Doc/howto/descriptor.rst +++ b/Doc/howto/descriptor.rst @@ -29,8 +29,8 @@ This HowTo guide has three major sections: Primer ^^^^^^ -In this primer, we start with most basic possible example and then we'll add -new capabilities one by one. +In this primer, we start with the most basic possible example and then we'll +add new capabilities one by one. Simple example: A descriptor that returns a constant @@ -197,7 +197,7 @@ be recorded, giving each descriptor its own *public_name* and *private_name*:: import logging - logging.basicConfig(level=logging.INFO) + logging.basicConfig(level=logging.INFO, force=True) class LoggedAccess: @@ -258,6 +258,10 @@ Closing thoughts A :term:`descriptor` is what we call any object that defines :meth:`__get__`, :meth:`__set__`, or :meth:`__delete__`. +Optionally, descriptors can have a :meth:`__set_name__` method. This is only +used in cases where a descriptor needs to know either the class where it is +created or the name of class variable it was assigned to. + Descriptors get invoked by the dot operator during attribute lookup. If a descriptor is accessed indirectly with ``vars(some_class)[descriptor_name]``, the descriptor instance is returned without invoking it. @@ -291,7 +295,7 @@ Validator class A validator is a descriptor for managed attribute access. Prior to storing any data, it verifies that the new value meets various type and range restrictions. If those restrictions aren't met, it raises an exception to -prevents data corruption at its source. +prevent data corruption at its source. This :class:`Validator` class is both an :term:`abstract base class` and a managed attribute descriptor:: @@ -438,12 +442,12 @@ In general, a descriptor is an object attribute with "binding behavior", one whose attribute access has been overridden by methods in the descriptor protocol. Those methods are :meth:`__get__`, :meth:`__set__`, and :meth:`__delete__`. If any of those methods are defined for an object, it is -said to be a descriptor. +said to be a :term:`descriptor`. The default behavior for attribute access is to get, set, or delete the attribute from an object's dictionary. For instance, ``a.x`` has a lookup chain starting with ``a.__dict__['x']``, then ``type(a).__dict__['x']``, and -continuing through the base classes of ``type(a)`` excluding metaclasses. If the +continuing through the base classes of ``type(a)``. If the looked-up value is an object defining one of the descriptor methods, then Python may override the default behavior and invoke the descriptor method instead. Where this occurs in the precedence chain depends on which descriptor methods @@ -492,60 +496,76 @@ Invoking Descriptors A descriptor can be called directly by its method name. For example, ``d.__get__(obj)``. -Alternatively, it is more common for a descriptor to be invoked automatically -upon attribute access. For example, ``obj.d`` looks up ``d`` in the dictionary -of ``obj``. If ``d`` defines the method :meth:`__get__`, then ``d.__get__(obj)`` +But it is more common for a descriptor to be invoked automatically from +attribute access. The expression ``obj.d`` looks up ``d`` in the dictionary of +``obj``. If ``d`` defines the method :meth:`__get__`, then ``d.__get__(obj)`` is invoked according to the precedence rules listed below. -The details of invocation depend on whether ``obj`` is an object or a class. +The details of invocation depend on whether ``obj`` is an object, class, or +instance of super. + +**Objects**: The machinery is in :meth:`object.__getattribute__`. + +It transforms ``b.x`` into ``type(b).__dict__['x'].__get__(b, type(b))``. -For objects, the machinery is in :meth:`object.__getattribute__` which -transforms ``b.x`` into ``type(b).__dict__['x'].__get__(b, type(b))``. The -implementation works through a precedence chain that gives data descriptors +The implementation works through a precedence chain that gives data descriptors priority over instance variables, instance variables priority over non-data descriptors, and assigns lowest priority to :meth:`__getattr__` if provided. + The full C implementation can be found in :c:func:`PyObject_GenericGetAttr()` in :source:`Objects/object.c`. -For classes, the machinery is in :meth:`type.__getattribute__` which transforms -``B.x`` into ``B.__dict__['x'].__get__(None, B)``. In pure Python, it looks -like:: +**Classes**: The machinery is in :meth:`type.__getattribute__`. + +It transforms ``A.x`` into ``A.__dict__['x'].__get__(None, A)``. + +In pure Python, it looks like this:: - def __getattribute__(self, key): + def __getattribute__(cls, key): "Emulate type_getattro() in Objects/typeobject.c" - v = object.__getattribute__(self, key) + v = object.__getattribute__(cls, key) if hasattr(v, '__get__'): - return v.__get__(None, self) + return v.__get__(None, cls) return v -The important points to remember are: +**Super**: The machinery is in the custom :meth:`__getattribute__` method for +object returned by :class:`super()`. -* descriptors are invoked by the :meth:`__getattribute__` method -* overriding :meth:`__getattribute__` prevents automatic descriptor calls -* :meth:`object.__getattribute__` and :meth:`type.__getattribute__` make - different calls to :meth:`__get__`. -* data descriptors always override instance dictionaries. -* non-data descriptors may be overridden by instance dictionaries. +The attribute lookup ``super(A, obj).m`` searches ``obj.__class__.__mro__`` for +the base class ``B`` immediately following ``A`` and then returns +``B.__dict__['m'].__get__(obj, A)``. -The object returned by ``super()`` also has a custom :meth:`__getattribute__` -method for invoking descriptors. The attribute lookup ``super(B, obj).m`` searches -``obj.__class__.__mro__`` for the base class ``A`` immediately following ``B`` -and then returns ``A.__dict__['m'].__get__(obj, B)``. If not a descriptor, -``m`` is returned unchanged. If not in the dictionary, ``m`` reverts to a -search using :meth:`object.__getattribute__`. +If not a descriptor, ``m`` is returned unchanged. If not in the dictionary, +``m`` reverts to a search using :meth:`object.__getattribute__`. The implementation details are in :c:func:`super_getattro()` in -:source:`Objects/typeobject.c`. and a pure Python equivalent can be found in +:source:`Objects/typeobject.c`. A pure Python equivalent can be found in `Guido's Tutorial`_. .. _`Guido's Tutorial`: https://www.python.org/download/releases/2.2.3/descrintro/#cooperation -The details above show that the mechanism for descriptors is embedded in the -:meth:`__getattribute__()` methods for :class:`object`, :class:`type`, and -:func:`super`. Classes inherit this machinery when they derive from -:class:`object` or if they have a metaclass providing similar functionality. -Likewise, classes can turn-off descriptor invocation by overriding -:meth:`__getattribute__()`. +**Summary**: The details listed above show that the mechanism for descriptors is +embedded in the :meth:`__getattribute__()` methods for :class:`object`, +:class:`type`, and :func:`super`. + +The important points to remember are: + +* Descriptors are invoked by the :meth:`__getattribute__` method. + +* Classes inherit this machinery from :class:`object`, :class:`type`, or + :func:`super`. + +* Overriding :meth:`__getattribute__` prevents automatic descriptor calls + because all the descriptor logic is in that method. + +* :meth:`object.__getattribute__` and :meth:`type.__getattribute__` make + different calls to :meth:`__get__`. The first includes the instance and may + include the class. The second puts in ``None`` for the instance and always + includes the class. + +* Data descriptors always override instance dictionaries. + +* Non-data descriptors may be overridden by instance dictionaries. Automatic Name Notification @@ -569,47 +589,70 @@ afterwards, :meth:`__set_name__` will need to be called manually. Descriptor Example ------------------ -The following code creates a class whose objects are data descriptors which -print a message for each get or set. Overriding :meth:`__getattribute__` is -alternate approach that could do this for every attribute. However, this -descriptor is useful for monitoring just a few chosen attributes:: +The following code is simplified skeleton showing how data descriptors could +be used to implement an `object relational mapping +`_. - class RevealAccess: - """A data descriptor that sets and returns values - normally and prints a message logging their access. - """ +The essential idea is that instances only hold keys to a database table. The +actual data is stored in an external table that is being dynamically updated:: - def __init__(self, initval=None, name='var'): - self.val = initval - self.name = name + class Field: + + def __set_name__(self, owner, name): + self.fetch = f'SELECT {name} FROM {owner.table} WHERE {owner.key}=?;' + self.store = f'UPDATE {owner.table} SET {name}=? WHERE {owner.key}=?;' def __get__(self, obj, objtype=None): - print('Retrieving', self.name) - return self.val + return conn.execute(self.fetch, [obj.key]).fetchone()[0] - def __set__(self, obj, val): - print('Updating', self.name) - self.val = val + def __set__(self, obj, value): + conn.execute(self.store, [value, obj.key]) + conn.commit() - class B: - x = RevealAccess(10, 'var "x"') - y = 5 +We can use the :class:`Field` to define "models" that describe the schema for +each table in a database:: - >>> m = B() - >>> m.x - Retrieving var "x" - 10 - >>> m.x = 20 - Updating var "x" - >>> m.x - Retrieving var "x" - 20 - >>> m.y - 5 + class Movie: + table = 'Movies' # Table name + key = 'title' # Primary key + director = Field() + year = Field() -The protocol is simple and offers exciting possibilities. Several use cases are -so common that they have been packaged into individual function calls. -Properties, bound methods, static methods, and class methods are all + def __init__(self, key): + self.key = key + + class Song: + table = 'Music' + key = 'title' + artist = Field() + year = Field() + genre = Field() + + def __init__(self, key): + self.key = key + +An interactive session shows how data is retrieved from the database and how +it can be updated:: + + >>> import sqlite3 + >>> conn = sqlite3.connect('entertainment.db') + + >>> Movie('Star Wars').director + 'George Lucas' + >>> jaws = Movie('Jaws') + >>> f'Released in {jaws.year} by {jaws.director}' + 'Released in 1975 by Steven Spielberg' + + >>> Song('Country Roads').artist + 'John Denver' + + >>> Movie('Star Wars').director = 'J.J. Abrams' + >>> Movie('Star Wars').director + 'J.J. Abrams' + +The descriptor protocol is simple and offers exciting possibilities. Several +use cases are so common that they have been packaged into individual function +calls. Properties, bound methods, static methods, and class methods are all based on the descriptor protocol. @@ -619,7 +662,7 @@ Properties Calling :func:`property` is a succinct way of building a data descriptor that triggers function calls upon access to an attribute. Its signature is:: - property(fget=None, fset=None, fdel=None, doc=None) -> property attribute + property(fget=None, fset=None, fdel=None, doc=None) -> property The documentation shows a typical use to define a managed attribute ``x``:: @@ -695,17 +738,30 @@ Functions and Methods Python's object oriented features are built upon a function based environment. Using non-data descriptors, the two are merged seamlessly. -Class dictionaries store methods as functions. In a class definition, methods -are written using :keyword:`def` or :keyword:`lambda`, the usual tools for -creating functions. Methods only differ from regular functions in that the -first argument is reserved for the object instance. By Python convention, the -instance reference is called *self* but may be called *this* or any other -variable name. +Functions stored in class dictionaries get turned into methods when invoked. +Methods only differ from regular functions in that the object instance is +prepended to the other arguments. By convention, the instance is called +*self* but could be called *this* or any other variable name. + +Methods can be created manually with :class:`types.MethodType` which is +roughly equivalent to:: + + class Method: + "Emulate Py_MethodType in Objects/classobject.c" + + def __init__(self, func, obj): + self.__func__ = func + self.__self__ = obj + + def __call__(self, *args, **kwargs): + func = self.__func__ + obj = self.__self__ + return func(obj, *args, **kwargs) -To support method calls, functions include the :meth:`__get__` method for -binding methods during attribute access. This means that all functions are -non-data descriptors which return bound methods when they are invoked from an -object. In pure Python, it works like this:: +To support automatic creation of methods, functions include the +:meth:`__get__` method for binding methods during attribute access. This +means that functions are non-data descriptors which return bound methods +during dotted lookup from an instance. Here's how it works:: class Function: ... @@ -716,15 +772,20 @@ object. In pure Python, it works like this:: return self return types.MethodType(self, obj) -Running the following in class in the interpreter shows how the function +Running the following class in the interpreter shows how the function descriptor works in practice:: class D: def f(self, x): return x -Access through the class dictionary does not invoke :meth:`__get__`. Instead, -it just returns the underlying function object:: +The function has a :term:`qualified name` attribute to support introspection:: + + >>> D.f.__qualname__ + 'D.f' + +Accessing the function through the class dictionary does not invoke +:meth:`__get__`. Instead, it just returns the underlying function object:: >>> D.__dict__['f'] @@ -735,13 +796,8 @@ underlying function unchanged:: >>> D.f -The function has a :term:`qualified name` attribute to support introspection:: - - >>> D.f.__qualname__ - 'D.f' - -Dotted access from an instance calls :meth:`__get__` which returns a bound -method object:: +The interesting behavior occurs during dotted access from an instance. The +dotted lookup calls :meth:`__get__` which returns a bound method object:: >>> d = D() >>> d.f @@ -752,9 +808,13 @@ instance:: >>> d.f.__func__ + >>> d.f.__self__ <__main__.D object at 0x1012e1f98> +If you have ever wondered where *self* comes from in regular methods or where +*cls* comes from in class methods, this is it! + Static Methods and Class Methods -------------------------------- @@ -798,8 +858,8 @@ in statistical work but does not directly depend on a particular dataset. It can be called either from an object or the class: ``s.erf(1.5) --> .9332`` or ``Sample.erf(1.5) --> .9332``. -Since staticmethods return the underlying function with no changes, the example -calls are unexciting:: +Since static methods return the underlying function with no changes, the +example calls are unexciting:: class E: @staticmethod @@ -840,7 +900,7 @@ for whether the caller is an object or a class:: This behavior is useful whenever the function only needs to have a class reference and does not care about any underlying data. One use for -classmethods is to create alternate class constructors. The classmethod +class methods is to create alternate class constructors. The classmethod :func:`dict.fromkeys` creates a new dictionary from a list of keys. The pure Python equivalent is:: From webhook-mailer at python.org Sat Oct 24 23:36:59 2020 From: webhook-mailer at python.org (miss-islington) Date: Sun, 25 Oct 2020 03:36:59 -0000 Subject: [Python-checkins] bpo-33987: Add master ttk Frame to IDLE search dialogs (GH-22942) Message-ID: https://github.com/python/cpython/commit/9cf26b00e42787bc668c62f1b5dde814f8307259 commit: 9cf26b00e42787bc668c62f1b5dde814f8307259 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-24T20:36:55-07:00 summary: bpo-33987: Add master ttk Frame to IDLE search dialogs (GH-22942) (cherry picked from commit 5df6c99cb450fe2f30be681dbf68cd1d34d3bbe4) Co-authored-by: Mark Roseman files: A Misc/NEWS.d/next/IDLE/2020-10-24-21-27-37.bpo-33987.fIh9JL.rst M Lib/idlelib/NEWS.txt M Lib/idlelib/idle_test/test_searchbase.py M Lib/idlelib/searchbase.py diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt index 5ea33226cc0c3..a7f74814d7251 100644 --- a/Lib/idlelib/NEWS.txt +++ b/Lib/idlelib/NEWS.txt @@ -3,6 +3,9 @@ Released on 2020-12-07? ====================================== +bpo-33987: Mostly finish using ttk widgets, mainly for editor, +settings, and searches. Some patches by Mark Roseman. + bpo-41775: Make 'IDLE Shell' the shell title. bpo-35764: Rewrite the Calltips doc section. diff --git a/Lib/idlelib/idle_test/test_searchbase.py b/Lib/idlelib/idle_test/test_searchbase.py index aee0c4c69929a..8c9c410ebaf47 100644 --- a/Lib/idlelib/idle_test/test_searchbase.py +++ b/Lib/idlelib/idle_test/test_searchbase.py @@ -76,7 +76,7 @@ def test_create_widgets(self): def test_make_entry(self): equal = self.assertEqual self.dialog.row = 0 - self.dialog.top = self.root + self.dialog.frame = Frame(self.root) entry, label = self.dialog.make_entry("Test:", 'hello') equal(label['text'], 'Test:') @@ -89,7 +89,7 @@ def test_make_entry(self): equal(self.dialog.row, 1) def test_create_entries(self): - self.dialog.top = self.root + self.dialog.frame = Frame(self.root) self.dialog.row = 0 self.engine.setpat('hello') self.dialog.create_entries() @@ -97,7 +97,7 @@ def test_create_entries(self): def test_make_frame(self): self.dialog.row = 0 - self.dialog.top = self.root + self.dialog.frame = Frame(self.root) frame, label = self.dialog.make_frame() self.assertEqual(label, '') self.assertEqual(str(type(frame)), "") @@ -108,7 +108,7 @@ def test_make_frame(self): self.assertEqual(label['text'], 'testlabel') def btn_test_setup(self, meth): - self.dialog.top = self.root + self.dialog.frame = Frame(self.root) self.dialog.row = 0 return meth() @@ -140,13 +140,13 @@ def test_create_other_buttons(self): self.assertEqual(var.get(), state) def test_make_button(self): - self.dialog.top = self.root - self.dialog.buttonframe = Frame(self.dialog.top) + self.dialog.frame = Frame(self.root) + self.dialog.buttonframe = Frame(self.dialog.frame) btn = self.dialog.make_button('Test', self.dialog.close) self.assertEqual(btn['text'], 'Test') def test_create_command_buttons(self): - self.dialog.top = self.root + self.dialog.frame = Frame(self.root) self.dialog.create_command_buttons() # Look for close button command in buttonframe closebuttoncommand = '' diff --git a/Lib/idlelib/searchbase.py b/Lib/idlelib/searchbase.py index 6fba0b8e583f2..fbef87aa2d3d0 100644 --- a/Lib/idlelib/searchbase.py +++ b/Lib/idlelib/searchbase.py @@ -33,6 +33,7 @@ def __init__(self, root, engine): '''Initialize root, engine, and top attributes. top (level widget): set in create_widgets() called from open(). + frame: container for all widgets in dialog. text (Text searched): set in open(), only used in subclasses(). ent (ry): created in make_entry() called from create_entry(). row (of grid): 0 in create_widgets(), +1 in make_entry/frame(). @@ -83,10 +84,14 @@ def create_widgets(self): top.wm_title(self.title) top.wm_iconname(self.icon) self.top = top + self.frame = Frame(top, padding="5px") + self.frame.grid(sticky="nwes") + top.grid_columnconfigure(0, weight=100) + top.grid_rowconfigure(0, weight=100) self.row = 0 - self.top.grid_columnconfigure(0, pad=2, weight=0) - self.top.grid_columnconfigure(1, pad=2, minsize=100, weight=100) + self.frame.grid_columnconfigure(0, pad=2, weight=0) + self.frame.grid_columnconfigure(1, pad=2, minsize=100, weight=100) self.create_entries() # row 0 (and maybe 1), cols 0, 1 self.create_option_buttons() # next row, cols 0, 1 @@ -99,9 +104,9 @@ def make_entry(self, label_text, var): entry - gridded labeled Entry for text entry. label - Label widget, returned for testing. ''' - label = Label(self.top, text=label_text) + label = Label(self.frame, text=label_text) label.grid(row=self.row, column=0, sticky="nw") - entry = Entry(self.top, textvariable=var, exportselection=0) + entry = Entry(self.frame, textvariable=var, exportselection=0) entry.grid(row=self.row, column=1, sticky="nwe") self.row = self.row + 1 return entry, label @@ -117,11 +122,11 @@ def make_frame(self,labeltext=None): label - Label widget, returned for testing. ''' if labeltext: - label = Label(self.top, text=labeltext) + label = Label(self.frame, text=labeltext) label.grid(row=self.row, column=0, sticky="nw") else: label = '' - frame = Frame(self.top) + frame = Frame(self.frame) frame.grid(row=self.row, column=1, columnspan=1, sticky="nwe") self.row = self.row + 1 return frame, label @@ -171,7 +176,7 @@ def make_button(self, label, command, isdef=0): def create_command_buttons(self): "Place buttons in vertical command frame gridded on right." - f = self.buttonframe = Frame(self.top) + f = self.buttonframe = Frame(self.frame) f.grid(row=0,column=2,padx=2,pady=2,ipadx=2,ipady=2) b = self.make_button("Close", self.close) diff --git a/Misc/NEWS.d/next/IDLE/2020-10-24-21-27-37.bpo-33987.fIh9JL.rst b/Misc/NEWS.d/next/IDLE/2020-10-24-21-27-37.bpo-33987.fIh9JL.rst new file mode 100644 index 0000000000000..1e67edc03c658 --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2020-10-24-21-27-37.bpo-33987.fIh9JL.rst @@ -0,0 +1,3 @@ +Mostly finish using ttk widgets, mainly for editor, settings, +and searches. Some patches by Mark Roseman. + From webhook-mailer at python.org Sat Oct 24 23:39:20 2020 From: webhook-mailer at python.org (rhettinger) Date: Sun, 25 Oct 2020 03:39:20 -0000 Subject: [Python-checkins] Second round of updates to the descriptor howto guide (GH-22946) (GH-22958) Message-ID: https://github.com/python/cpython/commit/3d43f1dce3e9aaded38f9a2c73e3c66acf85505c commit: 3d43f1dce3e9aaded38f9a2c73e3c66acf85505c branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: rhettinger date: 2020-10-24T20:39:15-07:00 summary: Second round of updates to the descriptor howto guide (GH-22946) (GH-22958) files: M Doc/howto/descriptor.rst diff --git a/Doc/howto/descriptor.rst b/Doc/howto/descriptor.rst index 4e9fad30d31c4..bed4078e3a3a9 100644 --- a/Doc/howto/descriptor.rst +++ b/Doc/howto/descriptor.rst @@ -29,8 +29,8 @@ This HowTo guide has three major sections: Primer ^^^^^^ -In this primer, we start with most basic possible example and then we'll add -new capabilities one by one. +In this primer, we start with the most basic possible example and then we'll +add new capabilities one by one. Simple example: A descriptor that returns a constant @@ -197,7 +197,7 @@ be recorded, giving each descriptor its own *public_name* and *private_name*:: import logging - logging.basicConfig(level=logging.INFO) + logging.basicConfig(level=logging.INFO, force=True) class LoggedAccess: @@ -258,6 +258,10 @@ Closing thoughts A :term:`descriptor` is what we call any object that defines :meth:`__get__`, :meth:`__set__`, or :meth:`__delete__`. +Optionally, descriptors can have a :meth:`__set_name__` method. This is only +used in cases where a descriptor needs to know either the class where it is +created or the name of class variable it was assigned to. + Descriptors get invoked by the dot operator during attribute lookup. If a descriptor is accessed indirectly with ``vars(some_class)[descriptor_name]``, the descriptor instance is returned without invoking it. @@ -291,7 +295,7 @@ Validator class A validator is a descriptor for managed attribute access. Prior to storing any data, it verifies that the new value meets various type and range restrictions. If those restrictions aren't met, it raises an exception to -prevents data corruption at its source. +prevent data corruption at its source. This :class:`Validator` class is both an :term:`abstract base class` and a managed attribute descriptor:: @@ -438,12 +442,12 @@ In general, a descriptor is an object attribute with "binding behavior", one whose attribute access has been overridden by methods in the descriptor protocol. Those methods are :meth:`__get__`, :meth:`__set__`, and :meth:`__delete__`. If any of those methods are defined for an object, it is -said to be a descriptor. +said to be a :term:`descriptor`. The default behavior for attribute access is to get, set, or delete the attribute from an object's dictionary. For instance, ``a.x`` has a lookup chain starting with ``a.__dict__['x']``, then ``type(a).__dict__['x']``, and -continuing through the base classes of ``type(a)`` excluding metaclasses. If the +continuing through the base classes of ``type(a)``. If the looked-up value is an object defining one of the descriptor methods, then Python may override the default behavior and invoke the descriptor method instead. Where this occurs in the precedence chain depends on which descriptor methods @@ -492,60 +496,76 @@ Invoking Descriptors A descriptor can be called directly by its method name. For example, ``d.__get__(obj)``. -Alternatively, it is more common for a descriptor to be invoked automatically -upon attribute access. For example, ``obj.d`` looks up ``d`` in the dictionary -of ``obj``. If ``d`` defines the method :meth:`__get__`, then ``d.__get__(obj)`` +But it is more common for a descriptor to be invoked automatically from +attribute access. The expression ``obj.d`` looks up ``d`` in the dictionary of +``obj``. If ``d`` defines the method :meth:`__get__`, then ``d.__get__(obj)`` is invoked according to the precedence rules listed below. -The details of invocation depend on whether ``obj`` is an object or a class. +The details of invocation depend on whether ``obj`` is an object, class, or +instance of super. + +**Objects**: The machinery is in :meth:`object.__getattribute__`. + +It transforms ``b.x`` into ``type(b).__dict__['x'].__get__(b, type(b))``. -For objects, the machinery is in :meth:`object.__getattribute__` which -transforms ``b.x`` into ``type(b).__dict__['x'].__get__(b, type(b))``. The -implementation works through a precedence chain that gives data descriptors +The implementation works through a precedence chain that gives data descriptors priority over instance variables, instance variables priority over non-data descriptors, and assigns lowest priority to :meth:`__getattr__` if provided. + The full C implementation can be found in :c:func:`PyObject_GenericGetAttr()` in :source:`Objects/object.c`. -For classes, the machinery is in :meth:`type.__getattribute__` which transforms -``B.x`` into ``B.__dict__['x'].__get__(None, B)``. In pure Python, it looks -like:: +**Classes**: The machinery is in :meth:`type.__getattribute__`. + +It transforms ``A.x`` into ``A.__dict__['x'].__get__(None, A)``. + +In pure Python, it looks like this:: - def __getattribute__(self, key): + def __getattribute__(cls, key): "Emulate type_getattro() in Objects/typeobject.c" - v = object.__getattribute__(self, key) + v = object.__getattribute__(cls, key) if hasattr(v, '__get__'): - return v.__get__(None, self) + return v.__get__(None, cls) return v -The important points to remember are: +**Super**: The machinery is in the custom :meth:`__getattribute__` method for +object returned by :class:`super()`. -* descriptors are invoked by the :meth:`__getattribute__` method -* overriding :meth:`__getattribute__` prevents automatic descriptor calls -* :meth:`object.__getattribute__` and :meth:`type.__getattribute__` make - different calls to :meth:`__get__`. -* data descriptors always override instance dictionaries. -* non-data descriptors may be overridden by instance dictionaries. +The attribute lookup ``super(A, obj).m`` searches ``obj.__class__.__mro__`` for +the base class ``B`` immediately following ``A`` and then returns +``B.__dict__['m'].__get__(obj, A)``. -The object returned by ``super()`` also has a custom :meth:`__getattribute__` -method for invoking descriptors. The attribute lookup ``super(B, obj).m`` searches -``obj.__class__.__mro__`` for the base class ``A`` immediately following ``B`` -and then returns ``A.__dict__['m'].__get__(obj, B)``. If not a descriptor, -``m`` is returned unchanged. If not in the dictionary, ``m`` reverts to a -search using :meth:`object.__getattribute__`. +If not a descriptor, ``m`` is returned unchanged. If not in the dictionary, +``m`` reverts to a search using :meth:`object.__getattribute__`. The implementation details are in :c:func:`super_getattro()` in -:source:`Objects/typeobject.c`. and a pure Python equivalent can be found in +:source:`Objects/typeobject.c`. A pure Python equivalent can be found in `Guido's Tutorial`_. .. _`Guido's Tutorial`: https://www.python.org/download/releases/2.2.3/descrintro/#cooperation -The details above show that the mechanism for descriptors is embedded in the -:meth:`__getattribute__()` methods for :class:`object`, :class:`type`, and -:func:`super`. Classes inherit this machinery when they derive from -:class:`object` or if they have a metaclass providing similar functionality. -Likewise, classes can turn-off descriptor invocation by overriding -:meth:`__getattribute__()`. +**Summary**: The details listed above show that the mechanism for descriptors is +embedded in the :meth:`__getattribute__()` methods for :class:`object`, +:class:`type`, and :func:`super`. + +The important points to remember are: + +* Descriptors are invoked by the :meth:`__getattribute__` method. + +* Classes inherit this machinery from :class:`object`, :class:`type`, or + :func:`super`. + +* Overriding :meth:`__getattribute__` prevents automatic descriptor calls + because all the descriptor logic is in that method. + +* :meth:`object.__getattribute__` and :meth:`type.__getattribute__` make + different calls to :meth:`__get__`. The first includes the instance and may + include the class. The second puts in ``None`` for the instance and always + includes the class. + +* Data descriptors always override instance dictionaries. + +* Non-data descriptors may be overridden by instance dictionaries. Automatic Name Notification @@ -569,47 +589,70 @@ afterwards, :meth:`__set_name__` will need to be called manually. Descriptor Example ------------------ -The following code creates a class whose objects are data descriptors which -print a message for each get or set. Overriding :meth:`__getattribute__` is -alternate approach that could do this for every attribute. However, this -descriptor is useful for monitoring just a few chosen attributes:: +The following code is simplified skeleton showing how data descriptors could +be used to implement an `object relational mapping +`_. - class RevealAccess: - """A data descriptor that sets and returns values - normally and prints a message logging their access. - """ +The essential idea is that instances only hold keys to a database table. The +actual data is stored in an external table that is being dynamically updated:: - def __init__(self, initval=None, name='var'): - self.val = initval - self.name = name + class Field: + + def __set_name__(self, owner, name): + self.fetch = f'SELECT {name} FROM {owner.table} WHERE {owner.key}=?;' + self.store = f'UPDATE {owner.table} SET {name}=? WHERE {owner.key}=?;' def __get__(self, obj, objtype=None): - print('Retrieving', self.name) - return self.val + return conn.execute(self.fetch, [obj.key]).fetchone()[0] - def __set__(self, obj, val): - print('Updating', self.name) - self.val = val + def __set__(self, obj, value): + conn.execute(self.store, [value, obj.key]) + conn.commit() - class B: - x = RevealAccess(10, 'var "x"') - y = 5 +We can use the :class:`Field` to define "models" that describe the schema for +each table in a database:: - >>> m = B() - >>> m.x - Retrieving var "x" - 10 - >>> m.x = 20 - Updating var "x" - >>> m.x - Retrieving var "x" - 20 - >>> m.y - 5 + class Movie: + table = 'Movies' # Table name + key = 'title' # Primary key + director = Field() + year = Field() -The protocol is simple and offers exciting possibilities. Several use cases are -so common that they have been packaged into individual function calls. -Properties, bound methods, static methods, and class methods are all + def __init__(self, key): + self.key = key + + class Song: + table = 'Music' + key = 'title' + artist = Field() + year = Field() + genre = Field() + + def __init__(self, key): + self.key = key + +An interactive session shows how data is retrieved from the database and how +it can be updated:: + + >>> import sqlite3 + >>> conn = sqlite3.connect('entertainment.db') + + >>> Movie('Star Wars').director + 'George Lucas' + >>> jaws = Movie('Jaws') + >>> f'Released in {jaws.year} by {jaws.director}' + 'Released in 1975 by Steven Spielberg' + + >>> Song('Country Roads').artist + 'John Denver' + + >>> Movie('Star Wars').director = 'J.J. Abrams' + >>> Movie('Star Wars').director + 'J.J. Abrams' + +The descriptor protocol is simple and offers exciting possibilities. Several +use cases are so common that they have been packaged into individual function +calls. Properties, bound methods, static methods, and class methods are all based on the descriptor protocol. @@ -619,7 +662,7 @@ Properties Calling :func:`property` is a succinct way of building a data descriptor that triggers function calls upon access to an attribute. Its signature is:: - property(fget=None, fset=None, fdel=None, doc=None) -> property attribute + property(fget=None, fset=None, fdel=None, doc=None) -> property The documentation shows a typical use to define a managed attribute ``x``:: @@ -695,17 +738,30 @@ Functions and Methods Python's object oriented features are built upon a function based environment. Using non-data descriptors, the two are merged seamlessly. -Class dictionaries store methods as functions. In a class definition, methods -are written using :keyword:`def` or :keyword:`lambda`, the usual tools for -creating functions. Methods only differ from regular functions in that the -first argument is reserved for the object instance. By Python convention, the -instance reference is called *self* but may be called *this* or any other -variable name. +Functions stored in class dictionaries get turned into methods when invoked. +Methods only differ from regular functions in that the object instance is +prepended to the other arguments. By convention, the instance is called +*self* but could be called *this* or any other variable name. + +Methods can be created manually with :class:`types.MethodType` which is +roughly equivalent to:: + + class Method: + "Emulate Py_MethodType in Objects/classobject.c" + + def __init__(self, func, obj): + self.__func__ = func + self.__self__ = obj + + def __call__(self, *args, **kwargs): + func = self.__func__ + obj = self.__self__ + return func(obj, *args, **kwargs) -To support method calls, functions include the :meth:`__get__` method for -binding methods during attribute access. This means that all functions are -non-data descriptors which return bound methods when they are invoked from an -object. In pure Python, it works like this:: +To support automatic creation of methods, functions include the +:meth:`__get__` method for binding methods during attribute access. This +means that functions are non-data descriptors which return bound methods +during dotted lookup from an instance. Here's how it works:: class Function: ... @@ -716,15 +772,20 @@ object. In pure Python, it works like this:: return self return types.MethodType(self, obj) -Running the following in class in the interpreter shows how the function +Running the following class in the interpreter shows how the function descriptor works in practice:: class D: def f(self, x): return x -Access through the class dictionary does not invoke :meth:`__get__`. Instead, -it just returns the underlying function object:: +The function has a :term:`qualified name` attribute to support introspection:: + + >>> D.f.__qualname__ + 'D.f' + +Accessing the function through the class dictionary does not invoke +:meth:`__get__`. Instead, it just returns the underlying function object:: >>> D.__dict__['f'] @@ -735,13 +796,8 @@ underlying function unchanged:: >>> D.f -The function has a :term:`qualified name` attribute to support introspection:: - - >>> D.f.__qualname__ - 'D.f' - -Dotted access from an instance calls :meth:`__get__` which returns a bound -method object:: +The interesting behavior occurs during dotted access from an instance. The +dotted lookup calls :meth:`__get__` which returns a bound method object:: >>> d = D() >>> d.f @@ -752,9 +808,13 @@ instance:: >>> d.f.__func__ + >>> d.f.__self__ <__main__.D object at 0x1012e1f98> +If you have ever wondered where *self* comes from in regular methods or where +*cls* comes from in class methods, this is it! + Static Methods and Class Methods -------------------------------- @@ -798,8 +858,8 @@ in statistical work but does not directly depend on a particular dataset. It can be called either from an object or the class: ``s.erf(1.5) --> .9332`` or ``Sample.erf(1.5) --> .9332``. -Since staticmethods return the underlying function with no changes, the example -calls are unexciting:: +Since static methods return the underlying function with no changes, the +example calls are unexciting:: class E: @staticmethod @@ -840,7 +900,7 @@ for whether the caller is an object or a class:: This behavior is useful whenever the function only needs to have a class reference and does not care about any underlying data. One use for -classmethods is to create alternate class constructors. The classmethod +class methods is to create alternate class constructors. The classmethod :func:`dict.fromkeys` creates a new dictionary from a list of keys. The pure Python equivalent is:: From webhook-mailer at python.org Sat Oct 24 23:41:30 2020 From: webhook-mailer at python.org (miss-islington) Date: Sun, 25 Oct 2020 03:41:30 -0000 Subject: [Python-checkins] bpo-33987: Add master ttk Frame to IDLE search dialogs (GH-22942) Message-ID: https://github.com/python/cpython/commit/8485d3b911c3cb73ed6217a47c7720f9277efc01 commit: 8485d3b911c3cb73ed6217a47c7720f9277efc01 branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-24T20:41:26-07:00 summary: bpo-33987: Add master ttk Frame to IDLE search dialogs (GH-22942) (cherry picked from commit 5df6c99cb450fe2f30be681dbf68cd1d34d3bbe4) Co-authored-by: Mark Roseman files: A Misc/NEWS.d/next/IDLE/2020-10-24-21-27-37.bpo-33987.fIh9JL.rst M Lib/idlelib/NEWS.txt M Lib/idlelib/idle_test/test_searchbase.py M Lib/idlelib/searchbase.py diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt index e538329d978b3..9d28a39a37c5b 100644 --- a/Lib/idlelib/NEWS.txt +++ b/Lib/idlelib/NEWS.txt @@ -3,6 +3,9 @@ Released on 2020-12-?? ====================================== +bpo-33987: Mostly finish using ttk widgets, mainly for editor, +settings, and searches. Some patches by Mark Roseman. + bpo-41775: Make 'IDLE Shell' the shell title. bpo-35764: Rewrite the Calltips doc section. diff --git a/Lib/idlelib/idle_test/test_searchbase.py b/Lib/idlelib/idle_test/test_searchbase.py index aee0c4c69929a..8c9c410ebaf47 100644 --- a/Lib/idlelib/idle_test/test_searchbase.py +++ b/Lib/idlelib/idle_test/test_searchbase.py @@ -76,7 +76,7 @@ def test_create_widgets(self): def test_make_entry(self): equal = self.assertEqual self.dialog.row = 0 - self.dialog.top = self.root + self.dialog.frame = Frame(self.root) entry, label = self.dialog.make_entry("Test:", 'hello') equal(label['text'], 'Test:') @@ -89,7 +89,7 @@ def test_make_entry(self): equal(self.dialog.row, 1) def test_create_entries(self): - self.dialog.top = self.root + self.dialog.frame = Frame(self.root) self.dialog.row = 0 self.engine.setpat('hello') self.dialog.create_entries() @@ -97,7 +97,7 @@ def test_create_entries(self): def test_make_frame(self): self.dialog.row = 0 - self.dialog.top = self.root + self.dialog.frame = Frame(self.root) frame, label = self.dialog.make_frame() self.assertEqual(label, '') self.assertEqual(str(type(frame)), "") @@ -108,7 +108,7 @@ def test_make_frame(self): self.assertEqual(label['text'], 'testlabel') def btn_test_setup(self, meth): - self.dialog.top = self.root + self.dialog.frame = Frame(self.root) self.dialog.row = 0 return meth() @@ -140,13 +140,13 @@ def test_create_other_buttons(self): self.assertEqual(var.get(), state) def test_make_button(self): - self.dialog.top = self.root - self.dialog.buttonframe = Frame(self.dialog.top) + self.dialog.frame = Frame(self.root) + self.dialog.buttonframe = Frame(self.dialog.frame) btn = self.dialog.make_button('Test', self.dialog.close) self.assertEqual(btn['text'], 'Test') def test_create_command_buttons(self): - self.dialog.top = self.root + self.dialog.frame = Frame(self.root) self.dialog.create_command_buttons() # Look for close button command in buttonframe closebuttoncommand = '' diff --git a/Lib/idlelib/searchbase.py b/Lib/idlelib/searchbase.py index 6fba0b8e583f2..fbef87aa2d3d0 100644 --- a/Lib/idlelib/searchbase.py +++ b/Lib/idlelib/searchbase.py @@ -33,6 +33,7 @@ def __init__(self, root, engine): '''Initialize root, engine, and top attributes. top (level widget): set in create_widgets() called from open(). + frame: container for all widgets in dialog. text (Text searched): set in open(), only used in subclasses(). ent (ry): created in make_entry() called from create_entry(). row (of grid): 0 in create_widgets(), +1 in make_entry/frame(). @@ -83,10 +84,14 @@ def create_widgets(self): top.wm_title(self.title) top.wm_iconname(self.icon) self.top = top + self.frame = Frame(top, padding="5px") + self.frame.grid(sticky="nwes") + top.grid_columnconfigure(0, weight=100) + top.grid_rowconfigure(0, weight=100) self.row = 0 - self.top.grid_columnconfigure(0, pad=2, weight=0) - self.top.grid_columnconfigure(1, pad=2, minsize=100, weight=100) + self.frame.grid_columnconfigure(0, pad=2, weight=0) + self.frame.grid_columnconfigure(1, pad=2, minsize=100, weight=100) self.create_entries() # row 0 (and maybe 1), cols 0, 1 self.create_option_buttons() # next row, cols 0, 1 @@ -99,9 +104,9 @@ def make_entry(self, label_text, var): entry - gridded labeled Entry for text entry. label - Label widget, returned for testing. ''' - label = Label(self.top, text=label_text) + label = Label(self.frame, text=label_text) label.grid(row=self.row, column=0, sticky="nw") - entry = Entry(self.top, textvariable=var, exportselection=0) + entry = Entry(self.frame, textvariable=var, exportselection=0) entry.grid(row=self.row, column=1, sticky="nwe") self.row = self.row + 1 return entry, label @@ -117,11 +122,11 @@ def make_frame(self,labeltext=None): label - Label widget, returned for testing. ''' if labeltext: - label = Label(self.top, text=labeltext) + label = Label(self.frame, text=labeltext) label.grid(row=self.row, column=0, sticky="nw") else: label = '' - frame = Frame(self.top) + frame = Frame(self.frame) frame.grid(row=self.row, column=1, columnspan=1, sticky="nwe") self.row = self.row + 1 return frame, label @@ -171,7 +176,7 @@ def make_button(self, label, command, isdef=0): def create_command_buttons(self): "Place buttons in vertical command frame gridded on right." - f = self.buttonframe = Frame(self.top) + f = self.buttonframe = Frame(self.frame) f.grid(row=0,column=2,padx=2,pady=2,ipadx=2,ipady=2) b = self.make_button("Close", self.close) diff --git a/Misc/NEWS.d/next/IDLE/2020-10-24-21-27-37.bpo-33987.fIh9JL.rst b/Misc/NEWS.d/next/IDLE/2020-10-24-21-27-37.bpo-33987.fIh9JL.rst new file mode 100644 index 0000000000000..1e67edc03c658 --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2020-10-24-21-27-37.bpo-33987.fIh9JL.rst @@ -0,0 +1,3 @@ +Mostly finish using ttk widgets, mainly for editor, settings, +and searches. Some patches by Mark Roseman. + From webhook-mailer at python.org Sun Oct 25 02:08:21 2020 From: webhook-mailer at python.org (pablogsal) Date: Sun, 25 Oct 2020 06:08:21 -0000 Subject: [Python-checkins] Correctly compare the hint against the keys in _PyDict_GetItemHint (GH-22960) Message-ID: https://github.com/python/cpython/commit/492d513ccbebeec40a8ba85cbd894a027ca5b2b3 commit: 492d513ccbebeec40a8ba85cbd894a027ca5b2b3 branch: master author: Pablo Galindo committer: pablogsal date: 2020-10-25T06:08:17Z summary: Correctly compare the hint against the keys in _PyDict_GetItemHint (GH-22960) files: M Objects/dictobject.c diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 8e74962345286..42d71e56d4547 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -1448,7 +1448,7 @@ _PyDict_GetItemHint(PyDictObject *mp, PyObject *key, assert(PyDict_CheckExact((PyObject*)mp)); assert(PyUnicode_CheckExact(key)); - if (hint >= 0 && hint < _PyDict_KeysSize(mp->ma_keys)) { + if (hint >= 0 && hint < mp->ma_keys->dk_nentries) { PyObject *res = NULL; PyDictKeyEntry *ep = DK_ENTRIES(mp->ma_keys) + (size_t)hint; From webhook-mailer at python.org Sun Oct 25 10:12:58 2020 From: webhook-mailer at python.org (rhettinger) Date: Sun, 25 Oct 2020 14:12:58 -0000 Subject: [Python-checkins] Split-out a fourth section in the descriptor HowTo guide (GH-22965) Message-ID: https://github.com/python/cpython/commit/e6a7ea4f2e0d6892ebd929235b1333f04b517eec commit: e6a7ea4f2e0d6892ebd929235b1333f04b517eec branch: master author: Raymond Hettinger committer: rhettinger date: 2020-10-25T07:12:50-07:00 summary: Split-out a fourth section in the descriptor HowTo guide (GH-22965) files: M Doc/howto/descriptor.rst diff --git a/Doc/howto/descriptor.rst b/Doc/howto/descriptor.rst index bed4078e3a3a9..f1d1ab1d1d610 100644 --- a/Doc/howto/descriptor.rst +++ b/Doc/howto/descriptor.rst @@ -13,7 +13,7 @@ Descriptor HowTo Guide :term:`Descriptors ` let objects customize attribute lookup, storage, and deletion. -This HowTo guide has three major sections: +This guide has four major sections: 1) The "primer" gives a basic overview, moving gently from simple examples, adding one feature at a time. It is a great place to start. @@ -25,6 +25,11 @@ This HowTo guide has three major sections: detailed mechanics of how descriptors work. Most people don't need this level of detail. +4) The last section has pure Python equivalents for built-in descriptors that + are written in C. Read this if you're curious about how functions turn + into bound methods or about how to implement common tools like + :func:`classmethod`, :func:`staticmethod`, and :func:`property`. + Primer ^^^^^^ @@ -99,7 +104,7 @@ different, updated answers each time:: 3 >>> os.system('touch games/newfile') # Add a fourth file to the directory 0 - >>> g.size + >>> g.size # Automatically updated 4 >>> s.size # The songs directory has twenty files 20 @@ -197,7 +202,7 @@ be recorded, giving each descriptor its own *public_name* and *private_name*:: import logging - logging.basicConfig(level=logging.INFO, force=True) + logging.basicConfig(level=logging.INFO) class LoggedAccess: @@ -259,7 +264,7 @@ A :term:`descriptor` is what we call any object that defines :meth:`__get__`, :meth:`__set__`, or :meth:`__delete__`. Optionally, descriptors can have a :meth:`__set_name__` method. This is only -used in cases where a descriptor needs to know either the class where it is +used in cases where a descriptor needs to know either the class where it was created or the name of class variable it was assigned to. Descriptors get invoked by the dot operator during attribute lookup. If a @@ -318,7 +323,7 @@ managed attribute descriptor:: def validate(self, value): pass -Custom validators need to subclass from :class:`Validator` and supply a +Custom validators need to inherit from :class:`Validator` and must supply a :meth:`validate` method to test various restrictions as needed. @@ -334,8 +339,9 @@ Here are three practical data validation utilities: minimum or maximum. 3) :class:`String` verifies that a value is a :class:`str`. Optionally, it - validates a given minimum or maximum length. Optionally, it can test for - another predicate as well. + validates a given minimum or maximum length. It can validate a + user-defined `predicate + `_ as well. :: @@ -398,7 +404,7 @@ Here's how the data validators can be used in a real class:: class Component: name = String(minsize=3, maxsize=10, predicate=str.isupper) - kind = OneOf('plastic', 'metal') + kind = OneOf('wood', 'metal', 'plastic') quantity = Number(minvalue=0) def __init__(self, name, kind, quantity): @@ -426,9 +432,7 @@ Abstract -------- Defines descriptors, summarizes the protocol, and shows how descriptors are -called. Examines a custom descriptor and several built-in Python descriptors -including functions, properties, static methods, and class methods. Shows how -each works by giving a pure Python equivalent and a sample application. +called. Provides an example showing how object relational mappings work. Learning about descriptors not only provides access to a larger toolset, it creates a deeper understanding of how Python works and an appreciation for the @@ -519,24 +523,17 @@ The full C implementation can be found in :c:func:`PyObject_GenericGetAttr()` in It transforms ``A.x`` into ``A.__dict__['x'].__get__(None, A)``. -In pure Python, it looks like this:: - - def __getattribute__(cls, key): - "Emulate type_getattro() in Objects/typeobject.c" - v = object.__getattribute__(cls, key) - if hasattr(v, '__get__'): - return v.__get__(None, cls) - return v +The full C implementation can be found in :c:func:`type_getattro()` in +:source:`Objects/typeobject.c`. **Super**: The machinery is in the custom :meth:`__getattribute__` method for object returned by :class:`super()`. The attribute lookup ``super(A, obj).m`` searches ``obj.__class__.__mro__`` for the base class ``B`` immediately following ``A`` and then returns -``B.__dict__['m'].__get__(obj, A)``. - -If not a descriptor, ``m`` is returned unchanged. If not in the dictionary, -``m`` reverts to a search using :meth:`object.__getattribute__`. +``B.__dict__['m'].__get__(obj, A)``. If not a descriptor, ``m`` is returned +unchanged. If not in the dictionary, ``m`` reverts to a search using +:meth:`object.__getattribute__`. The implementation details are in :c:func:`super_getattro()` in :source:`Objects/typeobject.c`. A pure Python equivalent can be found in @@ -544,9 +541,9 @@ The implementation details are in :c:func:`super_getattro()` in .. _`Guido's Tutorial`: https://www.python.org/download/releases/2.2.3/descrintro/#cooperation -**Summary**: The details listed above show that the mechanism for descriptors is -embedded in the :meth:`__getattribute__()` methods for :class:`object`, -:class:`type`, and :func:`super`. +**Summary**: The mechanism for descriptors is embedded in the +:meth:`__getattribute__()` methods for :class:`object`, :class:`type`, and +:func:`super`. The important points to remember are: @@ -586,15 +583,16 @@ place at the time of class creation. If descriptors are added to the class afterwards, :meth:`__set_name__` will need to be called manually. -Descriptor Example ------------------- +ORM Example +----------- The following code is simplified skeleton showing how data descriptors could be used to implement an `object relational mapping `_. -The essential idea is that instances only hold keys to a database table. The -actual data is stored in an external table that is being dynamically updated:: +The essential idea is that the data is stored in an external database. The +Python instances only hold keys to the database's tables. Descriptors take +care of lookups or updates:: class Field: @@ -609,8 +607,8 @@ actual data is stored in an external table that is being dynamically updated:: conn.execute(self.store, [value, obj.key]) conn.commit() -We can use the :class:`Field` to define "models" that describe the schema for -each table in a database:: +We can use the :class:`Field` class to define "models" that describe the schema +for each table in a database:: class Movie: table = 'Movies' # Table name @@ -650,10 +648,13 @@ it can be updated:: >>> Movie('Star Wars').director 'J.J. Abrams' +Pure Python Equivalents +^^^^^^^^^^^^^^^^^^^^^^^ + The descriptor protocol is simple and offers exciting possibilities. Several -use cases are so common that they have been packaged into individual function -calls. Properties, bound methods, static methods, and class methods are all -based on the descriptor protocol. +use cases are so common that they have been prepackaged into builtin tools. +Properties, bound methods, static methods, and class methods are all based on +the descriptor protocol. Properties @@ -746,7 +747,7 @@ prepended to the other arguments. By convention, the instance is called Methods can be created manually with :class:`types.MethodType` which is roughly equivalent to:: - class Method: + class MethodType: "Emulate Py_MethodType in Objects/classobject.c" def __init__(self, func, obj): @@ -770,7 +771,7 @@ during dotted lookup from an instance. Here's how it works:: "Simulate func_descr_get() in Objects/funcobject.c" if obj is None: return self - return types.MethodType(self, obj) + return MethodType(self, obj) Running the following class in the interpreter shows how the function descriptor works in practice:: @@ -816,8 +817,8 @@ If you have ever wondered where *self* comes from in regular methods or where *cls* comes from in class methods, this is it! -Static Methods and Class Methods --------------------------------- +Static Methods +-------------- Non-data descriptors provide a simple mechanism for variations on the usual patterns of binding functions into methods. @@ -883,6 +884,10 @@ Using the non-data descriptor protocol, a pure Python version of def __get__(self, obj, objtype=None): return self.f + +Class Methods +------------- + Unlike static methods, class methods prepend the class reference to the argument list before calling the function. This format is the same for whether the caller is an object or a class:: @@ -897,12 +902,11 @@ for whether the caller is an object or a class:: >>> print(F().f(3)) ('F', 3) - -This behavior is useful whenever the function only needs to have a class -reference and does not care about any underlying data. One use for -class methods is to create alternate class constructors. The classmethod -:func:`dict.fromkeys` creates a new dictionary from a list of keys. The pure -Python equivalent is:: +This behavior is useful whenever the method only needs to have a class +reference and does rely on data stored in a specific instance. One use for +class methods is to create alternate class constructors. For example, the +classmethod :func:`dict.fromkeys` creates a new dictionary from a list of +keys. The pure Python equivalent is:: class Dict: ... @@ -934,7 +938,7 @@ Using the non-data descriptor protocol, a pure Python version of cls = type(obj) if hasattr(obj, '__get__'): return self.f.__get__(cls) - return types.MethodType(self.f, cls) + return MethodType(self.f, cls) The code path for ``hasattr(obj, '__get__')`` was added in Python 3.9 and makes it possible for :func:`classmethod` to support chained decorators. From webhook-mailer at python.org Sun Oct 25 10:36:10 2020 From: webhook-mailer at python.org (rhettinger) Date: Sun, 25 Oct 2020 14:36:10 -0000 Subject: [Python-checkins] Split-out a fourth section in the descriptor HowTo guide (GH-22965) (GH-22969) Message-ID: https://github.com/python/cpython/commit/2990aef0c804b5d8956c221540bb639983fc0ae4 commit: 2990aef0c804b5d8956c221540bb639983fc0ae4 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: rhettinger date: 2020-10-25T07:35:56-07:00 summary: Split-out a fourth section in the descriptor HowTo guide (GH-22965) (GH-22969) files: M Doc/howto/descriptor.rst diff --git a/Doc/howto/descriptor.rst b/Doc/howto/descriptor.rst index bed4078e3a3a9..f1d1ab1d1d610 100644 --- a/Doc/howto/descriptor.rst +++ b/Doc/howto/descriptor.rst @@ -13,7 +13,7 @@ Descriptor HowTo Guide :term:`Descriptors ` let objects customize attribute lookup, storage, and deletion. -This HowTo guide has three major sections: +This guide has four major sections: 1) The "primer" gives a basic overview, moving gently from simple examples, adding one feature at a time. It is a great place to start. @@ -25,6 +25,11 @@ This HowTo guide has three major sections: detailed mechanics of how descriptors work. Most people don't need this level of detail. +4) The last section has pure Python equivalents for built-in descriptors that + are written in C. Read this if you're curious about how functions turn + into bound methods or about how to implement common tools like + :func:`classmethod`, :func:`staticmethod`, and :func:`property`. + Primer ^^^^^^ @@ -99,7 +104,7 @@ different, updated answers each time:: 3 >>> os.system('touch games/newfile') # Add a fourth file to the directory 0 - >>> g.size + >>> g.size # Automatically updated 4 >>> s.size # The songs directory has twenty files 20 @@ -197,7 +202,7 @@ be recorded, giving each descriptor its own *public_name* and *private_name*:: import logging - logging.basicConfig(level=logging.INFO, force=True) + logging.basicConfig(level=logging.INFO) class LoggedAccess: @@ -259,7 +264,7 @@ A :term:`descriptor` is what we call any object that defines :meth:`__get__`, :meth:`__set__`, or :meth:`__delete__`. Optionally, descriptors can have a :meth:`__set_name__` method. This is only -used in cases where a descriptor needs to know either the class where it is +used in cases where a descriptor needs to know either the class where it was created or the name of class variable it was assigned to. Descriptors get invoked by the dot operator during attribute lookup. If a @@ -318,7 +323,7 @@ managed attribute descriptor:: def validate(self, value): pass -Custom validators need to subclass from :class:`Validator` and supply a +Custom validators need to inherit from :class:`Validator` and must supply a :meth:`validate` method to test various restrictions as needed. @@ -334,8 +339,9 @@ Here are three practical data validation utilities: minimum or maximum. 3) :class:`String` verifies that a value is a :class:`str`. Optionally, it - validates a given minimum or maximum length. Optionally, it can test for - another predicate as well. + validates a given minimum or maximum length. It can validate a + user-defined `predicate + `_ as well. :: @@ -398,7 +404,7 @@ Here's how the data validators can be used in a real class:: class Component: name = String(minsize=3, maxsize=10, predicate=str.isupper) - kind = OneOf('plastic', 'metal') + kind = OneOf('wood', 'metal', 'plastic') quantity = Number(minvalue=0) def __init__(self, name, kind, quantity): @@ -426,9 +432,7 @@ Abstract -------- Defines descriptors, summarizes the protocol, and shows how descriptors are -called. Examines a custom descriptor and several built-in Python descriptors -including functions, properties, static methods, and class methods. Shows how -each works by giving a pure Python equivalent and a sample application. +called. Provides an example showing how object relational mappings work. Learning about descriptors not only provides access to a larger toolset, it creates a deeper understanding of how Python works and an appreciation for the @@ -519,24 +523,17 @@ The full C implementation can be found in :c:func:`PyObject_GenericGetAttr()` in It transforms ``A.x`` into ``A.__dict__['x'].__get__(None, A)``. -In pure Python, it looks like this:: - - def __getattribute__(cls, key): - "Emulate type_getattro() in Objects/typeobject.c" - v = object.__getattribute__(cls, key) - if hasattr(v, '__get__'): - return v.__get__(None, cls) - return v +The full C implementation can be found in :c:func:`type_getattro()` in +:source:`Objects/typeobject.c`. **Super**: The machinery is in the custom :meth:`__getattribute__` method for object returned by :class:`super()`. The attribute lookup ``super(A, obj).m`` searches ``obj.__class__.__mro__`` for the base class ``B`` immediately following ``A`` and then returns -``B.__dict__['m'].__get__(obj, A)``. - -If not a descriptor, ``m`` is returned unchanged. If not in the dictionary, -``m`` reverts to a search using :meth:`object.__getattribute__`. +``B.__dict__['m'].__get__(obj, A)``. If not a descriptor, ``m`` is returned +unchanged. If not in the dictionary, ``m`` reverts to a search using +:meth:`object.__getattribute__`. The implementation details are in :c:func:`super_getattro()` in :source:`Objects/typeobject.c`. A pure Python equivalent can be found in @@ -544,9 +541,9 @@ The implementation details are in :c:func:`super_getattro()` in .. _`Guido's Tutorial`: https://www.python.org/download/releases/2.2.3/descrintro/#cooperation -**Summary**: The details listed above show that the mechanism for descriptors is -embedded in the :meth:`__getattribute__()` methods for :class:`object`, -:class:`type`, and :func:`super`. +**Summary**: The mechanism for descriptors is embedded in the +:meth:`__getattribute__()` methods for :class:`object`, :class:`type`, and +:func:`super`. The important points to remember are: @@ -586,15 +583,16 @@ place at the time of class creation. If descriptors are added to the class afterwards, :meth:`__set_name__` will need to be called manually. -Descriptor Example ------------------- +ORM Example +----------- The following code is simplified skeleton showing how data descriptors could be used to implement an `object relational mapping `_. -The essential idea is that instances only hold keys to a database table. The -actual data is stored in an external table that is being dynamically updated:: +The essential idea is that the data is stored in an external database. The +Python instances only hold keys to the database's tables. Descriptors take +care of lookups or updates:: class Field: @@ -609,8 +607,8 @@ actual data is stored in an external table that is being dynamically updated:: conn.execute(self.store, [value, obj.key]) conn.commit() -We can use the :class:`Field` to define "models" that describe the schema for -each table in a database:: +We can use the :class:`Field` class to define "models" that describe the schema +for each table in a database:: class Movie: table = 'Movies' # Table name @@ -650,10 +648,13 @@ it can be updated:: >>> Movie('Star Wars').director 'J.J. Abrams' +Pure Python Equivalents +^^^^^^^^^^^^^^^^^^^^^^^ + The descriptor protocol is simple and offers exciting possibilities. Several -use cases are so common that they have been packaged into individual function -calls. Properties, bound methods, static methods, and class methods are all -based on the descriptor protocol. +use cases are so common that they have been prepackaged into builtin tools. +Properties, bound methods, static methods, and class methods are all based on +the descriptor protocol. Properties @@ -746,7 +747,7 @@ prepended to the other arguments. By convention, the instance is called Methods can be created manually with :class:`types.MethodType` which is roughly equivalent to:: - class Method: + class MethodType: "Emulate Py_MethodType in Objects/classobject.c" def __init__(self, func, obj): @@ -770,7 +771,7 @@ during dotted lookup from an instance. Here's how it works:: "Simulate func_descr_get() in Objects/funcobject.c" if obj is None: return self - return types.MethodType(self, obj) + return MethodType(self, obj) Running the following class in the interpreter shows how the function descriptor works in practice:: @@ -816,8 +817,8 @@ If you have ever wondered where *self* comes from in regular methods or where *cls* comes from in class methods, this is it! -Static Methods and Class Methods --------------------------------- +Static Methods +-------------- Non-data descriptors provide a simple mechanism for variations on the usual patterns of binding functions into methods. @@ -883,6 +884,10 @@ Using the non-data descriptor protocol, a pure Python version of def __get__(self, obj, objtype=None): return self.f + +Class Methods +------------- + Unlike static methods, class methods prepend the class reference to the argument list before calling the function. This format is the same for whether the caller is an object or a class:: @@ -897,12 +902,11 @@ for whether the caller is an object or a class:: >>> print(F().f(3)) ('F', 3) - -This behavior is useful whenever the function only needs to have a class -reference and does not care about any underlying data. One use for -class methods is to create alternate class constructors. The classmethod -:func:`dict.fromkeys` creates a new dictionary from a list of keys. The pure -Python equivalent is:: +This behavior is useful whenever the method only needs to have a class +reference and does rely on data stored in a specific instance. One use for +class methods is to create alternate class constructors. For example, the +classmethod :func:`dict.fromkeys` creates a new dictionary from a list of +keys. The pure Python equivalent is:: class Dict: ... @@ -934,7 +938,7 @@ Using the non-data descriptor protocol, a pure Python version of cls = type(obj) if hasattr(obj, '__get__'): return self.f.__get__(cls) - return types.MethodType(self.f, cls) + return MethodType(self.f, cls) The code path for ``hasattr(obj, '__get__')`` was added in Python 3.9 and makes it possible for :func:`classmethod` to support chained decorators. From webhook-mailer at python.org Sun Oct 25 10:59:10 2020 From: webhook-mailer at python.org (rhettinger) Date: Sun, 25 Oct 2020 14:59:10 -0000 Subject: [Python-checkins] bpo-39108: Document threading issues for random.gauss() (GH-22928) Message-ID: https://github.com/python/cpython/commit/3cde3788b68bce7deee1e6d31e265bbfce337731 commit: 3cde3788b68bce7deee1e6d31e265bbfce337731 branch: master author: Raymond Hettinger committer: rhettinger date: 2020-10-25T07:59:01-07:00 summary: bpo-39108: Document threading issues for random.gauss() (GH-22928) files: M Doc/library/random.rst diff --git a/Doc/library/random.rst b/Doc/library/random.rst index 5a9359484d11a..c243aced986e5 100644 --- a/Doc/library/random.rst +++ b/Doc/library/random.rst @@ -319,6 +319,13 @@ be found in any statistics text. deviation. This is slightly faster than the :func:`normalvariate` function defined below. + Multithreading note: When two threads call this function + simultaneously, it is possible that they will receive the + same return value. This can be avoided in three ways. + 1) Have each thread use a different instance of the random + number generator. 2) Put locks around all calls. 3) Use the + slower, but thread-safe :func:`normalvariate` function instead. + .. function:: lognormvariate(mu, sigma) From webhook-mailer at python.org Sun Oct 25 11:48:42 2020 From: webhook-mailer at python.org (rhettinger) Date: Sun, 25 Oct 2020 15:48:42 -0000 Subject: [Python-checkins] bpo-39108: Document threading issues for random.gauss() (GH-22928) (GH-22972) Message-ID: https://github.com/python/cpython/commit/af891a962b62268d76ace0d4768ab0e1934a2cd1 commit: af891a962b62268d76ace0d4768ab0e1934a2cd1 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: rhettinger date: 2020-10-25T08:48:38-07:00 summary: bpo-39108: Document threading issues for random.gauss() (GH-22928) (GH-22972) files: M Doc/library/random.rst diff --git a/Doc/library/random.rst b/Doc/library/random.rst index f9535d7d1b9a2..8154dfc18ccc6 100644 --- a/Doc/library/random.rst +++ b/Doc/library/random.rst @@ -319,6 +319,13 @@ be found in any statistics text. deviation. This is slightly faster than the :func:`normalvariate` function defined below. + Multithreading note: When two threads call this function + simultaneously, it is possible that they will receive the + same return value. This can be avoided in three ways. + 1) Have each thread use a different instance of the random + number generator. 2) Put locks around all calls. 3) Use the + slower, but thread-safe :func:`normalvariate` function instead. + .. function:: lognormvariate(mu, sigma) From webhook-mailer at python.org Sun Oct 25 11:56:28 2020 From: webhook-mailer at python.org (serhiy-storchaka) Date: Sun, 25 Oct 2020 15:56:28 -0000 Subject: [Python-checkins] [3.9] bpo-41052: Fix pickling heap types implemented in C with protocols 0 and 1 (GH-22870). (GH-22963) Message-ID: https://github.com/python/cpython/commit/0aaecb30483e98fc29c1f4253967d05da092f0c5 commit: 0aaecb30483e98fc29c1f4253967d05da092f0c5 branch: 3.9 author: Serhiy Storchaka committer: serhiy-storchaka date: 2020-10-25T17:56:17+02:00 summary: [3.9] bpo-41052: Fix pickling heap types implemented in C with protocols 0 and 1 (GH-22870). (GH-22963) (cherry picked from commit 8cd1dbae32d9303caac3a473d3332f17bc98c921) files: A Misc/NEWS.d/next/Library/2020-10-21-23-45-02.bpo-41052.3N7J2J.rst M Lib/copyreg.py M Lib/test/pickletester.py M Modules/_randommodule.c M Modules/clinic/_randommodule.c.h diff --git a/Lib/copyreg.py b/Lib/copyreg.py index dfc463c49a389..7ab8c128eb044 100644 --- a/Lib/copyreg.py +++ b/Lib/copyreg.py @@ -48,6 +48,7 @@ def _reconstructor(cls, base, state): return obj _HEAPTYPE = 1<<9 +_new_type = type(int.__new__) # Python code for object.__reduce_ex__ for protocols 0 and 1 @@ -57,6 +58,9 @@ def _reduce_ex(self, proto): for base in cls.__mro__: if hasattr(base, '__flags__') and not base.__flags__ & _HEAPTYPE: break + new = base.__new__ + if isinstance(new, _new_type) and new.__self__ is base: + break else: base = object # not really reachable if base is object: diff --git a/Lib/test/pickletester.py b/Lib/test/pickletester.py index 94d42c4f97115..3d54617f68ba4 100644 --- a/Lib/test/pickletester.py +++ b/Lib/test/pickletester.py @@ -1965,6 +1965,17 @@ def test_newobj_proxies(self): self.assertEqual(B(x), B(y), detail) self.assertEqual(x.__dict__, y.__dict__, detail) + def test_newobj_overridden_new(self): + # Test that Python class with C implemented __new__ is pickleable + for proto in protocols: + x = MyIntWithNew2(1) + x.foo = 42 + s = self.dumps(x, proto) + y = self.loads(s) + self.assertIs(type(y), MyIntWithNew2) + self.assertEqual(int(y), 1) + self.assertEqual(y.foo, 42) + def test_newobj_not_class(self): # Issue 24552 global SimpleNewObj @@ -3085,6 +3096,13 @@ class MyFrozenSet(frozenset): MyStr, MyUnicode, MyTuple, MyList, MyDict, MySet, MyFrozenSet] +class MyIntWithNew(int): + def __new__(cls, value): + raise AssertionError + +class MyIntWithNew2(MyIntWithNew): + __new__ = int.__new__ + class SlotList(MyList): __slots__ = ["foo"] diff --git a/Misc/NEWS.d/next/Library/2020-10-21-23-45-02.bpo-41052.3N7J2J.rst b/Misc/NEWS.d/next/Library/2020-10-21-23-45-02.bpo-41052.3N7J2J.rst new file mode 100644 index 0000000000000..528e90ed13493 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-10-21-23-45-02.bpo-41052.3N7J2J.rst @@ -0,0 +1,2 @@ +Pickling heap types implemented in C with protocols 0 and 1 raises now an +error instead of producing incorrect data. diff --git a/Modules/_randommodule.c b/Modules/_randommodule.c index 1b01491b068ff..a402b937f70af 100644 --- a/Modules/_randommodule.c +++ b/Modules/_randommodule.c @@ -537,29 +537,12 @@ random_new(PyTypeObject *type, PyObject *args, PyObject *kwds) } -/*[clinic input] - -_random.Random.__reduce__ - -[clinic start generated code]*/ - -static PyObject * -_random_Random___reduce___impl(RandomObject *self) -/*[clinic end generated code: output=ddea0dcdb60ffd6d input=bd38ec35fd157e0f]*/ -{ - PyErr_Format(PyExc_TypeError, - "cannot pickle %s object", - Py_TYPE(self)->tp_name); - return NULL; -} - static PyMethodDef random_methods[] = { _RANDOM_RANDOM_RANDOM_METHODDEF _RANDOM_RANDOM_SEED_METHODDEF _RANDOM_RANDOM_GETSTATE_METHODDEF _RANDOM_RANDOM_SETSTATE_METHODDEF _RANDOM_RANDOM_GETRANDBITS_METHODDEF - _RANDOM_RANDOM___REDUCE___METHODDEF {NULL, NULL} /* sentinel */ }; diff --git a/Modules/clinic/_randommodule.c.h b/Modules/clinic/_randommodule.c.h index 0a642dffc408e..a467811d93b27 100644 --- a/Modules/clinic/_randommodule.c.h +++ b/Modules/clinic/_randommodule.c.h @@ -114,21 +114,4 @@ _random_Random_getrandbits(RandomObject *self, PyObject *arg) exit: return return_value; } - -PyDoc_STRVAR(_random_Random___reduce____doc__, -"__reduce__($self, /)\n" -"--\n" -"\n"); - -#define _RANDOM_RANDOM___REDUCE___METHODDEF \ - {"__reduce__", (PyCFunction)_random_Random___reduce__, METH_NOARGS, _random_Random___reduce____doc__}, - -static PyObject * -_random_Random___reduce___impl(RandomObject *self); - -static PyObject * -_random_Random___reduce__(RandomObject *self, PyObject *Py_UNUSED(ignored)) -{ - return _random_Random___reduce___impl(self); -} -/*[clinic end generated code: output=d8a99be3f1192219 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=a7feb0c9c8d1b627 input=a9049054013a1b77]*/ From webhook-mailer at python.org Sun Oct 25 14:02:39 2020 From: webhook-mailer at python.org (miss-islington) Date: Sun, 25 Oct 2020 18:02:39 -0000 Subject: [Python-checkins] bpo-42144: Add a missing "goto error; " in the _ssl module (GH-22959) Message-ID: https://github.com/python/cpython/commit/c32f2976b8f4034724c3270397aa16f38daf470f commit: c32f2976b8f4034724c3270397aa16f38daf470f branch: master author: Zackery Spytz committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-25T11:02:30-07:00 summary: bpo-42144: Add a missing "goto error;" in the _ssl module (GH-22959) files: M Modules/_ssl.c diff --git a/Modules/_ssl.c b/Modules/_ssl.c index cb8f04a900a06..54c365b88e695 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -899,6 +899,7 @@ _ssl_configure_hostname(PySSLSocket *self, const char* server_hostname) if (ip == NULL) { if (!SSL_set_tlsext_host_name(self->ssl, server_hostname)) { _setSSLError(NULL, 0, __FILE__, __LINE__); + goto error; } } if (self->ctx->check_hostname) { From webhook-mailer at python.org Sun Oct 25 14:21:44 2020 From: webhook-mailer at python.org (miss-islington) Date: Sun, 25 Oct 2020 18:21:44 -0000 Subject: [Python-checkins] bpo-42144: Add a missing "goto error; " in the _ssl module (GH-22959) Message-ID: https://github.com/python/cpython/commit/8e980ecfb7b42063ed41d665288aff69f0ed7fdc commit: 8e980ecfb7b42063ed41d665288aff69f0ed7fdc branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-25T11:21:35-07:00 summary: bpo-42144: Add a missing "goto error;" in the _ssl module (GH-22959) (cherry picked from commit c32f2976b8f4034724c3270397aa16f38daf470f) Co-authored-by: Zackery Spytz files: M Modules/_ssl.c diff --git a/Modules/_ssl.c b/Modules/_ssl.c index e6dda298cbe25..49e8a191e5d2d 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -901,6 +901,7 @@ _ssl_configure_hostname(PySSLSocket *self, const char* server_hostname) if (ip == NULL) { if (!SSL_set_tlsext_host_name(self->ssl, server_hostname)) { _setSSLError(NULL, 0, __FILE__, __LINE__); + goto error; } } if (self->ctx->check_hostname) { From webhook-mailer at python.org Sun Oct 25 14:21:54 2020 From: webhook-mailer at python.org (jaraco) Date: Sun, 25 Oct 2020 18:21:54 -0000 Subject: [Python-checkins] bpo-41490: ``path`` and ``contents`` to aggressively close handles (#22915) Message-ID: https://github.com/python/cpython/commit/df8d4c83a6e1727e766191896aeabde886979587 commit: df8d4c83a6e1727e766191896aeabde886979587 branch: master author: Jason R. Coombs committer: jaraco date: 2020-10-25T14:21:46-04:00 summary: bpo-41490: ``path`` and ``contents`` to aggressively close handles (#22915) * bpo-41490: ``path`` method to aggressively close handles * Add blurb * In ZipReader.contents, eagerly evaluate the contents to release references to the zipfile. * Instead use _ensure_sequence to ensure any iterable from a reader is eagerly converted to a list if it's not already a sequence. files: A Misc/NEWS.d/next/Library/2020-10-23-08-54-47.bpo-41490.-Yk6OD.rst M Lib/importlib/_common.py M Lib/importlib/readers.py M Lib/importlib/resources.py M Lib/test/test_importlib/test_resource.py M Lib/test/test_importlib/zipdata01/ziptestdata.zip M Lib/test/test_importlib/zipdata02/ziptestdata.zip diff --git a/Lib/importlib/_common.py b/Lib/importlib/_common.py index b15c59eb9c98a..71ce6af8cc985 100644 --- a/Lib/importlib/_common.py +++ b/Lib/importlib/_common.py @@ -88,6 +88,7 @@ def _tempfile(reader, suffix=''): try: os.write(fd, reader()) os.close(fd) + del reader yield pathlib.Path(raw_path) finally: try: @@ -97,14 +98,12 @@ def _tempfile(reader, suffix=''): @functools.singledispatch - at contextlib.contextmanager def as_file(path): """ Given a Traversable object, return that object as a path on the local file system in a context manager. """ - with _tempfile(path.read_bytes, suffix=path.name) as local: - yield local + return _tempfile(path.read_bytes, suffix=path.name) @as_file.register(pathlib.Path) diff --git a/Lib/importlib/readers.py b/Lib/importlib/readers.py index 6331e4daf4313..74a63e4a7535b 100644 --- a/Lib/importlib/readers.py +++ b/Lib/importlib/readers.py @@ -22,8 +22,8 @@ def files(self): class ZipReader(abc.TraversableResources): def __init__(self, loader, module): _, _, name = module.rpartition('.') - prefix = loader.prefix.replace('\\', '/') + name + '/' - self.path = zipfile.Path(loader.archive, prefix) + self.prefix = loader.prefix.replace('\\', '/') + name + '/' + self.archive = loader.archive def open_resource(self, resource): try: @@ -38,4 +38,4 @@ def is_resource(self, path): return target.is_file() and target.exists() def files(self): - return self.path + return zipfile.Path(self.archive, self.prefix) diff --git a/Lib/importlib/resources.py b/Lib/importlib/resources.py index 4535619f4f014..4169171b189cc 100644 --- a/Lib/importlib/resources.py +++ b/Lib/importlib/resources.py @@ -1,8 +1,9 @@ import os +import io from . import _common from ._common import as_file, files -from contextlib import contextmanager, suppress +from contextlib import suppress from importlib.abc import ResourceLoader from io import BytesIO, TextIOWrapper from pathlib import Path @@ -10,6 +11,8 @@ from typing import ContextManager, Iterable, Union from typing import cast from typing.io import BinaryIO, TextIO +from collections.abc import Sequence +from functools import singledispatch __all__ = [ @@ -102,22 +105,26 @@ def path( """ reader = _common.get_resource_reader(_common.get_package(package)) return ( - _path_from_reader(reader, resource) + _path_from_reader(reader, _common.normalize_path(resource)) if reader else _common.as_file( _common.files(package).joinpath(_common.normalize_path(resource))) ) - at contextmanager def _path_from_reader(reader, resource): - norm_resource = _common.normalize_path(resource) + return _path_from_resource_path(reader, resource) or \ + _path_from_open_resource(reader, resource) + + +def _path_from_resource_path(reader, resource): with suppress(FileNotFoundError): - yield Path(reader.resource_path(norm_resource)) - return - opener_reader = reader.open_resource(norm_resource) - with _common._tempfile(opener_reader.read, suffix=norm_resource) as res: - yield res + return Path(reader.resource_path(resource)) + + +def _path_from_open_resource(reader, resource): + saved = io.BytesIO(reader.open_resource(resource).read()) + return _common._tempfile(saved.read, suffix=resource) def is_resource(package: Package, name: str) -> bool: @@ -146,7 +153,7 @@ def contents(package: Package) -> Iterable[str]: package = _common.get_package(package) reader = _common.get_resource_reader(package) if reader is not None: - return reader.contents() + return _ensure_sequence(reader.contents()) # Is the package a namespace package? By definition, namespace packages # cannot have resources. namespace = ( @@ -156,3 +163,13 @@ def contents(package: Package) -> Iterable[str]: if namespace or not package.__spec__.has_location: return () return list(item.name for item in _common.from_package(package).iterdir()) + + + at singledispatch +def _ensure_sequence(iterable): + return list(iterable) + + + at _ensure_sequence.register(Sequence) +def _(iterable): + return iterable diff --git a/Lib/test/test_importlib/test_resource.py b/Lib/test/test_importlib/test_resource.py index f88d92d154672..1d1bdad1b218d 100644 --- a/Lib/test/test_importlib/test_resource.py +++ b/Lib/test/test_importlib/test_resource.py @@ -1,10 +1,14 @@ import sys import unittest +import uuid +import pathlib from . import data01 from . import zipdata01, zipdata02 from . import util from importlib import resources, import_module +from test.support import import_helper +from test.support.os_helper import unlink class ResourceTests: @@ -162,5 +166,80 @@ def test_namespaces_cannot_have_resources(self): 'test.test_importlib.data03.namespace', 'resource1.txt') +class DeletingZipsTest(unittest.TestCase): + """Having accessed resources in a zip file should not keep an open + reference to the zip. + """ + ZIP_MODULE = zipdata01 + + def setUp(self): + modules = import_helper.modules_setup() + self.addCleanup(import_helper.modules_cleanup, *modules) + + data_path = pathlib.Path(self.ZIP_MODULE.__file__) + data_dir = data_path.parent + self.source_zip_path = data_dir / 'ziptestdata.zip' + self.zip_path = pathlib.Path('{}.zip'.format(uuid.uuid4())).absolute() + self.zip_path.write_bytes(self.source_zip_path.read_bytes()) + sys.path.append(str(self.zip_path)) + self.data = import_module('ziptestdata') + + def tearDown(self): + try: + sys.path.remove(str(self.zip_path)) + except ValueError: + pass + + try: + del sys.path_importer_cache[str(self.zip_path)] + del sys.modules[self.data.__name__] + except KeyError: + pass + + try: + unlink(self.zip_path) + except OSError: + # If the test fails, this will probably fail too + pass + + def test_contents_does_not_keep_open(self): + c = resources.contents('ziptestdata') + self.zip_path.unlink() + del c + + def test_is_resource_does_not_keep_open(self): + c = resources.is_resource('ziptestdata', 'binary.file') + self.zip_path.unlink() + del c + + def test_is_resource_failure_does_not_keep_open(self): + c = resources.is_resource('ziptestdata', 'not-present') + self.zip_path.unlink() + del c + + @unittest.skip("Desired but not supported.") + def test_path_does_not_keep_open(self): + c = resources.path('ziptestdata', 'binary.file') + self.zip_path.unlink() + del c + + def test_entered_path_does_not_keep_open(self): + # This is what certifi does on import to make its bundle + # available for the process duration. + c = resources.path('ziptestdata', 'binary.file').__enter__() + self.zip_path.unlink() + del c + + def test_read_binary_does_not_keep_open(self): + c = resources.read_binary('ziptestdata', 'binary.file') + self.zip_path.unlink() + del c + + def test_read_text_does_not_keep_open(self): + c = resources.read_text('ziptestdata', 'utf-8.file', encoding='utf-8') + self.zip_path.unlink() + del c + + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_importlib/zipdata01/ziptestdata.zip b/Lib/test/test_importlib/zipdata01/ziptestdata.zip index 8d8fa97f199cc..12f7872cd596a 100644 Binary files a/Lib/test/test_importlib/zipdata01/ziptestdata.zip and b/Lib/test/test_importlib/zipdata01/ziptestdata.zip differ diff --git a/Lib/test/test_importlib/zipdata02/ziptestdata.zip b/Lib/test/test_importlib/zipdata02/ziptestdata.zip index 6f348899a8049..9ee00586e75f1 100644 Binary files a/Lib/test/test_importlib/zipdata02/ziptestdata.zip and b/Lib/test/test_importlib/zipdata02/ziptestdata.zip differ diff --git a/Misc/NEWS.d/next/Library/2020-10-23-08-54-47.bpo-41490.-Yk6OD.rst b/Misc/NEWS.d/next/Library/2020-10-23-08-54-47.bpo-41490.-Yk6OD.rst new file mode 100644 index 0000000000000..5ad6af3d148e3 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-10-23-08-54-47.bpo-41490.-Yk6OD.rst @@ -0,0 +1,3 @@ +In ``importlib.resources``, ``.path`` method is more aggressive about +releasing handles to zipfile objects early, enabling use-cases like certifi +to leave the context open but delete the underlying zip file. From webhook-mailer at python.org Sun Oct 25 14:23:17 2020 From: webhook-mailer at python.org (miss-islington) Date: Sun, 25 Oct 2020 18:23:17 -0000 Subject: [Python-checkins] bpo-42144: Add a missing "goto error; " in the _ssl module (GH-22959) Message-ID: https://github.com/python/cpython/commit/83c86cf54b36a7325f615f5adf22b28e48f0e72d commit: 83c86cf54b36a7325f615f5adf22b28e48f0e72d branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-25T11:23:09-07:00 summary: bpo-42144: Add a missing "goto error;" in the _ssl module (GH-22959) (cherry picked from commit c32f2976b8f4034724c3270397aa16f38daf470f) Co-authored-by: Zackery Spytz files: M Modules/_ssl.c diff --git a/Modules/_ssl.c b/Modules/_ssl.c index 28796b376e862..7bdde452513fd 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -899,6 +899,7 @@ _ssl_configure_hostname(PySSLSocket *self, const char* server_hostname) if (ip == NULL) { if (!SSL_set_tlsext_host_name(self->ssl, server_hostname)) { _setSSLError(NULL, 0, __FILE__, __LINE__); + goto error; } } if (self->ctx->check_hostname) { From webhook-mailer at python.org Sun Oct 25 14:38:38 2020 From: webhook-mailer at python.org (pablogsal) Date: Sun, 25 Oct 2020 18:38:38 -0000 Subject: [Python-checkins] bpo-41919: Avoid resource leak in test_io (GH-22973) Message-ID: https://github.com/python/cpython/commit/14cdc215aa952d280c18626005d3aff967901d92 commit: 14cdc215aa952d280c18626005d3aff967901d92 branch: master author: Hai Shi committer: pablogsal date: 2020-10-25T18:38:33Z summary: bpo-41919: Avoid resource leak in test_io (GH-22973) Co-authored-by: Pablo Galindo files: M Lib/test/test_io.py diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index fbaea3aaec3cb..cc54d0ea0062f 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -2519,15 +2519,17 @@ def process_word(self): codecEnabled = False - @classmethod - def lookupTestDecoder(cls, name): - if cls.codecEnabled and name == 'test_decoder': - latin1 = codecs.lookup('latin-1') - return codecs.CodecInfo( - name='test_decoder', encode=latin1.encode, decode=None, - incrementalencoder=None, - streamreader=None, streamwriter=None, - incrementaldecoder=cls) + +# bpo-41919: This method is separated from StatefulIncrementalDecoder to avoid a resource leak +# when registering codecs and cleanup functions. +def lookupTestDecoder(name): + if StatefulIncrementalDecoder.codecEnabled and name == 'test_decoder': + latin1 = codecs.lookup('latin-1') + return codecs.CodecInfo( + name='test_decoder', encode=latin1.encode, decode=None, + incrementalencoder=None, + streamreader=None, streamwriter=None, + incrementaldecoder=StatefulIncrementalDecoder) class StatefulIncrementalDecoderTest(unittest.TestCase): @@ -2579,9 +2581,8 @@ def setUp(self): self.testdata = b"AAA\r\nBBB\rCCC\r\nDDD\nEEE\r\n" self.normalized = b"AAA\nBBB\nCCC\nDDD\nEEE\n".decode("ascii") os_helper.unlink(os_helper.TESTFN) - codecs.register(StatefulIncrementalDecoder.lookupTestDecoder) - self.addCleanup(codecs.unregister, - StatefulIncrementalDecoder.lookupTestDecoder) + codecs.register(lookupTestDecoder) + self.addCleanup(codecs.unregister, lookupTestDecoder) def tearDown(self): os_helper.unlink(os_helper.TESTFN) From webhook-mailer at python.org Sun Oct 25 14:45:13 2020 From: webhook-mailer at python.org (jaraco) Date: Sun, 25 Oct 2020 18:45:13 -0000 Subject: [Python-checkins] bpo-42043: Add support for zipfile.Path subclasses (#22716) Message-ID: https://github.com/python/cpython/commit/d1a0a960ee493262fb95a0f5b795b5b6d75cecb8 commit: d1a0a960ee493262fb95a0f5b795b5b6d75cecb8 branch: master author: Jason R. Coombs committer: jaraco date: 2020-10-25T14:45:05-04:00 summary: bpo-42043: Add support for zipfile.Path subclasses (#22716) * bpo-42043: Add support for zipfile.Path inheritance as introduced in zipp 3.2.0. * Add blurb. files: A Misc/NEWS.d/next/Library/2020-10-15-17-20-37.bpo-42043.OS0p_v.rst M Lib/test/test_zipfile.py M Lib/zipfile.py diff --git a/Lib/test/test_zipfile.py b/Lib/test/test_zipfile.py index 3bb9ce995c2a1..b3c24213f3474 100644 --- a/Lib/test/test_zipfile.py +++ b/Lib/test/test_zipfile.py @@ -13,6 +13,7 @@ import unittest import unittest.mock as mock import zipfile +import functools from tempfile import TemporaryFile @@ -2836,6 +2837,20 @@ def build_alpharep_fixture(): return zf +def pass_alpharep(meth): + """ + Given a method, wrap it in a for loop that invokes method + with each subtest. + """ + + @functools.wraps(meth) + def wrapper(self): + for alpharep in self.zipfile_alpharep(): + meth(self, alpharep=alpharep) + + return wrapper + + class TestPath(unittest.TestCase): def setUp(self): self.fixtures = contextlib.ExitStack() @@ -2847,47 +2862,58 @@ def zipfile_alpharep(self): with self.subTest(): yield add_dirs(build_alpharep_fixture()) - def zipfile_ondisk(self): + def zipfile_ondisk(self, alpharep): tmpdir = pathlib.Path(self.fixtures.enter_context(temp_dir())) - for alpharep in self.zipfile_alpharep(): - buffer = alpharep.fp - alpharep.close() - path = tmpdir / alpharep.filename - with path.open("wb") as strm: - strm.write(buffer.getvalue()) - yield path - - def test_iterdir_and_types(self): - for alpharep in self.zipfile_alpharep(): - root = zipfile.Path(alpharep) - assert root.is_dir() - a, b, g = root.iterdir() - assert a.is_file() - assert b.is_dir() - assert g.is_dir() - c, f, d = b.iterdir() - assert c.is_file() and f.is_file() - e, = d.iterdir() - assert e.is_file() - h, = g.iterdir() - i, = h.iterdir() - assert i.is_file() - - def test_subdir_is_dir(self): - for alpharep in self.zipfile_alpharep(): - root = zipfile.Path(alpharep) - assert (root / 'b').is_dir() - assert (root / 'b/').is_dir() - assert (root / 'g').is_dir() - assert (root / 'g/').is_dir() - - def test_open(self): - for alpharep in self.zipfile_alpharep(): - root = zipfile.Path(alpharep) - a, b, g = root.iterdir() - with a.open() as strm: - data = strm.read() - assert data == "content of a" + buffer = alpharep.fp + alpharep.close() + path = tmpdir / alpharep.filename + with path.open("wb") as strm: + strm.write(buffer.getvalue()) + return path + + @pass_alpharep + def test_iterdir_and_types(self, alpharep): + root = zipfile.Path(alpharep) + assert root.is_dir() + a, b, g = root.iterdir() + assert a.is_file() + assert b.is_dir() + assert g.is_dir() + c, f, d = b.iterdir() + assert c.is_file() and f.is_file() + (e,) = d.iterdir() + assert e.is_file() + (h,) = g.iterdir() + (i,) = h.iterdir() + assert i.is_file() + + @pass_alpharep + def test_is_file_missing(self, alpharep): + root = zipfile.Path(alpharep) + assert not root.joinpath('missing.txt').is_file() + + @pass_alpharep + def test_iterdir_on_file(self, alpharep): + root = zipfile.Path(alpharep) + a, b, g = root.iterdir() + with self.assertRaises(ValueError): + a.iterdir() + + @pass_alpharep + def test_subdir_is_dir(self, alpharep): + root = zipfile.Path(alpharep) + assert (root / 'b').is_dir() + assert (root / 'b/').is_dir() + assert (root / 'g').is_dir() + assert (root / 'g/').is_dir() + + @pass_alpharep + def test_open(self, alpharep): + root = zipfile.Path(alpharep) + a, b, g = root.iterdir() + with a.open() as strm: + data = strm.read() + assert data == "content of a" def test_open_write(self): """ @@ -2908,6 +2934,14 @@ def test_open_extant_directory(self): with self.assertRaises(IsADirectoryError): zf.joinpath('b').open() + @pass_alpharep + def test_open_binary_invalid_args(self, alpharep): + root = zipfile.Path(alpharep) + with self.assertRaises(ValueError): + root.joinpath('a.txt').open('rb', encoding='utf-8') + with self.assertRaises(ValueError): + root.joinpath('a.txt').open('rb', 'utf-8') + def test_open_missing_directory(self): """ Attempting to open a missing directory raises FileNotFoundError. @@ -2916,75 +2950,87 @@ def test_open_missing_directory(self): with self.assertRaises(FileNotFoundError): zf.joinpath('z').open() - def test_read(self): - for alpharep in self.zipfile_alpharep(): - root = zipfile.Path(alpharep) - a, b, g = root.iterdir() - assert a.read_text() == "content of a" - assert a.read_bytes() == b"content of a" - - def test_joinpath(self): - for alpharep in self.zipfile_alpharep(): - root = zipfile.Path(alpharep) - a = root.joinpath("a") - assert a.is_file() - e = root.joinpath("b").joinpath("d").joinpath("e.txt") - assert e.read_text() == "content of e" - - def test_traverse_truediv(self): - for alpharep in self.zipfile_alpharep(): - root = zipfile.Path(alpharep) - a = root / "a" - assert a.is_file() - e = root / "b" / "d" / "e.txt" - assert e.read_text() == "content of e" + @pass_alpharep + def test_read(self, alpharep): + root = zipfile.Path(alpharep) + a, b, g = root.iterdir() + assert a.read_text() == "content of a" + assert a.read_bytes() == b"content of a" + + @pass_alpharep + def test_joinpath(self, alpharep): + root = zipfile.Path(alpharep) + a = root.joinpath("a.txt") + assert a.is_file() + e = root.joinpath("b").joinpath("d").joinpath("e.txt") + assert e.read_text() == "content of e" + + @pass_alpharep + def test_traverse_truediv(self, alpharep): + root = zipfile.Path(alpharep) + a = root / "a.txt" + assert a.is_file() + e = root / "b" / "d" / "e.txt" + assert e.read_text() == "content of e" + + @pass_alpharep + def test_traverse_simplediv(self, alpharep): + """ + Disable the __future__.division when testing traversal. + """ + code = compile( + source="zipfile.Path(alpharep) / 'a'", + filename="(test)", + mode="eval", + dont_inherit=True, + ) + eval(code) - def test_pathlike_construction(self): + @pass_alpharep + def test_pathlike_construction(self, alpharep): """ zipfile.Path should be constructable from a path-like object """ - for zipfile_ondisk in self.zipfile_ondisk(): - pathlike = pathlib.Path(str(zipfile_ondisk)) - zipfile.Path(pathlike) - - def test_traverse_pathlike(self): - for alpharep in self.zipfile_alpharep(): - root = zipfile.Path(alpharep) - root / pathlib.Path("a") - - def test_parent(self): - for alpharep in self.zipfile_alpharep(): - root = zipfile.Path(alpharep) - assert (root / 'a').parent.at == '' - assert (root / 'a' / 'b').parent.at == 'a/' - - def test_dir_parent(self): - for alpharep in self.zipfile_alpharep(): - root = zipfile.Path(alpharep) - assert (root / 'b').parent.at == '' - assert (root / 'b/').parent.at == '' - - def test_missing_dir_parent(self): - for alpharep in self.zipfile_alpharep(): - root = zipfile.Path(alpharep) - assert (root / 'missing dir/').parent.at == '' - - def test_mutability(self): + zipfile_ondisk = self.zipfile_ondisk(alpharep) + pathlike = pathlib.Path(str(zipfile_ondisk)) + zipfile.Path(pathlike) + + @pass_alpharep + def test_traverse_pathlike(self, alpharep): + root = zipfile.Path(alpharep) + root / pathlib.Path("a") + + @pass_alpharep + def test_parent(self, alpharep): + root = zipfile.Path(alpharep) + assert (root / 'a').parent.at == '' + assert (root / 'a' / 'b').parent.at == 'a/' + + @pass_alpharep + def test_dir_parent(self, alpharep): + root = zipfile.Path(alpharep) + assert (root / 'b').parent.at == '' + assert (root / 'b/').parent.at == '' + + @pass_alpharep + def test_missing_dir_parent(self, alpharep): + root = zipfile.Path(alpharep) + assert (root / 'missing dir/').parent.at == '' + + @pass_alpharep + def test_mutability(self, alpharep): """ If the underlying zipfile is changed, the Path object should reflect that change. """ - for alpharep in self.zipfile_alpharep(): - root = zipfile.Path(alpharep) - a, b, g = root.iterdir() - alpharep.writestr('foo.txt', 'foo') - alpharep.writestr('bar/baz.txt', 'baz') - assert any( - child.name == 'foo.txt' - for child in root.iterdir()) - assert (root / 'foo.txt').read_text() == 'foo' - baz, = (root / 'bar').iterdir() - assert baz.read_text() == 'baz' + root = zipfile.Path(alpharep) + a, b, g = root.iterdir() + alpharep.writestr('foo.txt', 'foo') + alpharep.writestr('bar/baz.txt', 'baz') + assert any(child.name == 'foo.txt' for child in root.iterdir()) + assert (root / 'foo.txt').read_text() == 'foo' + (baz,) = (root / 'bar').iterdir() + assert baz.read_text() == 'baz' HUGE_ZIPFILE_NUM_ENTRIES = 2 ** 13 @@ -3013,11 +3059,65 @@ def test_implied_dirs_performance(self): data = ['/'.join(string.ascii_lowercase + str(n)) for n in range(10000)] zipfile.CompleteDirs._implied_dirs(data) - def test_read_does_not_close(self): - for alpharep in self.zipfile_ondisk(): - with zipfile.ZipFile(alpharep) as file: - for rep in range(2): - zipfile.Path(file, 'a.txt').read_text() + @pass_alpharep + def test_read_does_not_close(self, alpharep): + alpharep = self.zipfile_ondisk(alpharep) + with zipfile.ZipFile(alpharep) as file: + for rep in range(2): + zipfile.Path(file, 'a.txt').read_text() + + @pass_alpharep + def test_subclass(self, alpharep): + class Subclass(zipfile.Path): + pass + + root = Subclass(alpharep) + assert isinstance(root / 'b', Subclass) + + @pass_alpharep + def test_filename(self, alpharep): + root = zipfile.Path(alpharep) + assert root.filename == pathlib.Path('alpharep.zip') + + @pass_alpharep + def test_root_name(self, alpharep): + """ + The name of the root should be the name of the zipfile + """ + root = zipfile.Path(alpharep) + assert root.name == 'alpharep.zip' == root.filename.name + + @pass_alpharep + def test_root_parent(self, alpharep): + root = zipfile.Path(alpharep) + assert root.parent == pathlib.Path('.') + root.root.filename = 'foo/bar.zip' + assert root.parent == pathlib.Path('foo') + + @pass_alpharep + def test_root_unnamed(self, alpharep): + """ + It is an error to attempt to get the name + or parent of an unnamed zipfile. + """ + alpharep.filename = None + root = zipfile.Path(alpharep) + with self.assertRaises(TypeError): + root.name + with self.assertRaises(TypeError): + root.parent + + # .name and .parent should still work on subs + sub = root / "b" + assert sub.name == "b" + assert sub.parent + + @pass_alpharep + def test_inheritance(self, alpharep): + cls = type('PathChild', (zipfile.Path,), {}) + for alpharep in self.zipfile_alpharep(): + file = cls(alpharep).joinpath('some dir').parent + assert isinstance(file, cls) if __name__ == "__main__": diff --git a/Lib/zipfile.py b/Lib/zipfile.py index da3e40e5dbd41..e1a50a3eb51d9 100644 --- a/Lib/zipfile.py +++ b/Lib/zipfile.py @@ -16,6 +16,7 @@ import threading import time import contextlib +import pathlib try: import zlib # We may need its compression method @@ -2210,6 +2211,7 @@ class FastLookup(CompleteDirs): ZipFile subclass to ensure implicit dirs exist and are resolved rapidly. """ + def namelist(self): with contextlib.suppress(AttributeError): return self.__names @@ -2241,7 +2243,7 @@ class Path: >>> zf.writestr('a.txt', 'content of a') >>> zf.writestr('b/c.txt', 'content of c') >>> zf.writestr('b/d/e.txt', 'content of e') - >>> zf.filename = 'abcde.zip' + >>> zf.filename = 'mem/abcde.zip' Path accepts the zipfile object itself or a filename @@ -2253,9 +2255,9 @@ class Path: >>> a, b = root.iterdir() >>> a - Path('abcde.zip', 'a.txt') + Path('mem/abcde.zip', 'a.txt') >>> b - Path('abcde.zip', 'b/') + Path('mem/abcde.zip', 'b/') name property: @@ -2266,7 +2268,7 @@ class Path: >>> c = b / 'c.txt' >>> c - Path('abcde.zip', 'b/c.txt') + Path('mem/abcde.zip', 'b/c.txt') >>> c.name 'c.txt' @@ -2284,8 +2286,21 @@ class Path: Coercion to string: - >>> str(c) - 'abcde.zip/b/c.txt' + >>> import os + >>> str(c).replace(os.sep, posixpath.sep) + 'mem/abcde.zip/b/c.txt' + + At the root, ``name``, ``filename``, and ``parent`` + resolve to the zipfile. Note these attributes are not + valid and will raise a ``ValueError`` if the zipfile + has no filename. + + >>> root.name + 'abcde.zip' + >>> str(root.filename).replace(os.sep, posixpath.sep) + 'mem/abcde.zip' + >>> str(root.parent) + 'mem' """ __repr = "{self.__class__.__name__}({self.root.filename!r}, {self.at!r})" @@ -2323,7 +2338,11 @@ def open(self, mode='r', *args, pwd=None, **kwargs): @property def name(self): - return posixpath.basename(self.at.rstrip("/")) + return pathlib.Path(self.at).name or self.filename.name + + @property + def filename(self): + return pathlib.Path(self.root.filename).joinpath(self.at) def read_text(self, *args, **kwargs): with self.open('r', *args, **kwargs) as strm: @@ -2337,13 +2356,13 @@ def _is_child(self, path): return posixpath.dirname(path.at.rstrip("/")) == self.at.rstrip("/") def _next(self, at): - return Path(self.root, at) + return self.__class__(self.root, at) def is_dir(self): return not self.at or self.at.endswith("/") def is_file(self): - return not self.is_dir() + return self.exists() and not self.is_dir() def exists(self): return self.at in self.root._name_set() @@ -2368,6 +2387,8 @@ def joinpath(self, add): @property def parent(self): + if not self.at: + return self.filename.parent parent_at = posixpath.dirname(self.at.rstrip('/')) if parent_at: parent_at += '/' diff --git a/Misc/NEWS.d/next/Library/2020-10-15-17-20-37.bpo-42043.OS0p_v.rst b/Misc/NEWS.d/next/Library/2020-10-15-17-20-37.bpo-42043.OS0p_v.rst new file mode 100644 index 0000000000000..b6b296956c35d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-10-15-17-20-37.bpo-42043.OS0p_v.rst @@ -0,0 +1,4 @@ +Add support for ``zipfile.Path`` inheritance. ``zipfile.Path.is_file()`` now +returns False for non-existent names. ``zipfile.Path`` objects now expose a +``.filename`` attribute and rely on that to resolve ``.name`` and +``.parent`` when the ``Path`` object is at the root of the zipfile. From webhook-mailer at python.org Sun Oct 25 19:03:47 2020 From: webhook-mailer at python.org (pablogsal) Date: Sun, 25 Oct 2020 23:03:47 -0000 Subject: [Python-checkins] bpo-42150: Avoid buffer overflow in the new parser (GH-22978) Message-ID: https://github.com/python/cpython/commit/e68c67805e6a4c4ec80bea64be0e8373cc02d322 commit: e68c67805e6a4c4ec80bea64be0e8373cc02d322 branch: master author: Pablo Galindo committer: pablogsal date: 2020-10-25T23:03:41Z summary: bpo-42150: Avoid buffer overflow in the new parser (GH-22978) files: A Misc/NEWS.d/next/Core and Builtins/2020-10-25-21-14-18.bpo-42150.b70u_T.rst M Parser/pegen.c diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-10-25-21-14-18.bpo-42150.b70u_T.rst b/Misc/NEWS.d/next/Core and Builtins/2020-10-25-21-14-18.bpo-42150.b70u_T.rst new file mode 100644 index 0000000000000..62fabb857aa38 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2020-10-25-21-14-18.bpo-42150.b70u_T.rst @@ -0,0 +1,2 @@ +Fix possible buffer overflow in the new parser when checking for +continuation lines. Patch by Pablo Galindo. diff --git a/Parser/pegen.c b/Parser/pegen.c index efa5ed9f288ee..c7343f7f047c3 100644 --- a/Parser/pegen.c +++ b/Parser/pegen.c @@ -990,7 +990,8 @@ bad_single_statement(Parser *p) /* Newlines are allowed if preceded by a line continuation character or if they appear inside a string. */ - if (!cur || *(cur - 1) == '\\' || newline_in_string(p, cur)) { + if (!cur || (cur != p->tok->buf && *(cur - 1) == '\\') + || newline_in_string(p, cur)) { return 0; } char c = *cur; From webhook-mailer at python.org Sun Oct 25 19:25:05 2020 From: webhook-mailer at python.org (miss-islington) Date: Sun, 25 Oct 2020 23:25:05 -0000 Subject: [Python-checkins] bpo-42150: Avoid buffer overflow in the new parser (GH-22978) Message-ID: https://github.com/python/cpython/commit/0b290dd2171e745d94f48298cafb2327eb2de17c commit: 0b290dd2171e745d94f48298cafb2327eb2de17c branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-25T16:24:56-07:00 summary: bpo-42150: Avoid buffer overflow in the new parser (GH-22978) (cherry picked from commit e68c67805e6a4c4ec80bea64be0e8373cc02d322) Co-authored-by: Pablo Galindo files: A Misc/NEWS.d/next/Core and Builtins/2020-10-25-21-14-18.bpo-42150.b70u_T.rst M Parser/pegen/pegen.c diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-10-25-21-14-18.bpo-42150.b70u_T.rst b/Misc/NEWS.d/next/Core and Builtins/2020-10-25-21-14-18.bpo-42150.b70u_T.rst new file mode 100644 index 0000000000000..62fabb857aa38 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2020-10-25-21-14-18.bpo-42150.b70u_T.rst @@ -0,0 +1,2 @@ +Fix possible buffer overflow in the new parser when checking for +continuation lines. Patch by Pablo Galindo. diff --git a/Parser/pegen/pegen.c b/Parser/pegen/pegen.c index 2c435fb367836..a7643fc3a6ad9 100644 --- a/Parser/pegen/pegen.c +++ b/Parser/pegen/pegen.c @@ -989,7 +989,8 @@ bad_single_statement(Parser *p) /* Newlines are allowed if preceded by a line continuation character or if they appear inside a string. */ - if (!cur || *(cur - 1) == '\\' || newline_in_string(p, cur)) { + if (!cur || (cur != p->tok->buf && *(cur - 1) == '\\') + || newline_in_string(p, cur)) { return 0; } char c = *cur; From webhook-mailer at python.org Sun Oct 25 20:09:41 2020 From: webhook-mailer at python.org (gpshead) Date: Mon, 26 Oct 2020 00:09:41 -0000 Subject: [Python-checkins] bpo-42146: Fix memory leak in subprocess.Popen() in case of uid/gid overflow (GH-22966) Message-ID: https://github.com/python/cpython/commit/c0590c0033e86f98cdf5f2ca6898656f98ab4053 commit: c0590c0033e86f98cdf5f2ca6898656f98ab4053 branch: master author: Alexey Izbyshev committer: gpshead date: 2020-10-25T17:09:32-07:00 summary: bpo-42146: Fix memory leak in subprocess.Popen() in case of uid/gid overflow (GH-22966) Fix memory leak in subprocess.Popen() in case of uid/gid overflow Also add a test that would catch this leak with `--huntrleaks`. Alas, the test for `extra_groups` also exposes an inconsistency in our error reporting: we use a custom ValueError for `extra_groups`, but propagate OverflowError for `user` and `group`. files: A Misc/NEWS.d/next/Library/2020-10-25-19-25-02.bpo-42146.6A8uvS.rst M Lib/test/test_subprocess.py M Modules/_posixsubprocess.c diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index 9fc4434649dbc..e25474abed4b7 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -1895,6 +1895,10 @@ def test_user(self): with self.assertRaises(ValueError): subprocess.check_call(ZERO_RETURN_CMD, user=-1) + with self.assertRaises(OverflowError): + subprocess.check_call(ZERO_RETURN_CMD, + cwd=os.curdir, env=os.environ, user=2**64) + if pwd is None and name_uid is not None: with self.assertRaises(ValueError): subprocess.check_call(ZERO_RETURN_CMD, user=name_uid) @@ -1938,6 +1942,10 @@ def test_group(self): with self.assertRaises(ValueError): subprocess.check_call(ZERO_RETURN_CMD, group=-1) + with self.assertRaises(OverflowError): + subprocess.check_call(ZERO_RETURN_CMD, + cwd=os.curdir, env=os.environ, group=2**64) + if grp is None: with self.assertRaises(ValueError): subprocess.check_call(ZERO_RETURN_CMD, group=name_group) @@ -1986,6 +1994,11 @@ def test_extra_groups(self): with self.assertRaises(ValueError): subprocess.check_call(ZERO_RETURN_CMD, extra_groups=[-1]) + with self.assertRaises(ValueError): + subprocess.check_call(ZERO_RETURN_CMD, + cwd=os.curdir, env=os.environ, + extra_groups=[2**64]) + if grp is None: with self.assertRaises(ValueError): subprocess.check_call(ZERO_RETURN_CMD, diff --git a/Misc/NEWS.d/next/Library/2020-10-25-19-25-02.bpo-42146.6A8uvS.rst b/Misc/NEWS.d/next/Library/2020-10-25-19-25-02.bpo-42146.6A8uvS.rst new file mode 100644 index 0000000000000..041809803db6a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-10-25-19-25-02.bpo-42146.6A8uvS.rst @@ -0,0 +1,2 @@ +Fix memory leak in :func:`subprocess.Popen` in case an uid (gid) specified in +`user` (`group`, `extra_groups`) overflows `uid_t` (`gid_t`). diff --git a/Modules/_posixsubprocess.c b/Modules/_posixsubprocess.c index 8baea314f4e40..5e5fbb2e79a7f 100644 --- a/Modules/_posixsubprocess.c +++ b/Modules/_posixsubprocess.c @@ -772,7 +772,7 @@ subprocess_fork_exec(PyObject* self, PyObject *args) uid_t uid; gid_t gid, *groups = NULL; int child_umask; - PyObject *cwd_obj, *cwd_obj2; + PyObject *cwd_obj, *cwd_obj2 = NULL; const char *cwd; pid_t pid; int need_to_reenable_gc = 0; @@ -894,7 +894,6 @@ subprocess_fork_exec(PyObject* self, PyObject *args) cwd = PyBytes_AsString(cwd_obj2); } else { cwd = NULL; - cwd_obj2 = NULL; } if (groups_list != Py_None) { @@ -1080,6 +1079,7 @@ subprocess_fork_exec(PyObject* self, PyObject *args) return PyLong_FromPid(pid); cleanup: + Py_XDECREF(cwd_obj2); if (envp) _Py_FreeCharPArray(envp); if (argv) From webhook-mailer at python.org Sun Oct 25 20:34:37 2020 From: webhook-mailer at python.org (miss-islington) Date: Mon, 26 Oct 2020 00:34:37 -0000 Subject: [Python-checkins] [3.9] bpo-42146: Fix memory leak in subprocess.Popen() in case of uid/gid overflow (GH-22966) (GH-22980) Message-ID: https://github.com/python/cpython/commit/c12afa92b0db6f017e479b41f95d75bac7b59850 commit: c12afa92b0db6f017e479b41f95d75bac7b59850 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-25T17:34:29-07:00 summary: [3.9] bpo-42146: Fix memory leak in subprocess.Popen() in case of uid/gid overflow (GH-22966) (GH-22980) Fix memory leak in subprocess.Popen() in case of uid/gid overflow Also add a test that would catch this leak with `--huntrleaks`. Alas, the test for `extra_groups` also exposes an inconsistency in our error reporting: we use a custom ValueError for `extra_groups`, but propagate OverflowError for `user` and `group`. (cherry picked from commit c0590c0033e86f98cdf5f2ca6898656f98ab4053) Co-authored-by: Alexey Izbyshev Automerge-Triggered-By: GH:gpshead files: A Misc/NEWS.d/next/Library/2020-10-25-19-25-02.bpo-42146.6A8uvS.rst M Lib/test/test_subprocess.py M Modules/_posixsubprocess.c diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index ba2844da9add9..373542463f2e3 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -1826,6 +1826,10 @@ def test_user(self): with self.assertRaises(ValueError): subprocess.check_call(ZERO_RETURN_CMD, user=-1) + with self.assertRaises(OverflowError): + subprocess.check_call(ZERO_RETURN_CMD, + cwd=os.curdir, env=os.environ, user=2**64) + if pwd is None and name_uid is not None: with self.assertRaises(ValueError): subprocess.check_call(ZERO_RETURN_CMD, user=name_uid) @@ -1869,6 +1873,10 @@ def test_group(self): with self.assertRaises(ValueError): subprocess.check_call(ZERO_RETURN_CMD, group=-1) + with self.assertRaises(OverflowError): + subprocess.check_call(ZERO_RETURN_CMD, + cwd=os.curdir, env=os.environ, group=2**64) + if grp is None: with self.assertRaises(ValueError): subprocess.check_call(ZERO_RETURN_CMD, group=name_group) @@ -1917,6 +1925,11 @@ def test_extra_groups(self): with self.assertRaises(ValueError): subprocess.check_call(ZERO_RETURN_CMD, extra_groups=[-1]) + with self.assertRaises(ValueError): + subprocess.check_call(ZERO_RETURN_CMD, + cwd=os.curdir, env=os.environ, + extra_groups=[2**64]) + if grp is None: with self.assertRaises(ValueError): subprocess.check_call(ZERO_RETURN_CMD, diff --git a/Misc/NEWS.d/next/Library/2020-10-25-19-25-02.bpo-42146.6A8uvS.rst b/Misc/NEWS.d/next/Library/2020-10-25-19-25-02.bpo-42146.6A8uvS.rst new file mode 100644 index 0000000000000..041809803db6a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-10-25-19-25-02.bpo-42146.6A8uvS.rst @@ -0,0 +1,2 @@ +Fix memory leak in :func:`subprocess.Popen` in case an uid (gid) specified in +`user` (`group`, `extra_groups`) overflows `uid_t` (`gid_t`). diff --git a/Modules/_posixsubprocess.c b/Modules/_posixsubprocess.c index 5d1691ace4192..5356417dd7037 100644 --- a/Modules/_posixsubprocess.c +++ b/Modules/_posixsubprocess.c @@ -626,7 +626,7 @@ subprocess_fork_exec(PyObject* self, PyObject *args) uid_t uid; gid_t gid, *groups = NULL; int child_umask; - PyObject *cwd_obj, *cwd_obj2; + PyObject *cwd_obj, *cwd_obj2 = NULL; const char *cwd; pid_t pid; int need_to_reenable_gc = 0; @@ -748,7 +748,6 @@ subprocess_fork_exec(PyObject* self, PyObject *args) cwd = PyBytes_AsString(cwd_obj2); } else { cwd = NULL; - cwd_obj2 = NULL; } if (groups_list != Py_None) { @@ -908,6 +907,7 @@ subprocess_fork_exec(PyObject* self, PyObject *args) return PyLong_FromPid(pid); cleanup: + Py_XDECREF(cwd_obj2); if (envp) _Py_FreeCharPArray(envp); if (argv) From webhook-mailer at python.org Mon Oct 26 01:26:43 2020 From: webhook-mailer at python.org (methane) Date: Mon, 26 Oct 2020 05:26:43 -0000 Subject: [Python-checkins] Add a link to buffer protocol in bytearray() doc (GH-22675) Message-ID: https://github.com/python/cpython/commit/0f25c231b3a024e358c3e55d9aba2f7bcc49630c commit: 0f25c231b3a024e358c3e55d9aba2f7bcc49630c branch: master author: Antoine <43954001+awecx at users.noreply.github.com> committer: methane date: 2020-10-26T14:26:34+09:00 summary: Add a link to buffer protocol in bytearray() doc (GH-22675) files: M Doc/library/functions.rst diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index b33aa81c643dc..52a1512b5884f 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -164,8 +164,8 @@ are always available. They are listed here in alphabetical order. * If it is an *integer*, the array will have that size and will be initialized with null bytes. - * If it is an object conforming to the *buffer* interface, a read-only buffer - of the object will be used to initialize the bytes array. + * If it is an object conforming to the :ref:`buffer interface `, + a read-only buffer of the object will be used to initialize the bytes array. * If it is an *iterable*, it must be an iterable of integers in the range ``0 <= x < 256``, which are used as the initial contents of the array. From webhook-mailer at python.org Mon Oct 26 01:30:57 2020 From: webhook-mailer at python.org (methane) Date: Mon, 26 Oct 2020 05:30:57 -0000 Subject: [Python-checkins] Added some makefile generated files to .gitignore (GH-22435) Message-ID: https://github.com/python/cpython/commit/96a9eed2457c05af6953890d89463704c9d99c57 commit: 96a9eed2457c05af6953890d89463704c9d99c57 branch: master author: Marco Sulla committer: methane date: 2020-10-26T14:30:51+09:00 summary: Added some makefile generated files to .gitignore (GH-22435) files: M .gitignore diff --git a/.gitignore b/.gitignore index 0962d3a1841fc..80dcf34bf47a6 100644 --- a/.gitignore +++ b/.gitignore @@ -103,6 +103,8 @@ Tools/unicode/data/ /config.status /config.status.lineno /platform +/profile-clean-stamp +/profile-run-stamp /pybuilddir.txt /pyconfig.h /python-config From webhook-mailer at python.org Mon Oct 26 01:35:59 2020 From: webhook-mailer at python.org (miss-islington) Date: Mon, 26 Oct 2020 05:35:59 -0000 Subject: [Python-checkins] Add a link to buffer protocol in bytearray() doc (GH-22675) Message-ID: https://github.com/python/cpython/commit/77a9ef1bbac7c56924be775857c94d7fc8eeb5f5 commit: 77a9ef1bbac7c56924be775857c94d7fc8eeb5f5 branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-25T22:35:55-07:00 summary: Add a link to buffer protocol in bytearray() doc (GH-22675) (cherry picked from commit 0f25c231b3a024e358c3e55d9aba2f7bcc49630c) Co-authored-by: Antoine <43954001+awecx at users.noreply.github.com> files: M Doc/library/functions.rst diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index 76a0ba4b0c766..4f5fe6bc23069 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -151,8 +151,8 @@ are always available. They are listed here in alphabetical order. * If it is an *integer*, the array will have that size and will be initialized with null bytes. - * If it is an object conforming to the *buffer* interface, a read-only buffer - of the object will be used to initialize the bytes array. + * If it is an object conforming to the :ref:`buffer interface `, + a read-only buffer of the object will be used to initialize the bytes array. * If it is an *iterable*, it must be an iterable of integers in the range ``0 <= x < 256``, which are used as the initial contents of the array. From webhook-mailer at python.org Mon Oct 26 01:37:20 2020 From: webhook-mailer at python.org (methane) Date: Mon, 26 Oct 2020 05:37:20 -0000 Subject: [Python-checkins] bpo-39871: Fix an error in a news entry (GH-21749) Message-ID: https://github.com/python/cpython/commit/f6255a2ccb55a63194caf698f471c803c08be359 commit: f6255a2ccb55a63194caf698f471c803c08be359 branch: 3.8 author: Zackery Spytz committer: methane date: 2020-10-26T14:37:16+09:00 summary: bpo-39871: Fix an error in a news entry (GH-21749) (cherry picked from commit 54636355805dd2877bb54fbad8d967e1ddd8b553) files: M Misc/NEWS.d/3.8.3rc1.rst diff --git a/Misc/NEWS.d/3.8.3rc1.rst b/Misc/NEWS.d/3.8.3rc1.rst index f07bf9072bd61..b79b7bf330864 100644 --- a/Misc/NEWS.d/3.8.3rc1.rst +++ b/Misc/NEWS.d/3.8.3rc1.rst @@ -83,7 +83,7 @@ Wozniski. Fix a possible :exc:`SystemError` in ``math.{atan2,copysign,remainder}()`` when the first argument cannot be converted to a :class:`float`. Patch by -Zachary Spytz. +Zackery Spytz. .. From webhook-mailer at python.org Mon Oct 26 01:46:16 2020 From: webhook-mailer at python.org (methane) Date: Mon, 26 Oct 2020 05:46:16 -0000 Subject: [Python-checkins] bpo-41662: Fix bugs in binding parameters in sqlite3 (GH-21998) Message-ID: https://github.com/python/cpython/commit/a053a7ecfef006b834a9957468fa461dafb332db commit: a053a7ecfef006b834a9957468fa461dafb332db branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: methane date: 2020-10-26T14:46:10+09:00 summary: bpo-41662: Fix bugs in binding parameters in sqlite3 (GH-21998) * When the parameters argument is a list, correctly handle the case of changing it during iteration. * When the parameters argument is a custom sequence, no longer override an exception raised in ``__len__()``. (cherry picked from commit 0b419b791077414bbc011a412698ebb362b63761) Co-authored-by: Serhiy Storchaka files: A Misc/NEWS.d/next/Library/2020-08-29-16-07-36.bpo-41662.Mn79zh.rst A Misc/NEWS.d/next/Library/2020-08-30-21-38-57.bpo-41662.6e9iZn.rst M Lib/sqlite3/test/dbapi.py M Lib/sqlite3/test/regression.py M Modules/_sqlite/statement.c diff --git a/Lib/sqlite3/test/dbapi.py b/Lib/sqlite3/test/dbapi.py index 7c259d2af418f..2fb07485acf4c 100644 --- a/Lib/sqlite3/test/dbapi.py +++ b/Lib/sqlite3/test/dbapi.py @@ -276,7 +276,7 @@ def CheckExecuteParamList(self): self.assertEqual(row[0], "foo") def CheckExecuteParamSequence(self): - class L(object): + class L: def __len__(self): return 1 def __getitem__(self, x): @@ -288,6 +288,18 @@ def __getitem__(self, x): row = self.cu.fetchone() self.assertEqual(row[0], "foo") + def CheckExecuteParamSequenceBadLen(self): + # Issue41662: Error in __len__() was overridden with ProgrammingError. + class L: + def __len__(self): + 1/0 + def __getitem__(slf, x): + raise AssertionError + + self.cu.execute("insert into test(name) values ('foo')") + with self.assertRaises(ZeroDivisionError): + self.cu.execute("select name from test where name=?", L()) + def CheckExecuteDictMapping(self): self.cu.execute("insert into test(name) values ('foo')") self.cu.execute("select name from test where name=:name", {"name": "foo"}) diff --git a/Lib/sqlite3/test/regression.py b/Lib/sqlite3/test/regression.py index ce97655c66be8..206ecd7ac7537 100644 --- a/Lib/sqlite3/test/regression.py +++ b/Lib/sqlite3/test/regression.py @@ -133,6 +133,19 @@ def CheckTypeMapUsage(self): con.execute("insert into foo(bar) values (5)") con.execute(SELECT) + def CheckBindMutatingList(self): + # Issue41662: Crash when mutate a list of parameters during iteration. + class X: + def __conform__(self, protocol): + parameters.clear() + return "..." + parameters = [X(), 0] + con = sqlite.connect(":memory:",detect_types=sqlite.PARSE_DECLTYPES) + con.execute("create table foo(bar X, baz integer)") + # Should not crash + with self.assertRaises(IndexError): + con.execute("insert into foo(bar, baz) values (?, ?)", parameters) + def CheckErrorMsgDecodeError(self): # When porting the module to Python 3.0, the error message about # decoding errors disappeared. This verifies they're back again. diff --git a/Misc/NEWS.d/next/Library/2020-08-29-16-07-36.bpo-41662.Mn79zh.rst b/Misc/NEWS.d/next/Library/2020-08-29-16-07-36.bpo-41662.Mn79zh.rst new file mode 100644 index 0000000000000..0571c2d110bee --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-08-29-16-07-36.bpo-41662.Mn79zh.rst @@ -0,0 +1 @@ +Fixed crash when mutate list of parameters during iteration in :mod:`sqlite3`. diff --git a/Misc/NEWS.d/next/Library/2020-08-30-21-38-57.bpo-41662.6e9iZn.rst b/Misc/NEWS.d/next/Library/2020-08-30-21-38-57.bpo-41662.6e9iZn.rst new file mode 100644 index 0000000000000..aecb0a1ea4d08 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-08-30-21-38-57.bpo-41662.6e9iZn.rst @@ -0,0 +1,2 @@ +No longer override exceptions raised in ``__len__()`` of a sequence of +parameters in :mod:`sqlite3` with :exc:`~sqlite3.ProgrammingError`. diff --git a/Modules/_sqlite/statement.c b/Modules/_sqlite/statement.c index 491294b0209cc..70fd95f0fbbfb 100644 --- a/Modules/_sqlite/statement.c +++ b/Modules/_sqlite/statement.c @@ -225,6 +225,9 @@ void pysqlite_statement_bind_parameters(pysqlite_Statement* self, PyObject* para num_params = PyList_GET_SIZE(parameters); } else { num_params = PySequence_Size(parameters); + if (num_params == -1) { + return; + } } if (num_params != num_params_needed) { PyErr_Format(pysqlite_ProgrammingError, @@ -236,9 +239,9 @@ void pysqlite_statement_bind_parameters(pysqlite_Statement* self, PyObject* para for (i = 0; i < num_params; i++) { if (PyTuple_CheckExact(parameters)) { current_param = PyTuple_GET_ITEM(parameters, i); - Py_XINCREF(current_param); + Py_INCREF(current_param); } else if (PyList_CheckExact(parameters)) { - current_param = PyList_GET_ITEM(parameters, i); + current_param = PyList_GetItem(parameters, i); Py_XINCREF(current_param); } else { current_param = PySequence_GetItem(parameters, i); From webhook-mailer at python.org Mon Oct 26 01:50:16 2020 From: webhook-mailer at python.org (miss-islington) Date: Mon, 26 Oct 2020 05:50:16 -0000 Subject: [Python-checkins] Added some makefile generated files to .gitignore (GH-22435) Message-ID: https://github.com/python/cpython/commit/36cb0c88cf69c5d88440e784db0f214adf6154fc commit: 36cb0c88cf69c5d88440e784db0f214adf6154fc branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-25T22:50:11-07:00 summary: Added some makefile generated files to .gitignore (GH-22435) (cherry picked from commit 96a9eed2457c05af6953890d89463704c9d99c57) Co-authored-by: Marco Sulla files: M .gitignore diff --git a/.gitignore b/.gitignore index 0962d3a1841fc..80dcf34bf47a6 100644 --- a/.gitignore +++ b/.gitignore @@ -103,6 +103,8 @@ Tools/unicode/data/ /config.status /config.status.lineno /platform +/profile-clean-stamp +/profile-run-stamp /pybuilddir.txt /pyconfig.h /python-config From webhook-mailer at python.org Mon Oct 26 01:51:08 2020 From: webhook-mailer at python.org (methane) Date: Mon, 26 Oct 2020 05:51:08 -0000 Subject: [Python-checkins] Added some makefile generated files to .gitignore (GH-22435) Message-ID: https://github.com/python/cpython/commit/d2071097a7bb6ba71d542b30f73ad79dd824c893 commit: d2071097a7bb6ba71d542b30f73ad79dd824c893 branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: methane date: 2020-10-26T14:51:03+09:00 summary: Added some makefile generated files to .gitignore (GH-22435) (cherry picked from commit 96a9eed2457c05af6953890d89463704c9d99c57) Co-authored-by: Marco Sulla files: M .gitignore diff --git a/.gitignore b/.gitignore index b662374b1255c..960bc7ca59e31 100644 --- a/.gitignore +++ b/.gitignore @@ -99,6 +99,8 @@ PCbuild/win32/ /config.status /config.status.lineno /platform +/profile-clean-stamp +/profile-run-stamp /pybuilddir.txt /pyconfig.h /python-config From webhook-mailer at python.org Mon Oct 26 01:59:54 2020 From: webhook-mailer at python.org (miss-islington) Date: Mon, 26 Oct 2020 05:59:54 -0000 Subject: [Python-checkins] Add a link to buffer protocol in bytearray() doc (GH-22675) Message-ID: https://github.com/python/cpython/commit/df4790c071ea47ea3d5e0697dadc78105cf7f280 commit: df4790c071ea47ea3d5e0697dadc78105cf7f280 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-25T22:59:46-07:00 summary: Add a link to buffer protocol in bytearray() doc (GH-22675) (cherry picked from commit 0f25c231b3a024e358c3e55d9aba2f7bcc49630c) Co-authored-by: Antoine <43954001+awecx at users.noreply.github.com> files: M Doc/library/functions.rst diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index ca6dabfcdddaf..2a54ce5baf638 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -150,8 +150,8 @@ are always available. They are listed here in alphabetical order. * If it is an *integer*, the array will have that size and will be initialized with null bytes. - * If it is an object conforming to the *buffer* interface, a read-only buffer - of the object will be used to initialize the bytes array. + * If it is an object conforming to the :ref:`buffer interface `, + a read-only buffer of the object will be used to initialize the bytes array. * If it is an *iterable*, it must be an iterable of integers in the range ``0 <= x < 256``, which are used as the initial contents of the array. From webhook-mailer at python.org Mon Oct 26 02:43:50 2020 From: webhook-mailer at python.org (serhiy-storchaka) Date: Mon, 26 Oct 2020 06:43:50 -0000 Subject: [Python-checkins] bpo-42006: Stop using PyDict_GetItem, PyDict_GetItemString and _PyDict_GetItemId. (GH-22648) Message-ID: https://github.com/python/cpython/commit/fb5db7ec58624cab0797b4050735be865d380823 commit: fb5db7ec58624cab0797b4050735be865d380823 branch: master author: Serhiy Storchaka committer: serhiy-storchaka date: 2020-10-26T08:43:39+02:00 summary: bpo-42006: Stop using PyDict_GetItem, PyDict_GetItemString and _PyDict_GetItemId. (GH-22648) These functions are considered not safe because they suppress all internal errors and can return wrong result. PyDict_GetItemString and _PyDict_GetItemId can also silence current exception in rare cases. Remove no longer used _PyDict_GetItemId. Add _PyDict_ContainsId and rename _PyDict_Contains into _PyDict_Contains_KnownHash. files: M Include/cpython/dictobject.h M Modules/_decimal/_decimal.c M Modules/_threadmodule.c M Modules/_zoneinfo.c M Modules/signalmodule.c M Modules/socketmodule.c M Objects/dictobject.c M Objects/moduleobject.c M Objects/setobject.c M Objects/structseq.c M Objects/typeobject.c M Python/ceval.c M Python/compile.c M Python/pylifecycle.c M Python/pythonrun.c M Python/symtable.c M Python/sysmodule.c diff --git a/Include/cpython/dictobject.h b/Include/cpython/dictobject.h index f67c3214ddd9a..6822a65cad95e 100644 --- a/Include/cpython/dictobject.h +++ b/Include/cpython/dictobject.h @@ -46,7 +46,8 @@ PyAPI_FUNC(int) _PyDict_Next( /* Get the number of items of a dictionary. */ #define PyDict_GET_SIZE(mp) (assert(PyDict_Check(mp)),((PyDictObject *)mp)->ma_used) -PyAPI_FUNC(int) _PyDict_Contains(PyObject *mp, PyObject *key, Py_hash_t hash); +PyAPI_FUNC(int) _PyDict_Contains_KnownHash(PyObject *, PyObject *, Py_hash_t); +PyAPI_FUNC(int) _PyDict_ContainsId(PyObject *, struct _Py_Identifier *); PyAPI_FUNC(PyObject *) _PyDict_NewPresized(Py_ssize_t minused); PyAPI_FUNC(void) _PyDict_MaybeUntrack(PyObject *mp); PyAPI_FUNC(int) _PyDict_HasOnlyStringKeys(PyObject *mp); @@ -63,7 +64,6 @@ PyObject *_PyDict_FromKeys(PyObject *, PyObject *, PyObject *); argument is raised. */ PyAPI_FUNC(int) _PyDict_MergeEx(PyObject *mp, PyObject *other, int override); -PyAPI_FUNC(PyObject *) _PyDict_GetItemId(PyObject *dp, struct _Py_Identifier *key); PyAPI_FUNC(int) _PyDict_SetItemId(PyObject *dp, struct _Py_Identifier *key, PyObject *item); PyAPI_FUNC(int) _PyDict_DelItemId(PyObject *mp, struct _Py_Identifier *key); diff --git a/Modules/_decimal/_decimal.c b/Modules/_decimal/_decimal.c index e7c44acba02fc..ea16c5a6cd9cd 100644 --- a/Modules/_decimal/_decimal.c +++ b/Modules/_decimal/_decimal.c @@ -3186,6 +3186,31 @@ dotsep_as_utf8(const char *s) return utf8; } +static int +dict_get_item_string(PyObject *dict, const char *key, PyObject **valueobj, const char **valuestr) +{ + *valueobj = NULL; + PyObject *keyobj = PyUnicode_FromString(key); + if (keyobj == NULL) { + return -1; + } + PyObject *value = PyDict_GetItemWithError(dict, keyobj); + Py_DECREF(keyobj); + if (value == NULL) { + if (PyErr_Occurred()) { + return -1; + } + return 0; + } + value = PyUnicode_AsUTF8String(value); + if (value == NULL) { + return -1; + } + *valueobj = value; + *valuestr = PyBytes_AS_STRING(value); + return 0; +} + /* Formatted representation of a PyDecObject. */ static PyObject * dec_format(PyObject *dec, PyObject *args) @@ -3256,23 +3281,11 @@ dec_format(PyObject *dec, PyObject *args) "optional argument must be a dict"); goto finish; } - if ((dot = PyDict_GetItemString(override, "decimal_point"))) { - if ((dot = PyUnicode_AsUTF8String(dot)) == NULL) { - goto finish; - } - spec.dot = PyBytes_AS_STRING(dot); - } - if ((sep = PyDict_GetItemString(override, "thousands_sep"))) { - if ((sep = PyUnicode_AsUTF8String(sep)) == NULL) { - goto finish; - } - spec.sep = PyBytes_AS_STRING(sep); - } - if ((grouping = PyDict_GetItemString(override, "grouping"))) { - if ((grouping = PyUnicode_AsUTF8String(grouping)) == NULL) { - goto finish; - } - spec.grouping = PyBytes_AS_STRING(grouping); + if (dict_get_item_string(override, "decimal_point", &dot, &spec.dot) || + dict_get_item_string(override, "thousands_sep", &sep, &spec.sep) || + dict_get_item_string(override, "grouping", &grouping, &spec.grouping)) + { + goto finish; } if (mpd_validate_lconv(&spec) < 0) { PyErr_SetString(PyExc_ValueError, diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c index 77baba4847897..56ed8a2e2d3f1 100644 --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -816,10 +816,14 @@ local_clear(localobject *self) for(tstate = PyInterpreterState_ThreadHead(tstate->interp); tstate; tstate = PyThreadState_Next(tstate)) - if (tstate->dict && PyDict_GetItem(tstate->dict, self->key)) { - if (PyDict_DelItem(tstate->dict, self->key)) { + if (tstate->dict) { + PyObject *v = _PyDict_Pop(tstate->dict, self->key, Py_None); + if (v == NULL) { PyErr_Clear(); } + else { + Py_DECREF(v); + } } } return 0; diff --git a/Modules/_zoneinfo.c b/Modules/_zoneinfo.c index bee59b8d2ae0c..76b667d1543e9 100644 --- a/Modules/_zoneinfo.c +++ b/Modules/_zoneinfo.c @@ -722,17 +722,16 @@ zoneinfo__unpickle(PyTypeObject *cls, PyObject *args) static PyObject * load_timedelta(long seconds) { - PyObject *rv = NULL; + PyObject *rv; PyObject *pyoffset = PyLong_FromLong(seconds); if (pyoffset == NULL) { return NULL; } - int contains = PyDict_Contains(TIMEDELTA_CACHE, pyoffset); - if (contains == -1) { - goto error; - } - - if (!contains) { + rv = PyDict_GetItemWithError(TIMEDELTA_CACHE, pyoffset); + if (rv == NULL) { + if (PyErr_Occurred()) { + goto error; + } PyObject *tmp = PyDateTimeAPI->Delta_FromDelta( 0, seconds, 0, 1, PyDateTimeAPI->DeltaType); @@ -743,12 +742,9 @@ load_timedelta(long seconds) rv = PyDict_SetDefault(TIMEDELTA_CACHE, pyoffset, tmp); Py_DECREF(tmp); } - else { - rv = PyDict_GetItem(TIMEDELTA_CACHE, pyoffset); - } + Py_XINCREF(rv); Py_DECREF(pyoffset); - Py_INCREF(rv); return rv; error: Py_DECREF(pyoffset); diff --git a/Modules/signalmodule.c b/Modules/signalmodule.c index 3440894b2159d..0ab3a71b18099 100644 --- a/Modules/signalmodule.c +++ b/Modules/signalmodule.c @@ -1427,10 +1427,9 @@ signal_exec(PyObject *m) return -1; #endif - IntHandler = PyDict_GetItemString(d, "default_int_handler"); + IntHandler = PyMapping_GetItemString(d, "default_int_handler"); if (!IntHandler) return -1; - Py_INCREF(IntHandler); _Py_atomic_store_relaxed(&Handlers[0].tripped, 0); for (int i = 1; i < NSIG; i++) { diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index db0eeaafeec27..d7738367029e8 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -324,7 +324,7 @@ static FlagRuntimeInfo win_runtime_flags[] = { {14393, "TCP_FASTOPEN"} }; -static void +static int remove_unusable_flags(PyObject *m) { PyObject *dict; @@ -333,7 +333,7 @@ remove_unusable_flags(PyObject *m) dict = PyModule_GetDict(m); if (dict == NULL) { - return; + return -1; } /* set to Windows 10, except BuildNumber. */ @@ -359,19 +359,19 @@ remove_unusable_flags(PyObject *m) break; } else { - if (PyDict_GetItemString( - dict, - win_runtime_flags[i].flag_name) != NULL) - { - if (PyDict_DelItemString( - dict, - win_runtime_flags[i].flag_name)) - { - PyErr_Clear(); - } + PyObject *flag_name = PyUnicode_FromString(win_runtime_flags[i].flag_name); + if (flag_name == NULL) { + return -1; + } + PyObject *v = _PyDict_Pop(dict, flag_name, Py_None); + Py_DECREF(flag_name); + if (v == NULL) { + return -1; } + Py_DECREF(v); } } + return 0; } #endif @@ -8382,7 +8382,10 @@ PyInit__socket(void) #ifdef MS_WINDOWS /* remove some flags on older version Windows during run-time */ - remove_unusable_flags(m); + if (remove_unusable_flags(m) < 0) { + Py_DECREF(m); + return NULL; + } #endif return m; diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 42d71e56d4547..00d6ab3be2ffa 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -3434,7 +3434,7 @@ PyDict_Contains(PyObject *op, PyObject *key) /* Internal version of PyDict_Contains used when the hash value is already known */ int -_PyDict_Contains(PyObject *op, PyObject *key, Py_hash_t hash) +_PyDict_Contains_KnownHash(PyObject *op, PyObject *key, Py_hash_t hash) { PyDictObject *mp = (PyDictObject *)op; PyObject *value; @@ -3446,6 +3446,16 @@ _PyDict_Contains(PyObject *op, PyObject *key, Py_hash_t hash) return (ix != DKIX_EMPTY && value != NULL); } +int +_PyDict_ContainsId(PyObject *op, struct _Py_Identifier *key) +{ + PyObject *kv = _PyUnicode_FromId(key); /* borrowed */ + if (kv == NULL) { + return -1; + } + return PyDict_Contains(op, kv); +} + /* Hack to implement "key in dict" */ static PySequenceMethods dict_as_sequence = { 0, /* sq_length */ @@ -3590,18 +3600,6 @@ PyTypeObject PyDict_Type = { .tp_vectorcall = dict_vectorcall, }; -PyObject * -_PyDict_GetItemId(PyObject *dp, struct _Py_Identifier *key) -{ - PyObject *kv; - kv = _PyUnicode_FromId(key); /* borrowed */ - if (kv == NULL) { - PyErr_Clear(); - return NULL; - } - return PyDict_GetItem(dp, kv); -} - /* For backward compatibility with old dictionary interface */ PyObject * diff --git a/Objects/moduleobject.c b/Objects/moduleobject.c index ee4ed97588e29..c3ceb788e8e69 100644 --- a/Objects/moduleobject.c +++ b/Objects/moduleobject.c @@ -477,10 +477,12 @@ PyModule_GetNameObject(PyObject *m) } d = ((PyModuleObject *)m)->md_dict; if (d == NULL || - (name = _PyDict_GetItemId(d, &PyId___name__)) == NULL || + (name = _PyDict_GetItemIdWithError(d, &PyId___name__)) == NULL || !PyUnicode_Check(name)) { - PyErr_SetString(PyExc_SystemError, "nameless module"); + if (!PyErr_Occurred()) { + PyErr_SetString(PyExc_SystemError, "nameless module"); + } return NULL; } Py_INCREF(name); @@ -509,10 +511,12 @@ PyModule_GetFilenameObject(PyObject *m) } d = ((PyModuleObject *)m)->md_dict; if (d == NULL || - (fileobj = _PyDict_GetItemId(d, &PyId___file__)) == NULL || + (fileobj = _PyDict_GetItemIdWithError(d, &PyId___file__)) == NULL || !PyUnicode_Check(fileobj)) { - PyErr_SetString(PyExc_SystemError, "module filename missing"); + if (!PyErr_Occurred()) { + PyErr_SetString(PyExc_SystemError, "module filename missing"); + } return NULL; } Py_INCREF(fileobj); @@ -721,14 +725,21 @@ module_getattro(PyModuleObject *m, PyObject *name) PyErr_Clear(); if (m->md_dict) { _Py_IDENTIFIER(__getattr__); - getattr = _PyDict_GetItemId(m->md_dict, &PyId___getattr__); + getattr = _PyDict_GetItemIdWithError(m->md_dict, &PyId___getattr__); if (getattr) { return PyObject_CallOneArg(getattr, name); } - mod_name = _PyDict_GetItemId(m->md_dict, &PyId___name__); + if (PyErr_Occurred()) { + return NULL; + } + mod_name = _PyDict_GetItemIdWithError(m->md_dict, &PyId___name__); if (mod_name && PyUnicode_Check(mod_name)) { Py_INCREF(mod_name); - PyObject *spec = _PyDict_GetItemId(m->md_dict, &PyId___spec__); + PyObject *spec = _PyDict_GetItemIdWithError(m->md_dict, &PyId___spec__); + if (spec == NULL && PyErr_Occurred()) { + Py_DECREF(mod_name); + return NULL; + } Py_XINCREF(spec); if (_PyModuleSpec_IsInitializing(spec)) { PyErr_Format(PyExc_AttributeError, @@ -746,6 +757,9 @@ module_getattro(PyModuleObject *m, PyObject *name) Py_DECREF(mod_name); return NULL; } + else if (PyErr_Occurred()) { + return NULL; + } } PyErr_Format(PyExc_AttributeError, "module has no attribute '%U'", name); diff --git a/Objects/setobject.c b/Objects/setobject.c index b2711495b657b..af8ee03d831d6 100644 --- a/Objects/setobject.c +++ b/Objects/setobject.c @@ -1498,7 +1498,7 @@ set_difference(PySetObject *so, PyObject *other) while (set_next(so, &pos, &entry)) { key = entry->key; hash = entry->hash; - rv = _PyDict_Contains(other, key, hash); + rv = _PyDict_Contains_KnownHash(other, key, hash); if (rv < 0) { Py_DECREF(result); return NULL; diff --git a/Objects/structseq.c b/Objects/structseq.c index 8ae8f28cbc580..5caa3bd52e4d4 100644 --- a/Objects/structseq.c +++ b/Objects/structseq.c @@ -19,20 +19,33 @@ static const char unnamed_fields_key[] = "n_unnamed_fields"; /* Fields with this name have only a field index, not a field name. They are only allowed for indices < n_visible_fields. */ const char * const PyStructSequence_UnnamedField = "unnamed field"; + _Py_IDENTIFIER(n_sequence_fields); _Py_IDENTIFIER(n_fields); _Py_IDENTIFIER(n_unnamed_fields); -#define VISIBLE_SIZE(op) Py_SIZE(op) -#define VISIBLE_SIZE_TP(tp) PyLong_AsSsize_t( \ - _PyDict_GetItemId((tp)->tp_dict, &PyId_n_sequence_fields)) +static ssize_t +get_type_attr_as_size(PyTypeObject *tp, _Py_Identifier *id) +{ + PyObject *name = _PyUnicode_FromId(id); + if (name == NULL) { + return -1; + } + PyObject *v = PyDict_GetItemWithError(tp->tp_dict, name); + if (v == NULL && !PyErr_Occurred()) { + PyErr_Format(PyExc_TypeError, + "Missed attribute '%U' of type %s", + name, tp->tp_name); + } + return PyLong_AsSsize_t(v); +} -#define REAL_SIZE_TP(tp) PyLong_AsSsize_t( \ - _PyDict_GetItemId((tp)->tp_dict, &PyId_n_fields)) +#define VISIBLE_SIZE(op) Py_SIZE(op) +#define VISIBLE_SIZE_TP(tp) get_type_attr_as_size(tp, &PyId_n_sequence_fields) +#define REAL_SIZE_TP(tp) get_type_attr_as_size(tp, &PyId_n_fields) #define REAL_SIZE(op) REAL_SIZE_TP(Py_TYPE(op)) -#define UNNAMED_FIELDS_TP(tp) PyLong_AsSsize_t( \ - _PyDict_GetItemId((tp)->tp_dict, &PyId_n_unnamed_fields)) +#define UNNAMED_FIELDS_TP(tp) get_type_attr_as_size(tp, &PyId_n_unnamed_fields) #define UNNAMED_FIELDS(op) UNNAMED_FIELDS_TP(Py_TYPE(op)) @@ -41,13 +54,20 @@ PyStructSequence_New(PyTypeObject *type) { PyStructSequence *obj; Py_ssize_t size = REAL_SIZE_TP(type), i; + if (size < 0) { + return NULL; + } + Py_ssize_t vsize = VISIBLE_SIZE_TP(type); + if (vsize < 0) { + return NULL; + } obj = PyObject_GC_NewVar(PyStructSequence, type, size); if (obj == NULL) return NULL; /* Hack the size of the variable object, so invisible fields don't appear to Python code. */ - Py_SET_SIZE(obj, VISIBLE_SIZE_TP(type)); + Py_SET_SIZE(obj, vsize); for (i = 0; i < size; i++) obj->ob_item[i] = NULL; @@ -121,6 +141,19 @@ structseq_new_impl(PyTypeObject *type, PyObject *arg, PyObject *dict) PyStructSequence *res = NULL; Py_ssize_t len, min_len, max_len, i, n_unnamed_fields; + min_len = VISIBLE_SIZE_TP(type); + if (min_len < 0) { + return NULL; + } + max_len = REAL_SIZE_TP(type); + if (max_len < 0) { + return NULL; + } + n_unnamed_fields = UNNAMED_FIELDS_TP(type); + if (n_unnamed_fields < 0) { + return NULL; + } + arg = PySequence_Fast(arg, "constructor requires a sequence"); if (!arg) { @@ -136,10 +169,6 @@ structseq_new_impl(PyTypeObject *type, PyObject *arg, PyObject *dict) } len = PySequence_Fast_GET_SIZE(arg); - min_len = VISIBLE_SIZE_TP(type); - max_len = REAL_SIZE_TP(type); - n_unnamed_fields = UNNAMED_FIELDS_TP(type); - if (min_len != max_len) { if (len < min_len) { PyErr_Format(PyExc_TypeError, @@ -177,18 +206,26 @@ structseq_new_impl(PyTypeObject *type, PyObject *arg, PyObject *dict) Py_INCREF(v); res->ob_item[i] = v; } + Py_DECREF(arg); for (; i < max_len; ++i) { - if (dict && (ob = PyDict_GetItemString( - dict, type->tp_members[i-n_unnamed_fields].name))) { + if (dict == NULL) { + ob = Py_None; } else { - ob = Py_None; + ob = _PyDict_GetItemStringWithError(dict, + type->tp_members[i-n_unnamed_fields].name); + if (ob == NULL) { + if (PyErr_Occurred()) { + Py_DECREF(res); + return NULL; + } + ob = Py_None; + } } Py_INCREF(ob); res->ob_item[i] = ob; } - Py_DECREF(arg); _PyObject_GC_TRACK(res); return (PyObject*) res; } @@ -288,8 +325,14 @@ structseq_reduce(PyStructSequence* self, PyObject *Py_UNUSED(ignored)) Py_ssize_t n_fields, n_visible_fields, n_unnamed_fields, i; n_fields = REAL_SIZE(self); + if (n_fields < 0) { + return NULL; + } n_visible_fields = VISIBLE_SIZE(self); n_unnamed_fields = UNNAMED_FIELDS(self); + if (n_unnamed_fields < 0) { + return NULL; + } tup = _PyTuple_FromArray(self->ob_item, n_visible_fields); if (!tup) goto error; diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 36c7662e081a4..6626169ae37f6 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -4767,8 +4767,11 @@ object___reduce_ex___impl(PyObject *self, int protocol) _Py_IDENTIFIER(__reduce__); if (objreduce == NULL) { - objreduce = _PyDict_GetItemId(PyBaseObject_Type.tp_dict, - &PyId___reduce__); + objreduce = _PyDict_GetItemIdWithError(PyBaseObject_Type.tp_dict, + &PyId___reduce__); + if (objreduce == NULL && PyErr_Occurred()) { + return NULL; + } } if (_PyObject_LookupAttrId(self, &PyId___reduce__, &reduce) < 0) { @@ -5181,14 +5184,14 @@ overrides_hash(PyTypeObject *type) _Py_IDENTIFIER(__eq__); assert(dict != NULL); - if (_PyDict_GetItemId(dict, &PyId___eq__) != NULL) - return 1; - if (_PyDict_GetItemId(dict, &PyId___hash__) != NULL) - return 1; - return 0; + int r = _PyDict_ContainsId(dict, &PyId___eq__); + if (r == 0) { + r = _PyDict_ContainsId(dict, &PyId___hash__); + } + return r; } -static void +static int inherit_slots(PyTypeObject *type, PyTypeObject *base) { PyTypeObject *basebase; @@ -5331,11 +5334,16 @@ inherit_slots(PyTypeObject *type, PyTypeObject *base) /* Copy comparison-related slots only when not overriding them anywhere */ if (type->tp_richcompare == NULL && - type->tp_hash == NULL && - !overrides_hash(type)) + type->tp_hash == NULL) { - type->tp_richcompare = base->tp_richcompare; - type->tp_hash = base->tp_hash; + int r = overrides_hash(type); + if (r < 0) { + return -1; + } + if (!r) { + type->tp_richcompare = base->tp_richcompare; + type->tp_hash = base->tp_hash; + } } } { @@ -5378,6 +5386,7 @@ inherit_slots(PyTypeObject *type, PyTypeObject *base) * obvious to be done -- the type is on its own. */ } + return 0; } static int add_operators(PyTypeObject *); @@ -5507,8 +5516,11 @@ PyType_Ready(PyTypeObject *type) n = PyTuple_GET_SIZE(bases); for (i = 1; i < n; i++) { PyObject *b = PyTuple_GET_ITEM(bases, i); - if (PyType_Check(b)) - inherit_slots(type, (PyTypeObject *)b); + if (PyType_Check(b)) { + if (inherit_slots(type, (PyTypeObject *)b) < 0) { + goto error; + } + } } /* All bases of statically allocated type should be statically allocated */ diff --git a/Python/ceval.c b/Python/ceval.c index fafbf7524bb84..7338be57798fd 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1446,11 +1446,18 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) } #ifdef LLTRACE - lltrace = _PyDict_GetItemId(f->f_globals, &PyId___ltrace__) != NULL; + { + int r = _PyDict_ContainsId(f->f_globals, &PyId___ltrace__); + if (r < 0) { + goto exit_eval_frame; + } + lltrace = r; + } #endif - if (throwflag) /* support for generator.throw() */ + if (throwflag) { /* support for generator.throw() */ goto error; + } #ifdef Py_DEBUG /* _PyEval_EvalFrameDefault() must not be called with an exception set, diff --git a/Python/compile.c b/Python/compile.c index ddd2a049629c1..a0089b2d6dc18 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -489,8 +489,8 @@ dictbytype(PyObject *src, int scope_type, int flag, Py_ssize_t offset) /* XXX this should probably be a macro in symtable.h */ long vi; k = PyList_GET_ITEM(sorted_keys, key_i); - v = PyDict_GetItem(src, k); - assert(PyLong_Check(v)); + v = PyDict_GetItemWithError(src, k); + assert(v && PyLong_Check(v)); vi = PyLong_AS_LONG(v); scope = (vi >> SCOPE_OFFSET) & SCOPE_MASK; @@ -1889,7 +1889,7 @@ static int compiler_lookup_arg(PyObject *dict, PyObject *name) { PyObject *v; - v = PyDict_GetItem(dict, name); + v = PyDict_GetItemWithError(dict, name); if (v == NULL) return -1; return PyLong_AS_LONG(v); diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 75d57805c07b6..774a4f9de08e0 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -160,7 +160,7 @@ init_importlib(PyThreadState *tstate, PyObject *sysmod) interp->importlib = importlib; Py_INCREF(interp->importlib); - interp->import_func = PyDict_GetItemString(interp->builtins, "__import__"); + interp->import_func = _PyDict_GetItemStringWithError(interp->builtins, "__import__"); if (interp->import_func == NULL) return _PyStatus_ERR("__import__ not found"); Py_INCREF(interp->import_func); @@ -1683,7 +1683,10 @@ add_main_module(PyInterpreterState *interp) } Py_DECREF(ann_dict); - if (PyDict_GetItemString(d, "__builtins__") == NULL) { + if (_PyDict_GetItemStringWithError(d, "__builtins__") == NULL) { + if (PyErr_Occurred()) { + return _PyStatus_ERR("Failed to test __main__.__builtins__"); + } PyObject *bimod = PyImport_ImportModule("builtins"); if (bimod == NULL) { return _PyStatus_ERR("Failed to retrieve builtins module"); @@ -1700,8 +1703,11 @@ add_main_module(PyInterpreterState *interp) * be set if __main__ gets further initialized later in the startup * process. */ - loader = PyDict_GetItemString(d, "__loader__"); + loader = _PyDict_GetItemStringWithError(d, "__loader__"); if (loader == NULL || loader == Py_None) { + if (PyErr_Occurred()) { + return _PyStatus_ERR("Failed to test __main__.__loader__"); + } PyObject *loader = PyObject_GetAttrString(interp->importlib, "BuiltinImporter"); if (loader == NULL) { diff --git a/Python/pythonrun.c b/Python/pythonrun.c index a45ca3b18311d..bd49c40e9786c 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -351,7 +351,10 @@ PyRun_SimpleFileExFlags(FILE *fp, const char *filename, int closeit, return -1; Py_INCREF(m); d = PyModule_GetDict(m); - if (PyDict_GetItemString(d, "__file__") == NULL) { + if (_PyDict_GetItemStringWithError(d, "__file__") == NULL) { + if (PyErr_Occurred()) { + goto done; + } PyObject *f; f = PyUnicode_DecodeFSDefault(filename); if (f == NULL) @@ -1116,9 +1119,11 @@ run_eval_code_obj(PyThreadState *tstate, PyCodeObject *co, PyObject *globals, Py _Py_UnhandledKeyboardInterrupt = 0; /* Set globals['__builtins__'] if it doesn't exist */ - if (globals != NULL && PyDict_GetItemString(globals, "__builtins__") == NULL) { - if (PyDict_SetItemString(globals, "__builtins__", - tstate->interp->builtins) < 0) { + if (globals != NULL && _PyDict_GetItemStringWithError(globals, "__builtins__") == NULL) { + if (PyErr_Occurred() || + PyDict_SetItemString(globals, "__builtins__", + tstate->interp->builtins) < 0) + { return NULL; } } diff --git a/Python/symtable.c b/Python/symtable.c index 4a98e79e74a25..0464cd898b27f 100644 --- a/Python/symtable.c +++ b/Python/symtable.c @@ -392,7 +392,7 @@ PySymtable_Lookup(struct symtable *st, void *key) static long _PyST_GetSymbol(PySTEntryObject *ste, PyObject *name) { - PyObject *v = PyDict_GetItem(ste->ste_symbols, name); + PyObject *v = PyDict_GetItemWithError(ste->ste_symbols, name); if (!v) return 0; assert(PyLong_Check(v)); @@ -634,7 +634,7 @@ update_symbols(PyObject *symbols, PyObject *scopes, long scope, flags; assert(PyLong_Check(v)); flags = PyLong_AS_LONG(v); - v_scope = PyDict_GetItem(scopes, name); + v_scope = PyDict_GetItemWithError(scopes, name); assert(v_scope && PyLong_Check(v_scope)); scope = PyLong_AS_LONG(v_scope); flags |= (scope << SCOPE_OFFSET); @@ -1071,9 +1071,12 @@ symtable_add_def_helper(struct symtable *st, PyObject *name, int flag, struct _s /* XXX need to update DEF_GLOBAL for other flags too; perhaps only DEF_FREE_GLOBAL */ val = flag; - if ((o = PyDict_GetItem(st->st_global, mangled))) { + if ((o = PyDict_GetItemWithError(st->st_global, mangled))) { val |= PyLong_AS_LONG(o); } + else if (PyErr_Occurred()) { + goto error; + } o = PyLong_FromLong(val); if (o == NULL) goto error; diff --git a/Python/sysmodule.c b/Python/sysmodule.c index bfcf4e85140a8..749b96455d679 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -68,7 +68,13 @@ sys_get_object_id(PyThreadState *tstate, _Py_Identifier *key) if (sd == NULL) { return NULL; } - return _PyDict_GetItemId(sd, key); + PyObject *exc_type, *exc_value, *exc_tb; + _PyErr_Fetch(tstate, &exc_type, &exc_value, &exc_tb); + PyObject *value = _PyDict_GetItemIdWithError(sd, key); + /* XXX Suppress a new exception if it was raised and restore + * the old one. */ + _PyErr_Restore(tstate, exc_type, exc_value, exc_tb); + return value; } PyObject * @@ -86,26 +92,41 @@ PySys_GetObject(const char *name) if (sd == NULL) { return NULL; } - return PyDict_GetItemString(sd, name); + PyObject *exc_type, *exc_value, *exc_tb; + _PyErr_Fetch(tstate, &exc_type, &exc_value, &exc_tb); + PyObject *value = _PyDict_GetItemStringWithError(sd, name); + /* XXX Suppress a new exception if it was raised and restore + * the old one. */ + _PyErr_Restore(tstate, exc_type, exc_value, exc_tb); + return value; } static int -sys_set_object_id(PyThreadState *tstate, _Py_Identifier *key, PyObject *v) +sys_set_object(PyThreadState *tstate, PyObject *key, PyObject *v) { + if (key == NULL) { + return -1; + } PyObject *sd = tstate->interp->sysdict; if (v == NULL) { - if (_PyDict_GetItemId(sd, key) == NULL) { - return 0; - } - else { - return _PyDict_DelItemId(sd, key); + v = _PyDict_Pop(sd, key, Py_None); + if (v == NULL) { + return -1; } + Py_DECREF(v); + return 0; } else { - return _PyDict_SetItemId(sd, key, v); + return PyDict_SetItem(sd, key, v); } } +static int +sys_set_object_id(PyThreadState *tstate, _Py_Identifier *key, PyObject *v) +{ + return sys_set_object(tstate, _PyUnicode_FromId(key), v); +} + int _PySys_SetObjectId(_Py_Identifier *key, PyObject *v) { @@ -114,27 +135,20 @@ _PySys_SetObjectId(_Py_Identifier *key, PyObject *v) } static int -sys_set_object(PyThreadState *tstate, const char *name, PyObject *v) +sys_set_object_str(PyThreadState *tstate, const char *name, PyObject *v) { - PyObject *sd = tstate->interp->sysdict; - if (v == NULL) { - if (PyDict_GetItemString(sd, name) == NULL) { - return 0; - } - else { - return PyDict_DelItemString(sd, name); - } - } - else { - return PyDict_SetItemString(sd, name, v); - } + PyObject *key = v ? PyUnicode_InternFromString(name) + : PyUnicode_FromString(name); + int r = sys_set_object(tstate, key, v); + Py_XDECREF(key); + return r; } int PySys_SetObject(const char *name, PyObject *v) { PyThreadState *tstate = _PyThreadState_GET(); - return sys_set_object(tstate, name, v); + return sys_set_object_str(tstate, name, v); } @@ -3083,7 +3097,7 @@ PySys_SetArgvEx(int argc, wchar_t **argv, int updatepath) if (av == NULL) { Py_FatalError("no mem for sys.argv"); } - if (sys_set_object(tstate, "argv", av) != 0) { + if (sys_set_object_str(tstate, "argv", av) != 0) { Py_DECREF(av); Py_FatalError("can't assign sys.argv"); } From webhook-mailer at python.org Mon Oct 26 06:48:12 2020 From: webhook-mailer at python.org (serhiy-storchaka) Date: Mon, 26 Oct 2020 10:48:12 -0000 Subject: [Python-checkins] bpo-42152: Use PyDict_Contains and PyDict_SetDefault if appropriate. (GH-22986) Message-ID: https://github.com/python/cpython/commit/b510e101f8b5b31276bf97b921ca9247162881d2 commit: b510e101f8b5b31276bf97b921ca9247162881d2 branch: master author: Serhiy Storchaka committer: serhiy-storchaka date: 2020-10-26T12:47:57+02:00 summary: bpo-42152: Use PyDict_Contains and PyDict_SetDefault if appropriate. (GH-22986) If PyDict_GetItemWithError is only used to check whether the key is in dict, it is better to use PyDict_Contains instead. And if it is used in combination with PyDict_SetItem, PyDict_SetDefault can replace the combination. files: M Modules/_ctypes/_ctypes.c M Modules/_ctypes/callbacks.c M Modules/_pickle.c M Modules/posixmodule.c M Modules/pyexpat.c M Modules/selectmodule.c M Objects/dictobject.c M Objects/typeobject.c M Python/bltinmodule.c M Python/errors.c M Python/import.c diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 0ac48b92bff8b..9be90eb27bdf2 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -492,9 +492,10 @@ StructUnionType_new(PyTypeObject *type, PyObject *args, PyObject *kwds, int isSt return NULL; /* keep this for bw compatibility */ - if (_PyDict_GetItemIdWithError(result->tp_dict, &PyId__abstract_)) + int r = _PyDict_ContainsId(result->tp_dict, &PyId__abstract_); + if (r > 0) return (PyObject *)result; - if (PyErr_Occurred()) { + if (r < 0) { Py_DECREF(result); return NULL; } @@ -4397,15 +4398,13 @@ _init_pos_args(PyObject *self, PyTypeObject *type, } val = PyTuple_GET_ITEM(args, i + index); if (kwds) { - if (PyDict_GetItemWithError(kwds, name)) { - PyErr_Format(PyExc_TypeError, - "duplicate values for field %R", - name); - Py_DECREF(pair); - Py_DECREF(name); - return -1; - } - else if (PyErr_Occurred()) { + res = PyDict_Contains(kwds, name); + if (res != 0) { + if (res > 0) { + PyErr_Format(PyExc_TypeError, + "duplicate values for field %R", + name); + } Py_DECREF(pair); Py_DECREF(name); return -1; diff --git a/Modules/_ctypes/callbacks.c b/Modules/_ctypes/callbacks.c index 2abfa67cdc06b..5cd8577248514 100644 --- a/Modules/_ctypes/callbacks.c +++ b/Modules/_ctypes/callbacks.c @@ -109,8 +109,9 @@ TryAddRef(StgDictObject *dict, CDataObject *obj) IUnknown *punk; _Py_IDENTIFIER(_needs_com_addref_); - if (!_PyDict_GetItemIdWithError((PyObject *)dict, &PyId__needs_com_addref_)) { - if (PyErr_Occurred()) { + int r = _PyDict_ContainsId((PyObject *)dict, &PyId__needs_com_addref_); + if (r <= 0) { + if (r < 0) { PrintError("getting _needs_com_addref_"); } return; diff --git a/Modules/_pickle.c b/Modules/_pickle.c index bddd8f46f0e40..ed8afefe4c74c 100644 --- a/Modules/_pickle.c +++ b/Modules/_pickle.c @@ -2004,26 +2004,21 @@ fast_save_enter(PicklerObject *self, PyObject *obj) self->fast_nesting = -1; return 0; } - if (PyDict_GetItemWithError(self->fast_memo, key)) { - Py_DECREF(key); + int r = PyDict_Contains(self->fast_memo, key); + if (r > 0) { PyErr_Format(PyExc_ValueError, "fast mode: can't pickle cyclic objects " "including object type %.200s at %p", Py_TYPE(obj)->tp_name, obj); - self->fast_nesting = -1; - return 0; } - if (PyErr_Occurred()) { - Py_DECREF(key); - self->fast_nesting = -1; - return 0; + else if (r == 0) { + r = PyDict_SetItem(self->fast_memo, key, Py_None); } - if (PyDict_SetItem(self->fast_memo, key, Py_None) < 0) { - Py_DECREF(key); + Py_DECREF(key); + if (r != 0) { self->fast_nesting = -1; return 0; } - Py_DECREF(key); } return 1; } diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 6ce0bcb9fe8ca..ccd64d63dd0b3 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -1526,13 +1526,11 @@ convertenviron(void) Py_DECREF(d); return NULL; } - if (PyDict_GetItemWithError(d, k) == NULL) { - if (PyErr_Occurred() || PyDict_SetItem(d, k, v) != 0) { - Py_DECREF(v); - Py_DECREF(k); - Py_DECREF(d); - return NULL; - } + if (PyDict_SetDefault(d, k, v) == NULL) { + Py_DECREF(v); + Py_DECREF(k); + Py_DECREF(d); + return NULL; } Py_DECREF(k); Py_DECREF(v); diff --git a/Modules/pyexpat.c b/Modules/pyexpat.c index 12ae66d945bda..73ea51385ee80 100644 --- a/Modules/pyexpat.c +++ b/Modules/pyexpat.c @@ -1614,15 +1614,7 @@ static int init_handler_descrs(void) if (descr == NULL) return -1; - if (PyDict_GetItemWithError(Xmlparsetype.tp_dict, PyDescr_NAME(descr))) { - Py_DECREF(descr); - continue; - } - else if (PyErr_Occurred()) { - Py_DECREF(descr); - return -1; - } - if (PyDict_SetItem(Xmlparsetype.tp_dict, PyDescr_NAME(descr), descr) < 0) { + if (PyDict_SetDefault(Xmlparsetype.tp_dict, PyDescr_NAME(descr), descr) == NULL) { Py_DECREF(descr); return -1; } diff --git a/Modules/selectmodule.c b/Modules/selectmodule.c index fe852f93c37d1..d02e3905f57e5 100644 --- a/Modules/selectmodule.c +++ b/Modules/selectmodule.c @@ -499,11 +499,14 @@ select_poll_modify_impl(pollObject *self, int fd, unsigned short eventmask) key = PyLong_FromLong(fd); if (key == NULL) return NULL; - if (PyDict_GetItemWithError(self->dict, key) == NULL) { - if (!PyErr_Occurred()) { - errno = ENOENT; - PyErr_SetFromErrno(PyExc_OSError); - } + err = PyDict_Contains(self->dict, key); + if (err < 0) { + Py_DECREF(key); + return NULL; + } + if (err == 0) { + errno = ENOENT; + PyErr_SetFromErrno(PyExc_OSError); Py_DECREF(key); return NULL; } diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 00d6ab3be2ffa..faa8696153cb8 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -2544,8 +2544,8 @@ PyDict_MergeFromSeq2(PyObject *d, PyObject *seq2, int override) goto Fail; } } - else if (PyDict_GetItemWithError(d, key) == NULL) { - if (PyErr_Occurred() || PyDict_SetItem(d, key, value) < 0) { + else { + if (PyDict_SetDefault(d, key, value) == NULL) { Py_DECREF(key); Py_DECREF(value); goto Fail; @@ -2660,19 +2660,20 @@ dict_merge(PyObject *a, PyObject *b, int override) Py_INCREF(value); if (override == 1) err = insertdict(mp, key, hash, value); - else if (_PyDict_GetItem_KnownHash(a, key, hash) == NULL) { - if (PyErr_Occurred()) { - Py_DECREF(value); - Py_DECREF(key); - return -1; + else { + err = _PyDict_Contains_KnownHash(a, key, hash); + if (err == 0) { + err = insertdict(mp, key, hash, value); + } + else if (err > 0) { + if (override != 0) { + _PyErr_SetKeyError(key); + Py_DECREF(value); + Py_DECREF(key); + return -1; + } + err = 0; } - err = insertdict(mp, key, hash, value); - } - else if (override != 0) { - _PyErr_SetKeyError(key); - Py_DECREF(value); - Py_DECREF(key); - return -1; } Py_DECREF(value); Py_DECREF(key); @@ -2709,17 +2710,15 @@ dict_merge(PyObject *a, PyObject *b, int override) for (key = PyIter_Next(iter); key; key = PyIter_Next(iter)) { if (override != 1) { - if (PyDict_GetItemWithError(a, key) != NULL) { - if (override != 0) { + status = PyDict_Contains(a, key); + if (status != 0) { + if (status > 0) { + if (override == 0) { + Py_DECREF(key); + continue; + } _PyErr_SetKeyError(key); - Py_DECREF(key); - Py_DECREF(iter); - return -1; } - Py_DECREF(key); - continue; - } - else if (PyErr_Occurred()) { Py_DECREF(key); Py_DECREF(iter); return -1; diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 6626169ae37f6..bd1587ace876f 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -2388,7 +2388,7 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) PyHeapTypeObject *et; PyMemberDef *mp; Py_ssize_t i, nbases, nslots, slotoffset, name_size; - int j, may_add_dict, may_add_weak, add_dict, add_weak; + int j, r, may_add_dict, may_add_weak, add_dict, add_weak; _Py_IDENTIFIER(__qualname__); _Py_IDENTIFIER(__slots__); _Py_IDENTIFIER(__classcell__); @@ -2542,7 +2542,12 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) goto error; } PyList_SET_ITEM(newslots, j, tmp); - if (PyDict_GetItemWithError(dict, tmp)) { + r = PyDict_Contains(dict, tmp); + if (r < 0) { + Py_DECREF(newslots); + goto error; + } + if (r > 0) { /* CPython inserts __qualname__ and __classcell__ (when needed) into the namespace when creating a class. They will be deleted below so won't act as class variables. */ @@ -2555,10 +2560,6 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) goto error; } } - else if (PyErr_Occurred()) { - Py_DECREF(newslots); - goto error; - } j++; } assert(j == nslots - add_dict - add_weak); @@ -2643,10 +2644,11 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) type->tp_dict = dict; /* Set __module__ in the dict */ - if (_PyDict_GetItemIdWithError(dict, &PyId___module__) == NULL) { - if (PyErr_Occurred()) { - goto error; - } + r = _PyDict_ContainsId(dict, &PyId___module__); + if (r < 0) { + goto error; + } + if (r == 0) { tmp = PyEval_GetGlobals(); if (tmp != NULL) { tmp = _PyDict_GetItemIdWithError(tmp, &PyId___name__); @@ -2885,6 +2887,7 @@ PyType_FromModuleAndSpec(PyObject *module, PyType_Spec *spec, PyObject *bases) PyHeapTypeObject *res; PyObject *modname; PyTypeObject *type, *base; + int r; const PyType_Slot *slot; Py_ssize_t nmembers, weaklistoffset, dictoffset, vectorcalloffset; @@ -3052,9 +3055,9 @@ PyType_FromModuleAndSpec(PyObject *module, PyType_Spec *spec, PyObject *bases) PyObject *__doc__ = PyUnicode_FromString(_PyType_DocWithoutSignature(type->tp_name, type->tp_doc)); if (!__doc__) goto fail; - int ret = _PyDict_SetItemId(type->tp_dict, &PyId___doc__, __doc__); + r = _PyDict_SetItemId(type->tp_dict, &PyId___doc__, __doc__); Py_DECREF(__doc__); - if (ret < 0) + if (r < 0) goto fail; } @@ -3070,21 +3073,21 @@ PyType_FromModuleAndSpec(PyObject *module, PyType_Spec *spec, PyObject *bases) } /* Set type.__module__ */ - if (_PyDict_GetItemIdWithError(type->tp_dict, &PyId___module__) == NULL) { - if (PyErr_Occurred()) { - goto fail; - } + r = _PyDict_ContainsId(type->tp_dict, &PyId___module__); + if (r < 0) { + goto fail; + } + if (r == 0) { s = strrchr(spec->name, '.'); if (s != NULL) { - int err; modname = PyUnicode_FromStringAndSize( spec->name, (Py_ssize_t)(s - spec->name)); if (modname == NULL) { goto fail; } - err = _PyDict_SetItemId(type->tp_dict, &PyId___module__, modname); + r = _PyDict_SetItemId(type->tp_dict, &PyId___module__, modname); Py_DECREF(modname); - if (err != 0) + if (r != 0) goto fail; } else { if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1, @@ -5035,26 +5038,16 @@ add_methods(PyTypeObject *type, PyMethodDef *meth) } if (!(meth->ml_flags & METH_COEXIST)) { - if (PyDict_GetItemWithError(dict, name)) { - if (!isdescr) { - Py_DECREF(name); - } - Py_DECREF(descr); - continue; - } - else if (PyErr_Occurred()) { - if (!isdescr) { - Py_DECREF(name); - } - return -1; - } + err = PyDict_SetDefault(dict, name, descr) == NULL; + } + else { + err = PyDict_SetItem(dict, name, descr) < 0; } - err = PyDict_SetItem(dict, name, descr); if (!isdescr) { Py_DECREF(name); } Py_DECREF(descr); - if (err < 0) + if (err) return -1; } return 0; @@ -5070,15 +5063,7 @@ add_members(PyTypeObject *type, PyMemberDef *memb) if (descr == NULL) return -1; - if (PyDict_GetItemWithError(dict, PyDescr_NAME(descr))) { - Py_DECREF(descr); - continue; - } - else if (PyErr_Occurred()) { - Py_DECREF(descr); - return -1; - } - if (PyDict_SetItem(dict, PyDescr_NAME(descr), descr) < 0) { + if (PyDict_SetDefault(dict, PyDescr_NAME(descr), descr) == NULL) { Py_DECREF(descr); return -1; } @@ -5097,15 +5082,7 @@ add_getset(PyTypeObject *type, PyGetSetDef *gsp) if (descr == NULL) return -1; - if (PyDict_GetItemWithError(dict, PyDescr_NAME(descr))) { - Py_DECREF(descr); - continue; - } - else if (PyErr_Occurred()) { - Py_DECREF(descr); - return -1; - } - if (PyDict_SetItem(dict, PyDescr_NAME(descr), descr) < 0) { + if (PyDict_SetDefault(dict, PyDescr_NAME(descr), descr) == NULL) { Py_DECREF(descr); return -1; } @@ -5553,10 +5530,11 @@ PyType_Ready(PyTypeObject *type) /* if the type dictionary doesn't contain a __doc__, set it from the tp_doc slot. */ - if (_PyDict_GetItemIdWithError(type->tp_dict, &PyId___doc__) == NULL) { - if (PyErr_Occurred()) { - goto error; - } + int r = _PyDict_ContainsId(type->tp_dict, &PyId___doc__); + if (r < 0) { + goto error; + } + if (r == 0) { if (type->tp_doc != NULL) { const char *old_doc = _PyType_DocWithoutSignature(type->tp_name, type->tp_doc); @@ -5582,10 +5560,12 @@ PyType_Ready(PyTypeObject *type) This signals that __hash__ is not inherited. */ if (type->tp_hash == NULL) { - if (_PyDict_GetItemIdWithError(type->tp_dict, &PyId___hash__) == NULL) { - if (PyErr_Occurred() || - _PyDict_SetItemId(type->tp_dict, &PyId___hash__, Py_None) < 0) - { + r = _PyDict_ContainsId(type->tp_dict, &PyId___hash__); + if (r < 0) { + goto error; + } + if (r == 0) { + if (_PyDict_SetItemId(type->tp_dict, &PyId___hash__, Py_None) < 0) { goto error; } type->tp_hash = PyObject_HashNotImplemented; @@ -6270,19 +6250,17 @@ add_tp_new_wrapper(PyTypeObject *type) { PyObject *func; - if (_PyDict_GetItemIdWithError(type->tp_dict, &PyId___new__) != NULL) + int r = _PyDict_ContainsId(type->tp_dict, &PyId___new__); + if (r > 0) return 0; - if (PyErr_Occurred()) + if (r < 0) return -1; func = PyCFunction_NewEx(tp_new_methoddef, (PyObject *)type, NULL); if (func == NULL) return -1; - if (_PyDict_SetItemId(type->tp_dict, &PyId___new__, func)) { - Py_DECREF(func); - return -1; - } + r = _PyDict_SetItemId(type->tp_dict, &PyId___new__, func); Py_DECREF(func); - return 0; + return r; } /* Slot wrappers that call the corresponding __foo__ slot. See comments @@ -7795,10 +7773,11 @@ recurse_down_subclasses(PyTypeObject *type, PyObject *name, /* Avoid recursing down into unaffected classes */ dict = subclass->tp_dict; if (dict != NULL && PyDict_Check(dict)) { - if (PyDict_GetItemWithError(dict, name) != NULL) { + int r = PyDict_Contains(dict, name); + if (r > 0) { continue; } - if (PyErr_Occurred()) { + if (r < 0) { return -1; } } @@ -7853,9 +7832,10 @@ add_operators(PyTypeObject *type) ptr = slotptr(type, p->offset); if (!ptr || !*ptr) continue; - if (PyDict_GetItemWithError(dict, p->name_strobj)) + int r = PyDict_Contains(dict, p->name_strobj); + if (r > 0) continue; - if (PyErr_Occurred()) { + if (r < 0) { return -1; } if (*ptr == (void *)PyObject_HashNotImplemented) { diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 89b7fce8f4a9c..1ce55b6ec5a1c 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -924,12 +924,12 @@ builtin_eval_impl(PyObject *module, PyObject *source, PyObject *globals, return NULL; } - if (_PyDict_GetItemIdWithError(globals, &PyId___builtins__) == NULL) { - if (_PyDict_SetItemId(globals, &PyId___builtins__, - PyEval_GetBuiltins()) != 0) - return NULL; + int r = _PyDict_ContainsId(globals, &PyId___builtins__); + if (r == 0) { + r = _PyDict_SetItemId(globals, &PyId___builtins__, + PyEval_GetBuiltins()); } - else if (PyErr_Occurred()) { + if (r < 0) { return NULL; } @@ -1012,12 +1012,12 @@ builtin_exec_impl(PyObject *module, PyObject *source, PyObject *globals, Py_TYPE(locals)->tp_name); return NULL; } - if (_PyDict_GetItemIdWithError(globals, &PyId___builtins__) == NULL) { - if (_PyDict_SetItemId(globals, &PyId___builtins__, - PyEval_GetBuiltins()) != 0) - return NULL; + int r = _PyDict_ContainsId(globals, &PyId___builtins__); + if (r == 0) { + r = _PyDict_SetItemId(globals, &PyId___builtins__, + PyEval_GetBuiltins()); } - else if (PyErr_Occurred()) { + if (r < 0) { return NULL; } diff --git a/Python/errors.c b/Python/errors.c index 02cf47992b695..f80ae21fdde7c 100644 --- a/Python/errors.c +++ b/Python/errors.c @@ -1098,10 +1098,11 @@ PyErr_NewException(const char *name, PyObject *base, PyObject *dict) goto failure; } - if (_PyDict_GetItemIdWithError(dict, &PyId___module__) == NULL) { - if (_PyErr_Occurred(tstate)) { - goto failure; - } + int r = _PyDict_ContainsId(dict, &PyId___module__); + if (r < 0) { + goto failure; + } + if (r == 0) { modulename = PyUnicode_FromStringAndSize(name, (Py_ssize_t)(dot-name)); if (modulename == NULL) diff --git a/Python/import.c b/Python/import.c index 26b80f320c343..b79bda058db82 100644 --- a/Python/import.c +++ b/Python/import.c @@ -1018,14 +1018,14 @@ module_dict_for_exec(PyThreadState *tstate, PyObject *name) /* If the module is being reloaded, we get the old module back and re-use its dict to exec the new code. */ d = PyModule_GetDict(m); - if (_PyDict_GetItemIdWithError(d, &PyId___builtins__) == NULL) { - if (_PyErr_Occurred(tstate) || - _PyDict_SetItemId(d, &PyId___builtins__, - PyEval_GetBuiltins()) != 0) - { - remove_module(tstate, name); - return NULL; - } + int r = _PyDict_ContainsId(d, &PyId___builtins__); + if (r == 0) { + r = _PyDict_SetItemId(d, &PyId___builtins__, + PyEval_GetBuiltins()); + } + if (r < 0) { + remove_module(tstate, name); + return NULL; } return d; /* Return a borrowed reference. */ @@ -1660,10 +1660,14 @@ resolve_name(PyThreadState *tstate, PyObject *name, PyObject *globals, int level goto error; } - if (_PyDict_GetItemIdWithError(globals, &PyId___path__) == NULL) { + int haspath = _PyDict_ContainsId(globals, &PyId___path__); + if (haspath < 0) { + goto error; + } + if (!haspath) { Py_ssize_t dot; - if (_PyErr_Occurred(tstate) || PyUnicode_READY(package) < 0) { + if (PyUnicode_READY(package) < 0) { goto error; } From webhook-mailer at python.org Mon Oct 26 11:43:56 2020 From: webhook-mailer at python.org (vstinner) Date: Mon, 26 Oct 2020 15:43:56 -0000 Subject: [Python-checkins] bpo-1635741: _PyUnicode_Name_CAPI moves to internal C API (GH-22713) Message-ID: https://github.com/python/cpython/commit/47e1afd2a1793b5818a16c41307a4ce976331649 commit: 47e1afd2a1793b5818a16c41307a4ce976331649 branch: master author: Victor Stinner committer: vstinner date: 2020-10-26T16:43:47+01:00 summary: bpo-1635741: _PyUnicode_Name_CAPI moves to internal C API (GH-22713) The private _PyUnicode_Name_CAPI structure of the PyCapsule API unicodedata.ucnhash_CAPI moves to the internal C API. Moreover, the structure gets a new state member which must be passed to the getcode() and getname() functions. * Move Include/ucnhash.h to Include/internal/pycore_ucnhash.h * unicodedata module is now built with Py_BUILD_CORE_MODULE. * unicodedata: move hashAPI variable into unicodedata_module_state. files: A Include/internal/pycore_ucnhash.h A Misc/NEWS.d/next/C API/2020-10-16-10-47-17.bpo-1635741.e3BcPM.rst D Include/ucnhash.h M Doc/whatsnew/3.10.rst M Makefile.pre.in M Modules/Setup M Modules/unicodedata.c M Objects/unicodeobject.c M PCbuild/pythoncore.vcxproj M PCbuild/pythoncore.vcxproj.filters M Python/codecs.c M setup.py diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index 6206c949cc59b..581d3a57e8457 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -407,6 +407,12 @@ Porting to Python 3.10 Unicode object without initial data. (Contributed by Inada Naoki in :issue:`36346`.) +* The private ``_PyUnicode_Name_CAPI`` structure of the PyCapsule API + ``unicodedata.ucnhash_CAPI`` moves to the internal C API. Moreover, + the structure gets a new ``state`` member which must be passed to the + ``getcode()`` and ``getname()`` functions. + (Contributed by Victor Stinner in :issue:`1635741`.) + Deprecated ---------- diff --git a/Include/ucnhash.h b/Include/internal/pycore_ucnhash.h similarity index 60% rename from Include/ucnhash.h rename to Include/internal/pycore_ucnhash.h index 45362e997dfa1..380b9415d4280 100644 --- a/Include/ucnhash.h +++ b/Include/internal/pycore_ucnhash.h @@ -1,11 +1,14 @@ /* Unicode name database interface */ -#ifndef Py_LIMITED_API -#ifndef Py_UCNHASH_H -#define Py_UCNHASH_H +#ifndef Py_INTERNAL_UCNHASH_H +#define Py_INTERNAL_UCNHASH_H #ifdef __cplusplus extern "C" { #endif +#ifndef Py_BUILD_CORE +# error "this header requires Py_BUILD_CORE define" +#endif + /* revised ucnhash CAPI interface (exported through a "wrapper") */ #define PyUnicodeData_CAPSULE_NAME "unicodedata.ucnhash_CAPI" @@ -15,16 +18,22 @@ typedef struct { /* Size of this struct */ int size; + // state which must be passed as the first parameter to getname() + // and getcode() + void *state; + /* Get name for a given character code. Returns non-zero if success, zero if not. Does not set Python exceptions. If self is NULL, data come from the default version of the database. If it is not NULL, it should be a unicodedata.ucd_X_Y_Z object */ - int (*getname)(PyObject *self, Py_UCS4 code, char* buffer, int buflen, + int (*getname)(void *state, PyObject *self, Py_UCS4 code, + char* buffer, int buflen, int with_alias_and_seq); /* Get character code for a given name. Same error handling as for getname. */ - int (*getcode)(PyObject *self, const char* name, int namelen, Py_UCS4* code, + int (*getcode)(void *state, PyObject *self, + const char* name, int namelen, Py_UCS4* code, int with_named_seq); } _PyUnicode_Name_CAPI; @@ -32,5 +41,4 @@ typedef struct { #ifdef __cplusplus } #endif -#endif /* !Py_UCNHASH_H */ -#endif /* !Py_LIMITED_API */ +#endif /* !Py_INTERNAL_UCNHASH_H */ diff --git a/Makefile.pre.in b/Makefile.pre.in index 921bd08ea505d..fe226ce45d8e9 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1065,7 +1065,6 @@ PYTHON_HEADERS= \ $(srcdir)/Include/traceback.h \ $(srcdir)/Include/tracemalloc.h \ $(srcdir)/Include/tupleobject.h \ - $(srcdir)/Include/ucnhash.h \ $(srcdir)/Include/unicodeobject.h \ $(srcdir)/Include/warnings.h \ $(srcdir)/Include/weakrefobject.h \ @@ -1129,6 +1128,7 @@ PYTHON_HEADERS= \ $(srcdir)/Include/internal/pycore_sysmodule.h \ $(srcdir)/Include/internal/pycore_traceback.h \ $(srcdir)/Include/internal/pycore_tuple.h \ + $(srcdir)/Include/internal/pycore_ucnhash.h \ $(srcdir)/Include/internal/pycore_unionobject.h \ $(srcdir)/Include/internal/pycore_warnings.h \ $(DTRACE_HEADERS) diff --git a/Misc/NEWS.d/next/C API/2020-10-16-10-47-17.bpo-1635741.e3BcPM.rst b/Misc/NEWS.d/next/C API/2020-10-16-10-47-17.bpo-1635741.e3BcPM.rst new file mode 100644 index 0000000000000..5272ad577265a --- /dev/null +++ b/Misc/NEWS.d/next/C API/2020-10-16-10-47-17.bpo-1635741.e3BcPM.rst @@ -0,0 +1,4 @@ +The private ``_PyUnicode_Name_CAPI`` structure of the PyCapsule API +``unicodedata.ucnhash_CAPI`` moves to the internal C API. Moreover, the +structure gets a new ``state`` member which must be passed to the +``getcode()`` and ``getname()`` functions. Patch by Victor Stinner. diff --git a/Modules/Setup b/Modules/Setup index 87f3a7cb43a02..6f9bb813cef27 100644 --- a/Modules/Setup +++ b/Modules/Setup @@ -185,7 +185,7 @@ _symtable symtablemodule.c #_json -I$(srcdir)/Include/internal -DPy_BUILD_CORE_BUILTIN _json.c # _json speedups #_statistics _statisticsmodule.c # statistics accelerator -#unicodedata unicodedata.c # static Unicode character database +#unicodedata unicodedata.c -DPy_BUILD_CORE_BUILTIN # static Unicode character database # Modules with some UNIX dependencies -- on by default: diff --git a/Modules/unicodedata.c b/Modules/unicodedata.c index 941fd2faa742a..bfd8ab503c8cc 100644 --- a/Modules/unicodedata.c +++ b/Modules/unicodedata.c @@ -16,7 +16,7 @@ #define PY_SSIZE_T_CLEAN #include "Python.h" -#include "ucnhash.h" +#include "pycore_ucnhash.h" // _PyUnicode_Name_CAPI #include "structmember.h" // PyMemberDef #include @@ -97,6 +97,8 @@ typedef struct { // Borrowed reference to &UCD_Type. It is used to prepare the code // to convert the UCD_Type static type to a heap type. PyTypeObject *ucd_type; + + _PyUnicode_Name_CAPI capi; } unicodedata_module_state; // bpo-1635741: Temporary global state until the unicodedata module @@ -1180,10 +1182,11 @@ _getucname(unicodedata_module_state *state, PyObject *self, } static int -capi_getucname(PyObject *self, Py_UCS4 code, char* buffer, int buflen, +capi_getucname(void *state_raw, PyObject *self, Py_UCS4 code, + char* buffer, int buflen, int with_alias_and_seq) { - unicodedata_module_state *state = &global_module_state; + unicodedata_module_state *state = (unicodedata_module_state *)state_raw; return _getucname(state, self, code, buffer, buflen, with_alias_and_seq); } @@ -1323,21 +1326,15 @@ _getcode(unicodedata_module_state *state, PyObject* self, } static int -capi_getcode(PyObject* self, const char* name, int namelen, Py_UCS4* code, +capi_getcode(void *state_raw, PyObject* self, + const char* name, int namelen, Py_UCS4* code, int with_named_seq) { - unicodedata_module_state *state = &global_module_state; + unicodedata_module_state *state = (unicodedata_module_state *)state_raw; return _getcode(state, self, name, namelen, code, with_named_seq); } -static const _PyUnicode_Name_CAPI hashAPI = -{ - sizeof(_PyUnicode_Name_CAPI), - capi_getucname, - capi_getcode -}; - /* -------------------------------------------------------------------- */ /* Python bindings */ @@ -1510,6 +1507,11 @@ PyInit_unicodedata(void) PyObject *m, *v; unicodedata_module_state *state = &global_module_state; + state->capi.size = sizeof(_PyUnicode_Name_CAPI); + state->capi.state = state; + state->capi.getname = capi_getucname; + state->capi.getcode = capi_getcode; + Py_SET_TYPE(&UCD_Type, &PyType_Type); state->ucd_type = &UCD_Type; @@ -1528,7 +1530,7 @@ PyInit_unicodedata(void) PyModule_AddObject(m, "ucd_3_2_0", v); /* Export C API */ - v = PyCapsule_New((void *)&hashAPI, PyUnicodeData_CAPSULE_NAME, NULL); + v = PyCapsule_New((void *)&state->capi, PyUnicodeData_CAPSULE_NAME, NULL); if (v != NULL) PyModule_AddObject(m, "ucnhash_CAPI", v); return m; diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index f963deb0201a4..ba48d35aa40b1 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -40,16 +40,16 @@ OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #define PY_SSIZE_T_CLEAN #include "Python.h" -#include "pycore_abstract.h" // _PyIndex_Check() -#include "pycore_bytes_methods.h" // _Py_bytes_lower() -#include "pycore_initconfig.h" // _PyStatus_OK() -#include "pycore_interp.h" // PyInterpreterState.fs_codec -#include "pycore_object.h" // _PyObject_GC_TRACK() -#include "pycore_pathconfig.h" // _Py_DumpPathConfig() -#include "pycore_pylifecycle.h" // _Py_SetFileSystemEncoding() -#include "pycore_pystate.h" // _PyInterpreterState_GET() -#include "ucnhash.h" // _PyUnicode_Name_CAPI -#include "stringlib/eq.h" // unicode_eq() +#include "pycore_abstract.h" // _PyIndex_Check() +#include "pycore_bytes_methods.h" // _Py_bytes_lower() +#include "pycore_initconfig.h" // _PyStatus_OK() +#include "pycore_interp.h" // PyInterpreterState.fs_codec +#include "pycore_object.h" // _PyObject_GC_TRACK() +#include "pycore_pathconfig.h" // _Py_DumpPathConfig() +#include "pycore_pylifecycle.h" // _Py_SetFileSystemEncoding() +#include "pycore_pystate.h" // _PyInterpreterState_GET() +#include "pycore_ucnhash.h" // _PyUnicode_Name_CAPI +#include "stringlib/eq.h" // unicode_eq() #ifdef MS_WINDOWS #include @@ -6344,7 +6344,7 @@ PyUnicode_AsUTF16String(PyObject *unicode) /* --- Unicode Escape Codec ----------------------------------------------- */ -static _PyUnicode_Name_CAPI *ucnhash_CAPI = NULL; +static _PyUnicode_Name_CAPI *ucnhash_capi = NULL; PyObject * _PyUnicode_DecodeUnicodeEscape(const char *s, @@ -6497,11 +6497,11 @@ _PyUnicode_DecodeUnicodeEscape(const char *s, /* \N{name} */ case 'N': - if (ucnhash_CAPI == NULL) { + if (ucnhash_capi == NULL) { /* load the unicode data module */ - ucnhash_CAPI = (_PyUnicode_Name_CAPI *)PyCapsule_Import( + ucnhash_capi = (_PyUnicode_Name_CAPI *)PyCapsule_Import( PyUnicodeData_CAPSULE_NAME, 1); - if (ucnhash_CAPI == NULL) { + if (ucnhash_capi == NULL) { PyErr_SetString( PyExc_UnicodeError, "\\N escapes not supported (can't load unicodedata module)" @@ -6523,7 +6523,8 @@ _PyUnicode_DecodeUnicodeEscape(const char *s, s++; ch = 0xffffffff; /* in case 'getcode' messes up */ if (namelen <= INT_MAX && - ucnhash_CAPI->getcode(NULL, start, (int)namelen, + ucnhash_capi->getcode(ucnhash_capi->state, NULL, + start, (int)namelen, &ch, 0)) { assert(ch <= MAX_UNICODE); WRITE_CHAR(ch); diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index 266a193c1e86a..600f33b2c6e3b 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -196,6 +196,7 @@ + @@ -252,7 +253,6 @@ - diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters index 22d9b79157698..75b91d8ed87f9 100644 --- a/PCbuild/pythoncore.vcxproj.filters +++ b/PCbuild/pythoncore.vcxproj.filters @@ -273,9 +273,6 @@ Include - - Include - Include @@ -573,6 +570,9 @@ Include\internal + + Include\internal + Include\internal diff --git a/Python/codecs.c b/Python/codecs.c index ade14187204f0..62d1f3f3ac0d3 100644 --- a/Python/codecs.c +++ b/Python/codecs.c @@ -11,7 +11,7 @@ Copyright (c) Corporation for National Research Initiatives. #include "Python.h" #include "pycore_interp.h" // PyInterpreterState.codec_search_path #include "pycore_pystate.h" // _PyInterpreterState_GET() -#include "ucnhash.h" +#include "pycore_ucnhash.h" // _PyUnicode_Name_CAPI #include const char *Py_hexdigits = "0123456789abcdef"; @@ -954,7 +954,7 @@ PyObject *PyCodec_BackslashReplaceErrors(PyObject *exc) return Py_BuildValue("(Nn)", res, end); } -static _PyUnicode_Name_CAPI *ucnhash_CAPI = NULL; +static _PyUnicode_Name_CAPI *ucnhash_capi = NULL; PyObject *PyCodec_NameReplaceErrors(PyObject *exc) { @@ -976,17 +976,19 @@ PyObject *PyCodec_NameReplaceErrors(PyObject *exc) return NULL; if (!(object = PyUnicodeEncodeError_GetObject(exc))) return NULL; - if (!ucnhash_CAPI) { + if (!ucnhash_capi) { /* load the unicode data module */ - ucnhash_CAPI = (_PyUnicode_Name_CAPI *)PyCapsule_Import( + ucnhash_capi = (_PyUnicode_Name_CAPI *)PyCapsule_Import( PyUnicodeData_CAPSULE_NAME, 1); - if (!ucnhash_CAPI) + if (!ucnhash_capi) { return NULL; + } } for (i = start, ressize = 0; i < end; ++i) { /* object is guaranteed to be "ready" */ c = PyUnicode_READ_CHAR(object, i); - if (ucnhash_CAPI->getname(NULL, c, buffer, sizeof(buffer), 1)) { + if (ucnhash_capi->getname(ucnhash_capi->state, NULL, + c, buffer, sizeof(buffer), 1)) { replsize = 1+1+1+(int)strlen(buffer)+1; } else if (c >= 0x10000) { @@ -1009,7 +1011,8 @@ PyObject *PyCodec_NameReplaceErrors(PyObject *exc) i < end; ++i) { c = PyUnicode_READ_CHAR(object, i); *outp++ = '\\'; - if (ucnhash_CAPI->getname(NULL, c, buffer, sizeof(buffer), 1)) { + if (ucnhash_capi->getname(ucnhash_capi->state, NULL, + c, buffer, sizeof(buffer), 1)) { *outp++ = 'N'; *outp++ = '{'; strcpy((char *)outp, buffer); diff --git a/setup.py b/setup.py index d3fd7bca6438a..8a4abe5a648fd 100644 --- a/setup.py +++ b/setup.py @@ -878,7 +878,8 @@ def detect_simple_extensions(self): self.add(Extension('_lsprof', ['_lsprof.c', 'rotatingtree.c'])) # static Unicode character database self.add(Extension('unicodedata', ['unicodedata.c'], - depends=['unicodedata_db.h', 'unicodename_db.h'])) + depends=['unicodedata_db.h', 'unicodename_db.h'], + extra_compile_args=['-DPy_BUILD_CORE_MODULE'])) # _opcode module self.add(Extension('_opcode', ['_opcode.c'])) # asyncio speedups From webhook-mailer at python.org Mon Oct 26 12:28:34 2020 From: webhook-mailer at python.org (lisroach) Date: Mon, 26 Oct 2020 16:28:34 -0000 Subject: [Python-checkins] bpo-39101: Fixes BaseException hang in IsolatedAsyncioTestCase. (GH-22654) Message-ID: https://github.com/python/cpython/commit/8374d2ee1589791be8892b00f4bbf8121dde24bd commit: 8374d2ee1589791be8892b00f4bbf8121dde24bd branch: master author: Lisa Roach committer: lisroach date: 2020-10-26T09:28:17-07:00 summary: bpo-39101: Fixes BaseException hang in IsolatedAsyncioTestCase. (GH-22654) files: A Misc/NEWS.d/next/Library/2020-10-11-21-43-03.bpo-39101.-I49Pm.rst M Lib/unittest/async_case.py M Lib/unittest/test/test_async_case.py diff --git a/Lib/unittest/async_case.py b/Lib/unittest/async_case.py index 1bc1312c8c2ee..520213c372755 100644 --- a/Lib/unittest/async_case.py +++ b/Lib/unittest/async_case.py @@ -102,9 +102,9 @@ async def _asyncioLoopRunner(self, fut): ret = await awaitable if not fut.cancelled(): fut.set_result(ret) - except asyncio.CancelledError: + except (SystemExit, KeyboardInterrupt): raise - except Exception as ex: + except (BaseException, asyncio.CancelledError) as ex: if not fut.cancelled(): fut.set_exception(ex) diff --git a/Lib/unittest/test/test_async_case.py b/Lib/unittest/test/test_async_case.py index 2db441da202a0..d01864b6936ca 100644 --- a/Lib/unittest/test/test_async_case.py +++ b/Lib/unittest/test/test_async_case.py @@ -190,6 +190,33 @@ async def on_async_cleanup(self, val): 'async_cleanup 2', 'sync_cleanup 1']) + def test_base_exception_from_async_method(self): + events = [] + class Test(unittest.IsolatedAsyncioTestCase): + async def test_base(self): + events.append("test_base") + raise BaseException() + events.append("not it") + + async def test_no_err(self): + events.append("test_no_err") + + async def test_cancel(self): + raise asyncio.CancelledError() + + test = Test("test_base") + output = test.run() + self.assertFalse(output.wasSuccessful()) + + test = Test("test_no_err") + test.run() + self.assertEqual(events, ['test_base', 'test_no_err']) + + test = Test("test_cancel") + output = test.run() + self.assertFalse(output.wasSuccessful()) + + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Library/2020-10-11-21-43-03.bpo-39101.-I49Pm.rst b/Misc/NEWS.d/next/Library/2020-10-11-21-43-03.bpo-39101.-I49Pm.rst new file mode 100644 index 0000000000000..a571e8343cde1 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-10-11-21-43-03.bpo-39101.-I49Pm.rst @@ -0,0 +1 @@ +Fixed tests using IsolatedAsyncioTestCase from hanging on BaseExceptions. \ No newline at end of file From webhook-mailer at python.org Mon Oct 26 14:19:50 2020 From: webhook-mailer at python.org (vstinner) Date: Mon, 26 Oct 2020 18:19:50 -0000 Subject: [Python-checkins] bpo-42157: unicodedata avoids references to UCD_Type (GH-22990) Message-ID: https://github.com/python/cpython/commit/920cb647ba23feab7987d0dac1bd63bfc2ffc4c0 commit: 920cb647ba23feab7987d0dac1bd63bfc2ffc4c0 branch: master author: Victor Stinner committer: vstinner date: 2020-10-26T19:19:36+01:00 summary: bpo-42157: unicodedata avoids references to UCD_Type (GH-22990) * UCD_Check() uses PyModule_Check() * Simplify the internal _PyUnicode_Name_CAPI structure: * Remove size and state members * Remove state and self parameters of getcode() and getname() functions * Remove global_module_state files: A Misc/NEWS.d/next/C API/2020-10-16-10-47-17.bpo-42157.e3BcPM.rst D Misc/NEWS.d/next/C API/2020-10-16-10-47-17.bpo-1635741.e3BcPM.rst M Doc/whatsnew/3.10.rst M Include/internal/pycore_ucnhash.h M Modules/unicodedata.c M Objects/unicodeobject.c M Python/codecs.c diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index 581d3a57e8457..2ef2b5d19e585 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -408,10 +408,8 @@ Porting to Python 3.10 (Contributed by Inada Naoki in :issue:`36346`.) * The private ``_PyUnicode_Name_CAPI`` structure of the PyCapsule API - ``unicodedata.ucnhash_CAPI`` moves to the internal C API. Moreover, - the structure gets a new ``state`` member which must be passed to the - ``getcode()`` and ``getname()`` functions. - (Contributed by Victor Stinner in :issue:`1635741`.) + ``unicodedata.ucnhash_CAPI`` moves to the internal C API. + (Contributed by Victor Stinner in :issue:`42157`.) Deprecated ---------- diff --git a/Include/internal/pycore_ucnhash.h b/Include/internal/pycore_ucnhash.h index 380b9415d4280..5e7c035f81d2a 100644 --- a/Include/internal/pycore_ucnhash.h +++ b/Include/internal/pycore_ucnhash.h @@ -15,25 +15,15 @@ extern "C" { typedef struct { - /* Size of this struct */ - int size; - - // state which must be passed as the first parameter to getname() - // and getcode() - void *state; - - /* Get name for a given character code. Returns non-zero if - success, zero if not. Does not set Python exceptions. - If self is NULL, data come from the default version of the database. - If it is not NULL, it should be a unicodedata.ucd_X_Y_Z object */ - int (*getname)(void *state, PyObject *self, Py_UCS4 code, - char* buffer, int buflen, + /* Get name for a given character code. + Returns non-zero if success, zero if not. + Does not set Python exceptions. */ + int (*getname)(Py_UCS4 code, char* buffer, int buflen, int with_alias_and_seq); - /* Get character code for a given name. Same error handling - as for getname. */ - int (*getcode)(void *state, PyObject *self, - const char* name, int namelen, Py_UCS4* code, + /* Get character code for a given name. + Same error handling as for getname(). */ + int (*getcode)(const char* name, int namelen, Py_UCS4* code, int with_named_seq); } _PyUnicode_Name_CAPI; diff --git a/Misc/NEWS.d/next/C API/2020-10-16-10-47-17.bpo-1635741.e3BcPM.rst b/Misc/NEWS.d/next/C API/2020-10-16-10-47-17.bpo-1635741.e3BcPM.rst deleted file mode 100644 index 5272ad577265a..0000000000000 --- a/Misc/NEWS.d/next/C API/2020-10-16-10-47-17.bpo-1635741.e3BcPM.rst +++ /dev/null @@ -1,4 +0,0 @@ -The private ``_PyUnicode_Name_CAPI`` structure of the PyCapsule API -``unicodedata.ucnhash_CAPI`` moves to the internal C API. Moreover, the -structure gets a new ``state`` member which must be passed to the -``getcode()`` and ``getname()`` functions. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/C API/2020-10-16-10-47-17.bpo-42157.e3BcPM.rst b/Misc/NEWS.d/next/C API/2020-10-16-10-47-17.bpo-42157.e3BcPM.rst new file mode 100644 index 0000000000000..1f05186d9e0ef --- /dev/null +++ b/Misc/NEWS.d/next/C API/2020-10-16-10-47-17.bpo-42157.e3BcPM.rst @@ -0,0 +1,3 @@ +The private ``_PyUnicode_Name_CAPI`` structure of the PyCapsule API +``unicodedata.ucnhash_CAPI`` moves to the internal C API. +Patch by Victor Stinner. diff --git a/Modules/unicodedata.c b/Modules/unicodedata.c index bfd8ab503c8cc..6c802ba116ffe 100644 --- a/Modules/unicodedata.c +++ b/Modules/unicodedata.c @@ -93,29 +93,19 @@ static PyMemberDef DB_members[] = { /* forward declaration */ static PyTypeObject UCD_Type; -typedef struct { - // Borrowed reference to &UCD_Type. It is used to prepare the code - // to convert the UCD_Type static type to a heap type. - PyTypeObject *ucd_type; - - _PyUnicode_Name_CAPI capi; -} unicodedata_module_state; - -// bpo-1635741: Temporary global state until the unicodedata module -// gets a real module state. -static unicodedata_module_state global_module_state; - -// Check if self is an instance of ucd_type. -// Return 0 if self is NULL (when the PyCapsule C API is used). -#define UCD_Check(self, ucd_type) (self != NULL && Py_IS_TYPE(self, ucd_type)) +// Check if self is an unicodedata.UCD instance. +// If self is NULL (when the PyCapsule C API is used), return 0. +// PyModule_Check() is used to avoid having to retrieve the ucd_type. +// See unicodedata_functions comment to the rationale of this macro. +#define UCD_Check(self) (self != NULL && !PyModule_Check(self)) static PyObject* -new_previous_version(unicodedata_module_state *state, +new_previous_version(PyTypeObject *ucd_type, const char*name, const change_record* (*getrecord)(Py_UCS4), Py_UCS4 (*normalization)(Py_UCS4)) { PreviousDBVersion *self; - self = PyObject_New(PreviousDBVersion, state->ucd_type); + self = PyObject_New(PreviousDBVersion, ucd_type); if (self == NULL) return NULL; self->name = name; @@ -147,12 +137,11 @@ unicodedata_UCD_decimal_impl(PyObject *self, int chr, PyObject *default_value) /*[clinic end generated code: output=be23376e1a185231 input=933f8107993f23d0]*/ { - unicodedata_module_state *state = &global_module_state; int have_old = 0; long rc; Py_UCS4 c = (Py_UCS4)chr; - if (UCD_Check(self, state->ucd_type)) { + if (UCD_Check(self)) { const change_record *old = get_old_record(self, c); if (old->category_changed == 0) { /* unassigned */ @@ -236,12 +225,11 @@ unicodedata_UCD_numeric_impl(PyObject *self, int chr, PyObject *default_value) /*[clinic end generated code: output=53ce281fe85b10c4 input=fdf5871a5542893c]*/ { - unicodedata_module_state *state = &global_module_state; int have_old = 0; double rc; Py_UCS4 c = (Py_UCS4)chr; - if (UCD_Check(self, state->ucd_type)) { + if (UCD_Check(self)) { const change_record *old = get_old_record(self, c); if (old->category_changed == 0) { /* unassigned */ @@ -283,11 +271,10 @@ static PyObject * unicodedata_UCD_category_impl(PyObject *self, int chr) /*[clinic end generated code: output=8571539ee2e6783a input=27d6f3d85050bc06]*/ { - unicodedata_module_state *state = &global_module_state; int index; Py_UCS4 c = (Py_UCS4)chr; index = (int) _getrecord_ex(c)->category; - if (UCD_Check(self, state->ucd_type)) { + if (UCD_Check(self)) { const change_record *old = get_old_record(self, c); if (old->category_changed != 0xFF) index = old->category_changed; @@ -311,11 +298,10 @@ static PyObject * unicodedata_UCD_bidirectional_impl(PyObject *self, int chr) /*[clinic end generated code: output=d36310ce2039bb92 input=b3d8f42cebfcf475]*/ { - unicodedata_module_state *state = &global_module_state; int index; Py_UCS4 c = (Py_UCS4)chr; index = (int) _getrecord_ex(c)->bidirectional; - if (UCD_Check(self, state->ucd_type)) { + if (UCD_Check(self)) { const change_record *old = get_old_record(self, c); if (old->category_changed == 0) index = 0; /* unassigned */ @@ -341,11 +327,10 @@ static int unicodedata_UCD_combining_impl(PyObject *self, int chr) /*[clinic end generated code: output=cad056d0cb6a5920 input=9f2d6b2a95d0a22a]*/ { - unicodedata_module_state *state = &global_module_state; int index; Py_UCS4 c = (Py_UCS4)chr; index = (int) _getrecord_ex(c)->combining; - if (UCD_Check(self, state->ucd_type)) { + if (UCD_Check(self)) { const change_record *old = get_old_record(self, c); if (old->category_changed == 0) index = 0; /* unassigned */ @@ -370,11 +355,10 @@ static int unicodedata_UCD_mirrored_impl(PyObject *self, int chr) /*[clinic end generated code: output=2532dbf8121b50e6 input=5dd400d351ae6f3b]*/ { - unicodedata_module_state *state = &global_module_state; int index; Py_UCS4 c = (Py_UCS4)chr; index = (int) _getrecord_ex(c)->mirrored; - if (UCD_Check(self, state->ucd_type)) { + if (UCD_Check(self)) { const change_record *old = get_old_record(self, c); if (old->category_changed == 0) index = 0; /* unassigned */ @@ -398,11 +382,10 @@ static PyObject * unicodedata_UCD_east_asian_width_impl(PyObject *self, int chr) /*[clinic end generated code: output=484e8537d9ee8197 input=c4854798aab026e0]*/ { - unicodedata_module_state *state = &global_module_state; int index; Py_UCS4 c = (Py_UCS4)chr; index = (int) _getrecord_ex(c)->east_asian_width; - if (UCD_Check(self, state->ucd_type)) { + if (UCD_Check(self)) { const change_record *old = get_old_record(self, c); if (old->category_changed == 0) index = 0; /* unassigned */ @@ -428,7 +411,6 @@ static PyObject * unicodedata_UCD_decomposition_impl(PyObject *self, int chr) /*[clinic end generated code: output=7d699f3ec7565d27 input=e4c12459ad68507b]*/ { - unicodedata_module_state *state = &global_module_state; char decomp[256]; int code, index, count; size_t i; @@ -437,7 +419,7 @@ unicodedata_UCD_decomposition_impl(PyObject *self, int chr) code = (int)c; - if (UCD_Check(self, state->ucd_type)) { + if (UCD_Check(self)) { const change_record *old = get_old_record(self, c); if (old->category_changed == 0) return PyUnicode_FromString(""); /* unassigned */ @@ -480,13 +462,14 @@ unicodedata_UCD_decomposition_impl(PyObject *self, int chr) } static void -get_decomp_record(unicodedata_module_state *state, PyObject *self, - Py_UCS4 code, int *index, int *prefix, int *count) +get_decomp_record(PyObject *self, Py_UCS4 code, + int *index, int *prefix, int *count) { if (code >= 0x110000) { *index = 0; - } else if (UCD_Check(self, state->ucd_type) && - get_old_record(self, code)->category_changed==0) { + } + else if (UCD_Check(self) + && get_old_record(self, code)->category_changed==0) { /* unassigned in old version */ *index = 0; } @@ -515,8 +498,7 @@ get_decomp_record(unicodedata_module_state *state, PyObject *self, #define SCount (LCount*NCount) static PyObject* -nfd_nfkd(unicodedata_module_state *state, PyObject *self, - PyObject *input, int k) +nfd_nfkd(PyObject *self, PyObject *input, int k) { PyObject *result; Py_UCS4 *output; @@ -584,7 +566,7 @@ nfd_nfkd(unicodedata_module_state *state, PyObject *self, continue; } /* normalization changes */ - if (UCD_Check(self, state->ucd_type)) { + if (UCD_Check(self)) { Py_UCS4 value = ((PreviousDBVersion*)self)->normalization(code); if (value != 0) { stack[stackptr++] = value; @@ -593,7 +575,7 @@ nfd_nfkd(unicodedata_module_state *state, PyObject *self, } /* Other decompositions. */ - get_decomp_record(state, self, code, &index, &prefix, &count); + get_decomp_record(self, code, &index, &prefix, &count); /* Copy character if it is not decomposable, or has a compatibility decomposition, but we do NFD. */ @@ -665,7 +647,7 @@ find_nfc_index(const struct reindex* nfc, Py_UCS4 code) } static PyObject* -nfc_nfkc(unicodedata_module_state *state, PyObject *self, PyObject *input, int k) +nfc_nfkc(PyObject *self, PyObject *input, int k) { PyObject *result; int kind; @@ -677,7 +659,7 @@ nfc_nfkc(unicodedata_module_state *state, PyObject *self, PyObject *input, int k Py_ssize_t skipped[20]; int cskipped = 0; - result = nfd_nfkd(state, self, input, k); + result = nfd_nfkd(self, input, k); if (!result) return NULL; /* result will be "ready". */ @@ -820,13 +802,13 @@ typedef enum {YES = 0, MAYBE = 1, NO = 2} QuickcheckResult; * https://www.unicode.org/reports/tr15/#Detecting_Normalization_Forms */ static QuickcheckResult -is_normalized_quickcheck(unicodedata_module_state *state, PyObject *self, - PyObject *input, bool nfc, bool k, bool yes_only) +is_normalized_quickcheck(PyObject *self, PyObject *input, bool nfc, bool k, + bool yes_only) { - /* An older version of the database is requested, quickchecks must be - disabled. */ - if (UCD_Check(self, state->ucd_type)) + /* UCD 3.2.0 is requested, quickchecks must be disabled. */ + if (UCD_Check(self)) { return NO; + } Py_ssize_t i, len; int kind; @@ -885,7 +867,6 @@ unicodedata_UCD_is_normalized_impl(PyObject *self, PyObject *form, PyObject *input) /*[clinic end generated code: output=11e5a3694e723ca5 input=a544f14cea79e508]*/ { - unicodedata_module_state *state = &global_module_state; if (PyUnicode_READY(input) == -1) { return NULL; } @@ -921,10 +902,10 @@ unicodedata_UCD_is_normalized_impl(PyObject *self, PyObject *form, return NULL; } - m = is_normalized_quickcheck(state, self, input, nfc, k, false); + m = is_normalized_quickcheck(self, input, nfc, k, false); if (m == MAYBE) { - cmp = (nfc ? nfc_nfkc : nfd_nfkd)(state, self, input, k); + cmp = (nfc ? nfc_nfkc : nfd_nfkd)(self, input, k); if (cmp == NULL) { return NULL; } @@ -959,7 +940,6 @@ unicodedata_UCD_normalize_impl(PyObject *self, PyObject *form, PyObject *input) /*[clinic end generated code: output=05ca4385a2ad6983 input=3a5206c0ad2833fb]*/ { - unicodedata_module_state *state = &global_module_state; if (PyUnicode_GET_LENGTH(input) == 0) { /* Special case empty input strings, since resizing them later would cause internal errors. */ @@ -968,36 +948,36 @@ unicodedata_UCD_normalize_impl(PyObject *self, PyObject *form, } if (_PyUnicode_EqualToASCIIId(form, &PyId_NFC)) { - if (is_normalized_quickcheck(state, self, input, + if (is_normalized_quickcheck(self, input, true, false, true) == YES) { Py_INCREF(input); return input; } - return nfc_nfkc(state, self, input, 0); + return nfc_nfkc(self, input, 0); } if (_PyUnicode_EqualToASCIIId(form, &PyId_NFKC)) { - if (is_normalized_quickcheck(state, self, input, + if (is_normalized_quickcheck(self, input, true, true, true) == YES) { Py_INCREF(input); return input; } - return nfc_nfkc(state, self, input, 1); + return nfc_nfkc(self, input, 1); } if (_PyUnicode_EqualToASCIIId(form, &PyId_NFD)) { - if (is_normalized_quickcheck(state, self, input, + if (is_normalized_quickcheck(self, input, false, false, true) == YES) { Py_INCREF(input); return input; } - return nfd_nfkd(state, self, input, 0); + return nfd_nfkd(self, input, 0); } if (_PyUnicode_EqualToASCIIId(form, &PyId_NFKD)) { - if (is_normalized_quickcheck(state, self, input, + if (is_normalized_quickcheck(self, input, false, true, true) == YES) { Py_INCREF(input); return input; } - return nfd_nfkd(state, self, input, 1); + return nfd_nfkd(self, input, 1); } PyErr_SetString(PyExc_ValueError, "invalid normalization form"); return NULL; @@ -1080,7 +1060,7 @@ is_unified_ideograph(Py_UCS4 code) (cp < named_sequences_end)) static int -_getucname(unicodedata_module_state *state, PyObject *self, +_getucname(PyObject *self, Py_UCS4 code, char* buffer, int buflen, int with_alias_and_seq) { /* Find the name associated with the given code point. @@ -1098,7 +1078,7 @@ _getucname(unicodedata_module_state *state, PyObject *self, if (!with_alias_and_seq && (IS_ALIAS(code) || IS_NAMED_SEQ(code))) return 0; - if (UCD_Check(self, state->ucd_type)) { + if (UCD_Check(self)) { /* in 3.2.0 there are no aliases and named sequences */ const change_record *old; if (IS_ALIAS(code) || IS_NAMED_SEQ(code)) @@ -1182,23 +1162,21 @@ _getucname(unicodedata_module_state *state, PyObject *self, } static int -capi_getucname(void *state_raw, PyObject *self, Py_UCS4 code, +capi_getucname(Py_UCS4 code, char* buffer, int buflen, int with_alias_and_seq) { - unicodedata_module_state *state = (unicodedata_module_state *)state_raw; - return _getucname(state, self, code, buffer, buflen, with_alias_and_seq); + return _getucname(NULL, code, buffer, buflen, with_alias_and_seq); } static int -_cmpname(unicodedata_module_state *state, PyObject *self, - int code, const char* name, int namelen) +_cmpname(PyObject *self, int code, const char* name, int namelen) { /* check if code corresponds to the given name */ int i; char buffer[NAME_MAXLEN+1]; - if (!_getucname(state, self, code, buffer, NAME_MAXLEN, 1)) + if (!_getucname(self, code, buffer, NAME_MAXLEN, 1)) return 0; for (i = 0; i < namelen; i++) { if (Py_TOUPPER(name[i]) != buffer[i]) @@ -1243,7 +1221,7 @@ _check_alias_and_seq(unsigned int cp, Py_UCS4* code, int with_named_seq) } static int -_getcode(unicodedata_module_state *state, PyObject* self, +_getcode(PyObject* self, const char* name, int namelen, Py_UCS4* code, int with_named_seq) { /* Return the code point associated with the given name. @@ -1305,7 +1283,7 @@ _getcode(unicodedata_module_state *state, PyObject* self, v = code_hash[i]; if (!v) return 0; - if (_cmpname(state, self, v, name, namelen)) { + if (_cmpname(self, v, name, namelen)) { return _check_alias_and_seq(v, code, with_named_seq); } incr = (h ^ (h >> 3)) & mask; @@ -1316,7 +1294,7 @@ _getcode(unicodedata_module_state *state, PyObject* self, v = code_hash[i]; if (!v) return 0; - if (_cmpname(state, self, v, name, namelen)) { + if (_cmpname(self, v, name, namelen)) { return _check_alias_and_seq(v, code, with_named_seq); } incr = incr << 1; @@ -1326,15 +1304,20 @@ _getcode(unicodedata_module_state *state, PyObject* self, } static int -capi_getcode(void *state_raw, PyObject* self, - const char* name, int namelen, Py_UCS4* code, +capi_getcode(const char* name, int namelen, Py_UCS4* code, int with_named_seq) { - unicodedata_module_state *state = (unicodedata_module_state *)state_raw; - return _getcode(state, self, name, namelen, code, with_named_seq); + return _getcode(NULL, name, namelen, code, with_named_seq); } +static const _PyUnicode_Name_CAPI unicodedata_capi = +{ + .getname = capi_getucname, + .getcode = capi_getcode, +}; + + /* -------------------------------------------------------------------- */ /* Python bindings */ @@ -1356,11 +1339,10 @@ static PyObject * unicodedata_UCD_name_impl(PyObject *self, int chr, PyObject *default_value) /*[clinic end generated code: output=6bbb37a326407707 input=3e0367f534de56d9]*/ { - unicodedata_module_state *state = &global_module_state; char name[NAME_MAXLEN+1]; Py_UCS4 c = (Py_UCS4)chr; - if (!_getucname(state, self, c, name, NAME_MAXLEN, 0)) { + if (!_getucname(self, c, name, NAME_MAXLEN, 0)) { if (default_value == NULL) { PyErr_SetString(PyExc_ValueError, "no such name"); return NULL; @@ -1392,7 +1374,6 @@ unicodedata_UCD_lookup_impl(PyObject *self, const char *name, Py_ssize_clean_t name_length) /*[clinic end generated code: output=765cb8186788e6be input=a557be0f8607a0d6]*/ { - unicodedata_module_state *state = &global_module_state; Py_UCS4 code; unsigned int index; if (name_length > NAME_MAXLEN) { @@ -1400,7 +1381,7 @@ unicodedata_UCD_lookup_impl(PyObject *self, const char *name, return NULL; } - if (!_getcode(state, self, name, (int)name_length, &code, 1)) { + if (!_getcode(self, name, (int)name_length, &code, 1)) { PyErr_Format(PyExc_KeyError, "undefined character name '%s'", name); return NULL; } @@ -1415,8 +1396,10 @@ unicodedata_UCD_lookup_impl(PyObject *self, const char *name, return PyUnicode_FromOrdinal(code); } -/* XXX Add doc strings. */ - +// List of functions used to define module functions *AND* unicodedata.UCD +// methods. For module functions, self is the module. For UCD methods, self +// is an UCD instance. The UCD_Check() macro is used to check if self is +// an UCD instance. static PyMethodDef unicodedata_functions[] = { UNICODEDATA_UCD_DECIMAL_METHODDEF UNICODEDATA_UCD_DIGIT_METHODDEF @@ -1501,41 +1484,64 @@ static struct PyModuleDef unicodedatamodule = { NULL }; -PyMODINIT_FUNC -PyInit_unicodedata(void) -{ - PyObject *m, *v; - unicodedata_module_state *state = &global_module_state; - - state->capi.size = sizeof(_PyUnicode_Name_CAPI); - state->capi.state = state; - state->capi.getname = capi_getucname; - state->capi.getcode = capi_getcode; +static int +unicodedata_exec(PyObject *module) +{ Py_SET_TYPE(&UCD_Type, &PyType_Type); - state->ucd_type = &UCD_Type; + PyTypeObject *ucd_type = &UCD_Type; - m = PyModule_Create(&unicodedatamodule); - if (!m) - return NULL; + if (PyModule_AddStringConstant(module, "unidata_version", UNIDATA_VERSION) < 0) { + return -1; + } - PyModule_AddStringConstant(m, "unidata_version", UNIDATA_VERSION); - Py_INCREF(state->ucd_type); - PyModule_AddObject(m, "UCD", (PyObject*)state->ucd_type); + if (PyModule_AddType(module, ucd_type) < 0) { + return -1; + } /* Previous versions */ - v = new_previous_version(state, "3.2.0", + PyObject *v; + v = new_previous_version(ucd_type, "3.2.0", get_change_3_2_0, normalization_3_2_0); - if (v != NULL) - PyModule_AddObject(m, "ucd_3_2_0", v); + if (v == NULL) { + return -1; + } + if (PyModule_AddObject(module, "ucd_3_2_0", v) < 0) { + Py_DECREF(v); + return -1; + } /* Export C API */ - v = PyCapsule_New((void *)&state->capi, PyUnicodeData_CAPSULE_NAME, NULL); - if (v != NULL) - PyModule_AddObject(m, "ucnhash_CAPI", v); - return m; + v = PyCapsule_New((void *)&unicodedata_capi, PyUnicodeData_CAPSULE_NAME, + NULL); + if (v == NULL) { + return -1; + } + if (PyModule_AddObject(module, "ucnhash_CAPI", v) < 0) { + Py_DECREF(v); + return -1; + } + return 0; +} + + +PyMODINIT_FUNC +PyInit_unicodedata(void) +{ + PyObject *module = PyModule_Create(&unicodedatamodule); + if (!module) { + return NULL; + } + + if (unicodedata_exec(module) < 0) { + Py_DECREF(module); + return NULL; + } + + return module; } + /* Local variables: c-basic-offset: 4 diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index ba48d35aa40b1..9058018201039 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -6523,8 +6523,7 @@ _PyUnicode_DecodeUnicodeEscape(const char *s, s++; ch = 0xffffffff; /* in case 'getcode' messes up */ if (namelen <= INT_MAX && - ucnhash_capi->getcode(ucnhash_capi->state, NULL, - start, (int)namelen, + ucnhash_capi->getcode(start, (int)namelen, &ch, 0)) { assert(ch <= MAX_UNICODE); WRITE_CHAR(ch); diff --git a/Python/codecs.c b/Python/codecs.c index 62d1f3f3ac0d3..fa329ce243642 100644 --- a/Python/codecs.c +++ b/Python/codecs.c @@ -987,8 +987,7 @@ PyObject *PyCodec_NameReplaceErrors(PyObject *exc) for (i = start, ressize = 0; i < end; ++i) { /* object is guaranteed to be "ready" */ c = PyUnicode_READ_CHAR(object, i); - if (ucnhash_capi->getname(ucnhash_capi->state, NULL, - c, buffer, sizeof(buffer), 1)) { + if (ucnhash_capi->getname(c, buffer, sizeof(buffer), 1)) { replsize = 1+1+1+(int)strlen(buffer)+1; } else if (c >= 0x10000) { @@ -1011,8 +1010,7 @@ PyObject *PyCodec_NameReplaceErrors(PyObject *exc) i < end; ++i) { c = PyUnicode_READ_CHAR(object, i); *outp++ = '\\'; - if (ucnhash_capi->getname(ucnhash_capi->state, NULL, - c, buffer, sizeof(buffer), 1)) { + if (ucnhash_capi->getname(c, buffer, sizeof(buffer), 1)) { *outp++ = 'N'; *outp++ = '{'; strcpy((char *)outp, buffer); From webhook-mailer at python.org Mon Oct 26 18:19:32 2020 From: webhook-mailer at python.org (vstinner) Date: Mon, 26 Oct 2020 22:19:32 -0000 Subject: [Python-checkins] bpo-42157: Convert unicodedata.UCD to heap type (GH-22991) Message-ID: https://github.com/python/cpython/commit/c8c4200b65b2159bbb13cee10d67dfb3676fef26 commit: c8c4200b65b2159bbb13cee10d67dfb3676fef26 branch: master author: Victor Stinner committer: vstinner date: 2020-10-26T23:19:22+01:00 summary: bpo-42157: Convert unicodedata.UCD to heap type (GH-22991) Convert the unicodedata extension module to the multiphase initialization API (PEP 489) and convert the unicodedata.UCD static type to a heap type. Co-Authored-By: Mohamed Koubaa files: A Misc/NEWS.d/next/Library/2020-10-26-19-08-07.bpo-42157.Bdpa04.rst M Modules/_abc.c M Modules/unicodedata.c diff --git a/Misc/NEWS.d/next/Library/2020-10-26-19-08-07.bpo-42157.Bdpa04.rst b/Misc/NEWS.d/next/Library/2020-10-26-19-08-07.bpo-42157.Bdpa04.rst new file mode 100644 index 0000000000000..68778906bbbf3 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-10-26-19-08-07.bpo-42157.Bdpa04.rst @@ -0,0 +1,4 @@ +Convert the :mod:`unicodedata` extension module to the multiphase +initialization API (:pep:`489`) and convert the ``unicodedata.UCD`` +static type to a heap type. +Patch by Mohamed Koubaa and Victor Stinner. diff --git a/Modules/_abc.c b/Modules/_abc.c index 709b52ff96b29..7afaa759b2bfe 100644 --- a/Modules/_abc.c +++ b/Modules/_abc.c @@ -891,14 +891,14 @@ static PyModuleDef_Slot _abcmodule_slots[] = { static struct PyModuleDef _abcmodule = { PyModuleDef_HEAD_INIT, - "_abc", - _abc__doc__, - sizeof(_abcmodule_state), - _abcmodule_methods, - _abcmodule_slots, - _abcmodule_traverse, - _abcmodule_clear, - _abcmodule_free, + .m_name = "_abc", + .m_doc = _abc__doc__, + .m_size = sizeof(_abcmodule_state), + .m_methods = _abcmodule_methods, + .m_slots = _abcmodule_slots, + .m_traverse = _abcmodule_traverse, + .m_clear = _abcmodule_clear, + .m_free = _abcmodule_free, }; PyMODINIT_FUNC diff --git a/Modules/unicodedata.c b/Modules/unicodedata.c index 6c802ba116ffe..18b0a9af9d21c 100644 --- a/Modules/unicodedata.c +++ b/Modules/unicodedata.c @@ -28,9 +28,9 @@ _Py_IDENTIFIER(NFKD); /*[clinic input] module unicodedata -class unicodedata.UCD 'PreviousDBVersion *' '&UCD_Type' +class unicodedata.UCD 'PreviousDBVersion *' '' [clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=6dac153082d150bc]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=e47113e05924be43]*/ /* character properties */ @@ -90,9 +90,6 @@ static PyMemberDef DB_members[] = { {NULL} }; -/* forward declaration */ -static PyTypeObject UCD_Type; - // Check if self is an unicodedata.UCD instance. // If self is NULL (when the PyCapsule C API is used), return 0. // PyModule_Check() is used to avoid having to retrieve the ucd_type. @@ -1417,50 +1414,27 @@ static PyMethodDef unicodedata_functions[] = { {NULL, NULL} /* sentinel */ }; -static PyTypeObject UCD_Type = { - /* The ob_type field must be initialized in the module init function - * to be portable to Windows without using C++. */ - PyVarObject_HEAD_INIT(NULL, 0) - "unicodedata.UCD", /*tp_name*/ - sizeof(PreviousDBVersion), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - /* methods */ - (destructor)PyObject_Del, /*tp_dealloc*/ - 0, /*tp_vectorcall_offset*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_as_async*/ - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash*/ - 0, /*tp_call*/ - 0, /*tp_str*/ - PyObject_GenericGetAttr,/*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT, /*tp_flags*/ - 0, /*tp_doc*/ - 0, /*tp_traverse*/ - 0, /*tp_clear*/ - 0, /*tp_richcompare*/ - 0, /*tp_weaklistoffset*/ - 0, /*tp_iter*/ - 0, /*tp_iternext*/ - unicodedata_functions, /*tp_methods*/ - DB_members, /*tp_members*/ - 0, /*tp_getset*/ - 0, /*tp_base*/ - 0, /*tp_dict*/ - 0, /*tp_descr_get*/ - 0, /*tp_descr_set*/ - 0, /*tp_dictoffset*/ - 0, /*tp_init*/ - 0, /*tp_alloc*/ - 0, /*tp_new*/ - 0, /*tp_free*/ - 0, /*tp_is_gc*/ +static void +ucd_dealloc(PreviousDBVersion *self) +{ + PyTypeObject *tp = Py_TYPE(self); + PyObject_Del(self); + Py_DECREF(tp); +} + +static PyType_Slot ucd_type_slots[] = { + {Py_tp_dealloc, ucd_dealloc}, + {Py_tp_getattro, PyObject_GenericGetAttr}, + {Py_tp_methods, unicodedata_functions}, + {Py_tp_members, DB_members}, + {0, 0} +}; + +static PyType_Spec ucd_type_spec = { + .name = "unicodedata.UCD", + .basicsize = sizeof(PreviousDBVersion), + .flags = Py_TPFLAGS_DEFAULT, + .slots = ucd_type_slots }; PyDoc_STRVAR(unicodedata_docstring, @@ -1472,30 +1446,20 @@ this database is based on the UnicodeData.txt file version\n\ The module uses the same names and symbols as defined by the\n\ UnicodeData File Format " UNIDATA_VERSION "."); -static struct PyModuleDef unicodedatamodule = { - PyModuleDef_HEAD_INIT, - "unicodedata", - unicodedata_docstring, - -1, - unicodedata_functions, - NULL, - NULL, - NULL, - NULL -}; - - static int unicodedata_exec(PyObject *module) { - Py_SET_TYPE(&UCD_Type, &PyType_Type); - PyTypeObject *ucd_type = &UCD_Type; - if (PyModule_AddStringConstant(module, "unidata_version", UNIDATA_VERSION) < 0) { return -1; } + PyTypeObject *ucd_type = (PyTypeObject *)PyType_FromSpec(&ucd_type_spec); + if (ucd_type == NULL) { + return -1; + } + if (PyModule_AddType(module, ucd_type) < 0) { + Py_DECREF(ucd_type); return -1; } @@ -1503,6 +1467,7 @@ unicodedata_exec(PyObject *module) PyObject *v; v = new_previous_version(ucd_type, "3.2.0", get_change_3_2_0, normalization_3_2_0); + Py_DECREF(ucd_type); if (v == NULL) { return -1; } @@ -1524,21 +1489,24 @@ unicodedata_exec(PyObject *module) return 0; } +static PyModuleDef_Slot unicodedata_slots[] = { + {Py_mod_exec, unicodedata_exec}, + {0, NULL} +}; + +static struct PyModuleDef unicodedata_module = { + PyModuleDef_HEAD_INIT, + .m_name = "unicodedata", + .m_doc = unicodedata_docstring, + .m_size = 0, + .m_methods = unicodedata_functions, + .m_slots = unicodedata_slots, +}; PyMODINIT_FUNC PyInit_unicodedata(void) { - PyObject *module = PyModule_Create(&unicodedatamodule); - if (!module) { - return NULL; - } - - if (unicodedata_exec(module) < 0) { - Py_DECREF(module); - return NULL; - } - - return module; + return PyModuleDef_Init(&unicodedata_module); } From webhook-mailer at python.org Mon Oct 26 18:42:11 2020 From: webhook-mailer at python.org (lysnikolaou) Date: Mon, 26 Oct 2020 22:42:11 -0000 Subject: [Python-checkins] bpo-42123: Run the parser two times and only enable invalid rules on the second run (GH-22111) Message-ID: https://github.com/python/cpython/commit/bca701403253379409dece03053dbd739c0bd059 commit: bca701403253379409dece03053dbd739c0bd059 branch: master author: Lysandros Nikolaou committer: lysnikolaou date: 2020-10-27T00:42:04+02:00 summary: bpo-42123: Run the parser two times and only enable invalid rules on the second run (GH-22111) * Implement running the parser a second time for the errors messages The first parser run is only responsible for detecting whether there is a `SyntaxError` or not. If there isn't the AST gets returned. Otherwise, the parser is run a second time with all the `invalid_*` rules enabled so that all the customized error messages get produced. files: A Misc/NEWS.d/next/Core and Builtins/2020-10-23-02-43-24.bpo-42123.64gJWC.rst M Grammar/python.gram M Parser/parser.c M Parser/pegen.c M Parser/pegen.h M Tools/peg_generator/pegen/c_generator.py diff --git a/Grammar/python.gram b/Grammar/python.gram index d2d6fc0d3398b..19c85accf8d9a 100644 --- a/Grammar/python.gram +++ b/Grammar/python.gram @@ -551,7 +551,7 @@ yield_expr[expr_ty]: arguments[expr_ty] (memo): | a=args [','] &')' { a } - | incorrect_arguments + | invalid_arguments args[expr_ty]: | a[asdl_expr_seq*]=','.(starred_expression | named_expression !'=')+ b=[',' k=kwargs {k}] { _PyPegen_collect_call_seqs(p, a, b, EXTRA) } | a=kwargs { _Py_Call(_PyPegen_dummy_name(p), @@ -637,7 +637,7 @@ t_atom[expr_ty]: # From here on, there are rules for invalid syntax with specialised error messages -incorrect_arguments: +invalid_arguments: | args ',' '*' { RAISE_SYNTAX_ERROR("iterable argument unpacking follows keyword argument unpacking") } | a=expression for_if_clauses ',' [args | expression for_if_clauses] { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "Generator expression must be parenthesized") } diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-10-23-02-43-24.bpo-42123.64gJWC.rst b/Misc/NEWS.d/next/Core and Builtins/2020-10-23-02-43-24.bpo-42123.64gJWC.rst new file mode 100644 index 0000000000000..6461efd76f0f9 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2020-10-23-02-43-24.bpo-42123.64gJWC.rst @@ -0,0 +1,3 @@ +Run the parser two times. On the first run, disable all the 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. diff --git a/Parser/parser.c b/Parser/parser.c index 09be03c633a26..e438f06c9be9a 100644 --- a/Parser/parser.c +++ b/Parser/parser.c @@ -209,7 +209,7 @@ static KeywordToken *reserved_keywords[] = { #define t_primary_type 1140 // Left-recursive #define t_lookahead_type 1141 #define t_atom_type 1142 -#define incorrect_arguments_type 1143 +#define invalid_arguments_type 1143 #define invalid_kwarg_type 1144 #define invalid_named_expression_type 1145 #define invalid_assignment_type 1146 @@ -527,7 +527,7 @@ static expr_ty target_rule(Parser *p); static expr_ty t_primary_rule(Parser *p); static void *t_lookahead_rule(Parser *p); static expr_ty t_atom_rule(Parser *p); -static void *incorrect_arguments_rule(Parser *p); +static void *invalid_arguments_rule(Parser *p); static void *invalid_kwarg_rule(Parser *p); static void *invalid_named_expression_rule(Parser *p); static void *invalid_assignment_rule(Parser *p); @@ -2218,7 +2218,7 @@ assignment_rule(Parser *p) return NULL; } } - { // invalid_assignment + if (p->call_invalid_rules) { // invalid_assignment if (p->error_indicator) { D(p->level--); return NULL; @@ -2891,7 +2891,7 @@ del_stmt_rule(Parser *p) D(fprintf(stderr, "%*c%s del_stmt[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'del' del_targets &(';' | NEWLINE)")); } - { // invalid_del_stmt + if (p->call_invalid_rules) { // invalid_del_stmt if (p->error_indicator) { D(p->level--); return NULL; @@ -3242,7 +3242,7 @@ import_from_targets_rule(Parser *p) D(fprintf(stderr, "%*c%s import_from_targets[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'*'")); } - { // invalid_import_from_targets + if (p->call_invalid_rules) { // invalid_import_from_targets if (p->error_indicator) { D(p->level--); return NULL; @@ -4035,7 +4035,7 @@ for_stmt_rule(Parser *p) return NULL; } } - { // invalid_for_target + if (p->call_invalid_rules) { // invalid_for_target if (p->error_indicator) { D(p->level--); return NULL; @@ -4336,7 +4336,7 @@ with_item_rule(Parser *p) D(fprintf(stderr, "%*c%s with_item[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression 'as' star_target &(',' | ')' | ':')")); } - { // invalid_with_item + if (p->call_invalid_rules) { // invalid_with_item if (p->error_indicator) { D(p->level--); return NULL; @@ -5071,7 +5071,7 @@ func_type_comment_rule(Parser *p) D(fprintf(stderr, "%*c%s func_type_comment[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "NEWLINE TYPE_COMMENT &(NEWLINE INDENT)")); } - { // invalid_double_type_comments + if (p->call_invalid_rules) { // invalid_double_type_comments if (p->error_indicator) { D(p->level--); return NULL; @@ -5126,7 +5126,7 @@ params_rule(Parser *p) } arguments_ty _res = NULL; int _mark = p->mark; - { // invalid_parameters + if (p->call_invalid_rules) { // invalid_parameters if (p->error_indicator) { D(p->level--); return NULL; @@ -5601,7 +5601,7 @@ star_etc_rule(Parser *p) D(fprintf(stderr, "%*c%s star_etc[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "kwds")); } - { // invalid_star_etc + if (p->call_invalid_rules) { // invalid_star_etc if (p->error_indicator) { D(p->level--); return NULL; @@ -6304,7 +6304,7 @@ block_rule(Parser *p) D(fprintf(stderr, "%*c%s block[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "simple_stmt")); } - { // invalid_block + if (p->call_invalid_rules) { // invalid_block if (p->error_indicator) { D(p->level--); return NULL; @@ -6798,7 +6798,7 @@ named_expression_rule(Parser *p) D(fprintf(stderr, "%*c%s named_expression[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression !':='")); } - { // invalid_named_expression + if (p->call_invalid_rules) { // invalid_named_expression if (p->error_indicator) { D(p->level--); return NULL; @@ -7192,7 +7192,7 @@ lambda_params_rule(Parser *p) } arguments_ty _res = NULL; int _mark = p->mark; - { // invalid_lambda_parameters + if (p->call_invalid_rules) { // invalid_lambda_parameters if (p->error_indicator) { D(p->level--); return NULL; @@ -7669,7 +7669,7 @@ lambda_star_etc_rule(Parser *p) D(fprintf(stderr, "%*c%s lambda_star_etc[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_kwds")); } - { // invalid_lambda_star_etc + if (p->call_invalid_rules) { // invalid_lambda_star_etc if (p->error_indicator) { D(p->level--); return NULL; @@ -11163,7 +11163,7 @@ listcomp_rule(Parser *p) return NULL; } } - { // invalid_comprehension + if (p->call_invalid_rules) { // invalid_comprehension if (p->error_indicator) { D(p->level--); return NULL; @@ -11294,7 +11294,7 @@ group_rule(Parser *p) D(fprintf(stderr, "%*c%s group[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'(' (yield_expr | named_expression) ')'")); } - { // invalid_group + if (p->call_invalid_rules) { // invalid_group if (p->error_indicator) { D(p->level--); return NULL; @@ -11388,7 +11388,7 @@ genexp_rule(Parser *p) return NULL; } } - { // invalid_comprehension + if (p->call_invalid_rules) { // invalid_comprehension if (p->error_indicator) { D(p->level--); return NULL; @@ -11547,7 +11547,7 @@ setcomp_rule(Parser *p) return NULL; } } - { // invalid_comprehension + if (p->call_invalid_rules) { // invalid_comprehension if (p->error_indicator) { D(p->level--); return NULL; @@ -11699,7 +11699,7 @@ dictcomp_rule(Parser *p) D(fprintf(stderr, "%*c%s dictcomp[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' kvpair for_if_clauses '}'")); } - { // invalid_dict_comprehension + if (p->call_invalid_rules) { // invalid_dict_comprehension if (p->error_indicator) { D(p->level--); return NULL; @@ -12023,7 +12023,7 @@ for_if_clause_rule(Parser *p) return NULL; } } - { // invalid_for_target + if (p->call_invalid_rules) { // invalid_for_target if (p->error_indicator) { D(p->level--); return NULL; @@ -12149,7 +12149,7 @@ yield_expr_rule(Parser *p) return _res; } -// arguments: args ','? &')' | incorrect_arguments +// arguments: args ','? &')' | invalid_arguments static expr_ty arguments_rule(Parser *p) { @@ -12194,24 +12194,24 @@ arguments_rule(Parser *p) D(fprintf(stderr, "%*c%s arguments[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "args ','? &')'")); } - { // incorrect_arguments + if (p->call_invalid_rules) { // invalid_arguments if (p->error_indicator) { D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> arguments[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "incorrect_arguments")); - void *incorrect_arguments_var; + D(fprintf(stderr, "%*c> arguments[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "invalid_arguments")); + void *invalid_arguments_var; if ( - (incorrect_arguments_var = incorrect_arguments_rule(p)) // incorrect_arguments + (invalid_arguments_var = invalid_arguments_rule(p)) // invalid_arguments ) { - D(fprintf(stderr, "%*c+ arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "incorrect_arguments")); - _res = incorrect_arguments_var; + D(fprintf(stderr, "%*c+ arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "invalid_arguments")); + _res = invalid_arguments_var; goto done; } p->mark = _mark; D(fprintf(stderr, "%*c%s arguments[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "incorrect_arguments")); + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "invalid_arguments")); } _res = NULL; done: @@ -12548,7 +12548,7 @@ kwarg_or_starred_rule(Parser *p) D(fprintf(stderr, "%*c%s kwarg_or_starred[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "starred_expression")); } - { // invalid_kwarg + if (p->call_invalid_rules) { // invalid_kwarg if (p->error_indicator) { D(p->level--); return NULL; @@ -12668,7 +12668,7 @@ kwarg_or_double_starred_rule(Parser *p) D(fprintf(stderr, "%*c%s kwarg_or_double_starred[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'**' expression")); } - { // invalid_kwarg + if (p->call_invalid_rules) { // invalid_kwarg if (p->error_indicator) { D(p->level--); return NULL; @@ -14380,14 +14380,14 @@ t_atom_rule(Parser *p) return _res; } -// incorrect_arguments: +// invalid_arguments: // | args ',' '*' // | expression for_if_clauses ',' [args | expression for_if_clauses] // | args for_if_clauses // | args ',' expression for_if_clauses // | args ',' args static void * -incorrect_arguments_rule(Parser *p) +invalid_arguments_rule(Parser *p) { D(p->level++); if (p->error_indicator) { @@ -14401,7 +14401,7 @@ incorrect_arguments_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> incorrect_arguments[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "args ',' '*'")); + D(fprintf(stderr, "%*c> invalid_arguments[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "args ',' '*'")); Token * _literal; Token * _literal_1; expr_ty args_var; @@ -14413,7 +14413,7 @@ incorrect_arguments_rule(Parser *p) (_literal_1 = _PyPegen_expect_token(p, 16)) // token='*' ) { - D(fprintf(stderr, "%*c+ incorrect_arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "args ',' '*'")); + D(fprintf(stderr, "%*c+ invalid_arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "args ',' '*'")); _res = RAISE_SYNTAX_ERROR ( "iterable argument unpacking follows keyword argument unpacking" ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -14423,7 +14423,7 @@ incorrect_arguments_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s incorrect_arguments[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s invalid_arguments[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "args ',' '*'")); } { // expression for_if_clauses ',' [args | expression for_if_clauses] @@ -14431,7 +14431,7 @@ incorrect_arguments_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> incorrect_arguments[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression for_if_clauses ',' [args | expression for_if_clauses]")); + D(fprintf(stderr, "%*c> invalid_arguments[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression for_if_clauses ',' [args | expression for_if_clauses]")); Token * _literal; void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings @@ -14447,7 +14447,7 @@ incorrect_arguments_rule(Parser *p) (_opt_var = _tmp_127_rule(p), 1) // [args | expression for_if_clauses] ) { - D(fprintf(stderr, "%*c+ incorrect_arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression for_if_clauses ',' [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]")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "Generator expression must be parenthesized" ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -14457,7 +14457,7 @@ incorrect_arguments_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s incorrect_arguments[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s invalid_arguments[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression for_if_clauses ',' [args | expression for_if_clauses]")); } { // args for_if_clauses @@ -14465,7 +14465,7 @@ incorrect_arguments_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> incorrect_arguments[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "args for_if_clauses")); + D(fprintf(stderr, "%*c> invalid_arguments[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "args for_if_clauses")); expr_ty a; asdl_comprehension_seq* for_if_clauses_var; if ( @@ -14474,7 +14474,7 @@ incorrect_arguments_rule(Parser *p) (for_if_clauses_var = for_if_clauses_rule(p)) // for_if_clauses ) { - D(fprintf(stderr, "%*c+ incorrect_arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "args for_if_clauses")); + D(fprintf(stderr, "%*c+ invalid_arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "args for_if_clauses")); _res = _PyPegen_nonparen_genexp_in_call ( p , a ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -14484,7 +14484,7 @@ incorrect_arguments_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s incorrect_arguments[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s invalid_arguments[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "args for_if_clauses")); } { // args ',' expression for_if_clauses @@ -14492,7 +14492,7 @@ incorrect_arguments_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> incorrect_arguments[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "args ',' expression for_if_clauses")); + D(fprintf(stderr, "%*c> invalid_arguments[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "args ',' expression for_if_clauses")); Token * _literal; expr_ty a; expr_ty args_var; @@ -14507,7 +14507,7 @@ incorrect_arguments_rule(Parser *p) (for_if_clauses_var = for_if_clauses_rule(p)) // for_if_clauses ) { - D(fprintf(stderr, "%*c+ incorrect_arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "args ',' expression for_if_clauses")); + D(fprintf(stderr, "%*c+ invalid_arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "args ',' expression for_if_clauses")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "Generator expression must be parenthesized" ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -14517,7 +14517,7 @@ incorrect_arguments_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s incorrect_arguments[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s invalid_arguments[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "args ',' expression for_if_clauses")); } { // args ',' args @@ -14525,7 +14525,7 @@ incorrect_arguments_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> incorrect_arguments[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "args ',' args")); + D(fprintf(stderr, "%*c> invalid_arguments[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "args ',' args")); Token * _literal; expr_ty a; expr_ty args_var; @@ -14537,7 +14537,7 @@ incorrect_arguments_rule(Parser *p) (args_var = args_rule(p)) // args ) { - D(fprintf(stderr, "%*c+ incorrect_arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "args ',' args")); + D(fprintf(stderr, "%*c+ invalid_arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "args ',' args")); _res = _PyPegen_arguments_parsing_error ( p , a ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -14547,7 +14547,7 @@ incorrect_arguments_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s incorrect_arguments[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s invalid_arguments[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "args ',' args")); } _res = NULL; diff --git a/Parser/pegen.c b/Parser/pegen.c index c7343f7f047c3..827d4dace1fc3 100644 --- a/Parser/pegen.c +++ b/Parser/pegen.c @@ -1102,15 +1102,28 @@ _PyPegen_Parser_New(struct tok_state *tok, int start_rule, int flags, p->feature_version = feature_version; p->known_err_token = NULL; p->level = 0; + p->call_invalid_rules = 0; return p; } +static void +reset_parser_state(Parser *p) +{ + for (int i = 0; i < p->fill; i++) { + p->tokens[i]->memo = NULL; + } + p->mark = 0; + p->call_invalid_rules = 1; +} + void * _PyPegen_run_parser(Parser *p) { void *res = _PyPegen_parse(p); if (res == NULL) { + reset_parser_state(p); + _PyPegen_parse(p); if (PyErr_Occurred()) { return NULL; } diff --git a/Parser/pegen.h b/Parser/pegen.h index 9a280ae240a24..841f1e5eb4396 100644 --- a/Parser/pegen.h +++ b/Parser/pegen.h @@ -73,6 +73,7 @@ typedef struct { growable_comment_array type_ignore_comments; Token *known_err_token; int level; + int call_invalid_rules; } Parser; typedef struct { diff --git a/Tools/peg_generator/pegen/c_generator.py b/Tools/peg_generator/pegen/c_generator.py index 1a814aad11ccc..52bdb844e6bdd 100644 --- a/Tools/peg_generator/pegen/c_generator.py +++ b/Tools/peg_generator/pegen/c_generator.py @@ -736,7 +736,10 @@ def handle_alt_loop(self, node: Alt, is_gather: bool, rulename: Optional[str]) - def visit_Alt( self, node: Alt, is_loop: bool, is_gather: bool, rulename: Optional[str] ) -> None: - self.print(f"{{ // {node}") + if len(node.items) == 1 and str(node.items[0]).startswith('invalid_'): + self.print(f"if (p->call_invalid_rules) {{ // {node}") + else: + self.print(f"{{ // {node}") with self.indent(): self._check_for_errors() node_str = str(node).replace('"', '\\"') From webhook-mailer at python.org Mon Oct 26 19:00:11 2020 From: webhook-mailer at python.org (vstinner) Date: Mon, 26 Oct 2020 23:00:11 -0000 Subject: [Python-checkins] bpo-42161: Add _PyLong_GetZero() and _PyLong_GetOne() (GH-22993) Message-ID: https://github.com/python/cpython/commit/8e3b9f92835654943bb59d9658bb52e1b0f40a22 commit: 8e3b9f92835654943bb59d9658bb52e1b0f40a22 branch: master author: Victor Stinner committer: vstinner date: 2020-10-27T00:00:03+01:00 summary: bpo-42161: Add _PyLong_GetZero() and _PyLong_GetOne() (GH-22993) Add _PyLong_GetZero() and _PyLong_GetOne() functions and a new internal pycore_long.h header file. Python cannot be built without small integer singletons anymore. files: A Include/internal/pycore_long.h M Include/internal/pycore_interp.h M Makefile.pre.in M Objects/longobject.c M PCbuild/pythoncore.vcxproj M PCbuild/pythoncore.vcxproj.filters diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h index 538aa5a653fbf..eee369a44bfc7 100644 --- a/Include/internal/pycore_interp.h +++ b/Include/internal/pycore_interp.h @@ -163,6 +163,11 @@ struct _Py_exc_state { #define _PY_NSMALLPOSINTS 257 #define _PY_NSMALLNEGINTS 5 +// _PyLong_GetZero() and _PyLong_GetOne() must always be available +#if _PY_NSMALLPOSINTS < 2 +# error "_PY_NSMALLPOSINTS must be greater than 1" +#endif + // The PyInterpreterState typedef is in Include/pystate.h. struct _is { @@ -233,14 +238,12 @@ struct _is { PyObject *audit_hooks; -#if _PY_NSMALLNEGINTS + _PY_NSMALLPOSINTS > 0 /* Small integers are preallocated in this array so that they can be shared. The integers that are preallocated are those in the range -_PY_NSMALLNEGINTS (inclusive) to _PY_NSMALLPOSINTS (not inclusive). */ PyLongObject* small_ints[_PY_NSMALLNEGINTS + _PY_NSMALLPOSINTS]; -#endif struct _Py_bytes_state bytes; struct _Py_unicode_state unicode; struct _Py_float_state float_state; diff --git a/Include/internal/pycore_long.h b/Include/internal/pycore_long.h new file mode 100644 index 0000000000000..ec95786531c85 --- /dev/null +++ b/Include/internal/pycore_long.h @@ -0,0 +1,43 @@ +#ifndef Py_INTERNAL_LONG_H +#define Py_INTERNAL_LONG_H +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef Py_BUILD_CORE +# error "this header requires Py_BUILD_CORE define" +#endif + +#include "pycore_interp.h" // PyInterpreterState.small_ints +#include "pycore_pystate.h" // _PyThreadState_GET() + +// Don't call this function but _PyLong_GetZero() and _PyLong_GetOne() +static inline PyObject* __PyLong_GetSmallInt_internal(int value) +{ + PyThreadState *tstate = _PyThreadState_GET(); +#ifdef Py_DEBUG + _Py_EnsureTstateNotNULL(tstate); +#endif + assert(-_PY_NSMALLNEGINTS <= value && value < _PY_NSMALLPOSINTS); + size_t index = _PY_NSMALLNEGINTS + value; + PyObject *obj = (PyObject*)tstate->interp->small_ints[index]; + // _PyLong_GetZero() and _PyLong_GetOne() must not be called + // before _PyLong_Init() nor after _PyLong_Fini() + assert(obj != NULL); + return obj; +} + +// Return a borrowed reference to the zero singleton. +// The function cannot return NULL. +static inline PyObject* _PyLong_GetZero(void) +{ return __PyLong_GetSmallInt_internal(0); } + +// Return a borrowed reference to the one singleton. +// The function cannot return NULL. +static inline PyObject* _PyLong_GetOne(void) +{ return __PyLong_GetSmallInt_internal(1); } + +#ifdef __cplusplus +} +#endif +#endif /* !Py_INTERNAL_LONG_H */ diff --git a/Makefile.pre.in b/Makefile.pre.in index fe226ce45d8e9..31f61f3d5b836 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1117,6 +1117,7 @@ PYTHON_HEADERS= \ $(srcdir)/Include/internal/pycore_initconfig.h \ $(srcdir)/Include/internal/pycore_interp.h \ $(srcdir)/Include/internal/pycore_list.h \ + $(srcdir)/Include/internal/pycore_long.h \ $(srcdir)/Include/internal/pycore_object.h \ $(srcdir)/Include/internal/pycore_pathconfig.h \ $(srcdir)/Include/internal/pycore_pyerrors.h \ diff --git a/Objects/longobject.c b/Objects/longobject.c index 92514d4154e2c..ae63eba134504 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -5,6 +5,7 @@ #include "Python.h" #include "pycore_bitutils.h" // _Py_popcount32() #include "pycore_interp.h" // _PY_NSMALLPOSINTS +#include "pycore_long.h" // __PyLong_GetSmallInt_internal() #include "pycore_object.h" // _PyObject_InitVar() #include "pycore_pystate.h" // _Py_IsMainInterpreter() #include "longintrepr.h" @@ -19,8 +20,8 @@ class int "PyObject *" "&PyLong_Type" [clinic start generated code]*/ /*[clinic end generated code: output=da39a3ee5e6b4b0d input=ec0275e3422a36e3]*/ -#define NSMALLPOSINTS _PY_NSMALLPOSINTS #define NSMALLNEGINTS _PY_NSMALLNEGINTS +#define NSMALLPOSINTS _PY_NSMALLPOSINTS _Py_IDENTIFIER(little); _Py_IDENTIFIER(big); @@ -34,7 +35,6 @@ _Py_IDENTIFIER(big); PyObject *_PyLong_Zero = NULL; PyObject *_PyLong_One = NULL; -#if NSMALLNEGINTS + NSMALLPOSINTS > 0 #define IS_SMALL_INT(ival) (-NSMALLNEGINTS <= (ival) && (ival) < NSMALLPOSINTS) #define IS_SMALL_UINT(ival) ((ival) < NSMALLPOSINTS) @@ -42,8 +42,7 @@ static PyObject * get_small_int(sdigit ival) { assert(IS_SMALL_INT(ival)); - PyInterpreterState *interp = _PyInterpreterState_GET(); - PyObject *v = (PyObject*)interp->small_ints[ival + NSMALLNEGINTS]; + PyObject *v = __PyLong_GetSmallInt_internal(ival); Py_INCREF(v); return v; } @@ -60,12 +59,6 @@ maybe_small_long(PyLongObject *v) } return v; } -#else -#define IS_SMALL_INT(ival) 0 -#define IS_SMALL_UINT(ival) 0 -#define get_small_int(ival) (Py_UNREACHABLE(), NULL) -#define maybe_small_long(val) (val) -#endif /* If a freshly-allocated int is already shared, it must be a small integer, so negating it must go to PyLong_FromLong */ @@ -2559,8 +2552,9 @@ long_divrem(PyLongObject *a, PyLongObject *b, if (*prem == NULL) { return -1; } - Py_INCREF(_PyLong_Zero); - *pdiv = (PyLongObject*)_PyLong_Zero; + PyObject *zero = _PyLong_GetZero(); + Py_INCREF(zero); + *pdiv = (PyLongObject*)zero; return 0; } if (size_b == 1) { @@ -3669,7 +3663,7 @@ l_divmod(PyLongObject *v, PyLongObject *w, Py_DECREF(div); return -1; } - temp = (PyLongObject *) long_sub(div, (PyLongObject *)_PyLong_One); + temp = (PyLongObject *) long_sub(div, (PyLongObject *)_PyLong_GetOne()); if (temp == NULL) { Py_DECREF(mod); Py_DECREF(div); @@ -4078,7 +4072,7 @@ long_invmod(PyLongObject *a, PyLongObject *n) Py_DECREF(c); Py_DECREF(n); - if (long_compare(a, (PyLongObject *)_PyLong_One)) { + if (long_compare(a, (PyLongObject *)_PyLong_GetOne())) { /* a != 1; we don't have an inverse. */ Py_DECREF(a); Py_DECREF(b); @@ -4313,7 +4307,7 @@ long_invert(PyLongObject *v) PyLongObject *x; if (Py_ABS(Py_SIZE(v)) <=1) return PyLong_FromLong(-(MEDIUM_VALUE(v)+1)); - x = (PyLongObject *) long_add(v, (PyLongObject *)_PyLong_One); + x = (PyLongObject *) long_add(v, (PyLongObject *)_PyLong_GetOne()); if (x == NULL) return NULL; _PyLong_Negate(&x); @@ -5105,7 +5099,8 @@ _PyLong_DivmodNear(PyObject *a, PyObject *b) /* compare twice the remainder with the divisor, to see if we need to adjust the quotient and remainder */ - twice_rem = long_lshift((PyObject *)rem, _PyLong_One); + PyObject *one = _PyLong_GetOne(); // borrowed reference + twice_rem = long_lshift((PyObject *)rem, one); if (twice_rem == NULL) goto error; if (quo_is_neg) { @@ -5122,9 +5117,9 @@ _PyLong_DivmodNear(PyObject *a, PyObject *b) if ((Py_SIZE(b) < 0 ? cmp < 0 : cmp > 0) || (cmp == 0 && quo_is_odd)) { /* fix up quotient */ if (quo_is_neg) - temp = long_sub(quo, (PyLongObject *)_PyLong_One); + temp = long_sub(quo, (PyLongObject *)one); else - temp = long_add(quo, (PyLongObject *)_PyLong_One); + temp = long_add(quo, (PyLongObject *)one); Py_DECREF(quo); quo = (PyLongObject *)temp; if (quo == NULL) @@ -5406,7 +5401,7 @@ int_as_integer_ratio_impl(PyObject *self) if (numerator == NULL) { return NULL; } - ratio_tuple = PyTuple_Pack(2, numerator, _PyLong_One); + ratio_tuple = PyTuple_Pack(2, numerator, _PyLong_GetOne()); Py_DECREF(numerator); return ratio_tuple; } @@ -5712,7 +5707,6 @@ PyLong_GetInfo(void) int _PyLong_Init(PyThreadState *tstate) { -#if NSMALLNEGINTS + NSMALLPOSINTS > 0 for (Py_ssize_t i=0; i < NSMALLNEGINTS + NSMALLPOSINTS; i++) { sdigit ival = (sdigit)i - NSMALLNEGINTS; int size = (ival < 0) ? -1 : ((ival == 0) ? 0 : 1); @@ -5727,7 +5721,6 @@ _PyLong_Init(PyThreadState *tstate) tstate->interp->small_ints[i] = v; } -#endif if (_Py_IsMainInterpreter(tstate)) { _PyLong_Zero = PyLong_FromLong(0); @@ -5759,9 +5752,7 @@ _PyLong_Fini(PyThreadState *tstate) Py_CLEAR(_PyLong_Zero); } -#if NSMALLNEGINTS + NSMALLPOSINTS > 0 for (Py_ssize_t i = 0; i < NSMALLNEGINTS + NSMALLPOSINTS; i++) { Py_CLEAR(tstate->interp->small_ints[i]); } -#endif } diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index 600f33b2c6e3b..18edba855d616 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -185,6 +185,7 @@ + diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters index 75b91d8ed87f9..281bce1c5f498 100644 --- a/PCbuild/pythoncore.vcxproj.filters +++ b/PCbuild/pythoncore.vcxproj.filters @@ -537,6 +537,9 @@ Include\internal + + Include\internal + Include\internal From webhook-mailer at python.org Mon Oct 26 20:31:15 2020 From: webhook-mailer at python.org (miss-islington) Date: Tue, 27 Oct 2020 00:31:15 -0000 Subject: [Python-checkins] bpo-30681: Support invalid date format or value in email Date header (GH-22090) Message-ID: https://github.com/python/cpython/commit/303aac8c56609290e122eecc14c038e9b1e4174a commit: 303aac8c56609290e122eecc14c038e9b1e4174a branch: master author: Georges Toth committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-26T17:31:06-07:00 summary: bpo-30681: Support invalid date format or value in email Date header (GH-22090) I am re-submitting an older PR which was abandoned but is still relevant, #10783 by @timb07. The issue being solved () is still relevant. The original PR #10783 was closed as the final request changes were not applied and since abandoned. In this new PR I have re-used the original patch plus applied both comments from the review, by @maxking and @pganssle. For reference, here is the original PR description: In email.utils.parsedate_to_datetime(), a failure to parse the date, or invalid date components (such as hour outside 0..23) raises an exception. Document this behaviour, and add tests to test_email/test_utils.py to confirm this behaviour. In email.headerregistry.DateHeader.parse(), check when parsedate_to_datetime() raises an exception and add a new defect InvalidDateDefect; preserve the invalid value as the string value of the header, but set the datetime attribute to None. Add tests to test_email/test_headerregistry.py to confirm this behaviour; also added test to test_email/test_inversion.py to confirm emails with such defective date headers round trip successfully. This pull request incorporates feedback gratefully received from @bitdancer, @brettcannon, @Mariatta and @warsaw, and replaces the earlier PR #2254. Automerge-Triggered-By: GH:warsaw files: A Misc/NEWS.d/next/Library/2020-09-04-17-33-04.bpo-30681.LR4fnY.rst M Doc/library/email.errors.rst M Doc/library/email.utils.rst M Lib/email/_parseaddr.py M Lib/email/errors.py M Lib/email/headerregistry.py M Lib/email/utils.py M Lib/test/test_email/test_headerregistry.py M Lib/test/test_email/test_inversion.py M Lib/test/test_email/test_utils.py diff --git a/Doc/library/email.errors.rst b/Doc/library/email.errors.rst index f4b9f52509689..7a77640571cb1 100644 --- a/Doc/library/email.errors.rst +++ b/Doc/library/email.errors.rst @@ -112,3 +112,6 @@ All defect classes are subclassed from :class:`email.errors.MessageDefect`. * :class:`InvalidBase64LengthDefect` -- When decoding a block of base64 encoded bytes, the number of non-padding base64 characters was invalid (1 more than a multiple of 4). The encoded block was kept as-is. + +* :class:`InvalidDateDefect` -- When decoding an invalid or unparsable date field. + The original value is kept as-is. \ No newline at end of file diff --git a/Doc/library/email.utils.rst b/Doc/library/email.utils.rst index 4d0e920eb0ad2..0e266b6a45782 100644 --- a/Doc/library/email.utils.rst +++ b/Doc/library/email.utils.rst @@ -124,8 +124,10 @@ of the new API. .. function:: parsedate_to_datetime(date) The inverse of :func:`format_datetime`. Performs the same function as - :func:`parsedate`, but on success returns a :mod:`~datetime.datetime`. If - the input date has a timezone of ``-0000``, the ``datetime`` will be a naive + :func:`parsedate`, but on success returns a :mod:`~datetime.datetime`; + otherwise ``ValueError`` is raised if *date* contains an invalid value such + as an hour greater than 23 or a timezone offset not between -24 and 24 hours. + If the input date has a timezone of ``-0000``, the ``datetime`` will be a naive ``datetime``, and if the date is conforming to the RFCs it will represent a time in UTC but with no indication of the actual source timezone of the message the date comes from. If the input date has any other valid timezone diff --git a/Lib/email/_parseaddr.py b/Lib/email/_parseaddr.py index 41ff6f8c000d5..4d27f87974b20 100644 --- a/Lib/email/_parseaddr.py +++ b/Lib/email/_parseaddr.py @@ -65,7 +65,7 @@ def _parsedate_tz(data): """ if not data: - return + return None data = data.split() # The FWS after the comma after the day-of-week is optional, so search and # adjust for this. diff --git a/Lib/email/errors.py b/Lib/email/errors.py index d28a6800104ba..1d258c34fc9d4 100644 --- a/Lib/email/errors.py +++ b/Lib/email/errors.py @@ -108,3 +108,6 @@ class NonASCIILocalPartDefect(HeaderDefect): """local_part contains non-ASCII characters""" # This defect only occurs during unicode parsing, not when # parsing messages decoded from binary. + +class InvalidDateDefect(HeaderDefect): + """Header has unparseable or invalid date""" diff --git a/Lib/email/headerregistry.py b/Lib/email/headerregistry.py index 5d84fc0d82d0b..d8613ebf24e61 100644 --- a/Lib/email/headerregistry.py +++ b/Lib/email/headerregistry.py @@ -302,7 +302,14 @@ def parse(cls, value, kwds): kwds['parse_tree'] = parser.TokenList() return if isinstance(value, str): - value = utils.parsedate_to_datetime(value) + kwds['decoded'] = value + try: + value = utils.parsedate_to_datetime(value) + except ValueError: + kwds['defects'].append(errors.InvalidDateDefect('Invalid date value or format')) + kwds['datetime'] = None + kwds['parse_tree'] = parser.TokenList() + return kwds['datetime'] = value kwds['decoded'] = utils.format_datetime(kwds['datetime']) kwds['parse_tree'] = cls.value_parser(kwds['decoded']) diff --git a/Lib/email/utils.py b/Lib/email/utils.py index 1a7719dbc4898..a8e46a761bf92 100644 --- a/Lib/email/utils.py +++ b/Lib/email/utils.py @@ -195,7 +195,10 @@ def make_msgid(idstring=None, domain=None): def parsedate_to_datetime(data): - *dtuple, tz = _parsedate_tz(data) + parsed_date_tz = _parsedate_tz(data) + if parsed_date_tz is None: + raise ValueError('Invalid date value or format "%s"' % str(data)) + *dtuple, tz = parsed_date_tz if tz is None: return datetime.datetime(*dtuple[:6]) return datetime.datetime(*dtuple[:6], diff --git a/Lib/test/test_email/test_headerregistry.py b/Lib/test/test_email/test_headerregistry.py index 68bbc9561c4af..59fcd932e0ec4 100644 --- a/Lib/test/test_email/test_headerregistry.py +++ b/Lib/test/test_email/test_headerregistry.py @@ -204,6 +204,22 @@ def test_no_value_is_defect(self): self.assertEqual(len(h.defects), 1) self.assertIsInstance(h.defects[0], errors.HeaderMissingRequiredValue) + def test_invalid_date_format(self): + s = 'Not a date header' + h = self.make_header('date', s) + self.assertEqual(h, s) + self.assertIsNone(h.datetime) + self.assertEqual(len(h.defects), 1) + self.assertIsInstance(h.defects[0], errors.InvalidDateDefect) + + def test_invalid_date_value(self): + s = 'Tue, 06 Jun 2017 27:39:33 +0600' + h = self.make_header('date', s) + self.assertEqual(h, s) + self.assertIsNone(h.datetime) + self.assertEqual(len(h.defects), 1) + self.assertIsInstance(h.defects[0], errors.InvalidDateDefect) + def test_datetime_read_only(self): h = self.make_header('date', self.datestring) with self.assertRaises(AttributeError): diff --git a/Lib/test/test_email/test_inversion.py b/Lib/test/test_email/test_inversion.py index 8e8d67641b894..7bd7f2a72067a 100644 --- a/Lib/test/test_email/test_inversion.py +++ b/Lib/test/test_email/test_inversion.py @@ -46,6 +46,14 @@ def msg_as_input(self, msg): foo """),), + 'header_with_invalid_date': (dedent(b"""\ + Date: Tue, 06 Jun 2017 27:39:33 +0600 + From: abc at xyz.com + Subject: timezones + + How do they work even? + """),), + } payload_params = { diff --git a/Lib/test/test_email/test_utils.py b/Lib/test/test_email/test_utils.py index 4e3c3f3a195fc..e3d3eaebc9369 100644 --- a/Lib/test/test_email/test_utils.py +++ b/Lib/test/test_email/test_utils.py @@ -48,6 +48,16 @@ def test_parsedate_to_datetime_naive(self): utils.parsedate_to_datetime(self.datestring + ' -0000'), self.naive_dt) + def test_parsedate_to_datetime_with_invalid_raises_valueerror(self): + invalid_dates = ['', + '0', + 'A Complete Waste of Time' + 'Tue, 06 Jun 2017 27:39:33 +0600', + 'Tue, 06 Jun 2017 07:39:33 +2600', + 'Tue, 06 Jun 2017 27:39:33'] + for dtstr in invalid_dates: + with self.subTest(dtstr=dtstr): + self.assertRaises(ValueError, utils.parsedate_to_datetime, dtstr) class LocaltimeTests(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Library/2020-09-04-17-33-04.bpo-30681.LR4fnY.rst b/Misc/NEWS.d/next/Library/2020-09-04-17-33-04.bpo-30681.LR4fnY.rst new file mode 100644 index 0000000000000..83830e343da66 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-09-04-17-33-04.bpo-30681.LR4fnY.rst @@ -0,0 +1,2 @@ +Handle exceptions caused by unparseable date headers when using email +"default" policy. Patch by Tim Bell, Georges Toth From webhook-mailer at python.org Mon Oct 26 21:24:59 2020 From: webhook-mailer at python.org (vstinner) Date: Tue, 27 Oct 2020 01:24:59 -0000 Subject: [Python-checkins] bpo-42161: Use _PyLong_GetZero() and _PyLong_GetOne() (GH-22995) Message-ID: https://github.com/python/cpython/commit/c9bc290dd6e3994a4ead2a224178bcba86f0c0e4 commit: c9bc290dd6e3994a4ead2a224178bcba86f0c0e4 branch: master author: Victor Stinner committer: vstinner date: 2020-10-27T02:24:34+01:00 summary: bpo-42161: Use _PyLong_GetZero() and _PyLong_GetOne() (GH-22995) Use _PyLong_GetZero() and _PyLong_GetOne() in Objects/ and Python/ directories. files: M Objects/clinic/complexobject.c.h M Objects/clinic/floatobject.c.h M Objects/complexobject.c M Objects/enumobject.c M Objects/floatobject.c M Objects/rangeobject.c M Objects/sliceobject.c M Python/_warnings.c M Python/compile.c diff --git a/Objects/clinic/complexobject.c.h b/Objects/clinic/complexobject.c.h index 4c8191fa83123..557fbf9752faf 100644 --- a/Objects/clinic/complexobject.c.h +++ b/Objects/clinic/complexobject.c.h @@ -90,7 +90,7 @@ complex_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) PyObject * const *fastargs; Py_ssize_t nargs = PyTuple_GET_SIZE(args); Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 0; - PyObject *r = _PyLong_Zero; + PyObject *r = NULL; PyObject *i = NULL; fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 0, 2, 0, argsbuf); @@ -113,4 +113,4 @@ complex_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=193a37aebaaa5f89 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=056cac3226d94967 input=a9049054013a1b77]*/ diff --git a/Objects/clinic/floatobject.c.h b/Objects/clinic/floatobject.c.h index b7554832b5a8a..5643f0e3ac650 100644 --- a/Objects/clinic/floatobject.c.h +++ b/Objects/clinic/floatobject.c.h @@ -206,7 +206,7 @@ static PyObject * float_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; - PyObject *x = _PyLong_Zero; + PyObject *x = NULL; if ((type == &PyFloat_Type) && !_PyArg_NoKeywords("float", kwargs)) { @@ -387,4 +387,4 @@ float___format__(PyObject *self, PyObject *arg) exit: return return_value; } -/*[clinic end generated code: output=25fbbe253f44e2df input=a9049054013a1b77]*/ +/*[clinic end generated code: output=bb079c3e130e4ce6 input=a9049054013a1b77]*/ diff --git a/Objects/complexobject.c b/Objects/complexobject.c index 5ab839a9e9423..a481d9ad8bbaa 100644 --- a/Objects/complexobject.c +++ b/Objects/complexobject.c @@ -6,6 +6,7 @@ /* Submitted by Jim Hugunin */ #include "Python.h" +#include "pycore_long.h" // _PyLong_GetZero() #include "pycore_object.h" // _PyObject_Init() #include "structmember.h" // PyMemberDef @@ -870,7 +871,7 @@ complex_subtype_from_string(PyTypeObject *type, PyObject *v) /*[clinic input] @classmethod complex.__new__ as complex_new - real as r: object(c_default="_PyLong_Zero") = 0 + real as r: object(c_default="NULL") = 0 imag as i: object(c_default="NULL") = 0 Create a complex number from a real part and an optional imaginary part. @@ -880,7 +881,7 @@ This is equivalent to (real + imag*1j) where imag defaults to 0. static PyObject * complex_new_impl(PyTypeObject *type, PyObject *r, PyObject *i) -/*[clinic end generated code: output=b6c7dd577b537dc1 input=6f6b0bedba29bcb5]*/ +/*[clinic end generated code: output=b6c7dd577b537dc1 input=f4c667f2596d4fd1]*/ { PyObject *tmp; PyNumberMethods *nbr, *nbi = NULL; @@ -889,6 +890,10 @@ complex_new_impl(PyTypeObject *type, PyObject *r, PyObject *i) int cr_is_complex = 0; int ci_is_complex = 0; + if (r == NULL) { + r = _PyLong_GetZero(); + } + /* Special-case for a single argument when type(arg) is complex. */ if (PyComplex_CheckExact(r) && i == NULL && type == &PyComplex_Type) { diff --git a/Objects/enumobject.c b/Objects/enumobject.c index 9d8449bb30f2a..8b5e7d3a3c6dd 100644 --- a/Objects/enumobject.c +++ b/Objects/enumobject.c @@ -1,6 +1,7 @@ /* enumerate object */ #include "Python.h" +#include "pycore_long.h" // _PyLong_GetOne() #include "clinic/enumobject.c.h" @@ -115,7 +116,7 @@ enum_next_long(enumobject *en, PyObject* next_item) } next_index = en->en_longindex; assert(next_index != NULL); - stepped_up = PyNumber_Add(next_index, _PyLong_One); + stepped_up = PyNumber_Add(next_index, _PyLong_GetOne()); if (stepped_up == NULL) { Py_DECREF(next_item); return NULL; diff --git a/Objects/floatobject.c b/Objects/floatobject.c index 828bde18df70c..1550b2eedc862 100644 --- a/Objects/floatobject.c +++ b/Objects/floatobject.c @@ -6,6 +6,7 @@ #include "Python.h" #include "pycore_dtoa.h" // _Py_dg_dtoa() #include "pycore_interp.h" // _PyInterpreterState.float_state +#include "pycore_long.h" // _PyLong_GetOne() #include "pycore_object.h" // _PyObject_Init() #include "pycore_pystate.h" // _PyInterpreterState_GET() @@ -504,7 +505,7 @@ float_richcompare(PyObject *v, PyObject *w, int op) Py_DECREF(vv); vv = temp; - temp = PyNumber_Or(vv, _PyLong_One); + temp = PyNumber_Or(vv, _PyLong_GetOne()); if (temp == NULL) goto Error; Py_DECREF(vv); @@ -1605,7 +1606,7 @@ float_subtype_new(PyTypeObject *type, PyObject *x); /*[clinic input] @classmethod float.__new__ as float_new - x: object(c_default="_PyLong_Zero") = 0 + x: object(c_default="NULL") = 0 / Convert a string or number to a floating point number, if possible. @@ -1613,10 +1614,18 @@ Convert a string or number to a floating point number, if possible. static PyObject * float_new_impl(PyTypeObject *type, PyObject *x) -/*[clinic end generated code: output=ccf1e8dc460ba6ba input=540ee77c204ff87a]*/ +/*[clinic end generated code: output=ccf1e8dc460ba6ba input=f43661b7de03e9d8]*/ { - if (type != &PyFloat_Type) + if (type != &PyFloat_Type) { + if (x == NULL) { + x = _PyLong_GetZero(); + } return float_subtype_new(type, x); /* Wimp out */ + } + + if (x == NULL) { + return PyFloat_FromDouble(0.0); + } /* If it's a string, but not a string subclass, use PyFloat_FromString. */ if (PyUnicode_CheckExact(x)) @@ -1662,7 +1671,7 @@ float_vectorcall(PyObject *type, PyObject * const*args, return NULL; } - PyObject *x = nargs >= 1 ? args[0] : _PyLong_Zero; + PyObject *x = nargs >= 1 ? args[0] : NULL; return float_new_impl((PyTypeObject *)type, x); } diff --git a/Objects/rangeobject.c b/Objects/rangeobject.c index babf55b108b9a..787d1138009a0 100644 --- a/Objects/rangeobject.c +++ b/Objects/rangeobject.c @@ -2,6 +2,7 @@ #include "Python.h" #include "pycore_abstract.h" // _PyIndex_Check() +#include "pycore_long.h" // _PyLong_GetZero() #include "pycore_tuple.h" // _PyTuple_ITEMS() #include "structmember.h" // PyMemberDef @@ -105,10 +106,10 @@ range_from_array(PyTypeObject *type, PyObject *const *args, Py_ssize_t num_args) if (!stop) { return NULL; } - Py_INCREF(_PyLong_Zero); - start = _PyLong_Zero; - Py_INCREF(_PyLong_One); - step = _PyLong_One; + start = _PyLong_GetZero(); + Py_INCREF(start); + step = _PyLong_GetOne(); + Py_INCREF(step); break; case 0: PyErr_SetString(PyExc_TypeError, @@ -190,7 +191,10 @@ compute_range_length(PyObject *start, PyObject *stop, PyObject *step) PyObject *tmp1 = NULL, *tmp2 = NULL, *result; /* holds sub-expression evaluations */ - cmp_result = PyObject_RichCompareBool(step, _PyLong_Zero, Py_GT); + PyObject *zero = _PyLong_GetZero(); // borrowed reference + PyObject *one = _PyLong_GetOne(); // borrowed reference + + cmp_result = PyObject_RichCompareBool(step, zero, Py_GT); if (cmp_result == -1) return NULL; @@ -212,19 +216,21 @@ compute_range_length(PyObject *start, PyObject *stop, PyObject *step) Py_DECREF(step); if (cmp_result < 0) return NULL; - return PyLong_FromLong(0); + result = zero; + Py_INCREF(result); + return result; } if ((tmp1 = PyNumber_Subtract(hi, lo)) == NULL) goto Fail; - if ((diff = PyNumber_Subtract(tmp1, _PyLong_One)) == NULL) + if ((diff = PyNumber_Subtract(tmp1, one)) == NULL) goto Fail; if ((tmp2 = PyNumber_FloorDivide(diff, step)) == NULL) goto Fail; - if ((result = PyNumber_Add(tmp2, _PyLong_One)) == NULL) + if ((result = PyNumber_Add(tmp2, one)) == NULL) goto Fail; Py_DECREF(tmp2); @@ -254,7 +260,7 @@ compute_item(rangeobject *r, PyObject *i) /* PyLong equivalent to: * return r->start + (i * r->step) */ - if (r->step == _PyLong_One) { + if (r->step == _PyLong_GetOne()) { result = PyNumber_Add(r->start, i); } else { @@ -271,6 +277,7 @@ compute_item(rangeobject *r, PyObject *i) static PyObject * compute_range_item(rangeobject *r, PyObject *arg) { + PyObject *zero = _PyLong_GetZero(); // borrowed reference int cmp_result; PyObject *i, *result; @@ -281,7 +288,7 @@ compute_range_item(rangeobject *r, PyObject *arg) * i = arg * } */ - cmp_result = PyObject_RichCompareBool(arg, _PyLong_Zero, Py_LT); + cmp_result = PyObject_RichCompareBool(arg, zero, Py_LT); if (cmp_result == -1) { return NULL; } @@ -300,7 +307,7 @@ compute_range_item(rangeobject *r, PyObject *arg) * * } */ - cmp_result = PyObject_RichCompareBool(i, _PyLong_Zero, Py_LT); + cmp_result = PyObject_RichCompareBool(i, zero, Py_LT); if (cmp_result == 0) { cmp_result = PyObject_RichCompareBool(i, r->length, Py_GE); } @@ -375,6 +382,7 @@ compute_slice(rangeobject *r, PyObject *_slice) static int range_contains_long(rangeobject *r, PyObject *ob) { + PyObject *zero = _PyLong_GetZero(); // borrowed reference int cmp1, cmp2, cmp3; PyObject *tmp1 = NULL; PyObject *tmp2 = NULL; @@ -382,7 +390,7 @@ range_contains_long(rangeobject *r, PyObject *ob) /* Check if the value can possibly be in the range. */ - cmp1 = PyObject_RichCompareBool(r->step, _PyLong_Zero, Py_GT); + cmp1 = PyObject_RichCompareBool(r->step, zero, Py_GT); if (cmp1 == -1) goto end; if (cmp1 == 1) { /* positive steps: start <= ob < stop */ @@ -409,7 +417,7 @@ range_contains_long(rangeobject *r, PyObject *ob) if (tmp2 == NULL) goto end; /* result = ((int(ob) - start) % step) == 0 */ - result = PyObject_RichCompareBool(tmp2, _PyLong_Zero, Py_EQ); + result = PyObject_RichCompareBool(tmp2, zero, Py_EQ); end: Py_XDECREF(tmp1); Py_XDECREF(tmp2); @@ -460,7 +468,7 @@ range_equals(rangeobject *r0, rangeobject *r1) /* Return False or error to the caller. */ if (cmp_result != 1) return cmp_result; - cmp_result = PyObject_RichCompareBool(r0->length, _PyLong_One, Py_EQ); + cmp_result = PyObject_RichCompareBool(r0->length, _PyLong_GetOne(), Py_EQ); /* Return True or error to the caller. */ if (cmp_result != 0) return cmp_result; @@ -529,7 +537,7 @@ range_hash(rangeobject *r) else { Py_INCREF(r->start); PyTuple_SET_ITEM(t, 1, r->start); - cmp_result = PyObject_RichCompareBool(r->length, _PyLong_One, Py_EQ); + cmp_result = PyObject_RichCompareBool(r->length, _PyLong_GetOne(), Py_EQ); if (cmp_result == -1) goto end; if (cmp_result == 1) { @@ -587,7 +595,7 @@ range_index(rangeobject *r, PyObject *ob) return NULL; } - if (r->step == _PyLong_One) { + if (r->step == _PyLong_GetOne()) { return idx; } @@ -974,14 +982,15 @@ longrangeiter_reduce(longrangeiterobject *r, PyObject *Py_UNUSED(ignored)) static PyObject * longrangeiter_setstate(longrangeiterobject *r, PyObject *state) { + PyObject *zero = _PyLong_GetZero(); // borrowed reference int cmp; /* clip the value */ - cmp = PyObject_RichCompareBool(state, _PyLong_Zero, Py_LT); + cmp = PyObject_RichCompareBool(state, zero, Py_LT); if (cmp < 0) return NULL; if (cmp > 0) { - state = _PyLong_Zero; + state = zero; } else { cmp = PyObject_RichCompareBool(r->len, state, Py_LT); @@ -1022,7 +1031,7 @@ longrangeiter_next(longrangeiterobject *r) if (PyObject_RichCompareBool(r->index, r->len, Py_LT) != 1) return NULL; - new_index = PyNumber_Add(r->index, _PyLong_One); + new_index = PyNumber_Add(r->index, _PyLong_GetOne()); if (!new_index) return NULL; @@ -1119,7 +1128,7 @@ range_iter(PyObject *seq) it->start = r->start; it->step = r->step; it->len = r->length; - it->index = _PyLong_Zero; + it->index = _PyLong_GetZero(); Py_INCREF(it->start); Py_INCREF(it->step); Py_INCREF(it->len); @@ -1207,7 +1216,7 @@ range_reverse(PyObject *seq, PyObject *Py_UNUSED(ignored)) it->len = range->length; Py_INCREF(it->len); - diff = PyNumber_Subtract(it->len, _PyLong_One); + diff = PyNumber_Subtract(it->len, _PyLong_GetOne()); if (!diff) goto create_failure; @@ -1226,7 +1235,7 @@ range_reverse(PyObject *seq, PyObject *Py_UNUSED(ignored)) if (!it->step) goto create_failure; - it->index = _PyLong_Zero; + it->index = _PyLong_GetZero(); Py_INCREF(it->index); return (PyObject *)it; diff --git a/Objects/sliceobject.c b/Objects/sliceobject.c index e8af623142b84..02ba033a62a49 100644 --- a/Objects/sliceobject.c +++ b/Objects/sliceobject.c @@ -15,6 +15,7 @@ this type and there is exactly one in existence. #include "Python.h" #include "pycore_abstract.h" // _PyIndex_Check() +#include "pycore_long.h" // _PyLong_GetZero() #include "pycore_object.h" // _PyObject_GC_TRACK() #include "structmember.h" // PyMemberDef @@ -388,7 +389,7 @@ _PySlice_GetLongIndices(PySliceObject *self, PyObject *length, /* Convert step to an integer; raise for zero step. */ if (self->step == Py_None) { - step = _PyLong_One; + step = _PyLong_GetOne(); Py_INCREF(step); step_is_negative = 0; } @@ -417,7 +418,7 @@ _PySlice_GetLongIndices(PySliceObject *self, PyObject *length, goto error; } else { - lower = _PyLong_Zero; + lower = _PyLong_GetZero(); Py_INCREF(lower); upper = length; Py_INCREF(upper); diff --git a/Python/_warnings.c b/Python/_warnings.c index 86bbfa1c8db86..271cd47f4eee6 100644 --- a/Python/_warnings.c +++ b/Python/_warnings.c @@ -1,6 +1,7 @@ #include "Python.h" #include "pycore_initconfig.h" #include "pycore_interp.h" // PyInterpreterState.warnings +#include "pycore_long.h" // _PyLong_GetZero() #include "pycore_pyerrors.h" #include "pycore_pystate.h" // _PyThreadState_GET() #include "frameobject.h" // PyFrame_GetBack() @@ -73,7 +74,7 @@ create_filter(PyObject *category, _Py_Identifier *id, const char *modname) /* This assumes the line number is zero for now. */ return PyTuple_Pack(5, action_str, Py_None, - category, modname_obj, _PyLong_Zero); + category, modname_obj, _PyLong_GetZero()); } #endif @@ -472,7 +473,7 @@ update_registry(PyObject *registry, PyObject *text, PyObject *category, int rc; if (add_zero) - altkey = PyTuple_Pack(3, text, category, _PyLong_Zero); + altkey = PyTuple_Pack(3, text, category, _PyLong_GetZero()); else altkey = PyTuple_Pack(2, text, category); diff --git a/Python/compile.c b/Python/compile.c index a0089b2d6dc18..15a9046065b5d 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -22,6 +22,7 @@ */ #include "Python.h" +#include "pycore_long.h" // _PyLong_GetZero() #include "Python-ast.h" #include "ast.h" @@ -603,7 +604,7 @@ compiler_enter_scope(struct compiler *c, identifier name, compiler_unit_free(u); return 0; } - res = PyDict_SetItem(u->u_cellvars, name, _PyLong_Zero); + res = PyDict_SetItem(u->u_cellvars, name, _PyLong_GetZero()); if (res < 0) { compiler_unit_free(u); return 0; @@ -3218,11 +3219,12 @@ compiler_import(struct compiler *c, stmt_ty s) */ Py_ssize_t i, n = asdl_seq_LEN(s->v.Import.names); + PyObject *zero = _PyLong_GetZero(); // borrowed reference for (i = 0; i < n; i++) { alias_ty alias = (alias_ty)asdl_seq_GET(s->v.Import.names, i); int r; - ADDOP_LOAD_CONST(c, _PyLong_Zero); + ADDOP_LOAD_CONST(c, zero); ADDOP_LOAD_CONST(c, Py_None); ADDOP_NAME(c, IMPORT_NAME, alias->name, names); From webhook-mailer at python.org Mon Oct 26 23:36:31 2020 From: webhook-mailer at python.org (vstinner) Date: Tue, 27 Oct 2020 03:36:31 -0000 Subject: [Python-checkins] bpo-42157: Rename unicodedata.ucnhash_CAPI (GH-22994) Message-ID: https://github.com/python/cpython/commit/84f7382215b9e024a5590454726b6ae4b0ca70a0 commit: 84f7382215b9e024a5590454726b6ae4b0ca70a0 branch: master author: Victor Stinner committer: vstinner date: 2020-10-27T04:36:22+01:00 summary: bpo-42157: Rename unicodedata.ucnhash_CAPI (GH-22994) Removed the unicodedata.ucnhash_CAPI attribute which was an internal PyCapsule object. The related private _PyUnicode_Name_CAPI structure was moved to the internal C API. Rename unicodedata.ucnhash_CAPI as unicodedata._ucnhash_CAPI. files: A Misc/NEWS.d/next/Library/2020-10-26-23-29-16.bpo-42157.4wuwTe.rst M Doc/whatsnew/3.10.rst M Include/internal/pycore_ucnhash.h M Misc/NEWS.d/next/C API/2020-10-16-10-47-17.bpo-42157.e3BcPM.rst M Modules/unicodedata.c diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index 2ef2b5d19e585..b2c6d10ba8deb 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -300,6 +300,11 @@ Removed Python 3.5. (Contributed by Berker Peksag in :issue:`31844`.) +* Removed the ``unicodedata.ucnhash_CAPI`` attribute which was an internal + PyCapsule object. The related private ``_PyUnicode_Name_CAPI`` structure was + moved to the internal C API. + (Contributed by Victor Stinner in :issue:`42157`.) + Porting to Python 3.10 ====================== @@ -408,7 +413,7 @@ Porting to Python 3.10 (Contributed by Inada Naoki in :issue:`36346`.) * The private ``_PyUnicode_Name_CAPI`` structure of the PyCapsule API - ``unicodedata.ucnhash_CAPI`` moves to the internal C API. + ``unicodedata.ucnhash_CAPI`` has been moved to the internal C API. (Contributed by Victor Stinner in :issue:`42157`.) Deprecated diff --git a/Include/internal/pycore_ucnhash.h b/Include/internal/pycore_ucnhash.h index 5e7c035f81d2a..187dd68e7347f 100644 --- a/Include/internal/pycore_ucnhash.h +++ b/Include/internal/pycore_ucnhash.h @@ -11,7 +11,7 @@ extern "C" { /* revised ucnhash CAPI interface (exported through a "wrapper") */ -#define PyUnicodeData_CAPSULE_NAME "unicodedata.ucnhash_CAPI" +#define PyUnicodeData_CAPSULE_NAME "unicodedata._ucnhash_CAPI" typedef struct { diff --git a/Misc/NEWS.d/next/C API/2020-10-16-10-47-17.bpo-42157.e3BcPM.rst b/Misc/NEWS.d/next/C API/2020-10-16-10-47-17.bpo-42157.e3BcPM.rst index 1f05186d9e0ef..65a56188fa0ad 100644 --- a/Misc/NEWS.d/next/C API/2020-10-16-10-47-17.bpo-42157.e3BcPM.rst +++ b/Misc/NEWS.d/next/C API/2020-10-16-10-47-17.bpo-42157.e3BcPM.rst @@ -1,3 +1,3 @@ The private ``_PyUnicode_Name_CAPI`` structure of the PyCapsule API -``unicodedata.ucnhash_CAPI`` moves to the internal C API. +``unicodedata.ucnhash_CAPI`` has been moved to the internal C API. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/Library/2020-10-26-23-29-16.bpo-42157.4wuwTe.rst b/Misc/NEWS.d/next/Library/2020-10-26-23-29-16.bpo-42157.4wuwTe.rst new file mode 100644 index 0000000000000..39365677ecd19 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-10-26-23-29-16.bpo-42157.4wuwTe.rst @@ -0,0 +1,3 @@ +Removed the ``unicodedata.ucnhash_CAPI`` attribute which was an internal +PyCapsule object. The related private ``_PyUnicode_Name_CAPI`` structure was +moved to the internal C API. Patch by Victor Stinner. diff --git a/Modules/unicodedata.c b/Modules/unicodedata.c index 18b0a9af9d21c..fcf801dc9e4ad 100644 --- a/Modules/unicodedata.c +++ b/Modules/unicodedata.c @@ -1463,7 +1463,7 @@ unicodedata_exec(PyObject *module) return -1; } - /* Previous versions */ + // Unicode database version 3.2.0 used by the IDNA encoding PyObject *v; v = new_previous_version(ucd_type, "3.2.0", get_change_3_2_0, normalization_3_2_0); @@ -1482,7 +1482,7 @@ unicodedata_exec(PyObject *module) if (v == NULL) { return -1; } - if (PyModule_AddObject(module, "ucnhash_CAPI", v) < 0) { + if (PyModule_AddObject(module, "_ucnhash_CAPI", v) < 0) { Py_DECREF(v); return -1; } From webhook-mailer at python.org Mon Oct 26 23:41:38 2020 From: webhook-mailer at python.org (vstinner) Date: Tue, 27 Oct 2020 03:41:38 -0000 Subject: [Python-checkins] bpo-41474, Makefile: Add dependency on cpython/frameobject.h (GH-22999) Message-ID: https://github.com/python/cpython/commit/a6879d9445f98833c4e300e187956e2a218a2315 commit: a6879d9445f98833c4e300e187956e2a218a2315 branch: master author: Victor Stinner committer: vstinner date: 2020-10-27T04:41:30+01:00 summary: bpo-41474, Makefile: Add dependency on cpython/frameobject.h (GH-22999) Co-Authored-By: Skip Montanaro files: M Makefile.pre.in diff --git a/Makefile.pre.in b/Makefile.pre.in index 31f61f3d5b836..5b6c0b9b62d33 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1081,6 +1081,7 @@ PYTHON_HEADERS= \ $(srcdir)/Include/cpython/dictobject.h \ $(srcdir)/Include/cpython/fileobject.h \ $(srcdir)/Include/cpython/fileutils.h \ + $(srcdir)/Include/cpython/frameobject.h \ $(srcdir)/Include/cpython/import.h \ $(srcdir)/Include/cpython/initconfig.h \ $(srcdir)/Include/cpython/interpreteridobject.h \ From webhook-mailer at python.org Tue Oct 27 00:02:04 2020 From: webhook-mailer at python.org (miss-islington) Date: Tue, 27 Oct 2020 04:02:04 -0000 Subject: [Python-checkins] bpo-41474, Makefile: Add dependency on cpython/frameobject.h (GH-22999) Message-ID: https://github.com/python/cpython/commit/562ad7624e5fe22fdded9b39723c562255a49abd commit: 562ad7624e5fe22fdded9b39723c562255a49abd branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-26T21:01:51-07:00 summary: bpo-41474, Makefile: Add dependency on cpython/frameobject.h (GH-22999) Co-Authored-By: Skip Montanaro (cherry picked from commit a6879d9445f98833c4e300e187956e2a218a2315) Co-authored-by: Victor Stinner files: M Makefile.pre.in diff --git a/Makefile.pre.in b/Makefile.pre.in index 77f91e72b1919..f128444b98594 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1114,6 +1114,7 @@ PYTHON_HEADERS= \ $(srcdir)/Include/cpython/dictobject.h \ $(srcdir)/Include/cpython/fileobject.h \ $(srcdir)/Include/cpython/fileutils.h \ + $(srcdir)/Include/cpython/frameobject.h \ $(srcdir)/Include/cpython/import.h \ $(srcdir)/Include/cpython/initconfig.h \ $(srcdir)/Include/cpython/interpreteridobject.h \ From webhook-mailer at python.org Tue Oct 27 12:13:13 2020 From: webhook-mailer at python.org (vstinner) Date: Tue, 27 Oct 2020 16:13:13 -0000 Subject: [Python-checkins] bpo-42161: Modules/ uses _PyLong_GetZero() and _PyLong_GetOne() (GH-22998) Message-ID: https://github.com/python/cpython/commit/37834136d0afe51d274bfc79d8705514cbe73727 commit: 37834136d0afe51d274bfc79d8705514cbe73727 branch: master author: Victor Stinner committer: vstinner date: 2020-10-27T17:12:53+01:00 summary: bpo-42161: Modules/ uses _PyLong_GetZero() and _PyLong_GetOne() (GH-22998) Use _PyLong_GetZero() and _PyLong_GetOne() in Modules/ directory. _cursesmodule.c and zoneinfo.c are now built with Py_BUILD_CORE_MODULE macro defined. files: M Modules/Setup M Modules/_collectionsmodule.c M Modules/_ctypes/_ctypes.c M Modules/_cursesmodule.c M Modules/_datetimemodule.c M Modules/_functoolsmodule.c M Modules/_io/iobase.c M Modules/_io/textio.c M Modules/_sre.c M Modules/_zoneinfo.c M Modules/clinic/_cursesmodule.c.h M Modules/itertoolsmodule.c M Modules/mathmodule.c M setup.py diff --git a/Modules/Setup b/Modules/Setup index 6f9bb813cef27..a5fbaf6381be5 100644 --- a/Modules/Setup +++ b/Modules/Setup @@ -178,7 +178,7 @@ _symtable symtablemodule.c #_elementtree -I$(srcdir)/Modules/expat -DHAVE_EXPAT_CONFIG_H -DUSE_PYEXPAT_CAPI _elementtree.c # elementtree accelerator #_pickle _pickle.c # pickle accelerator #_datetime _datetimemodule.c # datetime accelerator -#_zoneinfo _zoneinfo.c # zoneinfo accelerator +#_zoneinfo _zoneinfo.c -DPy_BUILD_CORE_MODULE # zoneinfo accelerator #_bisect _bisectmodule.c # Bisection algorithms #_heapq _heapqmodule.c -DPy_BUILD_CORE_MODULE # Heap queue algorithm #_asyncio _asynciomodule.c # Fast asyncio Future @@ -306,7 +306,7 @@ _symtable symtablemodule.c # provided by the ncurses library. e.g. on Linux, link with -lncurses # instead of -lcurses). -#_curses _cursesmodule.c -lcurses -ltermcap +#_curses _cursesmodule.c -lcurses -ltermcap -DPy_BUILD_CORE_MODULE # Wrapper for the panel library that's part of ncurses and SYSV curses. #_curses_panel _curses_panel.c -lpanel -lncurses diff --git a/Modules/_collectionsmodule.c b/Modules/_collectionsmodule.c index 00198ff3eb7dd..8990071f519ea 100644 --- a/Modules/_collectionsmodule.c +++ b/Modules/_collectionsmodule.c @@ -1,4 +1,5 @@ #include "Python.h" +#include "pycore_long.h" // _PyLong_GetZero() #include "structmember.h" // PyMemberDef #ifdef STDC_HEADERS @@ -2323,10 +2324,10 @@ _collections__count_elements_impl(PyObject *module, PyObject *mapping, if (oldval == NULL) { if (PyErr_Occurred()) goto done; - if (_PyDict_SetItem_KnownHash(mapping, key, _PyLong_One, hash) < 0) + if (_PyDict_SetItem_KnownHash(mapping, key, _PyLong_GetOne(), hash) < 0) goto done; } else { - newval = PyNumber_Add(oldval, _PyLong_One); + newval = PyNumber_Add(oldval, _PyLong_GetOne()); if (newval == NULL) goto done; if (_PyDict_SetItem_KnownHash(mapping, key, newval, hash) < 0) @@ -2340,14 +2341,16 @@ _collections__count_elements_impl(PyObject *module, PyObject *mapping, if (bound_get == NULL) goto done; + PyObject *zero = _PyLong_GetZero(); // borrowed reference + PyObject *one = _PyLong_GetOne(); // borrowed reference while (1) { key = PyIter_Next(it); if (key == NULL) break; - oldval = PyObject_CallFunctionObjArgs(bound_get, key, _PyLong_Zero, NULL); + oldval = PyObject_CallFunctionObjArgs(bound_get, key, zero, NULL); if (oldval == NULL) break; - newval = PyNumber_Add(oldval, _PyLong_One); + newval = PyNumber_Add(oldval, one); Py_DECREF(oldval); if (newval == NULL) break; diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 9be90eb27bdf2..8d5594c62c417 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -116,6 +116,8 @@ bytes(cdata) #endif #include "ctypes.h" +#include "pycore_long.h" // _PyLong_GetZero() + PyObject *PyExc_ArgError = NULL; /* This dict maps ctypes types to POINTER types */ @@ -3929,8 +3931,9 @@ _build_callargs(PyCFuncPtrObject *self, PyObject *argtypes, case PARAMFLAG_FIN | PARAMFLAG_FLCID: /* ['in', 'lcid'] parameter. Always taken from defval, if given, else the integer 0. */ - if (defval == NULL) - defval = _PyLong_Zero; + if (defval == NULL) { + defval = _PyLong_GetZero(); + } Py_INCREF(defval); PyTuple_SET_ITEM(callargs, i, defval); break; diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 34331017f85c7..a59858632e76f 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -103,6 +103,7 @@ static const char PyCursesVersion[] = "2.2"; #define PY_SSIZE_T_CLEAN #include "Python.h" +#include "pycore_long.h" // _PyLong_GetZero() #ifdef __hpux @@ -1094,9 +1095,9 @@ _curses_window_border_impl(PyCursesWindowObject *self, PyObject *ls, _curses.window.box [ - verch: object(c_default="_PyLong_Zero") = 0 + verch: object(c_default="_PyLong_GetZero()") = 0 Left and right side. - horch: object(c_default="_PyLong_Zero") = 0 + horch: object(c_default="_PyLong_GetZero()") = 0 Top and bottom side. ] / @@ -1110,7 +1111,7 @@ horch. The default corner characters are always used by this function. static PyObject * _curses_window_box_impl(PyCursesWindowObject *self, int group_right_1, PyObject *verch, PyObject *horch) -/*[clinic end generated code: output=f3fcb038bb287192 input=465a121741c1efdf]*/ +/*[clinic end generated code: output=f3fcb038bb287192 input=f00435f9c8c98f60]*/ { chtype ch1 = 0, ch2 = 0; if (group_right_1) { diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 94868717e6a04..e59f89b3d10fb 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -8,6 +8,7 @@ #define _PY_DATETIME_IMPL #include "Python.h" +#include "pycore_long.h" // _PyLong_GetOne() #include "pycore_object.h" // _PyObject_Init() #include "datetime.h" #include "structmember.h" // PyMemberDef @@ -2448,7 +2449,7 @@ delta_new(PyTypeObject *type, PyObject *args, PyObject *kw) goto Done if (us) { - y = accum("microseconds", x, us, _PyLong_One, &leftover_us); + y = accum("microseconds", x, us, _PyLong_GetOne(), &leftover_us); CLEANUP; } if (ms) { @@ -2487,7 +2488,7 @@ delta_new(PyTypeObject *type, PyObject *args, PyObject *kw) * is odd. Note that x is odd when it's last bit is 1. The * code below uses bitwise and operation to check the last * bit. */ - temp = PyNumber_And(x, _PyLong_One); /* temp <- x & 1 */ + temp = PyNumber_And(x, _PyLong_GetOne()); /* temp <- x & 1 */ if (temp == NULL) { Py_DECREF(x); goto Done; diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index bb86fe862da6d..9fad21fc33213 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -1,4 +1,5 @@ #include "Python.h" +#include "pycore_long.h" // _PyLong_GetZero() #include "pycore_pystate.h" // _PyThreadState_GET() #include "pycore_tuple.h" // _PyTuple_ITEMS() #include "structmember.h" // PyMemberDef @@ -596,7 +597,7 @@ keyobject_richcompare(PyObject *ko, PyObject *other, int op) return NULL; } - answer = PyObject_RichCompare(res, _PyLong_Zero, op); + answer = PyObject_RichCompare(res, _PyLong_GetZero(), op); Py_DECREF(res); return answer; } diff --git a/Modules/_io/iobase.c b/Modules/_io/iobase.c index 195862df5dc06..5b687b78176e8 100644 --- a/Modules/_io/iobase.c +++ b/Modules/_io/iobase.c @@ -10,6 +10,7 @@ #define PY_SSIZE_T_CLEAN #include "Python.h" +#include "pycore_long.h" // _PyLong_GetOne() #include "pycore_object.h" #include // offsetof() #include "_iomodule.h" @@ -556,7 +557,7 @@ _io__IOBase_readline_impl(PyObject *self, Py_ssize_t limit) PyObject *b; if (peek != NULL) { - PyObject *readahead = PyObject_CallOneArg(peek, _PyLong_One); + PyObject *readahead = PyObject_CallOneArg(peek, _PyLong_GetOne()); if (readahead == NULL) { /* NOTE: PyErr_SetFromErrno() calls PyErr_CheckSignals() when EINTR occurs so we needn't do it ourselves. */ diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c index f2c72ebd51658..699b7e94c93bb 100644 --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -9,6 +9,7 @@ #define PY_SSIZE_T_CLEAN #include "Python.h" #include "pycore_interp.h" // PyInterpreterState.fs_codec +#include "pycore_long.h" // _PyLong_GetZero() #include "pycore_object.h" #include "pycore_pystate.h" // _PyInterpreterState_GET() #include "structmember.h" // PyMemberDef @@ -971,7 +972,7 @@ _textiowrapper_fix_encoder_state(textio *self) return -1; } - int cmp = PyObject_RichCompareBool(cookieObj, _PyLong_Zero, Py_EQ); + int cmp = PyObject_RichCompareBool(cookieObj, _PyLong_GetZero(), Py_EQ); Py_DECREF(cookieObj); if (cmp < 0) { return -1; @@ -980,7 +981,7 @@ _textiowrapper_fix_encoder_state(textio *self) if (cmp == 0) { self->encoding_start_of_stream = 0; PyObject *res = PyObject_CallMethodOneArg( - self->encoder, _PyIO_str_setstate, _PyLong_Zero); + self->encoder, _PyIO_str_setstate, _PyLong_GetZero()); if (res == NULL) { return -1; } @@ -2415,7 +2416,7 @@ _textiowrapper_encoder_reset(textio *self, int start_of_stream) } else { res = PyObject_CallMethodOneArg(self->encoder, _PyIO_str_setstate, - _PyLong_Zero); + _PyLong_GetZero()); self->encoding_start_of_stream = 0; } if (res == NULL) @@ -2459,10 +2460,12 @@ _io_TextIOWrapper_seek_impl(textio *self, PyObject *cookieObj, int whence) goto fail; } + PyObject *zero = _PyLong_GetZero(); // borrowed reference + switch (whence) { case SEEK_CUR: /* seek relative to current position */ - cmp = PyObject_RichCompareBool(cookieObj, _PyLong_Zero, Py_EQ); + cmp = PyObject_RichCompareBool(cookieObj, zero, Py_EQ); if (cmp < 0) goto fail; @@ -2482,7 +2485,7 @@ _io_TextIOWrapper_seek_impl(textio *self, PyObject *cookieObj, int whence) case SEEK_END: /* seek relative to end of file */ - cmp = PyObject_RichCompareBool(cookieObj, _PyLong_Zero, Py_EQ); + cmp = PyObject_RichCompareBool(cookieObj, zero, Py_EQ); if (cmp < 0) goto fail; @@ -2511,7 +2514,7 @@ _io_TextIOWrapper_seek_impl(textio *self, PyObject *cookieObj, int whence) goto fail; if (self->encoder) { /* If seek() == 0, we are at the start of stream, otherwise not */ - cmp = PyObject_RichCompareBool(res, _PyLong_Zero, Py_EQ); + cmp = PyObject_RichCompareBool(res, zero, Py_EQ); if (cmp < 0 || _textiowrapper_encoder_reset(self, cmp)) { Py_DECREF(res); goto fail; @@ -2529,7 +2532,7 @@ _io_TextIOWrapper_seek_impl(textio *self, PyObject *cookieObj, int whence) goto fail; } - cmp = PyObject_RichCompareBool(cookieObj, _PyLong_Zero, Py_LT); + cmp = PyObject_RichCompareBool(cookieObj, zero, Py_LT); if (cmp < 0) goto fail; diff --git a/Modules/_sre.c b/Modules/_sre.c index 70bd8baa01e20..fbabeb7c9f305 100644 --- a/Modules/_sre.c +++ b/Modules/_sre.c @@ -41,6 +41,7 @@ static const char copyright[] = #define PY_SSIZE_T_CLEAN #include "Python.h" +#include "pycore_long.h" // _PyLong_GetZero() #include "structmember.h" // PyMemberDef #include "sre.h" @@ -1999,7 +2000,7 @@ match_group(MatchObject* self, PyObject* args) switch (size) { case 0: - result = match_getslice(self, _PyLong_Zero, Py_None); + result = match_getslice(self, _PyLong_GetZero(), Py_None); break; case 1: result = match_getslice(self, PyTuple_GET_ITEM(args, 0), Py_None); diff --git a/Modules/_zoneinfo.c b/Modules/_zoneinfo.c index 76b667d1543e9..7888cf86de0a5 100644 --- a/Modules/_zoneinfo.c +++ b/Modules/_zoneinfo.c @@ -1,4 +1,5 @@ #include "Python.h" +#include "pycore_long.h" // _PyLong_GetOne() #include "structmember.h" #include @@ -585,7 +586,7 @@ zoneinfo_fromutc(PyObject *obj_self, PyObject *dt) } dt = NULL; - if (!PyDict_SetItemString(kwargs, "fold", _PyLong_One)) { + if (!PyDict_SetItemString(kwargs, "fold", _PyLong_GetOne())) { dt = PyObject_Call(replace, args, kwargs); } diff --git a/Modules/clinic/_cursesmodule.c.h b/Modules/clinic/_cursesmodule.c.h index c4c2b71e4cc22..34e09e443afff 100644 --- a/Modules/clinic/_cursesmodule.c.h +++ b/Modules/clinic/_cursesmodule.c.h @@ -509,8 +509,8 @@ _curses_window_box(PyCursesWindowObject *self, PyObject *args) { PyObject *return_value = NULL; int group_right_1 = 0; - PyObject *verch = _PyLong_Zero; - PyObject *horch = _PyLong_Zero; + PyObject *verch = _PyLong_GetZero(); + PyObject *horch = _PyLong_GetZero(); switch (PyTuple_GET_SIZE(args)) { case 0: @@ -4288,4 +4288,4 @@ _curses_has_extended_color_support(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=38b2531d17f119e1 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=92bad2172fef9747 input=a9049054013a1b77]*/ diff --git a/Modules/itertoolsmodule.c b/Modules/itertoolsmodule.c index 3809dc3843c14..ce8b4347ef220 100644 --- a/Modules/itertoolsmodule.c +++ b/Modules/itertoolsmodule.c @@ -1,6 +1,7 @@ #define PY_SSIZE_T_CLEAN #include "Python.h" +#include "pycore_long.h" // _PyLong_GetZero() #include "pycore_tuple.h" // _PyTuple_ITEMS() #include // offsetof() @@ -4040,13 +4041,14 @@ itertools_count_impl(PyTypeObject *type, PyObject *long_cnt, } } else { cnt = 0; - long_cnt = _PyLong_Zero; + long_cnt = _PyLong_GetZero(); } Py_INCREF(long_cnt); /* If not specified, step defaults to 1 */ - if (long_step == NULL) - long_step = _PyLong_One; + if (long_step == NULL) { + long_step = _PyLong_GetOne(); + } Py_INCREF(long_step); assert(long_cnt != NULL && long_step != NULL); diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 45b03028753a3..86b64fb422690 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -55,6 +55,7 @@ raised for division by zero and mod by zero. #include "Python.h" #include "pycore_bitutils.h" // _Py_bit_length() #include "pycore_dtoa.h" +#include "pycore_long.h" // _PyLong_GetZero() #include "_math.h" #include "clinic/mathmodule.c.h" @@ -850,7 +851,7 @@ math_gcd(PyObject *module, PyObject * const *args, Py_ssize_t nargs) Py_DECREF(res); return NULL; } - if (res == _PyLong_One) { + if (res == _PyLong_GetOne()) { /* Fast path: just check arguments. It is okay to use identity comparison here. */ Py_DECREF(x); @@ -923,7 +924,7 @@ math_lcm(PyObject *module, PyObject * const *args, Py_ssize_t nargs) Py_DECREF(res); return NULL; } - if (res == _PyLong_Zero) { + if (res == _PyLong_GetZero()) { /* Fast path: just check arguments. It is okay to use identity comparison here. */ Py_DECREF(x); @@ -1837,7 +1838,7 @@ math_isqrt(PyObject *module, PyObject *n) } if (a_too_large) { - Py_SETREF(a, PyNumber_Subtract(a, _PyLong_One)); + Py_SETREF(a, PyNumber_Subtract(a, _PyLong_GetOne())); } Py_DECREF(n); return a; @@ -3295,7 +3296,7 @@ math_perm_impl(PyObject *module, PyObject *n, PyObject *k) factor = n; Py_INCREF(factor); for (i = 1; i < factors; ++i) { - Py_SETREF(factor, PyNumber_Subtract(factor, _PyLong_One)); + Py_SETREF(factor, PyNumber_Subtract(factor, _PyLong_GetOne())); if (factor == NULL) { goto error; } @@ -3417,7 +3418,7 @@ math_comb_impl(PyObject *module, PyObject *n, PyObject *k) factor = n; Py_INCREF(factor); for (i = 1; i < factors; ++i) { - Py_SETREF(factor, PyNumber_Subtract(factor, _PyLong_One)); + Py_SETREF(factor, PyNumber_Subtract(factor, _PyLong_GetOne())); if (factor == NULL) { goto error; } diff --git a/setup.py b/setup.py index 8a4abe5a648fd..b3f47603f7ad6 100644 --- a/setup.py +++ b/setup.py @@ -856,7 +856,8 @@ def detect_simple_extensions(self): libraries=['m'], extra_compile_args=['-DPy_BUILD_CORE_MODULE'])) # zoneinfo module - self.add(Extension('_zoneinfo', ['_zoneinfo.c'])), + self.add(Extension('_zoneinfo', ['_zoneinfo.c'], + extra_compile_args=['-DPy_BUILD_CORE_MODULE'])) # random number generator implemented in C self.add(Extension("_random", ["_randommodule.c"], extra_compile_args=['-DPy_BUILD_CORE_MODULE'])) @@ -1094,6 +1095,7 @@ def detect_readline_curses(self): if curses_library.startswith('ncurses'): curses_libs = [curses_library] self.add(Extension('_curses', ['_cursesmodule.c'], + extra_compile_args=['-DPy_BUILD_CORE_MODULE'], include_dirs=curses_includes, define_macros=curses_defines, libraries=curses_libs)) @@ -1108,6 +1110,7 @@ def detect_readline_curses(self): curses_libs = ['curses'] self.add(Extension('_curses', ['_cursesmodule.c'], + extra_compile_args=['-DPy_BUILD_CORE_MODULE'], define_macros=curses_defines, libraries=curses_libs)) else: From webhook-mailer at python.org Tue Oct 27 12:19:06 2020 From: webhook-mailer at python.org (vstinner) Date: Tue, 27 Oct 2020 16:19:06 -0000 Subject: [Python-checkins] bpo-6761: Enhance __call__ documentation (GH-7987) Message-ID: https://github.com/python/cpython/commit/95f710c55714153f0c8cce48f8215bb3d866ac1d commit: 95f710c55714153f0c8cce48f8215bb3d866ac1d branch: master author: Andre Delfino committer: vstinner date: 2020-10-27T17:18:57+01:00 summary: bpo-6761: Enhance __call__ documentation (GH-7987) files: M Doc/reference/datamodel.rst diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index d92e19761a965..c882301ad3a4a 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -2168,7 +2168,7 @@ Emulating callable objects .. index:: pair: call; instance Called when the instance is "called" as a function; if this method is defined, - ``x(arg1, arg2, ...)`` is a shorthand for ``x.__call__(arg1, arg2, ...)``. + ``x(arg1, arg2, ...)`` roughly translates to ``type(x).__call__(x, arg1, ...)``. .. _sequence-types: From webhook-mailer at python.org Tue Oct 27 12:28:10 2020 From: webhook-mailer at python.org (miss-islington) Date: Tue, 27 Oct 2020 16:28:10 -0000 Subject: [Python-checkins] bpo-6761: Enhance __call__ documentation (GH-7987) Message-ID: https://github.com/python/cpython/commit/b1ce0440bfe87e092ca5e2e57875fb7fc1129137 commit: b1ce0440bfe87e092ca5e2e57875fb7fc1129137 branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-27T09:28:00-07:00 summary: bpo-6761: Enhance __call__ documentation (GH-7987) (cherry picked from commit 95f710c55714153f0c8cce48f8215bb3d866ac1d) Co-authored-by: Andre Delfino files: M Doc/reference/datamodel.rst diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index da38bba91b6b9..083d38cc9f160 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -2163,7 +2163,7 @@ Emulating callable objects .. index:: pair: call; instance Called when the instance is "called" as a function; if this method is defined, - ``x(arg1, arg2, ...)`` is a shorthand for ``x.__call__(arg1, arg2, ...)``. + ``x(arg1, arg2, ...)`` roughly translates to ``type(x).__call__(x, arg1, ...)``. .. _sequence-types: From webhook-mailer at python.org Tue Oct 27 12:42:47 2020 From: webhook-mailer at python.org (miss-islington) Date: Tue, 27 Oct 2020 16:42:47 -0000 Subject: [Python-checkins] bpo-6761: Enhance __call__ documentation (GH-7987) Message-ID: https://github.com/python/cpython/commit/2cb259fcf3cde56f359c2f393280689784dcc834 commit: 2cb259fcf3cde56f359c2f393280689784dcc834 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-27T09:42:36-07:00 summary: bpo-6761: Enhance __call__ documentation (GH-7987) (cherry picked from commit 95f710c55714153f0c8cce48f8215bb3d866ac1d) Co-authored-by: Andre Delfino files: M Doc/reference/datamodel.rst diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index c527719c40d06..89063876ccc9e 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -2168,7 +2168,7 @@ Emulating callable objects .. index:: pair: call; instance Called when the instance is "called" as a function; if this method is defined, - ``x(arg1, arg2, ...)`` is a shorthand for ``x.__call__(arg1, arg2, ...)``. + ``x(arg1, arg2, ...)`` roughly translates to ``type(x).__call__(x, arg1, ...)``. .. _sequence-types: From webhook-mailer at python.org Tue Oct 27 14:54:30 2020 From: webhook-mailer at python.org (lysnikolaou) Date: Tue, 27 Oct 2020 18:54:30 -0000 Subject: [Python-checkins] bpo-41659: Disallow curly brace directly after primary (GH-22996) Message-ID: https://github.com/python/cpython/commit/15acc4eaba8519d7d5f2acaffde65446b44dcf79 commit: 15acc4eaba8519d7d5f2acaffde65446b44dcf79 branch: master author: Lysandros Nikolaou committer: lysnikolaou date: 2020-10-27T20:54:20+02:00 summary: bpo-41659: Disallow curly brace directly after primary (GH-22996) files: A Misc/NEWS.d/next/Core and Builtins/2020-10-27-18-32-49.bpo-41659.d4a-8o.rst M Grammar/python.gram M Lib/test/test_exceptions.py M Lib/test/test_syntax.py M Parser/parser.c diff --git a/Grammar/python.gram b/Grammar/python.gram index 19c85accf8d9a..b8da554b8ec99 100644 --- a/Grammar/python.gram +++ b/Grammar/python.gram @@ -475,6 +475,7 @@ await_primary[expr_ty] (memo): | AWAIT a=primary { CHECK_VERSION(expr_ty, 5, "Await expressions are", _Py_Await(a, EXTRA)) } | primary primary[expr_ty]: + | invalid_primary # must be before 'primay genexp' because of invalid_genexp | a=primary '.' b=NAME { _Py_Attribute(a, b->v.Name.id, Load, EXTRA) } | a=primary b=genexp { _Py_Call(a, CHECK(asdl_expr_seq*, (asdl_expr_seq*)_PyPegen_singleton_seq(p, b)), NULL, EXTRA) } | a=primary '(' b=[arguments] ')' { @@ -682,6 +683,8 @@ invalid_del_stmt: RAISE_SYNTAX_ERROR_INVALID_TARGET(DEL_TARGETS, a) } invalid_block: | NEWLINE !INDENT { RAISE_INDENTATION_ERROR("expected an indented block") } +invalid_primary: + | primary a='{' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "invalid syntax") } invalid_comprehension: | ('[' | '(' | '{') a=starred_expression for_if_clauses { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "iterable unpacking cannot be used in comprehension") } diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index 1ec446887770e..4dbf5fe5d5bc3 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -208,6 +208,7 @@ def testSyntaxErrorOffset(self): check(b'Python = "\xcf\xb3\xf2\xee\xed" +', 1, 18) check('x = "a', 1, 7) check('lambda x: x = 2', 1, 1) + check('f{a + b + c}', 1, 2) # Errors thrown by compile.c check('class foo:return 1', 1, 11) diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index 7c3302c1d46ae..c25b85246b919 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -802,6 +802,9 @@ def _check_error(self, code, errtext, else: self.fail("compile() did not raise SyntaxError") + def test_curly_brace_after_primary_raises_immediately(self): + self._check_error("f{", "invalid syntax", mode="single") + def test_assign_call(self): self._check_error("f() = 1", "assign") diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-10-27-18-32-49.bpo-41659.d4a-8o.rst b/Misc/NEWS.d/next/Core and Builtins/2020-10-27-18-32-49.bpo-41659.d4a-8o.rst new file mode 100644 index 0000000000000..038749a7b16c9 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2020-10-27-18-32-49.bpo-41659.d4a-8o.rst @@ -0,0 +1,3 @@ +Fix a bug in the parser, where a curly brace following a `primary` didn't fail immediately. +This led to invalid expressions like `a {b}` to throw a :exc:`SyntaxError` with a wrong offset, +or invalid expressions ending with a curly brace like `a {` to not fail immediately in the REPL. \ No newline at end of file diff --git a/Parser/parser.c b/Parser/parser.c index e438f06c9be9a..a22cf2752d18d 100644 --- a/Parser/parser.c +++ b/Parser/parser.c @@ -216,173 +216,174 @@ static KeywordToken *reserved_keywords[] = { #define invalid_ann_assign_target_type 1147 #define invalid_del_stmt_type 1148 #define invalid_block_type 1149 -#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 _loop0_71_type 1230 -#define _gather_70_type 1231 -#define _loop1_72_type 1232 -#define _loop0_74_type 1233 -#define _gather_73_type 1234 -#define _loop1_75_type 1235 -#define _loop0_76_type 1236 -#define _loop0_77_type 1237 -#define _loop0_78_type 1238 -#define _loop1_79_type 1239 -#define _loop0_80_type 1240 -#define _loop1_81_type 1241 -#define _loop1_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 _loop0_88_type 1248 -#define _loop1_89_type 1249 -#define _loop1_90_type 1250 -#define _loop1_91_type 1251 -#define _loop1_92_type 1252 -#define _tmp_93_type 1253 -#define _loop0_95_type 1254 -#define _gather_94_type 1255 -#define _tmp_96_type 1256 -#define _tmp_97_type 1257 -#define _tmp_98_type 1258 -#define _tmp_99_type 1259 -#define _loop1_100_type 1260 -#define _tmp_101_type 1261 -#define _tmp_102_type 1262 -#define _loop0_104_type 1263 -#define _gather_103_type 1264 -#define _loop1_105_type 1265 -#define _loop0_106_type 1266 -#define _loop0_107_type 1267 -#define _loop0_109_type 1268 -#define _gather_108_type 1269 -#define _tmp_110_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_118_type 1277 -#define _gather_117_type 1278 -#define _loop0_119_type 1279 -#define _loop0_121_type 1280 -#define _gather_120_type 1281 -#define _tmp_122_type 1282 -#define _loop0_124_type 1283 -#define _gather_123_type 1284 -#define _loop0_126_type 1285 -#define _gather_125_type 1286 -#define _tmp_127_type 1287 -#define _loop0_128_type 1288 -#define _loop0_129_type 1289 -#define _loop0_130_type 1290 -#define _tmp_131_type 1291 -#define _tmp_132_type 1292 -#define _loop0_133_type 1293 -#define _tmp_134_type 1294 -#define _loop0_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 _tmp_151_type 1311 -#define _tmp_152_type 1312 -#define _loop1_153_type 1313 -#define _loop1_154_type 1314 -#define _tmp_155_type 1315 -#define _tmp_156_type 1316 +#define invalid_primary_type 1150 // Left-recursive +#define invalid_comprehension_type 1151 +#define invalid_dict_comprehension_type 1152 +#define invalid_parameters_type 1153 +#define invalid_lambda_parameters_type 1154 +#define invalid_star_etc_type 1155 +#define invalid_lambda_star_etc_type 1156 +#define invalid_double_type_comments_type 1157 +#define invalid_with_item_type 1158 +#define invalid_for_target_type 1159 +#define invalid_group_type 1160 +#define invalid_import_from_targets_type 1161 +#define _loop0_1_type 1162 +#define _loop0_2_type 1163 +#define _loop0_4_type 1164 +#define _gather_3_type 1165 +#define _loop0_6_type 1166 +#define _gather_5_type 1167 +#define _loop0_8_type 1168 +#define _gather_7_type 1169 +#define _loop0_10_type 1170 +#define _gather_9_type 1171 +#define _loop1_11_type 1172 +#define _loop0_13_type 1173 +#define _gather_12_type 1174 +#define _tmp_14_type 1175 +#define _tmp_15_type 1176 +#define _tmp_16_type 1177 +#define _tmp_17_type 1178 +#define _tmp_18_type 1179 +#define _tmp_19_type 1180 +#define _tmp_20_type 1181 +#define _tmp_21_type 1182 +#define _loop1_22_type 1183 +#define _tmp_23_type 1184 +#define _tmp_24_type 1185 +#define _loop0_26_type 1186 +#define _gather_25_type 1187 +#define _loop0_28_type 1188 +#define _gather_27_type 1189 +#define _tmp_29_type 1190 +#define _tmp_30_type 1191 +#define _loop0_31_type 1192 +#define _loop1_32_type 1193 +#define _loop0_34_type 1194 +#define _gather_33_type 1195 +#define _tmp_35_type 1196 +#define _loop0_37_type 1197 +#define _gather_36_type 1198 +#define _tmp_38_type 1199 +#define _loop0_40_type 1200 +#define _gather_39_type 1201 +#define _loop0_42_type 1202 +#define _gather_41_type 1203 +#define _loop0_44_type 1204 +#define _gather_43_type 1205 +#define _loop0_46_type 1206 +#define _gather_45_type 1207 +#define _tmp_47_type 1208 +#define _loop1_48_type 1209 +#define _tmp_49_type 1210 +#define _tmp_50_type 1211 +#define _tmp_51_type 1212 +#define _tmp_52_type 1213 +#define _tmp_53_type 1214 +#define _loop0_54_type 1215 +#define _loop0_55_type 1216 +#define _loop0_56_type 1217 +#define _loop1_57_type 1218 +#define _loop0_58_type 1219 +#define _loop1_59_type 1220 +#define _loop1_60_type 1221 +#define _loop1_61_type 1222 +#define _loop0_62_type 1223 +#define _loop1_63_type 1224 +#define _loop0_64_type 1225 +#define _loop1_65_type 1226 +#define _loop0_66_type 1227 +#define _loop1_67_type 1228 +#define _loop1_68_type 1229 +#define _tmp_69_type 1230 +#define _loop0_71_type 1231 +#define _gather_70_type 1232 +#define _loop1_72_type 1233 +#define _loop0_74_type 1234 +#define _gather_73_type 1235 +#define _loop1_75_type 1236 +#define _loop0_76_type 1237 +#define _loop0_77_type 1238 +#define _loop0_78_type 1239 +#define _loop1_79_type 1240 +#define _loop0_80_type 1241 +#define _loop1_81_type 1242 +#define _loop1_82_type 1243 +#define _loop1_83_type 1244 +#define _loop0_84_type 1245 +#define _loop1_85_type 1246 +#define _loop0_86_type 1247 +#define _loop1_87_type 1248 +#define _loop0_88_type 1249 +#define _loop1_89_type 1250 +#define _loop1_90_type 1251 +#define _loop1_91_type 1252 +#define _loop1_92_type 1253 +#define _tmp_93_type 1254 +#define _loop0_95_type 1255 +#define _gather_94_type 1256 +#define _tmp_96_type 1257 +#define _tmp_97_type 1258 +#define _tmp_98_type 1259 +#define _tmp_99_type 1260 +#define _loop1_100_type 1261 +#define _tmp_101_type 1262 +#define _tmp_102_type 1263 +#define _loop0_104_type 1264 +#define _gather_103_type 1265 +#define _loop1_105_type 1266 +#define _loop0_106_type 1267 +#define _loop0_107_type 1268 +#define _loop0_109_type 1269 +#define _gather_108_type 1270 +#define _tmp_110_type 1271 +#define _loop0_112_type 1272 +#define _gather_111_type 1273 +#define _loop0_114_type 1274 +#define _gather_113_type 1275 +#define _loop0_116_type 1276 +#define _gather_115_type 1277 +#define _loop0_118_type 1278 +#define _gather_117_type 1279 +#define _loop0_119_type 1280 +#define _loop0_121_type 1281 +#define _gather_120_type 1282 +#define _tmp_122_type 1283 +#define _loop0_124_type 1284 +#define _gather_123_type 1285 +#define _loop0_126_type 1286 +#define _gather_125_type 1287 +#define _tmp_127_type 1288 +#define _loop0_128_type 1289 +#define _loop0_129_type 1290 +#define _loop0_130_type 1291 +#define _tmp_131_type 1292 +#define _tmp_132_type 1293 +#define _loop0_133_type 1294 +#define _tmp_134_type 1295 +#define _loop0_135_type 1296 +#define _tmp_136_type 1297 +#define _tmp_137_type 1298 +#define _tmp_138_type 1299 +#define _tmp_139_type 1300 +#define _tmp_140_type 1301 +#define _tmp_141_type 1302 +#define _tmp_142_type 1303 +#define _tmp_143_type 1304 +#define _tmp_144_type 1305 +#define _tmp_145_type 1306 +#define _tmp_146_type 1307 +#define _tmp_147_type 1308 +#define _tmp_148_type 1309 +#define _tmp_149_type 1310 +#define _tmp_150_type 1311 +#define _tmp_151_type 1312 +#define _tmp_152_type 1313 +#define _loop1_153_type 1314 +#define _loop1_154_type 1315 +#define _tmp_155_type 1316 +#define _tmp_156_type 1317 static mod_ty file_rule(Parser *p); static mod_ty interactive_rule(Parser *p); @@ -534,6 +535,7 @@ static void *invalid_assignment_rule(Parser *p); static expr_ty invalid_ann_assign_target_rule(Parser *p); static void *invalid_del_stmt_rule(Parser *p); static void *invalid_block_rule(Parser *p); +static void *invalid_primary_rule(Parser *p); static void *invalid_comprehension_rule(Parser *p); static void *invalid_dict_comprehension_rule(Parser *p); static void *invalid_parameters_rule(Parser *p); @@ -10275,6 +10277,7 @@ await_primary_rule(Parser *p) // Left-recursive // primary: +// | invalid_primary // | primary '.' NAME // | primary genexp // | primary '(' arguments? ')' @@ -10328,6 +10331,25 @@ primary_raw(Parser *p) 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 + if (p->call_invalid_rules) { // invalid_primary + if (p->error_indicator) { + D(p->level--); + return NULL; + } + D(fprintf(stderr, "%*c> primary[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "invalid_primary")); + void *invalid_primary_var; + if ( + (invalid_primary_var = invalid_primary_rule(p)) // invalid_primary + ) + { + D(fprintf(stderr, "%*c+ primary[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "invalid_primary")); + _res = invalid_primary_var; + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s primary[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "invalid_primary")); + } { // primary '.' NAME if (p->error_indicator) { D(p->level--); @@ -15028,6 +15050,51 @@ invalid_block_rule(Parser *p) return _res; } +// Left-recursive +// invalid_primary: primary '{' +static void * +invalid_primary_rule(Parser *p) +{ + D(p->level++); + if (p->error_indicator) { + D(p->level--); + return NULL; + } + void * _res = NULL; + int _mark = p->mark; + { // primary '{' + if (p->error_indicator) { + D(p->level--); + return NULL; + } + D(fprintf(stderr, "%*c> invalid_primary[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "primary '{'")); + Token * a; + expr_ty primary_var; + if ( + (primary_var = primary_rule(p)) // primary + && + (a = _PyPegen_expect_token(p, 25)) // token='{' + ) + { + D(fprintf(stderr, "%*c+ invalid_primary[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "primary '{'")); + _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "invalid syntax" ); + if (_res == NULL && PyErr_Occurred()) { + p->error_indicator = 1; + D(p->level--); + return NULL; + } + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s invalid_primary[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "primary '{'")); + } + _res = NULL; + done: + D(p->level--); + return _res; +} + // invalid_comprehension: ('[' | '(' | '{') starred_expression for_if_clauses static void * invalid_comprehension_rule(Parser *p) From webhook-mailer at python.org Tue Oct 27 14:56:03 2020 From: webhook-mailer at python.org (markshannon) Date: Tue, 27 Oct 2020 18:56:03 -0000 Subject: [Python-checkins] bpo-42099: Fix reference to ob_type in unionobject.c and ceval (GH-22829) Message-ID: https://github.com/python/cpython/commit/0564aafb71a153dd0aca4b9266dfae9336a4f2cb commit: 0564aafb71a153dd0aca4b9266dfae9336a4f2cb branch: master author: Neil Schemenauer committer: markshannon date: 2020-10-27T18:55:52Z summary: bpo-42099: Fix reference to ob_type in unionobject.c and ceval (GH-22829) * Use Py_TYPE() rather than o->ob_type. files: M Objects/unionobject.c M Python/ceval.c diff --git a/Objects/unionobject.c b/Objects/unionobject.c index 89fdaf42560c1..1b7f8ab51a4ce 100644 --- a/Objects/unionobject.c +++ b/Objects/unionobject.c @@ -15,7 +15,7 @@ unionobject_dealloc(PyObject *self) unionobject *alias = (unionobject *)self; Py_XDECREF(alias->args); - self->ob_type->tp_free(self); + Py_TYPE(self)->tp_free(self); } static Py_hash_t diff --git a/Python/ceval.c b/Python/ceval.c index 7338be57798fd..13b209fc706b6 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3193,7 +3193,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) descr = _PyType_Lookup(type, name); if (descr == NULL || - descr->ob_type->tp_descr_get == NULL || + Py_TYPE(descr)->tp_descr_get == NULL || !PyDescr_IsData(descr)) { dictptr = (PyObject **) ((char *)owner + type->tp_dictoffset); From webhook-mailer at python.org Tue Oct 27 16:34:47 2020 From: webhook-mailer at python.org (vstinner) Date: Tue, 27 Oct 2020 20:34:47 -0000 Subject: [Python-checkins] bpo-42161: Remove private _PyLong_Zero and _PyLong_One (GH-23003) Message-ID: https://github.com/python/cpython/commit/c310185c081110741fae914c06c7aaf673ad3d0d commit: c310185c081110741fae914c06c7aaf673ad3d0d branch: master author: Victor Stinner committer: vstinner date: 2020-10-27T21:34:33+01:00 summary: bpo-42161: Remove private _PyLong_Zero and _PyLong_One (GH-23003) Use PyLong_FromLong(0) and PyLong_FromLong(1) of the public C API instead. For Python internals, _PyLong_GetZero() and _PyLong_GetOne() of pycore_long.h can be used. files: M Include/longobject.h M Objects/longobject.c M Tools/c-analyzer/TODO diff --git a/Include/longobject.h b/Include/longobject.h index 06e3e2490401e..e2301d7abfccc 100644 --- a/Include/longobject.h +++ b/Include/longobject.h @@ -210,9 +210,6 @@ PyAPI_FUNC(PyObject *) _PyLong_GCD(PyObject *, PyObject *); #endif /* !Py_LIMITED_API */ #ifndef Py_LIMITED_API -PyAPI_DATA(PyObject *) _PyLong_Zero; -PyAPI_DATA(PyObject *) _PyLong_One; - PyAPI_FUNC(PyObject *) _PyLong_Rshift(PyObject *, size_t); PyAPI_FUNC(PyObject *) _PyLong_Lshift(PyObject *, size_t); #endif diff --git a/Objects/longobject.c b/Objects/longobject.c index ae63eba134504..e0d6410fe6818 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -32,9 +32,6 @@ _Py_IDENTIFIER(big); (Py_SIZE(x) == 0 ? (sdigit)0 : \ (sdigit)(x)->ob_digit[0])) -PyObject *_PyLong_Zero = NULL; -PyObject *_PyLong_One = NULL; - #define IS_SMALL_INT(ival) (-NSMALLNEGINTS <= (ival) && (ival) < NSMALLPOSINTS) #define IS_SMALL_UINT(ival) ((ival) < NSMALLPOSINTS) @@ -5723,16 +5720,6 @@ _PyLong_Init(PyThreadState *tstate) } if (_Py_IsMainInterpreter(tstate)) { - _PyLong_Zero = PyLong_FromLong(0); - if (_PyLong_Zero == NULL) { - return 0; - } - - _PyLong_One = PyLong_FromLong(1); - if (_PyLong_One == NULL) { - return 0; - } - /* initialize int_info */ if (Int_InfoType.tp_name == NULL) { if (PyStructSequence_InitType2(&Int_InfoType, &int_info_desc) < 0) { @@ -5747,11 +5734,6 @@ _PyLong_Init(PyThreadState *tstate) void _PyLong_Fini(PyThreadState *tstate) { - if (_Py_IsMainInterpreter(tstate)) { - Py_CLEAR(_PyLong_One); - Py_CLEAR(_PyLong_Zero); - } - for (Py_ssize_t i = 0; i < NSMALLNEGINTS + NSMALLPOSINTS; i++) { Py_CLEAR(tstate->interp->small_ints[i]); } diff --git a/Tools/c-analyzer/TODO b/Tools/c-analyzer/TODO index f5c1a92f85695..1fd8052268be0 100644 --- a/Tools/c-analyzer/TODO +++ b/Tools/c-analyzer/TODO @@ -154,8 +154,6 @@ Objects/bytesobject.c:nullstring static PyBytesO Objects/codeobject.c:PyCode_NewEmpty():nulltuple static PyObject *nulltuple Objects/dictobject.c:empty_values static PyObject *empty_values[1] Objects/listobject.c:indexerr static PyObject *indexerr -Objects/longobject.c:_PyLong_One PyObject *_PyLong_One -Objects/longobject.c:_PyLong_Zero PyObject *_PyLong_Zero Objects/longobject.c:small_ints static PyLongObject small_ints[NSMALLNEGINTS + NSMALLPOSINTS] Objects/setobject.c:emptyfrozenset static PyObject *emptyfrozenset Python/context.c:_token_missing static PyObject *_token_missing From webhook-mailer at python.org Tue Oct 27 17:24:42 2020 From: webhook-mailer at python.org (vstinner) Date: Tue, 27 Oct 2020 21:24:42 -0000 Subject: [Python-checkins] bpo-42161: Micro-optimize _collections._count_elements() (GH-23008) Message-ID: https://github.com/python/cpython/commit/35b95aaf21534e4a8e3370dfd6f7482265316c9e commit: 35b95aaf21534e4a8e3370dfd6f7482265316c9e branch: master author: Victor Stinner committer: vstinner date: 2020-10-27T22:24:33+01:00 summary: bpo-42161: Micro-optimize _collections._count_elements() (GH-23008) Move the _PyLong_GetOne() call outside the fast-path loop. files: M Modules/_collectionsmodule.c diff --git a/Modules/_collectionsmodule.c b/Modules/_collectionsmodule.c index 8990071f519ea..157875067635a 100644 --- a/Modules/_collectionsmodule.c +++ b/Modules/_collectionsmodule.c @@ -2278,6 +2278,7 @@ _collections__count_elements_impl(PyObject *module, PyObject *mapping, PyObject *dict_get; PyObject *mapping_setitem; PyObject *dict_setitem; + PyObject *one = _PyLong_GetOne(); // borrowed reference it = PyObject_GetIter(iterable); if (it == NULL) @@ -2324,10 +2325,10 @@ _collections__count_elements_impl(PyObject *module, PyObject *mapping, if (oldval == NULL) { if (PyErr_Occurred()) goto done; - if (_PyDict_SetItem_KnownHash(mapping, key, _PyLong_GetOne(), hash) < 0) + if (_PyDict_SetItem_KnownHash(mapping, key, one, hash) < 0) goto done; } else { - newval = PyNumber_Add(oldval, _PyLong_GetOne()); + newval = PyNumber_Add(oldval, one); if (newval == NULL) goto done; if (_PyDict_SetItem_KnownHash(mapping, key, newval, hash) < 0) @@ -2336,13 +2337,13 @@ _collections__count_elements_impl(PyObject *module, PyObject *mapping, } Py_DECREF(key); } - } else { + } + else { bound_get = _PyObject_GetAttrId(mapping, &PyId_get); if (bound_get == NULL) goto done; PyObject *zero = _PyLong_GetZero(); // borrowed reference - PyObject *one = _PyLong_GetOne(); // borrowed reference while (1) { key = PyIter_Next(it); if (key == NULL) From webhook-mailer at python.org Tue Oct 27 17:37:21 2020 From: webhook-mailer at python.org (gvanrossum) Date: Tue, 27 Oct 2020 21:37:21 -0000 Subject: [Python-checkins] bpo-41805: Documentation for PEP 585 (GH-22615) Message-ID: https://github.com/python/cpython/commit/4173320920706b49a004bdddd8d7108e8984e3fc commit: 4173320920706b49a004bdddd8d7108e8984e3fc branch: master author: kj <28750310+Fidget-Spinner at users.noreply.github.com> committer: gvanrossum date: 2020-10-27T14:37:18-07:00 summary: bpo-41805: Documentation for PEP 585 (GH-22615) files: A Misc/NEWS.d/next/Documentation/2020-10-10-01-36-37.bpo-41805.l-CGv5.rst M Doc/glossary.rst M Doc/library/stdtypes.rst M Doc/library/types.rst diff --git a/Doc/glossary.rst b/Doc/glossary.rst index 847500e556056..4fd01e0160c26 100644 --- a/Doc/glossary.rst +++ b/Doc/glossary.rst @@ -483,6 +483,13 @@ Glossary See also the :term:`single dispatch` glossary entry, the :func:`functools.singledispatch` decorator, and :pep:`443`. + generic type + A :term:`type` that can be parameterized; typically a container like + :class:`list`. Used for :term:`type hints ` and + :term:`annotations `. + + See :pep:`483` for more details, and :mod:`typing` or + :ref:`generic alias type ` for its uses. GIL See :term:`global interpreter lock`. diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index c74d164463601..3fd94ea1bd310 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -4763,6 +4763,200 @@ define these methods must provide them as a normal Python accessible method. Compared to the overhead of setting up the runtime context, the overhead of a single class dictionary lookup is negligible. + +.. _types-genericalias: + +Generic Alias Type +================== + +.. index:: + object: GenericAlias + pair: Generic; Alias + +``GenericAlias`` objects are created by subscripting a class (usually a +container), such as ``list[int]``. They are intended primarily for +:term:`type annotations `. + +Usually, the :ref:`subscription ` of container objects calls the +method :meth:`__getitem__` of the object. However, the subscription of some +containers' classes may call the classmethod :meth:`__class_getitem__` of the +class instead. The classmethod :meth:`__class_getitem__` should return a +``GenericAlias`` object. + +.. note:: + If the :meth:`__getitem__` of the class' metaclass is present, it will take + precedence over the :meth:`__class_getitem__` defined in the class (see + :pep:`560` for more details). + +The ``GenericAlias`` object acts as a proxy for :term:`generic types +`, implementing *parameterized generics* - a specific instance +of a generic which provides the types for container elements. + +The user-exposed type for the ``GenericAlias`` object can be accessed from +:data:`types.GenericAlias` and used for :func:`isinstance` checks. + +.. describe:: T[X, Y, ...] + + Creates a ``GenericAlias`` representing a type ``T`` containing elements + of types *X*, *Y*, and more depending on the ``T`` used. + For example, a function expecting a :class:`list` containing + :class:`float` elements:: + + def average(values: list[float]) -> float: + return sum(values) / len(values) + + Another example for :term:`mapping` objects, using a :class:`dict`, which + is a generic type expecting two type parameters representing the key type + and the value type. In this example, the function expects a ``dict`` with + keys of type :class:`str` and values of type :class:`int`:: + + def send_post_request(url: str, body: dict[str, int]) -> None: + ... + +The builtin functions :func:`isinstance` and :func:`issubclass` do not accept +``GenericAlias`` types for their second argument:: + + >>> isinstance([1, 2], list[str]) + Traceback (most recent call last): + File "", line 1, in + TypeError: isinstance() argument 2 cannot be a parameterized generic + +The Python runtime does not enforce :term:`type annotations `. +This extends to generic types and their type parameters. When creating +an object from a ``GenericAlias``, container elements are not checked +against their type. For example, the following code is discouraged, but will +run without errors:: + + >>> t = list[str] + >>> t([1, 2, 3]) + [1, 2, 3] + +Furthermore, parameterized generics erase type parameters during object +creation:: + + >>> t = list[str] + >>> type(t) + + + >>> l = t() + >>> type(l) + + +Calling :func:`repr` or :func:`str` on a generic shows the parameterized type:: + + >>> repr(list[int]) + 'list[int]' + + >>> str(list[int]) + 'list[int]' + +The :meth:`__getitem__` method of generics will raise an exception to disallow +mistakes like ``dict[str][str]``:: + + >>> dict[str][str] + Traceback (most recent call last): + File "", line 1, in + TypeError: There are no type variables left in dict[str] + +However, such expressions are valid when :ref:`type variables ` are +used. The index must have as many elements as there are type variable items +in the ``GenericAlias`` object's :attr:`__args__ `. :: + + >>> from typing import TypeVar + >>> Y = TypeVar('Y') + >>> dict[str, Y][int] + dict[str, int] + + +Standard Generic Collections +---------------------------- + +These standard library collections support parameterized generics. + +* :class:`tuple` +* :class:`list` +* :class:`dict` +* :class:`set` +* :class:`frozenset` +* :class:`type` +* :class:`collections.deque` +* :class:`collections.defaultdict` +* :class:`collections.OrderedDict` +* :class:`collections.Counter` +* :class:`collections.ChainMap` +* :class:`collections.abc.Awaitable` +* :class:`collections.abc.Coroutine` +* :class:`collections.abc.AsyncIterable` +* :class:`collections.abc.AsyncIterator` +* :class:`collections.abc.AsyncGenerator` +* :class:`collections.abc.Iterable` +* :class:`collections.abc.Iterator` +* :class:`collections.abc.Generator` +* :class:`collections.abc.Reversible` +* :class:`collections.abc.Container` +* :class:`collections.abc.Collection` +* :class:`collections.abc.Callable` +* :class:`collections.abc.Set` +* :class:`collections.abc.MutableSet` +* :class:`collections.abc.Mapping` +* :class:`collections.abc.MutableMapping` +* :class:`collections.abc.Sequence` +* :class:`collections.abc.MutableSequence` +* :class:`collections.abc.ByteString` +* :class:`collections.abc.MappingView` +* :class:`collections.abc.KeysView` +* :class:`collections.abc.ItemsView` +* :class:`collections.abc.ValuesView` +* :class:`contextlib.AbstractContextManager` +* :class:`contextlib.AbstractAsyncContextManager` +* :ref:`re.Pattern ` +* :ref:`re.Match ` + + +Special Attributes of Generic Alias +----------------------------------- + +All parameterized generics implement special read-only attributes. + +.. attribute:: genericalias.__origin__ + + This attribute points at the non-parameterized generic class:: + + >>> list[int].__origin__ + + + +.. attribute:: genericalias.__args__ + + This attribute is a :class:`tuple` (possibly of length 1) of generic + types passed to the original :meth:`__class_getitem__` + of the generic container:: + + >>> dict[str, list[int]].__args__ + (, list[int]) + + +.. attribute:: genericalias.__parameters__ + + This attribute is a lazily computed tuple (possibly empty) of unique type + variables found in ``__args__``:: + + >>> from typing import TypeVar + + >>> T = TypeVar('T') + >>> list[T].__parameters__ + (~T,) + + +.. seealso:: + + * :pep:`585` -- "Type Hinting Generics In Standard Collections" + * :meth:`__class_getitem__` -- Used to implement parameterized generics. + * :ref:`generics` -- Generics in the :mod:`typing` module. + +.. versionadded:: 3.9 + + .. _types-union: Union Type diff --git a/Doc/library/types.rst b/Doc/library/types.rst index a5cf9ab1344a3..00720559d0a4a 100644 --- a/Doc/library/types.rst +++ b/Doc/library/types.rst @@ -262,6 +262,13 @@ Standard names are defined for the following types: .. versionadded:: 3.10 +.. data:: GenericAlias + + The type of :ref:`parameterized generics ` such as + ``list[int]``. + + .. versionadded:: 3.9 + .. data:: Union The type of :ref:`union type expressions`. diff --git a/Misc/NEWS.d/next/Documentation/2020-10-10-01-36-37.bpo-41805.l-CGv5.rst b/Misc/NEWS.d/next/Documentation/2020-10-10-01-36-37.bpo-41805.l-CGv5.rst new file mode 100644 index 0000000000000..9c9134350a317 --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2020-10-10-01-36-37.bpo-41805.l-CGv5.rst @@ -0,0 +1,3 @@ +Documented :ref:`generic alias type ` and +:data:`types.GenericAlias`. Also added an entry in glossary for +:term:`generic types `. From webhook-mailer at python.org Tue Oct 27 18:37:44 2020 From: webhook-mailer at python.org (lysnikolaou) Date: Tue, 27 Oct 2020 22:37:44 -0000 Subject: [Python-checkins] [3.9] Remove git conflict lines from test_parse_directory in peg_generator (GH-23007) Message-ID: https://github.com/python/cpython/commit/8aee019d71b9ed44107bdc8a0c9c132059c0e1ef commit: 8aee019d71b9ed44107bdc8a0c9c132059c0e1ef branch: 3.9 author: Lysandros Nikolaou committer: lysnikolaou date: 2020-10-28T00:37:36+02:00 summary: [3.9] Remove git conflict lines from test_parse_directory in peg_generator (GH-23007) files: M Tools/peg_generator/scripts/test_parse_directory.py diff --git a/Tools/peg_generator/scripts/test_parse_directory.py b/Tools/peg_generator/scripts/test_parse_directory.py index 100db1d424c7c..6dfdbb9170e6e 100755 --- a/Tools/peg_generator/scripts/test_parse_directory.py +++ b/Tools/peg_generator/scripts/test_parse_directory.py @@ -5,14 +5,9 @@ import os import sys import time -import traceback import tokenize -<<<<<<< HEAD import _peg_parser -from glob import glob -======= from glob import glob, escape ->>>>>>> 9355868458... bpo-41043: Escape literal part of the path for glob(). (GH-20994) from pathlib import PurePath from typing import List, Optional, Any, Tuple From webhook-mailer at python.org Tue Oct 27 18:38:49 2020 From: webhook-mailer at python.org (lysnikolaou) Date: Tue, 27 Oct 2020 22:38:49 -0000 Subject: [Python-checkins] [3.9] bpo-41659: Disallow curly brace directly after primary (GH-22996) (#23006) Message-ID: https://github.com/python/cpython/commit/c4b58cea4771afc0ddfdb857b0fb5115b9f4bc9f commit: c4b58cea4771afc0ddfdb857b0fb5115b9f4bc9f branch: 3.9 author: Lysandros Nikolaou committer: lysnikolaou date: 2020-10-28T00:38:42+02:00 summary: [3.9] bpo-41659: Disallow curly brace directly after primary (GH-22996) (#23006) (cherry picked from commit 15acc4eaba8519d7d5f2acaffde65446b44dcf79) files: A Misc/NEWS.d/next/Core and Builtins/2020-10-27-18-32-49.bpo-41659.d4a-8o.rst M Grammar/python.gram M Lib/test/test_exceptions.py M Lib/test/test_syntax.py M Parser/pegen/parse.c diff --git a/Grammar/python.gram b/Grammar/python.gram index 8f4d250dc3ae4..6c36b6acb5352 100644 --- a/Grammar/python.gram +++ b/Grammar/python.gram @@ -460,6 +460,7 @@ await_primary[expr_ty] (memo): | AWAIT a=primary { CHECK_VERSION(5, "Await expressions are", _Py_Await(a, EXTRA)) } | primary primary[expr_ty]: + | invalid_primary # must be before 'primay genexp' because of invalid_genexp | a=primary '.' b=NAME { _Py_Attribute(a, b->v.Name.id, Load, EXTRA) } | a=primary b=genexp { _Py_Call(a, CHECK(_PyPegen_singleton_seq(p, b)), NULL, EXTRA) } | a=primary '(' b=[arguments] ')' { @@ -664,6 +665,8 @@ invalid_del_stmt: RAISE_SYNTAX_ERROR_INVALID_TARGET(DEL_TARGETS, a) } invalid_block: | NEWLINE !INDENT { RAISE_INDENTATION_ERROR("expected an indented block") } +invalid_primary: + | primary a='{' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "invalid syntax") } invalid_comprehension: | ('[' | '(' | '{') a=starred_expression for_if_clauses { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "iterable unpacking cannot be used in comprehension") } diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index 47f37114f0259..8d125b57ad6d5 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -205,6 +205,7 @@ def testSyntaxErrorOffset(self): check(b'Python = "\xcf\xb3\xf2\xee\xed" +', 1, 18) check('x = "a', 1, 7) check('lambda x: x = 2', 1, 1) + check('f{a + b + c}', 1, 2) # Errors thrown by compile.c check('class foo:return 1', 1, 11) diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index d6583b9d30c27..b0527e6cd85ba 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -802,6 +802,9 @@ def _check_error(self, code, errtext, else: self.fail("compile() did not raise SyntaxError") + def test_curly_brace_after_primary_raises_immediately(self): + self._check_error("f{", "invalid syntax", mode="single") + def test_assign_call(self): self._check_error("f() = 1", "assign") diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-10-27-18-32-49.bpo-41659.d4a-8o.rst b/Misc/NEWS.d/next/Core and Builtins/2020-10-27-18-32-49.bpo-41659.d4a-8o.rst new file mode 100644 index 0000000000000..038749a7b16c9 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2020-10-27-18-32-49.bpo-41659.d4a-8o.rst @@ -0,0 +1,3 @@ +Fix a bug in the parser, where a curly brace following a `primary` didn't fail immediately. +This led to invalid expressions like `a {b}` to throw a :exc:`SyntaxError` with a wrong offset, +or invalid expressions ending with a curly brace like `a {` to not fail immediately in the REPL. \ No newline at end of file diff --git a/Parser/pegen/parse.c b/Parser/pegen/parse.c index 1a4df757eae33..2eb9e03d52819 100644 --- a/Parser/pegen/parse.c +++ b/Parser/pegen/parse.c @@ -225,173 +225,174 @@ static KeywordToken *reserved_keywords[] = { #define invalid_ann_assign_target_type 1147 #define invalid_del_stmt_type 1148 #define invalid_block_type 1149 -#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 _loop0_71_type 1230 -#define _gather_70_type 1231 -#define _loop1_72_type 1232 -#define _loop0_74_type 1233 -#define _gather_73_type 1234 -#define _loop1_75_type 1235 -#define _loop0_76_type 1236 -#define _loop0_77_type 1237 -#define _loop0_78_type 1238 -#define _loop1_79_type 1239 -#define _loop0_80_type 1240 -#define _loop1_81_type 1241 -#define _loop1_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 _loop0_88_type 1248 -#define _loop1_89_type 1249 -#define _loop1_90_type 1250 -#define _loop1_91_type 1251 -#define _loop1_92_type 1252 -#define _tmp_93_type 1253 -#define _loop0_95_type 1254 -#define _gather_94_type 1255 -#define _tmp_96_type 1256 -#define _tmp_97_type 1257 -#define _tmp_98_type 1258 -#define _tmp_99_type 1259 -#define _loop1_100_type 1260 -#define _tmp_101_type 1261 -#define _tmp_102_type 1262 -#define _loop0_104_type 1263 -#define _gather_103_type 1264 -#define _loop1_105_type 1265 -#define _loop0_106_type 1266 -#define _loop0_107_type 1267 -#define _loop0_109_type 1268 -#define _gather_108_type 1269 -#define _tmp_110_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_118_type 1277 -#define _gather_117_type 1278 -#define _loop0_119_type 1279 -#define _loop0_121_type 1280 -#define _gather_120_type 1281 -#define _tmp_122_type 1282 -#define _loop0_124_type 1283 -#define _gather_123_type 1284 -#define _loop0_126_type 1285 -#define _gather_125_type 1286 -#define _tmp_127_type 1287 -#define _loop0_128_type 1288 -#define _loop0_129_type 1289 -#define _loop0_130_type 1290 -#define _tmp_131_type 1291 -#define _tmp_132_type 1292 -#define _loop0_133_type 1293 -#define _tmp_134_type 1294 -#define _loop0_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 _tmp_151_type 1311 -#define _tmp_152_type 1312 -#define _loop1_153_type 1313 -#define _loop1_154_type 1314 -#define _tmp_155_type 1315 -#define _tmp_156_type 1316 +#define invalid_primary_type 1150 // Left-recursive +#define invalid_comprehension_type 1151 +#define invalid_dict_comprehension_type 1152 +#define invalid_parameters_type 1153 +#define invalid_lambda_parameters_type 1154 +#define invalid_star_etc_type 1155 +#define invalid_lambda_star_etc_type 1156 +#define invalid_double_type_comments_type 1157 +#define invalid_with_item_type 1158 +#define invalid_for_target_type 1159 +#define invalid_group_type 1160 +#define invalid_import_from_targets_type 1161 +#define _loop0_1_type 1162 +#define _loop0_2_type 1163 +#define _loop0_4_type 1164 +#define _gather_3_type 1165 +#define _loop0_6_type 1166 +#define _gather_5_type 1167 +#define _loop0_8_type 1168 +#define _gather_7_type 1169 +#define _loop0_10_type 1170 +#define _gather_9_type 1171 +#define _loop1_11_type 1172 +#define _loop0_13_type 1173 +#define _gather_12_type 1174 +#define _tmp_14_type 1175 +#define _tmp_15_type 1176 +#define _tmp_16_type 1177 +#define _tmp_17_type 1178 +#define _tmp_18_type 1179 +#define _tmp_19_type 1180 +#define _tmp_20_type 1181 +#define _tmp_21_type 1182 +#define _loop1_22_type 1183 +#define _tmp_23_type 1184 +#define _tmp_24_type 1185 +#define _loop0_26_type 1186 +#define _gather_25_type 1187 +#define _loop0_28_type 1188 +#define _gather_27_type 1189 +#define _tmp_29_type 1190 +#define _tmp_30_type 1191 +#define _loop0_31_type 1192 +#define _loop1_32_type 1193 +#define _loop0_34_type 1194 +#define _gather_33_type 1195 +#define _tmp_35_type 1196 +#define _loop0_37_type 1197 +#define _gather_36_type 1198 +#define _tmp_38_type 1199 +#define _loop0_40_type 1200 +#define _gather_39_type 1201 +#define _loop0_42_type 1202 +#define _gather_41_type 1203 +#define _loop0_44_type 1204 +#define _gather_43_type 1205 +#define _loop0_46_type 1206 +#define _gather_45_type 1207 +#define _tmp_47_type 1208 +#define _loop1_48_type 1209 +#define _tmp_49_type 1210 +#define _tmp_50_type 1211 +#define _tmp_51_type 1212 +#define _tmp_52_type 1213 +#define _tmp_53_type 1214 +#define _loop0_54_type 1215 +#define _loop0_55_type 1216 +#define _loop0_56_type 1217 +#define _loop1_57_type 1218 +#define _loop0_58_type 1219 +#define _loop1_59_type 1220 +#define _loop1_60_type 1221 +#define _loop1_61_type 1222 +#define _loop0_62_type 1223 +#define _loop1_63_type 1224 +#define _loop0_64_type 1225 +#define _loop1_65_type 1226 +#define _loop0_66_type 1227 +#define _loop1_67_type 1228 +#define _loop1_68_type 1229 +#define _tmp_69_type 1230 +#define _loop0_71_type 1231 +#define _gather_70_type 1232 +#define _loop1_72_type 1233 +#define _loop0_74_type 1234 +#define _gather_73_type 1235 +#define _loop1_75_type 1236 +#define _loop0_76_type 1237 +#define _loop0_77_type 1238 +#define _loop0_78_type 1239 +#define _loop1_79_type 1240 +#define _loop0_80_type 1241 +#define _loop1_81_type 1242 +#define _loop1_82_type 1243 +#define _loop1_83_type 1244 +#define _loop0_84_type 1245 +#define _loop1_85_type 1246 +#define _loop0_86_type 1247 +#define _loop1_87_type 1248 +#define _loop0_88_type 1249 +#define _loop1_89_type 1250 +#define _loop1_90_type 1251 +#define _loop1_91_type 1252 +#define _loop1_92_type 1253 +#define _tmp_93_type 1254 +#define _loop0_95_type 1255 +#define _gather_94_type 1256 +#define _tmp_96_type 1257 +#define _tmp_97_type 1258 +#define _tmp_98_type 1259 +#define _tmp_99_type 1260 +#define _loop1_100_type 1261 +#define _tmp_101_type 1262 +#define _tmp_102_type 1263 +#define _loop0_104_type 1264 +#define _gather_103_type 1265 +#define _loop1_105_type 1266 +#define _loop0_106_type 1267 +#define _loop0_107_type 1268 +#define _loop0_109_type 1269 +#define _gather_108_type 1270 +#define _tmp_110_type 1271 +#define _loop0_112_type 1272 +#define _gather_111_type 1273 +#define _loop0_114_type 1274 +#define _gather_113_type 1275 +#define _loop0_116_type 1276 +#define _gather_115_type 1277 +#define _loop0_118_type 1278 +#define _gather_117_type 1279 +#define _loop0_119_type 1280 +#define _loop0_121_type 1281 +#define _gather_120_type 1282 +#define _tmp_122_type 1283 +#define _loop0_124_type 1284 +#define _gather_123_type 1285 +#define _loop0_126_type 1286 +#define _gather_125_type 1287 +#define _tmp_127_type 1288 +#define _loop0_128_type 1289 +#define _loop0_129_type 1290 +#define _loop0_130_type 1291 +#define _tmp_131_type 1292 +#define _tmp_132_type 1293 +#define _loop0_133_type 1294 +#define _tmp_134_type 1295 +#define _loop0_135_type 1296 +#define _tmp_136_type 1297 +#define _tmp_137_type 1298 +#define _tmp_138_type 1299 +#define _tmp_139_type 1300 +#define _tmp_140_type 1301 +#define _tmp_141_type 1302 +#define _tmp_142_type 1303 +#define _tmp_143_type 1304 +#define _tmp_144_type 1305 +#define _tmp_145_type 1306 +#define _tmp_146_type 1307 +#define _tmp_147_type 1308 +#define _tmp_148_type 1309 +#define _tmp_149_type 1310 +#define _tmp_150_type 1311 +#define _tmp_151_type 1312 +#define _tmp_152_type 1313 +#define _loop1_153_type 1314 +#define _loop1_154_type 1315 +#define _tmp_155_type 1316 +#define _tmp_156_type 1317 static mod_ty file_rule(Parser *p); static mod_ty interactive_rule(Parser *p); @@ -543,6 +544,7 @@ static void *invalid_assignment_rule(Parser *p); static expr_ty invalid_ann_assign_target_rule(Parser *p); static void *invalid_del_stmt_rule(Parser *p); static void *invalid_block_rule(Parser *p); +static void *invalid_primary_rule(Parser *p); static void *invalid_comprehension_rule(Parser *p); static void *invalid_dict_comprehension_rule(Parser *p); static void *invalid_parameters_rule(Parser *p); @@ -10274,6 +10276,7 @@ await_primary_rule(Parser *p) // Left-recursive // primary: +// | invalid_primary // | primary '.' NAME // | primary genexp // | primary '(' arguments? ')' @@ -10327,6 +10330,25 @@ primary_raw(Parser *p) 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 + { // invalid_primary + if (p->error_indicator) { + D(p->level--); + return NULL; + } + D(fprintf(stderr, "%*c> primary[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "invalid_primary")); + void *invalid_primary_var; + if ( + (invalid_primary_var = invalid_primary_rule(p)) // invalid_primary + ) + { + D(fprintf(stderr, "%*c+ primary[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "invalid_primary")); + _res = invalid_primary_var; + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s primary[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "invalid_primary")); + } { // primary '.' NAME if (p->error_indicator) { D(p->level--); @@ -15047,6 +15069,51 @@ invalid_block_rule(Parser *p) return _res; } +// Left-recursive +// invalid_primary: primary '{' +static void * +invalid_primary_rule(Parser *p) +{ + D(p->level++); + if (p->error_indicator) { + D(p->level--); + return NULL; + } + void * _res = NULL; + int _mark = p->mark; + { // primary '{' + if (p->error_indicator) { + D(p->level--); + return NULL; + } + D(fprintf(stderr, "%*c> invalid_primary[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "primary '{'")); + Token * a; + expr_ty primary_var; + if ( + (primary_var = primary_rule(p)) // primary + && + (a = _PyPegen_expect_token(p, 25)) // token='{' + ) + { + D(fprintf(stderr, "%*c+ invalid_primary[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "primary '{'")); + _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "invalid syntax" ); + if (_res == NULL && PyErr_Occurred()) { + p->error_indicator = 1; + D(p->level--); + return NULL; + } + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s invalid_primary[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "primary '{'")); + } + _res = NULL; + done: + D(p->level--); + return _res; +} + // invalid_comprehension: ('[' | '(' | '{') starred_expression for_if_clauses static void * invalid_comprehension_rule(Parser *p) From webhook-mailer at python.org Tue Oct 27 20:14:19 2020 From: webhook-mailer at python.org (lysnikolaou) Date: Wed, 28 Oct 2020 00:14:19 -0000 Subject: [Python-checkins] [3.9] bpo-42123: Run the parser two times and only enable invalid rules on the second run (GH-22111) (GH-23011) Message-ID: https://github.com/python/cpython/commit/24a7c298d47658295673dc04d1b6d59f2b200a7c commit: 24a7c298d47658295673dc04d1b6d59f2b200a7c branch: 3.9 author: Lysandros Nikolaou committer: lysnikolaou date: 2020-10-28T02:14:15+02:00 summary: [3.9] bpo-42123: Run the parser two times and only enable invalid rules on the second run (GH-22111) (GH-23011) * Implement running the parser a second time for the errors messages The first parser run is only responsible for detecting whether there is a `SyntaxError` or not. If there isn't the AST gets returned. Otherwise, the parser is run a second time with all the `invalid_*` rules enabled so that all the customized error messages get produced. (cherry picked from commit bca701403253379409dece03053dbd739c0bd059) files: A Misc/NEWS.d/next/Core and Builtins/2020-10-23-02-43-24.bpo-42123.64gJWC.rst M Grammar/python.gram M Parser/pegen/parse.c M Parser/pegen/pegen.c M Parser/pegen/pegen.h M Tools/peg_generator/pegen/c_generator.py diff --git a/Grammar/python.gram b/Grammar/python.gram index 6c36b6acb5352..b709d3d5c3214 100644 --- a/Grammar/python.gram +++ b/Grammar/python.gram @@ -535,7 +535,7 @@ yield_expr[expr_ty]: arguments[expr_ty] (memo): | a=args [','] &')' { a } - | incorrect_arguments + | invalid_arguments args[expr_ty]: | a=','.(starred_expression | named_expression !'=')+ b=[',' k=kwargs {k}] { _PyPegen_collect_call_seqs(p, a, b, EXTRA) } | a=kwargs { _Py_Call(_PyPegen_dummy_name(p), @@ -620,7 +620,7 @@ t_atom[expr_ty]: # From here on, there are rules for invalid syntax with specialised error messages -incorrect_arguments: +invalid_arguments: | args ',' '*' { RAISE_SYNTAX_ERROR("iterable argument unpacking follows keyword argument unpacking") } | a=expression for_if_clauses ',' [args | expression for_if_clauses] { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "Generator expression must be parenthesized") } diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-10-23-02-43-24.bpo-42123.64gJWC.rst b/Misc/NEWS.d/next/Core and Builtins/2020-10-23-02-43-24.bpo-42123.64gJWC.rst new file mode 100644 index 0000000000000..6461efd76f0f9 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2020-10-23-02-43-24.bpo-42123.64gJWC.rst @@ -0,0 +1,3 @@ +Run the parser two times. On the first run, disable all the 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. diff --git a/Parser/pegen/parse.c b/Parser/pegen/parse.c index 2eb9e03d52819..48a2443474684 100644 --- a/Parser/pegen/parse.c +++ b/Parser/pegen/parse.c @@ -218,7 +218,7 @@ static KeywordToken *reserved_keywords[] = { #define t_primary_type 1140 // Left-recursive #define t_lookahead_type 1141 #define t_atom_type 1142 -#define incorrect_arguments_type 1143 +#define invalid_arguments_type 1143 #define invalid_kwarg_type 1144 #define invalid_named_expression_type 1145 #define invalid_assignment_type 1146 @@ -537,7 +537,7 @@ static expr_ty target_rule(Parser *p); static expr_ty t_primary_rule(Parser *p); static void *t_lookahead_rule(Parser *p); static expr_ty t_atom_rule(Parser *p); -static void *incorrect_arguments_rule(Parser *p); +static void *invalid_arguments_rule(Parser *p); static void *invalid_kwarg_rule(Parser *p); static void *invalid_named_expression_rule(Parser *p); static void *invalid_assignment_rule(Parser *p); @@ -2219,7 +2219,7 @@ assignment_rule(Parser *p) return NULL; } } - { // invalid_assignment + if (p->call_invalid_rules) { // invalid_assignment if (p->error_indicator) { D(p->level--); return NULL; @@ -2892,7 +2892,7 @@ del_stmt_rule(Parser *p) D(fprintf(stderr, "%*c%s del_stmt[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'del' del_targets &(';' | NEWLINE)")); } - { // invalid_del_stmt + if (p->call_invalid_rules) { // invalid_del_stmt if (p->error_indicator) { D(p->level--); return NULL; @@ -3243,7 +3243,7 @@ import_from_targets_rule(Parser *p) D(fprintf(stderr, "%*c%s import_from_targets[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'*'")); } - { // invalid_import_from_targets + if (p->call_invalid_rules) { // invalid_import_from_targets if (p->error_indicator) { D(p->level--); return NULL; @@ -4036,7 +4036,7 @@ for_stmt_rule(Parser *p) return NULL; } } - { // invalid_for_target + if (p->call_invalid_rules) { // invalid_for_target if (p->error_indicator) { D(p->level--); return NULL; @@ -4337,7 +4337,7 @@ with_item_rule(Parser *p) D(fprintf(stderr, "%*c%s with_item[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression 'as' star_target &(',' | ')' | ':')")); } - { // invalid_with_item + if (p->call_invalid_rules) { // invalid_with_item if (p->error_indicator) { D(p->level--); return NULL; @@ -5072,7 +5072,7 @@ func_type_comment_rule(Parser *p) D(fprintf(stderr, "%*c%s func_type_comment[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "NEWLINE TYPE_COMMENT &(NEWLINE INDENT)")); } - { // invalid_double_type_comments + if (p->call_invalid_rules) { // invalid_double_type_comments if (p->error_indicator) { D(p->level--); return NULL; @@ -5127,7 +5127,7 @@ params_rule(Parser *p) } arguments_ty _res = NULL; int _mark = p->mark; - { // invalid_parameters + if (p->call_invalid_rules) { // invalid_parameters if (p->error_indicator) { D(p->level--); return NULL; @@ -5602,7 +5602,7 @@ star_etc_rule(Parser *p) D(fprintf(stderr, "%*c%s star_etc[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "kwds")); } - { // invalid_star_etc + if (p->call_invalid_rules) { // invalid_star_etc if (p->error_indicator) { D(p->level--); return NULL; @@ -6305,7 +6305,7 @@ block_rule(Parser *p) D(fprintf(stderr, "%*c%s block[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "simple_stmt")); } - { // invalid_block + if (p->call_invalid_rules) { // invalid_block if (p->error_indicator) { D(p->level--); return NULL; @@ -6799,7 +6799,7 @@ named_expression_rule(Parser *p) D(fprintf(stderr, "%*c%s named_expression[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression !':='")); } - { // invalid_named_expression + if (p->call_invalid_rules) { // invalid_named_expression if (p->error_indicator) { D(p->level--); return NULL; @@ -7193,7 +7193,7 @@ lambda_params_rule(Parser *p) } arguments_ty _res = NULL; int _mark = p->mark; - { // invalid_lambda_parameters + if (p->call_invalid_rules) { // invalid_lambda_parameters if (p->error_indicator) { D(p->level--); return NULL; @@ -7670,7 +7670,7 @@ lambda_star_etc_rule(Parser *p) D(fprintf(stderr, "%*c%s lambda_star_etc[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_kwds")); } - { // invalid_lambda_star_etc + if (p->call_invalid_rules) { // invalid_lambda_star_etc if (p->error_indicator) { D(p->level--); return NULL; @@ -10330,7 +10330,7 @@ primary_raw(Parser *p) 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 - { // invalid_primary + if (p->call_invalid_rules) { // invalid_primary if (p->error_indicator) { D(p->level--); return NULL; @@ -11209,7 +11209,7 @@ listcomp_rule(Parser *p) return NULL; } } - { // invalid_comprehension + if (p->call_invalid_rules) { // invalid_comprehension if (p->error_indicator) { D(p->level--); return NULL; @@ -11340,7 +11340,7 @@ group_rule(Parser *p) D(fprintf(stderr, "%*c%s group[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'(' (yield_expr | named_expression) ')'")); } - { // invalid_group + if (p->call_invalid_rules) { // invalid_group if (p->error_indicator) { D(p->level--); return NULL; @@ -11434,7 +11434,7 @@ genexp_rule(Parser *p) return NULL; } } - { // invalid_comprehension + if (p->call_invalid_rules) { // invalid_comprehension if (p->error_indicator) { D(p->level--); return NULL; @@ -11593,7 +11593,7 @@ setcomp_rule(Parser *p) return NULL; } } - { // invalid_comprehension + if (p->call_invalid_rules) { // invalid_comprehension if (p->error_indicator) { D(p->level--); return NULL; @@ -11745,7 +11745,7 @@ dictcomp_rule(Parser *p) D(fprintf(stderr, "%*c%s dictcomp[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' kvpair for_if_clauses '}'")); } - { // invalid_dict_comprehension + if (p->call_invalid_rules) { // invalid_dict_comprehension if (p->error_indicator) { D(p->level--); return NULL; @@ -12064,7 +12064,7 @@ for_if_clause_rule(Parser *p) return NULL; } } - { // invalid_for_target + if (p->call_invalid_rules) { // invalid_for_target if (p->error_indicator) { D(p->level--); return NULL; @@ -12190,7 +12190,7 @@ yield_expr_rule(Parser *p) return _res; } -// arguments: args ','? &')' | incorrect_arguments +// arguments: args ','? &')' | invalid_arguments static expr_ty arguments_rule(Parser *p) { @@ -12235,24 +12235,24 @@ arguments_rule(Parser *p) D(fprintf(stderr, "%*c%s arguments[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "args ','? &')'")); } - { // incorrect_arguments + if (p->call_invalid_rules) { // invalid_arguments if (p->error_indicator) { D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> arguments[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "incorrect_arguments")); - void *incorrect_arguments_var; + D(fprintf(stderr, "%*c> arguments[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "invalid_arguments")); + void *invalid_arguments_var; if ( - (incorrect_arguments_var = incorrect_arguments_rule(p)) // incorrect_arguments + (invalid_arguments_var = invalid_arguments_rule(p)) // invalid_arguments ) { - D(fprintf(stderr, "%*c+ arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "incorrect_arguments")); - _res = incorrect_arguments_var; + D(fprintf(stderr, "%*c+ arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "invalid_arguments")); + _res = invalid_arguments_var; goto done; } p->mark = _mark; D(fprintf(stderr, "%*c%s arguments[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "incorrect_arguments")); + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "invalid_arguments")); } _res = NULL; done: @@ -12589,7 +12589,7 @@ kwarg_or_starred_rule(Parser *p) D(fprintf(stderr, "%*c%s kwarg_or_starred[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "starred_expression")); } - { // invalid_kwarg + if (p->call_invalid_rules) { // invalid_kwarg if (p->error_indicator) { D(p->level--); return NULL; @@ -12709,7 +12709,7 @@ kwarg_or_double_starred_rule(Parser *p) D(fprintf(stderr, "%*c%s kwarg_or_double_starred[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'**' expression")); } - { // invalid_kwarg + if (p->call_invalid_rules) { // invalid_kwarg if (p->error_indicator) { D(p->level--); return NULL; @@ -14421,14 +14421,14 @@ t_atom_rule(Parser *p) return _res; } -// incorrect_arguments: +// invalid_arguments: // | args ',' '*' // | expression for_if_clauses ',' [args | expression for_if_clauses] // | args for_if_clauses // | args ',' expression for_if_clauses // | args ',' args static void * -incorrect_arguments_rule(Parser *p) +invalid_arguments_rule(Parser *p) { D(p->level++); if (p->error_indicator) { @@ -14442,7 +14442,7 @@ incorrect_arguments_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> incorrect_arguments[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "args ',' '*'")); + D(fprintf(stderr, "%*c> invalid_arguments[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "args ',' '*'")); Token * _literal; Token * _literal_1; expr_ty args_var; @@ -14454,7 +14454,7 @@ incorrect_arguments_rule(Parser *p) (_literal_1 = _PyPegen_expect_token(p, 16)) // token='*' ) { - D(fprintf(stderr, "%*c+ incorrect_arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "args ',' '*'")); + D(fprintf(stderr, "%*c+ invalid_arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "args ',' '*'")); _res = RAISE_SYNTAX_ERROR ( "iterable argument unpacking follows keyword argument unpacking" ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -14464,7 +14464,7 @@ incorrect_arguments_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s incorrect_arguments[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s invalid_arguments[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "args ',' '*'")); } { // expression for_if_clauses ',' [args | expression for_if_clauses] @@ -14472,7 +14472,7 @@ incorrect_arguments_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> incorrect_arguments[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression for_if_clauses ',' [args | expression for_if_clauses]")); + D(fprintf(stderr, "%*c> invalid_arguments[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression for_if_clauses ',' [args | expression for_if_clauses]")); Token * _literal; void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings @@ -14488,7 +14488,7 @@ incorrect_arguments_rule(Parser *p) (_opt_var = _tmp_127_rule(p), 1) // [args | expression for_if_clauses] ) { - D(fprintf(stderr, "%*c+ incorrect_arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression for_if_clauses ',' [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]")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "Generator expression must be parenthesized" ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -14498,7 +14498,7 @@ incorrect_arguments_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s incorrect_arguments[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s invalid_arguments[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression for_if_clauses ',' [args | expression for_if_clauses]")); } { // args for_if_clauses @@ -14506,7 +14506,7 @@ incorrect_arguments_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> incorrect_arguments[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "args for_if_clauses")); + D(fprintf(stderr, "%*c> invalid_arguments[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "args for_if_clauses")); expr_ty a; asdl_seq* for_if_clauses_var; if ( @@ -14515,7 +14515,7 @@ incorrect_arguments_rule(Parser *p) (for_if_clauses_var = for_if_clauses_rule(p)) // for_if_clauses ) { - D(fprintf(stderr, "%*c+ incorrect_arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "args for_if_clauses")); + D(fprintf(stderr, "%*c+ invalid_arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "args for_if_clauses")); _res = _PyPegen_nonparen_genexp_in_call ( p , a ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -14525,7 +14525,7 @@ incorrect_arguments_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s incorrect_arguments[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s invalid_arguments[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "args for_if_clauses")); } { // args ',' expression for_if_clauses @@ -14533,7 +14533,7 @@ incorrect_arguments_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> incorrect_arguments[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "args ',' expression for_if_clauses")); + D(fprintf(stderr, "%*c> invalid_arguments[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "args ',' expression for_if_clauses")); Token * _literal; expr_ty a; expr_ty args_var; @@ -14548,7 +14548,7 @@ incorrect_arguments_rule(Parser *p) (for_if_clauses_var = for_if_clauses_rule(p)) // for_if_clauses ) { - D(fprintf(stderr, "%*c+ incorrect_arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "args ',' expression for_if_clauses")); + D(fprintf(stderr, "%*c+ invalid_arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "args ',' expression for_if_clauses")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "Generator expression must be parenthesized" ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -14558,7 +14558,7 @@ incorrect_arguments_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s incorrect_arguments[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s invalid_arguments[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "args ',' expression for_if_clauses")); } { // args ',' args @@ -14566,7 +14566,7 @@ incorrect_arguments_rule(Parser *p) D(p->level--); return NULL; } - D(fprintf(stderr, "%*c> incorrect_arguments[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "args ',' args")); + D(fprintf(stderr, "%*c> invalid_arguments[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "args ',' args")); Token * _literal; expr_ty a; expr_ty args_var; @@ -14578,7 +14578,7 @@ incorrect_arguments_rule(Parser *p) (args_var = args_rule(p)) // args ) { - D(fprintf(stderr, "%*c+ incorrect_arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "args ',' args")); + D(fprintf(stderr, "%*c+ invalid_arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "args ',' args")); _res = _PyPegen_arguments_parsing_error ( p , a ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -14588,7 +14588,7 @@ incorrect_arguments_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s incorrect_arguments[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s invalid_arguments[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "args ',' args")); } _res = NULL; diff --git a/Parser/pegen/pegen.c b/Parser/pegen/pegen.c index a7643fc3a6ad9..78891af825007 100644 --- a/Parser/pegen/pegen.c +++ b/Parser/pegen/pegen.c @@ -1101,15 +1101,28 @@ _PyPegen_Parser_New(struct tok_state *tok, int start_rule, int flags, p->feature_version = feature_version; p->known_err_token = NULL; p->level = 0; + p->call_invalid_rules = 0; return p; } +static void +reset_parser_state(Parser *p) +{ + for (int i = 0; i < p->fill; i++) { + p->tokens[i]->memo = NULL; + } + p->mark = 0; + p->call_invalid_rules = 1; +} + void * _PyPegen_run_parser(Parser *p) { void *res = _PyPegen_parse(p); if (res == NULL) { + reset_parser_state(p); + _PyPegen_parse(p); if (PyErr_Occurred()) { return NULL; } diff --git a/Parser/pegen/pegen.h b/Parser/pegen/pegen.h index bd35d4ff71ae8..2fea84fd22ab4 100644 --- a/Parser/pegen/pegen.h +++ b/Parser/pegen/pegen.h @@ -73,6 +73,7 @@ typedef struct { growable_comment_array type_ignore_comments; Token *known_err_token; int level; + int call_invalid_rules; } Parser; typedef struct { diff --git a/Tools/peg_generator/pegen/c_generator.py b/Tools/peg_generator/pegen/c_generator.py index aee668c3f329a..d0abc12b4026a 100644 --- a/Tools/peg_generator/pegen/c_generator.py +++ b/Tools/peg_generator/pegen/c_generator.py @@ -730,7 +730,10 @@ def handle_alt_loop(self, node: Alt, is_gather: bool, rulename: Optional[str]) - def visit_Alt( self, node: Alt, is_loop: bool, is_gather: bool, rulename: Optional[str] ) -> None: - self.print(f"{{ // {node}") + if len(node.items) == 1 and str(node.items[0]).startswith('invalid_'): + self.print(f"if (p->call_invalid_rules) {{ // {node}") + else: + self.print(f"{{ // {node}") with self.indent(): self._check_for_errors() node_str = str(node).replace('"', '\\"') From webhook-mailer at python.org Wed Oct 28 11:34:15 2020 From: webhook-mailer at python.org (miss-islington) Date: Wed, 28 Oct 2020 15:34:15 -0000 Subject: [Python-checkins] [3.9] bpo-41805: Documentation for PEP 585 (GH-22615) (GH-23016) Message-ID: https://github.com/python/cpython/commit/577d7c4e628260eb7926d043ca9c355ece583eb7 commit: 577d7c4e628260eb7926d043ca9c355ece583eb7 branch: 3.9 author: kj <28750310+Fidget-Spinner at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-28T08:33:36-07:00 summary: [3.9] bpo-41805: Documentation for PEP 585 (GH-22615) (GH-23016) Backport of #22615 to 3.9 since that couldn't be auto-merged due to conflicts. files: A Misc/NEWS.d/next/Documentation/2020-10-10-01-36-37.bpo-41805.l-CGv5.rst M Doc/glossary.rst M Doc/library/stdtypes.rst M Doc/library/types.rst diff --git a/Doc/glossary.rst b/Doc/glossary.rst index 847500e556056..4fd01e0160c26 100644 --- a/Doc/glossary.rst +++ b/Doc/glossary.rst @@ -483,6 +483,13 @@ Glossary See also the :term:`single dispatch` glossary entry, the :func:`functools.singledispatch` decorator, and :pep:`443`. + generic type + A :term:`type` that can be parameterized; typically a container like + :class:`list`. Used for :term:`type hints ` and + :term:`annotations `. + + See :pep:`483` for more details, and :mod:`typing` or + :ref:`generic alias type ` for its uses. GIL See :term:`global interpreter lock`. diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 1de48e13e271f..e19e76f8311c8 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -4731,6 +4731,199 @@ Compared to the overhead of setting up the runtime context, the overhead of a single class dictionary lookup is negligible. +.. _types-genericalias: + +Generic Alias Type +================== + +.. index:: + object: GenericAlias + pair: Generic; Alias + +``GenericAlias`` objects are created by subscripting a class (usually a +container), such as ``list[int]``. They are intended primarily for +:term:`type annotations `. + +Usually, the :ref:`subscription ` of container objects calls the +method :meth:`__getitem__` of the object. However, the subscription of some +containers' classes may call the classmethod :meth:`__class_getitem__` of the +class instead. The classmethod :meth:`__class_getitem__` should return a +``GenericAlias`` object. + +.. note:: + If the :meth:`__getitem__` of the class' metaclass is present, it will take + precedence over the :meth:`__class_getitem__` defined in the class (see + :pep:`560` for more details). + +The ``GenericAlias`` object acts as a proxy for :term:`generic types +`, implementing *parameterized generics* - a specific instance +of a generic which provides the types for container elements. + +The user-exposed type for the ``GenericAlias`` object can be accessed from +:data:`types.GenericAlias` and used for :func:`isinstance` checks. + +.. describe:: T[X, Y, ...] + + Creates a ``GenericAlias`` representing a type ``T`` containing elements + of types *X*, *Y*, and more depending on the ``T`` used. + For example, a function expecting a :class:`list` containing + :class:`float` elements:: + + def average(values: list[float]) -> float: + return sum(values) / len(values) + + Another example for :term:`mapping` objects, using a :class:`dict`, which + is a generic type expecting two type parameters representing the key type + and the value type. In this example, the function expects a ``dict`` with + keys of type :class:`str` and values of type :class:`int`:: + + def send_post_request(url: str, body: dict[str, int]) -> None: + ... + +The builtin functions :func:`isinstance` and :func:`issubclass` do not accept +``GenericAlias`` types for their second argument:: + + >>> isinstance([1, 2], list[str]) + Traceback (most recent call last): + File "", line 1, in + TypeError: isinstance() argument 2 cannot be a parameterized generic + +The Python runtime does not enforce :term:`type annotations `. +This extends to generic types and their type parameters. When creating +an object from a ``GenericAlias``, container elements are not checked +against their type. For example, the following code is discouraged, but will +run without errors:: + + >>> t = list[str] + >>> t([1, 2, 3]) + [1, 2, 3] + +Furthermore, parameterized generics erase type parameters during object +creation:: + + >>> t = list[str] + >>> type(t) + + + >>> l = t() + >>> type(l) + + +Calling :func:`repr` or :func:`str` on a generic shows the parameterized type:: + + >>> repr(list[int]) + 'list[int]' + + >>> str(list[int]) + 'list[int]' + +The :meth:`__getitem__` method of generics will raise an exception to disallow +mistakes like ``dict[str][str]``:: + + >>> dict[str][str] + Traceback (most recent call last): + File "", line 1, in + TypeError: There are no type variables left in dict[str] + +However, such expressions are valid when :ref:`type variables ` are +used. The index must have as many elements as there are type variable items +in the ``GenericAlias`` object's :attr:`__args__ `. :: + + >>> from typing import TypeVar + >>> Y = TypeVar('Y') + >>> dict[str, Y][int] + dict[str, int] + + +Standard Generic Collections +---------------------------- + +These standard library collections support parameterized generics. + +* :class:`tuple` +* :class:`list` +* :class:`dict` +* :class:`set` +* :class:`frozenset` +* :class:`type` +* :class:`collections.deque` +* :class:`collections.defaultdict` +* :class:`collections.OrderedDict` +* :class:`collections.Counter` +* :class:`collections.ChainMap` +* :class:`collections.abc.Awaitable` +* :class:`collections.abc.Coroutine` +* :class:`collections.abc.AsyncIterable` +* :class:`collections.abc.AsyncIterator` +* :class:`collections.abc.AsyncGenerator` +* :class:`collections.abc.Iterable` +* :class:`collections.abc.Iterator` +* :class:`collections.abc.Generator` +* :class:`collections.abc.Reversible` +* :class:`collections.abc.Container` +* :class:`collections.abc.Collection` +* :class:`collections.abc.Callable` +* :class:`collections.abc.Set` +* :class:`collections.abc.MutableSet` +* :class:`collections.abc.Mapping` +* :class:`collections.abc.MutableMapping` +* :class:`collections.abc.Sequence` +* :class:`collections.abc.MutableSequence` +* :class:`collections.abc.ByteString` +* :class:`collections.abc.MappingView` +* :class:`collections.abc.KeysView` +* :class:`collections.abc.ItemsView` +* :class:`collections.abc.ValuesView` +* :class:`contextlib.AbstractContextManager` +* :class:`contextlib.AbstractAsyncContextManager` +* :ref:`re.Pattern ` +* :ref:`re.Match ` + + +Special Attributes of Generic Alias +----------------------------------- + +All parameterized generics implement special read-only attributes. + +.. attribute:: genericalias.__origin__ + + This attribute points at the non-parameterized generic class:: + + >>> list[int].__origin__ + + + +.. attribute:: genericalias.__args__ + + This attribute is a :class:`tuple` (possibly of length 1) of generic + types passed to the original :meth:`__class_getitem__` + of the generic container:: + + >>> dict[str, list[int]].__args__ + (, list[int]) + + +.. attribute:: genericalias.__parameters__ + + This attribute is a lazily computed tuple (possibly empty) of unique type + variables found in ``__args__``:: + + >>> from typing import TypeVar + + >>> T = TypeVar('T') + >>> list[T].__parameters__ + (~T,) + + +.. seealso:: + + * :pep:`585` -- "Type Hinting Generics In Standard Collections" + * :meth:`__class_getitem__` -- Used to implement parameterized generics. + * :ref:`generics` -- Generics in the :mod:`typing` module. + +.. versionadded:: 3.9 + + .. _typesother: Other Built-in Types diff --git a/Doc/library/types.rst b/Doc/library/types.rst index a88e882305ff3..1573e460f2f2b 100644 --- a/Doc/library/types.rst +++ b/Doc/library/types.rst @@ -242,6 +242,14 @@ Standard names are defined for the following types: Defaults to ``None``. Previously the attribute was optional. +.. data:: GenericAlias + + The type of :ref:`parameterized generics ` such as + ``list[int]``. + + .. versionadded:: 3.9 + + .. class:: TracebackType(tb_next, tb_frame, tb_lasti, tb_lineno) The type of traceback objects such as found in ``sys.exc_info()[2]``. diff --git a/Misc/NEWS.d/next/Documentation/2020-10-10-01-36-37.bpo-41805.l-CGv5.rst b/Misc/NEWS.d/next/Documentation/2020-10-10-01-36-37.bpo-41805.l-CGv5.rst new file mode 100644 index 0000000000000..9c9134350a317 --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2020-10-10-01-36-37.bpo-41805.l-CGv5.rst @@ -0,0 +1,3 @@ +Documented :ref:`generic alias type ` and +:data:`types.GenericAlias`. Also added an entry in glossary for +:term:`generic types `. From webhook-mailer at python.org Thu Oct 29 05:45:11 2020 From: webhook-mailer at python.org (miss-islington) Date: Thu, 29 Oct 2020 09:45:11 -0000 Subject: [Python-checkins] bpo-34204: Use pickle.DEFAULT_PROTOCOL in shelve (GH-19639) Message-ID: https://github.com/python/cpython/commit/df59273c7a384ea8c890fa8e9b80c92825df841c commit: df59273c7a384ea8c890fa8e9b80c92825df841c branch: master author: Zackery Spytz committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-29T02:44:35-07:00 summary: bpo-34204: Use pickle.DEFAULT_PROTOCOL in shelve (GH-19639) Use pickle.DEFAULT_PROTOCOL (currently 5) in shelve instead of a hardcoded 3. files: A Misc/NEWS.d/next/Library/2020-04-21-17-18-33.bpo-34204.9wXTtY.rst M Doc/library/shelve.rst M Doc/whatsnew/3.10.rst M Lib/shelve.py M Lib/test/test_shelve.py diff --git a/Doc/library/shelve.rst b/Doc/library/shelve.rst index f08c58179a2f9..07caf91d5b7d9 100644 --- a/Doc/library/shelve.rst +++ b/Doc/library/shelve.rst @@ -25,8 +25,9 @@ lots of shared sub-objects. The keys are ordinary strings. database file is opened for reading and writing. The optional *flag* parameter has the same interpretation as the *flag* parameter of :func:`dbm.open`. - By default, version 3 pickles are used to serialize values. The version of the - pickle protocol can be specified with the *protocol* parameter. + By default, pickles created with :data:`pickle.DEFAULT_PROTOCOL` are used + to serialize values. The version of the pickle protocol can be specified + with the *protocol* parameter. Because of Python semantics, a shelf cannot know when a mutable persistent-dictionary entry is modified. By default modified objects are @@ -40,6 +41,10 @@ lots of shared sub-objects. The keys are ordinary strings. determine which accessed entries are mutable, nor which ones were actually mutated). + .. versionchanged:: 3.10 + :data:`pickle.DEFAULT_PROTOCOL` is now used as the default pickle + protocol. + .. note:: Do not rely on the shelf being closed automatically; always call @@ -108,9 +113,10 @@ Restrictions A subclass of :class:`collections.abc.MutableMapping` which stores pickled values in the *dict* object. - By default, version 3 pickles are used to serialize values. The version of the - pickle protocol can be specified with the *protocol* parameter. See the - :mod:`pickle` documentation for a discussion of the pickle protocols. + By default, pickles created with :data:`pickle.DEFAULT_PROTOCOL` are used + to serialize values. The version of the pickle protocol can be specified + with the *protocol* parameter. See the :mod:`pickle` documentation for a + discussion of the pickle protocols. If the *writeback* parameter is ``True``, the object will hold a cache of all entries accessed and write them back to the *dict* at sync and close times. @@ -130,6 +136,10 @@ Restrictions .. versionchanged:: 3.4 Added context manager support. + .. versionchanged:: 3.10 + :data:`pickle.DEFAULT_PROTOCOL` is now used as the default pickle + protocol. + .. class:: BsdDbShelf(dict, protocol=None, writeback=False, keyencoding='utf-8') diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index b2c6d10ba8deb..45258db492571 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -210,6 +210,13 @@ py_compile Added ``--quiet`` option to command-line interface of :mod:`py_compile`. (Contributed by Gregory Schevchenko in :issue:`38731`.) +shelve +------ + +The :mod:`shelve` module now uses :data:`pickle.DEFAULT_PROTOCOL` by default +instead of :mod:`pickle` protocol ``3`` when creating shelves. +(Contributed by Zackery Spytz in :issue:`34204`.) + sys --- diff --git a/Lib/shelve.py b/Lib/shelve.py index 5d443a0fa8d4f..e053c397345a0 100644 --- a/Lib/shelve.py +++ b/Lib/shelve.py @@ -56,7 +56,7 @@ the persistent dictionary on disk, if feasible). """ -from pickle import Pickler, Unpickler +from pickle import DEFAULT_PROTOCOL, Pickler, Unpickler from io import BytesIO import collections.abc @@ -85,7 +85,7 @@ def __init__(self, dict, protocol=None, writeback=False, keyencoding="utf-8"): self.dict = dict if protocol is None: - protocol = 3 + protocol = DEFAULT_PROTOCOL self._protocol = protocol self.writeback = writeback self.cache = {} diff --git a/Lib/test/test_shelve.py b/Lib/test/test_shelve.py index ac25eee2e52fd..cfdd67c26c5f5 100644 --- a/Lib/test/test_shelve.py +++ b/Lib/test/test_shelve.py @@ -1,6 +1,8 @@ import unittest import shelve import glob +import pickle + from test import support from test.support import os_helper from collections.abc import MutableMapping @@ -160,7 +162,7 @@ def test_with(self): def test_default_protocol(self): with shelve.Shelf({}) as s: - self.assertEqual(s._protocol, 3) + self.assertEqual(s._protocol, pickle.DEFAULT_PROTOCOL) from test import mapping_tests diff --git a/Misc/NEWS.d/next/Library/2020-04-21-17-18-33.bpo-34204.9wXTtY.rst b/Misc/NEWS.d/next/Library/2020-04-21-17-18-33.bpo-34204.9wXTtY.rst new file mode 100644 index 0000000000000..bce6d39148a37 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-04-21-17-18-33.bpo-34204.9wXTtY.rst @@ -0,0 +1,2 @@ +The :mod:`shelve` module now uses :data:`pickle.DEFAULT_PROTOCOL` by default +instead of :mod:`pickle` protocol ``3``. From webhook-mailer at python.org Thu Oct 29 05:59:30 2020 From: webhook-mailer at python.org (serhiy-storchaka) Date: Thu, 29 Oct 2020 09:59:30 -0000 Subject: [Python-checkins] bpo-42143: Ensure PyFunction_NewWithQualName() can't fail after creating the func object (GH-22953) Message-ID: https://github.com/python/cpython/commit/350526105fa9b131d8b941ae753378b741dabb2f commit: 350526105fa9b131d8b941ae753378b741dabb2f branch: master author: Yonatan Goldschmidt committer: serhiy-storchaka date: 2020-10-29T11:58:52+02:00 summary: bpo-42143: Ensure PyFunction_NewWithQualName() can't fail after creating the func object (GH-22953) func_dealloc() does not handle partially-created objects. Best not to give it any. files: A Misc/NEWS.d/next/Core and Builtins/2020-10-27-21-34-05.bpo-42143.N6KXUO.rst M Objects/funcobject.c diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-10-27-21-34-05.bpo-42143.N6KXUO.rst b/Misc/NEWS.d/next/Core and Builtins/2020-10-27-21-34-05.bpo-42143.N6KXUO.rst new file mode 100644 index 0000000000000..2b16e69da73b5 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2020-10-27-21-34-05.bpo-42143.N6KXUO.rst @@ -0,0 +1,2 @@ +Fix handling of errors during creation of ``PyFunctionObject``, which resulted +in operations on uninitialized memory. Patch by Yonatan Goldschmidt. diff --git a/Objects/funcobject.c b/Objects/funcobject.c index 09a188664e861..9b4302a13c10f 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -19,9 +19,23 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname return NULL; } + /* __module__: If module name is in globals, use it. + Otherwise, use None. */ + module = PyDict_GetItemWithError(globals, __name__); + if (module) { + Py_INCREF(module); + } + else if (PyErr_Occurred()) { + return NULL; + } + op = PyObject_GC_New(PyFunctionObject, &PyFunction_Type); - if (op == NULL) + if (op == NULL) { + Py_XDECREF(module); return NULL; + } + /* Note: No failures from this point on, since func_dealloc() does not + expect a partially-created object. */ op->func_weakreflist = NULL; Py_INCREF(code); @@ -34,6 +48,7 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname op->func_kwdefaults = NULL; /* No keyword only defaults */ op->func_closure = NULL; op->vectorcall = _PyFunction_Vectorcall; + op->func_module = module; consts = ((PyCodeObject *)code)->co_consts; if (PyTuple_Size(consts) >= 1) { @@ -47,20 +62,8 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname op->func_doc = doc; op->func_dict = NULL; - op->func_module = NULL; op->func_annotations = NULL; - /* __module__: If module name is in globals, use it. - Otherwise, use None. */ - module = PyDict_GetItemWithError(globals, __name__); - if (module) { - Py_INCREF(module); - op->func_module = module; - } - else if (PyErr_Occurred()) { - Py_DECREF(op); - return NULL; - } if (qualname) op->func_qualname = qualname; else From webhook-mailer at python.org Thu Oct 29 06:24:17 2020 From: webhook-mailer at python.org (miss-islington) Date: Thu, 29 Oct 2020 10:24:17 -0000 Subject: [Python-checkins] bpo-42143: Ensure PyFunction_NewWithQualName() can't fail after creating the func object (GH-22953) Message-ID: https://github.com/python/cpython/commit/9ede1b071bf5250ba5f2d04d7ba86a24c06d41a1 commit: 9ede1b071bf5250ba5f2d04d7ba86a24c06d41a1 branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-29T03:24:12-07:00 summary: bpo-42143: Ensure PyFunction_NewWithQualName() can't fail after creating the func object (GH-22953) func_dealloc() does not handle partially-created objects. Best not to give it any. (cherry picked from commit 350526105fa9b131d8b941ae753378b741dabb2f) Co-authored-by: Yonatan Goldschmidt files: A Misc/NEWS.d/next/Core and Builtins/2020-10-27-21-34-05.bpo-42143.N6KXUO.rst M Objects/funcobject.c diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-10-27-21-34-05.bpo-42143.N6KXUO.rst b/Misc/NEWS.d/next/Core and Builtins/2020-10-27-21-34-05.bpo-42143.N6KXUO.rst new file mode 100644 index 0000000000000..2b16e69da73b5 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2020-10-27-21-34-05.bpo-42143.N6KXUO.rst @@ -0,0 +1,2 @@ +Fix handling of errors during creation of ``PyFunctionObject``, which resulted +in operations on uninitialized memory. Patch by Yonatan Goldschmidt. diff --git a/Objects/funcobject.c b/Objects/funcobject.c index df5cc2d3f5702..0fc4e127081bf 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -22,9 +22,23 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname return NULL; } + /* __module__: If module name is in globals, use it. + Otherwise, use None. */ + module = PyDict_GetItemWithError(globals, __name__); + if (module) { + Py_INCREF(module); + } + else if (PyErr_Occurred()) { + return NULL; + } + op = PyObject_GC_New(PyFunctionObject, &PyFunction_Type); - if (op == NULL) + if (op == NULL) { + Py_XDECREF(module); return NULL; + } + /* Note: No failures from this point on, since func_dealloc() does not + expect a partially-created object. */ op->func_weakreflist = NULL; Py_INCREF(code); @@ -37,6 +51,7 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname op->func_kwdefaults = NULL; /* No keyword only defaults */ op->func_closure = NULL; op->vectorcall = _PyFunction_Vectorcall; + op->func_module = module; consts = ((PyCodeObject *)code)->co_consts; if (PyTuple_Size(consts) >= 1) { @@ -50,20 +65,8 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname op->func_doc = doc; op->func_dict = NULL; - op->func_module = NULL; op->func_annotations = NULL; - /* __module__: If module name is in globals, use it. - Otherwise, use None. */ - module = PyDict_GetItemWithError(globals, __name__); - if (module) { - Py_INCREF(module); - op->func_module = module; - } - else if (PyErr_Occurred()) { - Py_DECREF(op); - return NULL; - } if (qualname) op->func_qualname = qualname; else From webhook-mailer at python.org Thu Oct 29 07:03:21 2020 From: webhook-mailer at python.org (serhiy-storchaka) Date: Thu, 29 Oct 2020 11:03:21 -0000 Subject: [Python-checkins] bpo-42143: Ensure PyFunction_NewWithQualName() can't fail after creating the func object (GH-22953) (GH-23021) Message-ID: https://github.com/python/cpython/commit/60324d26b58c89d68abb23fb42f1563d395c3910 commit: 60324d26b58c89d68abb23fb42f1563d395c3910 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: serhiy-storchaka date: 2020-10-29T13:02:50+02:00 summary: bpo-42143: Ensure PyFunction_NewWithQualName() can't fail after creating the func object (GH-22953) (GH-23021) func_dealloc() does not handle partially-created objects. Best not to give it any. (cherry picked from commit 350526105fa9b131d8b941ae753378b741dabb2f) Co-authored-by: Yonatan Goldschmidt files: A Misc/NEWS.d/next/Core and Builtins/2020-10-27-21-34-05.bpo-42143.N6KXUO.rst M Objects/funcobject.c diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-10-27-21-34-05.bpo-42143.N6KXUO.rst b/Misc/NEWS.d/next/Core and Builtins/2020-10-27-21-34-05.bpo-42143.N6KXUO.rst new file mode 100644 index 0000000000000..2b16e69da73b5 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2020-10-27-21-34-05.bpo-42143.N6KXUO.rst @@ -0,0 +1,2 @@ +Fix handling of errors during creation of ``PyFunctionObject``, which resulted +in operations on uninitialized memory. Patch by Yonatan Goldschmidt. diff --git a/Objects/funcobject.c b/Objects/funcobject.c index bd24f67b9740a..2c60275d90619 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -20,9 +20,23 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname return NULL; } + /* __module__: If module name is in globals, use it. + Otherwise, use None. */ + module = PyDict_GetItemWithError(globals, __name__); + if (module) { + Py_INCREF(module); + } + else if (PyErr_Occurred()) { + return NULL; + } + op = PyObject_GC_New(PyFunctionObject, &PyFunction_Type); - if (op == NULL) + if (op == NULL) { + Py_XDECREF(module); return NULL; + } + /* Note: No failures from this point on, since func_dealloc() does not + expect a partially-created object. */ op->func_weakreflist = NULL; Py_INCREF(code); @@ -35,6 +49,7 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname op->func_kwdefaults = NULL; /* No keyword only defaults */ op->func_closure = NULL; op->vectorcall = _PyFunction_Vectorcall; + op->func_module = module; consts = ((PyCodeObject *)code)->co_consts; if (PyTuple_Size(consts) >= 1) { @@ -48,20 +63,8 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname op->func_doc = doc; op->func_dict = NULL; - op->func_module = NULL; op->func_annotations = NULL; - /* __module__: If module name is in globals, use it. - Otherwise, use None. */ - module = PyDict_GetItemWithError(globals, __name__); - if (module) { - Py_INCREF(module); - op->func_module = module; - } - else if (PyErr_Occurred()) { - Py_DECREF(op); - return NULL; - } if (qualname) op->func_qualname = qualname; else From webhook-mailer at python.org Thu Oct 29 10:16:44 2020 From: webhook-mailer at python.org (vstinner) Date: Thu, 29 Oct 2020 14:16:44 -0000 Subject: [Python-checkins] bpo-42029: Remove IRIX code (GH-23023) Message-ID: https://github.com/python/cpython/commit/5776663675b48f011d428a5874cc3c79d1deb59e commit: 5776663675b48f011d428a5874cc3c79d1deb59e branch: master author: Victor Stinner committer: vstinner date: 2020-10-29T15:16:23+01:00 summary: bpo-42029: Remove IRIX code (GH-23023) IRIX code was slowy removed in Python 2.4 (--with-sgi-dl), Python 3.3 (Irix threads), and Python 3.7. files: M Doc/library/posix.rst M Lib/platform.py M Modules/posixmodule.c M Modules/resource.c diff --git a/Doc/library/posix.rst b/Doc/library/posix.rst index 2105af9215f76..ad417a17879c1 100644 --- a/Doc/library/posix.rst +++ b/Doc/library/posix.rst @@ -37,7 +37,7 @@ Large File Support .. sectionauthor:: Steve Clift -Several operating systems (including AIX, HP-UX, Irix and Solaris) provide +Several operating systems (including AIX, HP-UX and Solaris) provide support for files that are larger than 2 GiB from a C programming model where :c:type:`int` and :c:type:`long` are 32-bit values. This is typically accomplished by defining the relevant size and offset types as 64-bit values. Such files are @@ -47,8 +47,8 @@ Large file support is enabled in Python when the size of an :c:type:`off_t` is larger than a :c:type:`long` and the :c:type:`long long` is at least as large as an :c:type:`off_t`. It may be necessary to configure and compile Python with certain compiler flags -to enable this mode. For example, it is enabled by default with recent versions -of Irix, but with Solaris 2.6 and 2.7 you need to do something like:: +to enable this mode. For example, with Solaris 2.6 and 2.7 you need to do +something like:: CFLAGS="`getconf LFS_CFLAGS`" OPT="-g -O2 $CFLAGS" \ ./configure diff --git a/Lib/platform.py b/Lib/platform.py index e9f50ab622d31..0eb5167d584f7 100755 --- a/Lib/platform.py +++ b/Lib/platform.py @@ -524,16 +524,6 @@ def system_alias(system, release, version): # XXX Whatever the new SunOS marketing name is... system = 'Solaris' - elif system == 'IRIX64': - # IRIX reports IRIX64 on platforms with 64-bit support; yet it - # is really a version and not a different platform, since 32-bit - # apps are also supported.. - system = 'IRIX' - if version: - version = version + ' (64bit)' - else: - version = '64bit' - elif system in ('win32', 'win16'): # In case one of the other tricks system = 'Windows' @@ -698,9 +688,6 @@ def architecture(executable=sys.executable, bits='', linkage=''): # Bits if '32-bit' in fileout: bits = '32bit' - elif 'N32' in fileout: - # On Irix only - bits = 'n32bit' elif '64-bit' in fileout: bits = '64bit' diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index ccd64d63dd0b3..203f98515dfda 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -6763,12 +6763,11 @@ os_sched_getaffinity_impl(PyObject *module, pid_t pid) /* AIX uses /dev/ptc but is otherwise the same as /dev/ptmx */ -/* IRIX has both /dev/ptc and /dev/ptmx, use ptmx */ #if defined(HAVE_DEV_PTC) && !defined(HAVE_DEV_PTMX) -#define DEV_PTY_FILE "/dev/ptc" -#define HAVE_DEV_PTMX +# define DEV_PTY_FILE "/dev/ptc" +# define HAVE_DEV_PTMX #else -#define DEV_PTY_FILE "/dev/ptmx" +# define DEV_PTY_FILE "/dev/ptmx" #endif #if defined(HAVE_OPENPTY) || defined(HAVE_FORKPTY) || defined(HAVE_DEV_PTMX) diff --git a/Modules/resource.c b/Modules/resource.c index ddbf80be9c69e..4f5dcf8438788 100644 --- a/Modules/resource.c +++ b/Modules/resource.c @@ -313,13 +313,10 @@ resource_getpagesize_impl(PyObject *module) long pagesize = 0; #if defined(HAVE_GETPAGESIZE) pagesize = getpagesize(); -#elif defined(HAVE_SYSCONF) -#if defined(_SC_PAGE_SIZE) +#elif defined(HAVE_SYSCONF) && defined(_SC_PAGE_SIZE) pagesize = sysconf(_SC_PAGE_SIZE); #else - /* Irix 5.3 has _SC_PAGESIZE, but not _SC_PAGE_SIZE */ - pagesize = sysconf(_SC_PAGESIZE); -#endif +# error "unsupported platform: resource.getpagesize()" #endif return pagesize; } From webhook-mailer at python.org Thu Oct 29 16:48:25 2020 From: webhook-mailer at python.org (pablogsal) Date: Thu, 29 Oct 2020 20:48:25 -0000 Subject: [Python-checkins] bpo-42180: fix plural in arguments and control (GH-23015) Message-ID: https://github.com/python/cpython/commit/b76a8400217827e604ebb543f45156f3caacd9a0 commit: b76a8400217827e604ebb543f45156f3caacd9a0 branch: master author: Rafael Fontenelle committer: pablogsal date: 2020-10-29T20:48:21Z summary: bpo-42180: fix plural in arguments and control (GH-23015) https://bugs.python.org/issue42180 files: M Doc/library/functions.rst diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index 52a1512b5884f..a8a4ca42007c6 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -259,7 +259,7 @@ are always available. They are listed here in alphabetical order. interactive statement (in the latter case, expression statements that evaluate to something other than ``None`` will be printed). - The optional argument *flags* and *dont_inherit* controls which + The optional arguments *flags* and *dont_inherit* control which :ref:`compiler options ` should be activated and which :ref:`future features ` should be allowed. If neither is present (or both are zero) the code is compiled with the same flags that From webhook-mailer at python.org Thu Oct 29 18:18:09 2020 From: webhook-mailer at python.org (miss-islington) Date: Thu, 29 Oct 2020 22:18:09 -0000 Subject: [Python-checkins] bpo-42061: Document __format__ for IP addresses (GH-23018) Message-ID: https://github.com/python/cpython/commit/3317466061509c83dce257caab3661d52571cab1 commit: 3317466061509c83dce257caab3661d52571cab1 branch: master author: Teugea Ioan-Teodor committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-29T15:17:59-07:00 summary: bpo-42061: Document __format__ for IP addresses (GH-23018) Automerge-Triggered-By: GH:ericvsmith files: A Misc/NEWS.d/next/Documentation/2020-10-28-21-39-45.bpo-42061._x-0sg.rst M Doc/library/ipaddress.rst M Doc/tools/susp-ignored.csv diff --git a/Doc/library/ipaddress.rst b/Doc/library/ipaddress.rst index 5f5e66412da47..d6d1f1e362137 100644 --- a/Doc/library/ipaddress.rst +++ b/Doc/library/ipaddress.rst @@ -202,6 +202,32 @@ write code that handles both IP versions correctly. Address objects are .. _iana-ipv4-special-registry: https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml .. _iana-ipv6-special-registry: https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml +.. method:: IPv4Address.__format__(fmt) + + Returns a string representation of the IP address, controlled by + an explicit format string. + *fmt* can be one of the following: ``'s'``, the default option, + equivalent to :func:`str`, ``'b'`` for a zero-padded binary string, + ``'X'`` or ``'x'`` for an uppercase or lowercase hexadecimal + representation, or ``'n'``, which is equivalent to ``'b'`` for IPv4 + addresses and ``'x'`` for IPv6. For binary and hexadecimal + representations, the form specifier ``'#'`` and the grouping option + ``'_'`` are available. ``__format__`` is used by ``format``, ``str.format`` + and f-strings. + + >>> format(ipaddress.IPv4Address('192.168.0.1')) + '192.168.0.1' + >>> '{:#b}'.format(ipaddress.IPv4Address('192.168.0.1')) + '0b11000000101010000000000000000001' + >>> f'{ipaddress.IPv6Address("2001:db8::1000"):s}' + '2001:db8::1000' + >>> format(ipaddress.IPv6Address('2001:db8::1000'), '_X') + '2001_0DB8_0000_0000_0000_0000_0000_1000' + >>> '{:#_n}'.format(ipaddress.IPv6Address('2001:db8::1000')) + '0x2001_0db8_0000_0000_0000_0000_0000_1000' + + .. versionadded:: 3.9 + .. class:: IPv6Address(address) @@ -246,8 +272,8 @@ write code that handles both IP versions correctly. Address objects are groups consisting entirely of zeroes included. - For the following attributes, see the corresponding documentation of the - :class:`IPv4Address` class: + For the following attributes and methods, see the corresponding + documentation of the :class:`IPv4Address` class: .. attribute:: packed .. attribute:: reverse_pointer @@ -297,6 +323,12 @@ write code that handles both IP versions correctly. Address objects are the embedded ``(server, client)`` IP address pair. For any other address, this property will be ``None``. +.. method:: IPv6Address.__format__(fmt) + + Refer to the corresponding method documentation in + :class:`IPv4Address`. + + .. versionadded:: 3.9 Conversion to Strings and Integers ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/Doc/tools/susp-ignored.csv b/Doc/tools/susp-ignored.csv index b15fd32b357f0..0c1dec700121b 100644 --- a/Doc/tools/susp-ignored.csv +++ b/Doc/tools/susp-ignored.csv @@ -147,8 +147,10 @@ library/ipaddress,,:db8,IPv6Address('2001:db8::') library/ipaddress,,::,IPv6Address('2001:db8::') library/ipaddress,,:db8,>>> ipaddress.IPv6Address('2001:db8::1000') library/ipaddress,,::,>>> ipaddress.IPv6Address('2001:db8::1000') -library/ipaddress,,:db8,IPv6Address('2001:db8::1000') -library/ipaddress,,::,IPv6Address('2001:db8::1000') +library/ipaddress,,:db8,'2001:db8::1000' +library/ipaddress,,::,'2001:db8::1000' +library/ipaddress,231,:db8,">>> f'{ipaddress.IPv6Address(""2001:db8::1000""):s}'" +library/ipaddress,231,::,">>> f'{ipaddress.IPv6Address(""2001:db8::1000""):s}'" library/ipaddress,,::,IPv6Address('ff02::5678%1') library/ipaddress,,::,fe80::1234 library/ipaddress,,:db8,">>> ipaddress.ip_address(""2001:db8::1"").reverse_pointer" diff --git a/Misc/NEWS.d/next/Documentation/2020-10-28-21-39-45.bpo-42061._x-0sg.rst b/Misc/NEWS.d/next/Documentation/2020-10-28-21-39-45.bpo-42061._x-0sg.rst new file mode 100644 index 0000000000000..b38bb84350171 --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2020-10-28-21-39-45.bpo-42061._x-0sg.rst @@ -0,0 +1 @@ +Document __format__ functionality for IP addresses. \ No newline at end of file From webhook-mailer at python.org Thu Oct 29 18:38:20 2020 From: webhook-mailer at python.org (miss-islington) Date: Thu, 29 Oct 2020 22:38:20 -0000 Subject: [Python-checkins] bpo-42061: Document __format__ for IP addresses (GH-23018) Message-ID: https://github.com/python/cpython/commit/b626d2218c03127502025ccdf93d555e0487ea95 commit: b626d2218c03127502025ccdf93d555e0487ea95 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-29T15:38:12-07:00 summary: bpo-42061: Document __format__ for IP addresses (GH-23018) Automerge-Triggered-By: GH:ericvsmith (cherry picked from commit 3317466061509c83dce257caab3661d52571cab1) Co-authored-by: Teugea Ioan-Teodor files: A Misc/NEWS.d/next/Documentation/2020-10-28-21-39-45.bpo-42061._x-0sg.rst M Doc/library/ipaddress.rst M Doc/tools/susp-ignored.csv diff --git a/Doc/library/ipaddress.rst b/Doc/library/ipaddress.rst index 5f5e66412da47..d6d1f1e362137 100644 --- a/Doc/library/ipaddress.rst +++ b/Doc/library/ipaddress.rst @@ -202,6 +202,32 @@ write code that handles both IP versions correctly. Address objects are .. _iana-ipv4-special-registry: https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml .. _iana-ipv6-special-registry: https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml +.. method:: IPv4Address.__format__(fmt) + + Returns a string representation of the IP address, controlled by + an explicit format string. + *fmt* can be one of the following: ``'s'``, the default option, + equivalent to :func:`str`, ``'b'`` for a zero-padded binary string, + ``'X'`` or ``'x'`` for an uppercase or lowercase hexadecimal + representation, or ``'n'``, which is equivalent to ``'b'`` for IPv4 + addresses and ``'x'`` for IPv6. For binary and hexadecimal + representations, the form specifier ``'#'`` and the grouping option + ``'_'`` are available. ``__format__`` is used by ``format``, ``str.format`` + and f-strings. + + >>> format(ipaddress.IPv4Address('192.168.0.1')) + '192.168.0.1' + >>> '{:#b}'.format(ipaddress.IPv4Address('192.168.0.1')) + '0b11000000101010000000000000000001' + >>> f'{ipaddress.IPv6Address("2001:db8::1000"):s}' + '2001:db8::1000' + >>> format(ipaddress.IPv6Address('2001:db8::1000'), '_X') + '2001_0DB8_0000_0000_0000_0000_0000_1000' + >>> '{:#_n}'.format(ipaddress.IPv6Address('2001:db8::1000')) + '0x2001_0db8_0000_0000_0000_0000_0000_1000' + + .. versionadded:: 3.9 + .. class:: IPv6Address(address) @@ -246,8 +272,8 @@ write code that handles both IP versions correctly. Address objects are groups consisting entirely of zeroes included. - For the following attributes, see the corresponding documentation of the - :class:`IPv4Address` class: + For the following attributes and methods, see the corresponding + documentation of the :class:`IPv4Address` class: .. attribute:: packed .. attribute:: reverse_pointer @@ -297,6 +323,12 @@ write code that handles both IP versions correctly. Address objects are the embedded ``(server, client)`` IP address pair. For any other address, this property will be ``None``. +.. method:: IPv6Address.__format__(fmt) + + Refer to the corresponding method documentation in + :class:`IPv4Address`. + + .. versionadded:: 3.9 Conversion to Strings and Integers ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/Doc/tools/susp-ignored.csv b/Doc/tools/susp-ignored.csv index 80e77be45f95b..7739e7ab7603d 100644 --- a/Doc/tools/susp-ignored.csv +++ b/Doc/tools/susp-ignored.csv @@ -149,8 +149,10 @@ library/ipaddress,,:db8,IPv6Address('2001:db8::') library/ipaddress,,::,IPv6Address('2001:db8::') library/ipaddress,,:db8,>>> ipaddress.IPv6Address('2001:db8::1000') library/ipaddress,,::,>>> ipaddress.IPv6Address('2001:db8::1000') -library/ipaddress,,:db8,IPv6Address('2001:db8::1000') -library/ipaddress,,::,IPv6Address('2001:db8::1000') +library/ipaddress,,:db8,'2001:db8::1000' +library/ipaddress,,::,'2001:db8::1000' +library/ipaddress,231,:db8,">>> f'{ipaddress.IPv6Address(""2001:db8::1000""):s}'" +library/ipaddress,231,::,">>> f'{ipaddress.IPv6Address(""2001:db8::1000""):s}'" library/ipaddress,,::,IPv6Address('ff02::5678%1') library/ipaddress,,::,fe80::1234 library/ipaddress,,:db8,">>> ipaddress.ip_address(""2001:db8::1"").reverse_pointer" diff --git a/Misc/NEWS.d/next/Documentation/2020-10-28-21-39-45.bpo-42061._x-0sg.rst b/Misc/NEWS.d/next/Documentation/2020-10-28-21-39-45.bpo-42061._x-0sg.rst new file mode 100644 index 0000000000000..b38bb84350171 --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2020-10-28-21-39-45.bpo-42061._x-0sg.rst @@ -0,0 +1 @@ +Document __format__ functionality for IP addresses. \ No newline at end of file From webhook-mailer at python.org Fri Oct 30 00:01:38 2020 From: webhook-mailer at python.org (miss-islington) Date: Fri, 30 Oct 2020 04:01:38 -0000 Subject: [Python-checkins] bpo-42198: Link to GenericAlias in typing and expressions (GH-23030) Message-ID: https://github.com/python/cpython/commit/9129af6050b747f288baa9d4e7d43031647ed222 commit: 9129af6050b747f288baa9d4e7d43031647ed222 branch: master author: kj <28750310+Fidget-Spinner at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-29T21:01:17-07:00 summary: bpo-42198: Link to GenericAlias in typing and expressions (GH-23030) Follow up to 7cdf30fff39ea97f403b5472096349998d190e30 and 4173320920706b49a004bdddd8d7108e8984e3fc. This addresses the point "1. Update links in typing, subscription and union to point to GenericAlias." in the bpo for this PR. files: M Doc/library/typing.rst M Doc/reference/expressions.rst diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 402dd24fde6e0..9a993c073277f 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -524,7 +524,8 @@ These can be used as types in annotations using ``[]``, each having a unique syn is equivalent to ``Tuple[Any, ...]``, and in turn to :class:`tuple`. .. deprecated:: 3.9 - :class:`builtins.tuple ` now supports ``[]``. See :pep:`585`. + :class:`builtins.tuple ` now supports ``[]``. See :pep:`585` and + :ref:`types-genericalias`. .. data:: Union @@ -602,7 +603,8 @@ These can be used as types in annotations using ``[]``, each having a unique syn :class:`collections.abc.Callable`. .. deprecated:: 3.9 - :class:`collections.abc.Callable` now supports ``[]``. See :pep:`585`. + :class:`collections.abc.Callable` now supports ``[]``. See :pep:`585` and + :ref:`types-genericalias`. .. class:: Type(Generic[CT_co]) @@ -647,7 +649,8 @@ These can be used as types in annotations using ``[]``, each having a unique syn .. versionadded:: 3.5.2 .. deprecated:: 3.9 - :class:`builtins.type ` now supports ``[]``. See :pep:`585`. + :class:`builtins.type ` now supports ``[]``. See :pep:`585` and + :ref:`types-genericalias`. .. data:: Literal @@ -1079,7 +1082,8 @@ Corresponding to built-in types ... .. deprecated:: 3.9 - :class:`builtins.dict ` now supports ``[]``. See :pep:`585`. + :class:`builtins.dict ` now supports ``[]``. See :pep:`585` and + :ref:`types-genericalias`. .. class:: List(list, MutableSequence[T]) @@ -1099,7 +1103,8 @@ Corresponding to built-in types return [item for item in vector if item > 0] .. deprecated:: 3.9 - :class:`builtins.list ` now supports ``[]``. See :pep:`585`. + :class:`builtins.list ` now supports ``[]``. See :pep:`585` and + :ref:`types-genericalias`. .. class:: Set(set, MutableSet[T]) @@ -1108,14 +1113,16 @@ Corresponding to built-in types to use an abstract collection type such as :class:`AbstractSet`. .. deprecated:: 3.9 - :class:`builtins.set ` now supports ``[]``. See :pep:`585`. + :class:`builtins.set ` now supports ``[]``. See :pep:`585` and + :ref:`types-genericalias`. .. class:: FrozenSet(frozenset, AbstractSet[T_co]) A generic version of :class:`builtins.frozenset `. .. deprecated:: 3.9 - :class:`builtins.frozenset ` now supports ``[]``. See :pep:`585`. + :class:`builtins.frozenset ` now supports ``[]``. See + :pep:`585` and :ref:`types-genericalias`. .. note:: :data:`Tuple` is a special form. @@ -1129,7 +1136,8 @@ Corresponding to types in :mod:`collections` .. versionadded:: 3.5.2 .. deprecated:: 3.9 - :class:`collections.defaultdict` now supports ``[]``. See :pep:`585`. + :class:`collections.defaultdict` now supports ``[]``. See :pep:`585` and + :ref:`types-genericalias`. .. class:: OrderedDict(collections.OrderedDict, MutableMapping[KT, VT]) @@ -1138,7 +1146,8 @@ Corresponding to types in :mod:`collections` .. versionadded:: 3.7.2 .. deprecated:: 3.9 - :class:`collections.OrderedDict` now supports ``[]``. See :pep:`585`. + :class:`collections.OrderedDict` now supports ``[]``. See :pep:`585` and + :ref:`types-genericalias`. .. class:: ChainMap(collections.ChainMap, MutableMapping[KT, VT]) @@ -1148,7 +1157,8 @@ Corresponding to types in :mod:`collections` .. versionadded:: 3.6.1 .. deprecated:: 3.9 - :class:`collections.ChainMap` now supports ``[]``. See :pep:`585`. + :class:`collections.ChainMap` now supports ``[]``. See :pep:`585` and + :ref:`types-genericalias`. .. class:: Counter(collections.Counter, Dict[T, int]) @@ -1158,7 +1168,8 @@ Corresponding to types in :mod:`collections` .. versionadded:: 3.6.1 .. deprecated:: 3.9 - :class:`collections.Counter` now supports ``[]``. See :pep:`585`. + :class:`collections.Counter` now supports ``[]``. See :pep:`585` and + :ref:`types-genericalias`. .. class:: Deque(deque, MutableSequence[T]) @@ -1168,7 +1179,8 @@ Corresponding to types in :mod:`collections` .. versionadded:: 3.6.1 .. deprecated:: 3.9 - :class:`collections.deque` now supports ``[]``. See :pep:`585`. + :class:`collections.deque` now supports ``[]``. See :pep:`585` and + :ref:`types-genericalias`. Other concrete types """""""""""""""""""" @@ -1193,7 +1205,8 @@ Other concrete types ``Match[bytes]``. These types are also in the ``typing.re`` namespace. .. deprecated:: 3.9 - Classes ``Pattern`` and ``Match`` from :mod:`re` now support ``[]``. See :pep:`585`. + Classes ``Pattern`` and ``Match`` from :mod:`re` now support ``[]``. + See :pep:`585` and :ref:`types-genericalias`. .. class:: Text @@ -1220,7 +1233,8 @@ Corresponding to collections in :mod:`collections.abc` A generic version of :class:`collections.abc.Set`. .. deprecated:: 3.9 - :class:`collections.abc.Set` now supports ``[]``. See :pep:`585`. + :class:`collections.abc.Set` now supports ``[]``. See :pep:`585` and + :ref:`types-genericalias`. .. class:: ByteString(Sequence[int]) @@ -1233,7 +1247,8 @@ Corresponding to collections in :mod:`collections.abc` annotate arguments of any of the types mentioned above. .. deprecated:: 3.9 - :class:`collections.abc.ByteString` now supports ``[]``. See :pep:`585`. + :class:`collections.abc.ByteString` now supports ``[]``. See :pep:`585` + and :ref:`types-genericalias`. .. class:: Collection(Sized, Iterable[T_co], Container[T_co]) @@ -1242,28 +1257,32 @@ Corresponding to collections in :mod:`collections.abc` .. versionadded:: 3.6.0 .. deprecated:: 3.9 - :class:`collections.abc.Collection` now supports ``[]``. See :pep:`585`. + :class:`collections.abc.Collection` now supports ``[]``. See :pep:`585` + and :ref:`types-genericalias`. .. class:: Container(Generic[T_co]) A generic version of :class:`collections.abc.Container`. .. deprecated:: 3.9 - :class:`collections.abc.Container` now supports ``[]``. See :pep:`585`. + :class:`collections.abc.Container` now supports ``[]``. See :pep:`585` + and :ref:`types-genericalias`. .. class:: ItemsView(MappingView, Generic[KT_co, VT_co]) A generic version of :class:`collections.abc.ItemsView`. .. deprecated:: 3.9 - :class:`collections.abc.ItemsView` now supports ``[]``. See :pep:`585`. + :class:`collections.abc.ItemsView` now supports ``[]``. See :pep:`585` + and :ref:`types-genericalias`. .. class:: KeysView(MappingView[KT_co], AbstractSet[KT_co]) A generic version of :class:`collections.abc.KeysView`. .. deprecated:: 3.9 - :class:`collections.abc.KeysView` now supports ``[]``. See :pep:`585`. + :class:`collections.abc.KeysView` now supports ``[]``. See :pep:`585` + and :ref:`types-genericalias`. .. class:: Mapping(Sized, Collection[KT], Generic[VT_co]) @@ -1274,49 +1293,56 @@ Corresponding to collections in :mod:`collections.abc` return word_list[word] .. deprecated:: 3.9 - :class:`collections.abc.Mapping` now supports ``[]``. See :pep:`585`. + :class:`collections.abc.Mapping` now supports ``[]``. See :pep:`585` + and :ref:`types-genericalias`. .. class:: MappingView(Sized, Iterable[T_co]) A generic version of :class:`collections.abc.MappingView`. .. deprecated:: 3.9 - :class:`collections.abc.MappingView` now supports ``[]``. See :pep:`585`. + :class:`collections.abc.MappingView` now supports ``[]``. See :pep:`585` + and :ref:`types-genericalias`. .. class:: MutableMapping(Mapping[KT, VT]) A generic version of :class:`collections.abc.MutableMapping`. .. deprecated:: 3.9 - :class:`collections.abc.MutableMapping` now supports ``[]``. See :pep:`585`. + :class:`collections.abc.MutableMapping` now supports ``[]``. See + :pep:`585` and :ref:`types-genericalias`. .. class:: MutableSequence(Sequence[T]) A generic version of :class:`collections.abc.MutableSequence`. .. deprecated:: 3.9 - :class:`collections.abc.MutableSequence` now supports ``[]``. See :pep:`585`. + :class:`collections.abc.MutableSequence` now supports ``[]``. See + :pep:`585` and :ref:`types-genericalias`. .. class:: MutableSet(AbstractSet[T]) A generic version of :class:`collections.abc.MutableSet`. .. deprecated:: 3.9 - :class:`collections.abc.MutableSet` now supports ``[]``. See :pep:`585`. + :class:`collections.abc.MutableSet` now supports ``[]``. See :pep:`585` + and :ref:`types-genericalias`. .. class:: Sequence(Reversible[T_co], Collection[T_co]) A generic version of :class:`collections.abc.Sequence`. .. deprecated:: 3.9 - :class:`collections.abc.Sequence` now supports ``[]``. See :pep:`585`. + :class:`collections.abc.Sequence` now supports ``[]``. See :pep:`585` + and :ref:`types-genericalias`. .. class:: ValuesView(MappingView[VT_co]) A generic version of :class:`collections.abc.ValuesView`. .. deprecated:: 3.9 - :class:`collections.abc.ValuesView` now supports ``[]``. See :pep:`585`. + :class:`collections.abc.ValuesView` now supports ``[]``. See :pep:`585` + and :ref:`types-genericalias`. Corresponding to other types in :mod:`collections.abc` """""""""""""""""""""""""""""""""""""""""""""""""""""" @@ -1326,14 +1352,16 @@ Corresponding to other types in :mod:`collections.abc` A generic version of :class:`collections.abc.Iterable`. .. deprecated:: 3.9 - :class:`collections.abc.Iterable` now supports ``[]``. See :pep:`585`. + :class:`collections.abc.Iterable` now supports ``[]``. See :pep:`585` + and :ref:`types-genericalias`. .. class:: Iterator(Iterable[T_co]) A generic version of :class:`collections.abc.Iterator`. .. deprecated:: 3.9 - :class:`collections.abc.Iterator` now supports ``[]``. See :pep:`585`. + :class:`collections.abc.Iterator` now supports ``[]``. See :pep:`585` + and :ref:`types-genericalias`. .. class:: Generator(Iterator[T_co], Generic[T_co, T_contra, V_co]) @@ -1367,7 +1395,8 @@ Corresponding to other types in :mod:`collections.abc` start += 1 .. deprecated:: 3.9 - :class:`collections.abc.Generator` now supports ``[]``. See :pep:`585`. + :class:`collections.abc.Generator` now supports ``[]``. See :pep:`585` + and :ref:`types-genericalias`. .. class:: Hashable @@ -1378,7 +1407,8 @@ Corresponding to other types in :mod:`collections.abc` A generic version of :class:`collections.abc.Reversible`. .. deprecated:: 3.9 - :class:`collections.abc.Reversible` now supports ``[]``. See :pep:`585`. + :class:`collections.abc.Reversible` now supports ``[]``. See :pep:`585` + and :ref:`types-genericalias`. .. class:: Sized @@ -1403,7 +1433,8 @@ Asynchronous programming .. versionadded:: 3.5.3 .. deprecated:: 3.9 - :class:`collections.abc.Coroutine` now supports ``[]``. See :pep:`585`. + :class:`collections.abc.Coroutine` now supports ``[]``. See :pep:`585` + and :ref:`types-genericalias`. .. class:: AsyncGenerator(AsyncIterator[T_co], Generic[T_co, T_contra]) @@ -1439,7 +1470,8 @@ Asynchronous programming .. versionadded:: 3.6.1 .. deprecated:: 3.9 - :class:`collections.abc.AsyncGenerator` now supports ``[]``. See :pep:`585`. + :class:`collections.abc.AsyncGenerator` now supports ``[]``. See + :pep:`585` and :ref:`types-genericalias`. .. class:: AsyncIterable(Generic[T_co]) @@ -1448,7 +1480,8 @@ Asynchronous programming .. versionadded:: 3.5.2 .. deprecated:: 3.9 - :class:`collections.abc.AsyncIterable` now supports ``[]``. See :pep:`585`. + :class:`collections.abc.AsyncIterable` now supports ``[]``. See :pep:`585` + and :ref:`types-genericalias`. .. class:: AsyncIterator(AsyncIterable[T_co]) @@ -1457,7 +1490,8 @@ Asynchronous programming .. versionadded:: 3.5.2 .. deprecated:: 3.9 - :class:`collections.abc.AsyncIterator` now supports ``[]``. See :pep:`585`. + :class:`collections.abc.AsyncIterator` now supports ``[]``. See :pep:`585` + and :ref:`types-genericalias`. .. class:: Awaitable(Generic[T_co]) @@ -1466,7 +1500,8 @@ Asynchronous programming .. versionadded:: 3.5.2 .. deprecated:: 3.9 - :class:`collections.abc.Awaitable` now supports ``[]``. See :pep:`585`. + :class:`collections.abc.Awaitable` now supports ``[]``. See :pep:`585` + and :ref:`types-genericalias`. Context manager types @@ -1480,7 +1515,8 @@ Context manager types .. versionadded:: 3.6.0 .. deprecated:: 3.9 - :class:`contextlib.AbstractContextManager` now supports ``[]``. See :pep:`585`. + :class:`contextlib.AbstractContextManager` now supports ``[]``. See + :pep:`585` and :ref:`types-genericalias`. .. class:: AsyncContextManager(Generic[T_co]) @@ -1490,7 +1526,8 @@ Context manager types .. versionadded:: 3.6.2 .. deprecated:: 3.9 - :class:`contextlib.AbstractAsyncContextManager` now supports ``[]``. See :pep:`585`. + :class:`contextlib.AbstractAsyncContextManager` now supports ``[]``. See + :pep:`585` and :ref:`types-genericalias`. Protocols --------- diff --git a/Doc/reference/expressions.rst b/Doc/reference/expressions.rst index 938a9732f5a25..512aa5af95619 100644 --- a/Doc/reference/expressions.rst +++ b/Doc/reference/expressions.rst @@ -837,14 +837,8 @@ this method will need to explicitly add that support. A string's items are characters. A character is not a separate data type but a string of exactly one character. -.. - At the time of writing this, there is no documentation for generic alias - or PEP 585. Thus the link currently points to PEP 585 itself. - Please change the link for generic alias to reference the correct - documentation once documentation for PEP 585 becomes available. - Subscription of certain :term:`classes ` or :term:`types ` -creates a `generic alias `_. +creates a :ref:`generic alias `. In this case, user-defined classes can support subscription by providing a :meth:`__class_getitem__` classmethod. From webhook-mailer at python.org Fri Oct 30 00:23:43 2020 From: webhook-mailer at python.org (miss-islington) Date: Fri, 30 Oct 2020 04:23:43 -0000 Subject: [Python-checkins] bpo-42198: Link to GenericAlias in typing and expressions (GH-23030) Message-ID: https://github.com/python/cpython/commit/a39068bf48766e5520e175eae8cda33531e16039 commit: a39068bf48766e5520e175eae8cda33531e16039 branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-29T21:23:33-07:00 summary: bpo-42198: Link to GenericAlias in typing and expressions (GH-23030) Follow up to 7cdf30fff39ea97f403b5472096349998d190e30 and 4173320920706b49a004bdddd8d7108e8984e3fc. This addresses the point "1. Update links in typing, subscription and union to point to GenericAlias." in the bpo for this PR. (cherry picked from commit 9129af6050b747f288baa9d4e7d43031647ed222) Co-authored-by: kj <28750310+Fidget-Spinner at users.noreply.github.com> files: M Doc/library/typing.rst M Doc/reference/expressions.rst diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 67e8a3fa9e50d..a5f8b0403f803 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -509,7 +509,8 @@ These can be used as types in annotations using ``[]``, each having a unique syn is equivalent to ``Tuple[Any, ...]``, and in turn to :class:`tuple`. .. deprecated:: 3.9 - :class:`builtins.tuple ` now supports ``[]``. See :pep:`585`. + :class:`builtins.tuple ` now supports ``[]``. See :pep:`585` and + :ref:`types-genericalias`. .. data:: Union @@ -583,7 +584,8 @@ These can be used as types in annotations using ``[]``, each having a unique syn :class:`collections.abc.Callable`. .. deprecated:: 3.9 - :class:`collections.abc.Callable` now supports ``[]``. See :pep:`585`. + :class:`collections.abc.Callable` now supports ``[]``. See :pep:`585` and + :ref:`types-genericalias`. .. class:: Type(Generic[CT_co]) @@ -628,7 +630,8 @@ These can be used as types in annotations using ``[]``, each having a unique syn .. versionadded:: 3.5.2 .. deprecated:: 3.9 - :class:`builtins.type ` now supports ``[]``. See :pep:`585`. + :class:`builtins.type ` now supports ``[]``. See :pep:`585` and + :ref:`types-genericalias`. .. data:: Literal @@ -1060,7 +1063,8 @@ Corresponding to built-in types ... .. deprecated:: 3.9 - :class:`builtins.dict ` now supports ``[]``. See :pep:`585`. + :class:`builtins.dict ` now supports ``[]``. See :pep:`585` and + :ref:`types-genericalias`. .. class:: List(list, MutableSequence[T]) @@ -1080,7 +1084,8 @@ Corresponding to built-in types return [item for item in vector if item > 0] .. deprecated:: 3.9 - :class:`builtins.list ` now supports ``[]``. See :pep:`585`. + :class:`builtins.list ` now supports ``[]``. See :pep:`585` and + :ref:`types-genericalias`. .. class:: Set(set, MutableSet[T]) @@ -1089,14 +1094,16 @@ Corresponding to built-in types to use an abstract collection type such as :class:`AbstractSet`. .. deprecated:: 3.9 - :class:`builtins.set ` now supports ``[]``. See :pep:`585`. + :class:`builtins.set ` now supports ``[]``. See :pep:`585` and + :ref:`types-genericalias`. .. class:: FrozenSet(frozenset, AbstractSet[T_co]) A generic version of :class:`builtins.frozenset `. .. deprecated:: 3.9 - :class:`builtins.frozenset ` now supports ``[]``. See :pep:`585`. + :class:`builtins.frozenset ` now supports ``[]``. See + :pep:`585` and :ref:`types-genericalias`. .. note:: :data:`Tuple` is a special form. @@ -1110,7 +1117,8 @@ Corresponding to types in :mod:`collections` .. versionadded:: 3.5.2 .. deprecated:: 3.9 - :class:`collections.defaultdict` now supports ``[]``. See :pep:`585`. + :class:`collections.defaultdict` now supports ``[]``. See :pep:`585` and + :ref:`types-genericalias`. .. class:: OrderedDict(collections.OrderedDict, MutableMapping[KT, VT]) @@ -1119,7 +1127,8 @@ Corresponding to types in :mod:`collections` .. versionadded:: 3.7.2 .. deprecated:: 3.9 - :class:`collections.OrderedDict` now supports ``[]``. See :pep:`585`. + :class:`collections.OrderedDict` now supports ``[]``. See :pep:`585` and + :ref:`types-genericalias`. .. class:: ChainMap(collections.ChainMap, MutableMapping[KT, VT]) @@ -1129,7 +1138,8 @@ Corresponding to types in :mod:`collections` .. versionadded:: 3.6.1 .. deprecated:: 3.9 - :class:`collections.ChainMap` now supports ``[]``. See :pep:`585`. + :class:`collections.ChainMap` now supports ``[]``. See :pep:`585` and + :ref:`types-genericalias`. .. class:: Counter(collections.Counter, Dict[T, int]) @@ -1139,7 +1149,8 @@ Corresponding to types in :mod:`collections` .. versionadded:: 3.6.1 .. deprecated:: 3.9 - :class:`collections.Counter` now supports ``[]``. See :pep:`585`. + :class:`collections.Counter` now supports ``[]``. See :pep:`585` and + :ref:`types-genericalias`. .. class:: Deque(deque, MutableSequence[T]) @@ -1149,7 +1160,8 @@ Corresponding to types in :mod:`collections` .. versionadded:: 3.6.1 .. deprecated:: 3.9 - :class:`collections.deque` now supports ``[]``. See :pep:`585`. + :class:`collections.deque` now supports ``[]``. See :pep:`585` and + :ref:`types-genericalias`. Other concrete types """""""""""""""""""" @@ -1174,7 +1186,8 @@ Other concrete types ``Match[bytes]``. These types are also in the ``typing.re`` namespace. .. deprecated:: 3.9 - Classes ``Pattern`` and ``Match`` from :mod:`re` now support ``[]``. See :pep:`585`. + Classes ``Pattern`` and ``Match`` from :mod:`re` now support ``[]``. + See :pep:`585` and :ref:`types-genericalias`. .. class:: Text @@ -1201,7 +1214,8 @@ Corresponding to collections in :mod:`collections.abc` A generic version of :class:`collections.abc.Set`. .. deprecated:: 3.9 - :class:`collections.abc.Set` now supports ``[]``. See :pep:`585`. + :class:`collections.abc.Set` now supports ``[]``. See :pep:`585` and + :ref:`types-genericalias`. .. class:: ByteString(Sequence[int]) @@ -1214,7 +1228,8 @@ Corresponding to collections in :mod:`collections.abc` annotate arguments of any of the types mentioned above. .. deprecated:: 3.9 - :class:`collections.abc.ByteString` now supports ``[]``. See :pep:`585`. + :class:`collections.abc.ByteString` now supports ``[]``. See :pep:`585` + and :ref:`types-genericalias`. .. class:: Collection(Sized, Iterable[T_co], Container[T_co]) @@ -1223,28 +1238,32 @@ Corresponding to collections in :mod:`collections.abc` .. versionadded:: 3.6.0 .. deprecated:: 3.9 - :class:`collections.abc.Collection` now supports ``[]``. See :pep:`585`. + :class:`collections.abc.Collection` now supports ``[]``. See :pep:`585` + and :ref:`types-genericalias`. .. class:: Container(Generic[T_co]) A generic version of :class:`collections.abc.Container`. .. deprecated:: 3.9 - :class:`collections.abc.Container` now supports ``[]``. See :pep:`585`. + :class:`collections.abc.Container` now supports ``[]``. See :pep:`585` + and :ref:`types-genericalias`. .. class:: ItemsView(MappingView, Generic[KT_co, VT_co]) A generic version of :class:`collections.abc.ItemsView`. .. deprecated:: 3.9 - :class:`collections.abc.ItemsView` now supports ``[]``. See :pep:`585`. + :class:`collections.abc.ItemsView` now supports ``[]``. See :pep:`585` + and :ref:`types-genericalias`. .. class:: KeysView(MappingView[KT_co], AbstractSet[KT_co]) A generic version of :class:`collections.abc.KeysView`. .. deprecated:: 3.9 - :class:`collections.abc.KeysView` now supports ``[]``. See :pep:`585`. + :class:`collections.abc.KeysView` now supports ``[]``. See :pep:`585` + and :ref:`types-genericalias`. .. class:: Mapping(Sized, Collection[KT], Generic[VT_co]) @@ -1255,49 +1274,56 @@ Corresponding to collections in :mod:`collections.abc` return word_list[word] .. deprecated:: 3.9 - :class:`collections.abc.Mapping` now supports ``[]``. See :pep:`585`. + :class:`collections.abc.Mapping` now supports ``[]``. See :pep:`585` + and :ref:`types-genericalias`. .. class:: MappingView(Sized, Iterable[T_co]) A generic version of :class:`collections.abc.MappingView`. .. deprecated:: 3.9 - :class:`collections.abc.MappingView` now supports ``[]``. See :pep:`585`. + :class:`collections.abc.MappingView` now supports ``[]``. See :pep:`585` + and :ref:`types-genericalias`. .. class:: MutableMapping(Mapping[KT, VT]) A generic version of :class:`collections.abc.MutableMapping`. .. deprecated:: 3.9 - :class:`collections.abc.MutableMapping` now supports ``[]``. See :pep:`585`. + :class:`collections.abc.MutableMapping` now supports ``[]``. See + :pep:`585` and :ref:`types-genericalias`. .. class:: MutableSequence(Sequence[T]) A generic version of :class:`collections.abc.MutableSequence`. .. deprecated:: 3.9 - :class:`collections.abc.MutableSequence` now supports ``[]``. See :pep:`585`. + :class:`collections.abc.MutableSequence` now supports ``[]``. See + :pep:`585` and :ref:`types-genericalias`. .. class:: MutableSet(AbstractSet[T]) A generic version of :class:`collections.abc.MutableSet`. .. deprecated:: 3.9 - :class:`collections.abc.MutableSet` now supports ``[]``. See :pep:`585`. + :class:`collections.abc.MutableSet` now supports ``[]``. See :pep:`585` + and :ref:`types-genericalias`. .. class:: Sequence(Reversible[T_co], Collection[T_co]) A generic version of :class:`collections.abc.Sequence`. .. deprecated:: 3.9 - :class:`collections.abc.Sequence` now supports ``[]``. See :pep:`585`. + :class:`collections.abc.Sequence` now supports ``[]``. See :pep:`585` + and :ref:`types-genericalias`. .. class:: ValuesView(MappingView[VT_co]) A generic version of :class:`collections.abc.ValuesView`. .. deprecated:: 3.9 - :class:`collections.abc.ValuesView` now supports ``[]``. See :pep:`585`. + :class:`collections.abc.ValuesView` now supports ``[]``. See :pep:`585` + and :ref:`types-genericalias`. Corresponding to other types in :mod:`collections.abc` """""""""""""""""""""""""""""""""""""""""""""""""""""" @@ -1307,14 +1333,16 @@ Corresponding to other types in :mod:`collections.abc` A generic version of :class:`collections.abc.Iterable`. .. deprecated:: 3.9 - :class:`collections.abc.Iterable` now supports ``[]``. See :pep:`585`. + :class:`collections.abc.Iterable` now supports ``[]``. See :pep:`585` + and :ref:`types-genericalias`. .. class:: Iterator(Iterable[T_co]) A generic version of :class:`collections.abc.Iterator`. .. deprecated:: 3.9 - :class:`collections.abc.Iterator` now supports ``[]``. See :pep:`585`. + :class:`collections.abc.Iterator` now supports ``[]``. See :pep:`585` + and :ref:`types-genericalias`. .. class:: Generator(Iterator[T_co], Generic[T_co, T_contra, V_co]) @@ -1348,7 +1376,8 @@ Corresponding to other types in :mod:`collections.abc` start += 1 .. deprecated:: 3.9 - :class:`collections.abc.Generator` now supports ``[]``. See :pep:`585`. + :class:`collections.abc.Generator` now supports ``[]``. See :pep:`585` + and :ref:`types-genericalias`. .. class:: Hashable @@ -1359,7 +1388,8 @@ Corresponding to other types in :mod:`collections.abc` A generic version of :class:`collections.abc.Reversible`. .. deprecated:: 3.9 - :class:`collections.abc.Reversible` now supports ``[]``. See :pep:`585`. + :class:`collections.abc.Reversible` now supports ``[]``. See :pep:`585` + and :ref:`types-genericalias`. .. class:: Sized @@ -1384,7 +1414,8 @@ Asynchronous programming .. versionadded:: 3.5.3 .. deprecated:: 3.9 - :class:`collections.abc.Coroutine` now supports ``[]``. See :pep:`585`. + :class:`collections.abc.Coroutine` now supports ``[]``. See :pep:`585` + and :ref:`types-genericalias`. .. class:: AsyncGenerator(AsyncIterator[T_co], Generic[T_co, T_contra]) @@ -1420,7 +1451,8 @@ Asynchronous programming .. versionadded:: 3.6.1 .. deprecated:: 3.9 - :class:`collections.abc.AsyncGenerator` now supports ``[]``. See :pep:`585`. + :class:`collections.abc.AsyncGenerator` now supports ``[]``. See + :pep:`585` and :ref:`types-genericalias`. .. class:: AsyncIterable(Generic[T_co]) @@ -1429,7 +1461,8 @@ Asynchronous programming .. versionadded:: 3.5.2 .. deprecated:: 3.9 - :class:`collections.abc.AsyncIterable` now supports ``[]``. See :pep:`585`. + :class:`collections.abc.AsyncIterable` now supports ``[]``. See :pep:`585` + and :ref:`types-genericalias`. .. class:: AsyncIterator(AsyncIterable[T_co]) @@ -1438,7 +1471,8 @@ Asynchronous programming .. versionadded:: 3.5.2 .. deprecated:: 3.9 - :class:`collections.abc.AsyncIterator` now supports ``[]``. See :pep:`585`. + :class:`collections.abc.AsyncIterator` now supports ``[]``. See :pep:`585` + and :ref:`types-genericalias`. .. class:: Awaitable(Generic[T_co]) @@ -1447,7 +1481,8 @@ Asynchronous programming .. versionadded:: 3.5.2 .. deprecated:: 3.9 - :class:`collections.abc.Awaitable` now supports ``[]``. See :pep:`585`. + :class:`collections.abc.Awaitable` now supports ``[]``. See :pep:`585` + and :ref:`types-genericalias`. Context manager types @@ -1461,7 +1496,8 @@ Context manager types .. versionadded:: 3.6.0 .. deprecated:: 3.9 - :class:`contextlib.AbstractContextManager` now supports ``[]``. See :pep:`585`. + :class:`contextlib.AbstractContextManager` now supports ``[]``. See + :pep:`585` and :ref:`types-genericalias`. .. class:: AsyncContextManager(Generic[T_co]) @@ -1471,7 +1507,8 @@ Context manager types .. versionadded:: 3.6.2 .. deprecated:: 3.9 - :class:`contextlib.AbstractAsyncContextManager` now supports ``[]``. See :pep:`585`. + :class:`contextlib.AbstractAsyncContextManager` now supports ``[]``. See + :pep:`585` and :ref:`types-genericalias`. Protocols --------- diff --git a/Doc/reference/expressions.rst b/Doc/reference/expressions.rst index 938a9732f5a25..512aa5af95619 100644 --- a/Doc/reference/expressions.rst +++ b/Doc/reference/expressions.rst @@ -837,14 +837,8 @@ this method will need to explicitly add that support. A string's items are characters. A character is not a separate data type but a string of exactly one character. -.. - At the time of writing this, there is no documentation for generic alias - or PEP 585. Thus the link currently points to PEP 585 itself. - Please change the link for generic alias to reference the correct - documentation once documentation for PEP 585 becomes available. - Subscription of certain :term:`classes ` or :term:`types ` -creates a `generic alias `_. +creates a :ref:`generic alias `. In this case, user-defined classes can support subscription by providing a :meth:`__class_getitem__` classmethod. From webhook-mailer at python.org Fri Oct 30 00:56:41 2020 From: webhook-mailer at python.org (methane) Date: Fri, 30 Oct 2020 04:56:41 -0000 Subject: [Python-checkins] bpo-42160: tempfile: Reduce overhead of pid check. (GH-22997) Message-ID: https://github.com/python/cpython/commit/8e409cebad42032bb7d0f2cadd8b1e36081d98af commit: 8e409cebad42032bb7d0f2cadd8b1e36081d98af branch: master author: Eric W committer: methane date: 2020-10-30T13:56:28+09:00 summary: bpo-42160: tempfile: Reduce overhead of pid check. (GH-22997) The _RandomSequence class in tempfile used to check the current pid every time its rng property was used. This commit replaces this code with `os.register_at_fork` to reduce the overhead. files: A Misc/NEWS.d/next/Library/2020-10-27-00-42-09.bpo-42160.eiLOCi.rst M Lib/tempfile.py M Lib/test/test_tempfile.py diff --git a/Lib/tempfile.py b/Lib/tempfile.py index 770f72c25295c..1bc5c71fd0349 100644 --- a/Lib/tempfile.py +++ b/Lib/tempfile.py @@ -129,24 +129,22 @@ class _RandomNameSequence: _RandomNameSequence is an iterator.""" - characters = "abcdefghijklmnopqrstuvwxyz0123456789_" - - @property - def rng(self): - cur_pid = _os.getpid() - if cur_pid != getattr(self, '_rng_pid', None): - self._rng = _Random() - self._rng_pid = cur_pid - return self._rng + def __init__(self, characters="abcdefghijklmnopqrstuvwxyz0123456789_", length=8, rng=None): + if rng is None: + rng = _Random() + if hasattr(_os, "fork"): + # prevent same state after fork + _os.register_at_fork(after_in_child=rng.seed) + self.rng = rng + self.characters = characters + self.length = length def __iter__(self): return self def __next__(self): c = self.characters - choose = self.rng.choice - letters = [choose(c) for dummy in range(8)] - return ''.join(letters) + return ''.join(self.rng.choices(c, k=self.length)) def _candidate_tempdir_list(): """Generate a list of candidate temporary directories which diff --git a/Lib/test/test_tempfile.py b/Lib/test/test_tempfile.py index 8ace883d74bb2..77d710efaf107 100644 --- a/Lib/test/test_tempfile.py +++ b/Lib/test/test_tempfile.py @@ -153,8 +153,8 @@ def setUp(self): self.r = tempfile._RandomNameSequence() super().setUp() - def test_get_six_char_str(self): - # _RandomNameSequence returns a six-character string + def test_get_eight_char_str(self): + # _RandomNameSequence returns a eight-character string s = next(self.r) self.nameCheck(s, '', '', '') diff --git a/Misc/NEWS.d/next/Library/2020-10-27-00-42-09.bpo-42160.eiLOCi.rst b/Misc/NEWS.d/next/Library/2020-10-27-00-42-09.bpo-42160.eiLOCi.rst new file mode 100644 index 0000000000000..c5f3091283a87 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-10-27-00-42-09.bpo-42160.eiLOCi.rst @@ -0,0 +1 @@ +Replaced pid check in ``tempfile._RandomNameSequence`` with ``os.register_at_fork`` to reduce overhead. From webhook-mailer at python.org Fri Oct 30 02:04:00 2020 From: webhook-mailer at python.org (corona10) Date: Fri, 30 Oct 2020 06:04:00 -0000 Subject: [Python-checkins] bpo-42172: Correct typo for test_socket.py (GH-23013) Message-ID: https://github.com/python/cpython/commit/d6238ba82d07e8e0783b692f37dc4b7c8617294b commit: d6238ba82d07e8e0783b692f37dc4b7c8617294b branch: master author: Akashkumar D Khunt committer: corona10 date: 2020-10-30T15:03:51+09:00 summary: bpo-42172: Correct typo for test_socket.py (GH-23013) files: M Lib/test/test_socket.py diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 67ac045330f3e..80638325ba3a0 100755 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -4129,7 +4129,7 @@ def _testSecondCmsgTruncLen0Minus1(self): @requireAttrs(socket, "CMSG_SPACE", "IPV6_RECVHOPLIMIT", "IPV6_HOPLIMIT", "IPV6_RECVTCLASS", "IPV6_TCLASS") - def testSecomdCmsgTruncInData(self): + def testSecondCmsgTruncInData(self): # Test truncation of the second of two control messages inside # its associated data. self.serv_sock.setsockopt(socket.IPPROTO_IPV6, @@ -4164,8 +4164,8 @@ def testSecomdCmsgTruncInData(self): self.assertEqual(ancdata, []) - @testSecomdCmsgTruncInData.client_skip - def _testSecomdCmsgTruncInData(self): + @testSecondCmsgTruncInData.client_skip + def _testSecondCmsgTruncInData(self): self.assertTrue(self.misc_event.wait(timeout=self.fail_timeout)) self.sendToServer(MSG) From webhook-mailer at python.org Fri Oct 30 07:49:11 2020 From: webhook-mailer at python.org (pablogsal) Date: Fri, 30 Oct 2020 11:49:11 -0000 Subject: [Python-checkins] bpo-42206: Propagate and raise errors from PyAST_Validate in the parser (GH-23035) Message-ID: https://github.com/python/cpython/commit/3af4b585527743e455145d294049c46b7c823ed9 commit: 3af4b585527743e455145d294049c46b7c823ed9 branch: master author: Batuhan Taskaya committer: pablogsal date: 2020-10-30T11:48:41Z summary: bpo-42206: Propagate and raise errors from PyAST_Validate in the parser (GH-23035) files: A Misc/NEWS.d/next/Core and Builtins/2020-10-30-13-11-01.bpo-42206.xxssR8.rst M Parser/pegen.c diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-10-30-13-11-01.bpo-42206.xxssR8.rst b/Misc/NEWS.d/next/Core and Builtins/2020-10-30-13-11-01.bpo-42206.xxssR8.rst new file mode 100644 index 0000000000000..b9eb135fec5e9 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2020-10-30-13-11-01.bpo-42206.xxssR8.rst @@ -0,0 +1,2 @@ +Propagate and raise the errors caused by :c:func:`PyAST_Validate` in the +parser. diff --git a/Parser/pegen.c b/Parser/pegen.c index 827d4dace1fc3..216edd810e246 100644 --- a/Parser/pegen.c +++ b/Parser/pegen.c @@ -1157,7 +1157,9 @@ _PyPegen_run_parser(Parser *p) p->start_rule == Py_file_input || p->start_rule == Py_eval_input) { - assert(PyAST_Validate(res)); + if (!PyAST_Validate(res)) { + return NULL; + } } #endif return res; From webhook-mailer at python.org Fri Oct 30 10:52:49 2020 From: webhook-mailer at python.org (pablogsal) Date: Fri, 30 Oct 2020 14:52:49 -0000 Subject: [Python-checkins] DOC: attribute PyPy for the idea behind LOAD_ATTR cache (GH-23036) Message-ID: https://github.com/python/cpython/commit/99608c733c5960f7834adca933c02f6ddf9b1df9 commit: 99608c733c5960f7834adca933c02f6ddf9b1df9 branch: master author: Matti Picus committer: pablogsal date: 2020-10-30T14:52:39Z summary: DOC: attribute PyPy for the idea behind LOAD_ATTR cache (GH-23036) Co-authored-by: Pablo Galindo files: M Doc/whatsnew/3.10.rst diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index 45258db492571..f4d72908486dd 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -261,7 +261,7 @@ Optimizations * The ``LOAD_ATTR`` instruction now uses new "per opcode cache" mechanism. It is about 36% faster now. (Contributed by Pablo Galindo and Yury Selivanov - in :issue:`42093`.) + in :issue:`42093`, based on ideas implemented originally in PyPy and MicroPython.) * When building Python with ``--enable-optimizations`` now ``-fno-semantic-interposition`` is added to both the compile and link line. From webhook-mailer at python.org Fri Oct 30 12:00:09 2020 From: webhook-mailer at python.org (vstinner) Date: Fri, 30 Oct 2020 16:00:09 -0000 Subject: [Python-checkins] bpo-42208: Pass tstate to _PyGC_CollectNoFail() (GH-23038) Message-ID: https://github.com/python/cpython/commit/8b3414818f5289eac530bf38bcfbd7b2b851805c commit: 8b3414818f5289eac530bf38bcfbd7b2b851805c branch: master author: Victor Stinner committer: vstinner date: 2020-10-30T17:00:00+01:00 summary: bpo-42208: Pass tstate to _PyGC_CollectNoFail() (GH-23038) Move private _PyGC_CollectNoFail() to the internal C API. Remove the private _PyGC_CollectIfEnabled() which was just an alias to the public PyGC_Collect() function since Python 3.8. Rename functions: * collect() => gc_collect_main() * collect_with_callback() => gc_collect_with_callback() * collect_generations() => gc_collect_generations() files: M Include/cpython/objimpl.h M Include/internal/pycore_gc.h M Modules/gcmodule.c M Python/import.c M Python/pylifecycle.c diff --git a/Include/cpython/objimpl.h b/Include/cpython/objimpl.h index 15999a239f7a9..d83700e2a4647 100644 --- a/Include/cpython/objimpl.h +++ b/Include/cpython/objimpl.h @@ -79,10 +79,6 @@ PyAPI_FUNC(void) PyObject_GetArenaAllocator(PyObjectArenaAllocator *allocator); PyAPI_FUNC(void) PyObject_SetArenaAllocator(PyObjectArenaAllocator *allocator); -PyAPI_FUNC(Py_ssize_t) _PyGC_CollectNoFail(void); -PyAPI_FUNC(Py_ssize_t) _PyGC_CollectIfEnabled(void); - - /* Test if an object implements the garbage collector protocol */ PyAPI_FUNC(int) PyObject_IS_GC(PyObject *obj); diff --git a/Include/internal/pycore_gc.h b/Include/internal/pycore_gc.h index da202a1df532e..e2d47c90c10d8 100644 --- a/Include/internal/pycore_gc.h +++ b/Include/internal/pycore_gc.h @@ -161,7 +161,9 @@ struct _gc_runtime_state { Py_ssize_t long_lived_pending; }; -PyAPI_FUNC(void) _PyGC_InitState(struct _gc_runtime_state *); +extern void _PyGC_InitState(struct _gc_runtime_state *); + +extern Py_ssize_t _PyGC_CollectNoFail(PyThreadState *tstate); // Functions to clear types free lists diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c index 8833400caba75..d90ff33684fe8 100644 --- a/Modules/gcmodule.c +++ b/Modules/gcmodule.c @@ -1176,8 +1176,9 @@ handle_resurrected_objects(PyGC_Head *unreachable, PyGC_Head* still_unreachable, /* This is the main function. Read this to understand how the * collection process works. */ static Py_ssize_t -collect(PyThreadState *tstate, int generation, - Py_ssize_t *n_collected, Py_ssize_t *n_uncollectable, int nofail) +gc_collect_main(PyThreadState *tstate, int generation, + Py_ssize_t *n_collected, Py_ssize_t *n_uncollectable, + int nofail) { int i; Py_ssize_t m = 0; /* # objects collected */ @@ -1395,19 +1396,19 @@ invoke_gc_callback(PyThreadState *tstate, const char *phase, * progress callbacks. */ static Py_ssize_t -collect_with_callback(PyThreadState *tstate, int generation) +gc_collect_with_callback(PyThreadState *tstate, int generation) { assert(!_PyErr_Occurred(tstate)); Py_ssize_t result, collected, uncollectable; invoke_gc_callback(tstate, "start", generation, 0, 0); - result = collect(tstate, generation, &collected, &uncollectable, 0); + result = gc_collect_main(tstate, generation, &collected, &uncollectable, 0); invoke_gc_callback(tstate, "stop", generation, collected, uncollectable); assert(!_PyErr_Occurred(tstate)); return result; } static Py_ssize_t -collect_generations(PyThreadState *tstate) +gc_collect_generations(PyThreadState *tstate) { GCState *gcstate = &tstate->interp->gc; /* Find the oldest generation (highest numbered) where the count @@ -1455,7 +1456,7 @@ collect_generations(PyThreadState *tstate) if (i == NUM_GENERATIONS - 1 && gcstate->long_lived_pending < gcstate->long_lived_total / 4) continue; - n = collect_with_callback(tstate, i); + n = gc_collect_with_callback(tstate, i); break; } } @@ -1541,7 +1542,7 @@ gc_collect_impl(PyObject *module, int generation) } else { gcstate->collecting = 1; - n = collect_with_callback(tstate, generation); + n = gc_collect_with_callback(tstate, generation); gcstate->collecting = 0; } return n; @@ -2041,7 +2042,7 @@ PyInit_gc(void) return m; } -/* API to invoke gc.collect() from C */ +/* Public API to invoke gc.collect() from C */ Py_ssize_t PyGC_Collect(void) { @@ -2061,7 +2062,7 @@ PyGC_Collect(void) PyObject *exc, *value, *tb; gcstate->collecting = 1; _PyErr_Fetch(tstate, &exc, &value, &tb); - n = collect_with_callback(tstate, NUM_GENERATIONS - 1); + n = gc_collect_with_callback(tstate, NUM_GENERATIONS - 1); _PyErr_Restore(tstate, exc, value, tb); gcstate->collecting = 0; } @@ -2070,19 +2071,11 @@ PyGC_Collect(void) } Py_ssize_t -_PyGC_CollectIfEnabled(void) +_PyGC_CollectNoFail(PyThreadState *tstate) { - return PyGC_Collect(); -} - -Py_ssize_t -_PyGC_CollectNoFail(void) -{ - PyThreadState *tstate = _PyThreadState_GET(); assert(!_PyErr_Occurred(tstate)); GCState *gcstate = &tstate->interp->gc; - Py_ssize_t n; /* Ideally, this function is only called on interpreter shutdown, and therefore not recursively. Unfortunately, when there are daemon @@ -2091,13 +2084,13 @@ _PyGC_CollectNoFail(void) See http://bugs.python.org/issue8713#msg195178 for an example. */ if (gcstate->collecting) { - n = 0; - } - else { - gcstate->collecting = 1; - n = collect(tstate, NUM_GENERATIONS - 1, NULL, NULL, 1); - gcstate->collecting = 0; + return 0; } + + Py_ssize_t n; + gcstate->collecting = 1; + n = gc_collect_main(tstate, NUM_GENERATIONS - 1, NULL, NULL, 1); + gcstate->collecting = 0; return n; } @@ -2240,7 +2233,7 @@ _PyObject_GC_Alloc(int use_calloc, size_t basicsize) !_PyErr_Occurred(tstate)) { gcstate->collecting = 1; - collect_generations(tstate); + gc_collect_generations(tstate); gcstate->collecting = 0; } PyObject *op = FROM_GC(g); diff --git a/Python/import.c b/Python/import.c index b79bda058db82..8b9cc3066fc4a 100644 --- a/Python/import.c +++ b/Python/import.c @@ -566,7 +566,7 @@ _PyImport_Cleanup(PyThreadState *tstate) } Py_XDECREF(dict); /* Collect references */ - _PyGC_CollectNoFail(); + _PyGC_CollectNoFail(tstate); /* Dump GC stats before it's too late, since it uses the warnings machinery. */ _PyGC_DumpShutdownStats(tstate); @@ -626,7 +626,7 @@ _PyImport_Cleanup(PyThreadState *tstate) Py_DECREF(modules); /* Once more */ - _PyGC_CollectNoFail(); + _PyGC_CollectNoFail(tstate); #undef CLEAR_MODULE #undef STORE_MODULE_WEAKREF diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 774a4f9de08e0..71834f63f2a78 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -1293,7 +1293,7 @@ finalize_interp_clear(PyThreadState *tstate) PyInterpreterState_Clear(tstate->interp); /* Last explicit GC collection */ - _PyGC_CollectNoFail(); + _PyGC_CollectNoFail(tstate); /* Clear all loghooks */ /* Both _PySys_Audit function and users still need PyObject, such as tuple. @@ -1414,7 +1414,7 @@ Py_FinalizeEx(void) * XXX but I'm unclear on exactly how that one happens. In any case, * XXX I haven't seen a real-life report of either of these. */ - _PyGC_CollectIfEnabled(); + PyGC_Collect(); /* Destroy all modules */ _PyImport_Cleanup(tstate); From webhook-mailer at python.org Fri Oct 30 13:03:36 2020 From: webhook-mailer at python.org (vstinner) Date: Fri, 30 Oct 2020 17:03:36 -0000 Subject: [Python-checkins] bpo-42208: Move _PyImport_Cleanup() to pylifecycle.c (GH-23040) Message-ID: https://github.com/python/cpython/commit/dff1ad509051f7e07e77d1e3ec83314d53fb1118 commit: dff1ad509051f7e07e77d1e3ec83314d53fb1118 branch: master author: Victor Stinner committer: vstinner date: 2020-10-30T18:03:28+01:00 summary: bpo-42208: Move _PyImport_Cleanup() to pylifecycle.c (GH-23040) Move _PyImport_Cleanup() to pylifecycle.c, rename it to finalize_modules(), split it (200 lines) into many smaller sub-functions and cleanup the code. files: M Python/import.c M Python/pylifecycle.c diff --git a/Python/import.c b/Python/import.c index 8b9cc3066fc4a..77e6baef011e3 100644 --- a/Python/import.c +++ b/Python/import.c @@ -406,233 +406,6 @@ import_ensure_initialized(PyThreadState *tstate, PyObject *mod, PyObject *name) } -/* List of names to clear in sys */ -static const char * const sys_deletes[] = { - "path", "argv", "ps1", "ps2", - "last_type", "last_value", "last_traceback", - "path_hooks", "path_importer_cache", "meta_path", - "__interactivehook__", - NULL -}; - -static const char * const sys_files[] = { - "stdin", "__stdin__", - "stdout", "__stdout__", - "stderr", "__stderr__", - NULL -}; - -/* Un-initialize things, as good as we can */ - -void -_PyImport_Cleanup(PyThreadState *tstate) -{ - PyInterpreterState *interp = tstate->interp; - PyObject *modules = interp->modules; - if (modules == NULL) { - /* Already done */ - return; - } - - /* Delete some special variables first. These are common - places where user values hide and people complain when their - destructors fail. Since the modules containing them are - deleted *last* of all, they would come too late in the normal - destruction order. Sigh. */ - - /* XXX Perhaps these precautions are obsolete. Who knows? */ - - int verbose = _PyInterpreterState_GetConfig(interp)->verbose; - if (verbose) { - PySys_WriteStderr("# clear builtins._\n"); - } - if (PyDict_SetItemString(interp->builtins, "_", Py_None) < 0) { - PyErr_WriteUnraisable(NULL); - } - - const char * const *p; - for (p = sys_deletes; *p != NULL; p++) { - if (verbose) { - PySys_WriteStderr("# clear sys.%s\n", *p); - } - if (PyDict_SetItemString(interp->sysdict, *p, Py_None) < 0) { - PyErr_WriteUnraisable(NULL); - } - } - for (p = sys_files; *p != NULL; p+=2) { - if (verbose) { - PySys_WriteStderr("# restore sys.%s\n", *p); - } - PyObject *value = _PyDict_GetItemStringWithError(interp->sysdict, - *(p+1)); - if (value == NULL) { - if (_PyErr_Occurred(tstate)) { - PyErr_WriteUnraisable(NULL); - } - value = Py_None; - } - if (PyDict_SetItemString(interp->sysdict, *p, value) < 0) { - PyErr_WriteUnraisable(NULL); - } - } - - /* We prepare a list which will receive (name, weakref) tuples of - modules when they are removed from sys.modules. The name is used - for diagnosis messages (in verbose mode), while the weakref helps - detect those modules which have been held alive. */ - PyObject *weaklist = PyList_New(0); - if (weaklist == NULL) { - PyErr_WriteUnraisable(NULL); - } - -#define STORE_MODULE_WEAKREF(name, mod) \ - if (weaklist != NULL) { \ - PyObject *wr = PyWeakref_NewRef(mod, NULL); \ - if (wr) { \ - PyObject *tup = PyTuple_Pack(2, name, wr); \ - if (!tup || PyList_Append(weaklist, tup) < 0) { \ - PyErr_WriteUnraisable(NULL); \ - } \ - Py_XDECREF(tup); \ - Py_DECREF(wr); \ - } \ - else { \ - PyErr_WriteUnraisable(NULL); \ - } \ - } -#define CLEAR_MODULE(name, mod) \ - if (PyModule_Check(mod)) { \ - if (verbose && PyUnicode_Check(name)) { \ - PySys_FormatStderr("# cleanup[2] removing %U\n", name); \ - } \ - STORE_MODULE_WEAKREF(name, mod); \ - if (PyObject_SetItem(modules, name, Py_None) < 0) { \ - PyErr_WriteUnraisable(NULL); \ - } \ - } - - /* Remove all modules from sys.modules, hoping that garbage collection - can reclaim most of them. */ - if (PyDict_CheckExact(modules)) { - Py_ssize_t pos = 0; - PyObject *key, *value; - while (PyDict_Next(modules, &pos, &key, &value)) { - CLEAR_MODULE(key, value); - } - } - else { - PyObject *iterator = PyObject_GetIter(modules); - if (iterator == NULL) { - PyErr_WriteUnraisable(NULL); - } - else { - PyObject *key; - while ((key = PyIter_Next(iterator))) { - PyObject *value = PyObject_GetItem(modules, key); - if (value == NULL) { - PyErr_WriteUnraisable(NULL); - continue; - } - CLEAR_MODULE(key, value); - Py_DECREF(value); - Py_DECREF(key); - } - if (PyErr_Occurred()) { - PyErr_WriteUnraisable(NULL); - } - Py_DECREF(iterator); - } - } - - /* Clear the modules dict. */ - if (PyDict_CheckExact(modules)) { - PyDict_Clear(modules); - } - else { - _Py_IDENTIFIER(clear); - if (_PyObject_CallMethodIdNoArgs(modules, &PyId_clear) == NULL) { - PyErr_WriteUnraisable(NULL); - } - } - /* Restore the original builtins dict, to ensure that any - user data gets cleared. */ - PyObject *dict = PyDict_Copy(interp->builtins); - if (dict == NULL) { - PyErr_WriteUnraisable(NULL); - } - PyDict_Clear(interp->builtins); - if (PyDict_Update(interp->builtins, interp->builtins_copy)) { - _PyErr_Clear(tstate); - } - Py_XDECREF(dict); - /* Collect references */ - _PyGC_CollectNoFail(tstate); - /* Dump GC stats before it's too late, since it uses the warnings - machinery. */ - _PyGC_DumpShutdownStats(tstate); - - /* Now, if there are any modules left alive, clear their globals to - minimize potential leaks. All C extension modules actually end - up here, since they are kept alive in the interpreter state. - - The special treatment of "builtins" here is because even - when it's not referenced as a module, its dictionary is - referenced by almost every module's __builtins__. Since - deleting a module clears its dictionary (even if there are - references left to it), we need to delete the "builtins" - module last. Likewise, we don't delete sys until the very - end because it is implicitly referenced (e.g. by print). */ - if (weaklist != NULL) { - Py_ssize_t i; - /* Since dict is ordered in CPython 3.6+, modules are saved in - importing order. First clear modules imported later. */ - for (i = PyList_GET_SIZE(weaklist) - 1; i >= 0; i--) { - PyObject *tup = PyList_GET_ITEM(weaklist, i); - PyObject *name = PyTuple_GET_ITEM(tup, 0); - PyObject *mod = PyWeakref_GET_OBJECT(PyTuple_GET_ITEM(tup, 1)); - if (mod == Py_None) - continue; - assert(PyModule_Check(mod)); - dict = PyModule_GetDict(mod); - if (dict == interp->builtins || dict == interp->sysdict) - continue; - Py_INCREF(mod); - if (verbose && PyUnicode_Check(name)) { - PySys_FormatStderr("# cleanup[3] wiping %U\n", name); - } - _PyModule_Clear(mod); - Py_DECREF(mod); - } - Py_DECREF(weaklist); - } - - /* Next, delete sys and builtins (in that order) */ - if (verbose) { - PySys_FormatStderr("# cleanup[3] wiping sys\n"); - } - _PyModule_ClearDict(interp->sysdict); - if (verbose) { - PySys_FormatStderr("# cleanup[3] wiping builtins\n"); - } - _PyModule_ClearDict(interp->builtins); - - /* Clear module dict copies stored in the interpreter state */ - _PyInterpreterState_ClearModules(interp); - - /* Clear and delete the modules directory. Actual modules will - still be there only if imported during the execution of some - destructor. */ - interp->modules = NULL; - Py_DECREF(modules); - - /* Once more */ - _PyGC_CollectNoFail(tstate); - -#undef CLEAR_MODULE -#undef STORE_MODULE_WEAKREF -} - - /* Helper for pythonrun.c -- return magic number and tag. */ long diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 71834f63f2a78..adef1617f6132 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -8,7 +8,6 @@ #include "pycore_ceval.h" // _PyEval_FiniGIL() #include "pycore_context.h" // _PyContext_Init() #include "pycore_fileutils.h" // _Py_ResetForceASCII() -#include "pycore_import.h" // _PyImport_Cleanup() #include "pycore_initconfig.h" // _PyStatus_OK() #include "pycore_object.h" // _PyDebug_PrintTotalRefs() #include "pycore_pathconfig.h" // _PyConfig_WritePathConfig() @@ -1192,6 +1191,292 @@ Py_Initialize(void) } +static void +finalize_modules_delete_special(PyThreadState *tstate, int verbose) +{ + // List of names to clear in sys + static const char * const sys_deletes[] = { + "path", "argv", "ps1", "ps2", + "last_type", "last_value", "last_traceback", + "path_hooks", "path_importer_cache", "meta_path", + "__interactivehook__", + NULL + }; + + static const char * const sys_files[] = { + "stdin", "__stdin__", + "stdout", "__stdout__", + "stderr", "__stderr__", + NULL + }; + + PyInterpreterState *interp = tstate->interp; + if (verbose) { + PySys_WriteStderr("# clear builtins._\n"); + } + if (PyDict_SetItemString(interp->builtins, "_", Py_None) < 0) { + PyErr_WriteUnraisable(NULL); + } + + const char * const *p; + for (p = sys_deletes; *p != NULL; p++) { + if (verbose) { + PySys_WriteStderr("# clear sys.%s\n", *p); + } + if (PyDict_SetItemString(interp->sysdict, *p, Py_None) < 0) { + PyErr_WriteUnraisable(NULL); + } + } + for (p = sys_files; *p != NULL; p+=2) { + const char *name = p[0]; + const char *orig_name = p[1]; + if (verbose) { + PySys_WriteStderr("# restore sys.%s\n", name); + } + PyObject *value = _PyDict_GetItemStringWithError(interp->sysdict, + orig_name); + if (value == NULL) { + if (_PyErr_Occurred(tstate)) { + PyErr_WriteUnraisable(NULL); + } + value = Py_None; + } + if (PyDict_SetItemString(interp->sysdict, name, value) < 0) { + PyErr_WriteUnraisable(NULL); + } + } +} + + +static PyObject* +finalize_remove_modules(PyObject *modules, int verbose) +{ + PyObject *weaklist = PyList_New(0); + if (weaklist == NULL) { + PyErr_WriteUnraisable(NULL); + } + +#define STORE_MODULE_WEAKREF(name, mod) \ + if (weaklist != NULL) { \ + PyObject *wr = PyWeakref_NewRef(mod, NULL); \ + if (wr) { \ + PyObject *tup = PyTuple_Pack(2, name, wr); \ + if (!tup || PyList_Append(weaklist, tup) < 0) { \ + PyErr_WriteUnraisable(NULL); \ + } \ + Py_XDECREF(tup); \ + Py_DECREF(wr); \ + } \ + else { \ + PyErr_WriteUnraisable(NULL); \ + } \ + } + +#define CLEAR_MODULE(name, mod) \ + if (PyModule_Check(mod)) { \ + if (verbose && PyUnicode_Check(name)) { \ + PySys_FormatStderr("# cleanup[2] removing %U\n", name); \ + } \ + STORE_MODULE_WEAKREF(name, mod); \ + if (PyObject_SetItem(modules, name, Py_None) < 0) { \ + PyErr_WriteUnraisable(NULL); \ + } \ + } + + if (PyDict_CheckExact(modules)) { + Py_ssize_t pos = 0; + PyObject *key, *value; + while (PyDict_Next(modules, &pos, &key, &value)) { + CLEAR_MODULE(key, value); + } + } + else { + PyObject *iterator = PyObject_GetIter(modules); + if (iterator == NULL) { + PyErr_WriteUnraisable(NULL); + } + else { + PyObject *key; + while ((key = PyIter_Next(iterator))) { + PyObject *value = PyObject_GetItem(modules, key); + if (value == NULL) { + PyErr_WriteUnraisable(NULL); + continue; + } + CLEAR_MODULE(key, value); + Py_DECREF(value); + Py_DECREF(key); + } + if (PyErr_Occurred()) { + PyErr_WriteUnraisable(NULL); + } + Py_DECREF(iterator); + } + } +#undef CLEAR_MODULE +#undef STORE_MODULE_WEAKREF + + return weaklist; +} + + +static void +finalize_clear_modules_dict(PyObject *modules) +{ + if (PyDict_CheckExact(modules)) { + PyDict_Clear(modules); + } + else { + _Py_IDENTIFIER(clear); + if (_PyObject_CallMethodIdNoArgs(modules, &PyId_clear) == NULL) { + PyErr_WriteUnraisable(NULL); + } + } +} + + +static void +finalize_restore_builtins(PyThreadState *tstate) +{ + PyInterpreterState *interp = tstate->interp; + PyObject *dict = PyDict_Copy(interp->builtins); + if (dict == NULL) { + PyErr_WriteUnraisable(NULL); + } + PyDict_Clear(interp->builtins); + if (PyDict_Update(interp->builtins, interp->builtins_copy)) { + _PyErr_Clear(tstate); + } + Py_XDECREF(dict); +} + + +static void +finalize_modules_clear_weaklist(PyInterpreterState *interp, + PyObject *weaklist, int verbose) +{ + // First clear modules imported later + for (Py_ssize_t i = PyList_GET_SIZE(weaklist) - 1; i >= 0; i--) { + PyObject *tup = PyList_GET_ITEM(weaklist, i); + PyObject *name = PyTuple_GET_ITEM(tup, 0); + PyObject *mod = PyWeakref_GET_OBJECT(PyTuple_GET_ITEM(tup, 1)); + if (mod == Py_None) { + continue; + } + assert(PyModule_Check(mod)); + PyObject *dict = PyModule_GetDict(mod); + if (dict == interp->builtins || dict == interp->sysdict) { + continue; + } + Py_INCREF(mod); + if (verbose && PyUnicode_Check(name)) { + PySys_FormatStderr("# cleanup[3] wiping %U\n", name); + } + _PyModule_Clear(mod); + Py_DECREF(mod); + } +} + + +static void +finalize_clear_sys_builtins_dict(PyInterpreterState *interp, int verbose) +{ + // Clear sys dict + if (verbose) { + PySys_FormatStderr("# cleanup[3] wiping sys\n"); + } + _PyModule_ClearDict(interp->sysdict); + + // Clear builtins dict + if (verbose) { + PySys_FormatStderr("# cleanup[3] wiping builtins\n"); + } + _PyModule_ClearDict(interp->builtins); +} + + +/* Clear modules, as good as we can */ +static void +finalize_modules(PyThreadState *tstate) +{ + PyInterpreterState *interp = tstate->interp; + PyObject *modules = interp->modules; + if (modules == NULL) { + // Already done + return; + } + int verbose = _PyInterpreterState_GetConfig(interp)->verbose; + + // Delete some special builtins._ and sys attributes first. These are + // common places where user values hide and people complain when their + // destructors fail. Since the modules containing them are + // deleted *last* of all, they would come too late in the normal + // destruction order. Sigh. + // + // XXX Perhaps these precautions are obsolete. Who knows? + finalize_modules_delete_special(tstate, verbose); + + // Remove all modules from sys.modules, hoping that garbage collection + // can reclaim most of them: set all sys.modules values to None. + // + // We prepare a list which will receive (name, weakref) tuples of + // modules when they are removed from sys.modules. The name is used + // for diagnosis messages (in verbose mode), while the weakref helps + // detect those modules which have been held alive. + PyObject *weaklist = finalize_remove_modules(modules, verbose); + + // Clear the modules dict + finalize_clear_modules_dict(modules); + + // Restore the original builtins dict, to ensure that any + // user data gets cleared. + finalize_restore_builtins(tstate); + + // Collect garbage + _PyGC_CollectNoFail(tstate); + + // Dump GC stats before it's too late, since it uses the warnings + // machinery. + _PyGC_DumpShutdownStats(tstate); + + if (weaklist != NULL) { + // Now, if there are any modules left alive, clear their globals to + // minimize potential leaks. All C extension modules actually end + // up here, since they are kept alive in the interpreter state. + // + // The special treatment of "builtins" here is because even + // when it's not referenced as a module, its dictionary is + // referenced by almost every module's __builtins__. Since + // deleting a module clears its dictionary (even if there are + // references left to it), we need to delete the "builtins" + // module last. Likewise, we don't delete sys until the very + // end because it is implicitly referenced (e.g. by print). + // + // Since dict is ordered in CPython 3.6+, modules are saved in + // importing order. First clear modules imported later. + finalize_modules_clear_weaklist(interp, weaklist, verbose); + Py_DECREF(weaklist); + } + + // Clear sys and builtins modules dict + finalize_clear_sys_builtins_dict(interp, verbose); + + // Clear module dict copies stored in the interpreter state: + // clear PyInterpreterState.modules_by_index and + // clear PyModuleDef.m_base.m_copy (of extensions not using the multi-phase + // initialization API) + _PyInterpreterState_ClearModules(interp); + + // Clear and delete the modules directory. Actual modules will + // still be there only if imported during the execution of some + // destructor. + Py_SETREF(interp->modules, NULL); + + // Collect garbage once more + _PyGC_CollectNoFail(tstate); +} + + /* Flush stdout and stderr */ static int @@ -1210,6 +1495,7 @@ file_is_closed(PyObject *fobj) return r > 0; } + static int flush_std_files(void) { @@ -1417,7 +1703,7 @@ Py_FinalizeEx(void) PyGC_Collect(); /* Destroy all modules */ - _PyImport_Cleanup(tstate); + finalize_modules(tstate); /* Print debug stats if any */ _PyEval_Fini(); @@ -1660,7 +1946,8 @@ Py_EndInterpreter(PyThreadState *tstate) Py_FatalError("not the last thread"); } - _PyImport_Cleanup(tstate); + finalize_modules(tstate); + finalize_interp_clear(tstate); finalize_interp_delete(tstate); } From webhook-mailer at python.org Fri Oct 30 16:09:56 2020 From: webhook-mailer at python.org (vstinner) Date: Fri, 30 Oct 2020 20:09:56 -0000 Subject: [Python-checkins] bpo-42208: Fix test_gdb for gc_collect_main() name (GH-23041) Message-ID: https://github.com/python/cpython/commit/b9ee4af4c643a323779fd7076e80b29d611f2709 commit: b9ee4af4c643a323779fd7076e80b29d611f2709 branch: master author: Victor Stinner committer: vstinner date: 2020-10-30T21:09:48+01:00 summary: bpo-42208: Fix test_gdb for gc_collect_main() name (GH-23041) The gcmodule.c collect() function was renamed to gc_collect_main(): update gdb/libpython.py (python-gdb.py). files: M Tools/gdb/libpython.py diff --git a/Tools/gdb/libpython.py b/Tools/gdb/libpython.py index 33bf5ac821fff..83a5fa93cf447 100755 --- a/Tools/gdb/libpython.py +++ b/Tools/gdb/libpython.py @@ -1605,8 +1605,8 @@ def is_waiting_for_gil(self): return (name == 'take_gil') def is_gc_collect(self): - '''Is this frame "collect" within the garbage-collector?''' - return self._gdbframe.name() == 'collect' + '''Is this frame gc_collect_main() within the garbage-collector?''' + return self._gdbframe.name() in ('collect', 'gc_collect_main') def get_pyop(self): try: From webhook-mailer at python.org Fri Oct 30 17:46:56 2020 From: webhook-mailer at python.org (ericsnowcurrently) Date: Fri, 30 Oct 2020 21:46:56 -0000 Subject: [Python-checkins] bpo-36876: Small adjustments to the C-analyzer tool. (GH-23045) Message-ID: https://github.com/python/cpython/commit/4fe72090deb7fb7bc09bfa56c92f6b3b0967d395 commit: 4fe72090deb7fb7bc09bfa56c92f6b3b0967d395 branch: master author: Eric Snow committer: ericsnowcurrently date: 2020-10-30T15:46:52-06:00 summary: bpo-36876: Small adjustments to the C-analyzer tool. (GH-23045) This is a little bit of clean-up, small fixes, and additional helpers prior to building an updated & accurate list of globals to eliminate. files: A Tools/c-analyzer/c_analyzer/match.py A Tools/c-analyzer/c_parser/match.py M Tools/c-analyzer/c_analyzer/__init__.py M Tools/c-analyzer/c_analyzer/__main__.py M Tools/c-analyzer/c_analyzer/analyze.py M Tools/c-analyzer/c_analyzer/datafiles.py M Tools/c-analyzer/c_analyzer/info.py M Tools/c-analyzer/c_common/scriptutil.py M Tools/c-analyzer/c_parser/datafiles.py M Tools/c-analyzer/c_parser/info.py M Tools/c-analyzer/c_parser/parser/__init__.py M Tools/c-analyzer/c_parser/parser/_info.py M Tools/c-analyzer/c_parser/parser/_regexes.py M Tools/c-analyzer/cpython/__main__.py M Tools/c-analyzer/cpython/_analyzer.py M Tools/c-analyzer/cpython/_parser.py diff --git a/Tools/c-analyzer/c_analyzer/__init__.py b/Tools/c-analyzer/c_analyzer/__init__.py index 4a01cd396f5f5..171fa25102bff 100644 --- a/Tools/c-analyzer/c_analyzer/__init__.py +++ b/Tools/c-analyzer/c_analyzer/__init__.py @@ -4,10 +4,12 @@ from c_parser.info import ( KIND, TypeDeclaration, - filter_by_kind, - collate_by_kind_group, resolve_parsed, ) +from c_parser.match import ( + filter_by_kind, + group_by_kinds, +) from . import ( analyze as _analyze, datafiles as _datafiles, @@ -55,7 +57,7 @@ def analyze_decls(decls, known, *, ) decls = list(decls) - collated = collate_by_kind_group(decls) + collated = group_by_kinds(decls) types = {decl: None for decl in collated['type']} typespecs = _analyze.get_typespecs(types) diff --git a/Tools/c-analyzer/c_analyzer/__main__.py b/Tools/c-analyzer/c_analyzer/__main__.py index 1fd45b985d9bc..4cff1d4efb5fe 100644 --- a/Tools/c-analyzer/c_analyzer/__main__.py +++ b/Tools/c-analyzer/c_analyzer/__main__.py @@ -1,5 +1,6 @@ import io import logging +import os import os.path import re import sys @@ -9,6 +10,7 @@ add_verbosity_cli, add_traceback_cli, add_sepval_cli, + add_progress_cli, add_files_cli, add_commands_cli, process_args_by_key, @@ -17,11 +19,13 @@ filter_filenames, iter_marks, ) -from c_parser.info import KIND, is_type_decl +from c_parser.info import KIND +from c_parser.match import is_type_decl +from .match import filter_forward from . import ( analyze as _analyze, - check_all as _check_all, datafiles as _datafiles, + check_all as _check_all, ) @@ -44,7 +48,7 @@ TABLE_SECTIONS = { 'types': ( ['kind', 'name', 'data', 'file'], - is_type_decl, + KIND.is_type_decl, (lambda v: (v.kind.value, v.filename or '', v.name)), ), 'typedefs': 'types', @@ -167,9 +171,7 @@ def handle_failure(failure, data): print(f'{data.filename}:{name} - {failure}') elif fmt == 'summary': def handle_failure(failure, data): - parent = data.parent or '' - funcname = parent if isinstance(parent, str) else parent.name - print(f'{data.filename:35}\t{funcname or "-":35}\t{data.name:40}\t{failure}') + print(_fmt_one_summary(data, failure)) elif fmt == 'full': div = '' def handle_failure(failure, data): @@ -230,6 +232,15 @@ def section(name): yield f'grand total: {total}' +def _fmt_one_summary(item, extra=None): + parent = item.parent or '' + funcname = parent if isinstance(parent, str) else parent.name + if extra: + return f'{item.filename:35}\t{funcname or "-":35}\t{item.name:40}\t{extra}' + else: + return f'{item.filename:35}\t{funcname or "-":35}\t{item.name}' + + def fmt_full(analysis): # XXX Support sorting. items = sorted(analysis, key=lambda v: v.key) @@ -272,10 +283,12 @@ def process_checks(args): args.checks = [check] else: process_checks = add_checks_cli(parser, checks=checks) + process_progress = add_progress_cli(parser) process_output = add_output_cli(parser, default=None) process_files = add_files_cli(parser, **kwargs) return [ process_checks, + process_progress, process_output, process_files, ] @@ -288,6 +301,7 @@ def cmd_check(filenames, *, relroot=None, failfast=False, iter_filenames=None, + track_progress=None, verbosity=VERBOSITY, _analyze=_analyze, _CHECKS=CHECKS, @@ -304,36 +318,53 @@ def cmd_check(filenames, *, ) = _get_check_handlers(fmt, printer, verbosity) filenames = filter_filenames(filenames, iter_filenames) + if track_progress: + filenames = track_progress(filenames) - logger.info('analyzing...') + logger.info('analyzing files...') analyzed = _analyze(filenames, **kwargs) if relroot: analyzed.fix_filenames(relroot) + decls = filter_forward(analyzed, markpublic=True) - logger.info('checking...') - numfailed = 0 - for data, failure in _check_all(analyzed, checks, failfast=failfast): + logger.info('checking analysis results...') + failed = [] + for data, failure in _check_all(decls, checks, failfast=failfast): if data is None: printer.info('stopping after one failure') break - if div is not None and numfailed > 0: + if div is not None and len(failed) > 0: printer.info(div) - numfailed += 1 + failed.append(data) handle_failure(failure, data) handle_after() printer.info('-------------------------') - logger.info(f'total failures: {numfailed}') + logger.info(f'total failures: {len(failed)}') logger.info('done checking') - if numfailed > 0: - sys.exit(numfailed) + if fmt == 'summary': + print('Categorized by storage:') + print() + from .match import group_by_storage + grouped = group_by_storage(failed, ignore_non_match=False) + for group, decls in grouped.items(): + print() + print(group) + for decl in decls: + print(' ', _fmt_one_summary(decl)) + print(f'subtotal: {len(decls)}') + + if len(failed) > 0: + sys.exit(len(failed)) def _cli_analyze(parser, **kwargs): + process_progress = add_progress_cli(parser) process_output = add_output_cli(parser) process_files = add_files_cli(parser, **kwargs) return [ + process_progress, process_output, process_files, ] @@ -343,6 +374,7 @@ def _cli_analyze(parser, **kwargs): def cmd_analyze(filenames, *, fmt=None, iter_filenames=None, + track_progress=None, verbosity=None, _analyze=_analyze, formats=FORMATS, @@ -356,49 +388,46 @@ def cmd_analyze(filenames, *, raise ValueError(f'unsupported fmt {fmt!r}') filenames = filter_filenames(filenames, iter_filenames) - if verbosity == 2: - def iter_filenames(filenames=filenames): - marks = iter_marks() - for filename in filenames: - print(next(marks), end='') - yield filename - filenames = iter_filenames() - elif verbosity > 2: - def iter_filenames(filenames=filenames): - for filename in filenames: - print(f'<{filename}>') - yield filename - filenames = iter_filenames() - - logger.info('analyzing...') + if track_progress: + filenames = track_progress(filenames) + + logger.info('analyzing files...') analyzed = _analyze(filenames, **kwargs) + decls = filter_forward(analyzed, markpublic=True) - for line in do_fmt(analyzed): + for line in do_fmt(decls): print(line) def _cli_data(parser, filenames=None, known=None): ArgumentParser = type(parser) common = ArgumentParser(add_help=False) - if filenames is None: - common.add_argument('filenames', metavar='FILE', nargs='+') + # These flags will get processed by the top-level parse_args(). + add_verbosity_cli(common) + add_traceback_cli(common) subs = parser.add_subparsers(dest='datacmd') sub = subs.add_parser('show', parents=[common]) if known is None: sub.add_argument('--known', required=True) + if filenames is None: + sub.add_argument('filenames', metavar='FILE', nargs='+') - sub = subs.add_parser('dump') + sub = subs.add_parser('dump', parents=[common]) if known is None: sub.add_argument('--known') sub.add_argument('--show', action='store_true') + process_progress = add_progress_cli(sub) - sub = subs.add_parser('check') + sub = subs.add_parser('check', parents=[common]) if known is None: sub.add_argument('--known', required=True) - return None + def process_args(args): + if args.datacmd == 'dump': + process_progress(args) + return process_args def cmd_data(datacmd, filenames, known=None, *, @@ -406,6 +435,7 @@ def cmd_data(datacmd, filenames, known=None, *, formats=FORMATS, extracolumns=None, relroot=None, + track_progress=None, **kwargs ): kwargs.pop('verbosity', None) @@ -417,6 +447,8 @@ def cmd_data(datacmd, filenames, known=None, *, for line in do_fmt(known): print(line) elif datacmd == 'dump': + if track_progress: + filenames = track_progress(filenames) analyzed = _analyze(filenames, **kwargs) if known is None or usestdout: outfile = io.StringIO() diff --git a/Tools/c-analyzer/c_analyzer/analyze.py b/Tools/c-analyzer/c_analyzer/analyze.py index d8ae915e42002..267d058e07abd 100644 --- a/Tools/c-analyzer/c_analyzer/analyze.py +++ b/Tools/c-analyzer/c_analyzer/analyze.py @@ -3,15 +3,19 @@ TypeDeclaration, POTSType, FuncPtr, +) +from c_parser.match import ( is_pots, is_funcptr, ) from .info import ( IGNORED, UNKNOWN, - is_system_type, SystemType, ) +from .match import ( + is_system_type, +) def get_typespecs(typedecls): diff --git a/Tools/c-analyzer/c_analyzer/datafiles.py b/Tools/c-analyzer/c_analyzer/datafiles.py index 0de438cce470f..d37a4eefe351a 100644 --- a/Tools/c-analyzer/c_analyzer/datafiles.py +++ b/Tools/c-analyzer/c_analyzer/datafiles.py @@ -1,5 +1,6 @@ import c_common.tables as _tables import c_parser.info as _info +import c_parser.match as _match import c_parser.datafiles as _parser from . import analyze as _analyze @@ -17,7 +18,7 @@ def analyze_known(known, *, handle_unresolved=True, ): knowntypes = knowntypespecs = {} - collated = _info.collate_by_kind_group(known) + collated = _match.group_by_kinds(known) types = {decl: None for decl in collated['type']} typespecs = _analyze.get_typespecs(types) def analyze_decl(decl): diff --git a/Tools/c-analyzer/c_analyzer/info.py b/Tools/c-analyzer/c_analyzer/info.py index 23d77611a4c3c..be9281502d250 100644 --- a/Tools/c-analyzer/c_analyzer/info.py +++ b/Tools/c-analyzer/c_analyzer/info.py @@ -7,7 +7,11 @@ HighlevelParsedItem, Declaration, TypeDeclaration, +) +from c_parser.match import ( is_type_decl, +) +from .match import ( is_process_global, ) @@ -16,44 +20,6 @@ UNKNOWN = _misc.Labeled('UNKNOWN') -# XXX Use known.tsv for these? -SYSTEM_TYPES = { - 'int8_t', - 'uint8_t', - 'int16_t', - 'uint16_t', - 'int32_t', - 'uint32_t', - 'int64_t', - 'uint64_t', - 'size_t', - 'ssize_t', - 'intptr_t', - 'uintptr_t', - 'wchar_t', - '', - # OS-specific - 'pthread_cond_t', - 'pthread_mutex_t', - 'pthread_key_t', - 'atomic_int', - 'atomic_uintptr_t', - '', - # lib-specific - 'WINDOW', # curses - 'XML_LChar', - 'XML_Size', - 'XML_Parser', - 'enum XML_Error', - 'enum XML_Status', - '', -} - - -def is_system_type(typespec): - return typespec in SYSTEM_TYPES - - class SystemType(TypeDeclaration): def __init__(self, name): diff --git a/Tools/c-analyzer/c_analyzer/match.py b/Tools/c-analyzer/c_analyzer/match.py new file mode 100644 index 0000000000000..5c27e4a224afc --- /dev/null +++ b/Tools/c-analyzer/c_analyzer/match.py @@ -0,0 +1,212 @@ +import os.path + +from c_parser import ( + info as _info, + match as _match, +) + + +_KIND = _info.KIND + + +# XXX Use known.tsv for these? +SYSTEM_TYPES = { + 'int8_t', + 'uint8_t', + 'int16_t', + 'uint16_t', + 'int32_t', + 'uint32_t', + 'int64_t', + 'uint64_t', + 'size_t', + 'ssize_t', + 'intptr_t', + 'uintptr_t', + 'wchar_t', + '', + # OS-specific + 'pthread_cond_t', + 'pthread_mutex_t', + 'pthread_key_t', + 'atomic_int', + 'atomic_uintptr_t', + '', + # lib-specific + 'WINDOW', # curses + 'XML_LChar', + 'XML_Size', + 'XML_Parser', + 'enum XML_Error', + 'enum XML_Status', + '', +} + + +def is_system_type(typespec): + return typespec in SYSTEM_TYPES + + +################################## +# decl matchers + +def is_public(decl): + if not decl.filename.endswith('.h'): + return False + if 'Include' not in decl.filename.split(os.path.sep): + return False + return True + + +def is_process_global(vardecl): + kind, storage, _, _, _ = _info.get_parsed_vartype(vardecl) + if kind is not _KIND.VARIABLE: + raise NotImplementedError(vardecl) + if 'static' in (storage or ''): + return True + + if hasattr(vardecl, 'parent'): + parent = vardecl.parent + else: + parent = vardecl.get('parent') + return not parent + + +def is_fixed_type(vardecl): + if not vardecl: + return None + _, _, _, typespec, abstract = _info.get_parsed_vartype(vardecl) + if 'typeof' in typespec: + raise NotImplementedError(vardecl) + elif not abstract: + return True + + if '*' not in abstract: + # XXX What about []? + return True + elif _match._is_funcptr(abstract): + return True + else: + for after in abstract.split('*')[1:]: + if not after.lstrip().startswith('const'): + return False + else: + return True + + +def is_immutable(vardecl): + if not vardecl: + return None + if not is_fixed_type(vardecl): + return False + _, _, typequal, _, _ = _info.get_parsed_vartype(vardecl) + # If there, it can only be "const" or "volatile". + return typequal == 'const' + + +def is_public_api(decl): + if not is_public(decl): + return False + if decl.kind is _KIND.TYPEDEF: + return True + elif _match.is_type_decl(decl): + return not _match.is_forward_decl(decl) + else: + return _match.is_external_reference(decl) + + +def is_public_declaration(decl): + if not is_public(decl): + return False + if decl.kind is _KIND.TYPEDEF: + return True + elif _match.is_type_decl(decl): + return _match.is_forward_decl(decl) + else: + return _match.is_external_reference(decl) + + +def is_public_definition(decl): + if not is_public(decl): + return False + if decl.kind is _KIND.TYPEDEF: + return True + elif _match.is_type_decl(decl): + return not _match.is_forward_decl(decl) + else: + return not _match.is_external_reference(decl) + + +def is_public_impl(decl): + if not _KIND.is_decl(decl.kind): + return False + # See filter_forward() about "is_public". + return getattr(decl, 'is_public', False) + + +def is_module_global_decl(decl): + if is_public_impl(decl): + return False + if _match.is_forward_decl(decl): + return False + return not _match.is_local_var(decl) + + +################################## +# filtering with matchers + +def filter_forward(items, *, markpublic=False): + if markpublic: + public = set() + actual = [] + for item in items: + if is_public_api(item): + public.add(item.id) + elif not _match.is_forward_decl(item): + actual.append(item) + else: + # non-public duplicate! + # XXX + raise Exception(item) + for item in actual: + _info.set_flag(item, 'is_public', item.id in public) + yield item + else: + for item in items: + if _match.is_forward_decl(item): + continue + yield item + + +################################## +# grouping with matchers + +def group_by_storage(decls, **kwargs): + def is_module_global(decl): + if not is_module_global_decl(decl): + return False + if decl.kind == _KIND.VARIABLE: + if _info.get_effective_storage(decl) == 'static': + # This is covered by is_static_module_global(). + return False + return True + def is_static_module_global(decl): + if not _match.is_global_var(decl): + return False + return _info.get_effective_storage(decl) == 'static' + def is_static_local(decl): + if not _match.is_local_var(decl): + return False + return _info.get_effective_storage(decl) == 'static' + #def is_local(decl): + # if not _match.is_local_var(decl): + # return False + # return _info.get_effective_storage(decl) != 'static' + categories = { + #'extern': is_extern, + 'published': is_public_impl, + 'module-global': is_module_global, + 'static-module-global': is_static_module_global, + 'static-local': is_static_local, + } + return _match.group_by_category(decls, categories, **kwargs) diff --git a/Tools/c-analyzer/c_common/scriptutil.py b/Tools/c-analyzer/c_common/scriptutil.py index 939a85003b296..222059015d76e 100644 --- a/Tools/c-analyzer/c_common/scriptutil.py +++ b/Tools/c-analyzer/c_common/scriptutil.py @@ -10,6 +10,9 @@ from . import fsutil, strutil, iterutil, logging as loggingutil +_NOT_SET = object() + + def get_prog(spec=None, *, absolute=False, allowsuffix=True): if spec is None: _, spec = _find_script() @@ -313,6 +316,22 @@ def _parse_files(filenames): yield filename.strip() +def add_progress_cli(parser, *, threshold=VERBOSITY, **kwargs): + parser.add_argument('--progress', dest='track_progress', action='store_const', const=True) + parser.add_argument('--no-progress', dest='track_progress', action='store_false') + parser.set_defaults(track_progress=True) + + def process_args(args): + if args.track_progress: + ns = vars(args) + verbosity = ns.get('verbosity', VERBOSITY) + if verbosity <= threshold: + args.track_progress = track_progress_compact + else: + args.track_progress = track_progress_flat + return process_args + + def add_failure_filtering_cli(parser, pool, *, default=False): parser.add_argument('--fail', action='append', metavar=f'"{{all|{"|".join(sorted(pool))}}},..."') @@ -551,13 +570,39 @@ def _iter_filenames(filenames, iter_files): raise NotImplementedError -def iter_marks(mark='.', *, group=5, groups=2, lines=10, sep=' '): +def track_progress_compact(items, *, groups=5, **mark_kwargs): + last = os.linesep + marks = iter_marks(groups=groups, **mark_kwargs) + for item in items: + last = next(marks) + print(last, end='', flush=True) + yield item + if not last.endswith(os.linesep): + print() + + +def track_progress_flat(items, fmt='<{}>'): + for item in items: + print(fmt.format(item), flush=True) + yield item + + +def iter_marks(mark='.', *, group=5, groups=2, lines=_NOT_SET, sep=' '): mark = mark or '' + group = group if group and group > 1 else 1 + groups = groups if groups and groups > 1 else 1 + sep = f'{mark}{sep}' if sep else mark end = f'{mark}{os.linesep}' div = os.linesep perline = group * groups - perlines = perline * lines + if lines is _NOT_SET: + # By default we try to put about 100 in each line group. + perlines = 100 // perline * perline + elif not lines or lines < 0: + perlines = None + else: + perlines = perline * lines if perline == 1: yield end @@ -568,7 +613,7 @@ def iter_marks(mark='.', *, group=5, groups=2, lines=10, sep=' '): while True: if count % perline == 0: yield end - if count % perlines == 0: + if perlines and count % perlines == 0: yield div elif count % group == 0: yield sep diff --git a/Tools/c-analyzer/c_parser/datafiles.py b/Tools/c-analyzer/c_parser/datafiles.py index 5bdb946b1772a..cdd69b1f9b2d8 100644 --- a/Tools/c-analyzer/c_parser/datafiles.py +++ b/Tools/c-analyzer/c_parser/datafiles.py @@ -92,7 +92,7 @@ def write_decls_tsv(decls, outfile, extracolumns=None, *, **kwargs ): # XXX Move the row rendering here. - _write_decls_tsv(rows, outfile, extracolumns, relroot, kwargs) + _write_decls_tsv(decls, outfile, extracolumns, relroot, kwargs) def _iter_decls_tsv(infile, extracolumns=None, relroot=None): diff --git a/Tools/c-analyzer/c_parser/info.py b/Tools/c-analyzer/c_parser/info.py index a07ce2e0ccb8d..798a45d2e08e7 100644 --- a/Tools/c-analyzer/c_parser/info.py +++ b/Tools/c-analyzer/c_parser/info.py @@ -7,85 +7,12 @@ import c_common.misc as _misc import c_common.strutil as _strutil import c_common.tables as _tables -from .parser._regexes import SIMPLE_TYPE +from .parser._regexes import SIMPLE_TYPE, _STORAGE FIXED_TYPE = _misc.Labeled('FIXED_TYPE') -POTS_REGEX = re.compile(rf'^{SIMPLE_TYPE}$', re.VERBOSE) - - -def is_pots(typespec): - if not typespec: - return None - if type(typespec) is not str: - _, _, _, typespec, _ = get_parsed_vartype(typespec) - return POTS_REGEX.match(typespec) is not None - - -def is_funcptr(vartype): - if not vartype: - return None - _, _, _, _, abstract = get_parsed_vartype(vartype) - return _is_funcptr(abstract) - - -def _is_funcptr(declstr): - if not declstr: - return None - # XXX Support "(*)(". - return '(*)(' in declstr.replace(' ', '') - - -def is_exported_symbol(decl): - _, storage, _, _, _ = get_parsed_vartype(decl) - raise NotImplementedError - - -def is_process_global(vardecl): - kind, storage, _, _, _ = get_parsed_vartype(vardecl) - if kind is not KIND.VARIABLE: - raise NotImplementedError(vardecl) - if 'static' in (storage or ''): - return True - - if hasattr(vardecl, 'parent'): - parent = vardecl.parent - else: - parent = vardecl.get('parent') - return not parent - - -def is_fixed_type(vardecl): - if not vardecl: - return None - _, _, _, typespec, abstract = get_parsed_vartype(vardecl) - if 'typeof' in typespec: - raise NotImplementedError(vardecl) - elif not abstract: - return True - - if '*' not in abstract: - # XXX What about []? - return True - elif _is_funcptr(abstract): - return True - else: - for after in abstract.split('*')[1:]: - if not after.lstrip().startswith('const'): - return False - else: - return True - - -def is_immutable(vardecl): - if not vardecl: - return None - if not is_fixed_type(vardecl): - return False - _, _, typequal, _, _ = get_parsed_vartype(vardecl) - # If there, it can only be "const" or "volatile". - return typequal == 'const' +STORAGE = frozenset(_STORAGE) ############################# @@ -214,58 +141,8 @@ def resolve_group(cls, group): KIND._GROUPS.update((k.value, {k}) for k in KIND) -# The module-level kind-related helpers (below) deal with .kind: - -def is_type_decl(kind): - # Handle ParsedItem, Declaration, etc.. - kind = getattr(kind, 'kind', kind) - return KIND.is_type_decl(kind) - - -def is_decl(kind): - # Handle ParsedItem, Declaration, etc.. - kind = getattr(kind, 'kind', kind) - return KIND.is_decl(kind) - - -def filter_by_kind(items, kind): - if kind == 'type': - kinds = KIND._TYPE_DECLS - elif kind == 'decl': - kinds = KIND._TYPE_DECLS - try: - okay = kind in KIND - except TypeError: - kinds = set(kind) - else: - kinds = {kind} if okay else set(kind) - for item in items: - if item.kind in kinds: - yield item - - -def collate_by_kind(items): - collated = {kind: [] for kind in KIND} - for item in items: - try: - collated[item.kind].append(item) - except KeyError: - raise ValueError(f'unsupported kind in {item!r}') - return collated - - -def get_kind_group(kind): - # Handle ParsedItem, Declaration, etc.. - kind = getattr(kind, 'kind', kind) - return KIND.get_group(kind) - - -def collate_by_kind_group(items): - collated = {KIND.get_group(k): [] for k in KIND} - for item in items: - group = KIND.get_group(item.kind) - collated[group].append(item) - return collated +def get_kind_group(item): + return KIND.get_group(item.kind) ############################# @@ -484,6 +361,27 @@ def get_parsed_vartype(decl): return kind, storage, typequal, typespec, abstract +def get_default_storage(decl): + if decl.kind not in (KIND.VARIABLE, KIND.FUNCTION): + return None + return 'extern' if decl.parent is None else 'auto' + + +def get_effective_storage(decl, *, default=None): + # Note that "static" limits access to just that C module + # and "extern" (the default for module-level) allows access + # outside the C module. + if default is None: + default = get_default_storage(decl) + if default is None: + return None + try: + storage = decl.storage + except AttributeError: + storage, _ = _get_vartype(decl.data) + return storage or default + + ############################# # high-level @@ -997,7 +895,7 @@ def _unformat_data(cls, datastr, fmt=None): def __init__(self, file, name, data, parent=None, storage=None): super().__init__(file, name, data, parent, - _extra={'storage': storage}, + _extra={'storage': storage or None}, _shortkey=f'({parent.name}).{name}' if parent else name, _key=(str(file), # Tilde comes after all other ascii characters. @@ -1005,6 +903,11 @@ def __init__(self, file, name, data, parent=None, storage=None): name, ), ) + if storage: + if storage not in STORAGE: + # The parser must need an update. + raise NotImplementedError(storage) + # Otherwise we trust the compiler to have validated it. @property def vartype(self): @@ -1413,6 +1316,13 @@ def resolve_parsed(parsed): return cls.from_parsed(parsed) +def set_flag(item, name, value): + try: + setattr(item, name, value) + except AttributeError: + object.__setattr__(item, name, value) + + ############################# # composite diff --git a/Tools/c-analyzer/c_parser/match.py b/Tools/c-analyzer/c_parser/match.py new file mode 100644 index 0000000000000..3b5068fd11b68 --- /dev/null +++ b/Tools/c-analyzer/c_parser/match.py @@ -0,0 +1,177 @@ +import re + +from . import info as _info +from .parser._regexes import SIMPLE_TYPE + + +_KIND = _info.KIND + + +def match_storage(decl, expected): + default = _info.get_default_storage(decl) + #assert default + if expected is None: + expected = {default} + elif isinstance(expected, str): + expected = {expected or default} + elif not expected: + expected = _info.STORAGE + else: + expected = {v or default for v in expected} + storage = _info.get_effective_storage(decl, default=default) + return storage in expected + + +################################## +# decl matchers + +def is_type_decl(item): + return _KIND.is_type_decl(item.kind) + + +def is_decl(item): + return _KIND.is_decl(item.kind) + + +def is_pots(typespec, *, + _regex=re.compile(rf'^{SIMPLE_TYPE}$', re.VERBOSE), + ): + + if not typespec: + return None + if type(typespec) is not str: + _, _, _, typespec, _ = _info.get_parsed_vartype(typespec) + return _regex.match(typespec) is not None + + +def is_funcptr(vartype): + if not vartype: + return None + _, _, _, _, abstract = _info.get_parsed_vartype(vartype) + return _is_funcptr(abstract) + + +def _is_funcptr(declstr): + if not declstr: + return None + # XXX Support "(*)(". + return '(*)(' in declstr.replace(' ', '') + + +def is_forward_decl(decl): + if decl.kind is _KIND.TYPEDEF: + return False + elif is_type_decl(decl): + return not decl.data + elif decl.kind is _KIND.FUNCTION: + # XXX This doesn't work with ParsedItem. + return decl.signature.isforward + elif decl.kind is _KIND.VARIABLE: + # No var decls are considered forward (or all are...). + return False + else: + raise NotImplementedError(decl) + + +def can_have_symbol(decl): + return decl.kind in (_KIND.VARIABLE, _KIND.FUNCTION) + + +def has_external_symbol(decl): + if not can_have_symbol(decl): + return False + if _info.get_effective_storage(decl) != 'extern': + return False + if decl.kind is _KIND.FUNCTION: + return not decl.signature.isforward + else: + # It must be a variable, which can only be implicitly extern here. + return decl.storage != 'extern' + + +def has_internal_symbol(decl): + if not can_have_symbol(decl): + return False + return _info.get_actual_storage(decl) == 'static' + + +def is_external_reference(decl): + if not can_have_symbol(decl): + return False + # We have to check the declared storage rather tnan the effective. + if decl.storage != 'extern': + return False + if decl.kind is _KIND.FUNCTION: + return decl.signature.isforward + # Otherwise it's a variable. + return True + + +def is_local_var(decl): + if not decl.kind is _KIND.VARIABLE: + return False + return True if decl.parent else False + + +def is_global_var(decl): + if not decl.kind is _KIND.VARIABLE: + return False + return False if decl.parent else True + + +################################## +# filtering with matchers + +def filter_by_kind(items, kind): + if kind == 'type': + kinds = _KIND._TYPE_DECLS + elif kind == 'decl': + kinds = _KIND._TYPE_DECLS + try: + okay = kind in _KIND + except TypeError: + kinds = set(kind) + else: + kinds = {kind} if okay else set(kind) + for item in items: + if item.kind in kinds: + yield item + + +################################## +# grouping with matchers + +def group_by_category(decls, categories, *, ignore_non_match=True): + collated = {} + for decl in decls: + # Matchers should be mutually exclusive. (First match wins.) + for category, match in categories.items(): + if match(decl): + if category not in collated: + collated[category] = [decl] + else: + collated[category].append(decl) + break + else: + if not ignore_non_match: + raise Exception(f'no match for {decl!r}') + return collated + + +def group_by_kind(items): + collated = {kind: [] for kind in _KIND} + for item in items: + try: + collated[item.kind].append(item) + except KeyError: + raise ValueError(f'unsupported kind in {item!r}') + return collated + + +def group_by_kinds(items): + # Collate into kind groups (decl, type, etc.). + collated = {_KIND.get_group(k): [] for k in _KIND} + for item in items: + group = _KIND.get_group(item.kind) + collated[group].append(item) + return collated diff --git a/Tools/c-analyzer/c_parser/parser/__init__.py b/Tools/c-analyzer/c_parser/parser/__init__.py index 7cb34caf09eba..4b201c6354023 100644 --- a/Tools/c-analyzer/c_parser/parser/__init__.py +++ b/Tools/c-analyzer/c_parser/parser/__init__.py @@ -163,6 +163,8 @@ def _parse(srclines, anon_name): def _iter_source(lines, *, maxtext=20_000, maxlines=700, showtext=False): + maxtext = maxtext if maxtext and maxtext > 0 else None + maxlines = maxlines if maxlines and maxlines > 0 else None filestack = [] allinfo = {} # "lines" should be (fileinfo, data), as produced by the preprocessor code. @@ -181,9 +183,7 @@ def _iter_source(lines, *, maxtext=20_000, maxlines=700, showtext=False): _logger.debug(f'-> {line}') srcinfo._add_line(line, fileinfo.lno) - if len(srcinfo.text) > maxtext: - break - if srcinfo.end - srcinfo.start > maxlines: + if srcinfo.too_much(maxtext, maxlines): break while srcinfo._used(): yield srcinfo diff --git a/Tools/c-analyzer/c_parser/parser/_info.py b/Tools/c-analyzer/c_parser/parser/_info.py index 2dcd5e5e760b7..cc21931b66cc5 100644 --- a/Tools/c-analyzer/c_parser/parser/_info.py +++ b/Tools/c-analyzer/c_parser/parser/_info.py @@ -1,3 +1,5 @@ +import re + from ..info import KIND, ParsedItem, FileInfo @@ -121,6 +123,19 @@ def resolve(self, kind, data, name, parent=None): def done(self): self._set_ready() + def too_much(self, maxtext, maxlines): + if maxtext and len(self.text) > maxtext: + pass + elif maxlines and self.end - self.start > maxlines: + pass + else: + return False + + #if re.fullmatch(r'[^;]+\[\][ ]*=[ ]*[{]([ ]*\d+,)*([ ]*\d+,?)\s*', + # self._current.text): + # return False + return True + def _set_ready(self): if self._current is None: self._ready = False diff --git a/Tools/c-analyzer/c_parser/parser/_regexes.py b/Tools/c-analyzer/c_parser/parser/_regexes.py index e9bc31d335a7d..cb85a59aaa16c 100644 --- a/Tools/c-analyzer/c_parser/parser/_regexes.py +++ b/Tools/c-analyzer/c_parser/parser/_regexes.py @@ -137,7 +137,8 @@ def _ind(text, level=1, edges='both'): ####################################### # variable declarations -STORAGE_CLASS = r'(?: \b (?: auto | register | static | extern ) \b )' +_STORAGE = 'auto register static extern'.split() +STORAGE_CLASS = rf'(?: \b (?: {" | ".join(_STORAGE)} ) \b )' TYPE_QUALIFIER = r'(?: \b (?: const | volatile ) \b )' PTR_QUALIFIER = rf'(?: [*] (?: \s* {TYPE_QUALIFIER} )? )' diff --git a/Tools/c-analyzer/cpython/__main__.py b/Tools/c-analyzer/cpython/__main__.py index 23a3de06f639c..23ce29776ca68 100644 --- a/Tools/c-analyzer/cpython/__main__.py +++ b/Tools/c-analyzer/cpython/__main__.py @@ -31,6 +31,9 @@ def _resolve_filenames(filenames): return resolved +####################################### +# the formats + def fmt_summary(analysis): # XXX Support sorting and grouping. supported = [] @@ -179,7 +182,7 @@ def analyze(files, **kwargs): analyze_resolved=_analyzer.analyze_resolved, ) return _analyzer.Analysis.from_results(results) - else: + else: # check known = _analyzer.read_known() def analyze(files, **kwargs): return _analyzer.iter_decls(files, **kwargs) diff --git a/Tools/c-analyzer/cpython/_analyzer.py b/Tools/c-analyzer/cpython/_analyzer.py index 98f8888651e57..978831d1fd949 100644 --- a/Tools/c-analyzer/cpython/_analyzer.py +++ b/Tools/c-analyzer/cpython/_analyzer.py @@ -11,9 +11,14 @@ Struct, Member, FIXED_TYPE, +) +from c_parser.match import ( is_type_decl, is_pots, is_funcptr, +) +from c_analyzer.match import ( + is_system_type, is_process_global, is_fixed_type, is_immutable, @@ -246,7 +251,7 @@ def _check_typespec(decl, typedecl, types, knowntypes): # Fall back to default known types. if is_pots(typespec): return None - elif _info.is_system_type(typespec): + elif is_system_type(typespec): return None elif is_funcptr(decl.vartype): return None diff --git a/Tools/c-analyzer/cpython/_parser.py b/Tools/c-analyzer/cpython/_parser.py index 35fa296251e2e..7c8c296665398 100644 --- a/Tools/c-analyzer/cpython/_parser.py +++ b/Tools/c-analyzer/cpython/_parser.py @@ -46,10 +46,14 @@ def clean_lines(text): GLOBS = [ 'Include/*.h', 'Include/internal/*.h', + 'Modules/**/*.h', 'Modules/**/*.c', + 'Objects/**/*.h', 'Objects/**/*.c', + 'Python/**/*.h', + 'Parser/**/*.c', + 'Python/**/*.h', 'Parser/**/*.c', - 'Python/**/*.c', ] EXCLUDED = clean_lines(''' @@ -67,11 +71,24 @@ def clean_lines(text): Modules/_winapi.c # windows.h Modules/overlapped.c # winsock.h Python/dynload_win.c # windows.h +Modules/expat/winconfig.h +Python/thread_nt.h # other OS-dependent Python/dynload_dl.c # dl.h Python/dynload_hpux.c # dl.h Python/dynload_aix.c # sys/ldr.h +Python/thread_pthread.h + +# only huge constants (safe but parsing is slow) +Modules/_ssl_data.h +Modules/unicodedata_db.h +Modules/unicodename_db.h +Modules/cjkcodecs/mappings_*.h +Objects/unicodetype_db.h +Python/importlib.h +Python/importlib_external.h +Python/importlib_zipimport.h # @end=conf@ ''') @@ -80,6 +97,17 @@ def clean_lines(text): EXCLUDED += clean_lines(''' # The tool should be able to parse these... +Modules/hashlib.h +Objects/stringlib/codecs.h +Objects/stringlib/count.h +Objects/stringlib/ctype.h +Objects/stringlib/fastsearch.h +Objects/stringlib/find.h +Objects/stringlib/find_max_char.h +Objects/stringlib/partition.h +Objects/stringlib/replace.h +Objects/stringlib/split.h + Modules/_dbmmodule.c Modules/cjkcodecs/_codecs_*.c Modules/expat/xmlrole.c @@ -134,6 +162,9 @@ def clean_lines(text): Modules/_ctypes/cfield.c Py_BUILD_CORE 1 Modules/_heapqmodule.c Py_BUILD_CORE 1 Modules/_posixsubprocess.c Py_BUILD_CORE 1 +Objects/stringlib/codecs.h Py_BUILD_CORE 1 +Python/ceval_gil.h Py_BUILD_CORE 1 +Python/condvar.h Py_BUILD_CORE 1 Modules/_json.c Py_BUILD_CORE_BUILTIN 1 Modules/_pickle.c Py_BUILD_CORE_BUILTIN 1 @@ -177,6 +208,12 @@ def clean_lines(text): Python/import.c PyMODINIT_FUNC PyObject* Modules/_testcapimodule.c PyAPI_FUNC(RTYPE) RTYPE Python/getargs.c PyAPI_FUNC(RTYPE) RTYPE +Objects/stringlib/unicode_format.h Py_LOCAL_INLINE(type) static inline type + +# implied include of pymacro.h +*/clinic/*.c.h PyDoc_VAR(name) static const char name[] +*/clinic/*.c.h PyDoc_STR(str) str +*/clinic/*.c.h PyDoc_STRVAR(name,str) PyDoc_VAR(name) = PyDoc_STR(str) # implied include of exports.h #Modules/_io/bytesio.c Py_EXPORTED_SYMBOL /* */ @@ -212,6 +249,11 @@ def clean_lines(text): Modules/expat/xmlparse.c XML_POOR_ENTROPY 1 Modules/_dbmmodule.c HAVE_GDBM_DASH_NDBM_H 1 +# others +Modules/sre_lib.h LOCAL(type) static inline type +Modules/sre_lib.h SRE(F) sre_ucs2_##F +Objects/stringlib/codecs.h STRINGLIB_IS_UNICODE 1 + # @end=tsv@ ''')[1:] From webhook-mailer at python.org Fri Oct 30 17:51:06 2020 From: webhook-mailer at python.org (vstinner) Date: Fri, 30 Oct 2020 21:51:06 -0000 Subject: [Python-checkins] bpo-42208: Call GC collect earlier in PyInterpreterState_Clear() (GH-23044) Message-ID: https://github.com/python/cpython/commit/eba5bf2f5672bf4861c626937597b85ac0c242b9 commit: eba5bf2f5672bf4861c626937597b85ac0c242b9 branch: master author: Victor Stinner committer: vstinner date: 2020-10-30T22:51:02+01:00 summary: bpo-42208: Call GC collect earlier in PyInterpreterState_Clear() (GH-23044) The last GC collection is now done before clearing builtins and sys dictionaries. Add also assertions to ensure that gc.collect() is no longer called after _PyGC_Fini(). Pass also the tstate to PyInterpreterState_Clear() to pass the correct tstate to _PyGC_CollectNoFail() and _PyGC_Fini(). files: M Include/internal/pycore_interp.h M Modules/gcmodule.c M Python/pylifecycle.c M Python/pystate.c diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h index eee369a44bfc7..69d2108da4322 100644 --- a/Include/internal/pycore_interp.h +++ b/Include/internal/pycore_interp.h @@ -267,6 +267,7 @@ extern PyStatus _PyInterpreterState_SetConfig( PyInterpreterState *interp, const PyConfig *config); +extern void _PyInterpreterState_Clear(PyThreadState *tstate); /* cross-interpreter data registry */ diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c index d90ff33684fe8..e6ad0f2dd4222 100644 --- a/Modules/gcmodule.c +++ b/Modules/gcmodule.c @@ -1191,6 +1191,11 @@ gc_collect_main(PyThreadState *tstate, int generation, _PyTime_t t1 = 0; /* initialize to prevent a compiler warning */ GCState *gcstate = &tstate->interp->gc; + // gc_collect_main() must not be called before _PyGC_Init + // or after _PyGC_Fini() + assert(gcstate->garbage != NULL); + assert(!_PyErr_Occurred(tstate)); + #ifdef EXPERIMENTAL_ISOLATED_SUBINTERPRETERS if (tstate->interp->config._isolated_interpreter) { // bpo-40533: The garbage collector must not be run on parallel on @@ -2073,16 +2078,13 @@ PyGC_Collect(void) Py_ssize_t _PyGC_CollectNoFail(PyThreadState *tstate) { - assert(!_PyErr_Occurred(tstate)); - - GCState *gcstate = &tstate->interp->gc; - /* Ideally, this function is only called on interpreter shutdown, and therefore not recursively. Unfortunately, when there are daemon threads, a daemon thread can start a cyclic garbage collection during interpreter shutdown (and then never finish it). See http://bugs.python.org/issue8713#msg195178 for an example. */ + GCState *gcstate = &tstate->interp->gc; if (gcstate->collecting) { return 0; } diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index adef1617f6132..ff58c1b9153bd 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -1576,10 +1576,7 @@ finalize_interp_clear(PyThreadState *tstate) int is_main_interp = _Py_IsMainInterpreter(tstate); /* Clear interpreter state and all thread states */ - PyInterpreterState_Clear(tstate->interp); - - /* Last explicit GC collection */ - _PyGC_CollectNoFail(tstate); + _PyInterpreterState_Clear(tstate); /* Clear all loghooks */ /* Both _PySys_Audit function and users still need PyObject, such as tuple. @@ -1588,8 +1585,6 @@ finalize_interp_clear(PyThreadState *tstate) _PySys_ClearAuditHooks(tstate); } - _PyGC_Fini(tstate); - if (is_main_interp) { _Py_HashRandomization_Fini(); _PyArg_Fini(); diff --git a/Python/pystate.c b/Python/pystate.c index eb24f2b800607..e88898670cdff 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -268,14 +268,11 @@ PyInterpreterState_New(void) } -void -PyInterpreterState_Clear(PyInterpreterState *interp) +static void +interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate) { _PyRuntimeState *runtime = interp->runtime; - /* Use the current Python thread state to call audit hooks, - not the current Python thread state of 'interp'. */ - PyThreadState *tstate = _PyThreadState_GET(); if (_PySys_Audit(tstate, "cpython.PyInterpreterState_Clear", NULL) < 0) { _PyErr_Clear(tstate); } @@ -306,6 +303,12 @@ PyInterpreterState_Clear(PyInterpreterState *interp) if (_PyRuntimeState_GetFinalizing(runtime) == NULL) { _PyWarnings_Fini(interp); } + + /* Last garbage collection on this interpreter */ + _PyGC_CollectNoFail(tstate); + + _PyGC_Fini(tstate); + /* We don't clear sysdict and builtins until the end of this function. Because clearing other attributes can execute arbitrary Python code which requires sysdict and builtins. */ @@ -320,6 +323,25 @@ PyInterpreterState_Clear(PyInterpreterState *interp) } +void +PyInterpreterState_Clear(PyInterpreterState *interp) +{ + // Use the current Python thread state to call audit hooks and to collect + // garbage. It can be different than the current Python thread state + // of 'interp'. + PyThreadState *current_tstate = _PyThreadState_GET(); + + interpreter_clear(interp, current_tstate); +} + + +void +_PyInterpreterState_Clear(PyThreadState *tstate) +{ + interpreter_clear(tstate->interp, tstate); +} + + static void zapthreads(PyInterpreterState *interp, int check_current) { From webhook-mailer at python.org Fri Oct 30 17:52:34 2020 From: webhook-mailer at python.org (vstinner) Date: Fri, 30 Oct 2020 21:52:34 -0000 Subject: [Python-checkins] GitHub Action: Add gdb to posix dependencies (GH-23043) Message-ID: https://github.com/python/cpython/commit/6e03c0ad156797cd6e9132e895d55dac0344d340 commit: 6e03c0ad156797cd6e9132e895d55dac0344d340 branch: master author: Victor Stinner committer: vstinner date: 2020-10-30T22:52:30+01:00 summary: GitHub Action: Add gdb to posix dependencies (GH-23043) Sort also dependencies and remove duplicates (liblzma-dev). files: M .github/workflows/posix-deps-apt.sh diff --git a/.github/workflows/posix-deps-apt.sh b/.github/workflows/posix-deps-apt.sh index 2b879d32f8150..5c7b9988be451 100755 --- a/.github/workflows/posix-deps-apt.sh +++ b/.github/workflows/posix-deps-apt.sh @@ -3,19 +3,19 @@ apt-get update apt-get -yq install \ build-essential \ - zlib1g-dev \ + gdb \ + lcov \ libbz2-dev \ + libffi-dev \ + libgdbm-dev \ liblzma-dev \ libncurses5-dev \ libreadline6-dev \ libsqlite3-dev \ libssl-dev \ - libgdbm-dev \ - tk-dev \ lzma \ lzma-dev \ - liblzma-dev \ - libffi-dev \ + tk-dev \ uuid-dev \ xvfb \ - lcov + zlib1g-dev From webhook-mailer at python.org Fri Oct 30 18:16:27 2020 From: webhook-mailer at python.org (vstinner) Date: Fri, 30 Oct 2020 22:16:27 -0000 Subject: [Python-checkins] bpo-42208: GitHub Action: Add gdb to posix dependencies (GH-23043) (GH-23047) Message-ID: https://github.com/python/cpython/commit/09c6120be8c70366495b027ae3daa213609de3ed commit: 09c6120be8c70366495b027ae3daa213609de3ed branch: 3.9 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: vstinner date: 2020-10-30T23:16:17+01:00 summary: bpo-42208: GitHub Action: Add gdb to posix dependencies (GH-23043) (GH-23047) Sort also dependencies and remove duplicates (liblzma-dev). (cherry picked from commit 6e03c0ad156797cd6e9132e895d55dac0344d340) Co-authored-by: Victor Stinner Co-authored-by: Victor Stinner files: M .github/workflows/posix-deps-apt.sh diff --git a/.github/workflows/posix-deps-apt.sh b/.github/workflows/posix-deps-apt.sh index 2b879d32f8150..5c7b9988be451 100755 --- a/.github/workflows/posix-deps-apt.sh +++ b/.github/workflows/posix-deps-apt.sh @@ -3,19 +3,19 @@ apt-get update apt-get -yq install \ build-essential \ - zlib1g-dev \ + gdb \ + lcov \ libbz2-dev \ + libffi-dev \ + libgdbm-dev \ liblzma-dev \ libncurses5-dev \ libreadline6-dev \ libsqlite3-dev \ libssl-dev \ - libgdbm-dev \ - tk-dev \ lzma \ lzma-dev \ - liblzma-dev \ - libffi-dev \ + tk-dev \ uuid-dev \ xvfb \ - lcov + zlib1g-dev From webhook-mailer at python.org Fri Oct 30 18:36:38 2020 From: webhook-mailer at python.org (miss-islington) Date: Fri, 30 Oct 2020 22:36:38 -0000 Subject: [Python-checkins] GitHub Action: Add gdb to posix dependencies (GH-23043) Message-ID: https://github.com/python/cpython/commit/10260c737b19a99eab532fcccf2b0a1ffcb0805d commit: 10260c737b19a99eab532fcccf2b0a1ffcb0805d branch: 3.8 author: Miss Skeleton (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-30T15:36:31-07:00 summary: GitHub Action: Add gdb to posix dependencies (GH-23043) Sort also dependencies and remove duplicates (liblzma-dev). (cherry picked from commit 6e03c0ad156797cd6e9132e895d55dac0344d340) Co-authored-by: Victor Stinner files: M .github/workflows/posix-deps-apt.sh diff --git a/.github/workflows/posix-deps-apt.sh b/.github/workflows/posix-deps-apt.sh index 2b879d32f8150..5c7b9988be451 100755 --- a/.github/workflows/posix-deps-apt.sh +++ b/.github/workflows/posix-deps-apt.sh @@ -3,19 +3,19 @@ apt-get update apt-get -yq install \ build-essential \ - zlib1g-dev \ + gdb \ + lcov \ libbz2-dev \ + libffi-dev \ + libgdbm-dev \ liblzma-dev \ libncurses5-dev \ libreadline6-dev \ libsqlite3-dev \ libssl-dev \ - libgdbm-dev \ - tk-dev \ lzma \ lzma-dev \ - liblzma-dev \ - libffi-dev \ + tk-dev \ uuid-dev \ xvfb \ - lcov + zlib1g-dev From webhook-mailer at python.org Fri Oct 30 19:48:49 2020 From: webhook-mailer at python.org (pablogsal) Date: Fri, 30 Oct 2020 23:48:49 -0000 Subject: [Python-checkins] bpo-42214: Fix check for NOTEQUAL token in the PEG parser for the barry_as_flufl rule (GH-23048) Message-ID: https://github.com/python/cpython/commit/06f8c3328dcd81c84d1ee2b3a57b5381dcb38482 commit: 06f8c3328dcd81c84d1ee2b3a57b5381dcb38482 branch: master author: Pablo Galindo committer: pablogsal date: 2020-10-30T23:48:42Z summary: bpo-42214: Fix check for NOTEQUAL token in the PEG parser for the barry_as_flufl rule (GH-23048) files: A Misc/NEWS.d/next/Core and Builtins/2020-10-30-22-16-30.bpo-42214.lXskM_.rst M Grammar/python.gram M Lib/test/test_syntax.py M Parser/parser.c M Parser/pegen.c M Parser/pegen.h diff --git a/Grammar/python.gram b/Grammar/python.gram index b8da554b8ec99..ae5e4b5d4ca64 100644 --- a/Grammar/python.gram +++ b/Grammar/python.gram @@ -428,7 +428,7 @@ compare_op_bitwise_or_pair[CmpopExprPair*]: | is_bitwise_or eq_bitwise_or[CmpopExprPair*]: '==' a=bitwise_or { _PyPegen_cmpop_expr_pair(p, Eq, a) } noteq_bitwise_or[CmpopExprPair*]: - | (tok='!=' {_PyPegen_check_barry_as_flufl(p) ? NULL : tok}) a=bitwise_or {_PyPegen_cmpop_expr_pair(p, NotEq, a) } + | (tok='!=' { _PyPegen_check_barry_as_flufl(p, tok) ? NULL : tok}) a=bitwise_or {_PyPegen_cmpop_expr_pair(p, NotEq, a) } lte_bitwise_or[CmpopExprPair*]: '<=' a=bitwise_or { _PyPegen_cmpop_expr_pair(p, LtE, a) } lt_bitwise_or[CmpopExprPair*]: '<' a=bitwise_or { _PyPegen_cmpop_expr_pair(p, Lt, a) } gte_bitwise_or[CmpopExprPair*]: '>=' a=bitwise_or { _PyPegen_cmpop_expr_pair(p, GtE, a) } diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index c25b85246b919..e89d9401f2c39 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -955,6 +955,23 @@ def test_nested_named_except_blocks(self): code += f"{' '*4*12}pass" self._check_error(code, "too many statically nested blocks") + def test_barry_as_flufl_with_syntax_errors(self): + # The "barry_as_flufl" rule can produce some "bugs-at-a-distance" if + # is reading the wrong token in the presence of syntax errors later + # in the file. See bpo-42214 for more information. + code = """ +def func1(): + if a != b: + raise ValueError + +def func2(): + try + return 1 + finally: + pass +""" + self._check_error(code, "invalid syntax") + def test_main(): support.run_unittest(SyntaxTestCase) from test import test_syntax diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-10-30-22-16-30.bpo-42214.lXskM_.rst b/Misc/NEWS.d/next/Core and Builtins/2020-10-30-22-16-30.bpo-42214.lXskM_.rst new file mode 100644 index 0000000000000..3f85bbe83901a --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2020-10-30-22-16-30.bpo-42214.lXskM_.rst @@ -0,0 +1,2 @@ +Fixed a possible crash in the PEG parser when checking for the '!=' token in +the ``barry_as_flufl`` rule. Patch by Pablo Galindo. diff --git a/Parser/parser.c b/Parser/parser.c index a22cf2752d18d..a882a81344cc6 100644 --- a/Parser/parser.c +++ b/Parser/parser.c @@ -21288,7 +21288,7 @@ _tmp_93_rule(Parser *p) ) { D(fprintf(stderr, "%*c+ _tmp_93[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'!='")); - _res = _PyPegen_check_barry_as_flufl ( p ) ? NULL : tok; + _res = _PyPegen_check_barry_as_flufl ( p , tok ) ? NULL : tok; if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; D(p->level--); diff --git a/Parser/pegen.c b/Parser/pegen.c index 216edd810e246..188fd282b7604 100644 --- a/Parser/pegen.c +++ b/Parser/pegen.c @@ -62,8 +62,7 @@ init_normalization(Parser *p) /* Checks if the NOTEQUAL token is valid given the current parser flags 0 indicates success and nonzero indicates failure (an exception may be set) */ int -_PyPegen_check_barry_as_flufl(Parser *p) { - Token *t = p->tokens[p->fill - 1]; +_PyPegen_check_barry_as_flufl(Parser *p, Token* t) { assert(t->bytes != NULL); assert(t->type == NOTEQUAL); diff --git a/Parser/pegen.h b/Parser/pegen.h index 841f1e5eb4396..f82a3a00b2ba0 100644 --- a/Parser/pegen.h +++ b/Parser/pegen.h @@ -263,7 +263,7 @@ expr_ty _PyPegen_collect_call_seqs(Parser *, asdl_expr_seq *, asdl_seq *, int end_col_offset, PyArena *arena); expr_ty _PyPegen_concatenate_strings(Parser *p, asdl_seq *); asdl_seq *_PyPegen_join_sequences(Parser *, asdl_seq *, asdl_seq *); -int _PyPegen_check_barry_as_flufl(Parser *); +int _PyPegen_check_barry_as_flufl(Parser *, Token *); mod_ty _PyPegen_make_module(Parser *, asdl_stmt_seq *); // Error reporting helpers From webhook-mailer at python.org Fri Oct 30 20:02:17 2020 From: webhook-mailer at python.org (vstinner) Date: Sat, 31 Oct 2020 00:02:17 -0000 Subject: [Python-checkins] bpo-42208: Add _Py_GetLocaleEncoding() (GH-23050) Message-ID: https://github.com/python/cpython/commit/710e82630775774dceba5e8f24b1b10e6dfaf9b7 commit: 710e82630775774dceba5e8f24b1b10e6dfaf9b7 branch: master author: Victor Stinner committer: vstinner date: 2020-10-31T01:02:09+01:00 summary: bpo-42208: Add _Py_GetLocaleEncoding() (GH-23050) _io.TextIOWrapper no longer calls getpreferredencoding(False) of _bootlocale to get the locale encoding, but calls _Py_GetLocaleEncoding() instead. Add config_get_fs_encoding() sub-function. Reorganize also config_get_locale_encoding() code. files: M Include/internal/pycore_fileutils.h M Modules/_io/_iomodule.c M Modules/_io/_iomodule.h M Modules/_io/textio.c M Python/fileutils.c M Python/initconfig.c diff --git a/Include/internal/pycore_fileutils.h b/Include/internal/pycore_fileutils.h index 9cb5fc66ee2e0..ff7bc4874c797 100644 --- a/Include/internal/pycore_fileutils.h +++ b/Include/internal/pycore_fileutils.h @@ -50,6 +50,8 @@ PyAPI_FUNC(int) _Py_GetLocaleconvNumeric( PyAPI_FUNC(void) _Py_closerange(int first, int last); +PyAPI_FUNC(PyObject*) _Py_GetLocaleEncoding(void); + #ifdef __cplusplus } #endif diff --git a/Modules/_io/_iomodule.c b/Modules/_io/_iomodule.c index e430352a48e21..9147648b243be 100644 --- a/Modules/_io/_iomodule.c +++ b/Modules/_io/_iomodule.c @@ -593,31 +593,6 @@ _PyIO_get_module_state(void) return state; } -PyObject * -_PyIO_get_locale_module(_PyIO_State *state) -{ - PyObject *mod; - if (state->locale_module != NULL) { - assert(PyWeakref_CheckRef(state->locale_module)); - mod = PyWeakref_GET_OBJECT(state->locale_module); - if (mod != Py_None) { - Py_INCREF(mod); - return mod; - } - Py_CLEAR(state->locale_module); - } - mod = PyImport_ImportModule("_bootlocale"); - if (mod == NULL) - return NULL; - state->locale_module = PyWeakref_NewRef(mod, NULL); - if (state->locale_module == NULL) { - Py_DECREF(mod); - return NULL; - } - return mod; -} - - static int iomodule_traverse(PyObject *mod, visitproc visit, void *arg) { _PyIO_State *state = get_io_state(mod); diff --git a/Modules/_io/_iomodule.h b/Modules/_io/_iomodule.h index a8f3951e57feb..638797fd35736 100644 --- a/Modules/_io/_iomodule.h +++ b/Modules/_io/_iomodule.h @@ -150,7 +150,6 @@ typedef struct { #define IO_STATE() _PyIO_get_module_state() extern _PyIO_State *_PyIO_get_module_state(void); -extern PyObject *_PyIO_get_locale_module(_PyIO_State *); #ifdef MS_WINDOWS extern char _PyIO_get_console_type(PyObject *); diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c index 699b7e94c93bb..2078bb316b282 100644 --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -10,6 +10,7 @@ #include "Python.h" #include "pycore_interp.h" // PyInterpreterState.fs_codec #include "pycore_long.h" // _PyLong_GetZero() +#include "pycore_fileutils.h" // _Py_GetLocaleEncoding() #include "pycore_object.h" #include "pycore_pystate.h" // _PyInterpreterState_GET() #include "structmember.h" // PyMemberDef @@ -27,7 +28,6 @@ _Py_IDENTIFIER(_dealloc_warn); _Py_IDENTIFIER(decode); _Py_IDENTIFIER(fileno); _Py_IDENTIFIER(flush); -_Py_IDENTIFIER(getpreferredencoding); _Py_IDENTIFIER(isatty); _Py_IDENTIFIER(mode); _Py_IDENTIFIER(name); @@ -1155,29 +1155,11 @@ _io_TextIOWrapper___init___impl(textio *self, PyObject *buffer, } } if (encoding == NULL && self->encoding == NULL) { - PyObject *locale_module = _PyIO_get_locale_module(state); - if (locale_module == NULL) - goto catch_ImportError; - self->encoding = _PyObject_CallMethodIdOneArg( - locale_module, &PyId_getpreferredencoding, Py_False); - Py_DECREF(locale_module); + self->encoding = _Py_GetLocaleEncoding(); if (self->encoding == NULL) { - catch_ImportError: - /* - Importing locale can raise an ImportError because of - _functools, and locale.getpreferredencoding can raise an - ImportError if _locale is not available. These will happen - during module building. - */ - if (PyErr_ExceptionMatches(PyExc_ImportError)) { - PyErr_Clear(); - self->encoding = PyUnicode_FromString("ascii"); - } - else - goto error; + goto error; } - else if (!PyUnicode_Check(self->encoding)) - Py_CLEAR(self->encoding); + assert(PyUnicode_Check(self->encoding)); } if (self->encoding != NULL) { encoding = PyUnicode_AsUTF8(self->encoding); diff --git a/Python/fileutils.c b/Python/fileutils.c index e125ba46c21ba..ba2690429f366 100644 --- a/Python/fileutils.c +++ b/Python/fileutils.c @@ -1,5 +1,6 @@ #include "Python.h" -#include "pycore_fileutils.h" +#include "pycore_fileutils.h" // fileutils definitions +#include "pycore_runtime.h" // _PyRuntime #include "osdefs.h" // SEP #include @@ -820,6 +821,46 @@ _Py_EncodeLocaleEx(const wchar_t *text, char **str, } +// Get the current locale encoding: locale.getpreferredencoding(False). +// See also config_get_locale_encoding() +PyObject * +_Py_GetLocaleEncoding(void) +{ +#ifdef _Py_FORCE_UTF8_LOCALE + // On Android langinfo.h and CODESET are missing, + // and UTF-8 is always used in mbstowcs() and wcstombs(). + return PyUnicode_FromString("UTF-8"); +#else + const PyPreConfig *preconfig = &_PyRuntime.preconfig; + if (preconfig->utf8_mode) { + return PyUnicode_FromString("UTF-8"); + } + +#if defined(MS_WINDOWS) + return PyUnicode_FromFormat("cp%u", GetACP()); +#else + const char *encoding = nl_langinfo(CODESET); + if (!encoding || encoding[0] == '\0') { +#ifdef _Py_FORCE_UTF8_FS_ENCODING + // nl_langinfo() can return an empty string when the LC_CTYPE locale is + // not supported. Default to UTF-8 in that case, because UTF-8 is the + // default charset on macOS. + encoding = "UTF-8"; +#else + PyErr_SetString(PyExc_ValueError, + "failed to get the locale encoding: " + "nl_langinfo(CODESET) returns an empty string"); + return NULL; +#endif + } + // Decode from UTF-8 + return PyUnicode_FromString(encoding); +#endif // !CODESET + +#endif +} + + #ifdef MS_WINDOWS static __int64 secs_between_epochs = 11644473600; /* Seconds between 1.1.1601 and 1.1.1970 */ diff --git a/Python/initconfig.c b/Python/initconfig.c index 6a13dc52ed776..e129278d8f8ad 100644 --- a/Python/initconfig.c +++ b/Python/initconfig.c @@ -766,7 +766,7 @@ config_set_bytes_string(PyConfig *config, wchar_t **config_str, configured. */ PyStatus PyConfig_SetBytesString(PyConfig *config, wchar_t **config_str, - const char *str) + const char *str) { return CONFIG_SET_BYTES_STR(config, config_str, str, "string"); } @@ -1466,8 +1466,13 @@ config_read_complex_options(PyConfig *config) static const wchar_t * -config_get_stdio_errors(void) +config_get_stdio_errors(const PyPreConfig *preconfig) { + if (preconfig->utf8_mode) { + /* UTF-8 Mode uses UTF-8/surrogateescape */ + return L"surrogateescape"; + } + #ifndef MS_WINDOWS const char *loc = setlocale(LC_CTYPE, NULL); if (loc != NULL) { @@ -1492,26 +1497,41 @@ config_get_stdio_errors(void) } +// See also _Py_GetLocaleEncoding() and config_get_fs_encoding() static PyStatus -config_get_locale_encoding(PyConfig *config, wchar_t **locale_encoding) +config_get_locale_encoding(PyConfig *config, const PyPreConfig *preconfig, + wchar_t **locale_encoding) { +#ifdef _Py_FORCE_UTF8_LOCALE + return PyConfig_SetString(config, locale_encoding, L"utf-8"); +#else + if (preconfig->utf8_mode) { + return PyConfig_SetString(config, locale_encoding, L"utf-8"); + } + #ifdef MS_WINDOWS char encoding[20]; PyOS_snprintf(encoding, sizeof(encoding), "cp%u", GetACP()); return PyConfig_SetBytesString(config, locale_encoding, encoding); -#elif defined(_Py_FORCE_UTF8_LOCALE) - return PyConfig_SetString(config, locale_encoding, L"utf-8"); #else const char *encoding = nl_langinfo(CODESET); if (!encoding || encoding[0] == '\0') { +#ifdef _Py_FORCE_UTF8_FS_ENCODING + // nl_langinfo() can return an empty string when the LC_CTYPE locale is + // not supported. Default to UTF-8 in that case, because UTF-8 is the + // default charset on macOS. + encoding = "UTF-8"; +#else return _PyStatus_ERR("failed to get the locale encoding: " - "nl_langinfo(CODESET) failed"); + "nl_langinfo(CODESET) returns an empty string"); +#endif } /* nl_langinfo(CODESET) is decoded by Py_DecodeLocale() */ return CONFIG_SET_BYTES_STR(config, locale_encoding, encoding, "nl_langinfo(CODESET)"); -#endif +#endif // !MS_WINDOWS +#endif // !_Py_FORCE_UTF8_LOCALE } @@ -1596,33 +1616,16 @@ config_init_stdio_encoding(PyConfig *config, PyMem_RawFree(pythonioencoding); } - /* UTF-8 Mode uses UTF-8/surrogateescape */ - if (preconfig->utf8_mode) { - if (config->stdio_encoding == NULL) { - status = PyConfig_SetString(config, &config->stdio_encoding, - L"utf-8"); - if (_PyStatus_EXCEPTION(status)) { - return status; - } - } - if (config->stdio_errors == NULL) { - status = PyConfig_SetString(config, &config->stdio_errors, - L"surrogateescape"); - if (_PyStatus_EXCEPTION(status)) { - return status; - } - } - } - /* Choose the default error handler based on the current locale. */ if (config->stdio_encoding == NULL) { - status = config_get_locale_encoding(config, &config->stdio_encoding); + status = config_get_locale_encoding(config, preconfig, + &config->stdio_encoding); if (_PyStatus_EXCEPTION(status)) { return status; } } if (config->stdio_errors == NULL) { - const wchar_t *errors = config_get_stdio_errors(); + const wchar_t *errors = config_get_stdio_errors(preconfig); assert(errors != NULL); status = PyConfig_SetString(config, &config->stdio_errors, errors); @@ -1635,46 +1638,46 @@ config_init_stdio_encoding(PyConfig *config, } +// See also config_get_locale_encoding() +static PyStatus +config_get_fs_encoding(PyConfig *config, const PyPreConfig *preconfig, + wchar_t **fs_encoding) +{ +#ifdef _Py_FORCE_UTF8_FS_ENCODING + return PyConfig_SetString(config, fs_encoding, L"utf-8"); +#elif defined(MS_WINDOWS) + const wchar_t *encoding; + if (preconfig->legacy_windows_fs_encoding) { + // Legacy Windows filesystem encoding: mbcs/replace + encoding = L"mbcs"; + } + else { + // Windows defaults to utf-8/surrogatepass (PEP 529) + encoding = L"utf-8"; + } + return PyConfig_SetString(config, fs_encoding, encoding); +#else // !MS_WINDOWS + if (preconfig->utf8_mode) { + return PyConfig_SetString(config, fs_encoding, L"utf-8"); + } + else if (_Py_GetForceASCII()) { + return PyConfig_SetString(config, fs_encoding, L"ascii"); + } + else { + return config_get_locale_encoding(config, preconfig, fs_encoding); + } +#endif // !MS_WINDOWS +} + + static PyStatus config_init_fs_encoding(PyConfig *config, const PyPreConfig *preconfig) { PyStatus status; if (config->filesystem_encoding == NULL) { -#ifdef _Py_FORCE_UTF8_FS_ENCODING - status = PyConfig_SetString(config, &config->filesystem_encoding, L"utf-8"); -#else - -#ifdef MS_WINDOWS - if (preconfig->legacy_windows_fs_encoding) { - /* Legacy Windows filesystem encoding: mbcs/replace */ - status = PyConfig_SetString(config, &config->filesystem_encoding, - L"mbcs"); - } - else -#endif - if (preconfig->utf8_mode) { - status = PyConfig_SetString(config, &config->filesystem_encoding, - L"utf-8"); - } -#ifndef MS_WINDOWS - else if (_Py_GetForceASCII()) { - status = PyConfig_SetString(config, &config->filesystem_encoding, - L"ascii"); - } -#endif - else { -#ifdef MS_WINDOWS - /* Windows defaults to utf-8/surrogatepass (PEP 529). */ - status = PyConfig_SetString(config, &config->filesystem_encoding, - L"utf-8"); -#else - status = config_get_locale_encoding(config, - &config->filesystem_encoding); -#endif - } -#endif /* !_Py_FORCE_UTF8_FS_ENCODING */ - + status = config_get_fs_encoding(config, preconfig, + &config->filesystem_encoding); if (_PyStatus_EXCEPTION(status)) { return status; } From webhook-mailer at python.org Fri Oct 30 20:32:15 2020 From: webhook-mailer at python.org (vstinner) Date: Sat, 31 Oct 2020 00:32:15 -0000 Subject: [Python-checkins] bpo-42208: Add _locale._get_locale_encoding() (GH-23052) Message-ID: https://github.com/python/cpython/commit/b62bdf71ea0cd52041d49691d8ae3dc645bd48e1 commit: b62bdf71ea0cd52041d49691d8ae3dc645bd48e1 branch: master author: Victor Stinner committer: vstinner date: 2020-10-31T01:32:11+01:00 summary: bpo-42208: Add _locale._get_locale_encoding() (GH-23052) * Add a new _locale._get_locale_encoding() function to get the current locale encoding. * Modify locale.getpreferredencoding() to use it. * Remove the _bootlocale module. files: D Lib/_bootlocale.py M Lib/locale.py M Lib/test/test_mimetypes.py M Modules/_localemodule.c M Modules/clinic/_localemodule.c.h M PCbuild/lib.pyproj diff --git a/Lib/_bootlocale.py b/Lib/_bootlocale.py deleted file mode 100644 index 3273a3b42252b..0000000000000 --- a/Lib/_bootlocale.py +++ /dev/null @@ -1,46 +0,0 @@ -"""A minimal subset of the locale module used at interpreter startup -(imported by the _io module), in order to reduce startup time. - -Don't import directly from third-party code; use the `locale` module instead! -""" - -import sys -import _locale - -if sys.platform.startswith("win"): - def getpreferredencoding(do_setlocale=True): - if sys.flags.utf8_mode: - return 'UTF-8' - return _locale._getdefaultlocale()[1] -else: - try: - _locale.CODESET - except AttributeError: - if hasattr(sys, 'getandroidapilevel'): - # On Android langinfo.h and CODESET are missing, and UTF-8 is - # always used in mbstowcs() and wcstombs(). - def getpreferredencoding(do_setlocale=True): - return 'UTF-8' - else: - def getpreferredencoding(do_setlocale=True): - if sys.flags.utf8_mode: - return 'UTF-8' - # This path for legacy systems needs the more complex - # getdefaultlocale() function, import the full locale module. - import locale - return locale.getpreferredencoding(do_setlocale) - else: - def getpreferredencoding(do_setlocale=True): - assert not do_setlocale - if sys.flags.utf8_mode: - return 'UTF-8' - result = _locale.nl_langinfo(_locale.CODESET) - if not result and sys.platform == 'darwin': - # nl_langinfo can return an empty string - # when the setting has an invalid value. - # Default to UTF-8 in that case because - # UTF-8 is the default charset on OSX and - # returning nothing will crash the - # interpreter. - result = 'UTF-8' - return result diff --git a/Lib/locale.py b/Lib/locale.py index 1a4e9f694f309..ee841e8b8655e 100644 --- a/Lib/locale.py +++ b/Lib/locale.py @@ -619,53 +619,49 @@ def resetlocale(category=LC_ALL): """ _setlocale(category, _build_localename(getdefaultlocale())) -if sys.platform.startswith("win"): - # On Win32, this will return the ANSI code page - def getpreferredencoding(do_setlocale = True): - """Return the charset that the user is likely using.""" + +try: + from _locale import _get_locale_encoding +except ImportError: + def _get_locale_encoding(): + if hasattr(sys, 'getandroidapilevel'): + # On Android langinfo.h and CODESET are missing, and UTF-8 is + # always used in mbstowcs() and wcstombs(). + return 'UTF-8' if sys.flags.utf8_mode: return 'UTF-8' - import _bootlocale - return _bootlocale.getpreferredencoding(False) + encoding = getdefaultlocale()[1] + if encoding is None: + # LANG not set, default conservatively to ASCII + encoding = 'ascii' + return encoding + +try: + CODESET +except NameError: + def getpreferredencoding(do_setlocale=True): + """Return the charset that the user is likely using.""" + return _get_locale_encoding() else: # On Unix, if CODESET is available, use that. - try: - CODESET - except NameError: - if hasattr(sys, 'getandroidapilevel'): - # On Android langinfo.h and CODESET are missing, and UTF-8 is - # always used in mbstowcs() and wcstombs(). - def getpreferredencoding(do_setlocale = True): - return 'UTF-8' - else: - # Fall back to parsing environment variables :-( - def getpreferredencoding(do_setlocale = True): - """Return the charset that the user is likely using, - by looking at environment variables.""" - if sys.flags.utf8_mode: - return 'UTF-8' - res = getdefaultlocale()[1] - if res is None: - # LANG not set, default conservatively to ASCII - res = 'ascii' - return res - else: - def getpreferredencoding(do_setlocale = True): - """Return the charset that the user is likely using, - according to the system configuration.""" - if sys.flags.utf8_mode: - return 'UTF-8' - import _bootlocale - if do_setlocale: - oldloc = setlocale(LC_CTYPE) - try: - setlocale(LC_CTYPE, "") - except Error: - pass - result = _bootlocale.getpreferredencoding(False) - if do_setlocale: - setlocale(LC_CTYPE, oldloc) - return result + def getpreferredencoding(do_setlocale=True): + """Return the charset that the user is likely using, + according to the system configuration.""" + if sys.flags.utf8_mode: + return 'UTF-8' + + if not do_setlocale: + return _get_locale_encoding() + + old_loc = setlocale(LC_CTYPE) + try: + try: + setlocale(LC_CTYPE, "") + except Error: + pass + return _get_locale_encoding() + finally: + setlocale(LC_CTYPE, old_loc) ### Database diff --git a/Lib/test/test_mimetypes.py b/Lib/test/test_mimetypes.py index ddeae38e1372f..d63f6b66e10c9 100644 --- a/Lib/test/test_mimetypes.py +++ b/Lib/test/test_mimetypes.py @@ -3,7 +3,7 @@ import mimetypes import pathlib import sys -import unittest +import unittest.mock from test import support from test.support import os_helper @@ -71,14 +71,14 @@ def test_read_mime_types(self): # bpo-41048: read_mime_types should read the rule file with 'utf-8' encoding. # Not with locale encoding. _bootlocale has been imported because io.open(...) # uses it. - with os_helper.temp_dir() as directory: - data = "application/no-mans-land Fran\u00E7ais" - file = pathlib.Path(directory, "sample.mimetype") - file.write_text(data, encoding='utf-8') - import _bootlocale - with support.swap_attr(_bootlocale, 'getpreferredencoding', lambda do_setlocale=True: 'ASCII'): - mime_dict = mimetypes.read_mime_types(file) - eq(mime_dict[".Fran?ais"], "application/no-mans-land") + data = "application/no-mans-land Fran\u00E7ais" + filename = "filename" + fp = io.StringIO(data) + with unittest.mock.patch.object(mimetypes, 'open', + return_value=fp) as mock_open: + mime_dict = mimetypes.read_mime_types(filename) + mock_open.assert_called_with(filename, encoding='utf-8') + eq(mime_dict[".Fran?ais"], "application/no-mans-land") def test_non_standard_types(self): eq = self.assertEqual diff --git a/Modules/_localemodule.c b/Modules/_localemodule.c index 9c7ce876e4059..359deb7544043 100644 --- a/Modules/_localemodule.c +++ b/Modules/_localemodule.c @@ -768,9 +768,24 @@ _locale_bind_textdomain_codeset_impl(PyObject *module, const char *domain, } Py_RETURN_NONE; } -#endif +#endif // HAVE_BIND_TEXTDOMAIN_CODESET + +#endif // HAVE_LIBINTL_H + + +/*[clinic input] +_locale._get_locale_encoding + +Get the current locale encoding. +[clinic start generated code]*/ + +static PyObject * +_locale__get_locale_encoding_impl(PyObject *module) +/*[clinic end generated code: output=e8e2f6f6f184591a input=513d9961d2f45c76]*/ +{ + return _Py_GetLocaleEncoding(); +} -#endif static struct PyMethodDef PyLocale_Methods[] = { _LOCALE_SETLOCALE_METHODDEF @@ -797,6 +812,7 @@ static struct PyMethodDef PyLocale_Methods[] = { _LOCALE_BIND_TEXTDOMAIN_CODESET_METHODDEF #endif #endif + _LOCALE__GET_LOCALE_ENCODING_METHODDEF {NULL, NULL} }; diff --git a/Modules/clinic/_localemodule.c.h b/Modules/clinic/_localemodule.c.h index 5d1db3ece796d..703d034c32e80 100644 --- a/Modules/clinic/_localemodule.c.h +++ b/Modules/clinic/_localemodule.c.h @@ -545,6 +545,24 @@ _locale_bind_textdomain_codeset(PyObject *module, PyObject *const *args, Py_ssiz #endif /* defined(HAVE_LIBINTL_H) && defined(HAVE_BIND_TEXTDOMAIN_CODESET) */ +PyDoc_STRVAR(_locale__get_locale_encoding__doc__, +"_get_locale_encoding($module, /)\n" +"--\n" +"\n" +"Get the current locale encoding."); + +#define _LOCALE__GET_LOCALE_ENCODING_METHODDEF \ + {"_get_locale_encoding", (PyCFunction)_locale__get_locale_encoding, METH_NOARGS, _locale__get_locale_encoding__doc__}, + +static PyObject * +_locale__get_locale_encoding_impl(PyObject *module); + +static PyObject * +_locale__get_locale_encoding(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return _locale__get_locale_encoding_impl(module); +} + #ifndef _LOCALE_STRCOLL_METHODDEF #define _LOCALE_STRCOLL_METHODDEF #endif /* !defined(_LOCALE_STRCOLL_METHODDEF) */ @@ -584,4 +602,4 @@ _locale_bind_textdomain_codeset(PyObject *module, PyObject *const *args, Py_ssiz #ifndef _LOCALE_BIND_TEXTDOMAIN_CODESET_METHODDEF #define _LOCALE_BIND_TEXTDOMAIN_CODESET_METHODDEF #endif /* !defined(_LOCALE_BIND_TEXTDOMAIN_CODESET_METHODDEF) */ -/*[clinic end generated code: output=fe944779cd572d8e input=a9049054013a1b77]*/ +/*[clinic end generated code: output=cd703c8a3a75fcf4 input=a9049054013a1b77]*/ diff --git a/PCbuild/lib.pyproj b/PCbuild/lib.pyproj index f0c51edb9d1ca..a15165d92ef12 100644 --- a/PCbuild/lib.pyproj +++ b/PCbuild/lib.pyproj @@ -1572,7 +1572,6 @@ - From webhook-mailer at python.org Fri Oct 30 20:40:52 2020 From: webhook-mailer at python.org (pablogsal) Date: Sat, 31 Oct 2020 00:40:52 -0000 Subject: [Python-checkins] [3.9] bpo-42214: Fix check for NOTEQUAL token in the PEG parser for the barry_as_flufl rule (GH-23048) (GH-23051) Message-ID: https://github.com/python/cpython/commit/ddcd57e3ea75ab0ad370bbaaa6b76338edbca395 commit: ddcd57e3ea75ab0ad370bbaaa6b76338edbca395 branch: 3.9 author: Pablo Galindo committer: pablogsal date: 2020-10-31T00:40:42Z summary: [3.9] bpo-42214: Fix check for NOTEQUAL token in the PEG parser for the barry_as_flufl rule (GH-23048) (GH-23051) (cherry picked from commit 06f8c3328dcd81c84d1ee2b3a57b5381dcb38482) Co-authored-by: Pablo Galindo files: A Misc/NEWS.d/next/Core and Builtins/2020-10-30-22-16-30.bpo-42214.lXskM_.rst M Grammar/python.gram M Lib/test/test_syntax.py M Parser/pegen/parse.c M Parser/pegen/pegen.c M Parser/pegen/pegen.h diff --git a/Grammar/python.gram b/Grammar/python.gram index b709d3d5c3214..60eeb367e95ba 100644 --- a/Grammar/python.gram +++ b/Grammar/python.gram @@ -413,7 +413,7 @@ compare_op_bitwise_or_pair[CmpopExprPair*]: | is_bitwise_or eq_bitwise_or[CmpopExprPair*]: '==' a=bitwise_or { _PyPegen_cmpop_expr_pair(p, Eq, a) } noteq_bitwise_or[CmpopExprPair*]: - | (tok='!=' {_PyPegen_check_barry_as_flufl(p) ? NULL : tok}) a=bitwise_or {_PyPegen_cmpop_expr_pair(p, NotEq, a) } + | (tok='!=' { _PyPegen_check_barry_as_flufl(p, tok) ? NULL : tok}) a=bitwise_or {_PyPegen_cmpop_expr_pair(p, NotEq, a) } lte_bitwise_or[CmpopExprPair*]: '<=' a=bitwise_or { _PyPegen_cmpop_expr_pair(p, LtE, a) } lt_bitwise_or[CmpopExprPair*]: '<' a=bitwise_or { _PyPegen_cmpop_expr_pair(p, Lt, a) } gte_bitwise_or[CmpopExprPair*]: '>=' a=bitwise_or { _PyPegen_cmpop_expr_pair(p, GtE, a) } diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index b0527e6cd85ba..a95992d869e5e 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -947,6 +947,23 @@ def test_empty_line_after_linecont(self): self.fail("Empty line after a line continuation character is valid.") + def test_barry_as_flufl_with_syntax_errors(self): + # The "barry_as_flufl" rule can produce some "bugs-at-a-distance" if + # is reading the wrong token in the presence of syntax errors later + # in the file. See bpo-42214 for more information. + code = """ +def func1(): + if a != b: + raise ValueError + +def func2(): + try + return 1 + finally: + pass +""" + self._check_error(code, "invalid syntax") + def test_main(): support.run_unittest(SyntaxTestCase) from test import test_syntax diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-10-30-22-16-30.bpo-42214.lXskM_.rst b/Misc/NEWS.d/next/Core and Builtins/2020-10-30-22-16-30.bpo-42214.lXskM_.rst new file mode 100644 index 0000000000000..3f85bbe83901a --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2020-10-30-22-16-30.bpo-42214.lXskM_.rst @@ -0,0 +1,2 @@ +Fixed a possible crash in the PEG parser when checking for the '!=' token in +the ``barry_as_flufl`` rule. Patch by Pablo Galindo. diff --git a/Parser/pegen/parse.c b/Parser/pegen/parse.c index 48a2443474684..bae9463e274a3 100644 --- a/Parser/pegen/parse.c +++ b/Parser/pegen/parse.c @@ -21307,7 +21307,7 @@ _tmp_93_rule(Parser *p) ) { D(fprintf(stderr, "%*c+ _tmp_93[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'!='")); - _res = _PyPegen_check_barry_as_flufl ( p ) ? NULL : tok; + _res = _PyPegen_check_barry_as_flufl ( p , tok ) ? NULL : tok; if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; D(p->level--); diff --git a/Parser/pegen/pegen.c b/Parser/pegen/pegen.c index 78891af825007..4e742a5ec7175 100644 --- a/Parser/pegen/pegen.c +++ b/Parser/pegen/pegen.c @@ -62,8 +62,7 @@ init_normalization(Parser *p) /* Checks if the NOTEQUAL token is valid given the current parser flags 0 indicates success and nonzero indicates failure (an exception may be set) */ int -_PyPegen_check_barry_as_flufl(Parser *p) { - Token *t = p->tokens[p->fill - 1]; +_PyPegen_check_barry_as_flufl(Parser *p, Token* t) { assert(t->bytes != NULL); assert(t->type == NOTEQUAL); diff --git a/Parser/pegen/pegen.h b/Parser/pegen/pegen.h index 2fea84fd22ab4..a2f524a597d23 100644 --- a/Parser/pegen/pegen.h +++ b/Parser/pegen/pegen.h @@ -263,7 +263,7 @@ expr_ty _PyPegen_collect_call_seqs(Parser *, asdl_seq *, asdl_seq *, int end_col_offset, PyArena *arena); expr_ty _PyPegen_concatenate_strings(Parser *p, asdl_seq *); asdl_seq *_PyPegen_join_sequences(Parser *, asdl_seq *, asdl_seq *); -int _PyPegen_check_barry_as_flufl(Parser *); +int _PyPegen_check_barry_as_flufl(Parser *, Token *); mod_ty _PyPegen_make_module(Parser *, asdl_seq *); // Error reporting helpers From webhook-mailer at python.org Fri Oct 30 22:15:46 2020 From: webhook-mailer at python.org (methane) Date: Sat, 31 Oct 2020 02:15:46 -0000 Subject: [Python-checkins] Revert "bpo-42160: tempfile: Reduce overhead of pid check. (GH-22997)" Message-ID: https://github.com/python/cpython/commit/43ca084c88d1e46a44199f347c72e26db84903c9 commit: 43ca084c88d1e46a44199f347c72e26db84903c9 branch: master author: Inada Naoki committer: methane date: 2020-10-31T11:15:38+09:00 summary: Revert "bpo-42160: tempfile: Reduce overhead of pid check. (GH-22997)" `_RandomNameSequence` is not true singleton so using `os.register_at_fork` doesn't make sense unlike `random._inst`. This reverts commit 8e409cebad42032bb7d0f2cadd8b1e36081d98af. files: D Misc/NEWS.d/next/Library/2020-10-27-00-42-09.bpo-42160.eiLOCi.rst M Lib/tempfile.py M Lib/test/test_tempfile.py diff --git a/Lib/tempfile.py b/Lib/tempfile.py index 1bc5c71fd0349..770f72c25295c 100644 --- a/Lib/tempfile.py +++ b/Lib/tempfile.py @@ -129,22 +129,24 @@ class _RandomNameSequence: _RandomNameSequence is an iterator.""" - def __init__(self, characters="abcdefghijklmnopqrstuvwxyz0123456789_", length=8, rng=None): - if rng is None: - rng = _Random() - if hasattr(_os, "fork"): - # prevent same state after fork - _os.register_at_fork(after_in_child=rng.seed) - self.rng = rng - self.characters = characters - self.length = length + characters = "abcdefghijklmnopqrstuvwxyz0123456789_" + + @property + def rng(self): + cur_pid = _os.getpid() + if cur_pid != getattr(self, '_rng_pid', None): + self._rng = _Random() + self._rng_pid = cur_pid + return self._rng def __iter__(self): return self def __next__(self): c = self.characters - return ''.join(self.rng.choices(c, k=self.length)) + choose = self.rng.choice + letters = [choose(c) for dummy in range(8)] + return ''.join(letters) def _candidate_tempdir_list(): """Generate a list of candidate temporary directories which diff --git a/Lib/test/test_tempfile.py b/Lib/test/test_tempfile.py index 77d710efaf107..8ace883d74bb2 100644 --- a/Lib/test/test_tempfile.py +++ b/Lib/test/test_tempfile.py @@ -153,8 +153,8 @@ def setUp(self): self.r = tempfile._RandomNameSequence() super().setUp() - def test_get_eight_char_str(self): - # _RandomNameSequence returns a eight-character string + def test_get_six_char_str(self): + # _RandomNameSequence returns a six-character string s = next(self.r) self.nameCheck(s, '', '', '') diff --git a/Misc/NEWS.d/next/Library/2020-10-27-00-42-09.bpo-42160.eiLOCi.rst b/Misc/NEWS.d/next/Library/2020-10-27-00-42-09.bpo-42160.eiLOCi.rst deleted file mode 100644 index c5f3091283a87..0000000000000 --- a/Misc/NEWS.d/next/Library/2020-10-27-00-42-09.bpo-42160.eiLOCi.rst +++ /dev/null @@ -1 +0,0 @@ -Replaced pid check in ``tempfile._RandomNameSequence`` with ``os.register_at_fork`` to reduce overhead. From webhook-mailer at python.org Sat Oct 31 02:02:46 2020 From: webhook-mailer at python.org (miss-islington) Date: Sat, 31 Oct 2020 06:02:46 -0000 Subject: [Python-checkins] bpo-42198: Document __new__ for types.GenericAlias (GH-23039) Message-ID: https://github.com/python/cpython/commit/bcbf758476c1148993ddf4b54d3f6169b973ee1c commit: bcbf758476c1148993ddf4b54d3f6169b973ee1c branch: master author: kj <28750310+Fidget-Spinner at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-30T23:02:38-07:00 summary: bpo-42198: Document __new__ for types.GenericAlias (GH-23039) files: M Doc/library/stdtypes.rst M Doc/library/types.rst diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 3fd94ea1bd310..09477e63786c5 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -4793,7 +4793,8 @@ The ``GenericAlias`` object acts as a proxy for :term:`generic types of a generic which provides the types for container elements. The user-exposed type for the ``GenericAlias`` object can be accessed from -:data:`types.GenericAlias` and used for :func:`isinstance` checks. +:class:`types.GenericAlias` and used for :func:`isinstance` checks. It can +also be used to create ``GenericAlias`` objects directly. .. describe:: T[X, Y, ...] diff --git a/Doc/library/types.rst b/Doc/library/types.rst index 00720559d0a4a..ad40a9fbf3273 100644 --- a/Doc/library/types.rst +++ b/Doc/library/types.rst @@ -262,11 +262,22 @@ Standard names are defined for the following types: .. versionadded:: 3.10 -.. data:: GenericAlias +.. class:: GenericAlias(t_origin, t_args) The type of :ref:`parameterized generics ` such as ``list[int]``. + ``t_origin`` should be a non-parameterized generic class, such as ``list``, + ``tuple`` or ``dict``. ``t_args`` should be a :class:`tuple` (possibly of + length 1) of types which parameterize ``t_origin``:: + + >>> from types import GenericAlias + + >>> list[int] == GenericAlias(list, (int,)) + True + >>> dict[str, int] == GenericAlias(dict, (str, int)) + True + .. versionadded:: 3.9 .. data:: Union From webhook-mailer at python.org Sat Oct 31 02:07:52 2020 From: webhook-mailer at python.org (corona10) Date: Sat, 31 Oct 2020 06:07:52 -0000 Subject: [Python-checkins] bpo-40956: Convert _sqlite3 module level functions to Argument Clinic (GH-22484) Message-ID: https://github.com/python/cpython/commit/7d210271579ae31f43b32f73c2aff5bc4fe0d27f commit: 7d210271579ae31f43b32f73c2aff5bc4fe0d27f branch: master author: Erlend Egeberg Aasland committer: corona10 date: 2020-10-31T15:07:44+09:00 summary: bpo-40956: Convert _sqlite3 module level functions to Argument Clinic (GH-22484) files: A Modules/_sqlite/clinic/module.c.h M Modules/_sqlite/microprotocols.c M Modules/_sqlite/microprotocols.h M Modules/_sqlite/module.c diff --git a/Modules/_sqlite/clinic/module.c.h b/Modules/_sqlite/clinic/module.c.h new file mode 100644 index 0000000000000..fb1e1187b209f --- /dev/null +++ b/Modules/_sqlite/clinic/module.c.h @@ -0,0 +1,222 @@ +/*[clinic input] +preserve +[clinic start generated code]*/ + +PyDoc_STRVAR(pysqlite_complete_statement__doc__, +"complete_statement($module, /, statement)\n" +"--\n" +"\n" +"Checks if a string contains a complete SQL statement. Non-standard."); + +#define PYSQLITE_COMPLETE_STATEMENT_METHODDEF \ + {"complete_statement", (PyCFunction)(void(*)(void))pysqlite_complete_statement, METH_FASTCALL|METH_KEYWORDS, pysqlite_complete_statement__doc__}, + +static PyObject * +pysqlite_complete_statement_impl(PyObject *module, const char *statement); + +static PyObject * +pysqlite_complete_statement(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + static const char * const _keywords[] = {"statement", NULL}; + static _PyArg_Parser _parser = {NULL, _keywords, "complete_statement", 0}; + PyObject *argsbuf[1]; + const char *statement; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + if (!args) { + goto exit; + } + if (!PyUnicode_Check(args[0])) { + _PyArg_BadArgument("complete_statement", "argument 'statement'", "str", args[0]); + goto exit; + } + Py_ssize_t statement_length; + statement = PyUnicode_AsUTF8AndSize(args[0], &statement_length); + if (statement == NULL) { + goto exit; + } + if (strlen(statement) != (size_t)statement_length) { + PyErr_SetString(PyExc_ValueError, "embedded null character"); + goto exit; + } + return_value = pysqlite_complete_statement_impl(module, statement); + +exit: + return return_value; +} + +PyDoc_STRVAR(pysqlite_enable_shared_cache__doc__, +"enable_shared_cache($module, /, do_enable)\n" +"--\n" +"\n" +"Enable or disable shared cache mode for the calling thread.\n" +"\n" +"Experimental/Non-standard."); + +#define PYSQLITE_ENABLE_SHARED_CACHE_METHODDEF \ + {"enable_shared_cache", (PyCFunction)(void(*)(void))pysqlite_enable_shared_cache, METH_FASTCALL|METH_KEYWORDS, pysqlite_enable_shared_cache__doc__}, + +static PyObject * +pysqlite_enable_shared_cache_impl(PyObject *module, int do_enable); + +static PyObject * +pysqlite_enable_shared_cache(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + static const char * const _keywords[] = {"do_enable", NULL}; + static _PyArg_Parser _parser = {NULL, _keywords, "enable_shared_cache", 0}; + PyObject *argsbuf[1]; + int do_enable; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + if (!args) { + goto exit; + } + do_enable = _PyLong_AsInt(args[0]); + if (do_enable == -1 && PyErr_Occurred()) { + goto exit; + } + return_value = pysqlite_enable_shared_cache_impl(module, do_enable); + +exit: + return return_value; +} + +PyDoc_STRVAR(pysqlite_register_adapter__doc__, +"register_adapter($module, type, caster, /)\n" +"--\n" +"\n" +"Registers an adapter with pysqlite\'s adapter registry. Non-standard."); + +#define PYSQLITE_REGISTER_ADAPTER_METHODDEF \ + {"register_adapter", (PyCFunction)(void(*)(void))pysqlite_register_adapter, METH_FASTCALL, pysqlite_register_adapter__doc__}, + +static PyObject * +pysqlite_register_adapter_impl(PyObject *module, PyTypeObject *type, + PyObject *caster); + +static PyObject * +pysqlite_register_adapter(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyTypeObject *type; + PyObject *caster; + + if (!_PyArg_CheckPositional("register_adapter", nargs, 2, 2)) { + goto exit; + } + type = (PyTypeObject *)args[0]; + caster = args[1]; + return_value = pysqlite_register_adapter_impl(module, type, caster); + +exit: + return return_value; +} + +PyDoc_STRVAR(pysqlite_register_converter__doc__, +"register_converter($module, name, converter, /)\n" +"--\n" +"\n" +"Registers a converter with pysqlite. Non-standard."); + +#define PYSQLITE_REGISTER_CONVERTER_METHODDEF \ + {"register_converter", (PyCFunction)(void(*)(void))pysqlite_register_converter, METH_FASTCALL, pysqlite_register_converter__doc__}, + +static PyObject * +pysqlite_register_converter_impl(PyObject *module, PyObject *orig_name, + PyObject *callable); + +static PyObject * +pysqlite_register_converter(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *orig_name; + PyObject *callable; + + if (!_PyArg_CheckPositional("register_converter", nargs, 2, 2)) { + goto exit; + } + if (!PyUnicode_Check(args[0])) { + _PyArg_BadArgument("register_converter", "argument 1", "str", args[0]); + goto exit; + } + if (PyUnicode_READY(args[0]) == -1) { + goto exit; + } + orig_name = args[0]; + callable = args[1]; + return_value = pysqlite_register_converter_impl(module, orig_name, callable); + +exit: + return return_value; +} + +PyDoc_STRVAR(pysqlite_enable_callback_trace__doc__, +"enable_callback_tracebacks($module, enable, /)\n" +"--\n" +"\n" +"Enable or disable callback functions throwing errors to stderr."); + +#define PYSQLITE_ENABLE_CALLBACK_TRACE_METHODDEF \ + {"enable_callback_tracebacks", (PyCFunction)pysqlite_enable_callback_trace, METH_O, pysqlite_enable_callback_trace__doc__}, + +static PyObject * +pysqlite_enable_callback_trace_impl(PyObject *module, int enable); + +static PyObject * +pysqlite_enable_callback_trace(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + int enable; + + enable = _PyLong_AsInt(arg); + if (enable == -1 && PyErr_Occurred()) { + goto exit; + } + return_value = pysqlite_enable_callback_trace_impl(module, enable); + +exit: + return return_value; +} + +PyDoc_STRVAR(pysqlite_adapt__doc__, +"adapt($module, obj, proto=PrepareProtocolType, alt=, /)\n" +"--\n" +"\n" +"Adapt given object to given protocol. Non-standard."); + +#define PYSQLITE_ADAPT_METHODDEF \ + {"adapt", (PyCFunction)(void(*)(void))pysqlite_adapt, METH_FASTCALL, pysqlite_adapt__doc__}, + +static PyObject * +pysqlite_adapt_impl(PyObject *module, PyObject *obj, PyObject *proto, + PyObject *alt); + +static PyObject * +pysqlite_adapt(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *obj; + PyObject *proto = (PyObject*)pysqlite_PrepareProtocolType; + PyObject *alt = NULL; + + if (!_PyArg_CheckPositional("adapt", nargs, 1, 3)) { + goto exit; + } + obj = args[0]; + if (nargs < 2) { + goto skip_optional; + } + proto = args[1]; + if (nargs < 3) { + goto skip_optional; + } + alt = args[2]; +skip_optional: + return_value = pysqlite_adapt_impl(module, obj, proto, alt); + +exit: + return return_value; +} +/*[clinic end generated code: output=d87990f941c209fa input=a9049054013a1b77]*/ diff --git a/Modules/_sqlite/microprotocols.c b/Modules/_sqlite/microprotocols.c index ddc30e8a89b46..cf1fefd671851 100644 --- a/Modules/_sqlite/microprotocols.c +++ b/Modules/_sqlite/microprotocols.c @@ -29,7 +29,6 @@ #include "microprotocols.h" #include "prepare_protocol.h" - /** the adapters registry **/ static PyObject *psyco_adapters = NULL; @@ -150,15 +149,3 @@ pysqlite_microprotocols_adapt(PyObject *obj, PyObject *proto, PyObject *alt) PyErr_SetString(pysqlite_ProgrammingError, "can't adapt"); return NULL; } - -/** module-level functions **/ - -PyObject * -pysqlite_adapt(pysqlite_Cursor *self, PyObject *args) -{ - PyObject *obj, *alt = NULL; - PyObject *proto = (PyObject*)pysqlite_PrepareProtocolType; - - if (!PyArg_ParseTuple(args, "O|OO", &obj, &proto, &alt)) return NULL; - return pysqlite_microprotocols_adapt(obj, proto, alt); -} diff --git a/Modules/_sqlite/microprotocols.h b/Modules/_sqlite/microprotocols.h index 87df6bac55797..e9adef916e724 100644 --- a/Modules/_sqlite/microprotocols.h +++ b/Modules/_sqlite/microprotocols.h @@ -44,9 +44,4 @@ extern int pysqlite_microprotocols_add( extern PyObject *pysqlite_microprotocols_adapt( PyObject *obj, PyObject *proto, PyObject *alt); -extern PyObject * - pysqlite_adapt(pysqlite_Cursor* self, PyObject *args); -#define pysqlite_adapt_doc \ - "adapt(obj, protocol, alternate) -> adapt obj to given protocol. Non-standard." - #endif /* !defined(PSYCOPG_MICROPROTOCOLS_H) */ diff --git a/Modules/_sqlite/module.c b/Modules/_sqlite/module.c index 0297e2fab292e..33324402385f4 100644 --- a/Modules/_sqlite/module.c +++ b/Modules/_sqlite/module.c @@ -33,6 +33,12 @@ #error "SQLite 3.7.3 or higher required" #endif +#include "clinic/module.c.h" +/*[clinic input] +module _sqlite3 +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=81e330492d57488e]*/ + /* static objects at module-level */ PyObject *pysqlite_Error = NULL; @@ -102,18 +108,19 @@ Opens a connection to the SQLite database file *database*. You can use\n\ \":memory:\" to open a database connection to a database that resides in\n\ RAM instead of on disk."); -static PyObject* module_complete(PyObject* self, PyObject* args, PyObject* - kwargs) -{ - static char *kwlist[] = {"statement", NULL}; - char* statement; +/*[clinic input] +_sqlite3.complete_statement as pysqlite_complete_statement - PyObject* result; + statement: str - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &statement)) - { - return NULL; - } +Checks if a string contains a complete SQL statement. Non-standard. +[clinic start generated code]*/ + +static PyObject * +pysqlite_complete_statement_impl(PyObject *module, const char *statement) +/*[clinic end generated code: output=e55f1ff1952df558 input=f6b24996b31c5c33]*/ +{ + PyObject* result; if (sqlite3_complete(statement)) { result = Py_True; @@ -126,23 +133,22 @@ static PyObject* module_complete(PyObject* self, PyObject* args, PyObject* return result; } -PyDoc_STRVAR(module_complete_doc, -"complete_statement(sql)\n\ -\n\ -Checks if a string contains a complete SQL statement. Non-standard."); +/*[clinic input] +_sqlite3.enable_shared_cache as pysqlite_enable_shared_cache -static PyObject* module_enable_shared_cache(PyObject* self, PyObject* args, PyObject* - kwargs) + do_enable: int + +Enable or disable shared cache mode for the calling thread. + +Experimental/Non-standard. +[clinic start generated code]*/ + +static PyObject * +pysqlite_enable_shared_cache_impl(PyObject *module, int do_enable) +/*[clinic end generated code: output=259c74eedee1516b input=8400e41bc58b6b24]*/ { - static char *kwlist[] = {"do_enable", NULL}; - int do_enable; int rc; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &do_enable)) - { - return NULL; - } - rc = sqlite3_enable_shared_cache(do_enable); if (rc != SQLITE_OK) { @@ -153,22 +159,23 @@ static PyObject* module_enable_shared_cache(PyObject* self, PyObject* args, PyOb } } -PyDoc_STRVAR(module_enable_shared_cache_doc, -"enable_shared_cache(do_enable)\n\ -\n\ -Enable or disable shared cache mode for the calling thread.\n\ -Experimental/Non-standard."); +/*[clinic input] +_sqlite3.register_adapter as pysqlite_register_adapter + + type: object(type='PyTypeObject *') + caster: object + / -static PyObject* module_register_adapter(PyObject* self, PyObject* args) +Registers an adapter with pysqlite's adapter registry. Non-standard. +[clinic start generated code]*/ + +static PyObject * +pysqlite_register_adapter_impl(PyObject *module, PyTypeObject *type, + PyObject *caster) +/*[clinic end generated code: output=a287e8db18e8af23 input=839dad90e2492725]*/ { - PyTypeObject* type; - PyObject* caster; int rc; - if (!PyArg_ParseTuple(args, "OO", &type, &caster)) { - return NULL; - } - /* a basic type is adapted; there's a performance optimization if that's not the case * (99 % of all usages) */ if (type == &PyLong_Type || type == &PyFloat_Type @@ -183,23 +190,25 @@ static PyObject* module_register_adapter(PyObject* self, PyObject* args) Py_RETURN_NONE; } -PyDoc_STRVAR(module_register_adapter_doc, -"register_adapter(type, callable)\n\ -\n\ -Registers an adapter with pysqlite's adapter registry. Non-standard."); +/*[clinic input] +_sqlite3.register_converter as pysqlite_register_converter + + name as orig_name: unicode + converter as callable: object + / -static PyObject* module_register_converter(PyObject* self, PyObject* args) +Registers a converter with pysqlite. Non-standard. +[clinic start generated code]*/ + +static PyObject * +pysqlite_register_converter_impl(PyObject *module, PyObject *orig_name, + PyObject *callable) +/*[clinic end generated code: output=a2f2bfeed7230062 input=e074cf7f4890544f]*/ { - PyObject* orig_name; PyObject* name = NULL; - PyObject* callable; PyObject* retval = NULL; _Py_IDENTIFIER(upper); - if (!PyArg_ParseTuple(args, "UO", &orig_name, &callable)) { - return NULL; - } - /* convert the name to upper case */ name = _PyObject_CallMethodIdNoArgs(orig_name, &PyId_upper); if (!name) { @@ -217,24 +226,42 @@ static PyObject* module_register_converter(PyObject* self, PyObject* args) return retval; } -PyDoc_STRVAR(module_register_converter_doc, -"register_converter(typename, callable)\n\ -\n\ -Registers a converter with pysqlite. Non-standard."); +/*[clinic input] +_sqlite3.enable_callback_tracebacks as pysqlite_enable_callback_trace -static PyObject* enable_callback_tracebacks(PyObject* self, PyObject* args) + enable: int + / + +Enable or disable callback functions throwing errors to stderr. +[clinic start generated code]*/ + +static PyObject * +pysqlite_enable_callback_trace_impl(PyObject *module, int enable) +/*[clinic end generated code: output=4ff1d051c698f194 input=cb79d3581eb77c40]*/ { - if (!PyArg_ParseTuple(args, "i", &_pysqlite_enable_callback_tracebacks)) { - return NULL; - } + _pysqlite_enable_callback_tracebacks = enable; Py_RETURN_NONE; } -PyDoc_STRVAR(enable_callback_tracebacks_doc, -"enable_callback_tracebacks(flag)\n\ -\n\ -Enable or disable callback functions throwing errors to stderr."); +/*[clinic input] +_sqlite3.adapt as pysqlite_adapt + + obj: object + proto: object(c_default='(PyObject*)pysqlite_PrepareProtocolType') = PrepareProtocolType + alt: object = NULL + / + +Adapt given object to given protocol. Non-standard. +[clinic start generated code]*/ + +static PyObject * +pysqlite_adapt_impl(PyObject *module, PyObject *obj, PyObject *proto, + PyObject *alt) +/*[clinic end generated code: output=0c3927c5fcd23dd9 input=a58ab77fb5ae22dd]*/ +{ + return pysqlite_microprotocols_adapt(obj, proto, alt); +} static void converters_init(PyObject* module) { @@ -252,18 +279,12 @@ static void converters_init(PyObject* module) static PyMethodDef module_methods[] = { {"connect", (PyCFunction)(void(*)(void))module_connect, METH_VARARGS | METH_KEYWORDS, module_connect_doc}, - {"complete_statement", (PyCFunction)(void(*)(void))module_complete, - METH_VARARGS | METH_KEYWORDS, module_complete_doc}, - {"enable_shared_cache", (PyCFunction)(void(*)(void))module_enable_shared_cache, - METH_VARARGS | METH_KEYWORDS, module_enable_shared_cache_doc}, - {"register_adapter", (PyCFunction)module_register_adapter, - METH_VARARGS, module_register_adapter_doc}, - {"register_converter", (PyCFunction)module_register_converter, - METH_VARARGS, module_register_converter_doc}, - {"adapt", (PyCFunction)pysqlite_adapt, METH_VARARGS, - pysqlite_adapt_doc}, - {"enable_callback_tracebacks", (PyCFunction)enable_callback_tracebacks, - METH_VARARGS, enable_callback_tracebacks_doc}, + PYSQLITE_ADAPT_METHODDEF + PYSQLITE_COMPLETE_STATEMENT_METHODDEF + PYSQLITE_ENABLE_CALLBACK_TRACE_METHODDEF + PYSQLITE_ENABLE_SHARED_CACHE_METHODDEF + PYSQLITE_REGISTER_ADAPTER_METHODDEF + PYSQLITE_REGISTER_CONVERTER_METHODDEF {NULL, NULL} }; From webhook-mailer at python.org Sat Oct 31 11:05:40 2020 From: webhook-mailer at python.org (miss-islington) Date: Sat, 31 Oct 2020 15:05:40 -0000 Subject: [Python-checkins] [3.9] bpo-42198: Document __new__ for types.GenericAlias (GH-23039) (GH-23061) Message-ID: https://github.com/python/cpython/commit/dbaa07db671c4e5842629c8be912f2b0370be794 commit: dbaa07db671c4e5842629c8be912f2b0370be794 branch: 3.9 author: kj <28750310+Fidget-Spinner at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-31T08:05:36-07:00 summary: [3.9] bpo-42198: Document __new__ for types.GenericAlias (GH-23039) (GH-23061) (cherry picked from commit bcbf758476c1148993ddf4b54d3f6169b973ee1c) files: M Doc/library/stdtypes.rst M Doc/library/types.rst diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index e19e76f8311c8..a48cfa1327791 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -4760,7 +4760,8 @@ The ``GenericAlias`` object acts as a proxy for :term:`generic types of a generic which provides the types for container elements. The user-exposed type for the ``GenericAlias`` object can be accessed from -:data:`types.GenericAlias` and used for :func:`isinstance` checks. +:class:`types.GenericAlias` and used for :func:`isinstance` checks. It can +also be used to create ``GenericAlias`` objects directly. .. describe:: T[X, Y, ...] diff --git a/Doc/library/types.rst b/Doc/library/types.rst index 1573e460f2f2b..5d68c6852678c 100644 --- a/Doc/library/types.rst +++ b/Doc/library/types.rst @@ -242,11 +242,22 @@ Standard names are defined for the following types: Defaults to ``None``. Previously the attribute was optional. -.. data:: GenericAlias +.. class:: GenericAlias(t_origin, t_args) The type of :ref:`parameterized generics ` such as ``list[int]``. + ``t_origin`` should be a non-parameterized generic class, such as ``list``, + ``tuple`` or ``dict``. ``t_args`` should be a :class:`tuple` (possibly of + length 1) of types which parameterize ``t_origin``:: + + >>> from types import GenericAlias + + >>> list[int] == GenericAlias(list, (int,)) + True + >>> dict[str, int] == GenericAlias(dict, (str, int)) + True + .. versionadded:: 3.9 From webhook-mailer at python.org Sat Oct 31 11:08:21 2020 From: webhook-mailer at python.org (miss-islington) Date: Sat, 31 Oct 2020 15:08:21 -0000 Subject: [Python-checkins] bpo-42198: Improve consistency of Union docs (GH-23029) Message-ID: https://github.com/python/cpython/commit/d21cb2d5ee56b8975d92e2ee094aba81f0801be5 commit: d21cb2d5ee56b8975d92e2ee094aba81f0801be5 branch: master author: kj <28750310+Fidget-Spinner at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-31T08:08:17-07:00 summary: bpo-42198: Improve consistency of Union docs (GH-23029) No backport is required since union is only in 3.10. This addresses "3. Consistency nitpicks for Union's docs" in the bpo. Please skip news. Thank you. files: M Doc/library/stdtypes.rst M Doc/whatsnew/3.10.rst diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 09477e63786c5..8a9cadde7d5dc 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -4968,15 +4968,16 @@ Union Type pair: union; type A union object holds the value of the ``|`` (bitwise or) operation on -multiple :ref:`type objects`. These types are intended -primarily for type annotations. The union type expression enables cleaner -type hinting syntax compared to :data:`typing.Union`. +multiple :ref:`type objects `. These types are intended +primarily for :term:`type annotations `. The union type expression +enables cleaner type hinting syntax compared to :data:`typing.Union`. .. describe:: X | Y | ... Defines a union object which holds types *X*, *Y*, and so forth. ``X | Y`` means either X or Y. It is equivalent to ``typing.Union[X, Y]``. - Example:: + For example, the following function expects an argument of type + :class:`int` or :class:`float`:: def square(number: int | float) -> int | float: return number ** 2 @@ -4985,15 +4986,15 @@ type hinting syntax compared to :data:`typing.Union`. Union objects can be tested for equality with other union objects. Details: - * Unions of unions are flattened, e.g.:: + * Unions of unions are flattened:: (int | str) | float == int | str | float - * Redundant types are removed, e.g.:: + * Redundant types are removed:: int | str | int == int | str - * When comparing unions, the order is ignored, e.g.:: + * When comparing unions, the order is ignored:: int | str == str | int @@ -5012,14 +5013,8 @@ type hinting syntax compared to :data:`typing.Union`. >>> isinstance("", int | str) True - .. - At the time of writing this, there is no documentation for parameterized - generics or PEP 585. Thus the link currently points to PEP 585 itself. - Please change the link for parameterized generics to reference the correct - documentation once documentation for PEP 585 becomes available. - - However, union objects containing `parameterized generics - `_ cannot be used:: + However, union objects containing :ref:`parameterized generics + ` cannot be used:: >>> isinstance(1, int | list[int]) Traceback (most recent call last): @@ -5033,20 +5028,16 @@ type hinting syntax compared to :data:`typing.Union`. >>> issubclass(bool, int | str) True - .. - Once again, please change the link below for parameterized generics to - reference the correct documentation once documentation for PEP 585 - becomes available. - - However, union objects containing `parameterized generics - `_ cannot be used:: + However, union objects containing :ref:`parameterized generics + ` cannot be used:: >>> issubclass(bool, bool | list[str]) Traceback (most recent call last): File "", line 1, in TypeError: issubclass() argument 2 cannot contain a parameterized generic -The type of a union object is :data:`types.Union`. An object cannot be +The user-exposed type for the union object can be accessed from +:data:`types.Union` and used for :func:`isinstance` checks. An object cannot be instantiated from the type:: >>> import types diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index f4d72908486dd..60dee0c6bd165 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -134,7 +134,7 @@ arguments of multiple types, :data:`typing.Union` was used:: return number ** 2 -Now, type hints can be written in a more succinct manner:: +Type hints can now be written in a more succinct manner:: def square(number: int | float) -> int | float: return number ** 2 From webhook-mailer at python.org Sat Oct 31 14:31:49 2020 From: webhook-mailer at python.org (lysnikolaou) Date: Sat, 31 Oct 2020 18:31:49 -0000 Subject: [Python-checkins] bpo-42218: Correctly handle errors in left-recursive rules (GH-23065) Message-ID: https://github.com/python/cpython/commit/02cdfc93f82fecdb7eae97a868d4ee222b9875d9 commit: 02cdfc93f82fecdb7eae97a868d4ee222b9875d9 branch: master author: Lysandros Nikolaou committer: lysnikolaou date: 2020-10-31T20:31:41+02:00 summary: bpo-42218: Correctly handle errors in left-recursive rules (GH-23065) Left-recursive rules need to check for errors explicitly, since even if the rule returns NULL, the parsing might continue and lead to long-distance failures. Co-authored-by: Pablo Galindo files: A Misc/NEWS.d/next/Core and Builtins/2020-10-31-17-50-23.bpo-42218.Dp_Z3v.rst M Lib/test/test_syntax.py M Parser/parser.c M Tools/peg_generator/pegen/c_generator.py diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index e89d9401f2c39..91ca1db43a74f 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -972,6 +972,14 @@ def func2(): """ self._check_error(code, "invalid syntax") + def test_invalid_line_continuation_left_recursive(self): + # Check bpo-42218: SyntaxErrors following left-recursive rules + # (t_primary_raw in this case) need to be tested explicitly + self._check_error("A.\u018a\\ ", + "unexpected character after line continuation character") + self._check_error("A.\u03bc\\\n", + "unexpected EOF while parsing") + def test_main(): support.run_unittest(SyntaxTestCase) from test import test_syntax diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-10-31-17-50-23.bpo-42218.Dp_Z3v.rst b/Misc/NEWS.d/next/Core and Builtins/2020-10-31-17-50-23.bpo-42218.Dp_Z3v.rst new file mode 100644 index 0000000000000..a38a310e4b45b --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2020-10-31-17-50-23.bpo-42218.Dp_Z3v.rst @@ -0,0 +1,3 @@ +Fixed a bug in the PEG parser that was causing crashes in debug mode. Now errors are checked +in left-recursive rules to avoid cases where such errors do not get handled in time and appear +as long-distance crashes in other places. diff --git a/Parser/parser.c b/Parser/parser.c index a882a81344cc6..48ebfe65aedaf 100644 --- a/Parser/parser.c +++ b/Parser/parser.c @@ -3461,6 +3461,8 @@ dotted_name_rule(Parser *p) } p->mark = _mark; void *_raw = dotted_name_raw(p); + if (p->error_indicator) + return NULL; if (_raw == NULL || p->mark <= _resmark) break; _resmark = p->mark; @@ -9045,6 +9047,8 @@ bitwise_or_rule(Parser *p) } p->mark = _mark; void *_raw = bitwise_or_raw(p); + if (p->error_indicator) + return NULL; if (_raw == NULL || p->mark <= _resmark) break; _resmark = p->mark; @@ -9159,6 +9163,8 @@ bitwise_xor_rule(Parser *p) } p->mark = _mark; void *_raw = bitwise_xor_raw(p); + if (p->error_indicator) + return NULL; if (_raw == NULL || p->mark <= _resmark) break; _resmark = p->mark; @@ -9273,6 +9279,8 @@ bitwise_and_rule(Parser *p) } p->mark = _mark; void *_raw = bitwise_and_raw(p); + if (p->error_indicator) + return NULL; if (_raw == NULL || p->mark <= _resmark) break; _resmark = p->mark; @@ -9387,6 +9395,8 @@ shift_expr_rule(Parser *p) } p->mark = _mark; void *_raw = shift_expr_raw(p); + if (p->error_indicator) + return NULL; if (_raw == NULL || p->mark <= _resmark) break; _resmark = p->mark; @@ -9540,6 +9550,8 @@ sum_rule(Parser *p) } p->mark = _mark; void *_raw = sum_raw(p); + if (p->error_indicator) + return NULL; if (_raw == NULL || p->mark <= _resmark) break; _resmark = p->mark; @@ -9699,6 +9711,8 @@ term_rule(Parser *p) } p->mark = _mark; void *_raw = term_raw(p); + if (p->error_indicator) + return NULL; if (_raw == NULL || p->mark <= _resmark) break; _resmark = p->mark; @@ -10303,6 +10317,8 @@ primary_rule(Parser *p) } p->mark = _mark; void *_raw = primary_raw(p); + if (p->error_indicator) + return NULL; if (_raw == NULL || p->mark <= _resmark) break; _resmark = p->mark; @@ -13943,6 +13959,8 @@ t_primary_rule(Parser *p) } p->mark = _mark; void *_raw = t_primary_raw(p); + if (p->error_indicator) + return NULL; if (_raw == NULL || p->mark <= _resmark) break; _resmark = p->mark; diff --git a/Tools/peg_generator/pegen/c_generator.py b/Tools/peg_generator/pegen/c_generator.py index 52bdb844e6bdd..6af0d3f7a2a14 100644 --- a/Tools/peg_generator/pegen/c_generator.py +++ b/Tools/peg_generator/pegen/c_generator.py @@ -502,6 +502,9 @@ def _set_up_rule_memoization(self, node: Rule, result_type: str) -> None: ) self.print("p->mark = _mark;") self.print(f"void *_raw = {node.name}_raw(p);") + self.print("if (p->error_indicator)") + with self.indent(): + self.print("return NULL;") self.print("if (_raw == NULL || p->mark <= _resmark)") with self.indent(): self.print("break;") From webhook-mailer at python.org Sat Oct 31 15:06:12 2020 From: webhook-mailer at python.org (miss-islington) Date: Sat, 31 Oct 2020 19:06:12 -0000 Subject: [Python-checkins] [3.9] bpo-42218: Correctly handle errors in left-recursive rules (GH-23065) (GH-23066) Message-ID: https://github.com/python/cpython/commit/cfcb952e30e01d7cce430829af8edc7afc94e0b1 commit: cfcb952e30e01d7cce430829af8edc7afc94e0b1 branch: 3.9 author: Lysandros Nikolaou committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2020-10-31T12:06:03-07:00 summary: [3.9] bpo-42218: Correctly handle errors in left-recursive rules (GH-23065) (GH-23066) Left-recursive rules need to check for errors explicitly, since even if the rule returns NULL, the parsing might continue and lead to long-distance failures. Co-authored-by: Pablo Galindo (cherry picked from commit 02cdfc93f82fecdb7eae97a868d4ee222b9875d9) Automerge-Triggered-By: GH:lysnikolaou files: A Misc/NEWS.d/next/Core and Builtins/2020-10-31-17-50-23.bpo-42218.Dp_Z3v.rst M Lib/test/test_syntax.py M Parser/pegen/parse.c M Tools/peg_generator/pegen/c_generator.py diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index a95992d869e5e..1336231fbbfbf 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -964,6 +964,14 @@ def func2(): """ self._check_error(code, "invalid syntax") + def test_invalid_line_continuation_left_recursive(self): + # Check bpo-42218: SyntaxErrors following left-recursive rules + # (t_primary_raw in this case) need to be tested explicitly + self._check_error("A.\u018a\\ ", + "unexpected character after line continuation character") + self._check_error("A.\u03bc\\\n", + "unexpected EOF while parsing") + def test_main(): support.run_unittest(SyntaxTestCase) from test import test_syntax diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-10-31-17-50-23.bpo-42218.Dp_Z3v.rst b/Misc/NEWS.d/next/Core and Builtins/2020-10-31-17-50-23.bpo-42218.Dp_Z3v.rst new file mode 100644 index 0000000000000..a38a310e4b45b --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2020-10-31-17-50-23.bpo-42218.Dp_Z3v.rst @@ -0,0 +1,3 @@ +Fixed a bug in the PEG parser that was causing crashes in debug mode. Now errors are checked +in left-recursive rules to avoid cases where such errors do not get handled in time and appear +as long-distance crashes in other places. diff --git a/Parser/pegen/parse.c b/Parser/pegen/parse.c index bae9463e274a3..97cefa9c2a3d4 100644 --- a/Parser/pegen/parse.c +++ b/Parser/pegen/parse.c @@ -3460,6 +3460,8 @@ dotted_name_rule(Parser *p) } p->mark = _mark; void *_raw = dotted_name_raw(p); + if (p->error_indicator) + return NULL; if (_raw == NULL || p->mark <= _resmark) break; _resmark = p->mark; @@ -9044,6 +9046,8 @@ bitwise_or_rule(Parser *p) } p->mark = _mark; void *_raw = bitwise_or_raw(p); + if (p->error_indicator) + return NULL; if (_raw == NULL || p->mark <= _resmark) break; _resmark = p->mark; @@ -9158,6 +9162,8 @@ bitwise_xor_rule(Parser *p) } p->mark = _mark; void *_raw = bitwise_xor_raw(p); + if (p->error_indicator) + return NULL; if (_raw == NULL || p->mark <= _resmark) break; _resmark = p->mark; @@ -9272,6 +9278,8 @@ bitwise_and_rule(Parser *p) } p->mark = _mark; void *_raw = bitwise_and_raw(p); + if (p->error_indicator) + return NULL; if (_raw == NULL || p->mark <= _resmark) break; _resmark = p->mark; @@ -9386,6 +9394,8 @@ shift_expr_rule(Parser *p) } p->mark = _mark; void *_raw = shift_expr_raw(p); + if (p->error_indicator) + return NULL; if (_raw == NULL || p->mark <= _resmark) break; _resmark = p->mark; @@ -9539,6 +9549,8 @@ sum_rule(Parser *p) } p->mark = _mark; void *_raw = sum_raw(p); + if (p->error_indicator) + return NULL; if (_raw == NULL || p->mark <= _resmark) break; _resmark = p->mark; @@ -9698,6 +9710,8 @@ term_rule(Parser *p) } p->mark = _mark; void *_raw = term_raw(p); + if (p->error_indicator) + return NULL; if (_raw == NULL || p->mark <= _resmark) break; _resmark = p->mark; @@ -10302,6 +10316,8 @@ primary_rule(Parser *p) } p->mark = _mark; void *_raw = primary_raw(p); + if (p->error_indicator) + return NULL; if (_raw == NULL || p->mark <= _resmark) break; _resmark = p->mark; @@ -13962,6 +13978,8 @@ t_primary_rule(Parser *p) } p->mark = _mark; void *_raw = t_primary_raw(p); + if (p->error_indicator) + return NULL; if (_raw == NULL || p->mark <= _resmark) break; _resmark = p->mark; diff --git a/Tools/peg_generator/pegen/c_generator.py b/Tools/peg_generator/pegen/c_generator.py index d0abc12b4026a..b4d6a0bab51f4 100644 --- a/Tools/peg_generator/pegen/c_generator.py +++ b/Tools/peg_generator/pegen/c_generator.py @@ -496,6 +496,9 @@ def _set_up_rule_memoization(self, node: Rule, result_type: str) -> None: ) self.print("p->mark = _mark;") self.print(f"void *_raw = {node.name}_raw(p);") + self.print("if (p->error_indicator)") + with self.indent(): + self.print("return NULL;") self.print("if (_raw == NULL || p->mark <= _resmark)") with self.indent(): self.print("break;") From webhook-mailer at python.org Sat Oct 31 16:25:53 2020 From: webhook-mailer at python.org (gvanrossum) Date: Sat, 31 Oct 2020 20:25:53 -0000 Subject: [Python-checkins] bpo-42198: New section in stdtypes for type annotation types (GH-23063) Message-ID: https://github.com/python/cpython/commit/3bf0d02f2817c48b6ee61a95b52a6d76ad543be9 commit: 3bf0d02f2817c48b6ee61a95b52a6d76ad543be9 branch: master author: kj <28750310+Fidget-Spinner at users.noreply.github.com> committer: gvanrossum date: 2020-10-31T13:25:44-07:00 summary: bpo-42198: New section in stdtypes for type annotation types (GH-23063) files: M Doc/library/stdtypes.rst diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 8a9cadde7d5dc..84d5a3a59f0b4 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -4764,10 +4764,20 @@ Compared to the overhead of setting up the runtime context, the overhead of a single class dictionary lookup is negligible. +Type Annotation Types --- :ref:`Generic Alias `, :ref:`Union ` +=============================================================================================== + +.. index:: + single: annotation; type annotation; type hint + +The core built-in types for :term:`type annotations ` are +:ref:`Generic Alias ` and :ref:`Union `. + + .. _types-genericalias: Generic Alias Type -================== +------------------ .. index:: object: GenericAlias @@ -4870,7 +4880,7 @@ in the ``GenericAlias`` object's :attr:`__args__ `. :: Standard Generic Collections ----------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ These standard library collections support parameterized generics. @@ -4915,7 +4925,7 @@ These standard library collections support parameterized generics. Special Attributes of Generic Alias ------------------------------------ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ All parameterized generics implement special read-only attributes. @@ -4961,7 +4971,7 @@ All parameterized generics implement special read-only attributes. .. _types-union: Union Type -========== +---------- .. index:: object: Union