From webhook-mailer at python.org Sun Sep 1 04:18:44 2019 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Sun, 01 Sep 2019 08:18:44 -0000 Subject: [Python-checkins] bpo-36543: Remove old-deprecated ElementTree features. (GH-12707) Message-ID: https://github.com/python/cpython/commit/f02ea6225bc3b71bd5fe66224d199a6e3e23b14d commit: f02ea6225bc3b71bd5fe66224d199a6e3e23b14d branch: master author: Serhiy Storchaka committer: GitHub date: 2019-09-01T11:18:35+03:00 summary: bpo-36543: Remove old-deprecated ElementTree features. (GH-12707) Remove methods Element.getchildren(), Element.getiterator() and ElementTree.getiterator() and the xml.etree.cElementTree module. files: A Misc/NEWS.d/next/Library/2019-04-06-20-08-12.bpo-36543.RPjmUz.rst M Doc/library/xml.etree.elementtree.rst M Doc/whatsnew/3.9.rst M Lib/test/test_xml_etree.py M Lib/xml/etree/ElementTree.py M Modules/_elementtree.c M Modules/clinic/_elementtree.c.h diff --git a/Doc/library/xml.etree.elementtree.rst b/Doc/library/xml.etree.elementtree.rst index 6047e6e29b92..1b4e2c9b142e 100644 --- a/Doc/library/xml.etree.elementtree.rst +++ b/Doc/library/xml.etree.elementtree.rst @@ -873,18 +873,6 @@ Element Objects in the expression into the given namespace. - .. method:: getchildren() - - .. deprecated-removed:: 3.2 3.9 - Use ``list(elem)`` or iteration. - - - .. method:: getiterator(tag=None) - - .. deprecated-removed:: 3.2 3.9 - Use method :meth:`Element.iter` instead. - - .. method:: insert(index, subelement) Inserts *subelement* at the given position in this element. Raises @@ -1019,12 +1007,6 @@ ElementTree Objects Same as :meth:`Element.findtext`, starting at the root of the tree. - .. method:: getiterator(tag=None) - - .. deprecated-removed:: 3.2 3.9 - Use method :meth:`ElementTree.iter` instead. - - .. method:: getroot() Returns the root element for this tree. diff --git a/Doc/whatsnew/3.9.rst b/Doc/whatsnew/3.9.rst index e20ae47462eb..0a0b91670ca5 100644 --- a/Doc/whatsnew/3.9.rst +++ b/Doc/whatsnew/3.9.rst @@ -207,6 +207,12 @@ Removed Use :meth:`~threading.Thread.is_alive()` instead. (Contributed by Dong-hee Na in :issue:`37804`.) +* Methods ``getchildren()`` and ``getiterator()`` in the + :mod:`~xml.etree.ElementTree` module have been removed. They were + deprecated in Python 3.2. Use functions :func:`list` and :func:`iter` + instead. The ``xml.etree.cElementTree`` module has been removed. + (Contributed by Serhiy Storchaka in :issue:`36543`.) + Porting to Python 3.9 ===================== diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py index db06aceb1461..87a6fa146462 100644 --- a/Lib/test/test_xml_etree.py +++ b/Lib/test/test_xml_etree.py @@ -242,7 +242,6 @@ def check_method(method): check_method(element.extend) check_method(element.insert) check_method(element.remove) - check_method(element.getchildren) check_method(element.find) check_method(element.iterfind) check_method(element.findall) @@ -254,7 +253,6 @@ def check_method(method): check_method(element.items) check_method(element.iter) check_method(element.itertext) - check_method(element.getiterator) # These methods return an iterable. See bug 6472. @@ -741,24 +739,20 @@ def end_ns(self, prefix): ('end-ns', ''), ]) - # Element.getchildren() and ElementTree.getiterator() are deprecated. - @checkwarnings(("This method will be removed in future versions. " - "Use .+ instead.", - DeprecationWarning)) - def test_getchildren(self): - # Test Element.getchildren() + def test_children(self): + # Test Element children iteration with open(SIMPLE_XMLFILE, "rb") as f: tree = ET.parse(f) - self.assertEqual([summarize_list(elem.getchildren()) + self.assertEqual([summarize_list(elem) for elem in tree.getroot().iter()], [ ['element', 'element', 'empty-element'], [], [], [], ]) - self.assertEqual([summarize_list(elem.getchildren()) - for elem in tree.getiterator()], [ + self.assertEqual([summarize_list(elem) + for elem in tree.iter()], [ ['element', 'element', 'empty-element'], [], [], @@ -766,13 +760,13 @@ def test_getchildren(self): ]) elem = ET.XML(SAMPLE_XML) - self.assertEqual(len(elem.getchildren()), 3) - self.assertEqual(len(elem[2].getchildren()), 1) - self.assertEqual(elem[:], elem.getchildren()) + self.assertEqual(len(list(elem)), 3) + self.assertEqual(len(list(elem[2])), 1) + self.assertEqual(elem[:], list(elem)) child1 = elem[0] child2 = elem[2] del elem[1:2] - self.assertEqual(len(elem.getchildren()), 2) + self.assertEqual(len(list(elem)), 2) self.assertEqual(child1, elem[0]) self.assertEqual(child2, elem[1]) elem[0:2] = [child2, child1] @@ -780,7 +774,7 @@ def test_getchildren(self): self.assertEqual(child1, elem[1]) self.assertNotEqual(child1, elem[0]) elem.clear() - self.assertEqual(elem.getchildren(), []) + self.assertEqual(list(elem), []) def test_writestring(self): elem = ET.XML("text") @@ -2955,40 +2949,6 @@ def test_iter_by_tag(self): self.assertEqual(self._ilist(doc), all_tags) self.assertEqual(self._ilist(doc, '*'), all_tags) - # Element.getiterator() is deprecated. - @checkwarnings(("This method will be removed in future versions. " - "Use .+ instead.", DeprecationWarning)) - def test_getiterator(self): - doc = ET.XML(''' - - - bedroom1 - bedroom2 - - nothing here - - - bedroom8 - - ''') - - self.assertEqual(summarize_list(doc.getiterator('room')), - ['room'] * 3) - self.assertEqual(summarize_list(doc.getiterator('house')), - ['house'] * 2) - - # test that getiterator also accepts 'tag' as a keyword arg - self.assertEqual( - summarize_list(doc.getiterator(tag='room')), - ['room'] * 3) - - # make sure both tag=None and tag='*' return all tags - all_tags = ['document', 'house', 'room', 'room', - 'shed', 'house', 'room'] - self.assertEqual(summarize_list(doc.getiterator()), all_tags) - self.assertEqual(summarize_list(doc.getiterator(None)), all_tags) - self.assertEqual(summarize_list(doc.getiterator('*')), all_tags) - def test_copy(self): a = ET.Element('a') it = a.iter() diff --git a/Lib/xml/etree/ElementTree.py b/Lib/xml/etree/ElementTree.py index 431ecd0dddf1..beb2d68803a5 100644 --- a/Lib/xml/etree/ElementTree.py +++ b/Lib/xml/etree/ElementTree.py @@ -273,19 +273,6 @@ def remove(self, subelement): # assert iselement(element) self._children.remove(subelement) - def getchildren(self): - """(Deprecated) Return all subelements. - - Elements are returned in document order. - - """ - warnings.warn( - "This method will be removed in future versions. " - "Use 'list(elem)' or iteration over elem instead.", - DeprecationWarning, stacklevel=2 - ) - return self._children - def find(self, path, namespaces=None): """Find first matching element by tag name or path. @@ -409,15 +396,6 @@ def iter(self, tag=None): for e in self._children: yield from e.iter(tag) - # compatibility - def getiterator(self, tag=None): - warnings.warn( - "This method will be removed in future versions. " - "Use 'elem.iter()' or 'list(elem.iter())' instead.", - DeprecationWarning, stacklevel=2 - ) - return list(self.iter(tag)) - def itertext(self): """Create text iterator. @@ -617,15 +595,6 @@ def iter(self, tag=None): # assert self._root is not None return self._root.iter(tag) - # compatibility - def getiterator(self, tag=None): - warnings.warn( - "This method will be removed in future versions. " - "Use 'tree.iter()' or 'list(tree.iter())' instead.", - DeprecationWarning, stacklevel=2 - ) - return list(self.iter(tag)) - def find(self, path, namespaces=None): """Find first matching element by tag name or path. diff --git a/Misc/NEWS.d/next/Library/2019-04-06-20-08-12.bpo-36543.RPjmUz.rst b/Misc/NEWS.d/next/Library/2019-04-06-20-08-12.bpo-36543.RPjmUz.rst new file mode 100644 index 000000000000..1153de872bc7 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-04-06-20-08-12.bpo-36543.RPjmUz.rst @@ -0,0 +1,2 @@ +Removed methods Element.getchildren(), Element.getiterator() and +ElementTree.getiterator() and the xml.etree.cElementTree module. diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c index 830ce8635eae..b88e8a1d6e9e 100644 --- a/Modules/_elementtree.c +++ b/Modules/_elementtree.c @@ -1426,42 +1426,6 @@ _elementtree_Element_get_impl(ElementObject *self, PyObject *key, return value; } -/*[clinic input] -_elementtree.Element.getchildren - -[clinic start generated code]*/ - -static PyObject * -_elementtree_Element_getchildren_impl(ElementObject *self) -/*[clinic end generated code: output=e50ffe118637b14f input=0f754dfded150d5f]*/ -{ - Py_ssize_t i; - PyObject* list; - - if (PyErr_WarnEx(PyExc_DeprecationWarning, - "This method will be removed in future versions. " - "Use 'list(elem)' or iteration over elem instead.", - 1) < 0) { - return NULL; - } - - if (!self->extra) - return PyList_New(0); - - list = PyList_New(self->extra->length); - if (!list) - return NULL; - - for (i = 0; i < self->extra->length; i++) { - PyObject* item = self->extra->children[i]; - Py_INCREF(item); - PyList_SET_ITEM(list, i, item); - } - - return list; -} - - static PyObject * create_elementiter(ElementObject *self, PyObject *tag, int gettext); @@ -1492,27 +1456,6 @@ _elementtree_Element_iter_impl(ElementObject *self, PyObject *tag) } -/*[clinic input] -_elementtree.Element.getiterator - - tag: object = None - -[clinic start generated code]*/ - -static PyObject * -_elementtree_Element_getiterator_impl(ElementObject *self, PyObject *tag) -/*[clinic end generated code: output=cb69ff4a3742dfa1 input=500da1a03f7b9e28]*/ -{ - if (PyErr_WarnEx(PyExc_DeprecationWarning, - "This method will be removed in future versions. " - "Use 'tree.iter()' or 'list(tree.iter())' instead.", - 1) < 0) { - return NULL; - } - return _elementtree_Element_iter_impl(self, tag); -} - - /*[clinic input] _elementtree.Element.itertext @@ -4220,9 +4163,6 @@ static PyMethodDef element_methods[] = { _ELEMENTTREE_ELEMENT_ITERTEXT_METHODDEF _ELEMENTTREE_ELEMENT_ITERFIND_METHODDEF - _ELEMENTTREE_ELEMENT_GETITERATOR_METHODDEF - _ELEMENTTREE_ELEMENT_GETCHILDREN_METHODDEF - _ELEMENTTREE_ELEMENT_ITEMS_METHODDEF _ELEMENTTREE_ELEMENT_KEYS_METHODDEF diff --git a/Modules/clinic/_elementtree.c.h b/Modules/clinic/_elementtree.c.h index 324e549d486a..a58ad737f854 100644 --- a/Modules/clinic/_elementtree.c.h +++ b/Modules/clinic/_elementtree.c.h @@ -355,23 +355,6 @@ _elementtree_Element_get(ElementObject *self, PyObject *const *args, Py_ssize_t return return_value; } -PyDoc_STRVAR(_elementtree_Element_getchildren__doc__, -"getchildren($self, /)\n" -"--\n" -"\n"); - -#define _ELEMENTTREE_ELEMENT_GETCHILDREN_METHODDEF \ - {"getchildren", (PyCFunction)_elementtree_Element_getchildren, METH_NOARGS, _elementtree_Element_getchildren__doc__}, - -static PyObject * -_elementtree_Element_getchildren_impl(ElementObject *self); - -static PyObject * -_elementtree_Element_getchildren(ElementObject *self, PyObject *Py_UNUSED(ignored)) -{ - return _elementtree_Element_getchildren_impl(self); -} - PyDoc_STRVAR(_elementtree_Element_iter__doc__, "iter($self, /, tag=None)\n" "--\n" @@ -408,42 +391,6 @@ _elementtree_Element_iter(ElementObject *self, PyObject *const *args, Py_ssize_t return return_value; } -PyDoc_STRVAR(_elementtree_Element_getiterator__doc__, -"getiterator($self, /, tag=None)\n" -"--\n" -"\n"); - -#define _ELEMENTTREE_ELEMENT_GETITERATOR_METHODDEF \ - {"getiterator", (PyCFunction)(void(*)(void))_elementtree_Element_getiterator, METH_FASTCALL|METH_KEYWORDS, _elementtree_Element_getiterator__doc__}, - -static PyObject * -_elementtree_Element_getiterator_impl(ElementObject *self, PyObject *tag); - -static PyObject * -_elementtree_Element_getiterator(ElementObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) -{ - PyObject *return_value = NULL; - static const char * const _keywords[] = {"tag", NULL}; - static _PyArg_Parser _parser = {NULL, _keywords, "getiterator", 0}; - PyObject *argsbuf[1]; - Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; - PyObject *tag = Py_None; - - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); - if (!args) { - goto exit; - } - if (!noptargs) { - goto skip_optional_pos; - } - tag = args[0]; -skip_optional_pos: - return_value = _elementtree_Element_getiterator_impl(self, tag); - -exit: - return return_value; -} - PyDoc_STRVAR(_elementtree_Element_itertext__doc__, "itertext($self, /)\n" "--\n" @@ -969,4 +916,4 @@ _elementtree_XMLParser__setevents(XMLParserObject *self, PyObject *const *args, exit: return return_value; } -/*[clinic end generated code: output=50e0b1954c5f9e0f input=a9049054013a1b77]*/ +/*[clinic end generated code: output=f5dbf9b4a095d310 input=a9049054013a1b77]*/ From webhook-mailer at python.org Sun Sep 1 05:03:43 2019 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Sun, 01 Sep 2019 09:03:43 -0000 Subject: [Python-checkins] bpo-37994: Fix silencing all errors if an attribute lookup fails. (GH-15630) Message-ID: https://github.com/python/cpython/commit/41c57b335330ff48af098d47e379e0f9ba09d233 commit: 41c57b335330ff48af098d47e379e0f9ba09d233 branch: master author: Serhiy Storchaka committer: GitHub date: 2019-09-01T12:03:39+03:00 summary: bpo-37994: Fix silencing all errors if an attribute lookup fails. (GH-15630) Only AttributeError should be silenced. files: A Misc/NEWS.d/next/Core and Builtins/2019-08-31-11-13-25.bpo-37994.Rj6S4j.rst M Modules/_csv.c M Modules/_datetimemodule.c M Modules/_pickle.c M Modules/_threadmodule.c M Modules/pyexpat.c M Objects/bytearrayobject.c M Objects/descrobject.c M Objects/fileobject.c M Objects/setobject.c M Objects/typeobject.c M Objects/weakrefobject.c M Python/bltinmodule.c M Python/sysmodule.c diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-08-31-11-13-25.bpo-37994.Rj6S4j.rst b/Misc/NEWS.d/next/Core and Builtins/2019-08-31-11-13-25.bpo-37994.Rj6S4j.rst new file mode 100644 index 000000000000..103ac5a30905 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2019-08-31-11-13-25.bpo-37994.Rj6S4j.rst @@ -0,0 +1,2 @@ +Fixed silencing arbitrary errors if an attribute lookup fails in several +sites. Only AttributeError should be silenced. diff --git a/Modules/_csv.c b/Modules/_csv.c index 9653ff92ee9a..aaf377650c0b 100644 --- a/Modules/_csv.c +++ b/Modules/_csv.c @@ -1382,7 +1382,10 @@ csv_writer(PyObject *module, PyObject *args, PyObject *keyword_args) Py_DECREF(self); return NULL; } - self->write = _PyObject_GetAttrId(output_file, &PyId_write); + if (_PyObject_LookupAttrId(output_file, &PyId_write, &self->write) < 0) { + Py_DECREF(self); + return NULL; + } if (self->write == NULL || !PyCallable_Check(self->write)) { PyErr_SetString(PyExc_TypeError, "argument 1 must have a \"write\" method"); diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 8f4fa21da141..c1b24073436e 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -3607,24 +3607,24 @@ tzinfo_reduce(PyObject *self, PyObject *Py_UNUSED(ignored)) _Py_IDENTIFIER(__getinitargs__); _Py_IDENTIFIER(__getstate__); - getinitargs = _PyObject_GetAttrId(self, &PyId___getinitargs__); + if (_PyObject_LookupAttrId(self, &PyId___getinitargs__, &getinitargs) < 0) { + return NULL; + } if (getinitargs != NULL) { args = PyObject_CallNoArgs(getinitargs); Py_DECREF(getinitargs); - if (args == NULL) { - return NULL; - } } else { - PyErr_Clear(); - args = PyTuple_New(0); - if (args == NULL) { - return NULL; - } + } + if (args == NULL) { + return NULL; } - getstate = _PyObject_GetAttrId(self, &PyId___getstate__); + if (_PyObject_LookupAttrId(self, &PyId___getstate__, &getstate) < 0) { + Py_DECREF(args); + return NULL; + } if (getstate != NULL) { state = PyObject_CallNoArgs(getstate); Py_DECREF(getstate); @@ -3635,7 +3635,6 @@ tzinfo_reduce(PyObject *self, PyObject *Py_UNUSED(ignored)) } else { PyObject **dictptr; - PyErr_Clear(); state = Py_None; dictptr = _PyObject_GetDictPtr(self); if (dictptr && *dictptr && PyDict_GET_SIZE(*dictptr)) { diff --git a/Modules/_pickle.c b/Modules/_pickle.c index 0c53f2e18685..9e82d145cc66 100644 --- a/Modules/_pickle.c +++ b/Modules/_pickle.c @@ -4383,7 +4383,6 @@ save(PicklerObject *self, PyObject *obj, int pers_save) _Py_IDENTIFIER(__reduce__); _Py_IDENTIFIER(__reduce_ex__); - /* XXX: If the __reduce__ method is defined, __reduce_ex__ is automatically defined as __reduce__. While this is convenient, this make it impossible to know which method was actually called. Of @@ -4404,14 +4403,15 @@ save(PicklerObject *self, PyObject *obj, int pers_save) } } else { - PickleState *st = _Pickle_GetGlobalState(); - /* Check for a __reduce__ method. */ - reduce_func = _PyObject_GetAttrId(obj, &PyId___reduce__); + if (_PyObject_LookupAttrId(obj, &PyId___reduce__, &reduce_func) < 0) { + goto error; + } if (reduce_func != NULL) { reduce_value = PyObject_CallNoArgs(reduce_func); } else { + PickleState *st = _Pickle_GetGlobalState(); PyErr_Format(st->PicklingError, "can't pickle '%.200s' object: %R", type->tp_name, obj); @@ -6446,7 +6446,9 @@ do_append(UnpicklerObject *self, Py_ssize_t x) PyObject *extend_func; _Py_IDENTIFIER(extend); - extend_func = _PyObject_GetAttrId(list, &PyId_extend); + if (_PyObject_LookupAttrId(list, &PyId_extend, &extend_func) < 0) { + return -1; + } if (extend_func != NULL) { slice = Pdata_poplist(self->stack, x); if (!slice) { @@ -6466,7 +6468,6 @@ do_append(UnpicklerObject *self, Py_ssize_t x) /* Even if the PEP 307 requires extend() and append() methods, fall back on append() if the object has no extend() method for backward compatibility. */ - PyErr_Clear(); append_func = _PyObject_GetAttrId(list, &PyId_append); if (append_func == NULL) return -1; diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c index 6665827fe2ba..a3ecd2e74e8c 100644 --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -1313,6 +1313,7 @@ static int thread_excepthook_file(PyObject *file, PyObject *exc_type, PyObject *exc_value, PyObject *exc_traceback, PyObject *thread) { + _Py_IDENTIFIER(name); /* print(f"Exception in thread {thread.name}:", file=file) */ if (PyFile_WriteString("Exception in thread ", file) < 0) { return -1; @@ -1320,7 +1321,9 @@ thread_excepthook_file(PyObject *file, PyObject *exc_type, PyObject *exc_value, PyObject *name = NULL; if (thread != Py_None) { - name = PyObject_GetAttrString(thread, "name"); + if (_PyObject_LookupAttrId(thread, &PyId_name, &name) < 0) { + return -1; + } } if (name != NULL) { if (PyFile_WriteObject(name, file, Py_PRINT_RAW) < 0) { @@ -1330,8 +1333,6 @@ thread_excepthook_file(PyObject *file, PyObject *exc_type, PyObject *exc_value, Py_DECREF(name); } else { - PyErr_Clear(); - unsigned long ident = PyThread_get_thread_ident(); PyObject *str = PyUnicode_FromFormat("%lu", ident); if (str != NULL) { diff --git a/Modules/pyexpat.c b/Modules/pyexpat.c index b0096e664780..5d284cddcfc7 100644 --- a/Modules/pyexpat.c +++ b/Modules/pyexpat.c @@ -810,7 +810,9 @@ pyexpat_xmlparser_ParseFile(xmlparseobject *self, PyObject *file) PyObject *readmethod = NULL; _Py_IDENTIFIER(read); - readmethod = _PyObject_GetAttrId(file, &PyId_read); + if (_PyObject_LookupAttrId(file, &PyId_read, &readmethod) < 0) { + return NULL; + } if (readmethod == NULL) { PyErr_SetString(PyExc_TypeError, "argument must have 'read' attribute"); diff --git a/Objects/bytearrayobject.c b/Objects/bytearrayobject.c index 9dd67127b614..8c16cc630d71 100644 --- a/Objects/bytearrayobject.c +++ b/Objects/bytearrayobject.c @@ -2061,9 +2061,10 @@ _common_reduce(PyByteArrayObject *self, int proto) _Py_IDENTIFIER(__dict__); char *buf; - dict = _PyObject_GetAttrId((PyObject *)self, &PyId___dict__); + if (_PyObject_LookupAttrId((PyObject *)self, &PyId___dict__, &dict) < 0) { + return NULL; + } if (dict == NULL) { - PyErr_Clear(); dict = Py_None; Py_INCREF(dict); } diff --git a/Objects/descrobject.c b/Objects/descrobject.c index 9e1b281c4603..c50fe00ce80b 100644 --- a/Objects/descrobject.c +++ b/Objects/descrobject.c @@ -1616,29 +1616,25 @@ property_init_impl(propertyobject *self, PyObject *fget, PyObject *fset, /* if no docstring given and the getter has one, use that one */ if ((doc == NULL || doc == Py_None) && fget != NULL) { _Py_IDENTIFIER(__doc__); - PyObject *get_doc = _PyObject_GetAttrId(fget, &PyId___doc__); - if (get_doc) { - if (Py_TYPE(self) == &PyProperty_Type) { - Py_XSETREF(self->prop_doc, get_doc); - } - else { - /* If this is a property subclass, put __doc__ - in dict of the subclass instance instead, - otherwise it gets shadowed by __doc__ in the - class's dict. */ - int err = _PyObject_SetAttrId((PyObject *)self, &PyId___doc__, get_doc); - Py_DECREF(get_doc); - if (err < 0) - return -1; - } - self->getter_doc = 1; + PyObject *get_doc; + int rc = _PyObject_LookupAttrId(fget, &PyId___doc__, &get_doc); + if (rc <= 0) { + return rc; } - else if (PyErr_ExceptionMatches(PyExc_Exception)) { - PyErr_Clear(); + if (Py_TYPE(self) == &PyProperty_Type) { + Py_XSETREF(self->prop_doc, get_doc); } else { - return -1; + /* If this is a property subclass, put __doc__ + in dict of the subclass instance instead, + otherwise it gets shadowed by __doc__ in the + class's dict. */ + int err = _PyObject_SetAttrId((PyObject *)self, &PyId___doc__, get_doc); + Py_DECREF(get_doc); + if (err < 0) + return -1; } + self->getter_doc = 1; } return 0; diff --git a/Objects/fileobject.c b/Objects/fileobject.c index 0faf7e70b5d8..61c94280937b 100644 --- a/Objects/fileobject.c +++ b/Objects/fileobject.c @@ -185,8 +185,10 @@ PyObject_AsFileDescriptor(PyObject *o) if (PyLong_Check(o)) { fd = _PyLong_AsInt(o); } - else if ((meth = _PyObject_GetAttrId(o, &PyId_fileno)) != NULL) - { + else if (_PyObject_LookupAttrId(o, &PyId_fileno, &meth) < 0) { + return -1; + } + else if (meth != NULL) { PyObject *fno = _PyObject_CallNoArg(meth); Py_DECREF(meth); if (fno == NULL) diff --git a/Objects/setobject.c b/Objects/setobject.c index fafc2fa9e46d..924885d7505a 100644 --- a/Objects/setobject.c +++ b/Objects/setobject.c @@ -1970,9 +1970,10 @@ set_reduce(PySetObject *so, PyObject *Py_UNUSED(ignored)) args = PyTuple_Pack(1, keys); if (args == NULL) goto done; - dict = _PyObject_GetAttrId((PyObject *)so, &PyId___dict__); + if (_PyObject_LookupAttrId((PyObject *)so, &PyId___dict__, &dict) < 0) { + goto done; + } if (dict == NULL) { - PyErr_Clear(); dict = Py_None; Py_INCREF(dict); } diff --git a/Objects/typeobject.c b/Objects/typeobject.c index c816c3b6df23..1d8216d29887 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -1551,17 +1551,10 @@ tail_contains(PyObject *tuple, int whence, PyObject *o) static PyObject * class_name(PyObject *cls) { - PyObject *name = _PyObject_GetAttrId(cls, &PyId___name__); - if (name == NULL) { - PyErr_Clear(); + PyObject *name; + if (_PyObject_LookupAttrId(cls, &PyId___name__, &name) == 0) { name = PyObject_Repr(cls); } - if (name == NULL) - return NULL; - if (!PyUnicode_Check(name)) { - Py_DECREF(name); - return NULL; - } return name; } @@ -1579,13 +1572,15 @@ check_duplicates(PyObject *tuple) if (PyTuple_GET_ITEM(tuple, j) == o) { o = class_name(o); if (o != NULL) { - PyErr_Format(PyExc_TypeError, - "duplicate base class %U", - o); + if (PyUnicode_Check(o)) { + PyErr_Format(PyExc_TypeError, + "duplicate base class %U", o); + } + else { + PyErr_SetString(PyExc_TypeError, + "duplicate base class"); + } Py_DECREF(o); - } else { - PyErr_SetString(PyExc_TypeError, - "duplicate base class"); } return -1; } @@ -1629,13 +1624,20 @@ consistent method resolution\norder (MRO) for bases"); i = 0; while (PyDict_Next(set, &i, &k, &v) && (size_t)off < sizeof(buf)) { PyObject *name = class_name(k); - const char *name_str; + const char *name_str = NULL; if (name != NULL) { - name_str = PyUnicode_AsUTF8(name); - if (name_str == NULL) + if (PyUnicode_Check(name)) { + name_str = PyUnicode_AsUTF8(name); + } + else { name_str = "?"; - } else - name_str = "?"; + } + } + if (name_str == NULL) { + Py_XDECREF(name); + Py_DECREF(set); + return; + } off += PyOS_snprintf(buf + off, sizeof(buf) - off, " %s", name_str); Py_XDECREF(name); if (--n && (size_t)(off+1) < sizeof(buf)) { @@ -3422,10 +3424,10 @@ merge_class_dict(PyObject *dict, PyObject *aclass) assert(aclass); /* Merge in the type's dict (if any). */ - classdict = _PyObject_GetAttrId(aclass, &PyId___dict__); - if (classdict == NULL) - PyErr_Clear(); - else { + if (_PyObject_LookupAttrId(aclass, &PyId___dict__, &classdict) < 0) { + return -1; + } + if (classdict != NULL) { int status = PyDict_Update(dict, classdict); Py_DECREF(classdict); if (status < 0) @@ -3433,15 +3435,17 @@ merge_class_dict(PyObject *dict, PyObject *aclass) } /* Recursively merge in the base types' (if any) dicts. */ - bases = _PyObject_GetAttrId(aclass, &PyId___bases__); - if (bases == NULL) - PyErr_Clear(); - else { + if (_PyObject_LookupAttrId(aclass, &PyId___bases__, &bases) < 0) { + return -1; + } + if (bases != NULL) { /* We have no guarantee that bases is a real tuple */ Py_ssize_t i, n; n = PySequence_Size(bases); /* This better be right */ - if (n < 0) - PyErr_Clear(); + if (n < 0) { + Py_DECREF(bases); + return -1; + } else { for (i = 0; i < n; i++) { int status; @@ -4730,9 +4734,10 @@ object___dir___impl(PyObject *self) PyObject *itsclass = NULL; /* Get __dict__ (which may or may not be a real dict...) */ - dict = _PyObject_GetAttrId(self, &PyId___dict__); + if (_PyObject_LookupAttrId(self, &PyId___dict__, &dict) < 0) { + return NULL; + } if (dict == NULL) { - PyErr_Clear(); dict = PyDict_New(); } else if (!PyDict_Check(dict)) { @@ -4750,12 +4755,12 @@ object___dir___impl(PyObject *self) goto error; /* Merge in attrs reachable from its class. */ - itsclass = _PyObject_GetAttrId(self, &PyId___class__); - if (itsclass == NULL) - /* XXX(tomer): Perhaps fall back to obj->ob_type if no - __class__ exists? */ - PyErr_Clear(); - else if (merge_class_dict(dict, itsclass) != 0) + if (_PyObject_LookupAttrId(self, &PyId___class__, &itsclass) < 0) { + goto error; + } + /* XXX(tomer): Perhaps fall back to obj->ob_type if no + __class__ exists? */ + if (itsclass != NULL && merge_class_dict(dict, itsclass) < 0) goto error; result = PyDict_Keys(dict); @@ -6111,16 +6116,19 @@ method_is_overloaded(PyObject *left, PyObject *right, struct _Py_Identifier *nam PyObject *a, *b; int ok; - b = _PyObject_GetAttrId((PyObject *)(Py_TYPE(right)), name); + if (_PyObject_LookupAttrId((PyObject *)(Py_TYPE(right)), name, &b) < 0) { + return -1; + } if (b == NULL) { - PyErr_Clear(); /* If right doesn't have it, it's not overloaded */ return 0; } - a = _PyObject_GetAttrId((PyObject *)(Py_TYPE(left)), name); + if (_PyObject_LookupAttrId((PyObject *)(Py_TYPE(left)), name, &a) < 0) { + Py_DECREF(b); + return -1; + } if (a == NULL) { - PyErr_Clear(); Py_DECREF(b); /* If right has it but left doesn't, it's overloaded */ return 1; @@ -6129,11 +6137,6 @@ method_is_overloaded(PyObject *left, PyObject *right, struct _Py_Identifier *nam ok = PyObject_RichCompareBool(a, b, Py_NE); Py_DECREF(a); Py_DECREF(b); - if (ok < 0) { - PyErr_Clear(); - return 0; - } - return ok; } @@ -6151,16 +6154,20 @@ FUNCNAME(PyObject *self, PyObject *other) \ if (Py_TYPE(self)->tp_as_number != NULL && \ Py_TYPE(self)->tp_as_number->SLOTNAME == TESTFUNC) { \ PyObject *r; \ - if (do_other && \ - PyType_IsSubtype(Py_TYPE(other), Py_TYPE(self)) && \ - method_is_overloaded(self, other, &rop_id)) { \ - stack[0] = other; \ - stack[1] = self; \ - r = vectorcall_maybe(&rop_id, stack, 2); \ - if (r != Py_NotImplemented) \ - return r; \ - Py_DECREF(r); \ - do_other = 0; \ + if (do_other && PyType_IsSubtype(Py_TYPE(other), Py_TYPE(self))) { \ + int ok = method_is_overloaded(self, other, &rop_id); \ + if (ok < 0) { \ + return NULL; \ + } \ + if (ok) { \ + stack[0] = other; \ + stack[1] = self; \ + r = vectorcall_maybe(&rop_id, stack, 2); \ + if (r != Py_NotImplemented) \ + return r; \ + Py_DECREF(r); \ + do_other = 0; \ + } \ } \ stack[0] = self; \ stack[1] = other; \ @@ -7753,7 +7760,9 @@ supercheck(PyTypeObject *type, PyObject *obj) /* Try the slow way */ PyObject *class_attr; - class_attr = _PyObject_GetAttrId(obj, &PyId___class__); + if (_PyObject_LookupAttrId(obj, &PyId___class__, &class_attr) < 0) { + return NULL; + } if (class_attr != NULL && PyType_Check(class_attr) && (PyTypeObject *)class_attr != Py_TYPE(obj)) @@ -7763,11 +7772,7 @@ supercheck(PyTypeObject *type, PyObject *obj) if (ok) return (PyTypeObject *)class_attr; } - - if (class_attr == NULL) - PyErr_Clear(); - else - Py_DECREF(class_attr); + Py_XDECREF(class_attr); } PyErr_SetString(PyExc_TypeError, diff --git a/Objects/weakrefobject.c b/Objects/weakrefobject.c index e8a429ab5b54..daee476444a4 100644 --- a/Objects/weakrefobject.c +++ b/Objects/weakrefobject.c @@ -163,10 +163,10 @@ weakref_repr(PyWeakReference *self) if (PyWeakref_GET_OBJECT(self) == Py_None) return PyUnicode_FromFormat("", self); - name = _PyObject_GetAttrId(PyWeakref_GET_OBJECT(self), &PyId___name__); + if (_PyObject_LookupAttrId(PyWeakref_GET_OBJECT(self), &PyId___name__, &name) < 0) { + return NULL; + } if (name == NULL || !PyUnicode_Check(name)) { - if (name == NULL) - PyErr_Clear(); repr = PyUnicode_FromFormat( "", self, diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 63e58128651a..5053f7a1748f 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -2255,16 +2255,12 @@ builtin_vars(PyObject *self, PyObject *args) return NULL; if (v == NULL) { d = PyEval_GetLocals(); - if (d == NULL) - return NULL; - Py_INCREF(d); + Py_XINCREF(d); } else { - d = _PyObject_GetAttrId(v, &PyId___dict__); - if (d == NULL) { + if (_PyObject_LookupAttrId(v, &PyId___dict__, &d) == 0) { PyErr_SetString(PyExc_TypeError, "vars() argument must have __dict__ attribute"); - return NULL; } } return d; diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 0635e9d834cd..8509aaf86693 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -224,16 +224,12 @@ PySys_Audit(const char *event, const char *argFormat, ...) ts->tracing++; ts->use_tracing = 0; while ((hook = PyIter_Next(hooks)) != NULL) { + _Py_IDENTIFIER(__cantrace__); PyObject *o; - int canTrace = -1; - o = PyObject_GetAttrString(hook, "__cantrace__"); + int canTrace = _PyObject_LookupAttrId(hook, &PyId___cantrace__, &o); if (o) { canTrace = PyObject_IsTrue(o); Py_DECREF(o); - } else if (_PyErr_Occurred(ts) && - _PyErr_ExceptionMatches(ts, PyExc_AttributeError)) { - _PyErr_Clear(ts); - canTrace = 0; } if (canTrace < 0) { break; @@ -579,7 +575,10 @@ sys_displayhook_unencodable(PyThreadState *tstate, PyObject *outf, PyObject *o) if (encoded == NULL) goto error; - buffer = _PyObject_GetAttrId(outf, &PyId_buffer); + if (_PyObject_LookupAttrId(outf, &PyId_buffer, &buffer) < 0) { + Py_DECREF(encoded); + goto error; + } if (buffer) { result = _PyObject_CallMethodIdOneArg(buffer, &PyId_write, encoded); Py_DECREF(buffer); @@ -589,7 +588,6 @@ sys_displayhook_unencodable(PyThreadState *tstate, PyObject *outf, PyObject *o) Py_DECREF(result); } else { - _PyErr_Clear(tstate); escaped_str = PyUnicode_FromEncodedObject(encoded, stdout_encoding_str, "strict"); From webhook-mailer at python.org Sun Sep 1 05:11:47 2019 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Sun, 01 Sep 2019 09:11:47 -0000 Subject: [Python-checkins] bpo-15999: Always pass bool instead of int to the expat parser. (GH-15622) Message-ID: https://github.com/python/cpython/commit/eb8974616bc58f44b2a3c3e4ca2326894ae42c8f commit: eb8974616bc58f44b2a3c3e4ca2326894ae42c8f branch: master author: Serhiy Storchaka committer: GitHub date: 2019-09-01T12:11:43+03:00 summary: bpo-15999: Always pass bool instead of int to the expat parser. (GH-15622) files: M Lib/test/test_pyexpat.py M Lib/xml/dom/expatbuilder.py M Lib/xml/etree/ElementTree.py M Lib/xml/sax/expatreader.py M Lib/xmlrpc/client.py diff --git a/Lib/test/test_pyexpat.py b/Lib/test/test_pyexpat.py index a0ba59008ece..b2b4dea06053 100644 --- a/Lib/test/test_pyexpat.py +++ b/Lib/test/test_pyexpat.py @@ -231,7 +231,7 @@ def test_parse_bytes(self): parser = expat.ParserCreate(namespace_separator='!') self._hookup_callbacks(parser, out) - parser.Parse(data, 1) + parser.Parse(data, True) operations = out.out self._verify_parse_output(operations) @@ -243,7 +243,7 @@ def test_parse_str(self): parser = expat.ParserCreate(namespace_separator='!') self._hookup_callbacks(parser, out) - parser.Parse(data.decode('iso-8859-1'), 1) + parser.Parse(data.decode('iso-8859-1'), True) operations = out.out self._verify_parse_output(operations) @@ -316,7 +316,7 @@ def collector(name, *args): L.append(name) p.StartElementHandler = collector p.EndElementHandler = collector - p.Parse(b" ", 1) + p.Parse(b" ", True) tag = L[0] self.assertEqual(len(L), 6) for entry in L: @@ -332,14 +332,14 @@ def __init__(self, parser): def ExternalEntityRefHandler(self, context, base, sysId, pubId): external_parser = self.parser.ExternalEntityParserCreate("") - self.parser_result = external_parser.Parse(b"", 1) + self.parser_result = external_parser.Parse(b"", True) return 1 parser = expat.ParserCreate(namespace_separator='!') parser.buffer_text = 1 out = ExternalOutputter(parser) parser.ExternalEntityRefHandler = out.ExternalEntityRefHandler - parser.Parse(data, 1) + parser.Parse(data, True) self.assertEqual(out.parser_result, 1) @@ -383,7 +383,7 @@ def test_default_to_disabled(self): def test_buffering_enabled(self): # Make sure buffering is turned on self.assertTrue(self.parser.buffer_text) - self.parser.Parse(b"123", 1) + self.parser.Parse(b"123", True) self.assertEqual(self.stuff, ['123'], "buffered text not properly collapsed") @@ -391,39 +391,39 @@ def test1(self): # XXX This test exposes more detail of Expat's text chunking than we # XXX like, but it tests what we need to concisely. self.setHandlers(["StartElementHandler"]) - self.parser.Parse(b"12\n34\n5", 1) + self.parser.Parse(b"12\n34\n5", True) self.assertEqual(self.stuff, ["", "1", "", "2", "\n", "3", "", "4\n5"], "buffering control not reacting as expected") def test2(self): - self.parser.Parse(b"1<2> \n 3", 1) + self.parser.Parse(b"1<2> \n 3", True) self.assertEqual(self.stuff, ["1<2> \n 3"], "buffered text not properly collapsed") def test3(self): self.setHandlers(["StartElementHandler"]) - self.parser.Parse(b"123", 1) + self.parser.Parse(b"123", True) self.assertEqual(self.stuff, ["", "1", "", "2", "", "3"], "buffered text not properly split") def test4(self): self.setHandlers(["StartElementHandler", "EndElementHandler"]) self.parser.CharacterDataHandler = None - self.parser.Parse(b"123", 1) + self.parser.Parse(b"123", True) self.assertEqual(self.stuff, ["", "", "", "", "", ""]) def test5(self): self.setHandlers(["StartElementHandler", "EndElementHandler"]) - self.parser.Parse(b"123", 1) + self.parser.Parse(b"123", True) self.assertEqual(self.stuff, ["", "1", "", "", "2", "", "", "3", ""]) def test6(self): self.setHandlers(["CommentHandler", "EndElementHandler", "StartElementHandler"]) - self.parser.Parse(b"12345 ", 1) + self.parser.Parse(b"12345 ", True) self.assertEqual(self.stuff, ["", "1", "", "", "2", "", "", "345", ""], "buffered text not properly split") @@ -431,7 +431,7 @@ def test6(self): def test7(self): self.setHandlers(["CommentHandler", "EndElementHandler", "StartElementHandler"]) - self.parser.Parse(b"12345 ", 1) + self.parser.Parse(b"12345 ", True) self.assertEqual(self.stuff, ["", "1", "", "", "2", "", "", "3", "", "4", "", "5", ""], @@ -451,7 +451,7 @@ def test_exception(self): parser = expat.ParserCreate() parser.StartElementHandler = self.StartElementHandler try: - parser.Parse(b"", 1) + parser.Parse(b"", True) self.fail() except RuntimeError as e: self.assertEqual(e.args[0], 'a', @@ -499,7 +499,7 @@ def test(self): ('e', 15, 3, 6), ('e', 17, 4, 1), ('e', 22, 5, 0)] xml = b'\n \n \n \n' - self.parser.Parse(xml, 1) + self.parser.Parse(xml, True) class sf1296433Test(unittest.TestCase): @@ -579,7 +579,7 @@ def test_disabling_buffer(self): # Parse one chunk of XML self.n = 0 - parser.Parse(xml1, 0) + parser.Parse(xml1, False) self.assertEqual(parser.buffer_size, 1024) self.assertEqual(self.n, 1) @@ -588,13 +588,13 @@ def test_disabling_buffer(self): self.assertFalse(parser.buffer_text) self.assertEqual(parser.buffer_size, 1024) for i in range(10): - parser.Parse(xml2, 0) + parser.Parse(xml2, False) self.assertEqual(self.n, 11) parser.buffer_text = 1 self.assertTrue(parser.buffer_text) self.assertEqual(parser.buffer_size, 1024) - parser.Parse(xml3, 1) + parser.Parse(xml3, True) self.assertEqual(self.n, 12) def counting_handler(self, text): @@ -621,10 +621,10 @@ def test_change_size_1(self): self.assertEqual(parser.buffer_size, 1024) self.n = 0 - parser.Parse(xml1, 0) + parser.Parse(xml1, False) parser.buffer_size *= 2 self.assertEqual(parser.buffer_size, 2048) - parser.Parse(xml2, 1) + parser.Parse(xml2, True) self.assertEqual(self.n, 2) def test_change_size_2(self): @@ -637,10 +637,10 @@ def test_change_size_2(self): self.assertEqual(parser.buffer_size, 2048) self.n=0 - parser.Parse(xml1, 0) + parser.Parse(xml1, False) parser.buffer_size = parser.buffer_size // 2 self.assertEqual(parser.buffer_size, 1024) - parser.Parse(xml2, 1) + parser.Parse(xml2, True) self.assertEqual(self.n, 4) class MalformedInputTest(unittest.TestCase): diff --git a/Lib/xml/dom/expatbuilder.py b/Lib/xml/dom/expatbuilder.py index 2bd835b03594..199c22d0af34 100644 --- a/Lib/xml/dom/expatbuilder.py +++ b/Lib/xml/dom/expatbuilder.py @@ -204,11 +204,11 @@ def parseFile(self, file): buffer = file.read(16*1024) if not buffer: break - parser.Parse(buffer, 0) + parser.Parse(buffer, False) if first_buffer and self.document.documentElement: self._setup_subset(buffer) first_buffer = False - parser.Parse("", True) + parser.Parse(b"", True) except ParseEscape: pass doc = self.document @@ -637,7 +637,7 @@ def parseString(self, string): nsattrs = self._getNSattrs() # get ns decls from node's ancestors document = _FRAGMENT_BUILDER_TEMPLATE % (ident, subset, nsattrs) try: - parser.Parse(document, 1) + parser.Parse(document, True) except: self.reset() raise @@ -697,7 +697,7 @@ def external_entity_ref_handler(self, context, base, systemId, publicId): self.fragment = self.document.createDocumentFragment() self.curNode = self.fragment try: - parser.Parse(self._source, 1) + parser.Parse(self._source, True) finally: self.curNode = old_cur_node self.document = old_document diff --git a/Lib/xml/etree/ElementTree.py b/Lib/xml/etree/ElementTree.py index beb2d68803a5..e75200a1b8a3 100644 --- a/Lib/xml/etree/ElementTree.py +++ b/Lib/xml/etree/ElementTree.py @@ -1710,14 +1710,14 @@ def _default(self, text): def feed(self, data): """Feed encoded data to parser.""" try: - self.parser.Parse(data, 0) + self.parser.Parse(data, False) except self._error as v: self._raiseerror(v) def close(self): """Finish feeding data to parser and return element structure.""" try: - self.parser.Parse("", 1) # end of data + self.parser.Parse(b"", True) # end of data except self._error as v: self._raiseerror(v) try: diff --git a/Lib/xml/sax/expatreader.py b/Lib/xml/sax/expatreader.py index 5066ffc2fa51..e334ac9fea0d 100644 --- a/Lib/xml/sax/expatreader.py +++ b/Lib/xml/sax/expatreader.py @@ -93,7 +93,7 @@ def __init__(self, namespaceHandling=0, bufsize=2**16-20): self._parser = None self._namespaces = namespaceHandling self._lex_handler_prop = None - self._parsing = 0 + self._parsing = False self._entity_stack = [] self._external_ges = 0 self._interning = None @@ -203,10 +203,10 @@ def setProperty(self, name, value): # IncrementalParser methods - def feed(self, data, isFinal = 0): + def feed(self, data, isFinal=False): if not self._parsing: self.reset() - self._parsing = 1 + self._parsing = True self._cont_handler.startDocument() try: @@ -237,13 +237,13 @@ def close(self): # If we are completing an external entity, do nothing here return try: - self.feed("", isFinal = 1) + self.feed(b"", isFinal=True) self._cont_handler.endDocument() - self._parsing = 0 + self._parsing = False # break cycle created by expat handlers pointing to our methods self._parser = None finally: - self._parsing = 0 + self._parsing = False if self._parser is not None: # Keep ErrorColumnNumber and ErrorLineNumber after closing. parser = _ClosedParser() @@ -307,7 +307,7 @@ def reset(self): self._parser.SetParamEntityParsing( expat.XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE) - self._parsing = 0 + self._parsing = False self._entity_stack = [] # Locator methods diff --git a/Lib/xmlrpc/client.py b/Lib/xmlrpc/client.py index 246ef27ffc24..b4b2941ea5b4 100644 --- a/Lib/xmlrpc/client.py +++ b/Lib/xmlrpc/client.py @@ -442,7 +442,7 @@ def __init__(self, target): target.xml(encoding, None) def feed(self, data): - self._parser.Parse(data, 0) + self._parser.Parse(data, False) def close(self): try: From webhook-mailer at python.org Sun Sep 1 05:12:55 2019 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Sun, 01 Sep 2019 09:12:55 -0000 Subject: [Python-checkins] bpo-15999: Always pass bool instead of int to socket.setblocking(). (GH-15621) Message-ID: https://github.com/python/cpython/commit/5eca7f3f3836cc734dfe8dc5ec669f3b4e9333fe commit: 5eca7f3f3836cc734dfe8dc5ec669f3b4e9333fe branch: master author: Serhiy Storchaka committer: GitHub date: 2019-09-01T12:12:52+03:00 summary: bpo-15999: Always pass bool instead of int to socket.setblocking(). (GH-15621) files: M Doc/howto/sockets.rst M Lib/asyncore.py M Lib/test/test_asyncio/functional.py M Lib/test/test_socket.py M Lib/test/test_ssl.py M Lib/test/test_timeout.py M Modules/socketmodule.c diff --git a/Doc/howto/sockets.rst b/Doc/howto/sockets.rst index bc71d85a83e9..4655f2806089 100644 --- a/Doc/howto/sockets.rst +++ b/Doc/howto/sockets.rst @@ -317,7 +317,7 @@ know about the mechanics of using sockets. You'll still use the same calls, in much the same ways. It's just that, if you do it right, your app will be almost inside-out. -In Python, you use ``socket.setblocking(0)`` to make it non-blocking. In C, it's +In Python, you use ``socket.setblocking(False)`` to make it non-blocking. In C, it's more complex, (for one thing, you'll need to choose between the BSD flavor ``O_NONBLOCK`` and the almost indistinguishable Posix flavor ``O_NDELAY``, which is completely different from ``TCP_NODELAY``), but it's the exact same idea. You diff --git a/Lib/asyncore.py b/Lib/asyncore.py index 0e92be3ad191..ce16f11a2f5c 100644 --- a/Lib/asyncore.py +++ b/Lib/asyncore.py @@ -228,7 +228,7 @@ def __init__(self, sock=None, map=None): if sock: # Set to nonblocking just to make sure for cases where we # get a socket from a blocking source. - sock.setblocking(0) + sock.setblocking(False) self.set_socket(sock, map) self.connected = True # The constructor no longer requires that the socket @@ -280,7 +280,7 @@ def del_channel(self, map=None): def create_socket(self, family=socket.AF_INET, type=socket.SOCK_STREAM): self.family_and_type = family, type sock = socket.socket(family, type) - sock.setblocking(0) + sock.setblocking(False) self.set_socket(sock) def set_socket(self, sock, map=None): diff --git a/Lib/test/test_asyncio/functional.py b/Lib/test/test_asyncio/functional.py index 70cd140f4796..4620d3e7a331 100644 --- a/Lib/test/test_asyncio/functional.py +++ b/Lib/test/test_asyncio/functional.py @@ -225,7 +225,7 @@ def stop(self): def run(self): try: with self._sock: - self._sock.setblocking(0) + self._sock.setblocking(False) self._run() finally: self._s1.close() diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index ce816cd603ec..14ff561da94f 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -4597,7 +4597,7 @@ def _testInheritFlagsTimeout(self): def testAccept(self): # Testing non-blocking accept - self.serv.setblocking(0) + self.serv.setblocking(False) # connect() didn't start: non-blocking accept() fails start_time = time.monotonic() @@ -4628,7 +4628,7 @@ def testRecv(self): # Testing non-blocking recv conn, addr = self.serv.accept() self.addCleanup(conn.close) - conn.setblocking(0) + conn.setblocking(False) # the server didn't send data yet: non-blocking recv() fails with self.assertRaises(BlockingIOError): @@ -5698,15 +5698,15 @@ def test_SOCK_NONBLOCK(self): with socket.socket(socket.AF_INET, socket.SOCK_STREAM | socket.SOCK_NONBLOCK) as s: self.checkNonblock(s) - s.setblocking(1) + s.setblocking(True) self.checkNonblock(s, nonblock=False) - s.setblocking(0) + s.setblocking(False) self.checkNonblock(s) s.settimeout(None) self.checkNonblock(s, nonblock=False) s.settimeout(2.0) self.checkNonblock(s, timeout=2.0) - s.setblocking(1) + s.setblocking(True) self.checkNonblock(s, nonblock=False) # defaulttimeout t = socket.getdefaulttimeout() diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index afc5be9a7e99..6ad0b61d8a57 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -2209,7 +2209,7 @@ def __init__(self, server, connsock, addr): self.running = False self.sock = connsock self.addr = addr - self.sock.setblocking(1) + self.sock.setblocking(True) self.sslconn = None threading.Thread.__init__(self) self.daemon = True @@ -3255,7 +3255,7 @@ def test_starttls(self): wrapped = False with server: s = socket.socket() - s.setblocking(1) + s.setblocking(True) s.connect((HOST, server.port)) if support.verbose: sys.stdout.write("\n") diff --git a/Lib/test/test_timeout.py b/Lib/test/test_timeout.py index b07c07cbfc4d..0fe4c7ab6e75 100644 --- a/Lib/test/test_timeout.py +++ b/Lib/test/test_timeout.py @@ -79,24 +79,24 @@ def testRangeCheck(self): def testTimeoutThenBlocking(self): # Test settimeout() followed by setblocking() self.sock.settimeout(10) - self.sock.setblocking(1) + self.sock.setblocking(True) self.assertEqual(self.sock.gettimeout(), None) - self.sock.setblocking(0) + self.sock.setblocking(False) self.assertEqual(self.sock.gettimeout(), 0.0) self.sock.settimeout(10) - self.sock.setblocking(0) + self.sock.setblocking(False) self.assertEqual(self.sock.gettimeout(), 0.0) - self.sock.setblocking(1) + self.sock.setblocking(True) self.assertEqual(self.sock.gettimeout(), None) def testBlockingThenTimeout(self): # Test setblocking() followed by settimeout() - self.sock.setblocking(0) + self.sock.setblocking(False) self.sock.settimeout(1) self.assertEqual(self.sock.gettimeout(), 1) - self.sock.setblocking(1) + self.sock.setblocking(True) self.sock.settimeout(1) self.assertEqual(self.sock.gettimeout(), 1) diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index d4f2098e1e6f..3548b0c87875 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -146,7 +146,7 @@ recvfrom_into(buffer[, nbytes, [, flags])\n\ sendall(data[, flags]) -- send all data\n\ send(data[, flags]) -- send data, may not send all of it\n\ sendto(data[, flags], addr) -- send data to a given address\n\ -setblocking(0 | 1) -- set or clear the blocking I/O flag\n\ +setblocking(bool) -- set or clear the blocking I/O flag\n\ getblocking() -- return True if socket is blocking, False if non-blocking\n\ setsockopt(level, optname, value[, optlen]) -- set socket options\n\ settimeout(None | float) -- set or clear the timeout\n\ From webhook-mailer at python.org Sun Sep 1 05:16:54 2019 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Sun, 01 Sep 2019 09:16:54 -0000 Subject: [Python-checkins] bpo-15999: Clean up of handling boolean arguments. (GH-15610) Message-ID: https://github.com/python/cpython/commit/1f21eaa15e8a0d2b0f78d0e3f2b9e5b458eb0a70 commit: 1f21eaa15e8a0d2b0f78d0e3f2b9e5b458eb0a70 branch: master author: Serhiy Storchaka committer: GitHub date: 2019-09-01T12:16:51+03:00 summary: bpo-15999: Clean up of handling boolean arguments. (GH-15610) * Use the 'p' format unit instead of manually called PyObject_IsTrue(). * Pass boolean value instead 0/1 integers to functions that needs boolean. * Convert some arguments to boolean only once. files: M Lib/_pyio.py M Lib/codeop.py M Lib/doctest.py M Lib/quopri.py M Lib/test/datetimetester.py M Lib/test/lock_tests.py M Lib/test/test_builtin.py M Lib/test/test_ioctl.py M Lib/test/test_ordered_dict.py M Lib/test/test_unicode.py M Lib/threading.py M Lib/tkinter/__init__.py M Modules/_io/_iomodule.c M Modules/_io/stringio.c M Modules/_io/textio.c M Modules/itertoolsmodule.c M Modules/main.c M Modules/parsermodule.c M Objects/fileobject.c M Python/bltinmodule.c M Python/pylifecycle.c diff --git a/Lib/_pyio.py b/Lib/_pyio.py index 0d3f974c0724..c1bdac791319 100644 --- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -2295,7 +2295,7 @@ def _read_chunk(self): return not eof def _pack_cookie(self, position, dec_flags=0, - bytes_to_feed=0, need_eof=0, chars_to_skip=0): + bytes_to_feed=0, need_eof=False, chars_to_skip=0): # The meaning of a tell() cookie is: seek to position, set the # decoder flags to dec_flags, read bytes_to_feed bytes, feed them # into the decoder with need_eof as the EOF flag, then skip @@ -2309,7 +2309,7 @@ def _unpack_cookie(self, bigint): rest, dec_flags = divmod(rest, 1<<64) rest, bytes_to_feed = divmod(rest, 1<<64) need_eof, chars_to_skip = divmod(rest, 1<<64) - return position, dec_flags, bytes_to_feed, need_eof, chars_to_skip + return position, dec_flags, bytes_to_feed, bool(need_eof), chars_to_skip def tell(self): if not self._seekable: @@ -2383,7 +2383,7 @@ def tell(self): # (a point where the decoder has nothing buffered, so seek() # can safely start from there and advance to this location). bytes_fed = 0 - need_eof = 0 + need_eof = False # Chars decoded since `start_pos` chars_decoded = 0 for i in range(skip_bytes, len(next_input)): @@ -2400,7 +2400,7 @@ def tell(self): else: # We didn't get enough decoded data; signal EOF to get more. chars_decoded += len(decoder.decode(b'', final=True)) - need_eof = 1 + need_eof = True if chars_decoded < chars_to_skip: raise OSError("can't reconstruct logical file position") diff --git a/Lib/codeop.py b/Lib/codeop.py index fb759da42ad1..e5c7adea54fb 100644 --- a/Lib/codeop.py +++ b/Lib/codeop.py @@ -130,7 +130,7 @@ def __init__(self): self.flags = PyCF_DONT_IMPLY_DEDENT def __call__(self, source, filename, symbol): - codeob = compile(source, filename, symbol, self.flags, 1) + codeob = compile(source, filename, symbol, self.flags, True) for feature in _features: if codeob.co_flags & feature.compiler_flag: self.flags |= feature.compiler_flag diff --git a/Lib/doctest.py b/Lib/doctest.py index bf4889f59e0d..8fca6280b8aa 100644 --- a/Lib/doctest.py +++ b/Lib/doctest.py @@ -1326,7 +1326,7 @@ def __run(self, test, compileflags, out): try: # Don't blink! This is where the user's code gets run. exec(compile(example.source, filename, "single", - compileflags, 1), test.globs) + compileflags, True), test.globs) self.debugger.set_continue() # ==== Example Finished ==== exception = None except KeyboardInterrupt: diff --git a/Lib/quopri.py b/Lib/quopri.py index cbd979abdffc..08899c5cb73a 100755 --- a/Lib/quopri.py +++ b/Lib/quopri.py @@ -204,11 +204,11 @@ def main(): print("-t: quote tabs") print("-d: decode; default encode") sys.exit(2) - deco = 0 - tabs = 0 + deco = False + tabs = False for o, a in opts: - if o == '-t': tabs = 1 - if o == '-d': deco = 1 + if o == '-t': tabs = True + if o == '-d': deco = True if tabs and deco: sys.stdout = sys.stderr print("-t and -d are mutually exclusive") diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index d09255d79bfb..d5de5e91a982 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -6160,7 +6160,7 @@ class TZInfoSubclass(tzinfo): def test_date_from_date(self): exp_date = date(1993, 8, 26) - for macro in [0, 1]: + for macro in False, True: with self.subTest(macro=macro): c_api_date = _testcapi.get_date_fromdate( macro, @@ -6173,7 +6173,7 @@ def test_date_from_date(self): def test_datetime_from_dateandtime(self): exp_date = datetime(1993, 8, 26, 22, 12, 55, 99999) - for macro in [0, 1]: + for macro in False, True: with self.subTest(macro=macro): c_api_date = _testcapi.get_datetime_fromdateandtime( macro, @@ -6191,7 +6191,7 @@ def test_datetime_from_dateandtimeandfold(self): exp_date = datetime(1993, 8, 26, 22, 12, 55, 99999) for fold in [0, 1]: - for macro in [0, 1]: + for macro in False, True: with self.subTest(macro=macro, fold=fold): c_api_date = _testcapi.get_datetime_fromdateandtimeandfold( macro, @@ -6210,7 +6210,7 @@ def test_datetime_from_dateandtimeandfold(self): def test_time_from_time(self): exp_time = time(22, 12, 55, 99999) - for macro in [0, 1]: + for macro in False, True: with self.subTest(macro=macro): c_api_time = _testcapi.get_time_fromtime( macro, @@ -6225,7 +6225,7 @@ def test_time_from_timeandfold(self): exp_time = time(22, 12, 55, 99999) for fold in [0, 1]: - for macro in [0, 1]: + for macro in False, True: with self.subTest(macro=macro, fold=fold): c_api_time = _testcapi.get_time_fromtimeandfold( macro, @@ -6241,7 +6241,7 @@ def test_time_from_timeandfold(self): def test_delta_from_dsu(self): exp_delta = timedelta(26, 55, 99999) - for macro in [0, 1]: + for macro in False, True: with self.subTest(macro=macro): c_api_delta = _testcapi.get_delta_fromdsu( macro, @@ -6254,7 +6254,7 @@ def test_delta_from_dsu(self): def test_date_from_timestamp(self): ts = datetime(1995, 4, 12).timestamp() - for macro in [0, 1]: + for macro in False, True: with self.subTest(macro=macro): d = _testcapi.get_date_fromtimestamp(int(ts), macro) @@ -6272,7 +6272,7 @@ def test_datetime_from_timestamp(self): from_timestamp = _testcapi.get_datetime_fromtimestamp for case in cases: - for macro in [0, 1]: + for macro in False, True: with self.subTest(case=case, macro=macro): dtup, tzinfo, usetz = case dt_orig = datetime(*dtup, tzinfo=tzinfo) diff --git a/Lib/test/lock_tests.py b/Lib/test/lock_tests.py index d12a86e7e934..cd1155d34e99 100644 --- a/Lib/test/lock_tests.py +++ b/Lib/test/lock_tests.py @@ -182,7 +182,7 @@ def f(): def test_timeout(self): lock = self.locktype() # Can't set timeout if not blocking - self.assertRaises(ValueError, lock.acquire, 0, 1) + self.assertRaises(ValueError, lock.acquire, False, 1) # Invalid timeout values self.assertRaises(ValueError, lock.acquire, timeout=-100) self.assertRaises(OverflowError, lock.acquire, timeout=1e100) diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 1100c49e9b88..db2f6cd49b2c 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -320,8 +320,8 @@ def test_compile(self): bom = b'\xef\xbb\xbf' compile(bom + b'print(1)\n', '', 'exec') compile(source='pass', filename='?', mode='exec') - compile(dont_inherit=0, filename='tmp', source='0', mode='eval') - compile('pass', '?', dont_inherit=1, mode='exec') + compile(dont_inherit=False, filename='tmp', source='0', mode='eval') + compile('pass', '?', dont_inherit=True, mode='exec') compile(memoryview(b"text"), "name", "exec") self.assertRaises(TypeError, compile) self.assertRaises(ValueError, compile, 'print(42)\n', '', 'badmode') @@ -1853,7 +1853,7 @@ def test_basic(self): self.assertEqual(data, sorted(copy, key=lambda x: -x)) self.assertNotEqual(data, copy) random.shuffle(copy) - self.assertEqual(data, sorted(copy, reverse=1)) + self.assertEqual(data, sorted(copy, reverse=True)) self.assertNotEqual(data, copy) def test_bad_arguments(self): diff --git a/Lib/test/test_ioctl.py b/Lib/test/test_ioctl.py index d1a5db90810d..a2873582cf98 100644 --- a/Lib/test/test_ioctl.py +++ b/Lib/test/test_ioctl.py @@ -48,7 +48,7 @@ def _check_ioctl_mutate_len(self, nbytes=None): else: buf.append(fill) with open("/dev/tty", "rb") as tty: - r = fcntl.ioctl(tty, termios.TIOCGPGRP, buf, 1) + r = fcntl.ioctl(tty, termios.TIOCGPGRP, buf, True) rpgrp = buf[0] self.assertEqual(r, 0) self.assertIn(rpgrp, ids) diff --git a/Lib/test/test_ordered_dict.py b/Lib/test/test_ordered_dict.py index 148a9bdc35ee..f337be8eb855 100644 --- a/Lib/test/test_ordered_dict.py +++ b/Lib/test/test_ordered_dict.py @@ -407,9 +407,9 @@ def test_move_to_end(self): self.assertEqual(list(od), list('abcde')) od.move_to_end('c') self.assertEqual(list(od), list('abdec')) - od.move_to_end('c', 0) + od.move_to_end('c', False) self.assertEqual(list(od), list('cabde')) - od.move_to_end('c', 0) + od.move_to_end('c', False) self.assertEqual(list(od), list('cabde')) od.move_to_end('e') self.assertEqual(list(od), list('cabde')) @@ -418,7 +418,7 @@ def test_move_to_end(self): with self.assertRaises(KeyError): od.move_to_end('x') with self.assertRaises(KeyError): - od.move_to_end('x', 0) + od.move_to_end('x', False) def test_move_to_end_issue25406(self): OrderedDict = self.OrderedDict diff --git a/Lib/test/test_unicode.py b/Lib/test/test_unicode.py index 7bd7f51b592b..8636f2b0bd50 100644 --- a/Lib/test/test_unicode.py +++ b/Lib/test/test_unicode.py @@ -2820,15 +2820,15 @@ def test_asucs4(self): for s in ['abc', '\xa1\xa2', '\u4f60\u597d', 'a\U0001f600', 'a\ud800b\udfffc', '\ud834\udd1e']: l = len(s) - self.assertEqual(unicode_asucs4(s, l, 1), s+'\0') - self.assertEqual(unicode_asucs4(s, l, 0), s+'\uffff') - self.assertEqual(unicode_asucs4(s, l+1, 1), s+'\0\uffff') - self.assertEqual(unicode_asucs4(s, l+1, 0), s+'\0\uffff') - self.assertRaises(SystemError, unicode_asucs4, s, l-1, 1) - self.assertRaises(SystemError, unicode_asucs4, s, l-2, 0) + self.assertEqual(unicode_asucs4(s, l, True), s+'\0') + self.assertEqual(unicode_asucs4(s, l, False), s+'\uffff') + self.assertEqual(unicode_asucs4(s, l+1, True), s+'\0\uffff') + self.assertEqual(unicode_asucs4(s, l+1, False), s+'\0\uffff') + self.assertRaises(SystemError, unicode_asucs4, s, l-1, True) + self.assertRaises(SystemError, unicode_asucs4, s, l-2, False) s = '\0'.join([s, s]) - self.assertEqual(unicode_asucs4(s, len(s), 1), s+'\0') - self.assertEqual(unicode_asucs4(s, len(s), 0), s+'\uffff') + self.assertEqual(unicode_asucs4(s, len(s), True), s+'\0') + self.assertEqual(unicode_asucs4(s, len(s), False), s+'\uffff') # Test PyUnicode_AsUTF8() @support.cpython_only diff --git a/Lib/threading.py b/Lib/threading.py index c3bbb19acbe2..59323679c80d 100644 --- a/Lib/threading.py +++ b/Lib/threading.py @@ -262,7 +262,7 @@ def _acquire_restore(self, x): def _is_owned(self): # Return True if lock is owned by current_thread. # This method is called only if _lock doesn't have _is_owned(). - if self._lock.acquire(0): + if self._lock.acquire(False): self._lock.release() return False else: diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py index 9258484af5fd..5d5cf90e0bdd 100644 --- a/Lib/tkinter/__init__.py +++ b/Lib/tkinter/__init__.py @@ -2241,7 +2241,7 @@ class Tk(Misc, Wm): _w = '.' def __init__(self, screenName=None, baseName=None, className='Tk', - useTk=1, sync=0, use=None): + useTk=True, sync=False, use=None): """Return a new Toplevel widget on screen SCREENNAME. A new Tcl interpreter will be created. BASENAME will be used for the identification of the profile file (see readprofile). @@ -2259,7 +2259,7 @@ def __init__(self, screenName=None, baseName=None, className='Tk', baseName, ext = os.path.splitext(baseName) if ext not in ('.py', '.pyc'): baseName = baseName + ext - interactive = 0 + interactive = False self.tk = _tkinter.create(screenName, baseName, className, interactive, wantobjects, useTk, sync, use) if useTk: self._loadtk() @@ -2361,7 +2361,7 @@ def __getattr__(self, attr): # copied into the Pack, Place or Grid class. -def Tcl(screenName=None, baseName=None, className='Tk', useTk=0): +def Tcl(screenName=None, baseName=None, className='Tk', useTk=False): return Tk(screenName, baseName, className, useTk) diff --git a/Modules/_io/_iomodule.c b/Modules/_io/_iomodule.c index 96426e0276ab..162b288852b3 100644 --- a/Modules/_io/_iomodule.c +++ b/Modules/_io/_iomodule.c @@ -383,8 +383,10 @@ _io_open_impl(PyObject *module, PyObject *file, const char *mode, encoding = "utf-8"; } #endif - raw = PyObject_CallFunction(RawIO_class, - "OsiO", path_or_fd, rawmode, closefd, opener); + raw = PyObject_CallFunction(RawIO_class, "OsOO", + path_or_fd, rawmode, + closefd ? Py_True : Py_False, + opener); } if (raw == NULL) @@ -476,10 +478,10 @@ _io_open_impl(PyObject *module, PyObject *file, const char *mode, /* wraps into a TextIOWrapper */ wrapper = PyObject_CallFunction((PyObject *)&PyTextIOWrapper_Type, - "Osssi", + "OsssO", buffer, encoding, errors, newline, - line_buffering); + line_buffering ? Py_True : Py_False); if (wrapper == NULL) goto error; result = wrapper; diff --git a/Modules/_io/stringio.c b/Modules/_io/stringio.c index 3f7afad56347..89b29bb8fbb7 100644 --- a/Modules/_io/stringio.c +++ b/Modules/_io/stringio.c @@ -714,9 +714,9 @@ _io_StringIO___init___impl(stringio *self, PyObject *value, } if (self->readuniversal) { - self->decoder = PyObject_CallFunction( + self->decoder = PyObject_CallFunctionObjArgs( (PyObject *)&PyIncrementalNewlineDecoder_Type, - "Oi", Py_None, (int) self->readtranslate); + Py_None, self->readtranslate ? Py_True : Py_False, NULL); if (self->decoder == NULL) return -1; } diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c index 7ddac8062a96..1db8d94ed12a 100644 --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -880,9 +880,9 @@ _textiowrapper_set_decoder(textio *self, PyObject *codec_info, return -1; if (self->readuniversal) { - PyObject *incrementalDecoder = PyObject_CallFunction( + PyObject *incrementalDecoder = PyObject_CallFunctionObjArgs( (PyObject *)&PyIncrementalNewlineDecoder_Type, - "Oi", self->decoder, (int)self->readtranslate); + self->decoder, self->readtranslate ? Py_True : Py_False, NULL); if (incrementalDecoder == NULL) return -1; Py_CLEAR(self->decoder); @@ -2591,8 +2591,8 @@ _io_TextIOWrapper_seek_impl(textio *self, PyObject *cookieObj, int whence) } Py_XSETREF(self->snapshot, snapshot); - decoded = _PyObject_CallMethodId(self->decoder, &PyId_decode, - "Oi", input_chunk, (int)cookie.need_eof); + decoded = _PyObject_CallMethodIdObjArgs(self->decoder, &PyId_decode, + input_chunk, cookie.need_eof ? Py_True : Py_False, NULL); if (check_decoded(decoded) < 0) goto fail; @@ -2819,7 +2819,7 @@ _io_TextIOWrapper_tell_impl(textio *self) if (input == input_end) { /* We didn't get enough decoded data; signal EOF to get more. */ PyObject *decoded = _PyObject_CallMethodId( - self->decoder, &PyId_decode, "yi", "", /* final = */ 1); + self->decoder, &PyId_decode, "yO", "", /* final = */ Py_True); if (check_decoded(decoded) < 0) goto fail; chars_decoded += PyUnicode_GET_LENGTH(decoded); diff --git a/Modules/itertoolsmodule.c b/Modules/itertoolsmodule.c index ab473e29fbd5..2e398425a146 100644 --- a/Modules/itertoolsmodule.c +++ b/Modules/itertoolsmodule.c @@ -1059,10 +1059,10 @@ cycle_reduce(cycleobject *lz, PyObject *Py_UNUSED(ignored)) } Py_DECREF(res); } - return Py_BuildValue("O(N)(Oi)", Py_TYPE(lz), it, lz->saved, 1); + return Py_BuildValue("O(N)(OO)", Py_TYPE(lz), it, lz->saved, Py_True); } - return Py_BuildValue("O(O)(Oi)", Py_TYPE(lz), lz->it, lz->saved, - lz->firstpass); + return Py_BuildValue("O(O)(OO)", Py_TYPE(lz), lz->it, lz->saved, + lz->firstpass ? Py_True : Py_False); } static PyObject * diff --git a/Modules/main.c b/Modules/main.c index b8a1c9b79ce3..fdc7e0dabf17 100644 --- a/Modules/main.c +++ b/Modules/main.c @@ -292,7 +292,7 @@ pymain_run_module(const wchar_t *modname, int set_argv0) Py_DECREF(runmodule); return pymain_exit_err_print(); } - runargs = Py_BuildValue("(Oi)", module, set_argv0); + runargs = PyTuple_Pack(2, module, set_argv0 ? Py_True : Py_False); if (runargs == NULL) { fprintf(stderr, "Could not create arguments for runpy._run_module_as_main\n"); diff --git a/Modules/parsermodule.c b/Modules/parsermodule.c index b2495fc8b322..ef63ca936e91 100644 --- a/Modules/parsermodule.c +++ b/Modules/parsermodule.c @@ -1080,25 +1080,20 @@ parser__pickler(PyObject *self, PyObject *args) NOTE(ARGUNUSED(self)) PyObject *result = NULL; PyObject *st = NULL; - PyObject *empty_dict = NULL; if (PyArg_ParseTuple(args, "O!:_pickler", &PyST_Type, &st)) { PyObject *newargs; PyObject *tuple; - if ((empty_dict = PyDict_New()) == NULL) - goto finally; - if ((newargs = Py_BuildValue("Oi", st, 1)) == NULL) - goto finally; - tuple = parser_st2tuple((PyST_Object*)NULL, newargs, empty_dict); + if ((newargs = PyTuple_Pack(2, st, Py_True)) == NULL) + return NULL; + tuple = parser_st2tuple((PyST_Object*)NULL, newargs, NULL); if (tuple != NULL) { result = Py_BuildValue("O(O)", pickle_constructor, tuple); Py_DECREF(tuple); } Py_DECREF(newargs); } - finally: - Py_XDECREF(empty_dict); return (result); } diff --git a/Objects/fileobject.c b/Objects/fileobject.c index 61c94280937b..3ec5a00f30f6 100644 --- a/Objects/fileobject.c +++ b/Objects/fileobject.c @@ -38,9 +38,9 @@ PyFile_FromFd(int fd, const char *name, const char *mode, int buffering, const c io = PyImport_ImportModule("_io"); if (io == NULL) return NULL; - stream = _PyObject_CallMethodId(io, &PyId_open, "isisssi", fd, mode, + stream = _PyObject_CallMethodId(io, &PyId_open, "isisssO", fd, mode, buffering, encoding, errors, - newline, closefd); + newline, closefd ? Py_True : Py_False); Py_DECREF(io); if (stream == NULL) return NULL; diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 5053f7a1748f..3b0d64ff14ab 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -1820,8 +1820,9 @@ static PyObject * builtin_print(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { static const char * const _keywords[] = {"sep", "end", "file", "flush", 0}; - static struct _PyArg_Parser _parser = {"|OOOO:print", _keywords, 0}; - PyObject *sep = NULL, *end = NULL, *file = NULL, *flush = NULL; + static struct _PyArg_Parser _parser = {"|OOOp:print", _keywords, 0}; + PyObject *sep = NULL, *end = NULL, *file = NULL; + int flush = 0; int i, err; if (kwnames != NULL && @@ -1883,18 +1884,11 @@ builtin_print(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject if (err) return NULL; - if (flush != NULL) { - PyObject *tmp; - int do_flush = PyObject_IsTrue(flush); - if (do_flush == -1) + if (flush) { + PyObject *tmp = _PyObject_CallMethodIdNoArgs(file, &PyId_flush); + if (tmp == NULL) return NULL; - else if (do_flush) { - tmp = _PyObject_CallMethodIdNoArgs(file, &PyId_flush); - if (tmp == NULL) - return NULL; - else - Py_DECREF(tmp); - } + Py_DECREF(tmp); } Py_RETURN_NONE; diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 7cda44dc29d3..ea89b3a626bd 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -1735,10 +1735,10 @@ create_stdio(const PyConfig *config, PyObject* io, mode = "wb"; else mode = "rb"; - buf = _PyObject_CallMethodId(io, &PyId_open, "isiOOOi", + buf = _PyObject_CallMethodId(io, &PyId_open, "isiOOOO", fd, mode, buffering, Py_None, Py_None, /* encoding, errors */ - Py_None, 0); /* newline, closefd */ + Py_None, Py_False); /* newline, closefd */ if (buf == NULL) goto error; From webhook-mailer at python.org Sun Sep 1 07:01:09 2019 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Sun, 01 Sep 2019 11:01:09 -0000 Subject: [Python-checkins] [3.8] bpo-37994: Fix silencing all errors if an attribute lookup fails. (GH-15630) (GH-15635) Message-ID: https://github.com/python/cpython/commit/353053d9ad08fea0e205e6c008b8a4350c0188e6 commit: 353053d9ad08fea0e205e6c008b8a4350c0188e6 branch: 3.8 author: Serhiy Storchaka committer: GitHub date: 2019-09-01T14:01:05+03:00 summary: [3.8] bpo-37994: Fix silencing all errors if an attribute lookup fails. (GH-15630) (GH-15635) Only AttributeError should be silenced. (cherry picked from commit 41c57b335330ff48af098d47e379e0f9ba09d233) files: A Misc/NEWS.d/next/Core and Builtins/2019-08-31-11-13-25.bpo-37994.Rj6S4j.rst M Modules/_csv.c M Modules/_datetimemodule.c M Modules/_pickle.c M Modules/_threadmodule.c M Modules/pyexpat.c M Objects/bytearrayobject.c M Objects/descrobject.c M Objects/fileobject.c M Objects/setobject.c M Objects/typeobject.c M Objects/weakrefobject.c M Python/bltinmodule.c M Python/sysmodule.c diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-08-31-11-13-25.bpo-37994.Rj6S4j.rst b/Misc/NEWS.d/next/Core and Builtins/2019-08-31-11-13-25.bpo-37994.Rj6S4j.rst new file mode 100644 index 000000000000..103ac5a30905 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2019-08-31-11-13-25.bpo-37994.Rj6S4j.rst @@ -0,0 +1,2 @@ +Fixed silencing arbitrary errors if an attribute lookup fails in several +sites. Only AttributeError should be silenced. diff --git a/Modules/_csv.c b/Modules/_csv.c index 014cbb4e0231..46d414383cbd 100644 --- a/Modules/_csv.c +++ b/Modules/_csv.c @@ -1382,7 +1382,10 @@ csv_writer(PyObject *module, PyObject *args, PyObject *keyword_args) Py_DECREF(self); return NULL; } - self->write = _PyObject_GetAttrId(output_file, &PyId_write); + if (_PyObject_LookupAttrId(output_file, &PyId_write, &self->write) < 0) { + Py_DECREF(self); + return NULL; + } if (self->write == NULL || !PyCallable_Check(self->write)) { PyErr_SetString(PyExc_TypeError, "argument 1 must have a \"write\" method"); diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index d3c3f0905386..41c3f34269dd 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -3610,24 +3610,24 @@ tzinfo_reduce(PyObject *self, PyObject *Py_UNUSED(ignored)) _Py_IDENTIFIER(__getinitargs__); _Py_IDENTIFIER(__getstate__); - getinitargs = _PyObject_GetAttrId(self, &PyId___getinitargs__); + if (_PyObject_LookupAttrId(self, &PyId___getinitargs__, &getinitargs) < 0) { + return NULL; + } if (getinitargs != NULL) { args = _PyObject_CallNoArg(getinitargs); Py_DECREF(getinitargs); - if (args == NULL) { - return NULL; - } } else { - PyErr_Clear(); - args = PyTuple_New(0); - if (args == NULL) { - return NULL; - } + } + if (args == NULL) { + return NULL; } - getstate = _PyObject_GetAttrId(self, &PyId___getstate__); + if (_PyObject_LookupAttrId(self, &PyId___getstate__, &getstate) < 0) { + Py_DECREF(args); + return NULL; + } if (getstate != NULL) { state = _PyObject_CallNoArg(getstate); Py_DECREF(getstate); @@ -3638,7 +3638,6 @@ tzinfo_reduce(PyObject *self, PyObject *Py_UNUSED(ignored)) } else { PyObject **dictptr; - PyErr_Clear(); state = Py_None; dictptr = _PyObject_GetDictPtr(self); if (dictptr && *dictptr && PyDict_GET_SIZE(*dictptr)) { diff --git a/Modules/_pickle.c b/Modules/_pickle.c index bbe26034881a..fa2ef7d9dcd0 100644 --- a/Modules/_pickle.c +++ b/Modules/_pickle.c @@ -4385,7 +4385,6 @@ save(PicklerObject *self, PyObject *obj, int pers_save) _Py_IDENTIFIER(__reduce__); _Py_IDENTIFIER(__reduce_ex__); - /* XXX: If the __reduce__ method is defined, __reduce_ex__ is automatically defined as __reduce__. While this is convenient, this make it impossible to know which method was actually called. Of @@ -4406,14 +4405,15 @@ save(PicklerObject *self, PyObject *obj, int pers_save) } } else { - PickleState *st = _Pickle_GetGlobalState(); - /* Check for a __reduce__ method. */ - reduce_func = _PyObject_GetAttrId(obj, &PyId___reduce__); + if (_PyObject_LookupAttrId(obj, &PyId___reduce__, &reduce_func) < 0) { + goto error; + } if (reduce_func != NULL) { reduce_value = _PyObject_CallNoArg(reduce_func); } else { + PickleState *st = _Pickle_GetGlobalState(); PyErr_Format(st->PicklingError, "can't pickle '%.200s' object: %R", type->tp_name, obj); @@ -6448,7 +6448,9 @@ do_append(UnpicklerObject *self, Py_ssize_t x) PyObject *extend_func; _Py_IDENTIFIER(extend); - extend_func = _PyObject_GetAttrId(list, &PyId_extend); + if (_PyObject_LookupAttrId(list, &PyId_extend, &extend_func) < 0) { + return -1; + } if (extend_func != NULL) { slice = Pdata_poplist(self->stack, x); if (!slice) { @@ -6468,7 +6470,6 @@ do_append(UnpicklerObject *self, Py_ssize_t x) /* Even if the PEP 307 requires extend() and append() methods, fall back on append() if the object has no extend() method for backward compatibility. */ - PyErr_Clear(); append_func = _PyObject_GetAttrId(list, &PyId_append); if (append_func == NULL) return -1; diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c index d5e40ef999e3..fadf57aaa762 100644 --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -1305,6 +1305,7 @@ static int thread_excepthook_file(PyObject *file, PyObject *exc_type, PyObject *exc_value, PyObject *exc_traceback, PyObject *thread) { + _Py_IDENTIFIER(name); /* print(f"Exception in thread {thread.name}:", file=file) */ if (PyFile_WriteString("Exception in thread ", file) < 0) { return -1; @@ -1312,7 +1313,9 @@ thread_excepthook_file(PyObject *file, PyObject *exc_type, PyObject *exc_value, PyObject *name = NULL; if (thread != Py_None) { - name = PyObject_GetAttrString(thread, "name"); + if (_PyObject_LookupAttrId(thread, &PyId_name, &name) < 0) { + return -1; + } } if (name != NULL) { if (PyFile_WriteObject(name, file, Py_PRINT_RAW) < 0) { @@ -1322,8 +1325,6 @@ thread_excepthook_file(PyObject *file, PyObject *exc_type, PyObject *exc_value, Py_DECREF(name); } else { - PyErr_Clear(); - unsigned long ident = PyThread_get_thread_ident(); PyObject *str = PyUnicode_FromFormat("%lu", ident); if (str != NULL) { diff --git a/Modules/pyexpat.c b/Modules/pyexpat.c index 45a1e684d19f..bb5e25cc585d 100644 --- a/Modules/pyexpat.c +++ b/Modules/pyexpat.c @@ -810,7 +810,9 @@ pyexpat_xmlparser_ParseFile(xmlparseobject *self, PyObject *file) PyObject *readmethod = NULL; _Py_IDENTIFIER(read); - readmethod = _PyObject_GetAttrId(file, &PyId_read); + if (_PyObject_LookupAttrId(file, &PyId_read, &readmethod) < 0) { + return NULL; + } if (readmethod == NULL) { PyErr_SetString(PyExc_TypeError, "argument must have 'read' attribute"); diff --git a/Objects/bytearrayobject.c b/Objects/bytearrayobject.c index 1bb19a9271b6..590b80605618 100644 --- a/Objects/bytearrayobject.c +++ b/Objects/bytearrayobject.c @@ -2063,9 +2063,10 @@ _common_reduce(PyByteArrayObject *self, int proto) _Py_IDENTIFIER(__dict__); char *buf; - dict = _PyObject_GetAttrId((PyObject *)self, &PyId___dict__); + if (_PyObject_LookupAttrId((PyObject *)self, &PyId___dict__, &dict) < 0) { + return NULL; + } if (dict == NULL) { - PyErr_Clear(); dict = Py_None; Py_INCREF(dict); } diff --git a/Objects/descrobject.c b/Objects/descrobject.c index dbb44de846fd..119be35db08d 100644 --- a/Objects/descrobject.c +++ b/Objects/descrobject.c @@ -1622,29 +1622,25 @@ property_init_impl(propertyobject *self, PyObject *fget, PyObject *fset, /* if no docstring given and the getter has one, use that one */ if ((doc == NULL || doc == Py_None) && fget != NULL) { _Py_IDENTIFIER(__doc__); - PyObject *get_doc = _PyObject_GetAttrId(fget, &PyId___doc__); - if (get_doc) { - if (Py_TYPE(self) == &PyProperty_Type) { - Py_XSETREF(self->prop_doc, get_doc); - } - else { - /* If this is a property subclass, put __doc__ - in dict of the subclass instance instead, - otherwise it gets shadowed by __doc__ in the - class's dict. */ - int err = _PyObject_SetAttrId((PyObject *)self, &PyId___doc__, get_doc); - Py_DECREF(get_doc); - if (err < 0) - return -1; - } - self->getter_doc = 1; + PyObject *get_doc; + int rc = _PyObject_LookupAttrId(fget, &PyId___doc__, &get_doc); + if (rc <= 0) { + return rc; } - else if (PyErr_ExceptionMatches(PyExc_Exception)) { - PyErr_Clear(); + if (Py_TYPE(self) == &PyProperty_Type) { + Py_XSETREF(self->prop_doc, get_doc); } else { - return -1; + /* If this is a property subclass, put __doc__ + in dict of the subclass instance instead, + otherwise it gets shadowed by __doc__ in the + class's dict. */ + int err = _PyObject_SetAttrId((PyObject *)self, &PyId___doc__, get_doc); + Py_DECREF(get_doc); + if (err < 0) + return -1; } + self->getter_doc = 1; } return 0; diff --git a/Objects/fileobject.c b/Objects/fileobject.c index 3791241e5c7c..dd42d516898a 100644 --- a/Objects/fileobject.c +++ b/Objects/fileobject.c @@ -185,8 +185,10 @@ PyObject_AsFileDescriptor(PyObject *o) if (PyLong_Check(o)) { fd = _PyLong_AsInt(o); } - else if ((meth = _PyObject_GetAttrId(o, &PyId_fileno)) != NULL) - { + else if (_PyObject_LookupAttrId(o, &PyId_fileno, &meth) < 0) { + return -1; + } + else if (meth != NULL) { PyObject *fno = _PyObject_CallNoArg(meth); Py_DECREF(meth); if (fno == NULL) diff --git a/Objects/setobject.c b/Objects/setobject.c index 8cd95ba890dd..f8ae0c05e273 100644 --- a/Objects/setobject.c +++ b/Objects/setobject.c @@ -1954,9 +1954,10 @@ set_reduce(PySetObject *so, PyObject *Py_UNUSED(ignored)) args = PyTuple_Pack(1, keys); if (args == NULL) goto done; - dict = _PyObject_GetAttrId((PyObject *)so, &PyId___dict__); + if (_PyObject_LookupAttrId((PyObject *)so, &PyId___dict__, &dict) < 0) { + goto done; + } if (dict == NULL) { - PyErr_Clear(); dict = Py_None; Py_INCREF(dict); } diff --git a/Objects/typeobject.c b/Objects/typeobject.c index bf0c3e072919..8ece21827519 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -1547,17 +1547,10 @@ tail_contains(PyObject *tuple, int whence, PyObject *o) static PyObject * class_name(PyObject *cls) { - PyObject *name = _PyObject_GetAttrId(cls, &PyId___name__); - if (name == NULL) { - PyErr_Clear(); + PyObject *name; + if (_PyObject_LookupAttrId(cls, &PyId___name__, &name) == 0) { name = PyObject_Repr(cls); } - if (name == NULL) - return NULL; - if (!PyUnicode_Check(name)) { - Py_DECREF(name); - return NULL; - } return name; } @@ -1575,13 +1568,15 @@ check_duplicates(PyObject *tuple) if (PyTuple_GET_ITEM(tuple, j) == o) { o = class_name(o); if (o != NULL) { - PyErr_Format(PyExc_TypeError, - "duplicate base class %U", - o); + if (PyUnicode_Check(o)) { + PyErr_Format(PyExc_TypeError, + "duplicate base class %U", o); + } + else { + PyErr_SetString(PyExc_TypeError, + "duplicate base class"); + } Py_DECREF(o); - } else { - PyErr_SetString(PyExc_TypeError, - "duplicate base class"); } return -1; } @@ -1625,13 +1620,20 @@ consistent method resolution\norder (MRO) for bases"); i = 0; while (PyDict_Next(set, &i, &k, &v) && (size_t)off < sizeof(buf)) { PyObject *name = class_name(k); - const char *name_str; + const char *name_str = NULL; if (name != NULL) { - name_str = PyUnicode_AsUTF8(name); - if (name_str == NULL) + if (PyUnicode_Check(name)) { + name_str = PyUnicode_AsUTF8(name); + } + else { name_str = "?"; - } else - name_str = "?"; + } + } + if (name_str == NULL) { + Py_XDECREF(name); + Py_DECREF(set); + return; + } off += PyOS_snprintf(buf + off, sizeof(buf) - off, " %s", name_str); Py_XDECREF(name); if (--n && (size_t)(off+1) < sizeof(buf)) { @@ -3418,10 +3420,10 @@ merge_class_dict(PyObject *dict, PyObject *aclass) assert(aclass); /* Merge in the type's dict (if any). */ - classdict = _PyObject_GetAttrId(aclass, &PyId___dict__); - if (classdict == NULL) - PyErr_Clear(); - else { + if (_PyObject_LookupAttrId(aclass, &PyId___dict__, &classdict) < 0) { + return -1; + } + if (classdict != NULL) { int status = PyDict_Update(dict, classdict); Py_DECREF(classdict); if (status < 0) @@ -3429,15 +3431,17 @@ merge_class_dict(PyObject *dict, PyObject *aclass) } /* Recursively merge in the base types' (if any) dicts. */ - bases = _PyObject_GetAttrId(aclass, &PyId___bases__); - if (bases == NULL) - PyErr_Clear(); - else { + if (_PyObject_LookupAttrId(aclass, &PyId___bases__, &bases) < 0) { + return -1; + } + if (bases != NULL) { /* We have no guarantee that bases is a real tuple */ Py_ssize_t i, n; n = PySequence_Size(bases); /* This better be right */ - if (n < 0) - PyErr_Clear(); + if (n < 0) { + Py_DECREF(bases); + return -1; + } else { for (i = 0; i < n; i++) { int status; @@ -4725,9 +4729,10 @@ object___dir___impl(PyObject *self) PyObject *itsclass = NULL; /* Get __dict__ (which may or may not be a real dict...) */ - dict = _PyObject_GetAttrId(self, &PyId___dict__); + if (_PyObject_LookupAttrId(self, &PyId___dict__, &dict) < 0) { + return NULL; + } if (dict == NULL) { - PyErr_Clear(); dict = PyDict_New(); } else if (!PyDict_Check(dict)) { @@ -4745,12 +4750,12 @@ object___dir___impl(PyObject *self) goto error; /* Merge in attrs reachable from its class. */ - itsclass = _PyObject_GetAttrId(self, &PyId___class__); - if (itsclass == NULL) - /* XXX(tomer): Perhaps fall back to obj->ob_type if no - __class__ exists? */ - PyErr_Clear(); - else if (merge_class_dict(dict, itsclass) != 0) + if (_PyObject_LookupAttrId(self, &PyId___class__, &itsclass) < 0) { + goto error; + } + /* XXX(tomer): Perhaps fall back to obj->ob_type if no + __class__ exists? */ + if (itsclass != NULL && merge_class_dict(dict, itsclass) < 0) goto error; result = PyDict_Keys(dict); @@ -6105,16 +6110,19 @@ method_is_overloaded(PyObject *left, PyObject *right, struct _Py_Identifier *nam PyObject *a, *b; int ok; - b = _PyObject_GetAttrId((PyObject *)(Py_TYPE(right)), name); + if (_PyObject_LookupAttrId((PyObject *)(Py_TYPE(right)), name, &b) < 0) { + return -1; + } if (b == NULL) { - PyErr_Clear(); /* If right doesn't have it, it's not overloaded */ return 0; } - a = _PyObject_GetAttrId((PyObject *)(Py_TYPE(left)), name); + if (_PyObject_LookupAttrId((PyObject *)(Py_TYPE(left)), name, &a) < 0) { + Py_DECREF(b); + return -1; + } if (a == NULL) { - PyErr_Clear(); Py_DECREF(b); /* If right has it but left doesn't, it's overloaded */ return 1; @@ -6123,11 +6131,6 @@ method_is_overloaded(PyObject *left, PyObject *right, struct _Py_Identifier *nam ok = PyObject_RichCompareBool(a, b, Py_NE); Py_DECREF(a); Py_DECREF(b); - if (ok < 0) { - PyErr_Clear(); - return 0; - } - return ok; } @@ -6145,15 +6148,19 @@ FUNCNAME(PyObject *self, PyObject *other) \ if (Py_TYPE(self)->tp_as_number != NULL && \ Py_TYPE(self)->tp_as_number->SLOTNAME == TESTFUNC) { \ PyObject *r; \ - if (do_other && \ - PyType_IsSubtype(Py_TYPE(other), Py_TYPE(self)) && \ - method_is_overloaded(self, other, &rop_id)) { \ - stack[0] = self; \ - r = call_maybe(other, &rop_id, stack, 1); \ - if (r != Py_NotImplemented) \ - return r; \ - Py_DECREF(r); \ - do_other = 0; \ + if (do_other && PyType_IsSubtype(Py_TYPE(other), Py_TYPE(self))) { \ + int ok = method_is_overloaded(self, other, &rop_id); \ + if (ok < 0) { \ + return NULL; \ + } \ + if (ok) { \ + stack[0] = self; \ + r = call_maybe(other, &rop_id, stack, 1); \ + if (r != Py_NotImplemented) \ + return r; \ + Py_DECREF(r); \ + do_other = 0; \ + } \ } \ stack[0] = other; \ r = call_maybe(self, &op_id, stack, 1); \ @@ -7739,7 +7746,9 @@ supercheck(PyTypeObject *type, PyObject *obj) /* Try the slow way */ PyObject *class_attr; - class_attr = _PyObject_GetAttrId(obj, &PyId___class__); + if (_PyObject_LookupAttrId(obj, &PyId___class__, &class_attr) < 0) { + return NULL; + } if (class_attr != NULL && PyType_Check(class_attr) && (PyTypeObject *)class_attr != Py_TYPE(obj)) @@ -7749,11 +7758,7 @@ supercheck(PyTypeObject *type, PyObject *obj) if (ok) return (PyTypeObject *)class_attr; } - - if (class_attr == NULL) - PyErr_Clear(); - else - Py_DECREF(class_attr); + Py_XDECREF(class_attr); } PyErr_SetString(PyExc_TypeError, diff --git a/Objects/weakrefobject.c b/Objects/weakrefobject.c index 8b8e71031afa..4b81450de83f 100644 --- a/Objects/weakrefobject.c +++ b/Objects/weakrefobject.c @@ -163,10 +163,10 @@ weakref_repr(PyWeakReference *self) if (PyWeakref_GET_OBJECT(self) == Py_None) return PyUnicode_FromFormat("", self); - name = _PyObject_GetAttrId(PyWeakref_GET_OBJECT(self), &PyId___name__); + if (_PyObject_LookupAttrId(PyWeakref_GET_OBJECT(self), &PyId___name__, &name) < 0) { + return NULL; + } if (name == NULL || !PyUnicode_Check(name)) { - if (name == NULL) - PyErr_Clear(); repr = PyUnicode_FromFormat( "", self, diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index b85bfb2b91e3..8f9e81329c50 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -2256,16 +2256,12 @@ builtin_vars(PyObject *self, PyObject *args) return NULL; if (v == NULL) { d = PyEval_GetLocals(); - if (d == NULL) - return NULL; - Py_INCREF(d); + Py_XINCREF(d); } else { - d = _PyObject_GetAttrId(v, &PyId___dict__); - if (d == NULL) { + if (_PyObject_LookupAttrId(v, &PyId___dict__, &d) == 0) { PyErr_SetString(PyExc_TypeError, "vars() argument must have __dict__ attribute"); - return NULL; } } return d; diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 93ffce2a4783..11c20cf3c713 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -201,16 +201,12 @@ PySys_Audit(const char *event, const char *argFormat, ...) ts->tracing++; ts->use_tracing = 0; while ((hook = PyIter_Next(hooks)) != NULL) { + _Py_IDENTIFIER(__cantrace__); PyObject *o; - int canTrace = -1; - o = PyObject_GetAttrString(hook, "__cantrace__"); + int canTrace = _PyObject_LookupAttrId(hook, &PyId___cantrace__, &o); if (o) { canTrace = PyObject_IsTrue(o); Py_DECREF(o); - } else if (PyErr_Occurred() && - PyErr_ExceptionMatches(PyExc_AttributeError)) { - PyErr_Clear(); - canTrace = 0; } if (canTrace < 0) { break; @@ -538,7 +534,10 @@ sys_displayhook_unencodable(PyObject *outf, PyObject *o) if (encoded == NULL) goto error; - buffer = _PyObject_GetAttrId(outf, &PyId_buffer); + if (_PyObject_LookupAttrId(outf, &PyId_buffer, &buffer) < 0) { + Py_DECREF(encoded); + goto error; + } if (buffer) { result = _PyObject_CallMethodIdObjArgs(buffer, &PyId_write, encoded, NULL); Py_DECREF(buffer); @@ -548,7 +547,6 @@ sys_displayhook_unencodable(PyObject *outf, PyObject *o) Py_DECREF(result); } else { - PyErr_Clear(); escaped_str = PyUnicode_FromEncodedObject(encoded, stdout_encoding_str, "strict"); From webhook-mailer at python.org Mon Sep 2 11:08:07 2019 From: webhook-mailer at python.org (Jason R. Coombs) Date: Mon, 02 Sep 2019 15:08:07 -0000 Subject: [Python-checkins] bpo-38010 Sync importlib.metadata with importlib_metadata 0.20. (GH-15646) Message-ID: https://github.com/python/cpython/commit/102e9b40ff6ee45086a5f0d34d9c60c581a1e5e5 commit: 102e9b40ff6ee45086a5f0d34d9c60c581a1e5e5 branch: master author: Jason R. Coombs committer: GitHub date: 2019-09-02T11:08:03-04:00 summary: bpo-38010 Sync importlib.metadata with importlib_metadata 0.20. (GH-15646) Sync importlib.metadata with importlib_metadata 0.20. files: A Misc/NEWS.d/next/Library/2019-09-02-14-30-39.bpo-38010.JOnz9Z.rst M Doc/library/importlib.metadata.rst M Lib/importlib/metadata.py M Lib/test/test_importlib/test_metadata_api.py diff --git a/Doc/library/importlib.metadata.rst b/Doc/library/importlib.metadata.rst index 2126498e728c..23c8cdeb21c2 100644 --- a/Doc/library/importlib.metadata.rst +++ b/Doc/library/importlib.metadata.rst @@ -158,6 +158,13 @@ Once you have the file, you can also read its contents:: return s.encode('utf-8') return s +In the case where the metadata file listing files +(RECORD or SOURCES.txt) is missing, ``files()`` will +return ``None``. The caller may wish to wrap calls to +``files()`` in `always_iterable +`_ +or otherwise guard against this condition if the target +distribution is not known to have the metadata present. .. _requirements: diff --git a/Lib/importlib/metadata.py b/Lib/importlib/metadata.py index e80f4fa6409b..3b46142231ec 100644 --- a/Lib/importlib/metadata.py +++ b/Lib/importlib/metadata.py @@ -213,6 +213,15 @@ def entry_points(self): @property def files(self): + """Files in this distribution. + + :return: Iterable of PackagePath for this distribution or None + + Result is `None` if the metadata file that enumerates files + (i.e. RECORD for dist-info or SOURCES.txt for egg-info) is + missing. + Result may be empty if the metadata exists but is empty. + """ file_lines = self._read_files_distinfo() or self._read_files_egginfo() def make_file(name, hash=None, size_str=None): @@ -245,8 +254,7 @@ def requires(self): return self._read_dist_info_reqs() or self._read_egg_info_reqs() def _read_dist_info_reqs(self): - spec = self.metadata['Requires-Dist'] - return spec and filter(None, spec.splitlines()) + return self.metadata.get_all('Requires-Dist') def _read_egg_info_reqs(self): source = self.read_text('requires.txt') @@ -318,7 +326,11 @@ def find_distributions(self, name=None, path=None): class PathDistribution(Distribution): def __init__(self, path): - """Construct a distribution from a path to the metadata directory.""" + """Construct a distribution from a path to the metadata directory. + + :param path: A pathlib.Path or similar object supporting + .joinpath(), __div__, .parent, and .read_text(). + """ self._path = path def read_text(self, filename): diff --git a/Lib/test/test_importlib/test_metadata_api.py b/Lib/test/test_importlib/test_metadata_api.py index 899777f4b1ad..af3bab3558ef 100644 --- a/Lib/test/test_importlib/test_metadata_api.py +++ b/Lib/test/test_importlib/test_metadata_api.py @@ -109,6 +109,8 @@ def test_requires(self): def test_requires_dist_info(self): deps = list(requires('distinfo-pkg')) assert deps and all(deps) + assert 'wheel >= 1.0' in deps + assert "pytest; extra == 'test'" in deps def test_more_complex_deps_requires_text(self): requires = textwrap.dedent(""" diff --git a/Misc/NEWS.d/next/Library/2019-09-02-14-30-39.bpo-38010.JOnz9Z.rst b/Misc/NEWS.d/next/Library/2019-09-02-14-30-39.bpo-38010.JOnz9Z.rst new file mode 100644 index 000000000000..a97f9b7d9a40 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-09-02-14-30-39.bpo-38010.JOnz9Z.rst @@ -0,0 +1 @@ +In ``importlib.metadata`` sync with ``importlib_metadata`` 0.20, clarifying behavior of ``files()`` and fixing issue where only one requirement was returned for ``requires()`` on ``dist-info`` packages. From webhook-mailer at python.org Mon Sep 2 12:01:28 2019 From: webhook-mailer at python.org (Jason R. Coombs) Date: Mon, 02 Sep 2019 16:01:28 -0000 Subject: [Python-checkins] bpo-36853: Fix suspicious.py to actually print the unused rules (#13579) Message-ID: https://github.com/python/cpython/commit/e1786b54162e2bfb01ca5aafa19d596c4af5a803 commit: e1786b54162e2bfb01ca5aafa19d596c4af5a803 branch: master author: Anthony Sottile committer: Jason R. Coombs date: 2019-09-02T12:01:23-04:00 summary: bpo-36853: Fix suspicious.py to actually print the unused rules (#13579) * Fix suspicious.py to actually print the unused rules * Fix the other `self.warn` calls files: M Doc/tools/extensions/suspicious.py diff --git a/Doc/tools/extensions/suspicious.py b/Doc/tools/extensions/suspicious.py index 34a0112f5a00..9e814fb94d2b 100644 --- a/Doc/tools/extensions/suspicious.py +++ b/Doc/tools/extensions/suspicious.py @@ -115,10 +115,12 @@ def write_doc(self, docname, doctree): def finish(self): unused_rules = [rule for rule in self.rules if not rule.used] if unused_rules: - self.logger.warn('Found %s/%s unused rules:' % - (len(unused_rules), len(self.rules))) - for rule in unused_rules: - self.logger.info(repr(rule)) + self.logger.warning( + 'Found %s/%s unused rules: %s' % ( + len(unused_rules), len(self.rules), + ''.join(repr(rule) for rule in unused_rules), + ) + ) return def check_issue(self, line, lineno, issue): @@ -151,14 +153,15 @@ def report_issue(self, text, lineno, issue): self.any_issue = True self.write_log_entry(lineno, issue, text) if py3: - self.logger.warn('[%s:%d] "%s" found in "%-.120s"' % - (self.docname, lineno, issue, text)) + self.logger.warning('[%s:%d] "%s" found in "%-.120s"' % + (self.docname, lineno, issue, text)) else: - self.logger.warn('[%s:%d] "%s" found in "%-.120s"' % ( - self.docname.encode(sys.getdefaultencoding(),'replace'), - lineno, - issue.encode(sys.getdefaultencoding(),'replace'), - text.strip().encode(sys.getdefaultencoding(),'replace'))) + self.logger.warning( + '[%s:%d] "%s" found in "%-.120s"' % ( + self.docname.encode(sys.getdefaultencoding(),'replace'), + lineno, + issue.encode(sys.getdefaultencoding(),'replace'), + text.strip().encode(sys.getdefaultencoding(),'replace'))) self.app.statuscode = 1 def write_log_entry(self, lineno, issue, text): From webhook-mailer at python.org Mon Sep 2 12:11:07 2019 From: webhook-mailer at python.org (Jason R. Coombs) Date: Mon, 02 Sep 2019 16:11:07 -0000 Subject: [Python-checkins] bpo-38010 Sync importlib.metadata with importlib_metadata 0.20. (GH-15646) (GH-15648) Message-ID: https://github.com/python/cpython/commit/bf69e160c431dc40d6530d427cec71c6a97522f5 commit: bf69e160c431dc40d6530d427cec71c6a97522f5 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Jason R. Coombs date: 2019-09-02T12:11:01-04:00 summary: bpo-38010 Sync importlib.metadata with importlib_metadata 0.20. (GH-15646) (GH-15648) Sync importlib.metadata with importlib_metadata 0.20. (cherry picked from commit 102e9b40ff6ee45086a5f0d34d9c60c581a1e5e5) Co-authored-by: Jason R. Coombs files: A Misc/NEWS.d/next/Library/2019-09-02-14-30-39.bpo-38010.JOnz9Z.rst M Doc/library/importlib.metadata.rst M Lib/importlib/metadata.py M Lib/test/test_importlib/test_metadata_api.py diff --git a/Doc/library/importlib.metadata.rst b/Doc/library/importlib.metadata.rst index 2126498e728c..23c8cdeb21c2 100644 --- a/Doc/library/importlib.metadata.rst +++ b/Doc/library/importlib.metadata.rst @@ -158,6 +158,13 @@ Once you have the file, you can also read its contents:: return s.encode('utf-8') return s +In the case where the metadata file listing files +(RECORD or SOURCES.txt) is missing, ``files()`` will +return ``None``. The caller may wish to wrap calls to +``files()`` in `always_iterable +`_ +or otherwise guard against this condition if the target +distribution is not known to have the metadata present. .. _requirements: diff --git a/Lib/importlib/metadata.py b/Lib/importlib/metadata.py index e80f4fa6409b..3b46142231ec 100644 --- a/Lib/importlib/metadata.py +++ b/Lib/importlib/metadata.py @@ -213,6 +213,15 @@ def entry_points(self): @property def files(self): + """Files in this distribution. + + :return: Iterable of PackagePath for this distribution or None + + Result is `None` if the metadata file that enumerates files + (i.e. RECORD for dist-info or SOURCES.txt for egg-info) is + missing. + Result may be empty if the metadata exists but is empty. + """ file_lines = self._read_files_distinfo() or self._read_files_egginfo() def make_file(name, hash=None, size_str=None): @@ -245,8 +254,7 @@ def requires(self): return self._read_dist_info_reqs() or self._read_egg_info_reqs() def _read_dist_info_reqs(self): - spec = self.metadata['Requires-Dist'] - return spec and filter(None, spec.splitlines()) + return self.metadata.get_all('Requires-Dist') def _read_egg_info_reqs(self): source = self.read_text('requires.txt') @@ -318,7 +326,11 @@ def find_distributions(self, name=None, path=None): class PathDistribution(Distribution): def __init__(self, path): - """Construct a distribution from a path to the metadata directory.""" + """Construct a distribution from a path to the metadata directory. + + :param path: A pathlib.Path or similar object supporting + .joinpath(), __div__, .parent, and .read_text(). + """ self._path = path def read_text(self, filename): diff --git a/Lib/test/test_importlib/test_metadata_api.py b/Lib/test/test_importlib/test_metadata_api.py index 899777f4b1ad..af3bab3558ef 100644 --- a/Lib/test/test_importlib/test_metadata_api.py +++ b/Lib/test/test_importlib/test_metadata_api.py @@ -109,6 +109,8 @@ def test_requires(self): def test_requires_dist_info(self): deps = list(requires('distinfo-pkg')) assert deps and all(deps) + assert 'wheel >= 1.0' in deps + assert "pytest; extra == 'test'" in deps def test_more_complex_deps_requires_text(self): requires = textwrap.dedent(""" diff --git a/Misc/NEWS.d/next/Library/2019-09-02-14-30-39.bpo-38010.JOnz9Z.rst b/Misc/NEWS.d/next/Library/2019-09-02-14-30-39.bpo-38010.JOnz9Z.rst new file mode 100644 index 000000000000..a97f9b7d9a40 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-09-02-14-30-39.bpo-38010.JOnz9Z.rst @@ -0,0 +1 @@ +In ``importlib.metadata`` sync with ``importlib_metadata`` 0.20, clarifying behavior of ``files()`` and fixing issue where only one requirement was returned for ``requires()`` on ``dist-info`` packages. From webhook-mailer at python.org Mon Sep 2 12:12:23 2019 From: webhook-mailer at python.org (Jason R. Coombs) Date: Mon, 02 Sep 2019 16:12:23 -0000 Subject: [Python-checkins] bpo-36853: Fix suspicious.py to actually print the unused rules (GH-13579) (GH-15649) Message-ID: https://github.com/python/cpython/commit/b365cfae4675ae90df329cb1179a5664e8283c13 commit: b365cfae4675ae90df329cb1179a5664e8283c13 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Jason R. Coombs date: 2019-09-02T12:12:19-04:00 summary: bpo-36853: Fix suspicious.py to actually print the unused rules (GH-13579) (GH-15649) * Fix suspicious.py to actually print the unused rules * Fix the other `self.warn` calls (cherry picked from commit e1786b54162e2bfb01ca5aafa19d596c4af5a803) Co-authored-by: Anthony Sottile files: M Doc/tools/extensions/suspicious.py diff --git a/Doc/tools/extensions/suspicious.py b/Doc/tools/extensions/suspicious.py index 34a0112f5a00..9e814fb94d2b 100644 --- a/Doc/tools/extensions/suspicious.py +++ b/Doc/tools/extensions/suspicious.py @@ -115,10 +115,12 @@ def write_doc(self, docname, doctree): def finish(self): unused_rules = [rule for rule in self.rules if not rule.used] if unused_rules: - self.logger.warn('Found %s/%s unused rules:' % - (len(unused_rules), len(self.rules))) - for rule in unused_rules: - self.logger.info(repr(rule)) + self.logger.warning( + 'Found %s/%s unused rules: %s' % ( + len(unused_rules), len(self.rules), + ''.join(repr(rule) for rule in unused_rules), + ) + ) return def check_issue(self, line, lineno, issue): @@ -151,14 +153,15 @@ def report_issue(self, text, lineno, issue): self.any_issue = True self.write_log_entry(lineno, issue, text) if py3: - self.logger.warn('[%s:%d] "%s" found in "%-.120s"' % - (self.docname, lineno, issue, text)) + self.logger.warning('[%s:%d] "%s" found in "%-.120s"' % + (self.docname, lineno, issue, text)) else: - self.logger.warn('[%s:%d] "%s" found in "%-.120s"' % ( - self.docname.encode(sys.getdefaultencoding(),'replace'), - lineno, - issue.encode(sys.getdefaultencoding(),'replace'), - text.strip().encode(sys.getdefaultencoding(),'replace'))) + self.logger.warning( + '[%s:%d] "%s" found in "%-.120s"' % ( + self.docname.encode(sys.getdefaultencoding(),'replace'), + lineno, + issue.encode(sys.getdefaultencoding(),'replace'), + text.strip().encode(sys.getdefaultencoding(),'replace'))) self.app.statuscode = 1 def write_log_entry(self, lineno, issue, text): From webhook-mailer at python.org Mon Sep 2 15:17:29 2019 From: webhook-mailer at python.org (Jason R. Coombs) Date: Mon, 02 Sep 2019 19:17:29 -0000 Subject: [Python-checkins] bpo-36853: Fix suspicious.py to actually print the unused rules (#13579) (#15653) Message-ID: https://github.com/python/cpython/commit/ebe709dc1d7c1f9f07dc7d77e53674d2500b223e commit: ebe709dc1d7c1f9f07dc7d77e53674d2500b223e branch: 3.7 author: Anthony Sottile committer: Jason R. Coombs date: 2019-09-02T15:17:18-04:00 summary: bpo-36853: Fix suspicious.py to actually print the unused rules (#13579) (#15653) * Fix suspicious.py to actually print the unused rules * Fix the other `self.warn` calls (cherry picked from commit e1786b54162e2bfb01ca5aafa19d596c4af5a803) files: M Doc/tools/extensions/suspicious.py diff --git a/Doc/tools/extensions/suspicious.py b/Doc/tools/extensions/suspicious.py index 8d80f6759bff..fd50f318170b 100644 --- a/Doc/tools/extensions/suspicious.py +++ b/Doc/tools/extensions/suspicious.py @@ -115,10 +115,12 @@ def write_doc(self, docname, doctree): def finish(self): unused_rules = [rule for rule in self.rules if not rule.used] if unused_rules: - self.warn('Found %s/%s unused rules:' % - (len(unused_rules), len(self.rules))) - for rule in unused_rules: - self.logger.info(repr(rule)) + self.logger.warning( + 'Found %s/%s unused rules: %s' % ( + len(unused_rules), len(self.rules), + ''.join(repr(rule) for rule in unused_rules), + ) + ) return def check_issue(self, line, lineno, issue): @@ -152,14 +154,15 @@ def report_issue(self, text, lineno, issue): self.any_issue = True self.write_log_entry(lineno, issue, text) if py3: - self.warn('[%s:%d] "%s" found in "%-.120s"' % - (self.docname, lineno, issue, text)) + self.logger.warning('[%s:%d] "%s" found in "%-.120s"' % + (self.docname, lineno, issue, text)) else: - self.warn('[%s:%d] "%s" found in "%-.120s"' % ( - self.docname.encode(sys.getdefaultencoding(),'replace'), - lineno, - issue.encode(sys.getdefaultencoding(),'replace'), - text.strip().encode(sys.getdefaultencoding(),'replace'))) + self.logger.warning( + '[%s:%d] "%s" found in "%-.120s"' % ( + self.docname.encode(sys.getdefaultencoding(),'replace'), + lineno, + issue.encode(sys.getdefaultencoding(),'replace'), + text.strip().encode(sys.getdefaultencoding(),'replace'))) self.app.statuscode = 1 def write_log_entry(self, lineno, issue, text): From webhook-mailer at python.org Tue Sep 3 00:21:38 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Tue, 03 Sep 2019 04:21:38 -0000 Subject: [Python-checkins] Enforce PEP 257 conventions in ftplib.py (GH-15604) Message-ID: https://github.com/python/cpython/commit/efa3b51fd060352cc6220b27a1026e4d4d5401bd commit: efa3b51fd060352cc6220b27a1026e4d4d5401bd branch: master author: Alan Yee committer: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> date: 2019-09-02T21:21:33-07:00 summary: Enforce PEP 257 conventions in ftplib.py (GH-15604) -`"""` over `'''` -no blank line either before or after the docstring. -place the closing quotes on a line by themselves files: M Lib/ftplib.py diff --git a/Lib/ftplib.py b/Lib/ftplib.py index 58a46bca4a36..c339eb25bc2d 100644 --- a/Lib/ftplib.py +++ b/Lib/ftplib.py @@ -72,7 +72,6 @@ class error_proto(Error): pass # response does not begin with [1-5] # The class itself class FTP: - '''An FTP client class. To create a connection, call the class using these arguments: @@ -105,12 +104,13 @@ class FTP: passiveserver = 1 encoding = "latin-1" - # Initialization method (called by class instantiation). - # Initialize host to localhost, port to standard ftp port - # Optional arguments are host (for connect()), - # and user, passwd, acct (for login()) def __init__(self, host='', user='', passwd='', acct='', timeout=_GLOBAL_DEFAULT_TIMEOUT, source_address=None): + """Initialization method (called by class instantiation). + Initialize host to localhost, port to standard ftp port. + Optional arguments are host (for connect()), + and user, passwd, acct (for login()). + """ self.source_address = source_address self.timeout = timeout if host: @@ -823,7 +823,6 @@ def parse227(resp): '''Parse the '227' response for a PASV request. Raises error_proto if it does not contain '(h1,h2,h3,h4,p1,p2)' Return ('host.addr.as.numbers', port#) tuple.''' - if resp[:3] != '227': raise error_reply(resp) global _227_re @@ -843,7 +842,6 @@ def parse229(resp, peer): '''Parse the '229' response for an EPSV request. Raises error_proto if it does not contain '(|||port|)' Return ('host.addr.as.numbers', port#) tuple.''' - if resp[:3] != '229': raise error_reply(resp) left = resp.find('(') @@ -865,7 +863,6 @@ def parse257(resp): '''Parse the '257' response for a MKD or PWD request. This is a response to a MKD or PWD request: a directory name. Returns the directoryname in the 257 reply.''' - if resp[:3] != '257': raise error_reply(resp) if resp[3:5] != ' "': From webhook-mailer at python.org Tue Sep 3 01:17:04 2019 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Tue, 03 Sep 2019 05:17:04 -0000 Subject: [Python-checkins] bpo-35771: IDLE: Fix flaky tool-tip hover delay tests (GH-15634) Message-ID: https://github.com/python/cpython/commit/132acaba5a7f01373ca624b1a5975b190fe866f5 commit: 132acaba5a7f01373ca624b1a5975b190fe866f5 branch: master author: Tal Einat committer: Terry Jan Reedy date: 2019-09-03T01:17:00-04:00 summary: bpo-35771: IDLE: Fix flaky tool-tip hover delay tests (GH-15634) Extending the hover delay in test_tooltip should avoid spurious test_idle failures. One longer delay instead of two shorter delays results in a net speedup. files: A Misc/NEWS.d/next/IDLE/2019-09-01-10-22-55.bpo-35771.tdbmbP.rst M Lib/idlelib/NEWS.txt M Lib/idlelib/idle_test/test_tooltip.py M Lib/idlelib/tooltip.py diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt index 949b30bd99ca..47c2291d2377 100644 --- a/Lib/idlelib/NEWS.txt +++ b/Lib/idlelib/NEWS.txt @@ -3,6 +3,9 @@ Released on 2019-10-20? ====================================== +bpo-35771: To avoid occasional spurious test_idle failures on slower +machines, increase the ``hover_delay`` in test_tooltip. + bpo-37824: Properly handle user input warnings in IDLE shell. Cease turning SyntaxWarnings into SyntaxErrors. diff --git a/Lib/idlelib/idle_test/test_tooltip.py b/Lib/idlelib/idle_test/test_tooltip.py index 44ea1110e155..c616d4fde3b6 100644 --- a/Lib/idlelib/idle_test/test_tooltip.py +++ b/Lib/idlelib/idle_test/test_tooltip.py @@ -1,3 +1,10 @@ +"""Test tooltip, coverage 100%. + +Coverage is 100% after excluding 6 lines with "# pragma: no cover". +They involve TclErrors that either should or should not happen in a +particular situation, and which are 'pass'ed if they do. +""" + from idlelib.tooltip import TooltipBase, Hovertip from test.support import requires requires('gui') @@ -12,16 +19,13 @@ def setUpModule(): global root root = Tk() -def root_update(): - global root - root.update() - def tearDownModule(): global root root.update_idletasks() root.destroy() del root + def add_call_counting(func): @wraps(func) def wrapped_func(*args, **kwargs): @@ -65,22 +69,25 @@ class HovertipTest(unittest.TestCase): def setUp(self): self.top, self.button = _make_top_and_button(self) + def is_tipwindow_shown(self, tooltip): + return tooltip.tipwindow and tooltip.tipwindow.winfo_viewable() + def test_showtip(self): tooltip = Hovertip(self.button, 'ToolTip text') self.addCleanup(tooltip.hidetip) - self.assertFalse(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable()) + self.assertFalse(self.is_tipwindow_shown(tooltip)) tooltip.showtip() - self.assertTrue(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable()) + self.assertTrue(self.is_tipwindow_shown(tooltip)) def test_showtip_twice(self): tooltip = Hovertip(self.button, 'ToolTip text') self.addCleanup(tooltip.hidetip) - self.assertFalse(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable()) + self.assertFalse(self.is_tipwindow_shown(tooltip)) tooltip.showtip() - self.assertTrue(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable()) + self.assertTrue(self.is_tipwindow_shown(tooltip)) orig_tipwindow = tooltip.tipwindow tooltip.showtip() - self.assertTrue(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable()) + self.assertTrue(self.is_tipwindow_shown(tooltip)) self.assertIs(tooltip.tipwindow, orig_tipwindow) def test_hidetip(self): @@ -88,59 +95,67 @@ def test_hidetip(self): self.addCleanup(tooltip.hidetip) tooltip.showtip() tooltip.hidetip() - self.assertFalse(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable()) + self.assertFalse(self.is_tipwindow_shown(tooltip)) def test_showtip_on_mouse_enter_no_delay(self): tooltip = Hovertip(self.button, 'ToolTip text', hover_delay=None) self.addCleanup(tooltip.hidetip) tooltip.showtip = add_call_counting(tooltip.showtip) - root_update() - self.assertFalse(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable()) + root.update() + self.assertFalse(self.is_tipwindow_shown(tooltip)) self.button.event_generate('', x=0, y=0) - root_update() - self.assertTrue(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable()) + root.update() + self.assertTrue(self.is_tipwindow_shown(tooltip)) self.assertGreater(len(tooltip.showtip.call_args_list), 0) - def test_showtip_on_mouse_enter_hover_delay(self): - tooltip = Hovertip(self.button, 'ToolTip text', hover_delay=50) - self.addCleanup(tooltip.hidetip) - tooltip.showtip = add_call_counting(tooltip.showtip) - root_update() - self.assertFalse(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable()) + def test_hover_with_delay(self): + # Run multiple tests requiring an actual delay simultaneously. + + # Test #1: A hover tip with a non-zero delay appears after the delay. + tooltip1 = Hovertip(self.button, 'ToolTip text', hover_delay=100) + self.addCleanup(tooltip1.hidetip) + tooltip1.showtip = add_call_counting(tooltip1.showtip) + root.update() + self.assertFalse(self.is_tipwindow_shown(tooltip1)) self.button.event_generate('', x=0, y=0) - root_update() - self.assertFalse(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable()) - time.sleep(0.1) - root_update() - self.assertTrue(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable()) - self.assertGreater(len(tooltip.showtip.call_args_list), 0) + root.update() + self.assertFalse(self.is_tipwindow_shown(tooltip1)) + + # Test #2: A hover tip with a non-zero delay doesn't appear when + # the mouse stops hovering over the base widget before the delay + # expires. + tooltip2 = Hovertip(self.button, 'ToolTip text', hover_delay=100) + self.addCleanup(tooltip2.hidetip) + tooltip2.showtip = add_call_counting(tooltip2.showtip) + root.update() + self.button.event_generate('', x=0, y=0) + root.update() + self.button.event_generate('', x=0, y=0) + root.update() + + time.sleep(0.15) + root.update() + + # Test #1 assertions. + self.assertTrue(self.is_tipwindow_shown(tooltip1)) + self.assertGreater(len(tooltip1.showtip.call_args_list), 0) + + # Test #2 assertions. + self.assertFalse(self.is_tipwindow_shown(tooltip2)) + self.assertEqual(tooltip2.showtip.call_args_list, []) def test_hidetip_on_mouse_leave(self): tooltip = Hovertip(self.button, 'ToolTip text', hover_delay=None) self.addCleanup(tooltip.hidetip) tooltip.showtip = add_call_counting(tooltip.showtip) - root_update() + root.update() self.button.event_generate('', x=0, y=0) - root_update() + root.update() self.button.event_generate('', x=0, y=0) - root_update() - self.assertFalse(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable()) + root.update() + self.assertFalse(self.is_tipwindow_shown(tooltip)) self.assertGreater(len(tooltip.showtip.call_args_list), 0) - def test_dont_show_on_mouse_leave_before_delay(self): - tooltip = Hovertip(self.button, 'ToolTip text', hover_delay=50) - self.addCleanup(tooltip.hidetip) - tooltip.showtip = add_call_counting(tooltip.showtip) - root_update() - self.button.event_generate('', x=0, y=0) - root_update() - self.button.event_generate('', x=0, y=0) - root_update() - time.sleep(0.1) - root_update() - self.assertFalse(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable()) - self.assertEqual(tooltip.showtip.call_args_list, []) - if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/Lib/idlelib/tooltip.py b/Lib/idlelib/tooltip.py index f54ea36f059d..69658264dbd4 100644 --- a/Lib/idlelib/tooltip.py +++ b/Lib/idlelib/tooltip.py @@ -75,7 +75,7 @@ def hidetip(self): if tw: try: tw.destroy() - except TclError: + except TclError: # pragma: no cover pass @@ -103,8 +103,8 @@ def __init__(self, anchor_widget, hover_delay=1000): def __del__(self): try: self.anchor_widget.unbind("", self._id1) - self.anchor_widget.unbind("", self._id2) - self.anchor_widget.unbind("