From python-checkins at python.org Fri Jun 1 00:01:31 2012 From: python-checkins at python.org (r.david.murray) Date: Fri, 01 Jun 2012 00:01:31 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Don=27t_use_metaclasses_whe?= =?utf8?q?n_class_decorators_can_do_the_job=2E?= Message-ID: http://hg.python.org/cpython/rev/01b72be1ce0c changeset: 77273:01b72be1ce0c user: R David Murray date: Thu May 31 18:00:45 2012 -0400 summary: Don't use metaclasses when class decorators can do the job. Thanks to Nick Coghlan for pointing out that I'd forgotten about class decorators. files: Lib/email/_policybase.py | 39 ++--- Lib/email/policy.py | 3 +- Lib/test/test_email/__init__.py | 66 ++++----- Lib/test/test_email/test_generator.py | 5 +- Lib/test/test_email/test_headerregistry.py | 5 +- Lib/test/test_email/test_pickleable.py | 9 +- 6 files changed, 63 insertions(+), 64 deletions(-) diff --git a/Lib/email/_policybase.py b/Lib/email/_policybase.py --- a/Lib/email/_policybase.py +++ b/Lib/email/_policybase.py @@ -91,31 +91,25 @@ return self.clone(**other.__dict__) -# Conceptually this isn't a subclass of ABCMeta, but since we want Policy to -# use ABCMeta as a metaclass *and* we want it to use this one as well, we have -# to make this one a subclas of ABCMeta. -class _DocstringExtenderMetaclass(abc.ABCMeta): +def _append_doc(doc, added_doc): + doc = doc.rsplit('\n', 1)[0] + added_doc = added_doc.split('\n', 1)[1] + return doc + '\n' + added_doc - def __new__(meta, classname, bases, classdict): - if classdict.get('__doc__') and classdict['__doc__'].startswith('+'): - classdict['__doc__'] = meta._append_doc(bases[0].__doc__, - classdict['__doc__']) - for name, attr in classdict.items(): - if attr.__doc__ and attr.__doc__.startswith('+'): - for cls in (cls for base in bases for cls in base.mro()): - doc = getattr(getattr(cls, name), '__doc__') - if doc: - attr.__doc__ = meta._append_doc(doc, attr.__doc__) - break - return super().__new__(meta, classname, bases, classdict) +def _extend_docstrings(cls): + if cls.__doc__ and cls.__doc__.startswith('+'): + cls.__doc__ = _append_doc(cls.__bases__[0].__doc__, cls.__doc__) + for name, attr in cls.__dict__.items(): + if attr.__doc__ and attr.__doc__.startswith('+'): + for c in (c for base in cls.__bases__ for c in base.mro()): + doc = getattr(getattr(c, name), '__doc__') + if doc: + attr.__doc__ = _append_doc(doc, attr.__doc__) + break + return cls - @staticmethod - def _append_doc(doc, added_doc): - added_doc = added_doc.split('\n', 1)[1] - return doc + '\n' + added_doc - -class Policy(_PolicyBase, metaclass=_DocstringExtenderMetaclass): +class Policy(_PolicyBase, metaclass=abc.ABCMeta): r"""Controls for how messages are interpreted and formatted. @@ -264,6 +258,7 @@ raise NotImplementedError + at _extend_docstrings class Compat32(Policy): """+ diff --git a/Lib/email/policy.py b/Lib/email/policy.py --- a/Lib/email/policy.py +++ b/Lib/email/policy.py @@ -2,7 +2,7 @@ code that adds all the email6 features. """ -from email._policybase import Policy, Compat32, compat32 +from email._policybase import Policy, Compat32, compat32, _extend_docstrings from email.utils import _has_surrogates from email.headerregistry import HeaderRegistry as HeaderRegistry @@ -17,6 +17,7 @@ 'HTTP', ] + at _extend_docstrings class EmailPolicy(Policy): """+ diff --git a/Lib/test/test_email/__init__.py b/Lib/test/test_email/__init__.py --- a/Lib/test/test_email/__init__.py +++ b/Lib/test/test_email/__init__.py @@ -73,10 +73,8 @@ 'item {}'.format(i)) -# Metaclass to allow for parameterized tests -class Parameterized(type): - - """Provide a test method parameterization facility. +def parameterize(cls): + """A test method parameterization class decorator. Parameters are specified as the value of a class attribute that ends with the string '_params'. Call the portion before '_params' the prefix. Then @@ -92,9 +90,10 @@ In a _params dictioanry, the keys become part of the name of the generated tests. In a _params list, the values in the list are converted into a string by joining the string values of the elements of the tuple by '_' and - converting any blanks into '_'s, and this become part of the name. The - full name of a generated test is the portion of the _params name before the - '_params' portion, plus an '_', plus the name derived as explained above. + converting any blanks into '_'s, and this become part of the name. + The full name of a generated test is a 'test_' prefix, the portion of the + test function name after the '_as_' separator, plus an '_', plus the name + derived as explained above. For example, if we have: @@ -123,30 +122,29 @@ be used to select the test individually from the unittest command line. """ - - def __new__(meta, classname, bases, classdict): - paramdicts = {} - for name, attr in classdict.items(): - if name.endswith('_params'): - if not hasattr(attr, 'keys'): - d = {} - for x in attr: - if not hasattr(x, '__iter__'): - x = (x,) - n = '_'.join(str(v) for v in x).replace(' ', '_') - d[n] = x - attr = d - paramdicts[name[:-7] + '_as_'] = attr - testfuncs = {} - for name, attr in classdict.items(): - for paramsname, paramsdict in paramdicts.items(): - if name.startswith(paramsname): - testnameroot = 'test_' + name[len(paramsname):] - for paramname, params in paramsdict.items(): - test = (lambda self, name=name, params=params: - getattr(self, name)(*params)) - testname = testnameroot + '_' + paramname - test.__name__ = testname - testfuncs[testname] = test - classdict.update(testfuncs) - return super().__new__(meta, classname, bases, classdict) + paramdicts = {} + for name, attr in cls.__dict__.items(): + if name.endswith('_params'): + if not hasattr(attr, 'keys'): + d = {} + for x in attr: + if not hasattr(x, '__iter__'): + x = (x,) + n = '_'.join(str(v) for v in x).replace(' ', '_') + d[n] = x + attr = d + paramdicts[name[:-7] + '_as_'] = attr + testfuncs = {} + for name, attr in cls.__dict__.items(): + for paramsname, paramsdict in paramdicts.items(): + if name.startswith(paramsname): + testnameroot = 'test_' + name[len(paramsname):] + for paramname, params in paramsdict.items(): + test = (lambda self, name=name, params=params: + getattr(self, name)(*params)) + testname = testnameroot + '_' + paramname + test.__name__ = testname + testfuncs[testname] = test + for key, value in testfuncs.items(): + setattr(cls, key, value) + return cls diff --git a/Lib/test/test_email/test_generator.py b/Lib/test/test_email/test_generator.py --- a/Lib/test/test_email/test_generator.py +++ b/Lib/test/test_email/test_generator.py @@ -4,10 +4,11 @@ from email import message_from_string, message_from_bytes from email.generator import Generator, BytesGenerator from email import policy -from test.test_email import TestEmailBase, Parameterized +from test.test_email import TestEmailBase, parameterize -class TestGeneratorBase(metaclass=Parameterized): + at parameterize +class TestGeneratorBase: policy = policy.default diff --git a/Lib/test/test_email/test_headerregistry.py b/Lib/test/test_email/test_headerregistry.py --- a/Lib/test/test_email/test_headerregistry.py +++ b/Lib/test/test_email/test_headerregistry.py @@ -4,7 +4,7 @@ from email import errors from email import policy from email.message import Message -from test.test_email import TestEmailBase, Parameterized +from test.test_email import TestEmailBase, parameterize from email import headerregistry from email.headerregistry import Address, Group @@ -175,7 +175,8 @@ self.assertEqual(m['Date'].datetime, self.dt) -class TestAddressHeader(TestHeaderBase, metaclass=Parameterized): + at parameterize +class TestAddressHeader(TestHeaderBase): example_params = { diff --git a/Lib/test/test_email/test_pickleable.py b/Lib/test/test_email/test_pickleable.py --- a/Lib/test/test_email/test_pickleable.py +++ b/Lib/test/test_email/test_pickleable.py @@ -6,9 +6,11 @@ import email.message from email import policy from email.headerregistry import HeaderRegistry -from test.test_email import TestEmailBase, Parameterized +from test.test_email import TestEmailBase, parameterize -class TestPickleCopyHeader(TestEmailBase, metaclass=Parameterized): + + at parameterize +class TestPickleCopyHeader(TestEmailBase): header_factory = HeaderRegistry() @@ -33,7 +35,8 @@ self.assertEqual(str(h), str(header)) -class TestPickleCopyMessage(TestEmailBase, metaclass=Parameterized): + at parameterize +class TestPickleCopyMessage(TestEmailBase): # Message objects are a sequence, so we have to make them a one-tuple in # msg_params so they get passed to the parameterized test method as a -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 1 00:21:41 2012 From: python-checkins at python.org (hynek.schlawack) Date: Fri, 01 Jun 2012 00:21:41 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_=2314814=3A_Remove_stale_?= =?utf8?q?=5F=5Fhex=5F=5F_method_from_ipaddress?= Message-ID: http://hg.python.org/cpython/rev/bd2c2def77a7 changeset: 77274:bd2c2def77a7 user: Hynek Schlawack date: Fri Jun 01 00:20:13 2012 +0200 summary: #14814: Remove stale __hex__ method from ipaddress Obsolete 2.x method. files: Lib/ipaddress.py | 3 --- 1 files changed, 0 insertions(+), 3 deletions(-) diff --git a/Lib/ipaddress.py b/Lib/ipaddress.py --- a/Lib/ipaddress.py +++ b/Lib/ipaddress.py @@ -482,9 +482,6 @@ def __int__(self): return self._ip - def __hex__(self): - return hex(self._ip) - def __eq__(self, other): try: return (self._ip == other._ip -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Fri Jun 1 05:51:32 2012 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Fri, 01 Jun 2012 05:51:32 +0200 Subject: [Python-checkins] Daily reference leaks (bd2c2def77a7): sum=465 Message-ID: results for bd2c2def77a7 on branch "default" -------------------------------------------- test_smtplib leaked [154, 154, 154] references, sum=462 test_super leaked [1, 1, 1] references, sum=3 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogSNO9GS', '-x'] From python-checkins at python.org Fri Jun 1 06:16:49 2012 From: python-checkins at python.org (eli.bendersky) Date: Fri, 01 Jun 2012 06:16:49 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Issue_=2314007=3A_make_XMLP?= =?utf8?q?arser_a_real_subclassable_type_exported_from?= Message-ID: http://hg.python.org/cpython/rev/a29ae1c2b8b2 changeset: 77275:a29ae1c2b8b2 user: Eli Bendersky date: Fri Jun 01 07:13:08 2012 +0300 summary: Issue #14007: make XMLParser a real subclassable type exported from _elementtree. +cleanups files: Doc/library/xml.etree.elementtree.rst | 10 +- Lib/test/test_xml_etree.py | 29 + Modules/_elementtree.c | 262 +++++++------ 3 files changed, 180 insertions(+), 121 deletions(-) diff --git a/Doc/library/xml.etree.elementtree.rst b/Doc/library/xml.etree.elementtree.rst --- a/Doc/library/xml.etree.elementtree.rst +++ b/Doc/library/xml.etree.elementtree.rst @@ -646,8 +646,8 @@ Loads an external XML section into this element tree. *source* is a file name or :term:`file object`. *parser* is an optional parser instance. - If not given, the standard XMLParser parser is used. Returns the section - root element. + If not given, the standard :class:`XMLParser` parser is used. Returns the + section root element. .. method:: write(file, encoding="us-ascii", xml_declaration=None, method="xml") @@ -767,9 +767,9 @@ :class:`Element` structure builder for XML source data, based on the expat parser. *html* are predefined HTML entities. This flag is not supported by the current implementation. *target* is the target object. If omitted, the - builder uses an instance of the standard TreeBuilder class. *encoding* [1]_ - is optional. If given, the value overrides the encoding specified in the - XML file. + builder uses an instance of the standard :class:`TreeBuilder` class. + *encoding* [1]_ is optional. If given, the value overrides the encoding + specified in the XML file. .. method:: close() diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py --- a/Lib/test/test_xml_etree.py +++ b/Lib/test/test_xml_etree.py @@ -2028,6 +2028,34 @@ 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd')) +class XMLParserTest(unittest.TestCase): + sample1 = '22' + + def _check_sample_element(self, e): + self.assertEqual(e.tag, 'file') + self.assertEqual(e[0].tag, 'line') + self.assertEqual(e[0].text, '22') + + def test_constructor_args(self): + # Positional args. The first (html) is not supported, but should be + # nevertheless correctly accepted. + parser = ET.XMLParser(None, ET.TreeBuilder(), 'utf-8') + parser.feed(self.sample1) + self._check_sample_element(parser.close()) + + # Now as keyword args. + parser2 = ET.XMLParser(encoding='utf-8', html=[{}], target=ET.TreeBuilder()) + parser2.feed(self.sample1) + self._check_sample_element(parser2.close()) + + def test_subclass(self): + class MyParser(ET.XMLParser): + pass + parser = MyParser() + parser.feed(self.sample1) + self._check_sample_element(parser.close()) + + class NoAcceleratorTest(unittest.TestCase): # Test that the C accelerator was not imported for pyET def test_correct_import_pyET(self): @@ -2245,6 +2273,7 @@ ElementTreeTest, NamespaceParseTest, TreeBuilderTest, + XMLParserTest, KeywordArgsTest] if module is pyET: # Run the tests specific to the Python implementation diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c --- a/Modules/_elementtree.c +++ b/Modules/_elementtree.c @@ -2257,6 +2257,9 @@ #define EXPAT(func) (XML_##func) #endif +static XML_Memory_Handling_Suite ExpatMemoryHandler = { + PyObject_Malloc, PyObject_Realloc, PyObject_Free}; + typedef struct { PyObject_HEAD @@ -2671,121 +2674,125 @@ } /* -------------------------------------------------------------------- */ -/* constructor and destructor */ - -static PyObject* -xmlparser(PyObject* self_, PyObject* args, PyObject* kw) + +static PyObject * +xmlparser_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { - XMLParserObject* self; - /* FIXME: does this need to be static? */ - static XML_Memory_Handling_Suite memory_handler; - - PyObject* target = NULL; - char* encoding = NULL; - static char* kwlist[] = { "target", "encoding", NULL }; - if (!PyArg_ParseTupleAndKeywords(args, kw, "|Oz:XMLParser", kwlist, - &target, &encoding)) - return NULL; - -#if defined(USE_PYEXPAT_CAPI) - if (!expat_capi) { - PyErr_SetString( - PyExc_RuntimeError, "cannot load dispatch table from pyexpat" - ); - return NULL; + XMLParserObject *self = (XMLParserObject *)type->tp_alloc(type, 0); + if (self) { + self->parser = NULL; + self->target = self->entity = self->names = NULL; + self->handle_start = self->handle_data = self->handle_end = NULL; + self->handle_comment = self->handle_pi = self->handle_close = NULL; } -#endif - - self = PyObject_New(XMLParserObject, &XMLParser_Type); - if (self == NULL) - return NULL; - - self->entity = PyDict_New(); - if (!self->entity) { - PyObject_Del(self); - return NULL; + return (PyObject *)self; +} + +static int +xmlparser_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + XMLParserObject *self_xp = (XMLParserObject *)self; + PyObject *target = NULL, *html = NULL; + char *encoding = NULL; + static char *kwlist[] = {"html", "target", "encoding"}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOz:XMLParser", kwlist, + &html, &target, &encoding)) { + return -1; } - self->names = PyDict_New(); - if (!self->names) { - PyObject_Del(self->entity); - PyObject_Del(self); - return NULL; + self_xp->entity = PyDict_New(); + if (!self_xp->entity) + return -1; + + self_xp->names = PyDict_New(); + if (!self_xp->names) { + Py_XDECREF(self_xp->entity); + return -1; } - memory_handler.malloc_fcn = PyObject_Malloc; - memory_handler.realloc_fcn = PyObject_Realloc; - memory_handler.free_fcn = PyObject_Free; - - self->parser = EXPAT(ParserCreate_MM)(encoding, &memory_handler, "}"); - if (!self->parser) { - PyObject_Del(self->names); - PyObject_Del(self->entity); - PyObject_Del(self); + self_xp->parser = EXPAT(ParserCreate_MM)(encoding, &ExpatMemoryHandler, "}"); + if (!self_xp->parser) { + Py_XDECREF(self_xp->entity); + Py_XDECREF(self_xp->names); PyErr_NoMemory(); - return NULL; + return -1; } - /* setup target handlers */ - if (!target) { + if (target) { + Py_INCREF(target); + } else { target = treebuilder_new(&TreeBuilder_Type, NULL, NULL); if (!target) { - EXPAT(ParserFree)(self->parser); - PyObject_Del(self->names); - PyObject_Del(self->entity); - PyObject_Del(self); - return NULL; + Py_XDECREF(self_xp->entity); + Py_XDECREF(self_xp->names); + EXPAT(ParserFree)(self_xp->parser); + return -1; } - } else - Py_INCREF(target); - self->target = target; - - self->handle_start = PyObject_GetAttrString(target, "start"); - self->handle_data = PyObject_GetAttrString(target, "data"); - self->handle_end = PyObject_GetAttrString(target, "end"); - self->handle_comment = PyObject_GetAttrString(target, "comment"); - self->handle_pi = PyObject_GetAttrString(target, "pi"); - self->handle_close = PyObject_GetAttrString(target, "close"); + } + self_xp->target = target; + + self_xp->handle_start = PyObject_GetAttrString(target, "start"); + self_xp->handle_data = PyObject_GetAttrString(target, "data"); + self_xp->handle_end = PyObject_GetAttrString(target, "end"); + self_xp->handle_comment = PyObject_GetAttrString(target, "comment"); + self_xp->handle_pi = PyObject_GetAttrString(target, "pi"); + self_xp->handle_close = PyObject_GetAttrString(target, "close"); PyErr_Clear(); - + /* configure parser */ - EXPAT(SetUserData)(self->parser, self); + EXPAT(SetUserData)(self_xp->parser, self_xp); EXPAT(SetElementHandler)( - self->parser, + self_xp->parser, (XML_StartElementHandler) expat_start_handler, (XML_EndElementHandler) expat_end_handler ); EXPAT(SetDefaultHandlerExpand)( - self->parser, + self_xp->parser, (XML_DefaultHandler) expat_default_handler ); EXPAT(SetCharacterDataHandler)( - self->parser, + self_xp->parser, (XML_CharacterDataHandler) expat_data_handler ); - if (self->handle_comment) + if (self_xp->handle_comment) EXPAT(SetCommentHandler)( - self->parser, + self_xp->parser, (XML_CommentHandler) expat_comment_handler ); - if (self->handle_pi) + if (self_xp->handle_pi) EXPAT(SetProcessingInstructionHandler)( - self->parser, + self_xp->parser, (XML_ProcessingInstructionHandler) expat_pi_handler ); EXPAT(SetUnknownEncodingHandler)( - self->parser, + self_xp->parser, (XML_UnknownEncodingHandler) expat_unknown_encoding_handler, NULL ); - ALLOC(sizeof(XMLParserObject), "create expatparser"); - - return (PyObject*) self; + return 0; } -static void -xmlparser_dealloc(XMLParserObject* self) +static int +xmlparser_gc_traverse(XMLParserObject *self, visitproc visit, void *arg) +{ + Py_VISIT(self->handle_close); + Py_VISIT(self->handle_pi); + Py_VISIT(self->handle_comment); + Py_VISIT(self->handle_end); + Py_VISIT(self->handle_data); + Py_VISIT(self->handle_start); + + Py_VISIT(self->target); + Py_VISIT(self->entity); + Py_VISIT(self->names); + + return 0; +} + +static int +xmlparser_gc_clear(XMLParserObject *self) { EXPAT(ParserFree)(self->parser); @@ -2796,17 +2803,20 @@ Py_XDECREF(self->handle_data); Py_XDECREF(self->handle_start); - Py_DECREF(self->target); - Py_DECREF(self->entity); - Py_DECREF(self->names); - - RELEASE(sizeof(XMLParserObject), "destroy expatparser"); - - PyObject_Del(self); + Py_XDECREF(self->target); + Py_XDECREF(self->entity); + Py_XDECREF(self->names); + + return 0; } -/* -------------------------------------------------------------------- */ -/* methods (in alphabetical order) */ +static void +xmlparser_dealloc(XMLParserObject* self) +{ + PyObject_GC_UnTrack(self); + xmlparser_gc_clear(self); + Py_TYPE(self)->tp_free((PyObject *)self); +} LOCAL(PyObject*) expat_parse(XMLParserObject* self, char* data, int data_len, int final) @@ -3083,31 +3093,42 @@ PyVarObject_HEAD_INIT(NULL, 0) "XMLParser", sizeof(XMLParserObject), 0, /* methods */ - (destructor)xmlparser_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_reserved */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - (getattrofunc)xmlparser_getattro, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ - 0, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - xmlparser_methods, /* tp_methods */ - 0, /* tp_members */ + (destructor)xmlparser_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + (getattrofunc)xmlparser_getattro, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, + /* tp_flags */ + 0, /* tp_doc */ + (traverseproc)xmlparser_gc_traverse, /* tp_traverse */ + (inquiry)xmlparser_gc_clear, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + xmlparser_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)xmlparser_init, /* tp_init */ + PyType_GenericAlloc, /* tp_alloc */ + xmlparser_new, /* tp_new */ + 0, /* tp_free */ }; #endif @@ -3117,9 +3138,6 @@ static PyMethodDef _functions[] = { {"SubElement", (PyCFunction) subelement, METH_VARARGS|METH_KEYWORDS}, -#if defined(USE_EXPAT) - {"XMLParser", (PyCFunction) xmlparser, METH_VARARGS|METH_KEYWORDS}, -#endif {NULL, NULL} }; @@ -3214,8 +3232,15 @@ expat_capi->size < sizeof(struct PyExpat_CAPI) || expat_capi->MAJOR_VERSION != XML_MAJOR_VERSION || expat_capi->MINOR_VERSION != XML_MINOR_VERSION || - expat_capi->MICRO_VERSION != XML_MICRO_VERSION) + expat_capi->MICRO_VERSION != XML_MICRO_VERSION) { expat_capi = NULL; + } + } + if (!expat_capi) { + PyErr_SetString( + PyExc_RuntimeError, "cannot load dispatch table from pyexpat" + ); + return NULL; } #endif @@ -3231,5 +3256,10 @@ Py_INCREF((PyObject *)&TreeBuilder_Type); PyModule_AddObject(m, "TreeBuilder", (PyObject *)&TreeBuilder_Type); +#if defined(USE_EXPAT) + Py_INCREF((PyObject *)&XMLParser_Type); + PyModule_AddObject(m, "XMLParser", (PyObject *)&XMLParser_Type); +#endif + return m; } -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 1 06:16:50 2012 From: python-checkins at python.org (eli.bendersky) Date: Fri, 01 Jun 2012 06:16:50 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_fix_trailing_whitespace?= Message-ID: http://hg.python.org/cpython/rev/925383843063 changeset: 77276:925383843063 user: Eli Bendersky date: Fri Jun 01 07:15:00 2012 +0300 summary: fix trailing whitespace files: Doc/library/xml.etree.elementtree.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/library/xml.etree.elementtree.rst b/Doc/library/xml.etree.elementtree.rst --- a/Doc/library/xml.etree.elementtree.rst +++ b/Doc/library/xml.etree.elementtree.rst @@ -767,7 +767,7 @@ :class:`Element` structure builder for XML source data, based on the expat parser. *html* are predefined HTML entities. This flag is not supported by the current implementation. *target* is the target object. If omitted, the - builder uses an instance of the standard :class:`TreeBuilder` class. + builder uses an instance of the standard :class:`TreeBuilder` class. *encoding* [1]_ is optional. If given, the value overrides the encoding specified in the XML file. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 1 07:07:49 2012 From: python-checkins at python.org (brian.curtin) Date: Fri, 01 Jun 2012 07:07:49 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Reformat_two_issue_numbers?= Message-ID: http://hg.python.org/cpython/rev/8ddf40f68def changeset: 77277:8ddf40f68def user: Brian Curtin date: Fri Jun 01 00:07:28 2012 -0500 summary: Reformat two issue numbers files: Misc/NEWS | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -92,13 +92,13 @@ - Issue #14443: Tell rpmbuild to use the correct version of Python in bdist_rpm. Initial patch by Ross Lagerwall. -- Issue14929: Stop Idle 3.x from closing on Unicode decode errors when grepping. +- Issue #14929: Stop Idle 3.x from closing on Unicode decode errors when grepping. Patch by Roger Serwy. - Issue #12515: email now registers a defect if it gets to EOF while parsing a MIME part without seeing the closing MIME boundary. -- Issue12510: Attempting to get invalid tooltip no longer closes Idle. +- Issue #12510: Attempting to get invalid tooltip no longer closes Idle. Original patch by Roger Serwy. - Issue #1672568: email now always decodes base64 payloads, adding padding and -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 1 08:50:27 2012 From: python-checkins at python.org (eli.bendersky) Date: Fri, 01 Jun 2012 08:50:27 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_We=27re_always_building_=5F?= =?utf8?q?elementtree_with_USE=5FPYEXPAT=5FCAPI=2C_so_the_=23ifdefs_in?= Message-ID: http://hg.python.org/cpython/rev/cf9c379a9859 changeset: 77278:cf9c379a9859 user: Eli Bendersky date: Fri Jun 01 09:48:37 2012 +0300 summary: We're always building _elementtree with USE_PYEXPAT_CAPI, so the #ifdefs in the code are unnecessary. files: Modules/_elementtree.c | 14 ++------------ 1 files changed, 2 insertions(+), 12 deletions(-) diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c --- a/Modules/_elementtree.c +++ b/Modules/_elementtree.c @@ -58,9 +58,6 @@ /* Leave defined to include the expat-based XMLParser type */ #define USE_EXPAT -/* Define to do all expat calls via pyexpat's embedded expat library */ -/* #define USE_PYEXPAT_CAPI */ - /* An element can hold this many children without extra memory allocations. */ #define STATIC_CHILDREN 4 @@ -2248,14 +2245,9 @@ #if defined(USE_EXPAT) #include "expat.h" - -#if defined(USE_PYEXPAT_CAPI) #include "pyexpat.h" -static struct PyExpat_CAPI* expat_capi; +static struct PyExpat_CAPI *expat_capi; #define EXPAT(func) (expat_capi->func) -#else -#define EXPAT(func) (XML_##func) -#endif static XML_Memory_Handling_Suite ExpatMemoryHandler = { PyObject_Malloc, PyObject_Realloc, PyObject_Free}; @@ -3223,8 +3215,7 @@ elementtree_iter_obj = PyDict_GetItemString(g, "iter"); elementtree_itertext_obj = PyDict_GetItemString(g, "itertext"); -#if defined(USE_PYEXPAT_CAPI) - /* link against pyexpat, if possible */ + /* link against pyexpat */ expat_capi = PyCapsule_Import(PyExpat_CAPSULE_NAME, 0); if (expat_capi) { /* check that it's usable */ @@ -3242,7 +3233,6 @@ ); return NULL; } -#endif elementtree_parseerror_obj = PyErr_NewException( "xml.etree.ElementTree.ParseError", PyExc_SyntaxError, NULL -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 1 10:34:30 2012 From: python-checkins at python.org (eli.bendersky) Date: Fri, 01 Jun 2012 10:34:30 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Issue_=2314007=3A_implement?= =?utf8?q?_doctype=28=29_method_calling_in_XMLParser_of_=5Felementtree=2E?= Message-ID: http://hg.python.org/cpython/rev/6f9bfcc1896f changeset: 77279:6f9bfcc1896f user: Eli Bendersky date: Fri Jun 01 11:32:34 2012 +0300 summary: Issue #14007: implement doctype() method calling in XMLParser of _elementtree. Includes exposing a doctype handler from expat through pyexpat. files: Include/pyexpat.h | 2 + Lib/test/test_xml_etree.py | 19 +++- Modules/_elementtree.c | 117 ++++++++++++++++++++++-- Modules/pyexpat.c | 1 + 4 files changed, 124 insertions(+), 15 deletions(-) diff --git a/Include/pyexpat.h b/Include/pyexpat.h --- a/Include/pyexpat.h +++ b/Include/pyexpat.h @@ -43,6 +43,8 @@ XML_Parser parser, XML_UnknownEncodingHandler handler, void *encodingHandlerData); void (*SetUserData)(XML_Parser parser, void *userData); + void (*SetStartDoctypeDeclHandler)(XML_Parser parser, + XML_StartDoctypeDeclHandler start); /* always add new stuff to the end! */ }; diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py --- a/Lib/test/test_xml_etree.py +++ b/Lib/test/test_xml_etree.py @@ -2009,7 +2009,6 @@ self.assertEqual(lst, ['toplevel']) - @unittest.expectedFailure # XXX issue 14007 with C ElementTree def test_doctype(self): class DoctypeParser: _doctype = None @@ -2030,6 +2029,10 @@ class XMLParserTest(unittest.TestCase): sample1 = '22' + sample2 = ('' + 'text') def _check_sample_element(self, e): self.assertEqual(e.tag, 'file') @@ -2055,6 +2058,20 @@ parser.feed(self.sample1) self._check_sample_element(parser.close()) + def test_subclass_doctype(self): + _doctype = None + class MyParserWithDoctype(ET.XMLParser): + def doctype(self, name, pubid, system): + nonlocal _doctype + _doctype = (name, pubid, system) + + parser = MyParserWithDoctype() + parser.feed(self.sample2) + parser.close() + self.assertEqual(_doctype, + ('html', '-//W3C//DTD XHTML 1.0 Transitional//EN', + 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd')) + class NoAcceleratorTest(unittest.TestCase): # Test that the C accelerator was not imported for pyET diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c --- a/Modules/_elementtree.c +++ b/Modules/_elementtree.c @@ -2257,24 +2257,27 @@ XML_Parser parser; - PyObject* target; - PyObject* entity; - - PyObject* names; - - PyObject* handle_start; - PyObject* handle_data; - PyObject* handle_end; - - PyObject* handle_comment; - PyObject* handle_pi; - - PyObject* handle_close; + PyObject *target; + PyObject *entity; + + PyObject *names; + + PyObject *handle_start; + PyObject *handle_data; + PyObject *handle_end; + + PyObject *handle_comment; + PyObject *handle_pi; + PyObject *handle_doctype; + + PyObject *handle_close; } XMLParserObject; static PyTypeObject XMLParser_Type; +#define XMLParser_CheckExact(op) (Py_TYPE(op) == &XMLParser_Type) + /* helpers */ LOCAL(PyObject*) @@ -2601,6 +2604,78 @@ } } +static void +expat_start_doctype_handler(XMLParserObject *self, + const XML_Char *doctype_name, + const XML_Char *sysid, + const XML_Char *pubid, + int has_internal_subset) +{ + PyObject *self_pyobj = (PyObject *)self; + PyObject *doctype_name_obj, *sysid_obj, *pubid_obj; + PyObject *parser_doctype = NULL; + PyObject *res = NULL; + + doctype_name_obj = makeuniversal(self, doctype_name); + if (!doctype_name_obj) + return; + + if (sysid) { + sysid_obj = makeuniversal(self, sysid); + if (!sysid_obj) { + Py_DECREF(doctype_name_obj); + return; + } + } else { + Py_INCREF(Py_None); + sysid_obj = Py_None; + } + + if (pubid) { + pubid_obj = makeuniversal(self, pubid); + if (!pubid_obj) { + Py_DECREF(doctype_name_obj); + Py_DECREF(sysid_obj); + return; + } + } else { + Py_INCREF(Py_None); + pubid_obj = Py_None; + } + + /* If the target has a handler for doctype, call it. */ + if (self->handle_doctype) { + res = PyObject_CallFunction(self->handle_doctype, "OOO", + doctype_name_obj, pubid_obj, sysid_obj); + Py_CLEAR(res); + } + + /* Now see if the parser itself has a doctype method. If yes and it's + * a subclass, call it but warn about deprecation. If it's not a subclass + * (i.e. vanilla XMLParser), do nothing. + */ + parser_doctype = PyObject_GetAttrString(self_pyobj, "doctype"); + if (parser_doctype) { + if (!XMLParser_CheckExact(self_pyobj)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "This method of XMLParser is deprecated. Define" + " doctype() method on the TreeBuilder target.", + 1) < 0) { + goto clear; + } + res = PyObject_CallFunction(parser_doctype, "OOO", + doctype_name_obj, pubid_obj, sysid_obj); + Py_CLEAR(res); + } + } + +clear: + Py_XDECREF(parser_doctype); + Py_DECREF(doctype_name_obj); + Py_DECREF(pubid_obj); + Py_DECREF(sysid_obj); +} + static void expat_pi_handler(XMLParserObject* self, const XML_Char* target_in, const XML_Char* data_in) @@ -2676,6 +2751,7 @@ self->target = self->entity = self->names = NULL; self->handle_start = self->handle_data = self->handle_end = NULL; self->handle_comment = self->handle_pi = self->handle_close = NULL; + self->handle_doctype = NULL; } return (PyObject *)self; } @@ -2730,6 +2806,7 @@ self_xp->handle_comment = PyObject_GetAttrString(target, "comment"); self_xp->handle_pi = PyObject_GetAttrString(target, "pi"); self_xp->handle_close = PyObject_GetAttrString(target, "close"); + self_xp->handle_doctype = PyObject_GetAttrString(target, "doctype"); PyErr_Clear(); @@ -2758,6 +2835,10 @@ self_xp->parser, (XML_ProcessingInstructionHandler) expat_pi_handler ); + EXPAT(SetStartDoctypeDeclHandler)( + self_xp->parser, + (XML_StartDoctypeDeclHandler) expat_start_doctype_handler + ); EXPAT(SetUnknownEncodingHandler)( self_xp->parser, (XML_UnknownEncodingHandler) expat_unknown_encoding_handler, NULL @@ -2794,6 +2875,7 @@ Py_XDECREF(self->handle_end); Py_XDECREF(self->handle_data); Py_XDECREF(self->handle_start); + Py_XDECREF(self->handle_doctype); Py_XDECREF(self->target); Py_XDECREF(self->entity); @@ -2950,7 +3032,13 @@ } static PyObject* -xmlparser_setevents(XMLParserObject* self, PyObject* args) +xmlparser_doctype(XMLParserObject *self, PyObject *args) +{ + Py_RETURN_NONE; +} + +static PyObject* +xmlparser_setevents(XMLParserObject *self, PyObject* args) { /* activate element event reporting */ @@ -3054,6 +3142,7 @@ {"close", (PyCFunction) xmlparser_close, METH_VARARGS}, {"_parse", (PyCFunction) xmlparser_parse, METH_VARARGS}, {"_setevents", (PyCFunction) xmlparser_setevents, METH_VARARGS}, + {"doctype", (PyCFunction) xmlparser_doctype, METH_VARARGS}, {NULL, NULL} }; diff --git a/Modules/pyexpat.c b/Modules/pyexpat.c --- a/Modules/pyexpat.c +++ b/Modules/pyexpat.c @@ -1904,6 +1904,7 @@ capi.SetProcessingInstructionHandler = XML_SetProcessingInstructionHandler; capi.SetUnknownEncodingHandler = XML_SetUnknownEncodingHandler; capi.SetUserData = XML_SetUserData; + capi.SetStartDoctypeDeclHandler = XML_SetStartDoctypeDeclHandler; /* export using capsule */ capi_object = PyCapsule_New(&capi, PyExpat_CAPSULE_NAME, NULL); -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 1 10:59:38 2012 From: python-checkins at python.org (stefan.krah) Date: Fri, 01 Jun 2012 10:59:38 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_word=2Edigits_are_always_in?= =?utf8?q?itialized_before_use_in_the_Taylor_series_loop=2C?= Message-ID: http://hg.python.org/cpython/rev/a118294b68db changeset: 77280:a118294b68db user: Stefan Krah date: Fri Jun 01 10:58:16 2012 +0200 summary: word.digits are always initialized before use in the Taylor series loop, but this is more readable. files: Modules/_decimal/libmpdec/mpdecimal.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Modules/_decimal/libmpdec/mpdecimal.c b/Modules/_decimal/libmpdec/mpdecimal.c --- a/Modules/_decimal/libmpdec/mpdecimal.c +++ b/Modules/_decimal/libmpdec/mpdecimal.c @@ -3989,7 +3989,7 @@ mpd_context_t workctx; MPD_NEW_STATIC(tmp,0,0,0,0); MPD_NEW_STATIC(sum,0,0,0,0); - MPD_NEW_CONST(word,0,0,0,1,1,1); + MPD_NEW_CONST(word,0,0,1,1,1,1); mpd_ssize_t j, n, t; assert(!mpd_isspecial(a)); -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 1 11:54:12 2012 From: python-checkins at python.org (hynek.schlawack) Date: Fri, 01 Jun 2012 11:54:12 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_=2314814=3A_Remove_2=2Ex=27?= =?utf8?q?s_new-style_classes_syntax_from_ipaddress?= Message-ID: http://hg.python.org/cpython/rev/0eb63de72e96 changeset: 77281:0eb63de72e96 user: Hynek Schlawack date: Fri Jun 01 11:48:32 2012 +0200 summary: #14814: Remove 2.x's new-style classes syntax from ipaddress files: Lib/ipaddress.py | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/ipaddress.py b/Lib/ipaddress.py --- a/Lib/ipaddress.py +++ b/Lib/ipaddress.py @@ -400,7 +400,7 @@ return NotImplemented -class _IPAddressBase(object): +class _IPAddressBase: """The mother class.""" @@ -975,7 +975,7 @@ return t.__class__('%s/%d' % (str(t.network_address), t.prefixlen)) -class _BaseV4(object): +class _BaseV4: """Base IPv4 object. @@ -1511,7 +1511,7 @@ return '%s/%s' % (str(self.network_address), str(self.hostmask)) -class _BaseV6(object): +class _BaseV6: """Base IPv6 object. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 1 14:48:48 2012 From: python-checkins at python.org (nick.coghlan) Date: Fri, 01 Jun 2012 14:48:48 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Close_=2314969=3A_Improve_t?= =?utf8?q?he_handling_of_exception_chaining_in_contextlib=2EExitStack?= Message-ID: http://hg.python.org/cpython/rev/c108bc96aec6 changeset: 77282:c108bc96aec6 user: Nick Coghlan date: Fri Jun 01 22:48:32 2012 +1000 summary: Close #14969: Improve the handling of exception chaining in contextlib.ExitStack files: Lib/contextlib.py | 16 +++++++++++-- Lib/test/test_contextlib.py | 29 +++++++++++++++--------- Misc/NEWS | 2 + 3 files changed, 33 insertions(+), 14 deletions(-) diff --git a/Lib/contextlib.py b/Lib/contextlib.py --- a/Lib/contextlib.py +++ b/Lib/contextlib.py @@ -225,6 +225,17 @@ return self def __exit__(self, *exc_details): + # We manipulate the exception state so it behaves as though + # we were actually nesting multiple with statements + frame_exc = sys.exc_info()[1] + def _fix_exception_context(new_exc, old_exc): + while 1: + exc_context = new_exc.__context__ + if exc_context in (None, frame_exc): + break + new_exc = exc_context + new_exc.__context__ = old_exc + # Callbacks are invoked in LIFO order to match the behaviour of # nested context managers suppressed_exc = False @@ -236,9 +247,8 @@ exc_details = (None, None, None) except: new_exc_details = sys.exc_info() - if exc_details != (None, None, None): - # simulate the stack of exceptions by setting the context - new_exc_details[1].__context__ = exc_details[1] + # simulate the stack of exceptions by setting the context + _fix_exception_context(new_exc_details[1], exc_details[1]) if not self._exit_callbacks: raise exc_details = new_exc_details diff --git a/Lib/test/test_contextlib.py b/Lib/test/test_contextlib.py --- a/Lib/test/test_contextlib.py +++ b/Lib/test/test_contextlib.py @@ -505,6 +505,18 @@ def __exit__(self, *exc_details): raise self.exc + class RaiseExcWithContext: + def __init__(self, outer, inner): + self.outer = outer + self.inner = inner + def __enter__(self): + return self + def __exit__(self, *exc_details): + try: + raise self.inner + except: + raise self.outer + class SuppressExc: def __enter__(self): return self @@ -514,11 +526,10 @@ try: with RaiseExc(IndexError): - with RaiseExc(KeyError): - with RaiseExc(AttributeError): - with SuppressExc(): - with RaiseExc(ValueError): - 1 / 0 + with RaiseExcWithContext(KeyError, AttributeError): + with SuppressExc(): + with RaiseExc(ValueError): + 1 / 0 except IndexError as exc: self.assertIsInstance(exc.__context__, KeyError) self.assertIsInstance(exc.__context__.__context__, AttributeError) @@ -553,12 +564,8 @@ except IndexError as exc: self.assertIsInstance(exc.__context__, KeyError) self.assertIsInstance(exc.__context__.__context__, AttributeError) - # Inner exceptions were suppressed, but the with statement - # cleanup code adds the one from the body back in as the - # context of the exception raised by the outer callbacks - # See http://bugs.python.org/issue14969 - suite_exc = exc.__context__.__context__.__context__ - self.assertIsInstance(suite_exc, ZeroDivisionError) + # Inner exceptions were suppressed + self.assertIsNone(exc.__context__.__context__.__context__) else: self.fail("Expected IndexError, but no exception was raised") # Check the inner exceptions diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,8 @@ Library ------- +- Issue #14969: Better handling of exception chaining in contextlib.ExitStack + - Issue #14962: Update text coloring in IDLE shell window after changing options. Patch by Roger Serwy. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 1 15:09:18 2012 From: python-checkins at python.org (martin.v.loewis) Date: Fri, 01 Jun 2012 15:09:18 +0200 Subject: [Python-checkins] =?utf8?q?peps=3A_Convert_to_rst=2E_Add_section_?= =?utf8?q?on_Windows_lifecycle=2E?= Message-ID: http://hg.python.org/peps/rev/727662ab7f50 changeset: 4444:727662ab7f50 user: Martin v. L?wis date: Fri Jun 01 15:09:13 2012 +0200 summary: Convert to rst. Add section on Windows lifecycle. files: pep-0011.txt | 288 +++++++++++++++++++++----------------- 1 files changed, 162 insertions(+), 126 deletions(-) diff --git a/pep-0011.txt b/pep-0011.txt --- a/pep-0011.txt +++ b/pep-0011.txt @@ -5,189 +5,225 @@ Author: martin at v.loewis.de (Martin von L?wis) Status: Active Type: Process +Content-Type: text/x-rst Created: 07-Jul-2002 Post-History: 18-Aug-2007 Abstract +-------- - This PEP documents operating systems (platforms) which are not - supported in Python anymore. For some of these systems, - supporting code might be still part of Python, but will be removed - in a future release - unless somebody steps forward as a volunteer - to maintain this code. +This PEP documents operating systems (platforms) which are not +supported in Python anymore. For some of these systems, +supporting code might be still part of Python, but will be removed +in a future release - unless somebody steps forward as a volunteer +to maintain this code. Rationale +--------- - Over time, the Python source code has collected various pieces of - platform-specific code, which, at some point in time, was - considered necessary to use Python on a specific platform. - Without access to this platform, it is not possible to determine - whether this code is still needed. As a result, this code may - either break during the Python evolution, or it may become - unnecessary as the platforms evolve as well. +Over time, the Python source code has collected various pieces of +platform-specific code, which, at some point in time, was +considered necessary to use Python on a specific platform. +Without access to this platform, it is not possible to determine +whether this code is still needed. As a result, this code may +either break during the Python evolution, or it may become +unnecessary as the platforms evolve as well. - The growing amount of these fragments poses the risk of - unmaintainability: without having experts for a large number of - platforms, it is not possible to determine whether a certain - change to the Python source code will work on all supported - platforms. +The growing amount of these fragments poses the risk of +unmaintainability: without having experts for a large number of +platforms, it is not possible to determine whether a certain +change to the Python source code will work on all supported +platforms. - To reduce this risk, this PEP proposes a procedure to remove code - for platforms with no Python users. +To reduce this risk, this PEP proposes a procedure to remove code +for platforms with no Python users. Unsupporting platforms +---------------------- - If a certain platform that currently has special code in it is - deemed to be without Python users, a note must be posted in this - PEP that this platform is no longer actively supported. This - note must include: +If a certain platform that currently has special code in it is +deemed to be without Python users, a note must be posted in this +PEP that this platform is no longer actively supported. This +note must include: - - the name of the system - - the first release number that does not support this platform - anymore, and - - the first release where the historical support code is actively - removed +- the name of the system +- the first release number that does not support this platform + anymore, and +- the first release where the historical support code is actively + removed - In some cases, it is not possible to identify the specific list of - systems for which some code is used (e.g. when autoconf tests for - absence of some feature which is considered present on all - supported systems). In this case, the name will give the precise - condition (usually a preprocessor symbol) that will become - unsupported. +In some cases, it is not possible to identify the specific list of +systems for which some code is used (e.g. when autoconf tests for +absence of some feature which is considered present on all +supported systems). In this case, the name will give the precise +condition (usually a preprocessor symbol) that will become +unsupported. - At the same time, the Python source code must be changed to - produce a build-time error if somebody tries to install Python on - this platform. On platforms using autoconf, configure must fail. - This gives potential users of the platform a chance to step - forward and offer maintenance. +At the same time, the Python source code must be changed to +produce a build-time error if somebody tries to install Python on +this platform. On platforms using autoconf, configure must fail. +This gives potential users of the platform a chance to step +forward and offer maintenance. Resupporting platforms +---------------------- - If a user of a platform wants to see this platform supported - again, he may volunteer to maintain the platform support. Such an - offer must be recorded in the PEP, and the user can submit patches - to remove the build-time errors, and perform any other maintenance - work for the platform. +If a user of a platform wants to see this platform supported +again, he may volunteer to maintain the platform support. Such an +offer must be recorded in the PEP, and the user can submit patches +to remove the build-time errors, and perform any other maintenance +work for the platform. + +Microsoft Windows +----------------- + +Microsoft has established a policy called product support lifecycle +[1]_. Each product's lifecycle has a mainstream support phase, where +the product is generally commercially available, and an extended +support phase, where paid support is still available, and certain bug +fixes are released (in particular security fixes). + +Python's Windows support now follows this lifecycle. A new feature +release X.Y.0 will support all Windows releases whose extended support +phase is not yet expired. Subsequent bug fix releases will support +the same Windows releases as the original feature release (even if +the extended support phase has ended). + +Because of this policy, no further Windows releases need to be listed +in this PEP. + +Each feature release is built by a specific version of Microsoft +Visual Studio. That version should have mainstream support when the +release is made. Developers of extension modules will generally need +to use the same Visual Studio release; they are concerned both with +the availability of the versions they need to use, and with keeping +the zoo of versions small. The Python source tree will keep +unmaintained build files for older Visual Studio releases, for which +patches will be accepted. Such build files will be removed from the +source tree 3 years after the extended support for the compiler has +ended (but continue to remain available in revision control). No-longer-supported platforms +----------------------------- - Name: MS-DOS, MS-Windows 3.x - Unsupported in: Python 2.0 - Code removed in: Python 2.1 +* | Name: MS-DOS, MS-Windows 3.x + | Unsupported in: Python 2.0 + | Code removed in: Python 2.1 - Name: SunOS 4 - Unsupported in: Python 2.3 - Code removed in: Python 2.4 +* | Name: SunOS 4 + | Unsupported in: Python 2.3 + | Code removed in: Python 2.4 - Name: DYNIX - Unsupported in: Python 2.3 - Code removed in: Python 2.4 +* | Name: DYNIX + | Unsupported in: Python 2.3 + | Code removed in: Python 2.4 - Name: dgux - Unsupported in: Python 2.3 - Code removed in: Python 2.4 +* | Name: dgux + | Unsupported in: Python 2.3 + | Code removed in: Python 2.4 - Name: Minix - Unsupported in: Python 2.3 - Code removed in: Python 2.4 +* | Name: Minix + | Unsupported in: Python 2.3 + | Code removed in: Python 2.4 - Name: Irix 4 and --with-sgi-dl - Unsupported in: Python 2.3 - Code removed in: Python 2.4 +* | Name: Irix 4 and --with-sgi-dl + | Unsupported in: Python 2.3 + | Code removed in: Python 2.4 - Name: Linux 1 - Unsupported in: Python 2.3 - Code removed in: Python 2.4 +* | Name: Linux 1 + | Unsupported in: Python 2.3 + | Code removed in: Python 2.4 - Name: Systems defining __d6_pthread_create (configure.in) - Unsupported in: Python 2.3 - Code removed in: Python 2.4 +* | Name: Systems defining __d6_pthread_create (configure.in) + | Unsupported in: Python 2.3 + | Code removed in: Python 2.4 - Name: Systems defining PY_PTHREAD_D4, PY_PTHREAD_D6, +* | Name: Systems defining PY_PTHREAD_D4, PY_PTHREAD_D6, or PY_PTHREAD_D7 in thread_pthread.h - Unsupported in: Python 2.3 - Code removed in: Python 2.4 + | Unsupported in: Python 2.3 + | Code removed in: Python 2.4 - Name: Systems using --with-dl-dld - Unsupported in: Python 2.3 - Code removed in: Python 2.4 +* | Name: Systems using --with-dl-dld + | Unsupported in: Python 2.3 + | Code removed in: Python 2.4 - Name: Systems using --without-universal-newlines, - Unsupported in: Python 2.3 - Code removed in: Python 2.4 +* | Name: Systems using --without-universal-newlines, + | Unsupported in: Python 2.3 + | Code removed in: Python 2.4 - Name: MacOS 9 - Unsupported in: Python 2.4 - Code removed in: Python 2.4 +* | Name: MacOS 9 + | Unsupported in: Python 2.4 + | Code removed in: Python 2.4 - Name: Systems using --with-wctype-functions - Unsupported in: Python 2.6 - Code removed in: Python 2.6 +* | Name: Systems using --with-wctype-functions + | Unsupported in: Python 2.6 + | Code removed in: Python 2.6 - Name: Win9x, WinME, NT4 - Unsupported in: Python 2.6 (warning in 2.5 installer) - Code removed in: Python 2.6 +* | Name: Win9x, WinME, NT4 + | Unsupported in: Python 2.6 (warning in 2.5 installer) + | Code removed in: Python 2.6 - Name: AtheOS - Unsupported in: Python 2.6 (with "AtheOS" changed to "Syllable") - Build broken in: Python 2.7 (edit configure to reenable) - Code removed in: Python 3.0 - Details: http://www.syllable.org/discussion.php?id=2320 +* | Name: AtheOS + | Unsupported in: Python 2.6 (with "AtheOS" changed to "Syllable") + | Build broken in: Python 2.7 (edit configure to reenable) + | Code removed in: Python 3.0 + | Details: http://www.syllable.org/discussion.php?id=2320 - Name: BeOS - Unsupported in: Python 2.6 (warning in configure) - Build broken in: Python 2.7 (edit configure to reenable) - Code removed in: Python 3.0 +* | Name: BeOS + | Unsupported in: Python 2.6 (warning in configure) + | Build broken in: Python 2.7 (edit configure to reenable) + | Code removed in: Python 3.0 - Name: Systems using Mach C Threads - Unsupported in: Python 3.2 - Code removed in: Python 3.3 +* | Name: Systems using Mach C Threads + | Unsupported in: Python 3.2 + | Code removed in: Python 3.3 - Name: SunOS lightweight processes (LWP) - Unsupported in: Python 3.2 - Code removed in: Python 3.3 +* | Name: SunOS lightweight processes (LWP) + | Unsupported in: Python 3.2 + | Code removed in: Python 3.3 - Name: Systems using --with-pth (GNU pth threads) - Unsupported in: Python 3.2 - Code removed in: Python 3.3 +* | Name: Systems using --with-pth (GNU pth threads) + | Unsupported in: Python 3.2 + | Code removed in: Python 3.3 - Name: Systems using Irix threads - Unsupported in: Python 3.2 - Code removed in: Python 3.3 +* | Name: Systems using Irix threads + | Unsupported in: Python 3.2 + | Code removed in: Python 3.3 - Name: OSF* systems (issue 8606) - Unsupported in: Python 3.2 - Code removed in: Python 3.3 +* | Name: OSF* systems (issue 8606) + | Unsupported in: Python 3.2 + | Code removed in: Python 3.3 - Name: OS/2 - Unsupported in: Python 3.3 - Code removed in: Python 3.4 +* | Name: OS/2 + | Unsupported in: Python 3.3 + | Code removed in: Python 3.4 - Name: VMS - Unsupported in: Python 3.3 - Code removed in: Python 3.4 +* | Name: VMS + | Unsupported in: Python 3.3 + | Code removed in: Python 3.4 - Name: Windows 2000 - Unsupported in: Python 3.3 - Code removed in: Python 3.4 +* | Name: Windows 2000 + | Unsupported in: Python 3.3 + | Code removed in: Python 3.4 - Name: Windows systems where COMSPEC points to command.com - Unsupported in: Python 3.3 - Code removed in: Python 3.4 +* | Name: Windows systems where COMSPEC points to command.com + | Unsupported in: Python 3.3 + | Code removed in: Python 3.4 -Platform Maintainers +References +---------- - Cygwin Jason Tishler (jason at tishler.net) - More TBD. +.. [1] http://support.microsoft.com/lifecycle/ Copyright +--------- - This document has been placed in the public domain. +This document has been placed in the public domain. -- Repository URL: http://hg.python.org/peps From python-checkins at python.org Fri Jun 1 19:57:45 2012 From: python-checkins at python.org (hynek.schlawack) Date: Fri, 01 Jun 2012 19:57:45 +0200 Subject: [Python-checkins] =?utf8?q?devguide=3A_Add_interests?= Message-ID: http://hg.python.org/devguide/rev/365da1a16d73 changeset: 521:365da1a16d73 user: Hynek Schlawack date: Fri Jun 01 19:57:29 2012 +0200 summary: Add interests files: experts.rst | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/experts.rst b/experts.rst --- a/experts.rst +++ b/experts.rst @@ -280,7 +280,7 @@ FreeBSD HP-UX Linux -Mac OS X ronaldoussoren, ned.deily +Mac OS X ronaldoussoren, ned.deily, hynek NetBSD1 OS2/EMX aimacintyre Solaris/OpenIndiana jcea @@ -308,7 +308,7 @@ GUI i18n lemburg, eric.araujo import machinery brett.cannon, ncoghlan -io pitrou, benjamin.peterson, stutzbach +io pitrou, benjamin.peterson, stutzbach, hynek locale lemburg, loewis mathematics mark.dickinson, eric.smith, lemburg, stutzbach memory management tim_one, lemburg -- Repository URL: http://hg.python.org/devguide From python-checkins at python.org Fri Jun 1 20:18:29 2012 From: python-checkins at python.org (benjamin.peterson) Date: Fri, 01 Jun 2012 20:18:29 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_check_return_for_error?= Message-ID: http://hg.python.org/cpython/rev/2285a82504dc changeset: 77283:2285a82504dc user: Benjamin Peterson date: Fri Jun 01 11:18:22 2012 -0700 summary: check return for error files: Python/ceval.c | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-) diff --git a/Python/ceval.c b/Python/ceval.c --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3107,6 +3107,8 @@ tail = PyUnicode_FromFormat(", %U, and %U", PyList_GET_ITEM(names, len - 2), PyList_GET_ITEM(names, len - 1)); + if (tail == NULL) + return; /* Chop off the last two objects in the list. This shouldn't actually fail, but we can't be too careful. */ err = PyList_SetSlice(names, len - 2, len, NULL); -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 1 20:21:12 2012 From: python-checkins at python.org (hynek.schlawack) Date: Fri, 01 Jun 2012 20:21:12 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_=2314814=3A_Fix_errror_mess?= =?utf8?q?age_creation_in_ipaddress=2Ecollapse=5Faddresses?= Message-ID: http://hg.python.org/cpython/rev/000cc4e0e1cd changeset: 77284:000cc4e0e1cd user: Hynek Schlawack date: Fri Jun 01 20:12:17 2012 +0200 summary: #14814: Fix errror message creation in ipaddress.collapse_addresses files: Lib/ipaddress.py | 2 +- Lib/test/test_ipaddress.py | 15 +++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/Lib/ipaddress.py b/Lib/ipaddress.py --- a/Lib/ipaddress.py +++ b/Lib/ipaddress.py @@ -359,7 +359,7 @@ else: if nets and nets[-1]._version != ip._version: raise TypeError("%s and %s are not of the same version" % ( - str(ip), str(ips[-1]))) + str(ip), str(nets[-1]))) nets.append(ip) # sort and dedup diff --git a/Lib/test/test_ipaddress.py b/Lib/test/test_ipaddress.py --- a/Lib/test/test_ipaddress.py +++ b/Lib/test/test_ipaddress.py @@ -596,10 +596,17 @@ self.assertEqual(list(collapsed), [ip3]) # the toejam test - ip1 = ipaddress.ip_address('1.1.1.1') - ip2 = ipaddress.ip_address('::1') - self.assertRaises(TypeError, ipaddress.collapse_addresses, - [ip1, ip2]) + addr_tuples = [ + (ipaddress.ip_address('1.1.1.1'), + ipaddress.ip_address('::1')), + (ipaddress.IPv4Network('1.1.0.0/24'), + ipaddress.IPv6Network('2001::/120')), + (ipaddress.IPv4Network('1.1.0.0/32'), + ipaddress.IPv6Network('2001::/128')), + ] + for ip1, ip2 in addr_tuples: + self.assertRaises(TypeError, ipaddress.collapse_addresses, + [ip1, ip2]) def testSummarizing(self): #ip = ipaddress.ip_address -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 1 20:26:30 2012 From: python-checkins at python.org (sandro.tosi) Date: Fri, 01 Jun 2012 20:26:30 +0200 Subject: [Python-checkins] =?utf8?b?Y3B5dGhvbiAoMy4yKTogSXNzdWUgIzE0OTY4?= =?utf8?q?=3A_set_=27Inplace_Operators=27_as_subsection=3B_patch_by_Lars_B?= =?utf8?q?uitinck?= Message-ID: http://hg.python.org/cpython/rev/bf6305bce3af changeset: 77285:bf6305bce3af branch: 3.2 parent: 77266:9d0c3a835bfe user: Sandro Tosi date: Fri Jun 01 20:23:20 2012 +0200 summary: Issue #14968: set 'Inplace Operators' as subsection; patch by Lars Buitinck files: Doc/library/operator.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/library/operator.rst b/Doc/library/operator.rst --- a/Doc/library/operator.rst +++ b/Doc/library/operator.rst @@ -404,7 +404,7 @@ +-----------------------+-------------------------+---------------------------------------+ Inplace Operators -================= +----------------- Many operations have an "in-place" version. Listed below are functions providing a more primitive access to in-place operators than the usual syntax -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 1 20:26:31 2012 From: python-checkins at python.org (sandro.tosi) Date: Fri, 01 Jun 2012 20:26:31 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=28merge_3=2E2_-=3E_default=29?= =?utf8?q?=3A_Issue_=2314968=3A_merge_with_3=2E2?= Message-ID: http://hg.python.org/cpython/rev/7c9702b08bfb changeset: 77286:7c9702b08bfb parent: 77283:2285a82504dc parent: 77285:bf6305bce3af user: Sandro Tosi date: Fri Jun 01 20:23:46 2012 +0200 summary: Issue #14968: merge with 3.2 files: Doc/library/operator.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/library/operator.rst b/Doc/library/operator.rst --- a/Doc/library/operator.rst +++ b/Doc/library/operator.rst @@ -404,7 +404,7 @@ +-----------------------+-------------------------+---------------------------------------+ Inplace Operators -================= +----------------- Many operations have an "in-place" version. Listed below are functions providing a more primitive access to in-place operators than the usual syntax -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 1 20:26:32 2012 From: python-checkins at python.org (sandro.tosi) Date: Fri, 01 Jun 2012 20:26:32 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=28merge_default_-=3E_default?= =?utf8?q?=29=3A_merge_heads?= Message-ID: http://hg.python.org/cpython/rev/d7c4089e9637 changeset: 77287:d7c4089e9637 parent: 77286:7c9702b08bfb parent: 77284:000cc4e0e1cd user: Sandro Tosi date: Fri Jun 01 20:25:36 2012 +0200 summary: merge heads files: Lib/ipaddress.py | 2 +- Lib/test/test_ipaddress.py | 15 +++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/Lib/ipaddress.py b/Lib/ipaddress.py --- a/Lib/ipaddress.py +++ b/Lib/ipaddress.py @@ -359,7 +359,7 @@ else: if nets and nets[-1]._version != ip._version: raise TypeError("%s and %s are not of the same version" % ( - str(ip), str(ips[-1]))) + str(ip), str(nets[-1]))) nets.append(ip) # sort and dedup diff --git a/Lib/test/test_ipaddress.py b/Lib/test/test_ipaddress.py --- a/Lib/test/test_ipaddress.py +++ b/Lib/test/test_ipaddress.py @@ -596,10 +596,17 @@ self.assertEqual(list(collapsed), [ip3]) # the toejam test - ip1 = ipaddress.ip_address('1.1.1.1') - ip2 = ipaddress.ip_address('::1') - self.assertRaises(TypeError, ipaddress.collapse_addresses, - [ip1, ip2]) + addr_tuples = [ + (ipaddress.ip_address('1.1.1.1'), + ipaddress.ip_address('::1')), + (ipaddress.IPv4Network('1.1.0.0/24'), + ipaddress.IPv6Network('2001::/120')), + (ipaddress.IPv4Network('1.1.0.0/32'), + ipaddress.IPv6Network('2001::/128')), + ] + for ip1, ip2 in addr_tuples: + self.assertRaises(TypeError, ipaddress.collapse_addresses, + [ip1, ip2]) def testSummarizing(self): #ip = ipaddress.ip_address -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 1 22:21:22 2012 From: python-checkins at python.org (r.david.murray) Date: Fri, 01 Jun 2012 22:21:22 +0200 Subject: [Python-checkins] =?utf8?b?Y3B5dGhvbiAoMy4yKTogIzE0OTU3OiBjbGFy?= =?utf8?q?ify_splitlines_docs=2E?= Message-ID: http://hg.python.org/cpython/rev/24572015e24f changeset: 77288:24572015e24f branch: 3.2 parent: 77285:bf6305bce3af user: R David Murray date: Fri Jun 01 16:19:36 2012 -0400 summary: #14957: clarify splitlines docs. Initial patch by Michael Driscoll, I added the example. files: Doc/library/stdtypes.rst | 8 +++++++- 1 files changed, 7 insertions(+), 1 deletions(-) diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -1329,7 +1329,13 @@ Return a list of the lines in the string, breaking at line boundaries. Line breaks are not included in the resulting list unless *keepends* is given and - true. + true. This method uses the universal newlines approach to splitting lines. + Unlike :meth:`~str.split`, if the string ends with line boundary characters + the returned list does ``not`` have an empty last element. + + For example, ``'ab c\n\nde fg\rkl\r\n'.splitlines()`` returns + ``['ab c', '', 'de fg', 'kl']``, while the same call with ``splinelines(True)`` + returns ``['ab c\n', '\n, 'de fg\r', 'kl\r\n']``. .. method:: str.startswith(prefix[, start[, end]]) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 1 22:21:23 2012 From: python-checkins at python.org (r.david.murray) Date: Fri, 01 Jun 2012 22:21:23 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=28merge_3=2E2_-=3E_default=29?= =?utf8?q?=3A_=2314957=3A_clarify_splitlines_docs=2E?= Message-ID: http://hg.python.org/cpython/rev/2a43088318ed changeset: 77289:2a43088318ed parent: 77287:d7c4089e9637 parent: 77288:24572015e24f user: R David Murray date: Fri Jun 01 16:20:26 2012 -0400 summary: #14957: clarify splitlines docs. Initial patch by Michael Driscoll, I added the example. files: Doc/library/stdtypes.rst | 8 +++++++- 1 files changed, 7 insertions(+), 1 deletions(-) diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -1353,7 +1353,13 @@ Return a list of the lines in the string, breaking at line boundaries. Line breaks are not included in the resulting list unless *keepends* is given and - true. + true. This method uses the universal newlines approach to splitting lines. + Unlike :meth:`~str.split`, if the string ends with line boundary characters + the returned list does ``not`` have an empty last element. + + For example, ``'ab c\n\nde fg\rkl\r\n'.splitlines()`` returns + ``['ab c', '', 'de fg', 'kl']``, while the same call with ``splinelines(True)`` + returns ``['ab c\n', '\n, 'de fg\r', 'kl\r\n']``. .. method:: str.startswith(prefix[, start[, end]]) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 1 22:21:24 2012 From: python-checkins at python.org (r.david.murray) Date: Fri, 01 Jun 2012 22:21:24 +0200 Subject: [Python-checkins] =?utf8?b?Y3B5dGhvbiAoMi43KTogIzE0OTU3OiBjbGFy?= =?utf8?q?ify_splitlines_docs=2E?= Message-ID: http://hg.python.org/cpython/rev/0df7594e4ebd changeset: 77290:0df7594e4ebd branch: 2.7 parent: 77265:1a4e99460438 user: R David Murray date: Fri Jun 01 16:21:06 2012 -0400 summary: #14957: clarify splitlines docs. Initial patch by Michael Driscoll, I added the example. files: Doc/library/stdtypes.rst | 8 +++++++- 1 files changed, 7 insertions(+), 1 deletions(-) diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -1185,7 +1185,13 @@ Return a list of the lines in the string, breaking at line boundaries. Line breaks are not included in the resulting list unless *keepends* is given and - true. + true. This method uses the universal newlines approach to splitting lines. + Unlike :meth:`~str.split`, if the string ends with line boundary characters + the returned list does ``not`` have an empty last element. + + For example, ``'ab c\n\nde fg\rkl\r\n'.splitlines()`` returns + ``['ab c', '', 'de fg', 'kl']``, while the same call with ``splinelines(True)`` + returns ``['ab c\n', '\n, 'de fg\r', 'kl\r\n']``. .. method:: str.startswith(prefix[, start[, end]]) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 1 22:47:26 2012 From: python-checkins at python.org (victor.stinner) Date: Fri, 01 Jun 2012 22:47:26 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Fix_sporadic_failure_of_tes?= =?utf8?q?t=5Ftime=2Etest=5Fprocess=5Ftime=28=29_on_Windows?= Message-ID: http://hg.python.org/cpython/rev/bdc7ad1f05ef changeset: 77291:bdc7ad1f05ef parent: 77289:2a43088318ed user: Victor Stinner date: Fri Jun 01 22:45:23 2012 +0200 summary: Fix sporadic failure of test_time.test_process_time() on Windows Use a threshold of 20 ms instead of 10 ms. files: Lib/test/test_time.py | 7 +++++-- 1 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_time.py b/Lib/test/test_time.py --- a/Lib/test/test_time.py +++ b/Lib/test/test_time.py @@ -380,10 +380,13 @@ time.perf_counter() def test_process_time(self): + # process_time() should not include time spend during a sleep start = time.process_time() - time.sleep(0.1) + time.sleep(0.100) stop = time.process_time() - self.assertLess(stop - start, 0.01) + # use 20 ms because process_time() has usually a resolution of 15 ms + # on Windows + self.assertLess(stop - start, 0.020) info = time.get_clock_info('process_time') self.assertTrue(info.monotonic) -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Sat Jun 2 05:53:13 2012 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Sat, 02 Jun 2012 05:53:13 +0200 Subject: [Python-checkins] Daily reference leaks (bdc7ad1f05ef): sum=465 Message-ID: results for bdc7ad1f05ef on branch "default" -------------------------------------------- test_smtplib leaked [154, 154, 154] references, sum=462 test_super leaked [1, 1, 1] references, sum=3 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogHoFJ_w', '-x'] From python-checkins at python.org Sat Jun 2 07:43:16 2012 From: python-checkins at python.org (raymond.hettinger) Date: Sat, 02 Jun 2012 07:43:16 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=282=2E7=29=3A_Improve_tooltip?= =?utf8?q?s_for_splitlines=28=29_by_showing_that_the_default_for_keepends_?= =?utf8?q?is?= Message-ID: http://hg.python.org/cpython/rev/2fbee0b741f7 changeset: 77292:2fbee0b741f7 branch: 2.7 parent: 77290:0df7594e4ebd user: Raymond Hettinger date: Sat Jun 02 01:42:58 2012 -0400 summary: Improve tooltips for splitlines() by showing that the default for keepends is False. files: Objects/bytearrayobject.c | 2 +- Objects/stringobject.c | 2 +- Objects/unicodeobject.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Objects/bytearrayobject.c b/Objects/bytearrayobject.c --- a/Objects/bytearrayobject.c +++ b/Objects/bytearrayobject.c @@ -2649,7 +2649,7 @@ } PyDoc_STRVAR(splitlines__doc__, -"B.splitlines([keepends]) -> list of lines\n\ +"B.splitlines(keepends=False) -> list of lines\n\ \n\ Return a list of the lines in B, breaking at line boundaries.\n\ Line breaks are not included in the resulting list unless keepends\n\ diff --git a/Objects/stringobject.c b/Objects/stringobject.c --- a/Objects/stringobject.c +++ b/Objects/stringobject.c @@ -3545,7 +3545,7 @@ PyDoc_STRVAR(splitlines__doc__, -"S.splitlines([keepends]) -> list of strings\n\ +"S.splitlines(keepends=False) -> list of strings\n\ \n\ Return a list of the lines in S, breaking at line boundaries.\n\ Line breaks are not included in the resulting list unless keepends\n\ diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -7521,7 +7521,7 @@ } PyDoc_STRVAR(splitlines__doc__, - "S.splitlines([keepends]) -> list of strings\n\ + "S.splitlines(keepends=False) -> list of strings\n\ \n\ Return a list of the lines in S, breaking at line boundaries.\n\ Line breaks are not included in the resulting list unless keepends\n\ -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 2 08:58:08 2012 From: python-checkins at python.org (benjamin.peterson) Date: Sat, 02 Jun 2012 08:58:08 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=283=2E2=29=3A_don=27t_leak_if?= =?utf8?q?_the_=5F=5Fclass=5F=5F_closure_is_set?= Message-ID: http://hg.python.org/cpython/rev/ba01cf9a8578 changeset: 77293:ba01cf9a8578 branch: 3.2 parent: 77288:24572015e24f user: Benjamin Peterson date: Fri Jun 01 23:57:36 2012 -0700 summary: don't leak if the __class__ closure is set files: Python/bltinmodule.c | 6 ++---- 1 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -158,10 +158,8 @@ cls = PyEval_CallObjectWithKeywords(meta, margs, mkw); Py_DECREF(margs); } - if (cls != NULL && PyCell_Check(cell)) { - Py_INCREF(cls); - PyCell_SET(cell, cls); - } + if (cls != NULL && PyCell_Check(cell)) + PyCell_Set(cell, cls); Py_DECREF(cell); } Py_DECREF(ns); -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 2 08:58:09 2012 From: python-checkins at python.org (benjamin.peterson) Date: Sat, 02 Jun 2012 08:58:09 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=28merge_3=2E2_-=3E_default=29?= =?utf8?q?=3A_merge_3=2E2?= Message-ID: http://hg.python.org/cpython/rev/56650edb95a2 changeset: 77294:56650edb95a2 parent: 77291:bdc7ad1f05ef parent: 77293:ba01cf9a8578 user: Benjamin Peterson date: Fri Jun 01 23:57:50 2012 -0700 summary: merge 3.2 files: Python/bltinmodule.c | 6 ++---- 1 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -163,10 +163,8 @@ cls = PyEval_CallObjectWithKeywords(meta, margs, mkw); Py_DECREF(margs); } - if (cls != NULL && PyCell_Check(cell)) { - Py_INCREF(cls); - PyCell_SET(cell, cls); - } + if (cls != NULL && PyCell_Check(cell)) + PyCell_Set(cell, cls); Py_DECREF(cell); } Py_DECREF(ns); -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 2 17:17:47 2012 From: python-checkins at python.org (sandro.tosi) Date: Sat, 02 Jun 2012 17:17:47 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Issue_=2314814=3A_minor_spe?= =?utf8?q?lling_fixes?= Message-ID: http://hg.python.org/cpython/rev/facdca62aa68 changeset: 77295:facdca62aa68 user: Sandro Tosi date: Sat Jun 02 17:14:22 2012 +0200 summary: Issue #14814: minor spelling fixes files: Lib/ipaddress.py | 8 ++++---- 1 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Lib/ipaddress.py b/Lib/ipaddress.py --- a/Lib/ipaddress.py +++ b/Lib/ipaddress.py @@ -143,7 +143,7 @@ """Represent an address as 16 packed bytes in network (big-endian) order. Args: - address: An integer representation of an IPv4 IP address. + address: An integer representation of an IPv6 IP address. Returns: The integer address packed as 16 bytes in network (big-endian) order. @@ -1181,7 +1181,7 @@ IPv4Address('192.0.2.1') Raises: - AddressValueError: If ipaddressisn't a valid IPv4 address. + AddressValueError: If ipaddress isn't a valid IPv4 address. """ _BaseAddress.__init__(self, address) @@ -1366,10 +1366,10 @@ IPv4Interface('192.0.2.1') Raises: - AddressValueError: If ipaddressisn't a valid IPv4 address. + AddressValueError: If ipaddress isn't a valid IPv4 address. NetmaskValueError: If the netmask isn't valid for an IPv4 address. - ValueError: If strict was True and a network address was not + ValueError: If strict is True and a network address is not supplied. """ -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 2 17:17:48 2012 From: python-checkins at python.org (sandro.tosi) Date: Sat, 02 Jun 2012 17:17:48 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Issue_=2314814=3A_use_print?= =?utf8?q?=28=29_function?= Message-ID: http://hg.python.org/cpython/rev/4b4044292d09 changeset: 77296:4b4044292d09 user: Sandro Tosi date: Sat Jun 02 17:16:33 2012 +0200 summary: Issue #14814: use print() function files: Doc/howto/ipaddress.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/howto/ipaddress.rst b/Doc/howto/ipaddress.rst --- a/Doc/howto/ipaddress.rst +++ b/Doc/howto/ipaddress.rst @@ -288,4 +288,4 @@ try: ipaddress.IPv4Address(address) except ValueError: - print 'address/netmask is invalid: %s' % address + print('address/netmask is invalid:', address) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 2 17:21:48 2012 From: python-checkins at python.org (r.david.murray) Date: Sat, 02 Jun 2012 17:21:48 +0200 Subject: [Python-checkins] =?utf8?b?Y3B5dGhvbiAoMy4yKTogIzE0OTU3OiBmaXgg?= =?utf8?q?doc_typo=2E?= Message-ID: http://hg.python.org/cpython/rev/4d9b3a58e208 changeset: 77297:4d9b3a58e208 branch: 3.2 parent: 77293:ba01cf9a8578 user: R David Murray date: Sat Jun 02 11:20:29 2012 -0400 summary: #14957: fix doc typo. files: Doc/library/stdtypes.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -1334,7 +1334,7 @@ the returned list does ``not`` have an empty last element. For example, ``'ab c\n\nde fg\rkl\r\n'.splitlines()`` returns - ``['ab c', '', 'de fg', 'kl']``, while the same call with ``splinelines(True)`` + ``['ab c', '', 'de fg', 'kl']``, while the same call with ``splitlines(True)`` returns ``['ab c\n', '\n, 'de fg\r', 'kl\r\n']``. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 2 17:21:48 2012 From: python-checkins at python.org (r.david.murray) Date: Sat, 02 Jun 2012 17:21:48 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=28merge_3=2E2_-=3E_default=29?= =?utf8?q?=3A_=2314957=3A_fix_doc_typo=2E?= Message-ID: http://hg.python.org/cpython/rev/3bb35ad5d9da changeset: 77298:3bb35ad5d9da parent: 77296:4b4044292d09 parent: 77297:4d9b3a58e208 user: R David Murray date: Sat Jun 02 11:20:53 2012 -0400 summary: #14957: fix doc typo. files: Doc/library/stdtypes.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -1358,7 +1358,7 @@ the returned list does ``not`` have an empty last element. For example, ``'ab c\n\nde fg\rkl\r\n'.splitlines()`` returns - ``['ab c', '', 'de fg', 'kl']``, while the same call with ``splinelines(True)`` + ``['ab c', '', 'de fg', 'kl']``, while the same call with ``splitlines(True)`` returns ``['ab c\n', '\n, 'de fg\r', 'kl\r\n']``. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 2 17:21:49 2012 From: python-checkins at python.org (r.david.murray) Date: Sat, 02 Jun 2012 17:21:49 +0200 Subject: [Python-checkins] =?utf8?b?Y3B5dGhvbiAoMi43KTogIzE0OTU3OiBmaXgg?= =?utf8?q?doc_typo=2E?= Message-ID: http://hg.python.org/cpython/rev/48564362b687 changeset: 77299:48564362b687 branch: 2.7 parent: 77292:2fbee0b741f7 user: R David Murray date: Sat Jun 02 11:21:31 2012 -0400 summary: #14957: fix doc typo. files: Doc/library/stdtypes.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -1190,7 +1190,7 @@ the returned list does ``not`` have an empty last element. For example, ``'ab c\n\nde fg\rkl\r\n'.splitlines()`` returns - ``['ab c', '', 'de fg', 'kl']``, while the same call with ``splinelines(True)`` + ``['ab c', '', 'de fg', 'kl']``, while the same call with ``splitlines(True)`` returns ``['ab c\n', '\n, 'de fg\r', 'kl\r\n']``. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 2 18:23:28 2012 From: python-checkins at python.org (sandro.tosi) Date: Sat, 02 Jun 2012 18:23:28 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=282=2E7=29=3A_refer_to_time?= =?utf8?q?=2Estrftime?= Message-ID: http://hg.python.org/cpython/rev/01ab23aadca3 changeset: 77300:01ab23aadca3 branch: 2.7 user: Sandro Tosi date: Sat Jun 02 18:21:06 2012 +0200 summary: refer to time.strftime files: Doc/library/locale.rst | 16 ++++++++-------- 1 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Doc/library/locale.rst b/Doc/library/locale.rst --- a/Doc/library/locale.rst +++ b/Doc/library/locale.rst @@ -164,22 +164,22 @@ .. data:: D_T_FMT - Get a string that can be used as a format string for :func:`strftime` to + Get a string that can be used as a format string for :func:`time.strftime` to represent date and time in a locale-specific way. .. data:: D_FMT - Get a string that can be used as a format string for :func:`strftime` to + Get a string that can be used as a format string for :func:`time.strftime` to represent a date in a locale-specific way. .. data:: T_FMT - Get a string that can be used as a format string for :func:`strftime` to + Get a string that can be used as a format string for :func:`time.strftime` to represent a time in a locale-specific way. .. data:: T_FMT_AMPM - Get a format string for :func:`strftime` to represent time in the am/pm + Get a format string for :func:`time.strftime` to represent time in the am/pm format. .. data:: DAY_1 ... DAY_7 @@ -243,24 +243,24 @@ then-emperor's reign. Normally it should not be necessary to use this value directly. Specifying - the ``E`` modifier in their format strings causes the :func:`strftime` + the ``E`` modifier in their format strings causes the :func:`time.strftime` function to use this information. The format of the returned string is not specified, and therefore you should not assume knowledge of it on different systems. .. data:: ERA_D_T_FMT - Get a format string for :func:`strftime` to represent date and time in a + Get a format string for :func:`time.strftime` to represent date and time in a locale-specific era-based way. .. data:: ERA_D_FMT - Get a format string for :func:`strftime` to represent a date in a + Get a format string for :func:`time.strftime` to represent a date in a locale-specific era-based way. .. data:: ERA_T_FMT - Get a format string for :func:`strftime` to represent a time in a + Get a format string for :func:`time.strftime` to represent a time in a locale-specific era-based way. .. data:: ALT_DIGITS -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 2 18:23:29 2012 From: python-checkins at python.org (sandro.tosi) Date: Sat, 02 Jun 2012 18:23:29 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=283=2E2=29=3A_refer_to_time?= =?utf8?q?=2Estrftime?= Message-ID: http://hg.python.org/cpython/rev/c41779327bac changeset: 77301:c41779327bac branch: 3.2 parent: 77297:4d9b3a58e208 user: Sandro Tosi date: Sat Jun 02 18:22:02 2012 +0200 summary: refer to time.strftime files: Doc/library/locale.rst | 16 ++++++++-------- 1 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Doc/library/locale.rst b/Doc/library/locale.rst --- a/Doc/library/locale.rst +++ b/Doc/library/locale.rst @@ -160,22 +160,22 @@ .. data:: D_T_FMT - Get a string that can be used as a format string for :func:`strftime` to + Get a string that can be used as a format string for :func:`time.strftime` to represent date and time in a locale-specific way. .. data:: D_FMT - Get a string that can be used as a format string for :func:`strftime` to + Get a string that can be used as a format string for :func:`time.strftime` to represent a date in a locale-specific way. .. data:: T_FMT - Get a string that can be used as a format string for :func:`strftime` to + Get a string that can be used as a format string for :func:`time.strftime` to represent a time in a locale-specific way. .. data:: T_FMT_AMPM - Get a format string for :func:`strftime` to represent time in the am/pm + Get a format string for :func:`time.strftime` to represent time in the am/pm format. .. data:: DAY_1 ... DAY_7 @@ -239,24 +239,24 @@ then-emperor's reign. Normally it should not be necessary to use this value directly. Specifying - the ``E`` modifier in their format strings causes the :func:`strftime` + the ``E`` modifier in their format strings causes the :func:`time.strftime` function to use this information. The format of the returned string is not specified, and therefore you should not assume knowledge of it on different systems. .. data:: ERA_D_T_FMT - Get a format string for :func:`strftime` to represent date and time in a + Get a format string for :func:`time.strftime` to represent date and time in a locale-specific era-based way. .. data:: ERA_D_FMT - Get a format string for :func:`strftime` to represent a date in a + Get a format string for :func:`time.strftime` to represent a date in a locale-specific era-based way. .. data:: ERA_T_FMT - Get a format string for :func:`strftime` to represent a time in a + Get a format string for :func:`time.strftime` to represent a time in a locale-specific era-based way. .. data:: ALT_DIGITS -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 2 18:23:30 2012 From: python-checkins at python.org (sandro.tosi) Date: Sat, 02 Jun 2012 18:23:30 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=28merge_3=2E2_-=3E_default=29?= =?utf8?q?=3A_merge_with_3=2E2?= Message-ID: http://hg.python.org/cpython/rev/dac347701b4f changeset: 77302:dac347701b4f parent: 77298:3bb35ad5d9da parent: 77301:c41779327bac user: Sandro Tosi date: Sat Jun 02 18:22:31 2012 +0200 summary: merge with 3.2 files: Doc/library/locale.rst | 16 ++++++++-------- 1 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Doc/library/locale.rst b/Doc/library/locale.rst --- a/Doc/library/locale.rst +++ b/Doc/library/locale.rst @@ -160,22 +160,22 @@ .. data:: D_T_FMT - Get a string that can be used as a format string for :func:`strftime` to + Get a string that can be used as a format string for :func:`time.strftime` to represent date and time in a locale-specific way. .. data:: D_FMT - Get a string that can be used as a format string for :func:`strftime` to + Get a string that can be used as a format string for :func:`time.strftime` to represent a date in a locale-specific way. .. data:: T_FMT - Get a string that can be used as a format string for :func:`strftime` to + Get a string that can be used as a format string for :func:`time.strftime` to represent a time in a locale-specific way. .. data:: T_FMT_AMPM - Get a format string for :func:`strftime` to represent time in the am/pm + Get a format string for :func:`time.strftime` to represent time in the am/pm format. .. data:: DAY_1 ... DAY_7 @@ -239,24 +239,24 @@ then-emperor's reign. Normally it should not be necessary to use this value directly. Specifying - the ``E`` modifier in their format strings causes the :func:`strftime` + the ``E`` modifier in their format strings causes the :func:`time.strftime` function to use this information. The format of the returned string is not specified, and therefore you should not assume knowledge of it on different systems. .. data:: ERA_D_T_FMT - Get a format string for :func:`strftime` to represent date and time in a + Get a format string for :func:`time.strftime` to represent date and time in a locale-specific era-based way. .. data:: ERA_D_FMT - Get a format string for :func:`strftime` to represent a date in a + Get a format string for :func:`time.strftime` to represent a date in a locale-specific era-based way. .. data:: ERA_T_FMT - Get a format string for :func:`strftime` to represent a time in a + Get a format string for :func:`time.strftime` to represent a time in a locale-specific era-based way. .. data:: ALT_DIGITS -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 2 19:40:42 2012 From: python-checkins at python.org (sandro.tosi) Date: Sat, 02 Jun 2012 19:40:42 +0200 Subject: [Python-checkins] =?utf8?b?Y3B5dGhvbiAoMy4yKTogSXNzdWUgIzE0OTI2?= =?utf8?q?=3A_fix_docstring_highlight?= Message-ID: http://hg.python.org/cpython/rev/e2739145657d changeset: 77303:e2739145657d branch: 3.2 parent: 77301:c41779327bac user: Sandro Tosi date: Sat Jun 02 19:40:02 2012 +0200 summary: Issue #14926: fix docstring highlight files: Lib/random.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Lib/random.py b/Lib/random.py --- a/Lib/random.py +++ b/Lib/random.py @@ -96,7 +96,7 @@ None or no argument seeds from current time or from an operating system specific randomness source if available. - For version 2 (the default), all of the bits are used if *a *is a str, + For version 2 (the default), all of the bits are used if *a* is a str, bytes, or bytearray. For version 1, the hash() of *a* is used instead. If *a* is an int, all bits are used. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 2 19:40:43 2012 From: python-checkins at python.org (sandro.tosi) Date: Sat, 02 Jun 2012 19:40:43 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=28merge_3=2E2_-=3E_default=29?= =?utf8?q?=3A_Issue_=2314926=3A_merge_with_3=2E2?= Message-ID: http://hg.python.org/cpython/rev/29148c027986 changeset: 77304:29148c027986 parent: 77302:dac347701b4f parent: 77303:e2739145657d user: Sandro Tosi date: Sat Jun 02 19:40:20 2012 +0200 summary: Issue #14926: merge with 3.2 files: Lib/random.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Lib/random.py b/Lib/random.py --- a/Lib/random.py +++ b/Lib/random.py @@ -96,7 +96,7 @@ None or no argument seeds from current time or from an operating system specific randomness source if available. - For version 2 (the default), all of the bits are used if *a *is a str, + For version 2 (the default), all of the bits are used if *a* is a str, bytes, or bytearray. For version 1, the hash() of *a* is used instead. If *a* is an int, all bits are used. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 2 23:43:34 2012 From: python-checkins at python.org (sandro.tosi) Date: Sat, 02 Jun 2012 23:43:34 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=282=2E7=29=3A_backport_c4bd68?= =?utf8?q?be5fc6_to_2=2E7?= Message-ID: http://hg.python.org/cpython/rev/50d6592791bd changeset: 77305:50d6592791bd branch: 2.7 parent: 77300:01ab23aadca3 user: Sandro Tosi date: Sat Jun 02 23:40:59 2012 +0200 summary: backport c4bd68be5fc6 to 2.7 files: Doc/glossary.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/glossary.rst b/Doc/glossary.rst --- a/Doc/glossary.rst +++ b/Doc/glossary.rst @@ -200,7 +200,7 @@ An object exposing a file-oriented API (with methods such as :meth:`read()` or :meth:`write()`) to an underlying resource. Depending on the way it was created, a file object can mediate access to a real - on-disk file or to another other type of storage or communication device + on-disk file or to another type of storage or communication device (for example standard input/output, in-memory buffers, sockets, pipes, etc.). File objects are also called :dfn:`file-like objects` or :dfn:`streams`. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 2 23:43:35 2012 From: python-checkins at python.org (sandro.tosi) Date: Sat, 02 Jun 2012 23:43:35 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=283=2E2=29=3A_backport_c4bd68?= =?utf8?q?be5fc6_to_3=2E2?= Message-ID: http://hg.python.org/cpython/rev/b17747289da1 changeset: 77306:b17747289da1 branch: 3.2 parent: 77303:e2739145657d user: Sandro Tosi date: Sat Jun 02 23:41:19 2012 +0200 summary: backport c4bd68be5fc6 to 3.2 files: Doc/glossary.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/glossary.rst b/Doc/glossary.rst --- a/Doc/glossary.rst +++ b/Doc/glossary.rst @@ -194,7 +194,7 @@ An object exposing a file-oriented API (with methods such as :meth:`read()` or :meth:`write()`) to an underlying resource. Depending on the way it was created, a file object can mediate access to a real - on-disk file or to another other type of storage or communication device + on-disk file or to another type of storage or communication device (for example standard input/output, in-memory buffers, sockets, pipes, etc.). File objects are also called :dfn:`file-like objects` or :dfn:`streams`. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 2 23:43:36 2012 From: python-checkins at python.org (sandro.tosi) Date: Sat, 02 Jun 2012 23:43:36 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=28merge_3=2E2_-=3E_default=29?= =?utf8?q?=3A_null_merge?= Message-ID: http://hg.python.org/cpython/rev/f9baf7ffff96 changeset: 77307:f9baf7ffff96 parent: 77304:29148c027986 parent: 77306:b17747289da1 user: Sandro Tosi date: Sat Jun 02 23:42:08 2012 +0200 summary: null merge files: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 2 23:57:10 2012 From: python-checkins at python.org (r.david.murray) Date: Sat, 02 Jun 2012 23:57:10 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_=231079=3A_Fix_parsing_of_e?= =?utf8?q?ncoded_words=2E?= Message-ID: http://hg.python.org/cpython/rev/8c03fe231877 changeset: 77308:8c03fe231877 user: R David Murray date: Sat Jun 02 17:56:49 2012 -0400 summary: #1079: Fix parsing of encoded words. This is a behavior change: before this leading and trailing spaces were stripped from ASCII parts, now they are preserved. Without this fix we didn't parse the examples in the RFC correctly, so I think breaking backward compatibility here is justified. Patch by Ralf Schlatterbeck. files: Lib/email/header.py | 45 ++++++- Lib/nntplib.py | 2 +- Lib/test/test_email/test_asian_codecs.py | 2 +- Lib/test/test_email/test_email.py | 81 +++++++++-- Misc/NEWS | 4 + 5 files changed, 114 insertions(+), 20 deletions(-) diff --git a/Lib/email/header.py b/Lib/email/header.py --- a/Lib/email/header.py +++ b/Lib/email/header.py @@ -40,7 +40,6 @@ \? # literal ? (?P.*?) # non-greedy up to the next ?= is the encoded string \?= # literal ?= - (?=[ \t]|$) # whitespace or the end of the string ''', re.VERBOSE | re.IGNORECASE | re.MULTILINE) # Field name regexp, including trailing colon, but not separating whitespace, @@ -86,8 +85,12 @@ words = [] for line in header.splitlines(): parts = ecre.split(line) + first = True while parts: - unencoded = parts.pop(0).strip() + unencoded = parts.pop(0) + if first: + unencoded = unencoded.lstrip() + first = False if unencoded: words.append((unencoded, None, None)) if parts: @@ -95,6 +98,16 @@ encoding = parts.pop(0).lower() encoded = parts.pop(0) words.append((encoded, encoding, charset)) + # Now loop over words and remove words that consist of whitespace + # between two encoded strings. + import sys + droplist = [] + for n, w in enumerate(words): + if n>1 and w[1] and words[n-2][1] and words[n-1][0].isspace(): + droplist.append(n-1) + for d in reversed(droplist): + del words[d] + # The next step is to decode each encoded word by applying the reverse # base64 or quopri transformation. decoded_words is now a list of the # form (decoded_word, charset). @@ -217,22 +230,27 @@ self._normalize() uchunks = [] lastcs = None + lastspace = None for string, charset in self._chunks: # We must preserve spaces between encoded and non-encoded word # boundaries, which means for us we need to add a space when we go # from a charset to None/us-ascii, or from None/us-ascii to a # charset. Only do this for the second and subsequent chunks. + # Don't add a space if the None/us-ascii string already has + # a space (trailing or leading depending on transition) nextcs = charset if nextcs == _charset.UNKNOWN8BIT: original_bytes = string.encode('ascii', 'surrogateescape') string = original_bytes.decode('ascii', 'replace') if uchunks: + hasspace = string and self._nonctext(string[0]) if lastcs not in (None, 'us-ascii'): - if nextcs in (None, 'us-ascii'): + if nextcs in (None, 'us-ascii') and not hasspace: uchunks.append(SPACE) nextcs = None - elif nextcs not in (None, 'us-ascii'): + elif nextcs not in (None, 'us-ascii') and not lastspace: uchunks.append(SPACE) + lastspace = string and self._nonctext(string[-1]) lastcs = nextcs uchunks.append(string) return EMPTYSTRING.join(uchunks) @@ -291,6 +309,11 @@ charset = UTF8 self._chunks.append((s, charset)) + def _nonctext(self, s): + """True if string s is not a ctext character of RFC822. + """ + return s.isspace() or s in ('(', ')', '\\') + def encode(self, splitchars=';, \t', maxlinelen=None, linesep='\n'): r"""Encode a message header into an RFC-compliant format. @@ -334,7 +357,20 @@ maxlinelen = 1000000 formatter = _ValueFormatter(self._headerlen, maxlinelen, self._continuation_ws, splitchars) + lastcs = None + hasspace = lastspace = None for string, charset in self._chunks: + if hasspace is not None: + hasspace = string and self._nonctext(string[0]) + import sys + if lastcs not in (None, 'us-ascii'): + if not hasspace or charset not in (None, 'us-ascii'): + formatter.add_transition() + elif charset not in (None, 'us-ascii') and not lastspace: + formatter.add_transition() + lastspace = string and self._nonctext(string[-1]) + lastcs = charset + hasspace = False lines = string.splitlines() if lines: formatter.feed('', lines[0], charset) @@ -351,6 +387,7 @@ formatter.feed(fws, sline, charset) if len(lines) > 1: formatter.newline() + if self._chunks: formatter.add_transition() value = formatter._str(linesep) if _embeded_header.search(value): diff --git a/Lib/nntplib.py b/Lib/nntplib.py --- a/Lib/nntplib.py +++ b/Lib/nntplib.py @@ -166,7 +166,7 @@ parts.append(v.decode(enc or 'ascii')) else: parts.append(v) - return ' '.join(parts) + return ''.join(parts) def _parse_overview_fmt(lines): """Parse a list of string representing the response to LIST OVERVIEW.FMT diff --git a/Lib/test/test_email/test_asian_codecs.py b/Lib/test/test_email/test_asian_codecs.py --- a/Lib/test/test_email/test_asian_codecs.py +++ b/Lib/test/test_email/test_asian_codecs.py @@ -41,7 +41,7 @@ Hello World! =?iso-2022-jp?b?GyRCJU8lbSE8JW8hPCVrJUkhKhsoQg==?= =?iso-8859-1?q?Gr=FC=DF_Gott!?=""") eq(decode_header(h.encode()), - [(b'Hello World!', None), + [(b'Hello World! ', None), (b'\x1b$B%O%m!<%o!<%k%I!*\x1b(B', 'iso-2022-jp'), (b'Gr\xfc\xdf Gott!', gcode)]) subject_bytes = (b'test-ja \xa4\xd8\xc5\xea\xb9\xc6\xa4\xb5' diff --git a/Lib/test/test_email/test_email.py b/Lib/test/test_email/test_email.py --- a/Lib/test/test_email/test_email.py +++ b/Lib/test/test_email/test_email.py @@ -1994,9 +1994,9 @@ foo bar =?mac-iceland?q?r=8Aksm=9Arg=8Cs?=""" dh = decode_header(s) eq(dh, [ - (b'Re:', None), + (b'Re: ', None), (b'r\x8aksm\x9arg\x8cs', 'mac-iceland'), - (b'baz foo bar', None), + (b' baz foo bar ', None), (b'r\x8aksm\x9arg\x8cs', 'mac-iceland')]) header = make_header(dh) eq(str(header), @@ -2005,36 +2005,38 @@ Re: =?mac-iceland?q?r=8Aksm=9Arg=8Cs?= baz foo bar =?mac-iceland?q?r=8Aksm?= =?mac-iceland?q?=9Arg=8Cs?=""") - def test_whitespace_eater_unicode(self): + def test_whitespace_keeper_unicode(self): eq = self.assertEqual s = '=?ISO-8859-1?Q?Andr=E9?= Pirard ' dh = decode_header(s) eq(dh, [(b'Andr\xe9', 'iso-8859-1'), - (b'Pirard ', None)]) + (b' Pirard ', None)]) header = str(make_header(dh)) eq(header, 'Andr\xe9 Pirard ') - def test_whitespace_eater_unicode_2(self): + def test_whitespace_keeper_unicode_2(self): eq = self.assertEqual s = 'The =?iso-8859-1?b?cXVpY2sgYnJvd24gZm94?= jumped over the =?iso-8859-1?b?bGF6eSBkb2c=?=' dh = decode_header(s) - eq(dh, [(b'The', None), (b'quick brown fox', 'iso-8859-1'), - (b'jumped over the', None), (b'lazy dog', 'iso-8859-1')]) + eq(dh, [(b'The ', None), (b'quick brown fox', 'iso-8859-1'), + (b' jumped over the ', None), (b'lazy dog', 'iso-8859-1')]) hu = str(make_header(dh)) eq(hu, 'The quick brown fox jumped over the lazy dog') def test_rfc2047_missing_whitespace(self): s = 'Sm=?ISO-8859-1?B?9g==?=rg=?ISO-8859-1?B?5Q==?=sbord' dh = decode_header(s) - self.assertEqual(dh, [(s, None)]) - - def test_rfc2047_with_whitespace(self): - s = 'Sm =?ISO-8859-1?B?9g==?= rg =?ISO-8859-1?B?5Q==?= sbord' - dh = decode_header(s) self.assertEqual(dh, [(b'Sm', None), (b'\xf6', 'iso-8859-1'), (b'rg', None), (b'\xe5', 'iso-8859-1'), (b'sbord', None)]) + def test_rfc2047_with_whitespace(self): + s = 'Sm =?ISO-8859-1?B?9g==?= rg =?ISO-8859-1?B?5Q==?= sbord' + dh = decode_header(s) + self.assertEqual(dh, [(b'Sm ', None), (b'\xf6', 'iso-8859-1'), + (b' rg ', None), (b'\xe5', 'iso-8859-1'), + (b' sbord', None)]) + def test_rfc2047_B_bad_padding(self): s = '=?iso-8859-1?B?%s?=' data = [ # only test complete bytes @@ -2051,6 +2053,57 @@ self.assertEqual(decode_header(s), [(b'andr\xe9=zz', 'iso-8659-1')]) + def test_rfc2047_rfc2047_1(self): + # 1st testcase at end of rfc2047 + s = '(=?ISO-8859-1?Q?a?=)' + self.assertEqual(decode_header(s), + [(b'(', None), (b'a', 'iso-8859-1'), (b')', None)]) + + def test_rfc2047_rfc2047_2(self): + # 2nd testcase at end of rfc2047 + s = '(=?ISO-8859-1?Q?a?= b)' + self.assertEqual(decode_header(s), + [(b'(', None), (b'a', 'iso-8859-1'), (b' b)', None)]) + + def test_rfc2047_rfc2047_3(self): + # 3rd testcase at end of rfc2047 + s = '(=?ISO-8859-1?Q?a?= =?ISO-8859-1?Q?b?=)' + self.assertEqual(decode_header(s), + [(b'(', None), (b'ab', 'iso-8859-1'), (b')', None)]) + + def test_rfc2047_rfc2047_4(self): + # 4th testcase at end of rfc2047 + s = '(=?ISO-8859-1?Q?a?= =?ISO-8859-1?Q?b?=)' + self.assertEqual(decode_header(s), + [(b'(', None), (b'ab', 'iso-8859-1'), (b')', None)]) + + def test_rfc2047_rfc2047_5a(self): + # 5th testcase at end of rfc2047 newline is \r\n + s = '(=?ISO-8859-1?Q?a?=\r\n =?ISO-8859-1?Q?b?=)' + self.assertEqual(decode_header(s), + [(b'(', None), (b'ab', 'iso-8859-1'), (b')', None)]) + + def test_rfc2047_rfc2047_5b(self): + # 5th testcase at end of rfc2047 newline is \n + s = '(=?ISO-8859-1?Q?a?=\n =?ISO-8859-1?Q?b?=)' + self.assertEqual(decode_header(s), + [(b'(', None), (b'ab', 'iso-8859-1'), (b')', None)]) + + def test_rfc2047_rfc2047_6(self): + # 6th testcase at end of rfc2047 + s = '(=?ISO-8859-1?Q?a_b?=)' + self.assertEqual(decode_header(s), + [(b'(', None), (b'a b', 'iso-8859-1'), (b')', None)]) + + def test_rfc2047_rfc2047_7(self): + # 7th testcase at end of rfc2047 + s = '(=?ISO-8859-1?Q?a?= =?ISO-8859-2?Q?_b?=)' + self.assertEqual(decode_header(s), + [(b'(', None), (b'a', 'iso-8859-1'), (b' b', 'iso-8859-2'), + (b')', None)]) + self.assertEqual(make_header(decode_header(s)).encode(), s.lower()) + self.assertEqual(str(make_header(decode_header(s))), '(a b)') + # Test the MIMEMessage class class TestMIMEMessage(TestEmailBase): @@ -4388,11 +4441,11 @@ h = make_header(decode_header(s)) eq(h.encode(), s) - def test_whitespace_eater(self): + def test_whitespace_keeper(self): eq = self.assertEqual s = 'Subject: =?koi8-r?b?8NLP18XSy8EgzsEgxsnOwczYztk=?= =?koi8-r?q?=CA?= zz.' parts = decode_header(s) - eq(parts, [(b'Subject:', None), (b'\xf0\xd2\xcf\xd7\xc5\xd2\xcb\xc1 \xce\xc1 \xc6\xc9\xce\xc1\xcc\xd8\xce\xd9\xca', 'koi8-r'), (b'zz.', None)]) + eq(parts, [(b'Subject: ', None), (b'\xf0\xd2\xcf\xd7\xc5\xd2\xcb\xc1 \xce\xc1 \xc6\xc9\xce\xc1\xcc\xd8\xce\xd9\xca', 'koi8-r'), (b' zz.', None)]) hdr = make_header(parts) eq(hdr.encode(), 'Subject: =?koi8-r?b?8NLP18XSy8EgzsEgxsnOwczYztnK?= zz.') diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,10 @@ Library ------- +- Issue #1079: email.header.decode_header now correctly parses all the examples + in RFC2047. There is a necessary visible behavior change: the leading and/or + trailing whitespace on ASCII parts is now preserved. + - Issue #14969: Better handling of exception chaining in contextlib.ExitStack - Issue #14962: Update text coloring in IDLE shell window after changing -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 3 02:25:06 2012 From: python-checkins at python.org (terry.reedy) Date: Sun, 03 Jun 2012 02:25:06 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=282=2E7=29=3A_Issue_10365=3A_?= =?utf8?q?Add_and_replace_comments=3B_condense_defaulted_attribute_access?= =?utf8?q?=2E?= Message-ID: http://hg.python.org/cpython/rev/5b267381eea0 changeset: 77309:5b267381eea0 branch: 2.7 parent: 77305:50d6592791bd user: Terry Jan Reedy date: Sat Jun 02 20:22:35 2012 -0400 summary: Issue 10365: Add and replace comments; condense defaulted attribute access. Code patch by Roger Serwy. files: Lib/idlelib/IOBinding.py | 29 ++++++++++++--------------- 1 files changed, 13 insertions(+), 16 deletions(-) diff --git a/Lib/idlelib/IOBinding.py b/Lib/idlelib/IOBinding.py --- a/Lib/idlelib/IOBinding.py +++ b/Lib/idlelib/IOBinding.py @@ -197,35 +197,32 @@ def open(self, event=None, editFile=None): flist = self.editwin.flist + # Save in case parent window is closed (ie, during askopenfile()). if flist: if not editFile: filename = self.askopenfile() else: filename=editFile if filename: - # If the current window has no filename and hasn't been - # modified, we replace its contents (no loss). Otherwise - # we open a new window. But we won't replace the - # shell window (which has an interp(reter) attribute), which - # gets set to "not modified" at every new prompt. - # Also, make sure the current window has not been closed, - # since it can be closed during the Open File dialog. - try: - interp = self.editwin.interp - except AttributeError: - interp = None - - if self.editwin and not self.filename and \ - self.get_saved() and not interp: + # If editFile is valid and already open, flist.open will + # shift focus to its existing window. + # If the current window exists and is a fresh unnamed, + # unmodified editor window (not an interpreter shell), + # pass self.loadfile to flist.open so it will load the file + # in the current window (if the file is not already open) + # instead of a new window. + if (self.editwin and + not getattr(self.editwin, 'interp', None) and + not self.filename and + self.get_saved()): flist.open(filename, self.loadfile) else: flist.open(filename) else: if self.text: self.text.focus_set() + return "break" - return "break" - # # Code for use outside IDLE: if self.get_saved(): reply = self.maybesave() -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 3 02:25:10 2012 From: python-checkins at python.org (terry.reedy) Date: Sun, 03 Jun 2012 02:25:10 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=283=2E2=29=3A_Issue_10365=3A_?= =?utf8?q?Add_and_replace_comments=3B_condense_defaulted_attribute_access?= =?utf8?q?=2E?= Message-ID: http://hg.python.org/cpython/rev/4f3d4ce8ac9f changeset: 77310:4f3d4ce8ac9f branch: 3.2 parent: 77306:b17747289da1 user: Terry Jan Reedy date: Sat Jun 02 20:22:58 2012 -0400 summary: Issue 10365: Add and replace comments; condense defaulted attribute access. Code patch by Roger Serwy. files: Lib/idlelib/IOBinding.py | 29 ++++++++++++--------------- 1 files changed, 13 insertions(+), 16 deletions(-) diff --git a/Lib/idlelib/IOBinding.py b/Lib/idlelib/IOBinding.py --- a/Lib/idlelib/IOBinding.py +++ b/Lib/idlelib/IOBinding.py @@ -157,35 +157,32 @@ def open(self, event=None, editFile=None): flist = self.editwin.flist + # Save in case parent window is closed (ie, during askopenfile()). if flist: if not editFile: filename = self.askopenfile() else: filename=editFile if filename: - # If the current window has no filename and hasn't been - # modified, we replace its contents (no loss). Otherwise - # we open a new window. But we won't replace the - # shell window (which has an interp(reter) attribute), which - # gets set to "not modified" at every new prompt. - # Also, make sure the current window has not been closed, - # since it can be closed during the Open File dialog. - try: - interp = self.editwin.interp - except AttributeError: - interp = None - - if self.editwin and not self.filename and \ - self.get_saved() and not interp: + # If editFile is valid and already open, flist.open will + # shift focus to its existing window. + # If the current window exists and is a fresh unnamed, + # unmodified editor window (not an interpreter shell), + # pass self.loadfile to flist.open so it will load the file + # in the current window (if the file is not already open) + # instead of a new window. + if (self.editwin and + not getattr(self.editwin, 'interp', None) and + not self.filename and + self.get_saved()): flist.open(filename, self.loadfile) else: flist.open(filename) else: if self.text: self.text.focus_set() + return "break" - return "break" - # # Code for use outside IDLE: if self.get_saved(): reply = self.maybesave() -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 3 02:25:11 2012 From: python-checkins at python.org (terry.reedy) Date: Sun, 03 Jun 2012 02:25:11 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=28merge_3=2E2_-=3E_default=29?= =?utf8?q?=3A_Merge_with_3=2E2_=2310365?= Message-ID: http://hg.python.org/cpython/rev/d9b7399d9e45 changeset: 77311:d9b7399d9e45 parent: 77308:8c03fe231877 parent: 77310:4f3d4ce8ac9f user: Terry Jan Reedy date: Sat Jun 02 20:24:21 2012 -0400 summary: Merge with 3.2 #10365 files: Lib/idlelib/IOBinding.py | 29 ++++++++++++--------------- 1 files changed, 13 insertions(+), 16 deletions(-) diff --git a/Lib/idlelib/IOBinding.py b/Lib/idlelib/IOBinding.py --- a/Lib/idlelib/IOBinding.py +++ b/Lib/idlelib/IOBinding.py @@ -157,35 +157,32 @@ def open(self, event=None, editFile=None): flist = self.editwin.flist + # Save in case parent window is closed (ie, during askopenfile()). if flist: if not editFile: filename = self.askopenfile() else: filename=editFile if filename: - # If the current window has no filename and hasn't been - # modified, we replace its contents (no loss). Otherwise - # we open a new window. But we won't replace the - # shell window (which has an interp(reter) attribute), which - # gets set to "not modified" at every new prompt. - # Also, make sure the current window has not been closed, - # since it can be closed during the Open File dialog. - try: - interp = self.editwin.interp - except AttributeError: - interp = None - - if self.editwin and not self.filename and \ - self.get_saved() and not interp: + # If editFile is valid and already open, flist.open will + # shift focus to its existing window. + # If the current window exists and is a fresh unnamed, + # unmodified editor window (not an interpreter shell), + # pass self.loadfile to flist.open so it will load the file + # in the current window (if the file is not already open) + # instead of a new window. + if (self.editwin and + not getattr(self.editwin, 'interp', None) and + not self.filename and + self.get_saved()): flist.open(filename, self.loadfile) else: flist.open(filename) else: if self.text: self.text.focus_set() + return "break" - return "break" - # # Code for use outside IDLE: if self.get_saved(): reply = self.maybesave() -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 3 04:28:49 2012 From: python-checkins at python.org (brett.cannon) Date: Sun, 03 Jun 2012 04:28:49 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Issue_=2314987=3A_Add_a_mis?= =?utf8?q?sing_import_statement?= Message-ID: http://hg.python.org/cpython/rev/3de5b053d924 changeset: 77312:3de5b053d924 user: Brett Cannon date: Sat Jun 02 22:28:42 2012 -0400 summary: Issue #14987: Add a missing import statement files: Lib/inspect.py | 1 + Misc/NEWS | 2 ++ 2 files changed, 3 insertions(+), 0 deletions(-) diff --git a/Lib/inspect.py b/Lib/inspect.py --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -38,6 +38,7 @@ import sys import tokenize import types +import warnings from operator import attrgetter from collections import namedtuple diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,8 @@ Library ------- +- Issue #14987: Add a missing import statement to inspect. + - Issue #1079: email.header.decode_header now correctly parses all the examples in RFC2047. There is a necessary visible behavior change: the leading and/or trailing whitespace on ASCII parts is now preserved. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 3 05:09:47 2012 From: python-checkins at python.org (eli.bendersky) Date: Sun, 03 Jun 2012 05:09:47 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Fix_unterminated_keyword_ar?= =?utf8?q?ray_passed_to_PyArg=5FParseTupleAndKeywords?= Message-ID: http://hg.python.org/cpython/rev/eb1d633fe307 changeset: 77313:eb1d633fe307 user: Eli Bendersky date: Sun Jun 03 06:09:42 2012 +0300 summary: Fix unterminated keyword array passed to PyArg_ParseTupleAndKeywords files: Modules/_elementtree.c | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c --- a/Modules/_elementtree.c +++ b/Modules/_elementtree.c @@ -1855,7 +1855,7 @@ static int treebuilder_init(PyObject *self, PyObject *args, PyObject *kwds) { - static char *kwlist[] = {"element_factory", NULL}; + static char *kwlist[] = {"element_factory", 0}; PyObject *element_factory = NULL; TreeBuilderObject *self_tb = (TreeBuilderObject *)self; @@ -2762,7 +2762,7 @@ XMLParserObject *self_xp = (XMLParserObject *)self; PyObject *target = NULL, *html = NULL; char *encoding = NULL; - static char *kwlist[] = {"html", "target", "encoding"}; + static char *kwlist[] = {"html", "target", "encoding", 0}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOz:XMLParser", kwlist, &html, &target, &encoding)) { -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 3 05:48:00 2012 From: python-checkins at python.org (eli.bendersky) Date: Sun, 03 Jun 2012 05:48:00 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Issue_=2314424=3A_Document_?= =?utf8?q?PyType=5FGenericAlloc=2C_and_fix_the_documentation_of?= Message-ID: http://hg.python.org/cpython/rev/3c43be281196 changeset: 77314:3c43be281196 user: Eli Bendersky date: Sun Jun 03 06:47:53 2012 +0300 summary: Issue #14424: Document PyType_GenericAlloc, and fix the documentation of PyType_GenericNew files: Doc/c-api/type.rst | 9 +++++---- 1 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Doc/c-api/type.rst b/Doc/c-api/type.rst --- a/Doc/c-api/type.rst +++ b/Doc/c-api/type.rst @@ -70,13 +70,14 @@ .. c:function:: PyObject* PyType_GenericAlloc(PyTypeObject *type, Py_ssize_t nitems) - XXX: Document. - + Generic handler for the :attr:`tp_alloc` slot of a type object. Use + Python's default memory allocation mechanism to allocate a new instance and + initialize all its contents to *NULL*. .. c:function:: PyObject* PyType_GenericNew(PyTypeObject *type, PyObject *args, PyObject *kwds) - Generic handler for the :attr:`tp_new` slot of a type object. Initialize - all instance variables to *NULL*. + Generic handler for the :attr:`tp_new` slot of a type object. Create a + new instance using the type's :attr:`tp_alloc` slot. .. c:function:: int PyType_Ready(PyTypeObject *type) -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Sun Jun 3 05:49:52 2012 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Sun, 03 Jun 2012 05:49:52 +0200 Subject: [Python-checkins] Daily reference leaks (d9b7399d9e45): sum=462 Message-ID: results for d9b7399d9e45 on branch "default" -------------------------------------------- test_smtplib leaked [154, 154, 154] references, sum=462 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogDoXFb_', '-x'] From benjamin at python.org Sun Jun 3 06:01:25 2012 From: benjamin at python.org (Benjamin Peterson) Date: Sat, 2 Jun 2012 21:01:25 -0700 Subject: [Python-checkins] Daily reference leaks (d9b7399d9e45): sum=462 In-Reply-To: References: Message-ID: 2012/6/2 : > results for d9b7399d9e45 on branch "default" > -------------------------------------------- > > test_smtplib leaked [154, 154, 154] references, sum=462 Can other people reproduce this one? I can't. -- Regards, Benjamin From eliben at gmail.com Sun Jun 3 06:28:10 2012 From: eliben at gmail.com (Eli Bendersky) Date: Sun, 3 Jun 2012 06:28:10 +0200 Subject: [Python-checkins] Daily reference leaks (d9b7399d9e45): sum=462 In-Reply-To: References: Message-ID: On Sun, Jun 3, 2012 at 6:01 AM, Benjamin Peterson wrote: > 2012/6/2 ?: >> results for d9b7399d9e45 on branch "default" >> -------------------------------------------- >> >> test_smtplib leaked [154, 154, 154] references, sum=462 > > Can other people reproduce this one? I can't. > I can't either: $ ./python -m test.regrtest -R : test_smtplib [1/1] test_smtplib beginning 9 repetitions 123456789 ......... 1 test OK. [172101 refs] (Ubuntu 10.04, x64 2.6.32-41-generic) From python-checkins at python.org Sun Jun 3 07:07:31 2012 From: python-checkins at python.org (terry.reedy) Date: Sun, 03 Jun 2012 07:07:31 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=282=2E7=29=3A_Issue_12510=3A_?= =?utf8?q?Expand_2_bare_excepts=2E_Improve_comments=2E_Change_deceptive_na?= =?utf8?q?me?= Message-ID: http://hg.python.org/cpython/rev/477508efe4ab changeset: 77315:477508efe4ab branch: 2.7 parent: 77309:5b267381eea0 user: Terry Jan Reedy date: Sun Jun 03 00:58:36 2012 -0400 summary: Issue 12510: Expand 2 bare excepts. Improve comments. Change deceptive name 'name' to 'expression' as the latter is what the string actually represents. The bug in this issue was only catching NameError and AttributeError when evaluating an expression that was not necessarily a name. files: Lib/idlelib/CallTips.py | 30 +++++++++++++++------------- 1 files changed, 16 insertions(+), 14 deletions(-) diff --git a/Lib/idlelib/CallTips.py b/Lib/idlelib/CallTips.py --- a/Lib/idlelib/CallTips.py +++ b/Lib/idlelib/CallTips.py @@ -71,16 +71,16 @@ if not sur_paren: return hp.set_index(sur_paren[0]) - name = hp.get_expression() - if not name or (not evalfuncs and name.find('(') != -1): + expression = hp.get_expression() + if not expression or (not evalfuncs and expression.find('(') != -1): return - arg_text = self.fetch_tip(name) + arg_text = self.fetch_tip(expression) if not arg_text: return self.calltip = self._make_calltip_window() self.calltip.showtip(arg_text, sur_paren[0], sur_paren[1]) - def fetch_tip(self, name): + def fetch_tip(self, expression): """Return the argument list and docstring of a function or class If there is a Python subprocess, get the calltip there. Otherwise, @@ -96,25 +96,27 @@ """ try: rpcclt = self.editwin.flist.pyshell.interp.rpcclt - except: + except AttributeError: rpcclt = None if rpcclt: return rpcclt.remotecall("exec", "get_the_calltip", - (name,), {}) + (expression,), {}) else: - entity = self.get_entity(name) + entity = self.get_entity(expression) return get_arg_text(entity) - def get_entity(self, name): - "Lookup name in a namespace spanning sys.modules and __main.dict__" - if name: + def get_entity(self, expression): + """Return the object corresponding to expression evaluated + in a namespace spanning sys.modules and __main.dict__. + """ + if expression: namespace = sys.modules.copy() namespace.update(__main__.__dict__) try: - return eval(name, namespace) - # any exception is possible if evalfuncs True in open_calltip - # at least Syntax, Name, Attribute, Index, and Key E. if not - except: + return eval(expression, namespace) + except BaseException: + # An uncaught exception closes idle, and eval can raise any + # exception, especially if user classes are involved. return None def _find_constructor(class_ob): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 3 07:07:33 2012 From: python-checkins at python.org (terry.reedy) Date: Sun, 03 Jun 2012 07:07:33 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=283=2E2=29=3A_Issue_12510=3A_?= =?utf8?q?Expand_2_bare_excepts=2E_Improve_comments=2E_Change_deceptive_na?= =?utf8?q?me?= Message-ID: http://hg.python.org/cpython/rev/f927a5c6e4be changeset: 77316:f927a5c6e4be branch: 3.2 parent: 77310:4f3d4ce8ac9f user: Terry Jan Reedy date: Sun Jun 03 00:27:54 2012 -0400 summary: Issue 12510: Expand 2 bare excepts. Improve comments. Change deceptive name 'name' to 'expression' as the latter is what the string actually represents. The bug in this issue was only catching NameError and AttributeError when evaluating an expression that was not necessarily a name. files: Lib/idlelib/CallTips.py | 32 +++++++++++++++------------- 1 files changed, 17 insertions(+), 15 deletions(-) diff --git a/Lib/idlelib/CallTips.py b/Lib/idlelib/CallTips.py --- a/Lib/idlelib/CallTips.py +++ b/Lib/idlelib/CallTips.py @@ -67,18 +67,18 @@ if not sur_paren: return hp.set_index(sur_paren[0]) - name = hp.get_expression() - if not name: + expression = hp.get_expression() + if not expression: return - if not evalfuncs and (name.find('(') != -1): + if not evalfuncs and (expression.find('(') != -1): return - argspec = self.fetch_tip(name) + argspec = self.fetch_tip(expression) if not argspec: return self.active_calltip = self._calltip_window() self.active_calltip.showtip(argspec, sur_paren[0], sur_paren[1]) - def fetch_tip(self, name): + def fetch_tip(self, expression): """Return the argument list and docstring of a function or class. If there is a Python subprocess, get the calltip there. Otherwise, @@ -94,25 +94,27 @@ """ try: rpcclt = self.editwin.flist.pyshell.interp.rpcclt - except: + except AttributeError: rpcclt = None if rpcclt: return rpcclt.remotecall("exec", "get_the_calltip", - (name,), {}) + (expression,), {}) else: - entity = self.get_entity(name) + entity = self.get_entity(expression) return get_argspec(entity) - def get_entity(self, name): - "Lookup name in a namespace spanning sys.modules and __main.dict__." - if name: + def get_entity(self, expression): + """Return the object corresponding to expression evaluated + in a namespace spanning sys.modules and __main.dict__. + """ + if expression: namespace = sys.modules.copy() namespace.update(__main__.__dict__) try: - return eval(name, namespace) - # any exception is possible if evalfuncs True in open_calltip - # at least Syntax, Name, Attribute, Index, and Key E. if not - except: + return eval(expression, namespace) + except BaseException: + # An uncaught exception closes idle, and eval can raise any + # exception, especially if user classes are involved. return None def _find_constructor(class_ob): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 3 07:07:33 2012 From: python-checkins at python.org (terry.reedy) Date: Sun, 03 Jun 2012 07:07:33 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=28merge_3=2E2_-=3E_default=29?= =?utf8?q?=3A_Merge_with_3=2E2_=2312510?= Message-ID: http://hg.python.org/cpython/rev/a7501ddf74ac changeset: 77317:a7501ddf74ac parent: 77314:3c43be281196 parent: 77316:f927a5c6e4be user: Terry Jan Reedy date: Sun Jun 03 01:06:38 2012 -0400 summary: Merge with 3.2 #12510 files: Lib/idlelib/CallTips.py | 32 +++++++++++++++------------- 1 files changed, 17 insertions(+), 15 deletions(-) diff --git a/Lib/idlelib/CallTips.py b/Lib/idlelib/CallTips.py --- a/Lib/idlelib/CallTips.py +++ b/Lib/idlelib/CallTips.py @@ -67,18 +67,18 @@ if not sur_paren: return hp.set_index(sur_paren[0]) - name = hp.get_expression() - if not name: + expression = hp.get_expression() + if not expression: return - if not evalfuncs and (name.find('(') != -1): + if not evalfuncs and (expression.find('(') != -1): return - argspec = self.fetch_tip(name) + argspec = self.fetch_tip(expression) if not argspec: return self.active_calltip = self._calltip_window() self.active_calltip.showtip(argspec, sur_paren[0], sur_paren[1]) - def fetch_tip(self, name): + def fetch_tip(self, expression): """Return the argument list and docstring of a function or class. If there is a Python subprocess, get the calltip there. Otherwise, @@ -94,25 +94,27 @@ """ try: rpcclt = self.editwin.flist.pyshell.interp.rpcclt - except: + except AttributeError: rpcclt = None if rpcclt: return rpcclt.remotecall("exec", "get_the_calltip", - (name,), {}) + (expression,), {}) else: - entity = self.get_entity(name) + entity = self.get_entity(expression) return get_argspec(entity) - def get_entity(self, name): - "Lookup name in a namespace spanning sys.modules and __main.dict__." - if name: + def get_entity(self, expression): + """Return the object corresponding to expression evaluated + in a namespace spanning sys.modules and __main.dict__. + """ + if expression: namespace = sys.modules.copy() namespace.update(__main__.__dict__) try: - return eval(name, namespace) - # any exception is possible if evalfuncs True in open_calltip - # at least Syntax, Name, Attribute, Index, and Key E. if not - except: + return eval(expression, namespace) + except BaseException: + # An uncaught exception closes idle, and eval can raise any + # exception, especially if user classes are involved. return None def _find_constructor(class_ob): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 3 07:09:48 2012 From: python-checkins at python.org (eli.bendersky) Date: Sun, 03 Jun 2012 07:09:48 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Issue_=2314090=3A_fix_some_?= =?utf8?q?minor_C_API_problems_in_default_branch_=283=2E3=29?= Message-ID: http://hg.python.org/cpython/rev/90f0dd118aa4 changeset: 77318:90f0dd118aa4 parent: 77314:3c43be281196 user: Eli Bendersky date: Sun Jun 03 08:07:47 2012 +0300 summary: Issue #14090: fix some minor C API problems in default branch (3.3) files: Doc/c-api/code.rst | 6 +++--- Doc/c-api/conversion.rst | 4 ++-- Doc/c-api/init.rst | 2 +- Doc/c-api/type.rst | 4 ++-- Doc/c-api/unicode.rst | 2 +- Doc/c-api/veryhigh.rst | 6 ------ Include/pythonrun.h | 7 +++++-- Misc/ACKS | 1 + 8 files changed, 15 insertions(+), 17 deletions(-) diff --git a/Doc/c-api/code.rst b/Doc/c-api/code.rst --- a/Doc/c-api/code.rst +++ b/Doc/c-api/code.rst @@ -31,11 +31,11 @@ Return true if *co* is a :class:`code` object -.. c:function:: int PyCode_GetNumFree(PyObject *co) +.. c:function:: int PyCode_GetNumFree(PyCodeObject *co) Return the number of free variables in *co*. -.. c:function:: PyCodeObject *PyCode_New(int argcount, int kwonlyargcount, int nlocals, int stacksize, int flags, PyObject *code, PyObject *consts, PyObject *names, PyObject *varnames, PyObject *freevars, PyObject *cellvars, PyObject *filename, PyObject *name, int firstlineno, PyObject *lnotab) +.. c:function:: PyCodeObject* PyCode_New(int argcount, int kwonlyargcount, int nlocals, int stacksize, int flags, PyObject *code, PyObject *consts, PyObject *names, PyObject *varnames, PyObject *freevars, PyObject *cellvars, PyObject *filename, PyObject *name, int firstlineno, PyObject *lnotab) Return a new code object. If you need a dummy code object to create a frame, use :c:func:`PyCode_NewEmpty` instead. Calling @@ -43,7 +43,7 @@ version since the definition of the bytecode changes often. -.. c:function:: int PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno) +.. c:function:: PyCodeObject* PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno) Return a new empty code object with the specified filename, function name, and first line number. It is illegal to diff --git a/Doc/c-api/conversion.rst b/Doc/c-api/conversion.rst --- a/Doc/c-api/conversion.rst +++ b/Doc/c-api/conversion.rst @@ -119,13 +119,13 @@ .. versionadded:: 3.1 -.. c:function:: char* PyOS_stricmp(char *s1, char *s2) +.. c:function:: int PyOS_stricmp(char *s1, char *s2) Case insensitive comparison of strings. The function works almost identically to :c:func:`strcmp` except that it ignores the case. -.. c:function:: char* PyOS_strnicmp(char *s1, char *s2, Py_ssize_t size) +.. c:function:: int PyOS_strnicmp(char *s1, char *s2, Py_ssize_t size) Case insensitive comparison of strings. The function works almost identically to :c:func:`strncmp` except that it ignores the case. diff --git a/Doc/c-api/init.rst b/Doc/c-api/init.rst --- a/Doc/c-api/init.rst +++ b/Doc/c-api/init.rst @@ -646,7 +646,7 @@ :c:func:`PyGILState_Release` on the same thread. -.. c:function:: PyThreadState PyGILState_GetThisThreadState() +.. c:function:: PyThreadState* PyGILState_GetThisThreadState() Get the current thread state for this thread. May return ``NULL`` if no GILState API has been used on the current thread. Note that the main thread diff --git a/Doc/c-api/type.rst b/Doc/c-api/type.rst --- a/Doc/c-api/type.rst +++ b/Doc/c-api/type.rst @@ -51,13 +51,13 @@ modification of the attributes or base classes of the type. -.. c:function:: int PyType_HasFeature(PyObject *o, int feature) +.. c:function:: int PyType_HasFeature(PyTypeObject *o, int feature) Return true if the type object *o* sets the feature *feature*. Type features are denoted by single bit flags. -.. c:function:: int PyType_IS_GC(PyObject *o) +.. c:function:: int PyType_IS_GC(PyTypeObject *o) Return true if the type object includes support for the cycle detector; this tests the type flag :const:`Py_TPFLAGS_HAVE_GC`. diff --git a/Doc/c-api/unicode.rst b/Doc/c-api/unicode.rst --- a/Doc/c-api/unicode.rst +++ b/Doc/c-api/unicode.rst @@ -1615,7 +1615,7 @@ ISO-8859-1 if it contains non-ASCII characters". -.. c:function:: int PyUnicode_RichCompare(PyObject *left, PyObject *right, int op) +.. c:function:: PyObject* PyUnicode_RichCompare(PyObject *left, PyObject *right, int op) Rich compare two unicode strings and return one of the following: diff --git a/Doc/c-api/veryhigh.rst b/Doc/c-api/veryhigh.rst --- a/Doc/c-api/veryhigh.rst +++ b/Doc/c-api/veryhigh.rst @@ -95,12 +95,6 @@ leaving *closeit* set to ``0`` and *flags* set to *NULL*. -.. c:function:: int PyRun_SimpleFileFlags(FILE *fp, const char *filename, PyCompilerFlags *flags) - - This is a simplified interface to :c:func:`PyRun_SimpleFileExFlags` below, - leaving *closeit* set to ``0``. - - .. c:function:: int PyRun_SimpleFileEx(FILE *fp, const char *filename, int closeit) This is a simplified interface to :c:func:`PyRun_SimpleFileExFlags` below, diff --git a/Include/pythonrun.h b/Include/pythonrun.h --- a/Include/pythonrun.h +++ b/Include/pythonrun.h @@ -82,9 +82,12 @@ PyParser_SimpleParseFileFlags(FP, S, B, 0) #endif PyAPI_FUNC(struct _node *) PyParser_SimpleParseStringFlags(const char *, int, - int); + int); +PyAPI_FUNC(struct _node *) PyParser_SimpleParseStringFlagsFilename(const char *, + const char *, + int, int); PyAPI_FUNC(struct _node *) PyParser_SimpleParseFileFlags(FILE *, const char *, - int, int); + int, int); #ifndef Py_LIMITED_API PyAPI_FUNC(PyObject *) PyRun_StringFlags(const char *, int, PyObject *, diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -989,6 +989,7 @@ Joel Stanley Oliver Steele Greg Stein +Baruch Sterin Chris Stern Alex Stewart Victor Stinner -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 3 07:09:49 2012 From: python-checkins at python.org (eli.bendersky) Date: Sun, 03 Jun 2012 07:09:49 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=28merge_default_-=3E_default?= =?utf8?q?=29=3A_merge?= Message-ID: http://hg.python.org/cpython/rev/5e6676be2224 changeset: 77319:5e6676be2224 parent: 77318:90f0dd118aa4 parent: 77317:a7501ddf74ac user: Eli Bendersky date: Sun Jun 03 08:09:33 2012 +0300 summary: merge files: Lib/idlelib/CallTips.py | 32 +++++++++++++++------------- 1 files changed, 17 insertions(+), 15 deletions(-) diff --git a/Lib/idlelib/CallTips.py b/Lib/idlelib/CallTips.py --- a/Lib/idlelib/CallTips.py +++ b/Lib/idlelib/CallTips.py @@ -67,18 +67,18 @@ if not sur_paren: return hp.set_index(sur_paren[0]) - name = hp.get_expression() - if not name: + expression = hp.get_expression() + if not expression: return - if not evalfuncs and (name.find('(') != -1): + if not evalfuncs and (expression.find('(') != -1): return - argspec = self.fetch_tip(name) + argspec = self.fetch_tip(expression) if not argspec: return self.active_calltip = self._calltip_window() self.active_calltip.showtip(argspec, sur_paren[0], sur_paren[1]) - def fetch_tip(self, name): + def fetch_tip(self, expression): """Return the argument list and docstring of a function or class. If there is a Python subprocess, get the calltip there. Otherwise, @@ -94,25 +94,27 @@ """ try: rpcclt = self.editwin.flist.pyshell.interp.rpcclt - except: + except AttributeError: rpcclt = None if rpcclt: return rpcclt.remotecall("exec", "get_the_calltip", - (name,), {}) + (expression,), {}) else: - entity = self.get_entity(name) + entity = self.get_entity(expression) return get_argspec(entity) - def get_entity(self, name): - "Lookup name in a namespace spanning sys.modules and __main.dict__." - if name: + def get_entity(self, expression): + """Return the object corresponding to expression evaluated + in a namespace spanning sys.modules and __main.dict__. + """ + if expression: namespace = sys.modules.copy() namespace.update(__main__.__dict__) try: - return eval(name, namespace) - # any exception is possible if evalfuncs True in open_calltip - # at least Syntax, Name, Attribute, Index, and Key E. if not - except: + return eval(expression, namespace) + except BaseException: + # An uncaught exception closes idle, and eval can raise any + # exception, especially if user classes are involved. return None def _find_constructor(class_ob): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 3 10:16:49 2012 From: python-checkins at python.org (senthil.kumaran) Date: Sun, 03 Jun 2012 10:16:49 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Issue_14989=3A_http=2Eserve?= =?utf8?q?r_--cgi_option_can_enable_the_CGI_http_server=2E?= Message-ID: http://hg.python.org/cpython/rev/935a656359ae changeset: 77320:935a656359ae parent: 77313:eb1d633fe307 user: Senthil Kumaran date: Sun Jun 03 16:15:54 2012 +0800 summary: Issue 14989: http.server --cgi option can enable the CGI http server. files: Doc/library/http.server.rst | 6 ++++++ Lib/http/server.py | 22 +++++++++++++++------- Misc/NEWS | 3 +++ 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/Doc/library/http.server.rst b/Doc/library/http.server.rst --- a/Doc/library/http.server.rst +++ b/Doc/library/http.server.rst @@ -400,3 +400,9 @@ Note that CGI scripts will be run with UID of user nobody, for security reasons. Problems with the CGI script will be translated to error 403. + +:class:`CGIHTTPRequestHandler` can be enabled in the command line by passing +the ``--cgi`` option.:: + + python -m http.server --cgi 8000 + diff --git a/Lib/http/server.py b/Lib/http/server.py --- a/Lib/http/server.py +++ b/Lib/http/server.py @@ -100,6 +100,8 @@ import time import urllib.parse import copy +import argparse + # Default error message template DEFAULT_ERROR_MESSAGE = """\ @@ -1173,18 +1175,13 @@ def test(HandlerClass = BaseHTTPRequestHandler, - ServerClass = HTTPServer, protocol="HTTP/1.0"): + ServerClass = HTTPServer, protocol="HTTP/1.0", port=8000): """Test the HTTP request handler class. This runs an HTTP server on port 8000 (or the first command line argument). """ - - if sys.argv[1:]: - port = int(sys.argv[1]) - else: - port = 8000 server_address = ('', port) HandlerClass.protocol_version = protocol @@ -1200,4 +1197,15 @@ sys.exit(0) if __name__ == '__main__': - test(HandlerClass=SimpleHTTPRequestHandler) + parser = argparse.ArgumentParser() + parser.add_argument('--cgi', action='store_true', + help='Run as CGI Server') + parser.add_argument('port', action='store', + default=8000, type=int, + nargs='?', + help='Specify alternate port [default: 8000]') + args = parser.parse_args() + if args.cgi: + test(HandlerClass=CGIHTTPRequestHandler, port=args.port) + else: + test(HandlerClass=SimpleHTTPRequestHandler, port=args.port) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,9 @@ Library ------- +- Issue #14989: Make the CGI enable option to http.server available via command + line. + - Issue #14987: Add a missing import statement to inspect. - Issue #1079: email.header.decode_header now correctly parses all the examples -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 3 10:16:50 2012 From: python-checkins at python.org (senthil.kumaran) Date: Sun, 03 Jun 2012 10:16:50 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=28merge_default_-=3E_default?= =?utf8?q?=29=3A_merge_heads?= Message-ID: http://hg.python.org/cpython/rev/1bbb3f481bae changeset: 77321:1bbb3f481bae parent: 77320:935a656359ae parent: 77319:5e6676be2224 user: Senthil Kumaran date: Sun Jun 03 16:16:39 2012 +0800 summary: merge heads files: Doc/c-api/code.rst | 6 ++-- Doc/c-api/conversion.rst | 4 +- Doc/c-api/init.rst | 2 +- Doc/c-api/type.rst | 13 ++++++----- Doc/c-api/unicode.rst | 2 +- Doc/c-api/veryhigh.rst | 6 ----- Include/pythonrun.h | 7 ++++- Lib/idlelib/CallTips.py | 32 ++++++++++++++------------- Misc/ACKS | 1 + 9 files changed, 37 insertions(+), 36 deletions(-) diff --git a/Doc/c-api/code.rst b/Doc/c-api/code.rst --- a/Doc/c-api/code.rst +++ b/Doc/c-api/code.rst @@ -31,11 +31,11 @@ Return true if *co* is a :class:`code` object -.. c:function:: int PyCode_GetNumFree(PyObject *co) +.. c:function:: int PyCode_GetNumFree(PyCodeObject *co) Return the number of free variables in *co*. -.. c:function:: PyCodeObject *PyCode_New(int argcount, int kwonlyargcount, int nlocals, int stacksize, int flags, PyObject *code, PyObject *consts, PyObject *names, PyObject *varnames, PyObject *freevars, PyObject *cellvars, PyObject *filename, PyObject *name, int firstlineno, PyObject *lnotab) +.. c:function:: PyCodeObject* PyCode_New(int argcount, int kwonlyargcount, int nlocals, int stacksize, int flags, PyObject *code, PyObject *consts, PyObject *names, PyObject *varnames, PyObject *freevars, PyObject *cellvars, PyObject *filename, PyObject *name, int firstlineno, PyObject *lnotab) Return a new code object. If you need a dummy code object to create a frame, use :c:func:`PyCode_NewEmpty` instead. Calling @@ -43,7 +43,7 @@ version since the definition of the bytecode changes often. -.. c:function:: int PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno) +.. c:function:: PyCodeObject* PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno) Return a new empty code object with the specified filename, function name, and first line number. It is illegal to diff --git a/Doc/c-api/conversion.rst b/Doc/c-api/conversion.rst --- a/Doc/c-api/conversion.rst +++ b/Doc/c-api/conversion.rst @@ -119,13 +119,13 @@ .. versionadded:: 3.1 -.. c:function:: char* PyOS_stricmp(char *s1, char *s2) +.. c:function:: int PyOS_stricmp(char *s1, char *s2) Case insensitive comparison of strings. The function works almost identically to :c:func:`strcmp` except that it ignores the case. -.. c:function:: char* PyOS_strnicmp(char *s1, char *s2, Py_ssize_t size) +.. c:function:: int PyOS_strnicmp(char *s1, char *s2, Py_ssize_t size) Case insensitive comparison of strings. The function works almost identically to :c:func:`strncmp` except that it ignores the case. diff --git a/Doc/c-api/init.rst b/Doc/c-api/init.rst --- a/Doc/c-api/init.rst +++ b/Doc/c-api/init.rst @@ -646,7 +646,7 @@ :c:func:`PyGILState_Release` on the same thread. -.. c:function:: PyThreadState PyGILState_GetThisThreadState() +.. c:function:: PyThreadState* PyGILState_GetThisThreadState() Get the current thread state for this thread. May return ``NULL`` if no GILState API has been used on the current thread. Note that the main thread diff --git a/Doc/c-api/type.rst b/Doc/c-api/type.rst --- a/Doc/c-api/type.rst +++ b/Doc/c-api/type.rst @@ -51,13 +51,13 @@ modification of the attributes or base classes of the type. -.. c:function:: int PyType_HasFeature(PyObject *o, int feature) +.. c:function:: int PyType_HasFeature(PyTypeObject *o, int feature) Return true if the type object *o* sets the feature *feature*. Type features are denoted by single bit flags. -.. c:function:: int PyType_IS_GC(PyObject *o) +.. c:function:: int PyType_IS_GC(PyTypeObject *o) Return true if the type object includes support for the cycle detector; this tests the type flag :const:`Py_TPFLAGS_HAVE_GC`. @@ -70,13 +70,14 @@ .. c:function:: PyObject* PyType_GenericAlloc(PyTypeObject *type, Py_ssize_t nitems) - XXX: Document. - + Generic handler for the :attr:`tp_alloc` slot of a type object. Use + Python's default memory allocation mechanism to allocate a new instance and + initialize all its contents to *NULL*. .. c:function:: PyObject* PyType_GenericNew(PyTypeObject *type, PyObject *args, PyObject *kwds) - Generic handler for the :attr:`tp_new` slot of a type object. Initialize - all instance variables to *NULL*. + Generic handler for the :attr:`tp_new` slot of a type object. Create a + new instance using the type's :attr:`tp_alloc` slot. .. c:function:: int PyType_Ready(PyTypeObject *type) diff --git a/Doc/c-api/unicode.rst b/Doc/c-api/unicode.rst --- a/Doc/c-api/unicode.rst +++ b/Doc/c-api/unicode.rst @@ -1615,7 +1615,7 @@ ISO-8859-1 if it contains non-ASCII characters". -.. c:function:: int PyUnicode_RichCompare(PyObject *left, PyObject *right, int op) +.. c:function:: PyObject* PyUnicode_RichCompare(PyObject *left, PyObject *right, int op) Rich compare two unicode strings and return one of the following: diff --git a/Doc/c-api/veryhigh.rst b/Doc/c-api/veryhigh.rst --- a/Doc/c-api/veryhigh.rst +++ b/Doc/c-api/veryhigh.rst @@ -95,12 +95,6 @@ leaving *closeit* set to ``0`` and *flags* set to *NULL*. -.. c:function:: int PyRun_SimpleFileFlags(FILE *fp, const char *filename, PyCompilerFlags *flags) - - This is a simplified interface to :c:func:`PyRun_SimpleFileExFlags` below, - leaving *closeit* set to ``0``. - - .. c:function:: int PyRun_SimpleFileEx(FILE *fp, const char *filename, int closeit) This is a simplified interface to :c:func:`PyRun_SimpleFileExFlags` below, diff --git a/Include/pythonrun.h b/Include/pythonrun.h --- a/Include/pythonrun.h +++ b/Include/pythonrun.h @@ -82,9 +82,12 @@ PyParser_SimpleParseFileFlags(FP, S, B, 0) #endif PyAPI_FUNC(struct _node *) PyParser_SimpleParseStringFlags(const char *, int, - int); + int); +PyAPI_FUNC(struct _node *) PyParser_SimpleParseStringFlagsFilename(const char *, + const char *, + int, int); PyAPI_FUNC(struct _node *) PyParser_SimpleParseFileFlags(FILE *, const char *, - int, int); + int, int); #ifndef Py_LIMITED_API PyAPI_FUNC(PyObject *) PyRun_StringFlags(const char *, int, PyObject *, diff --git a/Lib/idlelib/CallTips.py b/Lib/idlelib/CallTips.py --- a/Lib/idlelib/CallTips.py +++ b/Lib/idlelib/CallTips.py @@ -67,18 +67,18 @@ if not sur_paren: return hp.set_index(sur_paren[0]) - name = hp.get_expression() - if not name: + expression = hp.get_expression() + if not expression: return - if not evalfuncs and (name.find('(') != -1): + if not evalfuncs and (expression.find('(') != -1): return - argspec = self.fetch_tip(name) + argspec = self.fetch_tip(expression) if not argspec: return self.active_calltip = self._calltip_window() self.active_calltip.showtip(argspec, sur_paren[0], sur_paren[1]) - def fetch_tip(self, name): + def fetch_tip(self, expression): """Return the argument list and docstring of a function or class. If there is a Python subprocess, get the calltip there. Otherwise, @@ -94,25 +94,27 @@ """ try: rpcclt = self.editwin.flist.pyshell.interp.rpcclt - except: + except AttributeError: rpcclt = None if rpcclt: return rpcclt.remotecall("exec", "get_the_calltip", - (name,), {}) + (expression,), {}) else: - entity = self.get_entity(name) + entity = self.get_entity(expression) return get_argspec(entity) - def get_entity(self, name): - "Lookup name in a namespace spanning sys.modules and __main.dict__." - if name: + def get_entity(self, expression): + """Return the object corresponding to expression evaluated + in a namespace spanning sys.modules and __main.dict__. + """ + if expression: namespace = sys.modules.copy() namespace.update(__main__.__dict__) try: - return eval(name, namespace) - # any exception is possible if evalfuncs True in open_calltip - # at least Syntax, Name, Attribute, Index, and Key E. if not - except: + return eval(expression, namespace) + except BaseException: + # An uncaught exception closes idle, and eval can raise any + # exception, especially if user classes are involved. return None def _find_constructor(class_ob): diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -989,6 +989,7 @@ Joel Stanley Oliver Steele Greg Stein +Baruch Sterin Chris Stern Alex Stewart Victor Stinner -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 3 12:00:56 2012 From: python-checkins at python.org (martin.v.loewis) Date: Sun, 03 Jun 2012 12:00:56 +0200 Subject: [Python-checkins] =?utf8?b?Y3B5dGhvbiAoMy4yKTogSXNzdWUgIzE0OTM3?= =?utf8?q?=3A_Perform_auto-completion_of_filenames_in_strings_even_for?= Message-ID: http://hg.python.org/cpython/rev/41e85ac2ccef changeset: 77322:41e85ac2ccef branch: 3.2 parent: 77316:f927a5c6e4be user: Martin v. L?wis date: Sun Jun 03 11:55:32 2012 +0200 summary: Issue #14937: Perform auto-completion of filenames in strings even for non-ASCII filenames. files: Lib/idlelib/AutoComplete.py | 11 +++++++++-- Lib/idlelib/AutoCompleteWindow.py | 9 +++++++++ Lib/idlelib/NEWS.txt | 6 ++++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/Lib/idlelib/AutoComplete.py b/Lib/idlelib/AutoComplete.py --- a/Lib/idlelib/AutoComplete.py +++ b/Lib/idlelib/AutoComplete.py @@ -124,13 +124,20 @@ curline = self.text.get("insert linestart", "insert") i = j = len(curline) if hp.is_in_string() and (not mode or mode==COMPLETE_FILES): + # Find the beginning of the string + # fetch_completions will look at the file system to determine whether the + # string value constitutes an actual file name + # XXX could consider raw strings here and unescape the string value if it's + # not raw. self._remove_autocomplete_window() mode = COMPLETE_FILES - while i and curline[i-1] in FILENAME_CHARS: + # Find last separator or string start + while i and curline[i-1] not in "'\"" + SEPS: i -= 1 comp_start = curline[i:j] j = i - while i and curline[i-1] in FILENAME_CHARS + SEPS: + # Find string start + while i and curline[i-1] not in "'\"": i -= 1 comp_what = curline[i:j] elif hp.is_in_code() and (not mode or mode==COMPLETE_ATTRIBUTES): diff --git a/Lib/idlelib/AutoCompleteWindow.py b/Lib/idlelib/AutoCompleteWindow.py --- a/Lib/idlelib/AutoCompleteWindow.py +++ b/Lib/idlelib/AutoCompleteWindow.py @@ -354,6 +354,15 @@ # A modifier key, so ignore return + elif event.char: + # Regular character with a non-length-1 keycode + self._change_start(self.start + event.char) + self.lasttypedstart = self.start + self.listbox.select_clear(0, int(self.listbox.curselection()[0])) + self.listbox.select_set(self._binary_search(self.start)) + self._selection_changed() + return "break" + else: # Unknown event, close the window and let it through. self.hide_window() diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt --- a/Lib/idlelib/NEWS.txt +++ b/Lib/idlelib/NEWS.txt @@ -1,3 +1,9 @@ +What's New in IDLE 3.2.4? +========================= + +- Issue #14937: Perform auto-completion of filenames in strings even for + non-ASCII filenames. + What's New in IDLE 3.2.3? ========================= -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 3 12:00:57 2012 From: python-checkins at python.org (martin.v.loewis) Date: Sun, 03 Jun 2012 12:00:57 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=28merge_3=2E2_-=3E_default=29?= =?utf8?q?=3A_Merge_3=2E2=3A_issue_=2314937=2E?= Message-ID: http://hg.python.org/cpython/rev/9aa8af0761ef changeset: 77323:9aa8af0761ef parent: 77321:1bbb3f481bae parent: 77322:41e85ac2ccef user: Martin v. L?wis date: Sun Jun 03 12:00:48 2012 +0200 summary: Merge 3.2: issue #14937. files: Lib/idlelib/AutoComplete.py | 11 +++++++++-- Lib/idlelib/AutoCompleteWindow.py | 9 +++++++++ Lib/idlelib/NEWS.txt | 5 ++++- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/Lib/idlelib/AutoComplete.py b/Lib/idlelib/AutoComplete.py --- a/Lib/idlelib/AutoComplete.py +++ b/Lib/idlelib/AutoComplete.py @@ -124,13 +124,20 @@ curline = self.text.get("insert linestart", "insert") i = j = len(curline) if hp.is_in_string() and (not mode or mode==COMPLETE_FILES): + # Find the beginning of the string + # fetch_completions will look at the file system to determine whether the + # string value constitutes an actual file name + # XXX could consider raw strings here and unescape the string value if it's + # not raw. self._remove_autocomplete_window() mode = COMPLETE_FILES - while i and curline[i-1] in FILENAME_CHARS: + # Find last separator or string start + while i and curline[i-1] not in "'\"" + SEPS: i -= 1 comp_start = curline[i:j] j = i - while i and curline[i-1] in FILENAME_CHARS + SEPS: + # Find string start + while i and curline[i-1] not in "'\"": i -= 1 comp_what = curline[i:j] elif hp.is_in_code() and (not mode or mode==COMPLETE_ATTRIBUTES): diff --git a/Lib/idlelib/AutoCompleteWindow.py b/Lib/idlelib/AutoCompleteWindow.py --- a/Lib/idlelib/AutoCompleteWindow.py +++ b/Lib/idlelib/AutoCompleteWindow.py @@ -354,6 +354,15 @@ # A modifier key, so ignore return + elif event.char: + # Regular character with a non-length-1 keycode + self._change_start(self.start + event.char) + self.lasttypedstart = self.start + self.listbox.select_clear(0, int(self.listbox.curselection()[0])) + self.listbox.select_set(self._binary_search(self.start)) + self._selection_changed() + return "break" + else: # Unknown event, close the window and let it through. self.hide_window() diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt --- a/Lib/idlelib/NEWS.txt +++ b/Lib/idlelib/NEWS.txt @@ -1,6 +1,9 @@ -What's New in IDLE 3.3? +What's New in IDLE 3.3.0? ========================= +- Issue #14937: Perform auto-completion of filenames in strings even for + non-ASCII filenames. + - Issue #8515: Set __file__ when run file in IDLE. Initial patch by Bruce Frederiksen. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 3 12:03:35 2012 From: python-checkins at python.org (martin.v.loewis) Date: Sun, 03 Jun 2012 12:03:35 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Drop_unused_constant=2E?= Message-ID: http://hg.python.org/cpython/rev/8c07769d717e changeset: 77324:8c07769d717e user: Martin v. L?wis date: Sun Jun 03 12:03:29 2012 +0200 summary: Drop unused constant. files: Lib/idlelib/AutoComplete.py | 3 --- 1 files changed, 0 insertions(+), 3 deletions(-) diff --git a/Lib/idlelib/AutoComplete.py b/Lib/idlelib/AutoComplete.py --- a/Lib/idlelib/AutoComplete.py +++ b/Lib/idlelib/AutoComplete.py @@ -9,9 +9,6 @@ from idlelib.configHandler import idleConf -# This string includes all chars that may be in a file name (without a path -# separator) -FILENAME_CHARS = string.ascii_letters + string.digits + os.curdir + "._~#$:-" # This string includes all chars that may be in an identifier ID_CHARS = string.ascii_letters + string.digits + "_" -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 3 12:33:38 2012 From: python-checkins at python.org (martin.v.loewis) Date: Sun, 03 Jun 2012 12:33:38 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=283=2E2=29=3A_Do_not_try_to_i?= =?utf8?q?nsert_control_characters=2E?= Message-ID: http://hg.python.org/cpython/rev/ec5bc858df25 changeset: 77325:ec5bc858df25 branch: 3.2 parent: 77322:41e85ac2ccef user: Martin v. L?wis date: Sun Jun 03 12:26:09 2012 +0200 summary: Do not try to insert control characters. files: Lib/idlelib/AutoCompleteWindow.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Lib/idlelib/AutoCompleteWindow.py b/Lib/idlelib/AutoCompleteWindow.py --- a/Lib/idlelib/AutoCompleteWindow.py +++ b/Lib/idlelib/AutoCompleteWindow.py @@ -354,7 +354,7 @@ # A modifier key, so ignore return - elif event.char: + elif event.char and event.char >= ' ': # Regular character with a non-length-1 keycode self._change_start(self.start + event.char) self.lasttypedstart = self.start -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 3 12:33:39 2012 From: python-checkins at python.org (martin.v.loewis) Date: Sun, 03 Jun 2012 12:33:39 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=283=2E2=29=3A_PEP_3131=3A_sup?= =?utf8?q?port_non-ASCII_characters_in_auto-completion_of_identifiers=2E?= Message-ID: http://hg.python.org/cpython/rev/21a475aee5e0 changeset: 77326:21a475aee5e0 branch: 3.2 user: Martin v. L?wis date: Sun Jun 03 12:32:42 2012 +0200 summary: PEP 3131: support non-ASCII characters in auto-completion of identifiers. files: Lib/idlelib/AutoComplete.py | 2 +- Lib/idlelib/NEWS.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/idlelib/AutoComplete.py b/Lib/idlelib/AutoComplete.py --- a/Lib/idlelib/AutoComplete.py +++ b/Lib/idlelib/AutoComplete.py @@ -143,7 +143,7 @@ elif hp.is_in_code() and (not mode or mode==COMPLETE_ATTRIBUTES): self._remove_autocomplete_window() mode = COMPLETE_ATTRIBUTES - while i and curline[i-1] in ID_CHARS: + while i and curline[i-1] in ID_CHARS or ord(curline[i-1]) > 127: i -= 1 comp_start = curline[i:j] if i and curline[i-1] == '.': diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt --- a/Lib/idlelib/NEWS.txt +++ b/Lib/idlelib/NEWS.txt @@ -2,7 +2,7 @@ ========================= - Issue #14937: Perform auto-completion of filenames in strings even for - non-ASCII filenames. + non-ASCII filenames. Likewise for identifiers. What's New in IDLE 3.2.3? ========================= -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 3 12:33:40 2012 From: python-checkins at python.org (martin.v.loewis) Date: Sun, 03 Jun 2012 12:33:40 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=28merge_3=2E2_-=3E_default=29?= =?utf8?q?=3A_merge_3=2E2?= Message-ID: http://hg.python.org/cpython/rev/0c68d3412ee5 changeset: 77327:0c68d3412ee5 parent: 77324:8c07769d717e parent: 77326:21a475aee5e0 user: Martin v. L?wis date: Sun Jun 03 12:33:23 2012 +0200 summary: merge 3.2 files: Lib/idlelib/AutoComplete.py | 2 +- Lib/idlelib/AutoCompleteWindow.py | 2 +- Lib/idlelib/NEWS.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/idlelib/AutoComplete.py b/Lib/idlelib/AutoComplete.py --- a/Lib/idlelib/AutoComplete.py +++ b/Lib/idlelib/AutoComplete.py @@ -140,7 +140,7 @@ elif hp.is_in_code() and (not mode or mode==COMPLETE_ATTRIBUTES): self._remove_autocomplete_window() mode = COMPLETE_ATTRIBUTES - while i and curline[i-1] in ID_CHARS: + while i and curline[i-1] in ID_CHARS or ord(curline[i-1]) > 127: i -= 1 comp_start = curline[i:j] if i and curline[i-1] == '.': diff --git a/Lib/idlelib/AutoCompleteWindow.py b/Lib/idlelib/AutoCompleteWindow.py --- a/Lib/idlelib/AutoCompleteWindow.py +++ b/Lib/idlelib/AutoCompleteWindow.py @@ -354,7 +354,7 @@ # A modifier key, so ignore return - elif event.char: + elif event.char and event.char >= ' ': # Regular character with a non-length-1 keycode self._change_start(self.start + event.char) self.lasttypedstart = self.start diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt --- a/Lib/idlelib/NEWS.txt +++ b/Lib/idlelib/NEWS.txt @@ -2,7 +2,7 @@ ========================= - Issue #14937: Perform auto-completion of filenames in strings even for - non-ASCII filenames. + non-ASCII filenames. Likewise for identifiers. - Issue #8515: Set __file__ when run file in IDLE. Initial patch by Bruce Frederiksen. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 3 18:27:20 2012 From: python-checkins at python.org (r.david.murray) Date: Sun, 03 Jun 2012 18:27:20 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_=232658=3A_Add_test_for_iss?= =?utf8?q?ue_fixed_by_fix_for_=231079=2E?= Message-ID: http://hg.python.org/cpython/rev/0808cb8c60fd changeset: 77328:0808cb8c60fd user: R David Murray date: Sun Jun 03 12:27:07 2012 -0400 summary: #2658: Add test for issue fixed by fix for #1079. files: Lib/test/test_email/test_email.py | 10 ++++++++++ 1 files changed, 10 insertions(+), 0 deletions(-) diff --git a/Lib/test/test_email/test_email.py b/Lib/test/test_email/test_email.py --- a/Lib/test/test_email/test_email.py +++ b/Lib/test/test_email/test_email.py @@ -2104,6 +2104,16 @@ self.assertEqual(make_header(decode_header(s)).encode(), s.lower()) self.assertEqual(str(make_header(decode_header(s))), '(a b)') + def test_multiline_header(self): + s = '=?windows-1252?q?=22M=FCller_T=22?=\r\n ' + self.assertEqual(decode_header(s), + [(b'"M\xfcller T"', 'windows-1252'), + (b'', None)]) + self.assertEqual(make_header(decode_header(s)).encode(), + ''.join(s.splitlines())) + self.assertEqual(str(make_header(decode_header(s))), + '"M?ller T" ') + # Test the MIMEMessage class class TestMIMEMessage(TestEmailBase): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 3 23:36:52 2012 From: python-checkins at python.org (gregory.p.smith) Date: Sun, 03 Jun 2012 23:36:52 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=283=2E2=29=3A_Fixes_Issue_=23?= =?utf8?q?14992=3A_os=2Emakedirs=28path=2C_exist=5Fok=3DTrue=29_would_rais?= =?utf8?q?e_an_OSError?= Message-ID: http://hg.python.org/cpython/rev/fef529f3de5b changeset: 77329:fef529f3de5b branch: 3.2 parent: 77326:21a475aee5e0 user: Gregory P. Smith date: Sun Jun 03 14:30:44 2012 -0700 summary: Fixes Issue #14992: os.makedirs(path, exist_ok=True) would raise an OSError when the path existed and had the S_ISGID mode bit set when it was not explicitly asked for. This is no longer an exception as mkdir cannot control if the OS sets that bit for it or not. files: Lib/os.py | 16 +++++++++++- Lib/test/test_os.py | 40 ++++++++++++++++++++++++++++---- Misc/NEWS | 5 ++++ 3 files changed, 53 insertions(+), 8 deletions(-) diff --git a/Lib/os.py b/Lib/os.py --- a/Lib/os.py +++ b/Lib/os.py @@ -152,8 +152,20 @@ mkdir(name, mode) except OSError as e: import stat as st - if not (e.errno == errno.EEXIST and exist_ok and path.isdir(name) and - st.S_IMODE(lstat(name).st_mode) == _get_masked_mode(mode)): + dir_exists = path.isdir(name) + expected_mode = _get_masked_mode(mode) + if dir_exists: + # S_ISGID is automatically copied by the OS from parent to child + # directories on mkdir. Don't consider it being set to be a mode + # mismatch as mkdir does not unset it when not specified in mode. + actual_mode = st.S_IMODE(lstat(name).st_mode) & ~st.S_ISGID + else: + actual_mode = -1 + if not (e.errno == errno.EEXIST and exist_ok and dir_exists and + actual_mode == expected_mode): + if dir_exists and actual_mode != expected_mode: + e.strerror += ' (mode %o != expected mode %o)' % ( + actual_mode, expected_mode) raise def removedirs(name): diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -15,6 +15,7 @@ import contextlib import mmap import uuid +import stat from test.script_helper import assert_python_ok # Detect whether we're on a Linux system that uses the (now outdated @@ -574,12 +575,39 @@ path = os.path.join(support.TESTFN, 'dir1') mode = 0o777 old_mask = os.umask(0o022) - os.makedirs(path, mode) - self.assertRaises(OSError, os.makedirs, path, mode) - self.assertRaises(OSError, os.makedirs, path, mode, exist_ok=False) - self.assertRaises(OSError, os.makedirs, path, 0o776, exist_ok=True) - os.makedirs(path, mode=mode, exist_ok=True) - os.umask(old_mask) + try: + os.makedirs(path, mode) + self.assertRaises(OSError, os.makedirs, path, mode) + self.assertRaises(OSError, os.makedirs, path, mode, exist_ok=False) + self.assertRaises(OSError, os.makedirs, path, 0o776, exist_ok=True) + os.makedirs(path, mode=mode, exist_ok=True) + finally: + os.umask(old_mask) + + def test_exist_ok_s_isgid_directory(self): + path = os.path.join(support.TESTFN, 'dir1') + S_ISGID = stat.S_ISGID + mode = 0o777 + old_mask = os.umask(0o022) + try: + existing_testfn_mode = stat.S_IMODE( + os.lstat(support.TESTFN).st_mode) + os.chmod(support.TESTFN, existing_testfn_mode | S_ISGID) + if (os.lstat(support.TESTFN).st_mode & S_ISGID != S_ISGID): + raise unittest.SkipTest('No support for S_ISGID dir mode.') + # The os should apply S_ISGID from the parent dir for us, but + # this test need not depend on that behavior. Be explicit. + os.makedirs(path, mode | S_ISGID) + # http://bugs.python.org/issue14992 + # Should not fail when the bit is already set. + os.makedirs(path, mode, exist_ok=True) + # remove the bit. + os.chmod(path, stat.S_IMODE(os.lstat(path).st_mode) & ~S_ISGID) + with self.assertRaises(OSError): + # Should fail when the bit is not already set when demanded. + os.makedirs(path, mode | S_ISGID, exist_ok=True) + finally: + os.umask(old_mask) def test_exist_ok_existing_regular_file(self): base = support.TESTFN diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,11 @@ Core and Builtins ----------------- +- Issue #14992: os.makedirs(path, exist_ok=True) would raise an OSError + when the path existed and had the S_ISGID mode bit set when it was + not explicitly asked for. This is no longer an exception as mkdir + cannot control if the OS sets that bit for it or not. + - Issue #14775: Fix a potential quadratic dict build-up due to the garbage collector repeatedly trying to untrack dicts. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 3 23:36:53 2012 From: python-checkins at python.org (gregory.p.smith) Date: Sun, 03 Jun 2012 23:36:53 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=28merge_3=2E2_-=3E_default=29?= =?utf8?q?=3A_Fixes_Issue_=2314992=3A_os=2Emakedirs=28path=2C_exist=5Fok?= =?utf8?q?=3DTrue=29_would_raise_an_OSError?= Message-ID: http://hg.python.org/cpython/rev/eed26e508b7e changeset: 77330:eed26e508b7e parent: 77328:0808cb8c60fd parent: 77329:fef529f3de5b user: Gregory P. Smith date: Sun Jun 03 14:35:09 2012 -0700 summary: Fixes Issue #14992: os.makedirs(path, exist_ok=True) would raise an OSError when the path existed and had the S_ISGID mode bit set when it was not explicitly asked for. This is no longer an exception as mkdir cannot control if the OS sets that bit for it or not. files: Lib/os.py | 16 ++++++++++++++-- Lib/test/test_os.py | 25 +++++++++++++++++++++++++ Misc/NEWS | 5 +++++ 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/Lib/os.py b/Lib/os.py --- a/Lib/os.py +++ b/Lib/os.py @@ -160,8 +160,20 @@ try: mkdir(name, mode) except OSError as e: - if not (e.errno == errno.EEXIST and exist_ok and path.isdir(name) and - st.S_IMODE(lstat(name).st_mode) == _get_masked_mode(mode)): + dir_exists = path.isdir(name) + expected_mode = _get_masked_mode(mode) + if dir_exists: + # S_ISGID is automatically copied by the OS from parent to child + # directories on mkdir. Don't consider it being set to be a mode + # mismatch as mkdir does not unset it when not specified in mode. + actual_mode = st.S_IMODE(lstat(name).st_mode) & ~st.S_ISGID + else: + actual_mode = -1 + if not (e.errno == errno.EEXIST and exist_ok and dir_exists and + actual_mode == expected_mode): + if dir_exists and actual_mode != expected_mode: + e.strerror += ' (mode %o != expected mode %o)' % ( + actual_mode, expected_mode) raise def removedirs(name): diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -838,6 +838,31 @@ os.makedirs(path, mode=mode, exist_ok=True) os.umask(old_mask) + def test_exist_ok_s_isgid_directory(self): + path = os.path.join(support.TESTFN, 'dir1') + S_ISGID = stat.S_ISGID + mode = 0o777 + old_mask = os.umask(0o022) + try: + existing_testfn_mode = stat.S_IMODE( + os.lstat(support.TESTFN).st_mode) + os.chmod(support.TESTFN, existing_testfn_mode | S_ISGID) + if (os.lstat(support.TESTFN).st_mode & S_ISGID != S_ISGID): + raise unittest.SkipTest('No support for S_ISGID dir mode.') + # The os should apply S_ISGID from the parent dir for us, but + # this test need not depend on that behavior. Be explicit. + os.makedirs(path, mode | S_ISGID) + # http://bugs.python.org/issue14992 + # Should not fail when the bit is already set. + os.makedirs(path, mode, exist_ok=True) + # remove the bit. + os.chmod(path, stat.S_IMODE(os.lstat(path).st_mode) & ~S_ISGID) + with self.assertRaises(OSError): + # Should fail when the bit is not already set when demanded. + os.makedirs(path, mode | S_ISGID, exist_ok=True) + finally: + os.umask(old_mask) + def test_exist_ok_existing_regular_file(self): base = support.TESTFN path = os.path.join(support.TESTFN, 'dir1') diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,11 @@ Library ------- +- Issue #14992: os.makedirs(path, exist_ok=True) would raise an OSError + when the path existed and had the S_ISGID mode bit set when it was + not explicitly asked for. This is no longer an exception as mkdir + cannot control if the OS sets that bit for it or not. + - Issue #14989: Make the CGI enable option to http.server available via command line. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 3 23:36:54 2012 From: python-checkins at python.org (gregory.p.smith) Date: Sun, 03 Jun 2012 23:36:54 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=283=2E2=29=3A_Move_the_14992_?= =?utf8?q?note_to_the_correct_section=2E?= Message-ID: http://hg.python.org/cpython/rev/4c07e4806e69 changeset: 77331:4c07e4806e69 branch: 3.2 parent: 77329:fef529f3de5b user: Gregory P. Smith date: Sun Jun 03 14:36:01 2012 -0700 summary: Move the 14992 note to the correct section. files: Misc/NEWS | 119 +++++++++++++++++++++-------------------- 1 files changed, 60 insertions(+), 59 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,71 +10,71 @@ Core and Builtins ----------------- +- Issue #14775: Fix a potential quadratic dict build-up due to the garbage + collector repeatedly trying to untrack dicts. + +- Issue #14494: Fix __future__.py and its documentation to note that + absolute imports are the default behavior in 3.0 instead of 2.7. + Patch by Sven Marnach. + +- Issue #14761: Fix potential leak on an error case in the import machinery. + +- Issue #14699: Fix calling the classmethod descriptor directly. + +- Issue #14433: Prevent msvcrt crash in interactive prompt when stdin + is closed. + +- Issue #11603 (again): Setting __repr__ to __str__ now raises a RuntimeError + when repr() or str() is called on such an object. + +- Issue #14658: Fix binding a special method to a builtin implementation of a + special method with a different name. + +- Issue #14630: Fix a memory access bug for instances of a subclass of int + with value 0. + +- Issue #14612: Fix jumping around with blocks by setting f_lineno. + +- Issue #14607: Fix keyword-only arguments which started with ``__``. + +- Issue #13889: Check and (if necessary) set FPU control word before calling + any of the dtoa.c string <-> float conversion functions, on MSVC builds of + Python. This fixes issues when embedding Python in a Delphi app. + +- Issue #14474: Save and restore exception state in thread.start_new_thread() + while writing error message if the thread leaves a unhandled exception. + +- Issue #13019: Fix potential reference leaks in bytearray.extend(). Patch + by Suman Saha. + +- Issue #14378: Fix compiling ast.ImportFrom nodes with a "__future__" string as + the module name that was not interned. + +- Issue #14331: Use significantly less stack space when importing modules by + allocating path buffers on the heap instead of the stack. + +- Issue #14334: Prevent in a segfault in type.__getattribute__ when it was not + passed strings. + +- Issue #1469629: Allow cycles through an object's __dict__ slot to be + collected. (For example if ``x.__dict__ is x``). + +- Issue #14172: Fix reference leak when marshalling a buffer-like object + (other than a bytes object). + +- Issue #13521: dict.setdefault() now does only one lookup for the given key, + making it "atomic" for many purposes. Patch by Filip Gruszczy?ski. + +- Issue #14471: Fix a possible buffer overrun in the winreg module. + +Library +------- + - Issue #14992: os.makedirs(path, exist_ok=True) would raise an OSError when the path existed and had the S_ISGID mode bit set when it was not explicitly asked for. This is no longer an exception as mkdir cannot control if the OS sets that bit for it or not. -- Issue #14775: Fix a potential quadratic dict build-up due to the garbage - collector repeatedly trying to untrack dicts. - -- Issue #14494: Fix __future__.py and its documentation to note that - absolute imports are the default behavior in 3.0 instead of 2.7. - Patch by Sven Marnach. - -- Issue #14761: Fix potential leak on an error case in the import machinery. - -- Issue #14699: Fix calling the classmethod descriptor directly. - -- Issue #14433: Prevent msvcrt crash in interactive prompt when stdin - is closed. - -- Issue #11603 (again): Setting __repr__ to __str__ now raises a RuntimeError - when repr() or str() is called on such an object. - -- Issue #14658: Fix binding a special method to a builtin implementation of a - special method with a different name. - -- Issue #14630: Fix a memory access bug for instances of a subclass of int - with value 0. - -- Issue #14612: Fix jumping around with blocks by setting f_lineno. - -- Issue #14607: Fix keyword-only arguments which started with ``__``. - -- Issue #13889: Check and (if necessary) set FPU control word before calling - any of the dtoa.c string <-> float conversion functions, on MSVC builds of - Python. This fixes issues when embedding Python in a Delphi app. - -- Issue #14474: Save and restore exception state in thread.start_new_thread() - while writing error message if the thread leaves a unhandled exception. - -- Issue #13019: Fix potential reference leaks in bytearray.extend(). Patch - by Suman Saha. - -- Issue #14378: Fix compiling ast.ImportFrom nodes with a "__future__" string as - the module name that was not interned. - -- Issue #14331: Use significantly less stack space when importing modules by - allocating path buffers on the heap instead of the stack. - -- Issue #14334: Prevent in a segfault in type.__getattribute__ when it was not - passed strings. - -- Issue #1469629: Allow cycles through an object's __dict__ slot to be - collected. (For example if ``x.__dict__ is x``). - -- Issue #14172: Fix reference leak when marshalling a buffer-like object - (other than a bytes object). - -- Issue #13521: dict.setdefault() now does only one lookup for the given key, - making it "atomic" for many purposes. Patch by Filip Gruszczy?ski. - -- Issue #14471: Fix a possible buffer overrun in the winreg module. - -Library -------- - - Issue #14962: Update text coloring in IDLE shell window after changing options. Patch by Roger Serwy. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 3 23:36:55 2012 From: python-checkins at python.org (gregory.p.smith) Date: Sun, 03 Jun 2012 23:36:55 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=28merge_3=2E2_-=3E_default=29?= =?utf8?q?=3A_null_merge_from_3=2E2?= Message-ID: http://hg.python.org/cpython/rev/55cb07c01717 changeset: 77332:55cb07c01717 parent: 77330:eed26e508b7e parent: 77331:4c07e4806e69 user: Gregory P. Smith date: Sun Jun 03 14:36:38 2012 -0700 summary: null merge from 3.2 files: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 3 23:40:41 2012 From: python-checkins at python.org (gregory.p.smith) Date: Sun, 03 Jun 2012 23:40:41 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=283=2E2=29=3A_Revert_the_modi?= =?utf8?q?fication_of_e=2Estrerror_in_3=2E2_as_that_kind_of_change_could?= Message-ID: http://hg.python.org/cpython/rev/f3ce3e874a58 changeset: 77333:f3ce3e874a58 branch: 3.2 parent: 77331:4c07e4806e69 user: Gregory P. Smith date: Sun Jun 03 14:39:26 2012 -0700 summary: Revert the modification of e.strerror in 3.2 as that kind of change could break someone's over specified test that depends on the exact error message. files: Lib/os.py | 3 --- 1 files changed, 0 insertions(+), 3 deletions(-) diff --git a/Lib/os.py b/Lib/os.py --- a/Lib/os.py +++ b/Lib/os.py @@ -163,9 +163,6 @@ actual_mode = -1 if not (e.errno == errno.EEXIST and exist_ok and dir_exists and actual_mode == expected_mode): - if dir_exists and actual_mode != expected_mode: - e.strerror += ' (mode %o != expected mode %o)' % ( - actual_mode, expected_mode) raise def removedirs(name): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 3 23:40:42 2012 From: python-checkins at python.org (gregory.p.smith) Date: Sun, 03 Jun 2012 23:40:42 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=28merge_3=2E2_-=3E_default=29?= =?utf8?q?=3A_null_merge_=28not_removing_the_new_feature_in_3=2E3=29?= Message-ID: http://hg.python.org/cpython/rev/5c0ee973a39a changeset: 77334:5c0ee973a39a parent: 77332:55cb07c01717 parent: 77333:f3ce3e874a58 user: Gregory P. Smith date: Sun Jun 03 14:40:32 2012 -0700 summary: null merge (not removing the new feature in 3.3) files: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 4 03:18:26 2012 From: python-checkins at python.org (benjamin.peterson) Date: Mon, 04 Jun 2012 03:18:26 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_=5F=5FGNUC=5F=5F_does_not_i?= =?utf8?q?mply_gcc_version_is_present=2C_so_just_check_for_version?= Message-ID: http://hg.python.org/cpython/rev/696d3631a4a1 changeset: 77335:696d3631a4a1 user: Benjamin Peterson date: Sun Jun 03 18:15:15 2012 -0700 summary: __GNUC__ does not imply gcc version is present, so just check for version (closes #14994) files: Include/pyerrors.h | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Include/pyerrors.h b/Include/pyerrors.h --- a/Include/pyerrors.h +++ b/Include/pyerrors.h @@ -87,7 +87,7 @@ PyAPI_FUNC(void) PyErr_SetExcInfo(PyObject *, PyObject *, PyObject *); #if defined(__clang__) || \ - (defined(__GNUC__) && \ + (defined(__GNUC_MAJOR__) && \ ((__GNUC_MAJOR__ >= 3) || \ (__GNUC_MAJOR__ == 2) && (__GNUC_MINOR__ >= 5))) #define _Py_NO_RETURN __attribute__((__noreturn__)) -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Mon Jun 4 05:52:57 2012 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Mon, 04 Jun 2012 05:52:57 +0200 Subject: [Python-checkins] Daily reference leaks (696d3631a4a1): sum=464 Message-ID: results for 696d3631a4a1 on branch "default" -------------------------------------------- test_dbm leaked [0, 2, 0] references, sum=2 test_smtplib leaked [154, 154, 154] references, sum=462 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogESyyWk', '-x'] From python-checkins at python.org Mon Jun 4 09:21:39 2012 From: python-checkins at python.org (raymond.hettinger) Date: Mon, 04 Jun 2012 09:21:39 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Separate_key_creation_logic?= =?utf8?q?_from_the_sequence_class_that_memoizes_its_hash?= Message-ID: http://hg.python.org/cpython/rev/2d5ca0ea2aab changeset: 77336:2d5ca0ea2aab user: Raymond Hettinger date: Mon Jun 04 00:21:14 2012 -0700 summary: Separate key creation logic from the sequence class that memoizes its hash value. files: Lib/functools.py | 43 ++++++++++++++++++++--------------- 1 files changed, 24 insertions(+), 19 deletions(-) diff --git a/Lib/functools.py b/Lib/functools.py --- a/Lib/functools.py +++ b/Lib/functools.py @@ -142,30 +142,35 @@ _CacheInfo = namedtuple("CacheInfo", ["hits", "misses", "maxsize", "currsize"]) -class _CacheKey(list): - 'Make a cache key from optionally typed positional and keyword arguments' - +class _HashedSeq(list): __slots__ = 'hashvalue' - def __init__(self, args, kwds, typed, - kwd_mark = (object(),), - sorted=sorted, tuple=tuple, type=type, hash=hash): - key = args - if kwds: - sorted_items = sorted(kwds.items()) - key += kwd_mark - for item in sorted_items: - key += item - if typed: - key += tuple(type(v) for v in args) - if kwds: - key += tuple(type(v) for k, v in sorted_items) - self[:] = key - self.hashvalue = hash(key) # so we only have to hash just once + def __init__(self, tup, hash=hash): + self[:] = tup + self.hashvalue = hash(tup) def __hash__(self): return self.hashvalue +def _make_key(args, kwds, typed, + kwd_mark = (object(),), + fasttypes = {int, str, frozenset, type(None)}, + sorted=sorted, tuple=tuple, type=type, len=len): + 'Make a cache key from optionally typed positional and keyword arguments' + key = args + if kwds: + sorted_items = sorted(kwds.items()) + key += kwd_mark + for item in sorted_items: + key += item + if typed: + key += tuple(type(v) for v in args) + if kwds: + key += tuple(type(v) for k, v in sorted_items) + elif len(key) == 1 and type(key[0]) in fasttypes: + return key[0] + return _HashedSeq(key) + def lru_cache(maxsize=128, typed=False): """Least-recently-used cache decorator. @@ -193,7 +198,7 @@ # Constants shared by all lru cache instances: sentinel = object() # unique object used to signal cache misses - make_key = _CacheKey # build a key from the function arguments + make_key = _make_key # build a key from the function arguments PREV, NEXT, KEY, RESULT = 0, 1, 2, 3 # names for the link fields def decorating_function(user_function): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 4 09:32:35 2012 From: python-checkins at python.org (raymond.hettinger) Date: Mon, 04 Jun 2012 09:32:35 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Add_usage_note=2E?= Message-ID: http://hg.python.org/cpython/rev/44a382d49e86 changeset: 77337:44a382d49e86 user: Raymond Hettinger date: Mon Jun 04 00:32:15 2012 -0700 summary: Add usage note. files: Doc/library/functools.rst | 5 +++-- 1 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Doc/library/functools.rst b/Doc/library/functools.rst --- a/Doc/library/functools.rst +++ b/Doc/library/functools.rst @@ -49,8 +49,9 @@ Since a dictionary is used to cache results, the positional and keyword arguments to the function must be hashable. - If *maxsize* is set to None, the LRU feature is disabled and the cache - can grow without bound. + If *maxsize* is set to None, the LRU feature is disabled and the cache can + grow without bound. The LRU feature performs best when *maxsize* is a + power-of-two. If *typed* is set to True, function arguments of different types will be cached separately. For example, ``f(3)`` and ``f(3.0)`` will be treated -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 4 14:20:17 2012 From: python-checkins at python.org (hynek.schlawack) Date: Mon, 04 Jun 2012 14:20:17 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_=2314814=3A_Use_correct_com?= =?utf8?q?parison_for_IP_addresses?= Message-ID: http://hg.python.org/cpython/rev/6808a72fc9ec changeset: 77338:6808a72fc9ec user: Hynek Schlawack date: Mon Jun 04 14:19:39 2012 +0200 summary: #14814: Use correct comparison for IP addresses ipaddress._BaseV4.is_unspecified() compared IP addresses using "in" which fails. files: Lib/ipaddress.py | 2 +- Lib/test/test_ipaddress.py | 1 + 2 files changed, 2 insertions(+), 1 deletions(-) diff --git a/Lib/ipaddress.py b/Lib/ipaddress.py --- a/Lib/ipaddress.py +++ b/Lib/ipaddress.py @@ -1130,7 +1130,7 @@ """ unspecified_address = IPv4Address('0.0.0.0') if isinstance(self, _BaseAddress): - return self in unspecified_address + return self == unspecified_address return (self.network_address == self.broadcast_address == unspecified_address) diff --git a/Lib/test/test_ipaddress.py b/Lib/test/test_ipaddress.py --- a/Lib/test/test_ipaddress.py +++ b/Lib/test/test_ipaddress.py @@ -837,6 +837,7 @@ self.assertEqual(False, ipaddress.ip_network('128.0.0.0').is_loopback) # test addresses + self.assertEqual(True, ipaddress.ip_address('0.0.0.0').is_unspecified) self.assertEqual(True, ipaddress.ip_address('224.1.1.1').is_multicast) self.assertEqual(False, ipaddress.ip_address('240.0.0.0').is_multicast) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 4 15:52:00 2012 From: python-checkins at python.org (barry.warsaw) Date: Mon, 04 Jun 2012 15:52:00 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Eric_Snow=27s_implementatio?= =?utf8?q?n_of_PEP_421=2E?= Message-ID: http://hg.python.org/cpython/rev/9c445f4695c1 changeset: 77339:9c445f4695c1 parent: 77328:0808cb8c60fd user: Barry Warsaw date: Sun Jun 03 16:18:47 2012 -0400 summary: Eric Snow's implementation of PEP 421. Issue 14673: Add sys.implementation files: Doc/library/sys.rst | 38 ++++ Doc/library/types.rst | 24 ++ Include/Python.h | 1 + Include/namespaceobject.h | 17 + Lib/test/test_sys.py | 18 ++ Lib/test/test_types.py | 143 ++++++++++++++++- Lib/types.py | 1 + Makefile.pre.in | 2 + Objects/namespaceobject.c | 225 ++++++++++++++++++++++++++ Objects/object.c | 3 + Python/sysmodule.c | 72 ++++++++- 11 files changed, 541 insertions(+), 3 deletions(-) diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -616,6 +616,44 @@ Thus ``2.1.0a3`` is hexversion ``0x020100a3``. + +.. data:: implementation + + An object containing the information about the implementation of the + currently running Python interpreter. Its attributes are the those + that all Python implementations must implement. They are described + below. + + *name* is the implementation's identifier, like ``'cpython'``. + + *version* is a named tuple, in the same format as + :data:`sys.version_info`. It represents the version of the Python + *implementation*. This has a distinct meaning from the specific + version of the Python *language* to which the currently running + interpreter conforms, which ``sys.version_info`` represents. For + example, for PyPy 1.8 ``sys.implementation.version`` might be + ``sys.version_info(1, 8, 0, 'final', 0)``, whereas ``sys.version_info`` + would be ``sys.version_info(1, 8, 0, 'final', 0)``. For CPython they + are the same value, since it is the reference implementation. + + *hexversion* is the implementation version in hexadecimal format, like + :data:`sys.hexversion`. + + *cache_tag* is the tag used by the import machinery in the filenames of + cached modules. By convention, it would be a composite of the + implementation's name and version, like ``'cpython-33'``. However, a + Python implementation may use some other value if appropriate. If + ``cache_tag`` is set to ``None``, it indicates that module caching should + be disabled. + + Regardless of its contents, :data:`sys.implementation` will not + change during a run of the interpreter, nor between implementation + versions. (It may change between Python language versions, + however.) See `PEP 421` for more information. + + .. versionadded:: 3.3 + + .. data:: int_info A :term:`struct sequence` that holds information about Python's internal diff --git a/Doc/library/types.rst b/Doc/library/types.rst --- a/Doc/library/types.rst +++ b/Doc/library/types.rst @@ -194,3 +194,27 @@ Return a new view of the underlying mapping's values. +.. class:: SimpleNamespace + + A simple :class:`object` subclass that provides attribute access to its + namespace, as well as a meaningful repr. + + Unlike :class:`object`, with ``SimpleNamespace`` you can add and remove + attributes. If a ``SimpleNamespace`` object is initialized with keyword + arguments, those are directly added to the underlying namespace. + + The type is roughly equivalent to the following code:: + + class SimpleNamespace: + def __init__(self, **kwargs): + self.__dict__.update(kwargs) + def __repr__(self): + keys = sorted(self.__dict__) + items = ("{}={!r}".format(k, self.__dict__[k]) for k in keys) + return "{}({})".format(type(self).__name__, ", ".join(items)) + + ``SimpleNamespace`` may be useful as a replacement for ``class NS: pass``. + However, for a structured record type use :func:`~collections.namedtuple` + instead. + + .. versionadded:: 3.3 diff --git a/Include/Python.h b/Include/Python.h --- a/Include/Python.h +++ b/Include/Python.h @@ -101,6 +101,7 @@ #include "warnings.h" #include "weakrefobject.h" #include "structseq.h" +#include "namespaceobject.h" #include "codecs.h" #include "pyerrors.h" diff --git a/Include/namespaceobject.h b/Include/namespaceobject.h new file mode 100644 --- /dev/null +++ b/Include/namespaceobject.h @@ -0,0 +1,17 @@ + +/* simple namespace object interface */ + +#ifndef NAMESPACEOBJECT_H +#define NAMESPACEOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + +PyAPI_DATA(PyTypeObject) _PyNamespace_Type; + +PyAPI_FUNC(PyObject *) _PyNamespace_New(PyObject *kwds); + +#ifdef __cplusplus +} +#endif +#endif /* !NAMESPACEOBJECT_H */ diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -581,6 +581,24 @@ expected = None self.check_fsencoding(fs_encoding, expected) + def test_implementation(self): + # This test applies to all implementations equally. + + levels = {'alpha': 0xA, 'beta': 0xB, 'candidate': 0xC, 'release': 0xF} + + self.assertTrue(hasattr(sys.implementation, 'name')) + self.assertTrue(hasattr(sys.implementation, 'version')) + self.assertTrue(hasattr(sys.implementation, 'hexversion')) + self.assertTrue(hasattr(sys.implementation, 'cache_tag')) + + version = sys.implementation.version + self.assertEqual(version[:2], (version.major, version.minor)) + + hexversion = (version.major << 24 | version.minor << 16 | + version.micro << 8 | levels[version.releaselevel] << 4 | + version.serial << 0) + self.assertEqual(sys.implementation.hexversion, hexversion) + class SizeofTest(unittest.TestCase): diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py --- a/Lib/test/test_types.py +++ b/Lib/test/test_types.py @@ -996,8 +996,149 @@ X = types.new_class("X", (int(), C)) +class SimpleNamespaceTests(unittest.TestCase): + + def test_constructor(self): + ns1 = types.SimpleNamespace() + ns2 = types.SimpleNamespace(x=1, y=2) + ns3 = types.SimpleNamespace(**dict(x=1, y=2)) + + with self.assertRaises(TypeError): + types.SimpleNamespace(1, 2, 3) + + self.assertEqual(len(ns1.__dict__), 0) + self.assertEqual(vars(ns1), {}) + self.assertEqual(len(ns2.__dict__), 2) + self.assertEqual(vars(ns2), {'y': 2, 'x': 1}) + self.assertEqual(len(ns3.__dict__), 2) + self.assertEqual(vars(ns3), {'y': 2, 'x': 1}) + + def test_unbound(self): + ns1 = vars(types.SimpleNamespace()) + ns2 = vars(types.SimpleNamespace(x=1, y=2)) + + self.assertEqual(ns1, {}) + self.assertEqual(ns2, {'y': 2, 'x': 1}) + + def test_underlying_dict(self): + ns1 = types.SimpleNamespace() + ns2 = types.SimpleNamespace(x=1, y=2) + ns3 = types.SimpleNamespace(a=True, b=False) + mapping = ns3.__dict__ + del ns3 + + self.assertEqual(ns1.__dict__, {}) + self.assertEqual(ns2.__dict__, {'y': 2, 'x': 1}) + self.assertEqual(mapping, dict(a=True, b=False)) + + def test_attrget(self): + ns = types.SimpleNamespace(x=1, y=2, w=3) + + self.assertEqual(ns.x, 1) + self.assertEqual(ns.y, 2) + self.assertEqual(ns.w, 3) + with self.assertRaises(AttributeError): + ns.z + + def test_attrset(self): + ns1 = types.SimpleNamespace() + ns2 = types.SimpleNamespace(x=1, y=2, w=3) + ns1.a = 'spam' + ns1.b = 'ham' + ns2.z = 4 + ns2.theta = None + + self.assertEqual(ns1.__dict__, dict(a='spam', b='ham')) + self.assertEqual(ns2.__dict__, dict(x=1, y=2, w=3, z=4, theta=None)) + + def test_attrdel(self): + ns1 = types.SimpleNamespace() + ns2 = types.SimpleNamespace(x=1, y=2, w=3) + + with self.assertRaises(AttributeError): + del ns1.spam + with self.assertRaises(AttributeError): + del ns2.spam + + del ns2.y + self.assertEqual(vars(ns2), dict(w=3, x=1)) + ns2.y = 'spam' + self.assertEqual(vars(ns2), dict(w=3, x=1, y='spam')) + del ns2.y + self.assertEqual(vars(ns2), dict(w=3, x=1)) + + ns1.spam = 5 + self.assertEqual(vars(ns1), dict(spam=5)) + del ns1.spam + self.assertEqual(vars(ns1), {}) + + def test_repr(self): + ns1 = types.SimpleNamespace(x=1, y=2, w=3) + ns2 = types.SimpleNamespace() + ns2.x = "spam" + ns2._y = 5 + + self.assertEqual(repr(ns1), "namespace(w=3, x=1, y=2)") + self.assertEqual(repr(ns2), "namespace(_y=5, x='spam')") + + def test_nested(self): + ns1 = types.SimpleNamespace(a=1, b=2) + ns2 = types.SimpleNamespace() + ns3 = types.SimpleNamespace(x=ns1) + ns2.spam = ns1 + ns2.ham = '?' + ns2.spam = ns3 + + self.assertEqual(vars(ns1), dict(a=1, b=2)) + self.assertEqual(vars(ns2), dict(spam=ns3, ham='?')) + self.assertEqual(ns2.spam, ns3) + self.assertEqual(vars(ns3), dict(x=ns1)) + self.assertEqual(ns3.x.a, 1) + + def test_recursive(self): + ns1 = types.SimpleNamespace(c='cookie') + ns2 = types.SimpleNamespace() + ns3 = types.SimpleNamespace(x=1) + ns1.spam = ns1 + ns2.spam = ns3 + ns3.spam = ns2 + + self.assertEqual(ns1.spam, ns1) + self.assertEqual(ns1.spam.spam, ns1) + self.assertEqual(ns1.spam.spam, ns1.spam) + self.assertEqual(ns2.spam, ns3) + self.assertEqual(ns3.spam, ns2) + self.assertEqual(ns2.spam.spam, ns2) + + def test_recursive_repr(self): + ns1 = types.SimpleNamespace(c='cookie') + ns2 = types.SimpleNamespace() + ns3 = types.SimpleNamespace(x=1) + ns1.spam = ns1 + ns2.spam = ns3 + ns3.spam = ns2 + + self.assertEqual(repr(ns1), + "namespace(c='cookie', spam=namespace(...))") + self.assertEqual(repr(ns2), + "namespace(spam=namespace(spam=namespace(...), x=1))") + + def test_as_dict(self): + ns = types.SimpleNamespace(spam='spamspamspam') + + with self.assertRaises(TypeError): + len(ns) + with self.assertRaises(TypeError): + iter(ns) + with self.assertRaises(TypeError): + 'spam' in ns + with self.assertRaises(TypeError): + ns['spam'] + + def test_main(): - run_unittest(TypesTests, MappingProxyTests, ClassCreationTests) + run_unittest(TypesTests, MappingProxyTests, ClassCreationTests, + SimpleNamespaceTests) if __name__ == '__main__': test_main() diff --git a/Lib/types.py b/Lib/types.py --- a/Lib/types.py +++ b/Lib/types.py @@ -13,6 +13,7 @@ LambdaType = type(lambda: None) # Same as FunctionType CodeType = type(_f.__code__) MappingProxyType = type(type.__dict__) +SimpleNamespace = type(sys.implementation) def _g(): yield 1 diff --git a/Makefile.pre.in b/Makefile.pre.in --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -392,6 +392,7 @@ Objects/memoryobject.o \ Objects/methodobject.o \ Objects/moduleobject.o \ + Objects/namespaceobject.o \ Objects/object.o \ Objects/obmalloc.o \ Objects/capsule.o \ @@ -766,6 +767,7 @@ $(srcdir)/Include/methodobject.h \ $(srcdir)/Include/modsupport.h \ $(srcdir)/Include/moduleobject.h \ + $(srcdir)/Include/namespaceobject.h \ $(srcdir)/Include/node.h \ $(srcdir)/Include/object.h \ $(srcdir)/Include/objimpl.h \ diff --git a/Objects/namespaceobject.c b/Objects/namespaceobject.c new file mode 100644 --- /dev/null +++ b/Objects/namespaceobject.c @@ -0,0 +1,225 @@ +/* namespace object implementation */ + +#include "Python.h" +#include "structmember.h" + + +typedef struct { + PyObject_HEAD + PyObject *ns_dict; +} _PyNamespaceObject; + + +static PyMemberDef namespace_members[] = { + {"__dict__", T_OBJECT, offsetof(_PyNamespaceObject, ns_dict), READONLY}, + {NULL} +}; + + +/* Methods */ + +static PyObject * +namespace_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + _PyNamespaceObject *ns; + ns = PyObject_GC_New(_PyNamespaceObject, &_PyNamespace_Type); + if (ns == NULL) + return NULL; + + ns->ns_dict = PyDict_New(); + if (ns->ns_dict == NULL) { + Py_DECREF(ns); + return NULL; + } + + PyObject_GC_Track(ns); + return (PyObject *)ns; +} + + +static int +namespace_init(_PyNamespaceObject *ns, PyObject *args, PyObject *kwds) +{ + /* ignore args if it's NULL or empty */ + if (args != NULL) { + Py_ssize_t argcount = PyObject_Size(args); + if (argcount < 0) + return argcount; + else if (argcount > 0) { + PyErr_Format(PyExc_TypeError, "no positional arguments expected"); + return -1; + } + } + if (kwds == NULL) + return 0; + return PyDict_Update(ns->ns_dict, kwds); +} + + +static void +namespace_dealloc(_PyNamespaceObject *ns) +{ + PyObject_GC_UnTrack(ns); + Py_CLEAR(ns->ns_dict); + Py_TYPE(ns)->tp_free((PyObject *)ns); +} + + +static PyObject * +namespace_repr(_PyNamespaceObject *ns) +{ + int i, loop_error = 0; + PyObject *pairs = NULL, *d = NULL, *keys = NULL, *keys_iter = NULL; + PyObject *key; + PyObject *separator, *pairsrepr, *repr = NULL; + + i = Py_ReprEnter((PyObject *)ns); + if (i != 0) { + return i > 0 ? PyUnicode_FromString("namespace(...)") : NULL; + } + + pairs = PyList_New(0); + if (pairs == NULL) + goto error; + + d = ((_PyNamespaceObject *)ns)->ns_dict; + assert(d != NULL); + Py_INCREF(d); + + keys = PyDict_Keys(d); + if (keys == NULL) + goto error; + if (PyList_Sort(keys) != 0) + goto error; + + keys_iter = PyObject_GetIter(keys); + if (keys_iter == NULL) + goto error; + + while ((key = PyIter_Next(keys_iter)) != NULL) { + if (PyUnicode_Check(key) && PyUnicode_GET_SIZE(key) > 0) { + PyObject *value, *item; + + value = PyDict_GetItem(d, key); + assert(value != NULL); + + item = PyUnicode_FromFormat("%S=%R", key, value); + if (item == NULL) { + loop_error = 1; + } + else { + loop_error = PyList_Append(pairs, item); + Py_DECREF(item); + } + } + + Py_DECREF(key); + if (loop_error) + goto error; + } + + separator = PyUnicode_FromString(", "); + if (separator == NULL) + goto error; + + pairsrepr = PyUnicode_Join(separator, pairs); + Py_DECREF(separator); + if (pairsrepr == NULL) + goto error; + + repr = PyUnicode_FromFormat("%s(%S)", + ((PyObject *)ns)->ob_type->tp_name, pairsrepr); + Py_DECREF(pairsrepr); + +error: + Py_XDECREF(pairs); + Py_XDECREF(d); + Py_XDECREF(keys); + Py_XDECREF(keys_iter); + Py_ReprLeave((PyObject *)ns); + + return repr; +} + + +static int +namespace_traverse(_PyNamespaceObject *ns, visitproc visit, void *arg) +{ + Py_VISIT(ns->ns_dict); + return 0; +} + + +static int +namespace_clear(_PyNamespaceObject *ns) +{ + Py_CLEAR(ns->ns_dict); + return 0; +} + + +PyDoc_STRVAR(namespace_doc, +"A simple attribute-based namespace.\n\ +\n\ +namespace(**kwargs)"); + +PyTypeObject _PyNamespace_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + "namespace", /* tp_name */ + sizeof(_PyNamespaceObject), /* tp_size */ + 0, /* tp_itemsize */ + (destructor)namespace_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + (reprfunc)namespace_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + PyObject_GenericSetAttr, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_BASETYPE, /* tp_flags */ + namespace_doc, /* tp_doc */ + (traverseproc)namespace_traverse, /* tp_traverse */ + (inquiry)namespace_clear, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + namespace_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + offsetof(_PyNamespaceObject, ns_dict), /* tp_dictoffset */ + (initproc)namespace_init, /* tp_init */ + PyType_GenericAlloc, /* tp_alloc */ + (newfunc)namespace_new, /* tp_new */ + PyObject_GC_Del, /* tp_free */ +}; + + +PyObject * +_PyNamespace_New(PyObject *kwds) +{ + PyObject *ns = namespace_new(&_PyNamespace_Type, NULL, NULL); + if (ns == NULL) + return NULL; + + if (kwds == NULL) + return ns; + if (PyDict_Update(((_PyNamespaceObject *)ns)->ns_dict, kwds) != 0) { + Py_DECREF(ns); + return NULL; + } + + return (PyObject *)ns; +} diff --git a/Objects/object.c b/Objects/object.c --- a/Objects/object.c +++ b/Objects/object.c @@ -1707,6 +1707,9 @@ if (PyType_Ready(&PyZip_Type) < 0) Py_FatalError("Can't initialize zip type"); + + if (PyType_Ready(&_PyNamespace_Type) < 0) + Py_FatalError("Can't initialize namespace type"); } diff --git a/Python/sysmodule.c b/Python/sysmodule.c --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -1261,6 +1261,7 @@ float_info -- a struct sequence with information about the float implementation.\n\ float_repr_style -- string indicating the style of repr() output for floats\n\ hexversion -- version information encoded as a single integer\n\ +implementation -- Python implementation information.\n\ int_info -- a struct sequence with information about the int implementation.\n\ maxsize -- the largest supported length of containers.\n\ maxunicode -- the value of the largest Unicode codepoint\n\ @@ -1454,6 +1455,69 @@ return version_info; } +static PyObject * +make_impl_info(PyObject *version_info) +{ + int res; + PyObject *impl_info, *value, *ns; + + impl_info = PyDict_New(); + if (impl_info == NULL) + return NULL; + + /* populate the dict */ + +#define NAME "cpython" +#define QUOTE(arg) #arg +#define STRIFY(name) QUOTE(name) +#define MAJOR STRIFY(PY_MAJOR_VERSION) +#define MINOR STRIFY(PY_MINOR_VERSION) +#define TAG NAME "-" MAJOR MINOR + value = PyUnicode_FromString(NAME); + if (value == NULL) + goto error; + res = PyDict_SetItemString(impl_info, "name", value); + Py_DECREF(value); + if (res < 0) + goto error; + + value = PyUnicode_FromString(TAG); + if (value == NULL) + goto error; + res = PyDict_SetItemString(impl_info, "cache_tag", value); + Py_DECREF(value); + if (res < 0) + goto error; +#undef NAME +#undef QUOTE +#undef STRIFY +#undef MAJOR +#undef MINOR +#undef TAG + + res = PyDict_SetItemString(impl_info, "version", version_info); + if (res < 0) + goto error; + + value = PyLong_FromLong(PY_VERSION_HEX); + if (value == NULL) + goto error; + res = PyDict_SetItemString(impl_info, "hexversion", value); + Py_DECREF(value); + if (res < 0) + goto error; + + /* dict ready */ + + ns = _PyNamespace_New(impl_info); + Py_DECREF(impl_info); + return ns; + +error: + Py_CLEAR(impl_info); + return NULL; +} + static struct PyModuleDef sysmodule = { PyModuleDef_HEAD_INIT, "sys", @@ -1469,7 +1533,7 @@ PyObject * _PySys_Init(void) { - PyObject *m, *v, *sysdict; + PyObject *m, *v, *sysdict, *version_info; char *s; m = PyModule_Create(&sysmodule); @@ -1589,11 +1653,15 @@ /* version_info */ if (VersionInfoType.tp_name == 0) PyStructSequence_InitType(&VersionInfoType, &version_info_desc); - SET_SYS_FROM_STRING("version_info", make_version_info()); + version_info = make_version_info(); + SET_SYS_FROM_STRING("version_info", version_info); /* prevent user from creating new instances */ VersionInfoType.tp_init = NULL; VersionInfoType.tp_new = NULL; + /* implementation */ + SET_SYS_FROM_STRING("implementation", make_impl_info(version_info)); + /* flags */ if (FlagsType.tp_name == 0) PyStructSequence_InitType(&FlagsType, &flags_desc); -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 4 15:52:01 2012 From: python-checkins at python.org (barry.warsaw) Date: Mon, 04 Jun 2012 15:52:01 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=28merge_default_-=3E_default?= =?utf8?b?KTogVHJ1bmsgbWVyZ2Uu?= Message-ID: http://hg.python.org/cpython/rev/4b83cb7d1e81 changeset: 77340:4b83cb7d1e81 parent: 77339:9c445f4695c1 parent: 77338:6808a72fc9ec user: Barry Warsaw date: Mon Jun 04 09:41:48 2012 -0400 summary: Trunk merge. files: Doc/library/functools.rst | 5 +- Include/pyerrors.h | 2 +- Lib/functools.py | 43 ++++++++++++++----------- Lib/ipaddress.py | 2 +- Lib/os.py | 16 ++++++++- Lib/test/test_ipaddress.py | 1 + Lib/test/test_os.py | 25 +++++++++++++++ Misc/NEWS | 5 +++ 8 files changed, 74 insertions(+), 25 deletions(-) diff --git a/Doc/library/functools.rst b/Doc/library/functools.rst --- a/Doc/library/functools.rst +++ b/Doc/library/functools.rst @@ -49,8 +49,9 @@ Since a dictionary is used to cache results, the positional and keyword arguments to the function must be hashable. - If *maxsize* is set to None, the LRU feature is disabled and the cache - can grow without bound. + If *maxsize* is set to None, the LRU feature is disabled and the cache can + grow without bound. The LRU feature performs best when *maxsize* is a + power-of-two. If *typed* is set to True, function arguments of different types will be cached separately. For example, ``f(3)`` and ``f(3.0)`` will be treated diff --git a/Include/pyerrors.h b/Include/pyerrors.h --- a/Include/pyerrors.h +++ b/Include/pyerrors.h @@ -87,7 +87,7 @@ PyAPI_FUNC(void) PyErr_SetExcInfo(PyObject *, PyObject *, PyObject *); #if defined(__clang__) || \ - (defined(__GNUC__) && \ + (defined(__GNUC_MAJOR__) && \ ((__GNUC_MAJOR__ >= 3) || \ (__GNUC_MAJOR__ == 2) && (__GNUC_MINOR__ >= 5))) #define _Py_NO_RETURN __attribute__((__noreturn__)) diff --git a/Lib/functools.py b/Lib/functools.py --- a/Lib/functools.py +++ b/Lib/functools.py @@ -142,30 +142,35 @@ _CacheInfo = namedtuple("CacheInfo", ["hits", "misses", "maxsize", "currsize"]) -class _CacheKey(list): - 'Make a cache key from optionally typed positional and keyword arguments' - +class _HashedSeq(list): __slots__ = 'hashvalue' - def __init__(self, args, kwds, typed, - kwd_mark = (object(),), - sorted=sorted, tuple=tuple, type=type, hash=hash): - key = args - if kwds: - sorted_items = sorted(kwds.items()) - key += kwd_mark - for item in sorted_items: - key += item - if typed: - key += tuple(type(v) for v in args) - if kwds: - key += tuple(type(v) for k, v in sorted_items) - self[:] = key - self.hashvalue = hash(key) # so we only have to hash just once + def __init__(self, tup, hash=hash): + self[:] = tup + self.hashvalue = hash(tup) def __hash__(self): return self.hashvalue +def _make_key(args, kwds, typed, + kwd_mark = (object(),), + fasttypes = {int, str, frozenset, type(None)}, + sorted=sorted, tuple=tuple, type=type, len=len): + 'Make a cache key from optionally typed positional and keyword arguments' + key = args + if kwds: + sorted_items = sorted(kwds.items()) + key += kwd_mark + for item in sorted_items: + key += item + if typed: + key += tuple(type(v) for v in args) + if kwds: + key += tuple(type(v) for k, v in sorted_items) + elif len(key) == 1 and type(key[0]) in fasttypes: + return key[0] + return _HashedSeq(key) + def lru_cache(maxsize=128, typed=False): """Least-recently-used cache decorator. @@ -193,7 +198,7 @@ # Constants shared by all lru cache instances: sentinel = object() # unique object used to signal cache misses - make_key = _CacheKey # build a key from the function arguments + make_key = _make_key # build a key from the function arguments PREV, NEXT, KEY, RESULT = 0, 1, 2, 3 # names for the link fields def decorating_function(user_function): diff --git a/Lib/ipaddress.py b/Lib/ipaddress.py --- a/Lib/ipaddress.py +++ b/Lib/ipaddress.py @@ -1130,7 +1130,7 @@ """ unspecified_address = IPv4Address('0.0.0.0') if isinstance(self, _BaseAddress): - return self in unspecified_address + return self == unspecified_address return (self.network_address == self.broadcast_address == unspecified_address) diff --git a/Lib/os.py b/Lib/os.py --- a/Lib/os.py +++ b/Lib/os.py @@ -160,8 +160,20 @@ try: mkdir(name, mode) except OSError as e: - if not (e.errno == errno.EEXIST and exist_ok and path.isdir(name) and - st.S_IMODE(lstat(name).st_mode) == _get_masked_mode(mode)): + dir_exists = path.isdir(name) + expected_mode = _get_masked_mode(mode) + if dir_exists: + # S_ISGID is automatically copied by the OS from parent to child + # directories on mkdir. Don't consider it being set to be a mode + # mismatch as mkdir does not unset it when not specified in mode. + actual_mode = st.S_IMODE(lstat(name).st_mode) & ~st.S_ISGID + else: + actual_mode = -1 + if not (e.errno == errno.EEXIST and exist_ok and dir_exists and + actual_mode == expected_mode): + if dir_exists and actual_mode != expected_mode: + e.strerror += ' (mode %o != expected mode %o)' % ( + actual_mode, expected_mode) raise def removedirs(name): diff --git a/Lib/test/test_ipaddress.py b/Lib/test/test_ipaddress.py --- a/Lib/test/test_ipaddress.py +++ b/Lib/test/test_ipaddress.py @@ -837,6 +837,7 @@ self.assertEqual(False, ipaddress.ip_network('128.0.0.0').is_loopback) # test addresses + self.assertEqual(True, ipaddress.ip_address('0.0.0.0').is_unspecified) self.assertEqual(True, ipaddress.ip_address('224.1.1.1').is_multicast) self.assertEqual(False, ipaddress.ip_address('240.0.0.0').is_multicast) diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -838,6 +838,31 @@ os.makedirs(path, mode=mode, exist_ok=True) os.umask(old_mask) + def test_exist_ok_s_isgid_directory(self): + path = os.path.join(support.TESTFN, 'dir1') + S_ISGID = stat.S_ISGID + mode = 0o777 + old_mask = os.umask(0o022) + try: + existing_testfn_mode = stat.S_IMODE( + os.lstat(support.TESTFN).st_mode) + os.chmod(support.TESTFN, existing_testfn_mode | S_ISGID) + if (os.lstat(support.TESTFN).st_mode & S_ISGID != S_ISGID): + raise unittest.SkipTest('No support for S_ISGID dir mode.') + # The os should apply S_ISGID from the parent dir for us, but + # this test need not depend on that behavior. Be explicit. + os.makedirs(path, mode | S_ISGID) + # http://bugs.python.org/issue14992 + # Should not fail when the bit is already set. + os.makedirs(path, mode, exist_ok=True) + # remove the bit. + os.chmod(path, stat.S_IMODE(os.lstat(path).st_mode) & ~S_ISGID) + with self.assertRaises(OSError): + # Should fail when the bit is not already set when demanded. + os.makedirs(path, mode | S_ISGID, exist_ok=True) + finally: + os.umask(old_mask) + def test_exist_ok_existing_regular_file(self): base = support.TESTFN path = os.path.join(support.TESTFN, 'dir1') diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,11 @@ Library ------- +- Issue #14992: os.makedirs(path, exist_ok=True) would raise an OSError + when the path existed and had the S_ISGID mode bit set when it was + not explicitly asked for. This is no longer an exception as mkdir + cannot control if the OS sets that bit for it or not. + - Issue #14989: Make the CGI enable option to http.server available via command line. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 4 15:52:02 2012 From: python-checkins at python.org (barry.warsaw) Date: Mon, 04 Jun 2012 15:52:02 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Add_NEWS_entry=2E?= Message-ID: http://hg.python.org/cpython/rev/abb5ee3159a6 changeset: 77341:abb5ee3159a6 user: Barry Warsaw date: Mon Jun 04 09:51:53 2012 -0400 summary: Add NEWS entry. files: Misc/NEWS | 5 +++++ 1 files changed, 5 insertions(+), 0 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -7,6 +7,11 @@ *Release date: XX-XXX-2012* +Core and Builtins +----------------- + +- Issue #14673: Add Eric Snow's sys.implementation implementation. + Library ------- -- Repository URL: http://hg.python.org/cpython From brett at python.org Mon Jun 4 16:16:24 2012 From: brett at python.org (Brett Cannon) Date: Mon, 4 Jun 2012 10:16:24 -0400 Subject: [Python-checkins] cpython: Eric Snow's implementation of PEP 421. In-Reply-To: References: Message-ID: On Mon, Jun 4, 2012 at 9:52 AM, barry.warsaw wrote: > http://hg.python.org/cpython/rev/9c445f4695c1 > changeset: 77339:9c445f4695c1 > parent: 77328:0808cb8c60fd > user: Barry Warsaw > date: Sun Jun 03 16:18:47 2012 -0400 > summary: > Eric Snow's implementation of PEP 421. > > Issue 14673: Add sys.implementation > > files: > Doc/library/sys.rst | 38 ++++ > Doc/library/types.rst | 24 ++ > Include/Python.h | 1 + > Include/namespaceobject.h | 17 + > Lib/test/test_sys.py | 18 ++ > Lib/test/test_types.py | 143 ++++++++++++++++- > Lib/types.py | 1 + > Makefile.pre.in | 2 + > Objects/namespaceobject.c | 225 ++++++++++++++++++++++++++ > Objects/object.c | 3 + > Python/sysmodule.c | 72 ++++++++- > 11 files changed, 541 insertions(+), 3 deletions(-) > > > diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst > --- a/Doc/library/sys.rst > +++ b/Doc/library/sys.rst > @@ -616,6 +616,44 @@ > > Thus ``2.1.0a3`` is hexversion ``0x020100a3``. > > + > +.. data:: implementation > + > + An object containing the information about the implementation of the > + currently running Python interpreter. Its attributes are the those > "the those" -> "those" > + that all Python implementations must implement. Should you mention that VMs are allowed to add their own attributes that are not listed? > They are described > + below. > + > + *name* is the implementation's identifier, like ``'cpython'``. > Is this guaranteed to be lowercase, or does it simply happen to be lowercase in this instance? > + > + *version* is a named tuple, in the same format as > + :data:`sys.version_info`. It represents the version of the Python > + *implementation*. This has a distinct meaning from the specific > + version of the Python *language* to which the currently running > + interpreter conforms, which ``sys.version_info`` represents. For > + example, for PyPy 1.8 ``sys.implementation.version`` might be > + ``sys.version_info(1, 8, 0, 'final', 0)``, whereas ``sys.version_info`` > + would be ``sys.version_info(1, 8, 0, 'final', 0)``. I think you meant to say ``sys.version_info(2, 7, 2, 'final', 0)``. > For CPython they > + are the same value, since it is the reference implementation. > + > + *hexversion* is the implementation version in hexadecimal format, like > + :data:`sys.hexversion`. > + > + *cache_tag* is the tag used by the import machinery in the filenames of > + cached modules. By convention, it would be a composite of the > + implementation's name and version, like ``'cpython-33'``. However, a > + Python implementation may use some other value if appropriate. If > + ``cache_tag`` is set to ``None``, it indicates that module caching > should > + be disabled. > + > + Regardless of its contents, :data:`sys.implementation` will not > + change during a run of the interpreter, nor between implementation > + versions. (It may change between Python language versions, > + however.) See `PEP 421` for more information. > + > + .. versionadded:: 3.3 > + > + > .. data:: int_info > > A :term:`struct sequence` that holds information about Python's internal > diff --git a/Doc/library/types.rst b/Doc/library/types.rst > --- a/Doc/library/types.rst > +++ b/Doc/library/types.rst > @@ -194,3 +194,27 @@ > Return a new view of the underlying mapping's values. > > > +.. class:: SimpleNamespace > + > + A simple :class:`object` subclass that provides attribute access to its > + namespace, as well as a meaningful repr. > + > + Unlike :class:`object`, with ``SimpleNamespace`` you can add and remove > + attributes. If a ``SimpleNamespace`` object is initialized with > keyword > + arguments, those are directly added to the underlying namespace. > + > + The type is roughly equivalent to the following code:: > + > + class SimpleNamespace: > + def __init__(self, **kwargs): > + self.__dict__.update(kwargs) > + def __repr__(self): > + keys = sorted(self.__dict__) > + items = ("{}={!r}".format(k, self.__dict__[k]) for k in > keys) > + return "{}({})".format(type(self).__name__, ", > ".join(items)) > + > + ``SimpleNamespace`` may be useful as a replacement for ``class NS: > pass``. > + However, for a structured record type use > :func:`~collections.namedtuple` > What's with the ~? -Brett > + instead. > + > + .. versionadded:: 3.3 > diff --git a/Include/Python.h b/Include/Python.h > --- a/Include/Python.h > +++ b/Include/Python.h > @@ -101,6 +101,7 @@ > #include "warnings.h" > #include "weakrefobject.h" > #include "structseq.h" > +#include "namespaceobject.h" > > #include "codecs.h" > #include "pyerrors.h" > diff --git a/Include/namespaceobject.h b/Include/namespaceobject.h > new file mode 100644 > --- /dev/null > +++ b/Include/namespaceobject.h > @@ -0,0 +1,17 @@ > + > +/* simple namespace object interface */ > + > +#ifndef NAMESPACEOBJECT_H > +#define NAMESPACEOBJECT_H > +#ifdef __cplusplus > +extern "C" { > +#endif > + > +PyAPI_DATA(PyTypeObject) _PyNamespace_Type; > + > +PyAPI_FUNC(PyObject *) _PyNamespace_New(PyObject *kwds); > + > +#ifdef __cplusplus > +} > +#endif > +#endif /* !NAMESPACEOBJECT_H */ > diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py > --- a/Lib/test/test_sys.py > +++ b/Lib/test/test_sys.py > @@ -581,6 +581,24 @@ > expected = None > self.check_fsencoding(fs_encoding, expected) > > + def test_implementation(self): > + # This test applies to all implementations equally. > + > + levels = {'alpha': 0xA, 'beta': 0xB, 'candidate': 0xC, 'release': > 0xF} > + > + self.assertTrue(hasattr(sys.implementation, 'name')) > + self.assertTrue(hasattr(sys.implementation, 'version')) > + self.assertTrue(hasattr(sys.implementation, 'hexversion')) > + self.assertTrue(hasattr(sys.implementation, 'cache_tag')) > + > + version = sys.implementation.version > + self.assertEqual(version[:2], (version.major, version.minor)) > + > + hexversion = (version.major << 24 | version.minor << 16 | > + version.micro << 8 | levels[version.releaselevel] > << 4 | > + version.serial << 0) > + self.assertEqual(sys.implementation.hexversion, hexversion) > + > > class SizeofTest(unittest.TestCase): > > diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py > --- a/Lib/test/test_types.py > +++ b/Lib/test/test_types.py > @@ -996,8 +996,149 @@ > X = types.new_class("X", (int(), C)) > > > +class SimpleNamespaceTests(unittest.TestCase): > + > + def test_constructor(self): > + ns1 = types.SimpleNamespace() > + ns2 = types.SimpleNamespace(x=1, y=2) > + ns3 = types.SimpleNamespace(**dict(x=1, y=2)) > + > + with self.assertRaises(TypeError): > + types.SimpleNamespace(1, 2, 3) > + > + self.assertEqual(len(ns1.__dict__), 0) > + self.assertEqual(vars(ns1), {}) > + self.assertEqual(len(ns2.__dict__), 2) > + self.assertEqual(vars(ns2), {'y': 2, 'x': 1}) > + self.assertEqual(len(ns3.__dict__), 2) > + self.assertEqual(vars(ns3), {'y': 2, 'x': 1}) > + > + def test_unbound(self): > + ns1 = vars(types.SimpleNamespace()) > + ns2 = vars(types.SimpleNamespace(x=1, y=2)) > + > + self.assertEqual(ns1, {}) > + self.assertEqual(ns2, {'y': 2, 'x': 1}) > + > + def test_underlying_dict(self): > + ns1 = types.SimpleNamespace() > + ns2 = types.SimpleNamespace(x=1, y=2) > + ns3 = types.SimpleNamespace(a=True, b=False) > + mapping = ns3.__dict__ > + del ns3 > + > + self.assertEqual(ns1.__dict__, {}) > + self.assertEqual(ns2.__dict__, {'y': 2, 'x': 1}) > + self.assertEqual(mapping, dict(a=True, b=False)) > + > + def test_attrget(self): > + ns = types.SimpleNamespace(x=1, y=2, w=3) > + > + self.assertEqual(ns.x, 1) > + self.assertEqual(ns.y, 2) > + self.assertEqual(ns.w, 3) > + with self.assertRaises(AttributeError): > + ns.z > + > + def test_attrset(self): > + ns1 = types.SimpleNamespace() > + ns2 = types.SimpleNamespace(x=1, y=2, w=3) > + ns1.a = 'spam' > + ns1.b = 'ham' > + ns2.z = 4 > + ns2.theta = None > + > + self.assertEqual(ns1.__dict__, dict(a='spam', b='ham')) > + self.assertEqual(ns2.__dict__, dict(x=1, y=2, w=3, z=4, > theta=None)) > + > + def test_attrdel(self): > + ns1 = types.SimpleNamespace() > + ns2 = types.SimpleNamespace(x=1, y=2, w=3) > + > + with self.assertRaises(AttributeError): > + del ns1.spam > + with self.assertRaises(AttributeError): > + del ns2.spam > + > + del ns2.y > + self.assertEqual(vars(ns2), dict(w=3, x=1)) > + ns2.y = 'spam' > + self.assertEqual(vars(ns2), dict(w=3, x=1, y='spam')) > + del ns2.y > + self.assertEqual(vars(ns2), dict(w=3, x=1)) > + > + ns1.spam = 5 > + self.assertEqual(vars(ns1), dict(spam=5)) > + del ns1.spam > + self.assertEqual(vars(ns1), {}) > + > + def test_repr(self): > + ns1 = types.SimpleNamespace(x=1, y=2, w=3) > + ns2 = types.SimpleNamespace() > + ns2.x = "spam" > + ns2._y = 5 > + > + self.assertEqual(repr(ns1), "namespace(w=3, x=1, y=2)") > + self.assertEqual(repr(ns2), "namespace(_y=5, x='spam')") > + > + def test_nested(self): > + ns1 = types.SimpleNamespace(a=1, b=2) > + ns2 = types.SimpleNamespace() > + ns3 = types.SimpleNamespace(x=ns1) > + ns2.spam = ns1 > + ns2.ham = '?' > + ns2.spam = ns3 > + > + self.assertEqual(vars(ns1), dict(a=1, b=2)) > + self.assertEqual(vars(ns2), dict(spam=ns3, ham='?')) > + self.assertEqual(ns2.spam, ns3) > + self.assertEqual(vars(ns3), dict(x=ns1)) > + self.assertEqual(ns3.x.a, 1) > + > + def test_recursive(self): > + ns1 = types.SimpleNamespace(c='cookie') > + ns2 = types.SimpleNamespace() > + ns3 = types.SimpleNamespace(x=1) > + ns1.spam = ns1 > + ns2.spam = ns3 > + ns3.spam = ns2 > + > + self.assertEqual(ns1.spam, ns1) > + self.assertEqual(ns1.spam.spam, ns1) > + self.assertEqual(ns1.spam.spam, ns1.spam) > + self.assertEqual(ns2.spam, ns3) > + self.assertEqual(ns3.spam, ns2) > + self.assertEqual(ns2.spam.spam, ns2) > + > + def test_recursive_repr(self): > + ns1 = types.SimpleNamespace(c='cookie') > + ns2 = types.SimpleNamespace() > + ns3 = types.SimpleNamespace(x=1) > + ns1.spam = ns1 > + ns2.spam = ns3 > + ns3.spam = ns2 > + > + self.assertEqual(repr(ns1), > + "namespace(c='cookie', spam=namespace(...))") > + self.assertEqual(repr(ns2), > + "namespace(spam=namespace(spam=namespace(...), > x=1))") > + > + def test_as_dict(self): > + ns = types.SimpleNamespace(spam='spamspamspam') > + > + with self.assertRaises(TypeError): > + len(ns) > + with self.assertRaises(TypeError): > + iter(ns) > + with self.assertRaises(TypeError): > + 'spam' in ns > + with self.assertRaises(TypeError): > + ns['spam'] > + > + > def test_main(): > - run_unittest(TypesTests, MappingProxyTests, ClassCreationTests) > + run_unittest(TypesTests, MappingProxyTests, ClassCreationTests, > + SimpleNamespaceTests) > > if __name__ == '__main__': > test_main() > diff --git a/Lib/types.py b/Lib/types.py > --- a/Lib/types.py > +++ b/Lib/types.py > @@ -13,6 +13,7 @@ > LambdaType = type(lambda: None) # Same as FunctionType > CodeType = type(_f.__code__) > MappingProxyType = type(type.__dict__) > +SimpleNamespace = type(sys.implementation) > > def _g(): > yield 1 > diff --git a/Makefile.pre.in b/Makefile.pre.in > --- a/Makefile.pre.in > +++ b/Makefile.pre.in > @@ -392,6 +392,7 @@ > Objects/memoryobject.o \ > Objects/methodobject.o \ > Objects/moduleobject.o \ > + Objects/namespaceobject.o \ > Objects/object.o \ > Objects/obmalloc.o \ > Objects/capsule.o \ > @@ -766,6 +767,7 @@ > $(srcdir)/Include/methodobject.h \ > $(srcdir)/Include/modsupport.h \ > $(srcdir)/Include/moduleobject.h \ > + $(srcdir)/Include/namespaceobject.h \ > $(srcdir)/Include/node.h \ > $(srcdir)/Include/object.h \ > $(srcdir)/Include/objimpl.h \ > diff --git a/Objects/namespaceobject.c b/Objects/namespaceobject.c > new file mode 100644 > --- /dev/null > +++ b/Objects/namespaceobject.c > @@ -0,0 +1,225 @@ > +/* namespace object implementation */ > + > +#include "Python.h" > +#include "structmember.h" > + > + > +typedef struct { > + PyObject_HEAD > + PyObject *ns_dict; > +} _PyNamespaceObject; > + > + > +static PyMemberDef namespace_members[] = { > + {"__dict__", T_OBJECT, offsetof(_PyNamespaceObject, ns_dict), > READONLY}, > + {NULL} > +}; > + > + > +/* Methods */ > + > +static PyObject * > +namespace_new(PyTypeObject *type, PyObject *args, PyObject *kwds) > +{ > + _PyNamespaceObject *ns; > + ns = PyObject_GC_New(_PyNamespaceObject, &_PyNamespace_Type); > + if (ns == NULL) > + return NULL; > + > + ns->ns_dict = PyDict_New(); > + if (ns->ns_dict == NULL) { > + Py_DECREF(ns); > + return NULL; > + } > + > + PyObject_GC_Track(ns); > + return (PyObject *)ns; > +} > + > + > +static int > +namespace_init(_PyNamespaceObject *ns, PyObject *args, PyObject *kwds) > +{ > + /* ignore args if it's NULL or empty */ > + if (args != NULL) { > + Py_ssize_t argcount = PyObject_Size(args); > + if (argcount < 0) > + return argcount; > + else if (argcount > 0) { > + PyErr_Format(PyExc_TypeError, "no positional arguments > expected"); > + return -1; > + } > + } > + if (kwds == NULL) > + return 0; > + return PyDict_Update(ns->ns_dict, kwds); > +} > + > + > +static void > +namespace_dealloc(_PyNamespaceObject *ns) > +{ > + PyObject_GC_UnTrack(ns); > + Py_CLEAR(ns->ns_dict); > + Py_TYPE(ns)->tp_free((PyObject *)ns); > +} > + > + > +static PyObject * > +namespace_repr(_PyNamespaceObject *ns) > +{ > + int i, loop_error = 0; > + PyObject *pairs = NULL, *d = NULL, *keys = NULL, *keys_iter = NULL; > + PyObject *key; > + PyObject *separator, *pairsrepr, *repr = NULL; > + > + i = Py_ReprEnter((PyObject *)ns); > + if (i != 0) { > + return i > 0 ? PyUnicode_FromString("namespace(...)") : NULL; > + } > + > + pairs = PyList_New(0); > + if (pairs == NULL) > + goto error; > + > + d = ((_PyNamespaceObject *)ns)->ns_dict; > + assert(d != NULL); > + Py_INCREF(d); > + > + keys = PyDict_Keys(d); > + if (keys == NULL) > + goto error; > + if (PyList_Sort(keys) != 0) > + goto error; > + > + keys_iter = PyObject_GetIter(keys); > + if (keys_iter == NULL) > + goto error; > + > + while ((key = PyIter_Next(keys_iter)) != NULL) { > + if (PyUnicode_Check(key) && PyUnicode_GET_SIZE(key) > 0) { > + PyObject *value, *item; > + > + value = PyDict_GetItem(d, key); > + assert(value != NULL); > + > + item = PyUnicode_FromFormat("%S=%R", key, value); > + if (item == NULL) { > + loop_error = 1; > + } > + else { > + loop_error = PyList_Append(pairs, item); > + Py_DECREF(item); > + } > + } > + > + Py_DECREF(key); > + if (loop_error) > + goto error; > + } > + > + separator = PyUnicode_FromString(", "); > + if (separator == NULL) > + goto error; > + > + pairsrepr = PyUnicode_Join(separator, pairs); > + Py_DECREF(separator); > + if (pairsrepr == NULL) > + goto error; > + > + repr = PyUnicode_FromFormat("%s(%S)", > + ((PyObject *)ns)->ob_type->tp_name, > pairsrepr); > + Py_DECREF(pairsrepr); > + > +error: > + Py_XDECREF(pairs); > + Py_XDECREF(d); > + Py_XDECREF(keys); > + Py_XDECREF(keys_iter); > + Py_ReprLeave((PyObject *)ns); > + > + return repr; > +} > + > + > +static int > +namespace_traverse(_PyNamespaceObject *ns, visitproc visit, void *arg) > +{ > + Py_VISIT(ns->ns_dict); > + return 0; > +} > + > + > +static int > +namespace_clear(_PyNamespaceObject *ns) > +{ > + Py_CLEAR(ns->ns_dict); > + return 0; > +} > + > + > +PyDoc_STRVAR(namespace_doc, > +"A simple attribute-based namespace.\n\ > +\n\ > +namespace(**kwargs)"); > + > +PyTypeObject _PyNamespace_Type = { > + PyVarObject_HEAD_INIT(&PyType_Type, 0) > + "namespace", /* tp_name */ > + sizeof(_PyNamespaceObject), /* tp_size */ > + 0, /* tp_itemsize */ > + (destructor)namespace_dealloc, /* tp_dealloc */ > + 0, /* tp_print */ > + 0, /* tp_getattr */ > + 0, /* tp_setattr */ > + 0, /* tp_reserved */ > + (reprfunc)namespace_repr, /* tp_repr */ > + 0, /* tp_as_number */ > + 0, /* tp_as_sequence */ > + 0, /* tp_as_mapping */ > + 0, /* tp_hash */ > + 0, /* tp_call */ > + 0, /* tp_str */ > + PyObject_GenericGetAttr, /* tp_getattro */ > + PyObject_GenericSetAttr, /* tp_setattro */ > + 0, /* tp_as_buffer */ > + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | > + Py_TPFLAGS_BASETYPE, /* tp_flags */ > + namespace_doc, /* tp_doc */ > + (traverseproc)namespace_traverse, /* tp_traverse */ > + (inquiry)namespace_clear, /* tp_clear */ > + 0, /* tp_richcompare */ > + 0, /* tp_weaklistoffset */ > + 0, /* tp_iter */ > + 0, /* tp_iternext */ > + 0, /* tp_methods */ > + namespace_members, /* tp_members */ > + 0, /* tp_getset */ > + 0, /* tp_base */ > + 0, /* tp_dict */ > + 0, /* tp_descr_get */ > + 0, /* tp_descr_set */ > + offsetof(_PyNamespaceObject, ns_dict), /* tp_dictoffset */ > + (initproc)namespace_init, /* tp_init */ > + PyType_GenericAlloc, /* tp_alloc */ > + (newfunc)namespace_new, /* tp_new */ > + PyObject_GC_Del, /* tp_free */ > +}; > + > + > +PyObject * > +_PyNamespace_New(PyObject *kwds) > +{ > + PyObject *ns = namespace_new(&_PyNamespace_Type, NULL, NULL); > + if (ns == NULL) > + return NULL; > + > + if (kwds == NULL) > + return ns; > + if (PyDict_Update(((_PyNamespaceObject *)ns)->ns_dict, kwds) != 0) { > + Py_DECREF(ns); > + return NULL; > + } > + > + return (PyObject *)ns; > +} > diff --git a/Objects/object.c b/Objects/object.c > --- a/Objects/object.c > +++ b/Objects/object.c > @@ -1707,6 +1707,9 @@ > > if (PyType_Ready(&PyZip_Type) < 0) > Py_FatalError("Can't initialize zip type"); > + > + if (PyType_Ready(&_PyNamespace_Type) < 0) > + Py_FatalError("Can't initialize namespace type"); > } > > > diff --git a/Python/sysmodule.c b/Python/sysmodule.c > --- a/Python/sysmodule.c > +++ b/Python/sysmodule.c > @@ -1261,6 +1261,7 @@ > float_info -- a struct sequence with information about the float > implementation.\n\ > float_repr_style -- string indicating the style of repr() output for > floats\n\ > hexversion -- version information encoded as a single integer\n\ > +implementation -- Python implementation information.\n\ > int_info -- a struct sequence with information about the int > implementation.\n\ > maxsize -- the largest supported length of containers.\n\ > maxunicode -- the value of the largest Unicode codepoint\n\ > @@ -1454,6 +1455,69 @@ > return version_info; > } > > +static PyObject * > +make_impl_info(PyObject *version_info) > +{ > + int res; > + PyObject *impl_info, *value, *ns; > + > + impl_info = PyDict_New(); > + if (impl_info == NULL) > + return NULL; > + > + /* populate the dict */ > + > +#define NAME "cpython" > +#define QUOTE(arg) #arg > +#define STRIFY(name) QUOTE(name) > +#define MAJOR STRIFY(PY_MAJOR_VERSION) > +#define MINOR STRIFY(PY_MINOR_VERSION) > +#define TAG NAME "-" MAJOR MINOR > + value = PyUnicode_FromString(NAME); > + if (value == NULL) > + goto error; > + res = PyDict_SetItemString(impl_info, "name", value); > + Py_DECREF(value); > + if (res < 0) > + goto error; > + > + value = PyUnicode_FromString(TAG); > + if (value == NULL) > + goto error; > + res = PyDict_SetItemString(impl_info, "cache_tag", value); > + Py_DECREF(value); > + if (res < 0) > + goto error; > +#undef NAME > +#undef QUOTE > +#undef STRIFY > +#undef MAJOR > +#undef MINOR > +#undef TAG > + > + res = PyDict_SetItemString(impl_info, "version", version_info); > + if (res < 0) > + goto error; > + > + value = PyLong_FromLong(PY_VERSION_HEX); > + if (value == NULL) > + goto error; > + res = PyDict_SetItemString(impl_info, "hexversion", value); > + Py_DECREF(value); > + if (res < 0) > + goto error; > + > + /* dict ready */ > + > + ns = _PyNamespace_New(impl_info); > + Py_DECREF(impl_info); > + return ns; > + > +error: > + Py_CLEAR(impl_info); > + return NULL; > +} > + > static struct PyModuleDef sysmodule = { > PyModuleDef_HEAD_INIT, > "sys", > @@ -1469,7 +1533,7 @@ > PyObject * > _PySys_Init(void) > { > - PyObject *m, *v, *sysdict; > + PyObject *m, *v, *sysdict, *version_info; > char *s; > > m = PyModule_Create(&sysmodule); > @@ -1589,11 +1653,15 @@ > /* version_info */ > if (VersionInfoType.tp_name == 0) > PyStructSequence_InitType(&VersionInfoType, &version_info_desc); > - SET_SYS_FROM_STRING("version_info", make_version_info()); > + version_info = make_version_info(); > + SET_SYS_FROM_STRING("version_info", version_info); > /* prevent user from creating new instances */ > VersionInfoType.tp_init = NULL; > VersionInfoType.tp_new = NULL; > > + /* implementation */ > + SET_SYS_FROM_STRING("implementation", make_impl_info(version_info)); > + > /* flags */ > if (FlagsType.tp_name == 0) > PyStructSequence_InitType(&FlagsType, &flags_desc); > > -- > Repository URL: http://hg.python.org/cpython > > _______________________________________________ > Python-checkins mailing list > Python-checkins at python.org > http://mail.python.org/mailman/listinfo/python-checkins > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From brett at python.org Mon Jun 4 16:25:11 2012 From: brett at python.org (Brett Cannon) Date: Mon, 4 Jun 2012 10:25:11 -0400 Subject: [Python-checkins] cpython: Eric Snow's implementation of PEP 421. In-Reply-To: References: Message-ID: [Let's try this again since my last reply was rejected for being too large] On Mon, Jun 4, 2012 at 9:52 AM, barry.warsaw wrote: > http://hg.python.org/cpython/rev/9c445f4695c1 > changeset: 77339:9c445f4695c1 > parent: 77328:0808cb8c60fd > user: Barry Warsaw > date: Sun Jun 03 16:18:47 2012 -0400 > summary: > Eric Snow's implementation of PEP 421. > > Issue 14673: Add sys.implementation > > files: > Doc/library/sys.rst | 38 ++++ > Doc/library/types.rst | 24 ++ > Include/Python.h | 1 + > Include/namespaceobject.h | 17 + > Lib/test/test_sys.py | 18 ++ > Lib/test/test_types.py | 143 ++++++++++++++++- > Lib/types.py | 1 + > Makefile.pre.in | 2 + > Objects/namespaceobject.c | 225 ++++++++++++++++++++++++++ > Objects/object.c | 3 + > Python/sysmodule.c | 72 ++++++++- > 11 files changed, 541 insertions(+), 3 deletions(-) > > > diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst > --- a/Doc/library/sys.rst > +++ b/Doc/library/sys.rst > @@ -616,6 +616,44 @@ > > Thus ``2.1.0a3`` is hexversion ``0x020100a3``. > > + > +.. data:: implementation > + > + An object containing the information about the implementation of the > + currently running Python interpreter. Its attributes are the those > "the those" -> "those" > + that all Python implementations must implement. Should you mention that VMs are allowed to add their own attributes that are not listed? > They are described > + below. > + > + *name* is the implementation's identifier, like ``'cpython'``. > Is this guaranteed to be lowercase, or does it simply happen to be lowercase in this instance? > + > + *version* is a named tuple, in the same format as > + :data:`sys.version_info`. It represents the version of the Python > + *implementation*. This has a distinct meaning from the specific > + version of the Python *language* to which the currently running > + interpreter conforms, which ``sys.version_info`` represents. For > + example, for PyPy 1.8 ``sys.implementation.version`` might be > + ``sys.version_info(1, 8, 0, 'final', 0)``, whereas ``sys.version_info`` > + would be ``sys.version_info(1, 8, 0, 'final', 0)``. I think you meant to say ``sys.version_info(2, 7, 2, 'final', 0)``. What's with the ~? -Brett -------------- next part -------------- An HTML attachment was scrubbed... URL: From python-checkins at python.org Mon Jun 4 17:06:51 2012 From: python-checkins at python.org (barry.warsaw) Date: Mon, 04 Jun 2012 17:06:51 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_A_few_documentation_improve?= =?utf8?q?ments=2C_spurred_on_by_Brett=27s_review=2E?= Message-ID: http://hg.python.org/cpython/rev/20fd0568b3a1 changeset: 77342:20fd0568b3a1 user: Barry Warsaw date: Mon Jun 04 11:06:45 2012 -0400 summary: A few documentation improvements, spurred on by Brett's review. files: Doc/library/sys.rst | 23 +++++++++++++---------- 1 files changed, 13 insertions(+), 10 deletions(-) diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -619,12 +619,13 @@ .. data:: implementation - An object containing the information about the implementation of the - currently running Python interpreter. Its attributes are the those - that all Python implementations must implement. They are described - below. + An object containing information about the implementation of the + currently running Python interpreter. The following attributes are + required to exist in all Python implementations. - *name* is the implementation's identifier, like ``'cpython'``. + *name* is the implementation's identifier, e.g. ``'cpython'``. The actual + string is defined by the Python implementation, but it is guaranteed to be + lower case. *version* is a named tuple, in the same format as :data:`sys.version_info`. It represents the version of the Python @@ -633,7 +634,7 @@ interpreter conforms, which ``sys.version_info`` represents. For example, for PyPy 1.8 ``sys.implementation.version`` might be ``sys.version_info(1, 8, 0, 'final', 0)``, whereas ``sys.version_info`` - would be ``sys.version_info(1, 8, 0, 'final', 0)``. For CPython they + would be ``sys.version_info(2, 7, 2, 'final', 0)``. For CPython they are the same value, since it is the reference implementation. *hexversion* is the implementation version in hexadecimal format, like @@ -646,10 +647,12 @@ ``cache_tag`` is set to ``None``, it indicates that module caching should be disabled. - Regardless of its contents, :data:`sys.implementation` will not - change during a run of the interpreter, nor between implementation - versions. (It may change between Python language versions, - however.) See `PEP 421` for more information. + :data:`sys.implementation` may contain additional attributes specific to + the Python implementation. These non-standard attributes must start with + an underscore, and are not described here. Regardless of its contents, + :data:`sys.implementation` will not change during a run of the interpreter, + nor between implementation versions. (It may change between Python + language versions, however.) See `PEP 421` for more information. .. versionadded:: 3.3 -- Repository URL: http://hg.python.org/cpython From barry at python.org Mon Jun 4 17:10:02 2012 From: barry at python.org (Barry Warsaw) Date: Mon, 4 Jun 2012 11:10:02 -0400 Subject: [Python-checkins] cpython: Eric Snow's implementation of PEP 421. In-Reply-To: References: Message-ID: <20120604111002.221904c2@resist.wooz.org> Thanks for the second set of eyes, Brett. On Jun 04, 2012, at 10:16 AM, Brett Cannon wrote: >> +.. data:: implementation >> + >> + An object containing the information about the implementation of the >> + currently running Python interpreter. Its attributes are the those >> > >"the those" -> "those" I actually rewrote this section a bit: An object containing information about the implementation of the currently running Python interpreter. The following attributes are required to exist in all Python implementations. >> + that all Python implementations must implement. > >Should you mention that VMs are allowed to add their own attributes that >are not listed? Here's how I rewrote it: :data:`sys.implementation` may contain additional attributes specific to the Python implementation. These non-standard attributes must start with an underscore, and are not described here. Regardless of its contents, :data:`sys.implementation` will not change during a run of the interpreter, nor between implementation versions. (It may change between Python language versions, however.) See `PEP 421` for more information. >> They are described >> + below. >> + >> + *name* is the implementation's identifier, like ``'cpython'``. > >Is this guaranteed to be lowercase, or does it simply happen to be >lowercase in this instance? Yes, PEP 421 guarantees them to be lower cased. *name* is the implementation's identifier, e.g. ``'cpython'``. The actual string is defined by the Python implementation, but it is guaranteed to be lower case. >I think you meant to say ``sys.version_info(2, 7, 2, 'final', 0)``. Fixed. >> + However, for a structured record type use >> :func:`~collections.namedtuple` >> > >What's with the ~? I'm not sure, but it seems to result in a cross-reference, and I see tildes used elsewhere, so I guess it's some reST/docutils magic. I left this one in there. Cheers, -Barry From brett at python.org Mon Jun 4 17:39:40 2012 From: brett at python.org (Brett Cannon) Date: Mon, 4 Jun 2012 11:39:40 -0400 Subject: [Python-checkins] cpython: Eric Snow's implementation of PEP 421. In-Reply-To: <20120604111002.221904c2@resist.wooz.org> References: <20120604111002.221904c2@resist.wooz.org> Message-ID: On Mon, Jun 4, 2012 at 11:10 AM, Barry Warsaw wrote: > Thanks for the second set of eyes, Brett. > > On Jun 04, 2012, at 10:16 AM, Brett Cannon wrote: > > >> +.. data:: implementation > >> + > >> + An object containing the information about the implementation of the > >> + currently running Python interpreter. Its attributes are the those > >> > > > >"the those" -> "those" > > I actually rewrote this section a bit: > > An object containing information about the implementation of the > currently running Python interpreter. The following attributes are > required to exist in all Python implementations. > > >> + that all Python implementations must implement. > > > >Should you mention that VMs are allowed to add their own attributes that > >are not listed? > > Here's how I rewrote it: > > :data:`sys.implementation` may contain additional attributes specific to > the Python implementation. These non-standard attributes must start with > an underscore, and are not described here. Regardless of its contents, > :data:`sys.implementation` will not change during a run of the > interpreter, > nor between implementation versions. (It may change between Python > language versions, however.) See `PEP 421` for more information. > > >> They are described > >> + below. > >> + > >> + *name* is the implementation's identifier, like ``'cpython'``. > > > >Is this guaranteed to be lowercase, or does it simply happen to be > >lowercase in this instance? > > Yes, PEP 421 guarantees them to be lower cased. > *name* is the implementation's identifier, e.g. ``'cpython'``. The > actual > string is defined by the Python implementation, but it is guaranteed to > be > lower case. > > OK, then I would add a test to make sure this happens, like ``self.assertEqual(sys.implementation.name, sys.implement.name.lower())`` if you don't want to bother documenting it to make sure other VMs conform. -Brett >I think you meant to say ``sys.version_info(2, 7, 2, 'final', 0)``. > > Fixed. > > >> + However, for a structured record type use > >> :func:`~collections.namedtuple` > >> > > > >What's with the ~? > > I'm not sure, but it seems to result in a cross-reference, and I see tildes > used elsewhere, so I guess it's some reST/docutils magic. I left this one > in > there. > > Cheers, > -Barry > -------------- next part -------------- An HTML attachment was scrubbed... URL: From python-checkins at python.org Mon Jun 4 18:02:46 2012 From: python-checkins at python.org (barry.warsaw) Date: Mon, 04 Jun 2012 18:02:46 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_PEP_421_requires_that_=2Ena?= =?utf8?q?me_be_lower_case=2E?= Message-ID: http://hg.python.org/cpython/rev/a061ae246395 changeset: 77343:a061ae246395 user: Barry Warsaw date: Mon Jun 04 12:01:56 2012 -0400 summary: PEP 421 requires that .name be lower case. files: Lib/test/test_sys.py | 4 ++++ 1 files changed, 4 insertions(+), 0 deletions(-) diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -599,6 +599,10 @@ version.serial << 0) self.assertEqual(sys.implementation.hexversion, hexversion) + # PEP 421 requires that .name be lower case. + self.assertEqual(sys.implementation.name, + sys.implementation.name.lower()) + class SizeofTest(unittest.TestCase): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 4 18:02:48 2012 From: python-checkins at python.org (barry.warsaw) Date: Mon, 04 Jun 2012 18:02:48 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Whitespace_normalization?= Message-ID: http://hg.python.org/cpython/rev/59ae1e96db1c changeset: 77344:59ae1e96db1c user: Barry Warsaw date: Mon Jun 04 12:02:42 2012 -0400 summary: Whitespace normalization files: Lib/test/test_sys.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -600,7 +600,7 @@ self.assertEqual(sys.implementation.hexversion, hexversion) # PEP 421 requires that .name be lower case. - self.assertEqual(sys.implementation.name, + self.assertEqual(sys.implementation.name, sys.implementation.name.lower()) -- Repository URL: http://hg.python.org/cpython From barry at python.org Mon Jun 4 18:03:08 2012 From: barry at python.org (Barry Warsaw) Date: Mon, 4 Jun 2012 12:03:08 -0400 Subject: [Python-checkins] cpython: Eric Snow's implementation of PEP 421. In-Reply-To: References: <20120604111002.221904c2@resist.wooz.org> Message-ID: <20120604120308.73c41732@resist.wooz.org> On Jun 04, 2012, at 11:39 AM, Brett Cannon wrote: >OK, then I would add a test to make sure this happens, like >``self.assertEqual(sys.implementation.name, sys.implement.name.lower())`` >if you don't want to bother documenting it to make sure other VMs conform. Good idea. Done. -Barry -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 836 bytes Desc: not available URL: From python-checkins at python.org Mon Jun 4 18:34:56 2012 From: python-checkins at python.org (hynek.schlawack) Date: Mon, 04 Jun 2012 18:34:56 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_=2314814=3A_Remove_dead_cod?= =?utf8?q?e_from_ipaddress?= Message-ID: http://hg.python.org/cpython/rev/df6d1a4d83fa changeset: 77345:df6d1a4d83fa user: Hynek Schlawack date: Mon Jun 04 18:14:02 2012 +0200 summary: #14814: Remove dead code from ipaddress _BaseNetwork contained (faulty) methods for creating string representations. I've fixed them and put them to use by eliminating identical overrides. files: Lib/ipaddress.py | 45 ++++--------------------- Lib/test/test_ipaddress.py | 16 +++++++++ 2 files changed, 23 insertions(+), 38 deletions(-) diff --git a/Lib/ipaddress.py b/Lib/ipaddress.py --- a/Lib/ipaddress.py +++ b/Lib/ipaddress.py @@ -578,6 +578,10 @@ def __repr__(self): return '%s(%r)' % (self.__class__.__name__, str(self)) + def __str__(self): + return '%s/%d' % (str(self.network_address), + self.prefixlen) + def hosts(self): """Generate Iterator over usable hosts in a network. @@ -663,9 +667,6 @@ return NotImplemented return not eq - def __str__(self): - return '%s/%s' % (self.ip, self._prefixlen) - def __hash__(self): return hash(int(self.network_address) ^ int(self.netmask)) @@ -708,15 +709,15 @@ @property def with_prefixlen(self): - return '%s/%d' % (str(self.ip), self._prefixlen) + return '%s/%d' % (str(self.network_address), self._prefixlen) @property def with_netmask(self): - return '%s/%s' % (str(self.ip), str(self.netmask)) + return '%s/%s' % (str(self.network_address), str(self.netmask)) @property def with_hostmask(self): - return '%s/%s' % (str(self.ip), str(self.hostmask)) + return '%s/%s' % (str(self.network_address), str(self.hostmask)) @property def num_addresses(self): @@ -1447,10 +1448,6 @@ """The binary representation of this address.""" return v4_int_to_packed(self.network_address) - def __str__(self): - return '%s/%d' % (str(self.network_address), - self.prefixlen) - def _is_valid_netmask(self, netmask): """Verify that the netmask is valid. @@ -1498,18 +1495,6 @@ return True return False - @property - def with_prefixlen(self): - return '%s/%d' % (str(self.network_address), self._prefixlen) - - @property - def with_netmask(self): - return '%s/%s' % (str(self.network_address), str(self.netmask)) - - @property - def with_hostmask(self): - return '%s/%s' % (str(self.network_address), str(self.hostmask)) - class _BaseV6: @@ -2108,10 +2093,6 @@ if self._prefixlen == (self._max_prefixlen - 1): self.hosts = self.__iter__ - def __str__(self): - return '%s/%d' % (str(self.network_address), - self.prefixlen) - def _is_valid_netmask(self, prefixlen): """Verify that the netmask/prefixlen is valid. @@ -2128,15 +2109,3 @@ except ValueError: return False return 0 <= prefixlen <= self._max_prefixlen - - @property - def with_prefixlen(self): - return '%s/%d' % (str(self.network_address), self._prefixlen) - - @property - def with_netmask(self): - return '%s/%s' % (str(self.network_address), str(self.netmask)) - - @property - def with_hostmask(self): - return '%s/%s' % (str(self.network_address), str(self.hostmask)) diff --git a/Lib/test/test_ipaddress.py b/Lib/test/test_ipaddress.py --- a/Lib/test/test_ipaddress.py +++ b/Lib/test/test_ipaddress.py @@ -972,6 +972,22 @@ self.assertTrue(self.ipv4_address in dummy) self.assertTrue(ip2 in dummy) + def testIPv6NetworkHelpers(self): + net = self.ipv6_network + self.assertEqual('2001:658:22a:cafe::/64', net.with_prefixlen) + self.assertEqual('2001:658:22a:cafe::/ffff:ffff:ffff:ffff::', + net.with_netmask) + self.assertEqual('2001:658:22a:cafe::/::ffff:ffff:ffff:ffff', + net.with_hostmask) + self.assertEqual('2001:658:22a:cafe::/64', str(net)) + + def testIPv4NetworkHelpers(self): + net = self.ipv4_network + self.assertEqual('1.2.3.0/24', net.with_prefixlen) + self.assertEqual('1.2.3.0/255.255.255.0', net.with_netmask) + self.assertEqual('1.2.3.0/0.0.0.255', net.with_hostmask) + self.assertEqual('1.2.3.0/24', str(net)) + def testCopyConstructor(self): addr1 = ipaddress.ip_network('10.1.1.0/24') addr2 = ipaddress.ip_network(addr1) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 4 19:28:16 2012 From: python-checkins at python.org (richard.oudkerk) Date: Mon, 04 Jun 2012 19:28:16 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Add_namespaceobject=2Eh_and?= =?utf8?q?_namespaceobject=2Ec_to_pythoncore=2Evcxproj?= Message-ID: http://hg.python.org/cpython/rev/ee7cd7d51ed6 changeset: 77346:ee7cd7d51ed6 user: Richard Oudkerk date: Mon Jun 04 18:24:44 2012 +0100 summary: Add namespaceobject.h and namespaceobject.c to pythoncore.vcxproj files: PCbuild/pythoncore.vcxproj | 4 +++- 1 files changed, 3 insertions(+), 1 deletions(-) diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -1,4 +1,4 @@ -? + @@ -402,6 +402,7 @@ + @@ -579,6 +580,7 @@ + -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 4 20:01:05 2012 From: python-checkins at python.org (richard.oudkerk) Date: Mon, 04 Jun 2012 20:01:05 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Make_Finalize_reserve_a_ref?= =?utf8?q?erence_to_os=2Egetpid_in_case_called_at_shutdown?= Message-ID: http://hg.python.org/cpython/rev/9257ea91df3d changeset: 77347:9257ea91df3d user: Richard Oudkerk date: Mon Jun 04 18:58:59 2012 +0100 summary: Make Finalize reserve a reference to os.getpid in case called at shutdown files: Lib/multiprocessing/util.py | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/multiprocessing/util.py b/Lib/multiprocessing/util.py --- a/Lib/multiprocessing/util.py +++ b/Lib/multiprocessing/util.py @@ -170,7 +170,7 @@ # Need to bind these locally because the globals can have # been cleared at shutdown _finalizer_registry=_finalizer_registry, - sub_debug=sub_debug): + sub_debug=sub_debug, getpid=os.getpid): ''' Run the callback unless it has already been called or cancelled ''' @@ -179,7 +179,7 @@ except KeyError: sub_debug('finalizer no longer registered') else: - if self._pid != os.getpid(): + if self._pid != getpid(): sub_debug('finalizer ignored because different process') res = None else: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 4 20:01:07 2012 From: python-checkins at python.org (richard.oudkerk) Date: Mon, 04 Jun 2012 20:01:07 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Fix_potential_NameError_in_?= =?utf8?q?multiprocessing=2ECondition=2Ewait=28=29?= Message-ID: http://hg.python.org/cpython/rev/3baeb5e13dd2 changeset: 77348:3baeb5e13dd2 user: Richard Oudkerk date: Mon Jun 04 18:59:07 2012 +0100 summary: Fix potential NameError in multiprocessing.Condition.wait() files: Lib/multiprocessing/synchronize.py | 3 +-- 1 files changed, 1 insertions(+), 2 deletions(-) diff --git a/Lib/multiprocessing/synchronize.py b/Lib/multiprocessing/synchronize.py --- a/Lib/multiprocessing/synchronize.py +++ b/Lib/multiprocessing/synchronize.py @@ -216,7 +216,7 @@ try: # wait for notification or timeout - ret = self._wait_semaphore.acquire(True, timeout) + return self._wait_semaphore.acquire(True, timeout) finally: # indicate that this thread has woken self._woken_count.release() @@ -224,7 +224,6 @@ # reacquire lock for i in range(count): self._lock.acquire() - return ret def notify(self): assert self._lock._semlock._is_mine(), 'lock is not owned' -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 4 20:01:08 2012 From: python-checkins at python.org (richard.oudkerk) Date: Mon, 04 Jun 2012 20:01:08 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Prevent_handle_leak_if_Crea?= =?utf8?q?teProcess=28=29_fails_in_multiprocessing?= Message-ID: http://hg.python.org/cpython/rev/2959ef933b8b changeset: 77349:2959ef933b8b user: Richard Oudkerk date: Mon Jun 04 18:59:10 2012 +0100 summary: Prevent handle leak if CreateProcess() fails in multiprocessing files: Lib/multiprocessing/forking.py | 48 +++++++++++---------- 1 files changed, 25 insertions(+), 23 deletions(-) diff --git a/Lib/multiprocessing/forking.py b/Lib/multiprocessing/forking.py --- a/Lib/multiprocessing/forking.py +++ b/Lib/multiprocessing/forking.py @@ -209,6 +209,9 @@ _tls = _thread._local() def __init__(self, process_obj): + cmd = ' '.join('"%s"' % x for x in get_command_line()) + prep_data = get_preparation_data(process_obj._name) + # create pipe for communication with child rfd, wfd = os.pipe() @@ -216,31 +219,30 @@ rhandle = duplicate(msvcrt.get_osfhandle(rfd), inheritable=True) os.close(rfd) - # start process - cmd = get_command_line() + [rhandle] - cmd = ' '.join('"%s"' % x for x in cmd) - hp, ht, pid, tid = _winapi.CreateProcess( - _python_exe, cmd, None, None, 1, 0, None, None, None - ) - _winapi.CloseHandle(ht) - close(rhandle) + with open(wfd, 'wb', closefd=True) as to_child: + # start process + try: + hp, ht, pid, tid = _winapi.CreateProcess( + _python_exe, cmd + (' %s' % rhandle), + None, None, 1, 0, None, None, None + ) + _winapi.CloseHandle(ht) + finally: + close(rhandle) - # set attributes of self - self.pid = pid - self.returncode = None - self._handle = hp - self.sentinel = int(hp) + # set attributes of self + self.pid = pid + self.returncode = None + self._handle = hp + self.sentinel = int(hp) - # send information to child - prep_data = get_preparation_data(process_obj._name) - to_child = os.fdopen(wfd, 'wb') - Popen._tls.process_handle = int(hp) - try: - dump(prep_data, to_child, HIGHEST_PROTOCOL) - dump(process_obj, to_child, HIGHEST_PROTOCOL) - finally: - del Popen._tls.process_handle - to_child.close() + # send information to child + Popen._tls.process_handle = int(hp) + try: + dump(prep_data, to_child, HIGHEST_PROTOCOL) + dump(process_obj, to_child, HIGHEST_PROTOCOL) + finally: + del Popen._tls.process_handle @staticmethod def thread_is_spawning(): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 4 21:56:28 2012 From: python-checkins at python.org (r.david.murray) Date: Mon, 04 Jun 2012 21:56:28 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_=238739=3A_fix_omission_of_?= =?utf8?q?DEBUGSTREAM_reset_in_new_test_in_test=5Fsmtpd=2E?= Message-ID: http://hg.python.org/cpython/rev/079c1942eedf changeset: 77350:079c1942eedf user: R David Murray date: Mon Jun 04 15:55:51 2012 -0400 summary: #8739: fix omission of DEBUGSTREAM reset in new test in test_smtpd. This clears up an error in detected by refleak mode that showed up when test_smtplib was run after test_smtpd in the same refleak run. files: Lib/test/test_smtpd.py | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-) diff --git a/Lib/test/test_smtpd.py b/Lib/test/test_smtpd.py --- a/Lib/test/test_smtpd.py +++ b/Lib/test/test_smtpd.py @@ -507,6 +507,7 @@ def setUp(self): smtpd.socket = asyncore.socket = mock_socket + self.old_debugstream = smtpd.DEBUGSTREAM self.debug = smtpd.DEBUGSTREAM = io.StringIO() self.server = DummyServer('a', 'b') conn, addr = self.server.accept() @@ -516,6 +517,7 @@ def tearDown(self): asyncore.close_all() asyncore.socket = smtpd.socket = socket + smtpd.DEBUGSTREAM = self.old_debugstream def write_line(self, line): self.channel.socket.queue_recv(line) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 4 22:54:11 2012 From: python-checkins at python.org (victor.stinner) Date: Mon, 04 Jun 2012 22:54:11 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Issue_=2314993=3A_Use_stand?= =?utf8?q?ard_=22unsigned_char=22_instead_of_a_unsigned_char_bitfield?= Message-ID: http://hg.python.org/cpython/rev/09736ae1c314 changeset: 77351:09736ae1c314 user: Victor Stinner date: Mon Jun 04 22:52:12 2012 +0200 summary: Issue #14993: Use standard "unsigned char" instead of a unsigned char bitfield files: Include/unicodeobject.h | 10 +++----- Objects/stringlib/unicode_format.h | 2 +- Objects/unicodeobject.c | 20 +++++++++--------- 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/Include/unicodeobject.h b/Include/unicodeobject.h --- a/Include/unicodeobject.h +++ b/Include/unicodeobject.h @@ -901,12 +901,10 @@ /* minimum length of the buffer when overallocation is enabled, see _PyUnicodeWriter_Init() */ Py_ssize_t min_length; - struct { - unsigned char overallocate:1; - /* If readonly is 1, buffer is a shared string (cannot be modified) - and size is set to 0. */ - unsigned char readonly:1; - } flags; + unsigned char overallocate; + /* If readonly is 1, buffer is a shared string (cannot be modified) + and size is set to 0. */ + unsigned char readonly; } _PyUnicodeWriter ; /* Initialize a Unicode writer. diff --git a/Objects/stringlib/unicode_format.h b/Objects/stringlib/unicode_format.h --- a/Objects/stringlib/unicode_format.h +++ b/Objects/stringlib/unicode_format.h @@ -898,7 +898,7 @@ if (field_present) { if (iter.str.start == iter.str.end) - writer->flags.overallocate = 0; + writer->overallocate = 0; if (!output_markup(&field_name, &format_spec, format_spec_needs_expanding, conversion, writer, args, kwargs, recursion_depth, auto_number)) diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -12808,7 +12808,7 @@ writer->kind = 5; /* invalid kind */ #endif writer->min_length = Py_MAX(min_length, 100); - writer->flags.overallocate = (min_length > 0); + writer->overallocate = (min_length > 0); } int @@ -12827,7 +12827,7 @@ newlen = writer->pos + length; if (writer->buffer == NULL) { - if (writer->flags.overallocate) { + if (writer->overallocate) { /* overallocate 25% to limit the number of resize */ if (newlen <= (PY_SSIZE_T_MAX - newlen / 4)) newlen += newlen / 4; @@ -12842,7 +12842,7 @@ } if (newlen > writer->size) { - if (writer->flags.overallocate) { + if (writer->overallocate) { /* overallocate 25% to limit the number of resize */ if (newlen <= (PY_SSIZE_T_MAX - newlen / 4)) newlen += newlen / 4; @@ -12850,7 +12850,7 @@ newlen = writer->min_length; } - if (maxchar > writer->maxchar || writer->flags.readonly) { + if (maxchar > writer->maxchar || writer->readonly) { /* resize + widen */ newbuffer = PyUnicode_New(newlen, maxchar); if (newbuffer == NULL) @@ -12858,7 +12858,7 @@ _PyUnicode_FastCopyCharacters(newbuffer, 0, writer->buffer, 0, writer->pos); Py_DECREF(writer->buffer); - writer->flags.readonly = 0; + writer->readonly = 0; } else { newbuffer = resize_compact(writer->buffer, newlen); @@ -12869,7 +12869,7 @@ _PyUnicodeWriter_Update(writer); } else if (maxchar > writer->maxchar) { - assert(!writer->flags.readonly); + assert(!writer->readonly); newbuffer = PyUnicode_New(writer->size, maxchar); if (newbuffer == NULL) return -1; @@ -12895,11 +12895,11 @@ return 0; maxchar = PyUnicode_MAX_CHAR_VALUE(str); if (maxchar > writer->maxchar || len > writer->size - writer->pos) { - if (writer->buffer == NULL && !writer->flags.overallocate) { + if (writer->buffer == NULL && !writer->overallocate) { Py_INCREF(str); writer->buffer = str; _PyUnicodeWriter_Update(writer); - writer->flags.readonly = 1; + writer->readonly = 1; writer->size = 0; writer->pos += len; return 0; @@ -12921,7 +12921,7 @@ Py_INCREF(unicode_empty); return unicode_empty; } - if (writer->flags.readonly) { + if (writer->readonly) { assert(PyUnicode_GET_LENGTH(writer->buffer) == writer->pos); return writer->buffer; } @@ -13638,7 +13638,7 @@ goto onError; } if (fmtcnt == 0) - writer.flags.overallocate = 0; + writer.overallocate = 0; if (c == '%') { if (_PyUnicodeWriter_Prepare(&writer, 1, '%') == -1) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 4 23:49:46 2012 From: python-checkins at python.org (barry.warsaw) Date: Mon, 04 Jun 2012 23:49:46 +0200 Subject: [Python-checkins] =?utf8?q?peps=3A_Mark_PEP_421_as_final?= Message-ID: http://hg.python.org/peps/rev/c60be355412e changeset: 4445:c60be355412e user: Barry Warsaw date: Mon Jun 04 17:49:40 2012 -0400 summary: Mark PEP 421 as final files: pep-0421.txt | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/pep-0421.txt b/pep-0421.txt --- a/pep-0421.txt +++ b/pep-0421.txt @@ -4,7 +4,7 @@ Last-Modified: $Date$ Author: Eric Snow BDFL-Delegate: Barry Warsaw -Status: Accepted +Status: Final Type: Standards Track Content-Type: text/x-rst Created: 26-April-2012 -- Repository URL: http://hg.python.org/peps From python-checkins at python.org Mon Jun 4 23:55:52 2012 From: python-checkins at python.org (nadeem.vawda) Date: Mon, 04 Jun 2012 23:55:52 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Add_fileobj_support_to_gzip?= =?utf8?b?Lm9wZW4oKS4=?= Message-ID: http://hg.python.org/cpython/rev/d87ec233d514 changeset: 77352:d87ec233d514 user: Nadeem Vawda date: Mon Jun 04 23:21:38 2012 +0200 summary: Add fileobj support to gzip.open(). files: Doc/library/gzip.rst | 20 +++++++++++--------- Lib/gzip.py | 13 ++++++++++++- Lib/test/test_gzip.py | 13 +++++++++++++ Misc/NEWS | 2 ++ 4 files changed, 38 insertions(+), 10 deletions(-) diff --git a/Doc/library/gzip.rst b/Doc/library/gzip.rst --- a/Doc/library/gzip.rst +++ b/Doc/library/gzip.rst @@ -14,10 +14,10 @@ The data compression is provided by the :mod:`zlib` module. The :mod:`gzip` module provides the :class:`GzipFile` class, as well as the -:func:`gzip.open`, :func:`compress` and :func:`decompress` convenience -functions. The :class:`GzipFile` class reads and writes :program:`gzip`\ -format -files, automatically compressing or decompressing the data so that it looks like -an ordinary :term:`file object`. +:func:`.open`, :func:`compress` and :func:`decompress` convenience functions. +The :class:`GzipFile` class reads and writes :program:`gzip`\ -format files, +automatically compressing or decompressing the data so that it looks like an +ordinary :term:`file object`. Note that additional file formats which can be decompressed by the :program:`gzip` and :program:`gunzip` programs, such as those produced by @@ -28,9 +28,11 @@ .. function:: open(filename, mode='rb', compresslevel=9, encoding=None, errors=None, newline=None) - Open *filename* as a gzip-compressed file in binary or text mode. + Open a gzip-compressed file in binary or text mode, returning a :term:`file + object`. - Returns a :term:`file object`. + The *filename* argument can be an actual filename (a :class:`str` or + :class:`bytes` object), or an existing file object to read from or write to. The *mode* argument can be any of ``'r'``, ``'rb'``, ``'a'``, ``'ab'``, ``'w'``, or ``'wb'`` for binary mode, or ``'rt'``, ``'at'``, or ``'wt'`` for @@ -48,8 +50,8 @@ handling behavior, and line ending(s). .. versionchanged:: 3.3 - Support for text mode was added, along with the *encoding*, *errors* and - *newline* arguments. + Added support for *filename* being a file object, support for text mode, + and the *encoding*, *errors* and *newline* arguments. .. class:: GzipFile(filename=None, mode=None, compresslevel=9, fileobj=None, mtime=None) @@ -75,7 +77,7 @@ is the mode of *fileobj* if discernible; otherwise, the default is ``'rb'``. Note that the file is always opened in binary mode. To open a compressed file - in text mode, use :func:`gzip.open` (or wrap your :class:`GzipFile` with an + in text mode, use :func:`.open` (or wrap your :class:`GzipFile` with an :class:`io.TextIOWrapper`). The *compresslevel* argument is an integer from ``1`` to ``9`` controlling the diff --git a/Lib/gzip.py b/Lib/gzip.py --- a/Lib/gzip.py +++ b/Lib/gzip.py @@ -20,6 +20,9 @@ encoding=None, errors=None, newline=None): """Open a gzip-compressed file in binary or text mode. + The filename argument can be an actual filename (a str or bytes object), or + an existing file object to read from or write to. + The mode argument can be "r", "rb", "w", "wb", "a" or "ab" for binary mode, or "rt", "wt" or "at" for text mode. The default mode is "rb", and the default compresslevel is 9. @@ -43,7 +46,15 @@ raise ValueError("Argument 'errors' not supported in binary mode") if newline is not None: raise ValueError("Argument 'newline' not supported in binary mode") - binary_file = GzipFile(filename, mode.replace("t", ""), compresslevel) + + gz_mode = mode.replace("t", "") + if isinstance(filename, (str, bytes)): + binary_file = GzipFile(filename, gz_mode, compresslevel) + elif hasattr(filename, "read") or hasattr(filename, "write"): + binary_file = GzipFile(None, gz_mode, compresslevel, filename) + else: + raise TypeError("filename must be a str or bytes object, or a file") + if "t" in mode: return io.TextIOWrapper(binary_file, encoding, errors, newline) else: diff --git a/Lib/test/test_gzip.py b/Lib/test/test_gzip.py --- a/Lib/test/test_gzip.py +++ b/Lib/test/test_gzip.py @@ -424,8 +424,21 @@ file_data = gzip.decompress(f.read()).decode("ascii") self.assertEqual(file_data, uncompressed_raw * 2) + def test_fileobj(self): + uncompressed_bytes = data1 * 50 + uncompressed_str = uncompressed_bytes.decode("ascii") + compressed = gzip.compress(uncompressed_bytes) + with gzip.open(io.BytesIO(compressed), "r") as f: + self.assertEqual(f.read(), uncompressed_bytes) + with gzip.open(io.BytesIO(compressed), "rb") as f: + self.assertEqual(f.read(), uncompressed_bytes) + with gzip.open(io.BytesIO(compressed), "rt") as f: + self.assertEqual(f.read(), uncompressed_str) + def test_bad_params(self): # Test invalid parameter combinations. + with self.assertRaises(TypeError): + gzip.open(123.456) with self.assertRaises(ValueError): gzip.open(self.filename, "wbt") with self.assertRaises(ValueError): diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -15,6 +15,8 @@ Library ------- +- gzip.open() now accepts file objects as well as filenames. + - Issue #14992: os.makedirs(path, exist_ok=True) would raise an OSError when the path existed and had the S_ISGID mode bit set when it was not explicitly asked for. This is no longer an exception as mkdir -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 4 23:55:52 2012 From: python-checkins at python.org (nadeem.vawda) Date: Mon, 04 Jun 2012 23:55:52 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Make_BZ2File=27s_fileobj_su?= =?utf8?q?pport_easier_to_use=2E?= Message-ID: http://hg.python.org/cpython/rev/544e64e18a8a changeset: 77353:544e64e18a8a user: Nadeem Vawda date: Mon Jun 04 23:31:20 2012 +0200 summary: Make BZ2File's fileobj support easier to use. The fileobj argument was added during the 3.3 development cycle, so this change does not break backward compatibility with 3.2. files: Doc/library/bz2.rst | 16 +++++++----- Lib/bz2.py | 17 ++++++------- Lib/tarfile.py | 4 +- Lib/test/test_bz2.py | 40 +++++++++++++++++++++---------- Misc/NEWS | 3 ++ 5 files changed, 49 insertions(+), 31 deletions(-) diff --git a/Doc/library/bz2.rst b/Doc/library/bz2.rst --- a/Doc/library/bz2.rst +++ b/Doc/library/bz2.rst @@ -26,17 +26,18 @@ (De)compression of files ------------------------ -.. class:: BZ2File(filename=None, mode='r', buffering=None, compresslevel=9, \*, fileobj=None) +.. class:: BZ2File(filename, mode='r', buffering=None, compresslevel=9) Open a bzip2-compressed file. - The :class:`BZ2File` can wrap an existing :term:`file object` (given by - *fileobj*), or operate directly on a named file (named by *filename*). - Exactly one of these two parameters should be provided. + If *filename* is a :class:`str` or :class:`bytes` object, open the named file + directly. Otherwise, *filename* should be a :term:`file object`, which will + be used to read or write the compressed data. The *mode* argument can be either ``'r'`` for reading (default), ``'w'`` for - overwriting, or ``'a'`` for appending. If *fileobj* is provided, a mode of - ``'w'`` does not truncate the file, and is instead equivalent to ``'a'``. + overwriting, or ``'a'`` for appending. If *filename* is a file object (rather + than an actual file name), a mode of ``'w'`` does not truncate the file, and + is instead equivalent to ``'a'``. The *buffering* argument is ignored. Its use is deprecated. @@ -69,7 +70,8 @@ :meth:`read1` and :meth:`readinto` methods were added. .. versionchanged:: 3.3 - The *fileobj* argument to the constructor was added. + Support was added for *filename* being a :term:`file object` instead of an + actual filename. .. versionchanged:: 3.3 The ``'a'`` (append) mode was added, along with support for reading diff --git a/Lib/bz2.py b/Lib/bz2.py --- a/Lib/bz2.py +++ b/Lib/bz2.py @@ -39,13 +39,12 @@ returned as bytes, and data to be written should be given as bytes. """ - def __init__(self, filename=None, mode="r", buffering=None, - compresslevel=9, *, fileobj=None): + def __init__(self, filename, mode="r", buffering=None, compresslevel=9): """Open a bzip2-compressed file. - If filename is given, open the named file. Otherwise, operate on - the file object given by fileobj. Exactly one of these two - parameters should be provided. + If filename is a str or bytes object, is gives the name of the file to + be opened. Otherwise, it should be a file object, which will be used to + read or write the compressed data. mode can be 'r' for reading (default), 'w' for (over)writing, or 'a' for appending. @@ -91,15 +90,15 @@ else: raise ValueError("Invalid mode: {!r}".format(mode)) - if filename is not None and fileobj is None: + if isinstance(filename, (str, bytes)): self._fp = open(filename, mode) self._closefp = True self._mode = mode_code - elif fileobj is not None and filename is None: - self._fp = fileobj + elif hasattr(filename, "read") or hasattr(filename, "write"): + self._fp = filename self._mode = mode_code else: - raise ValueError("Must give exactly one of filename and fileobj") + raise TypeError("filename must be a str or bytes object, or a file") def close(self): """Flush and close the file. diff --git a/Lib/tarfile.py b/Lib/tarfile.py --- a/Lib/tarfile.py +++ b/Lib/tarfile.py @@ -1657,8 +1657,8 @@ except ImportError: raise CompressionError("bz2 module is not available") - fileobj = bz2.BZ2File(filename=name if fileobj is None else None, - mode=mode, fileobj=fileobj, compresslevel=compresslevel) + fileobj = bz2.BZ2File(fileobj or name, mode, + compresslevel=compresslevel) try: t = cls.taropen(name, mode, fileobj, **kwargs) diff --git a/Lib/test/test_bz2.py b/Lib/test/test_bz2.py --- a/Lib/test/test_bz2.py +++ b/Lib/test/test_bz2.py @@ -81,6 +81,20 @@ with open(self.filename, "wb") as f: f.write(self.DATA * streams) + def testBadArgs(self): + with self.assertRaises(TypeError): + BZ2File(123.456) + with self.assertRaises(ValueError): + BZ2File("/dev/null", "z") + with self.assertRaises(ValueError): + BZ2File("/dev/null", "rx") + with self.assertRaises(ValueError): + BZ2File("/dev/null", "rbt") + with self.assertRaises(ValueError): + BZ2File("/dev/null", compresslevel=0) + with self.assertRaises(ValueError): + BZ2File("/dev/null", compresslevel=10) + def testRead(self): self.createTempFile() with BZ2File(self.filename) as bz2f: @@ -348,7 +362,7 @@ def testFileno(self): self.createTempFile() with open(self.filename, 'rb') as rawf: - bz2f = BZ2File(fileobj=rawf) + bz2f = BZ2File(rawf) try: self.assertEqual(bz2f.fileno(), rawf.fileno()) finally: @@ -356,7 +370,7 @@ self.assertRaises(ValueError, bz2f.fileno) def testSeekable(self): - bz2f = BZ2File(fileobj=BytesIO(self.DATA)) + bz2f = BZ2File(BytesIO(self.DATA)) try: self.assertTrue(bz2f.seekable()) bz2f.read() @@ -365,7 +379,7 @@ bz2f.close() self.assertRaises(ValueError, bz2f.seekable) - bz2f = BZ2File(fileobj=BytesIO(), mode="w") + bz2f = BZ2File(BytesIO(), mode="w") try: self.assertFalse(bz2f.seekable()) finally: @@ -374,7 +388,7 @@ src = BytesIO(self.DATA) src.seekable = lambda: False - bz2f = BZ2File(fileobj=src) + bz2f = BZ2File(src) try: self.assertFalse(bz2f.seekable()) finally: @@ -382,7 +396,7 @@ self.assertRaises(ValueError, bz2f.seekable) def testReadable(self): - bz2f = BZ2File(fileobj=BytesIO(self.DATA)) + bz2f = BZ2File(BytesIO(self.DATA)) try: self.assertTrue(bz2f.readable()) bz2f.read() @@ -391,7 +405,7 @@ bz2f.close() self.assertRaises(ValueError, bz2f.readable) - bz2f = BZ2File(fileobj=BytesIO(), mode="w") + bz2f = BZ2File(BytesIO(), mode="w") try: self.assertFalse(bz2f.readable()) finally: @@ -399,7 +413,7 @@ self.assertRaises(ValueError, bz2f.readable) def testWritable(self): - bz2f = BZ2File(fileobj=BytesIO(self.DATA)) + bz2f = BZ2File(BytesIO(self.DATA)) try: self.assertFalse(bz2f.writable()) bz2f.read() @@ -408,7 +422,7 @@ bz2f.close() self.assertRaises(ValueError, bz2f.writable) - bz2f = BZ2File(fileobj=BytesIO(), mode="w") + bz2f = BZ2File(BytesIO(), mode="w") try: self.assertTrue(bz2f.writable()) finally: @@ -512,14 +526,14 @@ def testReadBytesIO(self): with BytesIO(self.DATA) as bio: - with BZ2File(fileobj=bio) as bz2f: + with BZ2File(bio) as bz2f: self.assertRaises(TypeError, bz2f.read, None) self.assertEqual(bz2f.read(), self.TEXT) self.assertFalse(bio.closed) def testPeekBytesIO(self): with BytesIO(self.DATA) as bio: - with BZ2File(fileobj=bio) as bz2f: + with BZ2File(bio) as bz2f: pdata = bz2f.peek() self.assertNotEqual(len(pdata), 0) self.assertTrue(self.TEXT.startswith(pdata)) @@ -527,7 +541,7 @@ def testWriteBytesIO(self): with BytesIO() as bio: - with BZ2File(fileobj=bio, mode="w") as bz2f: + with BZ2File(bio, "w") as bz2f: self.assertRaises(TypeError, bz2f.write) bz2f.write(self.TEXT) self.assertEqual(self.decompress(bio.getvalue()), self.TEXT) @@ -535,14 +549,14 @@ def testSeekForwardBytesIO(self): with BytesIO(self.DATA) as bio: - with BZ2File(fileobj=bio) as bz2f: + with BZ2File(bio) as bz2f: self.assertRaises(TypeError, bz2f.seek) bz2f.seek(150) self.assertEqual(bz2f.read(), self.TEXT[150:]) def testSeekBackwardsBytesIO(self): with BytesIO(self.DATA) as bio: - with BZ2File(fileobj=bio) as bz2f: + with BZ2File(bio) as bz2f: bz2f.read(500) bz2f.seek(-150, 1) self.assertEqual(bz2f.read(), self.TEXT[500-150:]) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -15,6 +15,9 @@ Library ------- +- BZ2File.__init__() now accepts a file object as its first argument, rather + than requiring a separate "fileobj" argument. + - gzip.open() now accepts file objects as well as filenames. - Issue #14992: os.makedirs(path, exist_ok=True) would raise an OSError -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 4 23:55:53 2012 From: python-checkins at python.org (nadeem.vawda) Date: Mon, 04 Jun 2012 23:55:53 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Clarify_acceptable_values_f?= =?utf8?b?b3IgQloyRmlsZS5fX2luaXRfXydzIG1vZGUgYXJndW1lbnQu?= Message-ID: http://hg.python.org/cpython/rev/3235748e6e81 changeset: 77354:3235748e6e81 user: Nadeem Vawda date: Mon Jun 04 23:31:22 2012 +0200 summary: Clarify acceptable values for BZ2File.__init__'s mode argument. files: Doc/library/bz2.rst | 8 +++++--- Lib/bz2.py | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Doc/library/bz2.rst b/Doc/library/bz2.rst --- a/Doc/library/bz2.rst +++ b/Doc/library/bz2.rst @@ -35,9 +35,11 @@ be used to read or write the compressed data. The *mode* argument can be either ``'r'`` for reading (default), ``'w'`` for - overwriting, or ``'a'`` for appending. If *filename* is a file object (rather - than an actual file name), a mode of ``'w'`` does not truncate the file, and - is instead equivalent to ``'a'``. + overwriting, or ``'a'`` for appending. These can equivalently be given as + ``'rb'``, ``'wb'``, and ``'ab'`` respectively. + + If *filename* is a file object (rather than an actual file name), a mode of + ``'w'`` does not truncate the file, and is instead equivalent to ``'a'``. The *buffering* argument is ignored. Its use is deprecated. diff --git a/Lib/bz2.py b/Lib/bz2.py --- a/Lib/bz2.py +++ b/Lib/bz2.py @@ -46,8 +46,8 @@ be opened. Otherwise, it should be a file object, which will be used to read or write the compressed data. - mode can be 'r' for reading (default), 'w' for (over)writing, or - 'a' for appending. + mode can be 'r' for reading (default), 'w' for (over)writing, or 'a' for + appending. These can equivalently be given as 'rb', 'wb', and 'ab'. buffering is ignored. Its use is deprecated. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 4 23:55:55 2012 From: python-checkins at python.org (nadeem.vawda) Date: Mon, 04 Jun 2012 23:55:55 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Add_a_function_bz2=2Eopen?= =?utf8?b?KCksIHRvIG1hdGNoIGd6aXAub3BlbigpLg==?= Message-ID: http://hg.python.org/cpython/rev/2143386a17a4 changeset: 77355:2143386a17a4 user: Nadeem Vawda date: Mon Jun 04 23:32:38 2012 +0200 summary: Add a function bz2.open(), to match gzip.open(). files: Doc/library/bz2.rst | 33 ++++++++- Lib/bz2.py | 47 +++++++++++- Lib/test/test_bz2.py | 117 ++++++++++++++++++++++++++++-- Misc/NEWS | 3 + 4 files changed, 184 insertions(+), 16 deletions(-) diff --git a/Doc/library/bz2.rst b/Doc/library/bz2.rst --- a/Doc/library/bz2.rst +++ b/Doc/library/bz2.rst @@ -14,7 +14,8 @@ The :mod:`bz2` module contains: -* The :class:`BZ2File` class for reading and writing compressed files. +* The :func:`.open` function and :class:`BZ2File` class for reading and + writing compressed files. * The :class:`BZ2Compressor` and :class:`BZ2Decompressor` classes for incremental (de)compression. * The :func:`compress` and :func:`decompress` functions for one-shot @@ -26,9 +27,37 @@ (De)compression of files ------------------------ +.. function:: open(filename, mode='r', compresslevel=9, encoding=None, errors=None, newline=None) + + Open a bzip2-compressed file in binary or text mode, returning a :term:`file + object`. + + As with the constructor for :class:`BZ2File`, the *filename* argument can be + an actual filename (a :class:`str` or :class:`bytes` object), or an existing + file object to read from or write to. + + The *mode* argument can be any of ``'r'``, ``'rb'``, ``'w'``, ``'wb'``, + ``'a'``, or ``'ab'`` for binary mode, or ``'rt'``, ``'wt'``, or ``'at'`` for + text mode. The default is ``'rb'``. + + The *compresslevel* argument is an integer from 1 to 9, as for the + :class:`BZ2File` constructor. + + For binary mode, this function is equivalent to the :class:`BZ2File` + constructor: ``BZ2File(filename, mode, compresslevel=compresslevel)``. In + this case, the *encoding*, *errors* and *newline* arguments must not be + provided. + + For text mode, a :class:`BZ2File` object is created, and wrapped in an + :class:`io.TextIOWrapper` instance with the specified encoding, error + handling behavior, and line ending(s). + + .. versionadded:: 3.3 + + .. class:: BZ2File(filename, mode='r', buffering=None, compresslevel=9) - Open a bzip2-compressed file. + Open a bzip2-compressed file in binary mode. If *filename* is a :class:`str` or :class:`bytes` object, open the named file directly. Otherwise, *filename* should be a :term:`file object`, which will diff --git a/Lib/bz2.py b/Lib/bz2.py --- a/Lib/bz2.py +++ b/Lib/bz2.py @@ -4,11 +4,12 @@ (de)compression, and functions for one-shot (de)compression. """ -__all__ = ["BZ2File", "BZ2Compressor", "BZ2Decompressor", "compress", - "decompress"] +__all__ = ["BZ2File", "BZ2Compressor", "BZ2Decompressor", + "open", "compress", "decompress"] __author__ = "Nadeem Vawda " +import builtins import io import warnings @@ -91,7 +92,7 @@ raise ValueError("Invalid mode: {!r}".format(mode)) if isinstance(filename, (str, bytes)): - self._fp = open(filename, mode) + self._fp = builtins.open(filename, mode) self._closefp = True self._mode = mode_code elif hasattr(filename, "read") or hasattr(filename, "write"): @@ -391,6 +392,46 @@ return self._pos +def open(filename, mode="rb", compresslevel=9, + encoding=None, errors=None, newline=None): + """Open a bzip2-compressed file in binary or text mode. + + The filename argument can be an actual filename (a str or bytes object), or + an existing file object to read from or write to. + + The mode argument can be "r", "rb", "w", "wb", "a" or "ab" for binary mode, + or "rt", "wt" or "at" for text mode. The default mode is "rb", and the + default compresslevel is 9. + + For binary mode, this function is equivalent to the BZ2File constructor: + BZ2File(filename, mode, compresslevel). In this case, the encoding, errors + and newline arguments must not be provided. + + For text mode, a BZ2File object is created, and wrapped in an + io.TextIOWrapper instance with the specified encoding, error handling + behavior, and line ending(s). + + """ + if "t" in mode: + if "b" in mode: + raise ValueError("Invalid mode: %r" % (mode,)) + else: + if encoding is not None: + raise ValueError("Argument 'encoding' not supported in binary mode") + if errors is not None: + raise ValueError("Argument 'errors' not supported in binary mode") + if newline is not None: + raise ValueError("Argument 'newline' not supported in binary mode") + + bz_mode = mode.replace("t", "") + binary_file = BZ2File(filename, bz_mode, compresslevel=compresslevel) + + if "t" in mode: + return io.TextIOWrapper(binary_file, encoding, errors, newline) + else: + return binary_file + + def compress(data, compresslevel=9): """Compress a block of data. diff --git a/Lib/test/test_bz2.py b/Lib/test/test_bz2.py --- a/Lib/test/test_bz2.py +++ b/Lib/test/test_bz2.py @@ -48,6 +48,13 @@ TEXT = b''.join(TEXT_LINES) DATA = b'BZh91AY&SY.\xc8N\x18\x00\x01>_\x80\x00\x10@\x02\xff\xf0\x01\x07n\x00?\xe7\xff\xe00\x01\x99\xaa\x00\xc0\x03F\x86\x8c#&\x83F\x9a\x03\x06\xa6\xd0\xa6\x93M\x0fQ\xa7\xa8\x06\x804hh\x12$\x11\xa4i4\xf14S\xd2\x88\xe5\xcd9gd6\x0b\n\xe9\x9b\xd5\x8a\x99\xf7\x08.K\x8ev\xfb\xf7xw\xbb\xdf\xa1\x92\xf1\xdd|/";\xa2\xba\x9f\xd5\xb1#A\xb6\xf6\xb3o\xc9\xc5y\\\xebO\xe7\x85\x9a\xbc\xb6f8\x952\xd5\xd7"%\x89>V,\xf7\xa6z\xe2\x9f\xa3\xdf\x11\x11"\xd6E)I\xa9\x13^\xca\xf3r\xd0\x03U\x922\xf26\xec\xb6\xed\x8b\xc3U\x13\x9d\xc5\x170\xa4\xfa^\x92\xacDF\x8a\x97\xd6\x19\xfe\xdd\xb8\xbd\x1a\x9a\x19\xa3\x80ankR\x8b\xe5\xd83]\xa9\xc6\x08\x82f\xf6\xb9"6l$\xb8j@\xc0\x8a\xb0l1..\xbak\x83ls\x15\xbc\xf4\xc1\x13\xbe\xf8E\xb8\x9d\r\xa8\x9dk\x84\xd3n\xfa\xacQ\x07\xb1%y\xaav\xb4\x08\xe0z\x1b\x16\xf5\x04\xe9\xcc\xb9\x08z\x1en7.G\xfc]\xc9\x14\xe1B@\xbb!8`' + def setUp(self): + self.filename = TESTFN + + def tearDown(self): + if os.path.isfile(self.filename): + os.unlink(self.filename) + if has_cmdline_bunzip2: def decompress(self, data): pop = subprocess.Popen("bunzip2", shell=True, @@ -70,13 +77,6 @@ class BZ2FileTest(BaseTest): "Test BZ2File type miscellaneous methods." - def setUp(self): - self.filename = TESTFN - - def tearDown(self): - if os.path.isfile(self.filename): - os.unlink(self.filename) - def createTempFile(self, streams=1): with open(self.filename, "wb") as f: f.write(self.DATA * streams) @@ -650,9 +650,7 @@ decompressed = None -class FuncTest(BaseTest): - "Test module functions" - +class CompressDecompressTest(BaseTest): def testCompress(self): data = bz2.compress(self.TEXT) self.assertEqual(self.decompress(data), self.TEXT) @@ -672,12 +670,109 @@ text = bz2.decompress(self.DATA * 5) self.assertEqual(text, self.TEXT * 5) + +class OpenTest(BaseTest): + def test_binary_modes(self): + with bz2.open(self.filename, "wb") as f: + f.write(self.TEXT) + with open(self.filename, "rb") as f: + file_data = bz2.decompress(f.read()) + self.assertEqual(file_data, self.TEXT) + with bz2.open(self.filename, "rb") as f: + self.assertEqual(f.read(), self.TEXT) + with bz2.open(self.filename, "ab") as f: + f.write(self.TEXT) + with open(self.filename, "rb") as f: + file_data = bz2.decompress(f.read()) + self.assertEqual(file_data, self.TEXT * 2) + + def test_implicit_binary_modes(self): + # Test implicit binary modes (no "b" or "t" in mode string). + with bz2.open(self.filename, "w") as f: + f.write(self.TEXT) + with open(self.filename, "rb") as f: + file_data = bz2.decompress(f.read()) + self.assertEqual(file_data, self.TEXT) + with bz2.open(self.filename, "r") as f: + self.assertEqual(f.read(), self.TEXT) + with bz2.open(self.filename, "a") as f: + f.write(self.TEXT) + with open(self.filename, "rb") as f: + file_data = bz2.decompress(f.read()) + self.assertEqual(file_data, self.TEXT * 2) + + def test_text_modes(self): + text = self.TEXT.decode("ascii") + text_native_eol = text.replace("\n", os.linesep) + with bz2.open(self.filename, "wt") as f: + f.write(text) + with open(self.filename, "rb") as f: + file_data = bz2.decompress(f.read()).decode("ascii") + self.assertEqual(file_data, text_native_eol) + with bz2.open(self.filename, "rt") as f: + self.assertEqual(f.read(), text) + with bz2.open(self.filename, "at") as f: + f.write(text) + with open(self.filename, "rb") as f: + file_data = bz2.decompress(f.read()).decode("ascii") + self.assertEqual(file_data, text_native_eol * 2) + + def test_fileobj(self): + with bz2.open(BytesIO(self.DATA), "r") as f: + self.assertEqual(f.read(), self.TEXT) + with bz2.open(BytesIO(self.DATA), "rb") as f: + self.assertEqual(f.read(), self.TEXT) + text = self.TEXT.decode("ascii") + with bz2.open(BytesIO(self.DATA), "rt") as f: + self.assertEqual(f.read(), text) + + def test_bad_params(self): + # Test invalid parameter combinations. + with self.assertRaises(ValueError): + bz2.open(self.filename, "wbt") + with self.assertRaises(ValueError): + bz2.open(self.filename, "rb", encoding="utf-8") + with self.assertRaises(ValueError): + bz2.open(self.filename, "rb", errors="ignore") + with self.assertRaises(ValueError): + bz2.open(self.filename, "rb", newline="\n") + + def test_encoding(self): + # Test non-default encoding. + text = self.TEXT.decode("ascii") + text_native_eol = text.replace("\n", os.linesep) + with bz2.open(self.filename, "wt", encoding="utf-16-le") as f: + f.write(text) + with open(self.filename, "rb") as f: + file_data = bz2.decompress(f.read()).decode("utf-16-le") + self.assertEqual(file_data, text_native_eol) + with bz2.open(self.filename, "rt", encoding="utf-16-le") as f: + self.assertEqual(f.read(), text) + + def test_encoding_error_handler(self): + # Test with non-default encoding error handler. + with bz2.open(self.filename, "wb") as f: + f.write(b"foo\xffbar") + with bz2.open(self.filename, "rt", encoding="ascii", errors="ignore") \ + as f: + self.assertEqual(f.read(), "foobar") + + def test_newline(self): + # Test with explicit newline (universal newline mode disabled). + text = self.TEXT.decode("ascii") + with bz2.open(self.filename, "wt", newline="\n") as f: + f.write(text) + with bz2.open(self.filename, "rt", newline="\r") as f: + self.assertEqual(f.readlines(), [text]) + + def test_main(): support.run_unittest( BZ2FileTest, BZ2CompressorTest, BZ2DecompressorTest, - FuncTest + CompressDecompressTest, + OpenTest, ) support.reap_children() diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -15,6 +15,9 @@ Library ------- +- The bz2 module now contains an open() function, allowing compressed files to + conveniently be opened in text mode as well as binary mode. + - BZ2File.__init__() now accepts a file object as its first argument, rather than requiring a separate "fileobj" argument. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 4 23:55:55 2012 From: python-checkins at python.org (nadeem.vawda) Date: Mon, 04 Jun 2012 23:55:55 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Simplify_usage_of_LZMAFile?= =?utf8?q?=27s_fileobj_support=2C_like_with_BZ2File=2E?= Message-ID: http://hg.python.org/cpython/rev/eae12226a66d changeset: 77356:eae12226a66d user: Nadeem Vawda date: Mon Jun 04 23:34:07 2012 +0200 summary: Simplify usage of LZMAFile's fileobj support, like with BZ2File. files: Doc/library/lzma.rst | 20 +- Lib/lzma.py | 19 +- Lib/tarfile.py | 3 +- Lib/test/test_lzma.py | 230 ++++++++++++++--------------- Misc/NEWS | 4 +- 5 files changed, 133 insertions(+), 143 deletions(-) diff --git a/Doc/library/lzma.rst b/Doc/library/lzma.rst --- a/Doc/library/lzma.rst +++ b/Doc/library/lzma.rst @@ -29,18 +29,20 @@ Reading and writing compressed files ------------------------------------ -.. class:: LZMAFile(filename=None, mode="r", \*, fileobj=None, format=None, check=-1, preset=None, filters=None) +.. class:: LZMAFile(filename=None, mode="r", \*, format=None, check=-1, preset=None, filters=None) - Open an LZMA-compressed file. + Open an LZMA-compressed file in binary mode. - An :class:`LZMAFile` can wrap an existing :term:`file object` (given by - *fileobj*), or operate directly on a named file (named by *filename*). - Exactly one of these two parameters should be provided. If *fileobj* is - provided, it is not closed when the :class:`LZMAFile` is closed. + An :class:`LZMAFile` can wrap an already-open :term:`file object`, or operate + directly on a named file. The *filename* argument specifies either the file + object to wrap, or the name of the file to open (as a :class:`str` or + :class:`bytes` object). When wrapping an existing file object, the wrapped + file will not be closed when the :class:`LZMAFile` is closed. The *mode* argument can be either ``"r"`` for reading (default), ``"w"`` for - overwriting, or ``"a"`` for appending. If *fileobj* is provided, a mode of - ``"w"`` does not truncate the file, and is instead equivalent to ``"a"``. + overwriting, or ``"a"`` for appending. If *filename* is an existing file + object, a mode of ``"w"`` does not truncate the file, and is instead + equivalent to ``"a"``. When opening a file for reading, the input file may be the concatenation of multiple separate compressed streams. These are transparently decoded as a @@ -360,7 +362,7 @@ import lzma with open("file.xz", "wb") as f: f.write(b"This data will not be compressed\n") - with lzma.LZMAFile(fileobj=f, mode="w") as lzf: + with lzma.LZMAFile(f, "w") as lzf: lzf.write(b"This *will* be compressed\n") f.write(b"Not compressed\n") diff --git a/Lib/lzma.py b/Lib/lzma.py --- a/Lib/lzma.py +++ b/Lib/lzma.py @@ -46,13 +46,12 @@ """ def __init__(self, filename=None, mode="r", *, - fileobj=None, format=None, check=-1, - preset=None, filters=None): - """Open an LZMA-compressed file. + format=None, check=-1, preset=None, filters=None): + """Open an LZMA-compressed file in binary mode. - If filename is given, open the named file. Otherwise, operate on - the file object given by fileobj. Exactly one of these two - parameters should be provided. + filename can be either an actual file name (given as a str or + bytes object), in which case the named file is opened, or it can + be an existing file object to read from or write to. mode can be "r" for reading (default), "w" for (over)writing, or "a" for appending. @@ -119,16 +118,16 @@ else: raise ValueError("Invalid mode: {!r}".format(mode)) - if filename is not None and fileobj is None: + if isinstance(filename, (str, bytes)): mode += "b" self._fp = open(filename, mode) self._closefp = True self._mode = mode_code - elif fileobj is not None and filename is None: - self._fp = fileobj + elif hasattr(filename, "read") or hasattr(filename, "write"): + self._fp = filename self._mode = mode_code else: - raise ValueError("Must give exactly one of filename and fileobj") + raise TypeError("filename must be a str or bytes object, or a file") def close(self): """Flush and close the file. diff --git a/Lib/tarfile.py b/Lib/tarfile.py --- a/Lib/tarfile.py +++ b/Lib/tarfile.py @@ -1681,8 +1681,7 @@ except ImportError: raise CompressionError("lzma module is not available") - fileobj = lzma.LZMAFile(filename=name if fileobj is None else None, - mode=mode, fileobj=fileobj, preset=preset) + fileobj = lzma.LZMAFile(fileobj or name, mode, preset=preset) try: t = cls.taropen(name, mode, fileobj, **kwargs) diff --git a/Lib/test/test_lzma.py b/Lib/test/test_lzma.py --- a/Lib/test/test_lzma.py +++ b/Lib/test/test_lzma.py @@ -358,11 +358,11 @@ class FileTestCase(unittest.TestCase): def test_init(self): - with LZMAFile(fileobj=BytesIO(COMPRESSED_XZ)) as f: + with LZMAFile(BytesIO(COMPRESSED_XZ)) as f: pass - with LZMAFile(fileobj=BytesIO(), mode="w") as f: + with LZMAFile(BytesIO(), "w") as f: pass - with LZMAFile(fileobj=BytesIO(), mode="a") as f: + with LZMAFile(BytesIO(), "a") as f: pass def test_init_with_filename(self): @@ -376,88 +376,84 @@ def test_init_bad_mode(self): with self.assertRaises(ValueError): - LZMAFile(fileobj=BytesIO(COMPRESSED_XZ), mode=(3, "x")) + LZMAFile(BytesIO(COMPRESSED_XZ), (3, "x")) with self.assertRaises(ValueError): - LZMAFile(fileobj=BytesIO(COMPRESSED_XZ), mode="") + LZMAFile(BytesIO(COMPRESSED_XZ), "") with self.assertRaises(ValueError): - LZMAFile(fileobj=BytesIO(COMPRESSED_XZ), mode="x") + LZMAFile(BytesIO(COMPRESSED_XZ), "x") with self.assertRaises(ValueError): - LZMAFile(fileobj=BytesIO(COMPRESSED_XZ), mode="rb") + LZMAFile(BytesIO(COMPRESSED_XZ), "rb") with self.assertRaises(ValueError): - LZMAFile(fileobj=BytesIO(COMPRESSED_XZ), mode="r+") + LZMAFile(BytesIO(COMPRESSED_XZ), "r+") with self.assertRaises(ValueError): - LZMAFile(fileobj=BytesIO(COMPRESSED_XZ), mode="wb") + LZMAFile(BytesIO(COMPRESSED_XZ), "wb") with self.assertRaises(ValueError): - LZMAFile(fileobj=BytesIO(COMPRESSED_XZ), mode="w+") + LZMAFile(BytesIO(COMPRESSED_XZ), "w+") with self.assertRaises(ValueError): - LZMAFile(fileobj=BytesIO(COMPRESSED_XZ), mode="rw") + LZMAFile(BytesIO(COMPRESSED_XZ), "rw") def test_init_bad_check(self): with self.assertRaises(TypeError): - LZMAFile(fileobj=BytesIO(), mode="w", check=b"asd") + LZMAFile(BytesIO(), "w", check=b"asd") # CHECK_UNKNOWN and anything above CHECK_ID_MAX should be invalid. with self.assertRaises(LZMAError): - LZMAFile(fileobj=BytesIO(), mode="w", check=lzma.CHECK_UNKNOWN) + LZMAFile(BytesIO(), "w", check=lzma.CHECK_UNKNOWN) with self.assertRaises(LZMAError): - LZMAFile(fileobj=BytesIO(), mode="w", check=lzma.CHECK_ID_MAX + 3) + LZMAFile(BytesIO(), "w", check=lzma.CHECK_ID_MAX + 3) # Cannot specify a check with mode="r". with self.assertRaises(ValueError): - LZMAFile(fileobj=BytesIO(COMPRESSED_XZ), check=lzma.CHECK_NONE) + LZMAFile(BytesIO(COMPRESSED_XZ), check=lzma.CHECK_NONE) with self.assertRaises(ValueError): - LZMAFile(fileobj=BytesIO(COMPRESSED_XZ), check=lzma.CHECK_CRC32) + LZMAFile(BytesIO(COMPRESSED_XZ), check=lzma.CHECK_CRC32) with self.assertRaises(ValueError): - LZMAFile(fileobj=BytesIO(COMPRESSED_XZ), check=lzma.CHECK_CRC64) + LZMAFile(BytesIO(COMPRESSED_XZ), check=lzma.CHECK_CRC64) with self.assertRaises(ValueError): - LZMAFile(fileobj=BytesIO(COMPRESSED_XZ), check=lzma.CHECK_SHA256) + LZMAFile(BytesIO(COMPRESSED_XZ), check=lzma.CHECK_SHA256) with self.assertRaises(ValueError): - LZMAFile(fileobj=BytesIO(COMPRESSED_XZ), check=lzma.CHECK_UNKNOWN) + LZMAFile(BytesIO(COMPRESSED_XZ), check=lzma.CHECK_UNKNOWN) def test_init_bad_preset(self): with self.assertRaises(TypeError): - LZMAFile(fileobj=BytesIO(), mode="w", preset=4.39) + LZMAFile(BytesIO(), "w", preset=4.39) with self.assertRaises(LZMAError): - LZMAFile(fileobj=BytesIO(), mode="w", preset=10) + LZMAFile(BytesIO(), "w", preset=10) with self.assertRaises(LZMAError): - LZMAFile(fileobj=BytesIO(), mode="w", preset=23) + LZMAFile(BytesIO(), "w", preset=23) with self.assertRaises(OverflowError): - LZMAFile(fileobj=BytesIO(), mode="w", preset=-1) + LZMAFile(BytesIO(), "w", preset=-1) with self.assertRaises(OverflowError): - LZMAFile(fileobj=BytesIO(), mode="w", preset=-7) + LZMAFile(BytesIO(), "w", preset=-7) with self.assertRaises(TypeError): - LZMAFile(fileobj=BytesIO(), mode="w", preset="foo") + LZMAFile(BytesIO(), "w", preset="foo") # Cannot specify a preset with mode="r". with self.assertRaises(ValueError): - LZMAFile(fileobj=BytesIO(COMPRESSED_XZ), preset=3) + LZMAFile(BytesIO(COMPRESSED_XZ), preset=3) def test_init_bad_filter_spec(self): with self.assertRaises(TypeError): - LZMAFile(fileobj=BytesIO(), mode="w", filters=[b"wobsite"]) + LZMAFile(BytesIO(), "w", filters=[b"wobsite"]) with self.assertRaises(ValueError): - LZMAFile(fileobj=BytesIO(), mode="w", filters=[{"xyzzy": 3}]) + LZMAFile(BytesIO(), "w", filters=[{"xyzzy": 3}]) with self.assertRaises(ValueError): - LZMAFile(fileobj=BytesIO(), mode="w", filters=[{"id": 98765}]) + LZMAFile(BytesIO(), "w", filters=[{"id": 98765}]) with self.assertRaises(ValueError): - LZMAFile(fileobj=BytesIO(), mode="w", + LZMAFile(BytesIO(), "w", filters=[{"id": lzma.FILTER_LZMA2, "foo": 0}]) with self.assertRaises(ValueError): - LZMAFile(fileobj=BytesIO(), mode="w", + LZMAFile(BytesIO(), "w", filters=[{"id": lzma.FILTER_DELTA, "foo": 0}]) with self.assertRaises(ValueError): - LZMAFile(fileobj=BytesIO(), mode="w", + LZMAFile(BytesIO(), "w", filters=[{"id": lzma.FILTER_X86, "foo": 0}]) def test_init_with_preset_and_filters(self): with self.assertRaises(ValueError): - LZMAFile(fileobj=BytesIO(), mode="w", format=lzma.FORMAT_RAW, - preset=6, filters=FILTERS_RAW_1) - - def test_init_with_filename_and_fileobj(self): - with self.assertRaises(ValueError): - LZMAFile("/dev/null", fileobj=BytesIO()) + LZMAFile(BytesIO(), "w", format=lzma.FORMAT_RAW, + preset=6, filters=FILTERS_RAW_1) def test_close(self): with BytesIO(COMPRESSED_XZ) as src: - f = LZMAFile(fileobj=src) + f = LZMAFile(src) f.close() # LZMAFile.close() should not close the underlying file object. self.assertFalse(src.closed) @@ -476,7 +472,7 @@ f.close() def test_closed(self): - f = LZMAFile(fileobj=BytesIO(COMPRESSED_XZ)) + f = LZMAFile(BytesIO(COMPRESSED_XZ)) try: self.assertFalse(f.closed) f.read() @@ -485,7 +481,7 @@ f.close() self.assertTrue(f.closed) - f = LZMAFile(fileobj=BytesIO(), mode="w") + f = LZMAFile(BytesIO(), "w") try: self.assertFalse(f.closed) finally: @@ -493,7 +489,7 @@ self.assertTrue(f.closed) def test_fileno(self): - f = LZMAFile(fileobj=BytesIO(COMPRESSED_XZ)) + f = LZMAFile(BytesIO(COMPRESSED_XZ)) try: self.assertRaises(UnsupportedOperation, f.fileno) finally: @@ -509,7 +505,7 @@ self.assertRaises(ValueError, f.fileno) def test_seekable(self): - f = LZMAFile(fileobj=BytesIO(COMPRESSED_XZ)) + f = LZMAFile(BytesIO(COMPRESSED_XZ)) try: self.assertTrue(f.seekable()) f.read() @@ -518,7 +514,7 @@ f.close() self.assertRaises(ValueError, f.seekable) - f = LZMAFile(fileobj=BytesIO(), mode="w") + f = LZMAFile(BytesIO(), "w") try: self.assertFalse(f.seekable()) finally: @@ -527,7 +523,7 @@ src = BytesIO(COMPRESSED_XZ) src.seekable = lambda: False - f = LZMAFile(fileobj=src) + f = LZMAFile(src) try: self.assertFalse(f.seekable()) finally: @@ -535,7 +531,7 @@ self.assertRaises(ValueError, f.seekable) def test_readable(self): - f = LZMAFile(fileobj=BytesIO(COMPRESSED_XZ)) + f = LZMAFile(BytesIO(COMPRESSED_XZ)) try: self.assertTrue(f.readable()) f.read() @@ -544,7 +540,7 @@ f.close() self.assertRaises(ValueError, f.readable) - f = LZMAFile(fileobj=BytesIO(), mode="w") + f = LZMAFile(BytesIO(), "w") try: self.assertFalse(f.readable()) finally: @@ -552,7 +548,7 @@ self.assertRaises(ValueError, f.readable) def test_writable(self): - f = LZMAFile(fileobj=BytesIO(COMPRESSED_XZ)) + f = LZMAFile(BytesIO(COMPRESSED_XZ)) try: self.assertFalse(f.writable()) f.read() @@ -561,7 +557,7 @@ f.close() self.assertRaises(ValueError, f.writable) - f = LZMAFile(fileobj=BytesIO(), mode="w") + f = LZMAFile(BytesIO(), "w") try: self.assertTrue(f.writable()) finally: @@ -569,50 +565,46 @@ self.assertRaises(ValueError, f.writable) def test_read(self): - with LZMAFile(fileobj=BytesIO(COMPRESSED_XZ)) as f: + with LZMAFile(BytesIO(COMPRESSED_XZ)) as f: self.assertEqual(f.read(), INPUT) self.assertEqual(f.read(), b"") - with LZMAFile(fileobj=BytesIO(COMPRESSED_ALONE)) as f: + with LZMAFile(BytesIO(COMPRESSED_ALONE)) as f: self.assertEqual(f.read(), INPUT) - with LZMAFile(fileobj=BytesIO(COMPRESSED_XZ), - format=lzma.FORMAT_XZ) as f: + with LZMAFile(BytesIO(COMPRESSED_XZ), format=lzma.FORMAT_XZ) as f: self.assertEqual(f.read(), INPUT) self.assertEqual(f.read(), b"") - with LZMAFile(fileobj=BytesIO(COMPRESSED_ALONE), - format=lzma.FORMAT_ALONE) as f: + with LZMAFile(BytesIO(COMPRESSED_ALONE), format=lzma.FORMAT_ALONE) as f: self.assertEqual(f.read(), INPUT) self.assertEqual(f.read(), b"") - with LZMAFile(fileobj=BytesIO(COMPRESSED_RAW_1), + with LZMAFile(BytesIO(COMPRESSED_RAW_1), format=lzma.FORMAT_RAW, filters=FILTERS_RAW_1) as f: self.assertEqual(f.read(), INPUT) self.assertEqual(f.read(), b"") - with LZMAFile(fileobj=BytesIO(COMPRESSED_RAW_2), + with LZMAFile(BytesIO(COMPRESSED_RAW_2), format=lzma.FORMAT_RAW, filters=FILTERS_RAW_2) as f: self.assertEqual(f.read(), INPUT) self.assertEqual(f.read(), b"") - with LZMAFile(fileobj=BytesIO(COMPRESSED_RAW_3), + with LZMAFile(BytesIO(COMPRESSED_RAW_3), format=lzma.FORMAT_RAW, filters=FILTERS_RAW_3) as f: self.assertEqual(f.read(), INPUT) self.assertEqual(f.read(), b"") - with LZMAFile(fileobj=BytesIO(COMPRESSED_RAW_4), + with LZMAFile(BytesIO(COMPRESSED_RAW_4), format=lzma.FORMAT_RAW, filters=FILTERS_RAW_4) as f: self.assertEqual(f.read(), INPUT) self.assertEqual(f.read(), b"") def test_read_0(self): - with LZMAFile(fileobj=BytesIO(COMPRESSED_XZ)) as f: + with LZMAFile(BytesIO(COMPRESSED_XZ)) as f: self.assertEqual(f.read(0), b"") - with LZMAFile(fileobj=BytesIO(COMPRESSED_ALONE)) as f: + with LZMAFile(BytesIO(COMPRESSED_ALONE)) as f: self.assertEqual(f.read(0), b"") - with LZMAFile(fileobj=BytesIO(COMPRESSED_XZ), - format=lzma.FORMAT_XZ) as f: + with LZMAFile(BytesIO(COMPRESSED_XZ), format=lzma.FORMAT_XZ) as f: self.assertEqual(f.read(0), b"") - with LZMAFile(fileobj=BytesIO(COMPRESSED_ALONE), - format=lzma.FORMAT_ALONE) as f: + with LZMAFile(BytesIO(COMPRESSED_ALONE), format=lzma.FORMAT_ALONE) as f: self.assertEqual(f.read(0), b"") def test_read_10(self): - with LZMAFile(fileobj=BytesIO(COMPRESSED_XZ)) as f: + with LZMAFile(BytesIO(COMPRESSED_XZ)) as f: chunks = [] while True: result = f.read(10) @@ -623,11 +615,11 @@ self.assertEqual(b"".join(chunks), INPUT) def test_read_multistream(self): - with LZMAFile(fileobj=BytesIO(COMPRESSED_XZ * 5)) as f: + with LZMAFile(BytesIO(COMPRESSED_XZ * 5)) as f: self.assertEqual(f.read(), INPUT * 5) - with LZMAFile(fileobj=BytesIO(COMPRESSED_XZ + COMPRESSED_ALONE)) as f: + with LZMAFile(BytesIO(COMPRESSED_XZ + COMPRESSED_ALONE)) as f: self.assertEqual(f.read(), INPUT * 2) - with LZMAFile(fileobj=BytesIO(COMPRESSED_RAW_3 * 4), + with LZMAFile(BytesIO(COMPRESSED_RAW_3 * 4), format=lzma.FORMAT_RAW, filters=FILTERS_RAW_3) as f: self.assertEqual(f.read(), INPUT * 4) @@ -637,7 +629,7 @@ saved_buffer_size = lzma._BUFFER_SIZE lzma._BUFFER_SIZE = len(COMPRESSED_XZ) try: - with LZMAFile(fileobj=BytesIO(COMPRESSED_XZ * 5)) as f: + with LZMAFile(BytesIO(COMPRESSED_XZ * 5)) as f: self.assertEqual(f.read(), INPUT * 5) finally: lzma._BUFFER_SIZE = saved_buffer_size @@ -649,20 +641,20 @@ self.assertEqual(f.read(), b"") def test_read_incomplete(self): - with LZMAFile(fileobj=BytesIO(COMPRESSED_XZ[:128])) as f: + with LZMAFile(BytesIO(COMPRESSED_XZ[:128])) as f: self.assertRaises(EOFError, f.read) def test_read_bad_args(self): - f = LZMAFile(fileobj=BytesIO(COMPRESSED_XZ)) + f = LZMAFile(BytesIO(COMPRESSED_XZ)) f.close() self.assertRaises(ValueError, f.read) - with LZMAFile(fileobj=BytesIO(), mode="w") as f: + with LZMAFile(BytesIO(), "w") as f: self.assertRaises(ValueError, f.read) - with LZMAFile(fileobj=BytesIO(COMPRESSED_XZ)) as f: + with LZMAFile(BytesIO(COMPRESSED_XZ)) as f: self.assertRaises(TypeError, f.read, None) def test_read1(self): - with LZMAFile(fileobj=BytesIO(COMPRESSED_XZ)) as f: + with LZMAFile(BytesIO(COMPRESSED_XZ)) as f: blocks = [] while True: result = f.read1() @@ -673,11 +665,11 @@ self.assertEqual(f.read1(), b"") def test_read1_0(self): - with LZMAFile(fileobj=BytesIO(COMPRESSED_XZ)) as f: + with LZMAFile(BytesIO(COMPRESSED_XZ)) as f: self.assertEqual(f.read1(0), b"") def test_read1_10(self): - with LZMAFile(fileobj=BytesIO(COMPRESSED_XZ)) as f: + with LZMAFile(BytesIO(COMPRESSED_XZ)) as f: blocks = [] while True: result = f.read1(10) @@ -688,7 +680,7 @@ self.assertEqual(f.read1(), b"") def test_read1_multistream(self): - with LZMAFile(fileobj=BytesIO(COMPRESSED_XZ * 5)) as f: + with LZMAFile(BytesIO(COMPRESSED_XZ * 5)) as f: blocks = [] while True: result = f.read1() @@ -699,78 +691,76 @@ self.assertEqual(f.read1(), b"") def test_read1_bad_args(self): - f = LZMAFile(fileobj=BytesIO(COMPRESSED_XZ)) + f = LZMAFile(BytesIO(COMPRESSED_XZ)) f.close() self.assertRaises(ValueError, f.read1) - with LZMAFile(fileobj=BytesIO(), mode="w") as f: + with LZMAFile(BytesIO(), "w") as f: self.assertRaises(ValueError, f.read1) - with LZMAFile(fileobj=BytesIO(COMPRESSED_XZ)) as f: + with LZMAFile(BytesIO(COMPRESSED_XZ)) as f: self.assertRaises(TypeError, f.read1, None) def test_peek(self): - with LZMAFile(fileobj=BytesIO(COMPRESSED_XZ)) as f: + with LZMAFile(BytesIO(COMPRESSED_XZ)) as f: result = f.peek() self.assertGreater(len(result), 0) self.assertTrue(INPUT.startswith(result)) self.assertEqual(f.read(), INPUT) - with LZMAFile(fileobj=BytesIO(COMPRESSED_XZ)) as f: + with LZMAFile(BytesIO(COMPRESSED_XZ)) as f: result = f.peek(10) self.assertGreater(len(result), 0) self.assertTrue(INPUT.startswith(result)) self.assertEqual(f.read(), INPUT) def test_peek_bad_args(self): - with LZMAFile(fileobj=BytesIO(), mode="w") as f: + with LZMAFile(BytesIO(), "w") as f: self.assertRaises(ValueError, f.peek) def test_iterator(self): with BytesIO(INPUT) as f: lines = f.readlines() - with LZMAFile(fileobj=BytesIO(COMPRESSED_XZ)) as f: + with LZMAFile(BytesIO(COMPRESSED_XZ)) as f: self.assertListEqual(list(iter(f)), lines) - with LZMAFile(fileobj=BytesIO(COMPRESSED_ALONE)) as f: + with LZMAFile(BytesIO(COMPRESSED_ALONE)) as f: self.assertListEqual(list(iter(f)), lines) - with LZMAFile(fileobj=BytesIO(COMPRESSED_XZ), - format=lzma.FORMAT_XZ) as f: + with LZMAFile(BytesIO(COMPRESSED_XZ), format=lzma.FORMAT_XZ) as f: self.assertListEqual(list(iter(f)), lines) - with LZMAFile(fileobj=BytesIO(COMPRESSED_ALONE), - format=lzma.FORMAT_ALONE) as f: + with LZMAFile(BytesIO(COMPRESSED_ALONE), format=lzma.FORMAT_ALONE) as f: self.assertListEqual(list(iter(f)), lines) - with LZMAFile(fileobj=BytesIO(COMPRESSED_RAW_2), + with LZMAFile(BytesIO(COMPRESSED_RAW_2), format=lzma.FORMAT_RAW, filters=FILTERS_RAW_2) as f: self.assertListEqual(list(iter(f)), lines) def test_readline(self): with BytesIO(INPUT) as f: lines = f.readlines() - with LZMAFile(fileobj=BytesIO(COMPRESSED_XZ)) as f: + with LZMAFile(BytesIO(COMPRESSED_XZ)) as f: for line in lines: self.assertEqual(f.readline(), line) def test_readlines(self): with BytesIO(INPUT) as f: lines = f.readlines() - with LZMAFile(fileobj=BytesIO(COMPRESSED_XZ)) as f: + with LZMAFile(BytesIO(COMPRESSED_XZ)) as f: self.assertListEqual(f.readlines(), lines) def test_write(self): with BytesIO() as dst: - with LZMAFile(fileobj=dst, mode="w") as f: + with LZMAFile(dst, "w") as f: f.write(INPUT) expected = lzma.compress(INPUT) self.assertEqual(dst.getvalue(), expected) with BytesIO() as dst: - with LZMAFile(fileobj=dst, mode="w", format=lzma.FORMAT_XZ) as f: + with LZMAFile(dst, "w", format=lzma.FORMAT_XZ) as f: f.write(INPUT) expected = lzma.compress(INPUT, format=lzma.FORMAT_XZ) self.assertEqual(dst.getvalue(), expected) with BytesIO() as dst: - with LZMAFile(fileobj=dst, mode="w", format=lzma.FORMAT_ALONE) as f: + with LZMAFile(dst, "w", format=lzma.FORMAT_ALONE) as f: f.write(INPUT) expected = lzma.compress(INPUT, format=lzma.FORMAT_ALONE) self.assertEqual(dst.getvalue(), expected) with BytesIO() as dst: - with LZMAFile(fileobj=dst, mode="w", format=lzma.FORMAT_RAW, + with LZMAFile(dst, "w", format=lzma.FORMAT_RAW, filters=FILTERS_RAW_2) as f: f.write(INPUT) expected = lzma.compress(INPUT, format=lzma.FORMAT_RAW, @@ -779,7 +769,7 @@ def test_write_10(self): with BytesIO() as dst: - with LZMAFile(fileobj=dst, mode="w") as f: + with LZMAFile(dst, "w") as f: for start in range(0, len(INPUT), 10): f.write(INPUT[start:start+10]) expected = lzma.compress(INPUT) @@ -791,11 +781,11 @@ part3 = INPUT[1536:] expected = b"".join(lzma.compress(x) for x in (part1, part2, part3)) with BytesIO() as dst: - with LZMAFile(fileobj=dst, mode="w") as f: + with LZMAFile(dst, "w") as f: f.write(part1) - with LZMAFile(fileobj=dst, mode="a") as f: + with LZMAFile(dst, "a") as f: f.write(part2) - with LZMAFile(fileobj=dst, mode="a") as f: + with LZMAFile(dst, "a") as f: f.write(part3) self.assertEqual(dst.getvalue(), expected) @@ -827,12 +817,12 @@ unlink(TESTFN) def test_write_bad_args(self): - f = LZMAFile(fileobj=BytesIO(), mode="w") + f = LZMAFile(BytesIO(), "w") f.close() self.assertRaises(ValueError, f.write, b"foo") - with LZMAFile(fileobj=BytesIO(COMPRESSED_XZ), mode="r") as f: + with LZMAFile(BytesIO(COMPRESSED_XZ), "r") as f: self.assertRaises(ValueError, f.write, b"bar") - with LZMAFile(fileobj=BytesIO(), mode="w") as f: + with LZMAFile(BytesIO(), "w") as f: self.assertRaises(TypeError, f.write, None) self.assertRaises(TypeError, f.write, "text") self.assertRaises(TypeError, f.write, 789) @@ -841,75 +831,75 @@ with BytesIO(INPUT) as f: lines = f.readlines() with BytesIO() as dst: - with LZMAFile(fileobj=dst, mode="w") as f: + with LZMAFile(dst, "w") as f: f.writelines(lines) expected = lzma.compress(INPUT) self.assertEqual(dst.getvalue(), expected) def test_seek_forward(self): - with LZMAFile(fileobj=BytesIO(COMPRESSED_XZ)) as f: + with LZMAFile(BytesIO(COMPRESSED_XZ)) as f: f.seek(555) self.assertEqual(f.read(), INPUT[555:]) def test_seek_forward_across_streams(self): - with LZMAFile(fileobj=BytesIO(COMPRESSED_XZ * 2)) as f: + with LZMAFile(BytesIO(COMPRESSED_XZ * 2)) as f: f.seek(len(INPUT) + 123) self.assertEqual(f.read(), INPUT[123:]) def test_seek_forward_relative_to_current(self): - with LZMAFile(fileobj=BytesIO(COMPRESSED_XZ)) as f: + with LZMAFile(BytesIO(COMPRESSED_XZ)) as f: f.read(100) f.seek(1236, 1) self.assertEqual(f.read(), INPUT[1336:]) def test_seek_forward_relative_to_end(self): - with LZMAFile(fileobj=BytesIO(COMPRESSED_XZ)) as f: + with LZMAFile(BytesIO(COMPRESSED_XZ)) as f: f.seek(-555, 2) self.assertEqual(f.read(), INPUT[-555:]) def test_seek_backward(self): - with LZMAFile(fileobj=BytesIO(COMPRESSED_XZ)) as f: + with LZMAFile(BytesIO(COMPRESSED_XZ)) as f: f.read(1001) f.seek(211) self.assertEqual(f.read(), INPUT[211:]) def test_seek_backward_across_streams(self): - with LZMAFile(fileobj=BytesIO(COMPRESSED_XZ * 2)) as f: + with LZMAFile(BytesIO(COMPRESSED_XZ * 2)) as f: f.read(len(INPUT) + 333) f.seek(737) self.assertEqual(f.read(), INPUT[737:] + INPUT) def test_seek_backward_relative_to_end(self): - with LZMAFile(fileobj=BytesIO(COMPRESSED_XZ)) as f: + with LZMAFile(BytesIO(COMPRESSED_XZ)) as f: f.seek(-150, 2) self.assertEqual(f.read(), INPUT[-150:]) def test_seek_past_end(self): - with LZMAFile(fileobj=BytesIO(COMPRESSED_XZ)) as f: + with LZMAFile(BytesIO(COMPRESSED_XZ)) as f: f.seek(len(INPUT) + 9001) self.assertEqual(f.tell(), len(INPUT)) self.assertEqual(f.read(), b"") def test_seek_past_start(self): - with LZMAFile(fileobj=BytesIO(COMPRESSED_XZ)) as f: + with LZMAFile(BytesIO(COMPRESSED_XZ)) as f: f.seek(-88) self.assertEqual(f.tell(), 0) self.assertEqual(f.read(), INPUT) def test_seek_bad_args(self): - f = LZMAFile(fileobj=BytesIO(COMPRESSED_XZ)) + f = LZMAFile(BytesIO(COMPRESSED_XZ)) f.close() self.assertRaises(ValueError, f.seek, 0) - with LZMAFile(fileobj=BytesIO(), mode="w") as f: + with LZMAFile(BytesIO(), "w") as f: self.assertRaises(ValueError, f.seek, 0) - with LZMAFile(fileobj=BytesIO(COMPRESSED_XZ)) as f: + with LZMAFile(BytesIO(COMPRESSED_XZ)) as f: self.assertRaises(ValueError, f.seek, 0, 3) self.assertRaises(ValueError, f.seek, 9, ()) self.assertRaises(TypeError, f.seek, None) self.assertRaises(TypeError, f.seek, b"derp") def test_tell(self): - with LZMAFile(fileobj=BytesIO(COMPRESSED_XZ)) as f: + with LZMAFile(BytesIO(COMPRESSED_XZ)) as f: pos = 0 while True: self.assertEqual(f.tell(), pos) @@ -918,14 +908,14 @@ break pos += len(result) self.assertEqual(f.tell(), len(INPUT)) - with LZMAFile(fileobj=BytesIO(), mode="w") as f: + with LZMAFile(BytesIO(), "w") as f: for pos in range(0, len(INPUT), 144): self.assertEqual(f.tell(), pos) f.write(INPUT[pos:pos+144]) self.assertEqual(f.tell(), len(INPUT)) def test_tell_bad_args(self): - f = LZMAFile(fileobj=BytesIO(COMPRESSED_XZ)) + f = LZMAFile(BytesIO(COMPRESSED_XZ)) f.close() self.assertRaises(ValueError, f.tell) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -18,8 +18,8 @@ - The bz2 module now contains an open() function, allowing compressed files to conveniently be opened in text mode as well as binary mode. -- BZ2File.__init__() now accepts a file object as its first argument, rather - than requiring a separate "fileobj" argument. +- BZ2File.__init__() and LZMAFile.__init__() now accept a file object as their + first argument, rather than requiring a separate "fileobj" argument. - gzip.open() now accepts file objects as well as filenames. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 4 23:55:56 2012 From: python-checkins at python.org (nadeem.vawda) Date: Mon, 04 Jun 2012 23:55:56 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Allow_LZMAFile_to_accept_mo?= =?utf8?q?des_with_a_=22b=22_suffix=2E?= Message-ID: http://hg.python.org/cpython/rev/8cf02a2a44f0 changeset: 77357:8cf02a2a44f0 user: Nadeem Vawda date: Mon Jun 04 23:36:24 2012 +0200 summary: Allow LZMAFile to accept modes with a "b" suffix. files: Doc/library/lzma.rst | 8 +++++--- Lib/lzma.py | 10 ++++++---- Lib/test/test_lzma.py | 19 +++++++++++++++++-- Misc/NEWS | 2 ++ 4 files changed, 30 insertions(+), 9 deletions(-) diff --git a/Doc/library/lzma.rst b/Doc/library/lzma.rst --- a/Doc/library/lzma.rst +++ b/Doc/library/lzma.rst @@ -40,9 +40,11 @@ file will not be closed when the :class:`LZMAFile` is closed. The *mode* argument can be either ``"r"`` for reading (default), ``"w"`` for - overwriting, or ``"a"`` for appending. If *filename* is an existing file - object, a mode of ``"w"`` does not truncate the file, and is instead - equivalent to ``"a"``. + overwriting, or ``"a"`` for appending. These can equivalently be given as + ``"rb"``, ``"wb"``, and ``"ab"`` respectively. + + If *filename* is a file object (rather than an actual file name), a mode of + ``"w"`` does not truncate the file, and is instead equivalent to ``"a"``. When opening a file for reading, the input file may be the concatenation of multiple separate compressed streams. These are transparently decoded as a diff --git a/Lib/lzma.py b/Lib/lzma.py --- a/Lib/lzma.py +++ b/Lib/lzma.py @@ -54,7 +54,8 @@ be an existing file object to read from or write to. mode can be "r" for reading (default), "w" for (over)writing, or - "a" for appending. + "a" for appending. These can equivalently be given as "rb", "wb", + and "ab" respectively. format specifies the container format to use for the file. If mode is "r", this defaults to FORMAT_AUTO. Otherwise, the @@ -93,7 +94,7 @@ self._pos = 0 self._size = -1 - if mode == "r": + if mode in ("r", "rb"): if check != -1: raise ValueError("Cannot specify an integrity check " "when opening a file for reading") @@ -109,7 +110,7 @@ self._init_args = {"format":format, "filters":filters} self._decompressor = LZMADecompressor(**self._init_args) self._buffer = None - elif mode in ("w", "a"): + elif mode in ("w", "wb", "a", "ab"): if format is None: format = FORMAT_XZ mode_code = _MODE_WRITE @@ -119,7 +120,8 @@ raise ValueError("Invalid mode: {!r}".format(mode)) if isinstance(filename, (str, bytes)): - mode += "b" + if "b" not in mode: + mode += "b" self._fp = open(filename, mode) self._closefp = True self._mode = mode_code diff --git a/Lib/test/test_lzma.py b/Lib/test/test_lzma.py --- a/Lib/test/test_lzma.py +++ b/Lib/test/test_lzma.py @@ -374,6 +374,21 @@ with LZMAFile(TESTFN, "a") as f: pass + def test_init_mode(self): + with TempFile(TESTFN): + with LZMAFile(TESTFN, "r"): + pass + with LZMAFile(TESTFN, "rb"): + pass + with LZMAFile(TESTFN, "w"): + pass + with LZMAFile(TESTFN, "wb"): + pass + with LZMAFile(TESTFN, "a"): + pass + with LZMAFile(TESTFN, "ab"): + pass + def test_init_bad_mode(self): with self.assertRaises(ValueError): LZMAFile(BytesIO(COMPRESSED_XZ), (3, "x")) @@ -382,11 +397,11 @@ with self.assertRaises(ValueError): LZMAFile(BytesIO(COMPRESSED_XZ), "x") with self.assertRaises(ValueError): - LZMAFile(BytesIO(COMPRESSED_XZ), "rb") + LZMAFile(BytesIO(COMPRESSED_XZ), "rt") with self.assertRaises(ValueError): LZMAFile(BytesIO(COMPRESSED_XZ), "r+") with self.assertRaises(ValueError): - LZMAFile(BytesIO(COMPRESSED_XZ), "wb") + LZMAFile(BytesIO(COMPRESSED_XZ), "wt") with self.assertRaises(ValueError): LZMAFile(BytesIO(COMPRESSED_XZ), "w+") with self.assertRaises(ValueError): diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -15,6 +15,8 @@ Library ------- +- LZMAFile now accepts the modes "rb"/"wb"/"ab" as synonyms of "r"/"w"/"a". + - The bz2 module now contains an open() function, allowing compressed files to conveniently be opened in text mode as well as binary mode. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 4 23:55:57 2012 From: python-checkins at python.org (nadeem.vawda) Date: Mon, 04 Jun 2012 23:55:57 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Add_a_function_lzma=2Eopen?= =?utf8?b?KCksIHRvIG1hdGNoIGd6aXAub3BlbigpIGFuZCBiejIub3BlbigpLg==?= Message-ID: http://hg.python.org/cpython/rev/3d82ced09043 changeset: 77358:3d82ced09043 user: Nadeem Vawda date: Mon Jun 04 23:38:12 2012 +0200 summary: Add a function lzma.open(), to match gzip.open() and bz2.open(). files: Doc/library/lzma.rst | 29 ++++++++ Lib/lzma.py | 50 ++++++++++++++- Lib/test/test_lzma.py | 101 ++++++++++++++++++++++++++++++ Misc/NEWS | 4 +- 4 files changed, 180 insertions(+), 4 deletions(-) diff --git a/Doc/library/lzma.rst b/Doc/library/lzma.rst --- a/Doc/library/lzma.rst +++ b/Doc/library/lzma.rst @@ -29,6 +29,35 @@ Reading and writing compressed files ------------------------------------ +.. function:: open(filename, mode="rb", \*, format=None, check=-1, preset=None, filters=None, encoding=None, errors=None, newline=None) + + Open an LZMA-compressed file in binary or text mode, returning a :term:`file + object`. + + The *filename* argument can be either an actual file name (given as a + :class:`str` or :class:`bytes` object), in which case the named file is + opened, or it can be an existing file object to read from or write to. + + The *mode* argument can be any of ``"r"``, ``"rb"``, ``"w"``, ``"wb"``, + ``"a"`` or ``"ab"`` for binary mode, or ``"rt"``, ``"wt"``, or ``"at"`` for + text mode. The default is ``"rb"``. + + When opening a file for reading, the *format* and *filters* arguments have + the same meanings as for :class:`LZMADecompressor`. In this case, the *check* + and *preset* arguments should not be used. + + When opening a file for writing, the *format*, *check*, *preset* and + *filters* arguments have the same meanings as for :class:`LZMACompressor`. + + For binary mode, this function is equivalent to the :class:`LZMAFile` + constructor: ``LZMAFile(filename, mode, ...)``. In this case, the *encoding*, + *errors* and *newline* arguments must not be provided. + + For text mode, a :class:`LZMAFile` object is created, and wrapped in an + :class:`io.TextIOWrapper` instance with the specified encoding, error + handling behavior, and line ending(s). + + .. class:: LZMAFile(filename=None, mode="r", \*, format=None, check=-1, preset=None, filters=None) Open an LZMA-compressed file in binary mode. diff --git a/Lib/lzma.py b/Lib/lzma.py --- a/Lib/lzma.py +++ b/Lib/lzma.py @@ -18,10 +18,11 @@ "MODE_FAST", "MODE_NORMAL", "PRESET_DEFAULT", "PRESET_EXTREME", "LZMACompressor", "LZMADecompressor", "LZMAFile", "LZMAError", - "compress", "decompress", "is_check_supported", + "open", "compress", "decompress", "is_check_supported", "encode_filter_properties", "decode_filter_properties", ] +import builtins import io from _lzma import * @@ -122,7 +123,7 @@ if isinstance(filename, (str, bytes)): if "b" not in mode: mode += "b" - self._fp = open(filename, mode) + self._fp = builtins.open(filename, mode) self._closefp = True self._mode = mode_code elif hasattr(filename, "read") or hasattr(filename, "write"): @@ -370,6 +371,51 @@ return self._pos +def open(filename, mode="rb", *, + format=None, check=-1, preset=None, filters=None, + encoding=None, errors=None, newline=None): + """Open an LZMA-compressed file in binary or text mode. + + filename can be either an actual file name (given as a str or bytes object), + in which case the named file is opened, or it can be an existing file object + to read from or write to. + + The mode argument can be "r", "rb" (default), "w", "wb", "a", or "ab" for + binary mode, or "rt", "wt" or "at" for text mode. + + The format, check, preset and filters arguments specify the compression + settings, as for LZMACompressor, LZMADecompressor and LZMAFile. + + For binary mode, this function is equivalent to the LZMAFile constructor: + LZMAFile(filename, mode, ...). In this case, the encoding, errors and + newline arguments must not be provided. + + For text mode, a LZMAFile object is created, and wrapped in an + io.TextIOWrapper instance with the specified encoding, error handling + behavior, and line ending(s). + + """ + if "t" in mode: + if "b" in mode: + raise ValueError("Invalid mode: %r" % (mode,)) + else: + if encoding is not None: + raise ValueError("Argument 'encoding' not supported in binary mode") + if errors is not None: + raise ValueError("Argument 'errors' not supported in binary mode") + if newline is not None: + raise ValueError("Argument 'newline' not supported in binary mode") + + lz_mode = mode.replace("t", "") + binary_file = LZMAFile(filename, lz_mode, format=format, check=check, + preset=preset, filters=filters) + + if "t" in mode: + return io.TextIOWrapper(binary_file, encoding, errors, newline) + else: + return binary_file + + def compress(data, format=FORMAT_XZ, check=-1, preset=None, filters=None): """Compress a block of data. diff --git a/Lib/test/test_lzma.py b/Lib/test/test_lzma.py --- a/Lib/test/test_lzma.py +++ b/Lib/test/test_lzma.py @@ -935,6 +935,106 @@ self.assertRaises(ValueError, f.tell) +class OpenTestCase(unittest.TestCase): + + def test_binary_modes(self): + with lzma.open(BytesIO(COMPRESSED_XZ), "rb") as f: + self.assertEqual(f.read(), INPUT) + with BytesIO() as bio: + with lzma.open(bio, "wb") as f: + f.write(INPUT) + file_data = lzma.decompress(bio.getvalue()) + self.assertEqual(file_data, INPUT) + with lzma.open(bio, "ab") as f: + f.write(INPUT) + file_data = lzma.decompress(bio.getvalue()) + self.assertEqual(file_data, INPUT * 2) + + def test_text_modes(self): + uncompressed = INPUT.decode("ascii") + uncompressed_raw = uncompressed.replace("\n", os.linesep) + with lzma.open(BytesIO(COMPRESSED_XZ), "rt") as f: + self.assertEqual(f.read(), uncompressed) + with BytesIO() as bio: + with lzma.open(bio, "wt") as f: + f.write(uncompressed) + file_data = lzma.decompress(bio.getvalue()).decode("ascii") + self.assertEqual(file_data, uncompressed_raw) + with lzma.open(bio, "at") as f: + f.write(uncompressed) + file_data = lzma.decompress(bio.getvalue()).decode("ascii") + self.assertEqual(file_data, uncompressed_raw * 2) + + def test_filename(self): + with TempFile(TESTFN): + with lzma.open(TESTFN, "wb") as f: + f.write(INPUT) + with open(TESTFN, "rb") as f: + file_data = lzma.decompress(f.read()) + self.assertEqual(file_data, INPUT) + with lzma.open(TESTFN, "rb") as f: + self.assertEqual(f.read(), INPUT) + with lzma.open(TESTFN, "ab") as f: + f.write(INPUT) + with lzma.open(TESTFN, "rb") as f: + self.assertEqual(f.read(), INPUT * 2) + + def test_bad_params(self): + # Test invalid parameter combinations. + with self.assertRaises(ValueError): + lzma.open(TESTFN, "") + with self.assertRaises(ValueError): + lzma.open(TESTFN, "x") + with self.assertRaises(ValueError): + lzma.open(TESTFN, "rbt") + with self.assertRaises(ValueError): + lzma.open(TESTFN, "rb", encoding="utf-8") + with self.assertRaises(ValueError): + lzma.open(TESTFN, "rb", errors="ignore") + with self.assertRaises(ValueError): + lzma.open(TESTFN, "rb", newline="\n") + + def test_format_and_filters(self): + # Test non-default format and filter chain. + options = {"format": lzma.FORMAT_RAW, "filters": FILTERS_RAW_1} + with lzma.open(BytesIO(COMPRESSED_RAW_1), "rb", **options) as f: + self.assertEqual(f.read(), INPUT) + with BytesIO() as bio: + with lzma.open(bio, "wb", **options) as f: + f.write(INPUT) + file_data = lzma.decompress(bio.getvalue(), **options) + self.assertEqual(file_data, INPUT) + + def test_encoding(self): + # Test non-default encoding. + uncompressed = INPUT.decode("ascii") + uncompressed_raw = uncompressed.replace("\n", os.linesep) + with BytesIO() as bio: + with lzma.open(bio, "wt", encoding="utf-16-le") as f: + f.write(uncompressed) + file_data = lzma.decompress(bio.getvalue()).decode("utf-16-le") + self.assertEqual(file_data, uncompressed_raw) + bio.seek(0) + with lzma.open(bio, "rt", encoding="utf-16-le") as f: + self.assertEqual(f.read(), uncompressed) + + def test_encoding_error_handler(self): + # Test wih non-default encoding error handler. + with BytesIO(lzma.compress(b"foo\xffbar")) as bio: + with lzma.open(bio, "rt", encoding="ascii", errors="ignore") as f: + self.assertEqual(f.read(), "foobar") + + def test_newline(self): + # Test with explicit newline (universal newline mode disabled). + text = INPUT.decode("ascii") + with BytesIO() as bio: + with lzma.open(bio, "wt", newline="\n") as f: + f.write(text) + bio.seek(0) + with lzma.open(bio, "rt", newline="\r") as f: + self.assertEqual(f.readlines(), [text]) + + class MiscellaneousTestCase(unittest.TestCase): def test_is_check_supported(self): @@ -1385,6 +1485,7 @@ CompressorDecompressorTestCase, CompressDecompressFunctionTestCase, FileTestCase, + OpenTestCase, MiscellaneousTestCase, ) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -17,8 +17,8 @@ - LZMAFile now accepts the modes "rb"/"wb"/"ab" as synonyms of "r"/"w"/"a". -- The bz2 module now contains an open() function, allowing compressed files to - conveniently be opened in text mode as well as binary mode. +- The bz2 and lzma modules now each contain an open() function, allowing + compressed files to readily be opened in text mode as well as binary mode. - BZ2File.__init__() and LZMAFile.__init__() now accept a file object as their first argument, rather than requiring a separate "fileobj" argument. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 5 01:32:08 2012 From: python-checkins at python.org (victor.stinner) Date: Tue, 05 Jun 2012 01:32:08 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Issue_=2314711=3A_os=2Estat?= =?utf8?q?=5Ffloat=5Ftimes=28=29_has_been_deprecated=2E?= Message-ID: http://hg.python.org/cpython/rev/7cb15b47c70e changeset: 77359:7cb15b47c70e user: Victor Stinner date: Tue Jun 05 01:22:15 2012 +0200 summary: Issue #14711: os.stat_float_times() has been deprecated. files: Doc/library/os.rst | 2 ++ Lib/test/test_os.py | 8 ++++++-- Misc/NEWS | 2 ++ Modules/posixmodule.c | 8 ++++++-- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/Doc/library/os.rst b/Doc/library/os.rst --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -2128,6 +2128,8 @@ are processed, this application should turn the feature off until the library has been corrected. + .. deprecated:: 3.3 + .. function:: statvfs(path) diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -30,7 +30,9 @@ threading = None from test.script_helper import assert_python_ok -os.stat_float_times(True) +with warnings.catch_warnings(): + warnings.simplefilter("ignore", DeprecationWarning) + os.stat_float_times(True) st = os.stat(__file__) stat_supports_subsecond = ( # check if float and int timestamps are different @@ -388,7 +390,9 @@ filename = self.fname os.utime(filename, (0, 0)) set_time_func(filename, atime, mtime) - os.stat_float_times(True) + with warnings.catch_warnings(): + warnings.simplefilter("ignore", DeprecationWarning) + os.stat_float_times(True) st = os.stat(filename) self.assertAlmostEqual(st.st_atime, atime, places=3) self.assertAlmostEqual(st.st_mtime, mtime, places=3) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -15,6 +15,8 @@ Library ------- +- Issue #14711: os.stat_float_times() has been deprecated. + - LZMAFile now accepts the modes "rb"/"wb"/"ab" as synonyms of "r"/"w"/"a". - The bz2 and lzma modules now each contain an open() function, allowing diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -1721,6 +1721,10 @@ int newval = -1; if (!PyArg_ParseTuple(args, "|i:stat_float_times", &newval)) return NULL; + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "stat_float_times() is deprecated", + 1)) + return NULL; if (newval == -1) /* Return old value */ return PyBool_FromLong(_stat_float_times); @@ -3605,7 +3609,7 @@ PyObject *args; PyObject *kwargs; - /* input/output */ + /* input/output */ PyObject **path; /* output only */ @@ -3655,7 +3659,7 @@ timet[1] = ua.mtime_s -/* +/* * utime_read_time_arguments() processes arguments for the utime * family of functions. */ -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Tue Jun 5 05:53:07 2012 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Tue, 05 Jun 2012 05:53:07 +0200 Subject: [Python-checkins] Daily reference leaks (7cb15b47c70e): sum=2 Message-ID: results for 7cb15b47c70e on branch "default" -------------------------------------------- test_dbm leaked [2, 0, 0] references, sum=2 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogRYoO0h', '-x'] From python-checkins at python.org Tue Jun 5 11:25:31 2012 From: python-checkins at python.org (vinay.sajip) Date: Tue, 05 Jun 2012 11:25:31 +0200 Subject: [Python-checkins] =?utf8?q?peps=3A_Move_PEP_405_to_Accepted_state?= =?utf8?q?=2E?= Message-ID: http://hg.python.org/peps/rev/9fae41fdc069 changeset: 4446:9fae41fdc069 parent: 4433:3b7e6e0316be user: Carl Meyer date: Fri May 25 09:13:13 2012 -0600 summary: Move PEP 405 to Accepted state. files: pep-0405.txt | 18 +++++++++--------- 1 files changed, 9 insertions(+), 9 deletions(-) diff --git a/pep-0405.txt b/pep-0405.txt --- a/pep-0405.txt +++ b/pep-0405.txt @@ -4,7 +4,7 @@ Last-Modified: $Date$ Author: Carl Meyer BDFL-Delegate: Nick Coghlan -Status: Draft +Status: Accepted Type: Standards Track Content-Type: text/x-rst Created: 13-Jun-2011 @@ -284,15 +284,15 @@ Current virtualenv handles include files in this way: -On POSIX systems where the installed Python's include files are found -in ``${base_prefix}/include/pythonX.X``, virtualenv creates -``${venv}/include/`` and symlink ``${base_prefix}/include/pythonX.X`` +On POSIX systems where the installed Python's include files are found in +``${base_prefix}/include/pythonX.X``, virtualenv creates +``${venv}/include/`` and symlinks ``${base_prefix}/include/pythonX.X`` to ``${venv}/include/pythonX.X``. On Windows, where Python's include files are found in ``{{ sys.prefix }}/Include`` and symlinks are not reliably available, virtualenv copies ``{{ sys.prefix }}/Include`` to ``${venv}/Include``. This ensures that extension modules built and -installed within the virtualenv will always find the Python header -files they need in the expected location relative to ``sys.prefix``. +installed within the virtualenv will always find the Python header files +they need in the expected location relative to ``sys.prefix``. This solution is not ideal when an extension module installs its own header files, as the default installation location for those header @@ -466,10 +466,10 @@ site-packages directories. The most notable case is probably `setuptools`_ and its fork -`distribute`_, which mostly use ``distutils``and ``sysconfig`` APIs, +`distribute`_, which mostly use ``distutils`` and ``sysconfig`` APIs, but do use ``sys.prefix`` directly to build up a list of site -directories for pre-flight checking where ``pth`` files can usefully -be placed. +directories for pre-flight checking where ``pth`` files can usefully be +placed. Otherwise, a `Google Code Search`_ turns up what appears to be a roughly even mix of usage between packages using ``sys.prefix`` to -- Repository URL: http://hg.python.org/peps From python-checkins at python.org Tue Jun 5 11:25:34 2012 From: python-checkins at python.org (vinay.sajip) Date: Tue, 05 Jun 2012 11:25:34 +0200 Subject: [Python-checkins] =?utf8?q?peps_=28merge_default_-=3E_default=29?= =?utf8?q?=3A_Merge_from_upstream=2E?= Message-ID: http://hg.python.org/peps/rev/6cc8a5492429 changeset: 4447:6cc8a5492429 parent: 4446:9fae41fdc069 parent: 4436:5a26cc296e83 user: Carl Meyer date: Fri May 25 09:13:31 2012 -0600 summary: Merge from upstream. files: pep-0405.txt | 3 ++- pep-0420.txt | 26 ++++++++++++++++---------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/pep-0405.txt b/pep-0405.txt --- a/pep-0405.txt +++ b/pep-0405.txt @@ -9,7 +9,8 @@ Content-Type: text/x-rst Created: 13-Jun-2011 Python-Version: 3.3 -Post-History: 24-Oct-2011, 28-Oct-2011, 06-Mar-2012 +Post-History: 24-Oct-2011, 28-Oct-2011, 06-Mar-2012, 24-May-2012 +Resolution: http://mail.python.org/pipermail/python-dev/2012-May/119668.html Abstract diff --git a/pep-0420.txt b/pep-0420.txt --- a/pep-0420.txt +++ b/pep-0420.txt @@ -4,12 +4,12 @@ Last-Modified: $Date$ Author: Eric V. Smith Status: Accepted -Resolution: http://mail.python.org/pipermail/python-dev/2012-May/119651.html Type: Standards Track Content-Type: text/x-rst Created: 19-Apr-2012 Python-Version: 3.3 Post-History: +Resolution: http://mail.python.org/pipermail/python-dev/2012-May/119651.html Abstract ======== @@ -372,9 +372,11 @@ child three.py -We add the first two parent paths to ``sys.path``. The third -``parent`` portion is added dynamically to ``parent.__path__``, and -the third portion is then found when it is imported:: +We add ``project1`` and ``project2`` to ``sys.path``, then import +``parent.child.one`` and ``parent.child.two``. Then we add the +``project3`` to ``sys.path`` and when ``parent.child.three`` is +imported, ``project3/parent`` is automatically added to +``parent.__path__``:: # add the first two parent paths to sys.path >>> import sys @@ -400,17 +402,21 @@ File "", line 1250, in _find_and_load_unlocked ImportError: No module named 'parent.child.three' - # now add the third parent portion to parent.__path__: - >>> parent.__path__.append('Lib/test/namespace_pkgs/project3/parent') - >>> parent.__path__ - _NamespacePath(['Lib/test/namespace_pkgs/project1/parent', 'Lib/test/namespace_pkgs/project2/parent', 'Lib/test/namespace_pkgs/project3/parent']) + # now add project3 to sys.path: + >>> sys.path.append('Lib/test/namespace_pkgs/project3') # and now parent.child.three can be imported: >>> import parent.child.three - # and project3/parent/child has dynamically been added to parent.child.__path__ + # project3/parent has been added to parent.__path__: + >>> parent.__path__ + _NamespacePath(['Lib/test/namespace_pkgs/project1/parent', 'Lib/test/namespace_pkgs/project2/parent', 'Lib/test/namespace_pkgs/project3/parent']) + + # and project3/parent/child has been added to parent.child.__path__ >>> parent.child.__path__ _NamespacePath(['Lib/test/namespace_pkgs/project1/parent/child', 'Lib/test/namespace_pkgs/project2/parent/child', 'Lib/test/namespace_pkgs/project3/parent/child']) + >>> + Discussion @@ -446,7 +452,7 @@ 4. Implicit package directories will permanently entrench current newbie-hostile behavior in ``__main__``. -Nick later gave a detailed response to his own objections[5]_, which +Nick later gave a detailed response to his own objections [5]_, which is summarized here: 1. The practicality of this PEP wins over other proposals and the -- Repository URL: http://hg.python.org/peps From python-checkins at python.org Tue Jun 5 11:25:41 2012 From: python-checkins at python.org (vinay.sajip) Date: Tue, 05 Jun 2012 11:25:41 +0200 Subject: [Python-checkins] =?utf8?q?peps_=28merge_default_-=3E_default=29?= =?utf8?q?=3A_Incorporated_PEP_405_tidy-ups=2C_and_marked_as_Final=2E?= Message-ID: http://hg.python.org/peps/rev/3a983cb78f6d changeset: 4448:3a983cb78f6d parent: 4445:c60be355412e parent: 4447:6cc8a5492429 user: Vinay Sajip date: Tue Jun 05 10:25:18 2012 +0100 summary: Incorporated PEP 405 tidy-ups, and marked as Final. files: pep-0405.txt | 18 +++++++++--------- 1 files changed, 9 insertions(+), 9 deletions(-) diff --git a/pep-0405.txt b/pep-0405.txt --- a/pep-0405.txt +++ b/pep-0405.txt @@ -4,7 +4,7 @@ Last-Modified: $Date$ Author: Carl Meyer BDFL-Delegate: Nick Coghlan -Status: Accepted +Status: Final Type: Standards Track Content-Type: text/x-rst Created: 13-Jun-2011 @@ -285,15 +285,15 @@ Current virtualenv handles include files in this way: -On POSIX systems where the installed Python's include files are found -in ``${base_prefix}/include/pythonX.X``, virtualenv creates -``${venv}/include/`` and symlink ``${base_prefix}/include/pythonX.X`` +On POSIX systems where the installed Python's include files are found in +``${base_prefix}/include/pythonX.X``, virtualenv creates +``${venv}/include/`` and symlinks ``${base_prefix}/include/pythonX.X`` to ``${venv}/include/pythonX.X``. On Windows, where Python's include files are found in ``{{ sys.prefix }}/Include`` and symlinks are not reliably available, virtualenv copies ``{{ sys.prefix }}/Include`` to ``${venv}/Include``. This ensures that extension modules built and -installed within the virtualenv will always find the Python header -files they need in the expected location relative to ``sys.prefix``. +installed within the virtualenv will always find the Python header files +they need in the expected location relative to ``sys.prefix``. This solution is not ideal when an extension module installs its own header files, as the default installation location for those header @@ -467,10 +467,10 @@ site-packages directories. The most notable case is probably `setuptools`_ and its fork -`distribute`_, which mostly use ``distutils``and ``sysconfig`` APIs, +`distribute`_, which mostly use ``distutils`` and ``sysconfig`` APIs, but do use ``sys.prefix`` directly to build up a list of site -directories for pre-flight checking where ``pth`` files can usefully -be placed. +directories for pre-flight checking where ``pth`` files can usefully be +placed. Otherwise, a `Google Code Search`_ turns up what appears to be a roughly even mix of usage between packages using ``sys.prefix`` to -- Repository URL: http://hg.python.org/peps From python-checkins at python.org Tue Jun 5 11:57:20 2012 From: python-checkins at python.org (hynek.schlawack) Date: Tue, 05 Jun 2012 11:57:20 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_=2314814=3A_ipaddress=3A_re?= =?utf8?q?factor_dup_code=2C_minor_janitoring=2C_bump_coverage?= Message-ID: http://hg.python.org/cpython/rev/cca2a1cc9598 changeset: 77360:cca2a1cc9598 user: Hynek Schlawack date: Tue Jun 05 11:55:58 2012 +0200 summary: #14814: ipaddress: refactor dup code, minor janitoring, bump coverage - remove duplicate netmask/hostmask code - make two ifs more pythonic - remove packed property for networks - some minor pep8 stuff - Test coverage is now at 97%, the rest are mostly unreachable safeguards. files: Lib/ipaddress.py | 171 ++++++++--------------- Lib/test/test_ipaddress.py | 176 +++++++++++++++++++++--- 2 files changed, 207 insertions(+), 140 deletions(-) diff --git a/Lib/ipaddress.py b/Lib/ipaddress.py --- a/Lib/ipaddress.py +++ b/Lib/ipaddress.py @@ -10,8 +10,10 @@ __version__ = '1.0' + import struct + IPV4LENGTH = 32 IPV6LENGTH = 128 @@ -424,7 +426,7 @@ An integer. """ - if not prefixlen and prefixlen != 0: + if prefixlen is None: prefixlen = self._prefixlen return self._ALL_ONES ^ (self._ALL_ONES >> prefixlen) @@ -989,6 +991,9 @@ _ALL_ONES = (2**IPV4LENGTH) - 1 _DECIMAL_DIGITS = frozenset('0123456789') + # the valid octets for host and netmasks. only useful for IPv4. + _valid_mask_octets = set((255, 254, 252, 248, 240, 224, 192, 128, 0)) + def __init__(self, address): self._version = 4 self._max_prefixlen = IPV4LENGTH @@ -1060,6 +1065,53 @@ ip_int >>= 8 return '.'.join(octets) + def _is_valid_netmask(self, netmask): + """Verify that the netmask is valid. + + Args: + netmask: A string, either a prefix or dotted decimal + netmask. + + Returns: + A boolean, True if the prefix represents a valid IPv4 + netmask. + + """ + mask = netmask.split('.') + if len(mask) == 4: + if [x for x in mask if int(x) not in self._valid_mask_octets]: + return False + if [y for idx, y in enumerate(mask) if idx > 0 and + y > mask[idx - 1]]: + return False + return True + try: + netmask = int(netmask) + except ValueError: + return False + return 0 <= netmask <= self._max_prefixlen + + def _is_hostmask(self, ip_str): + """Test if the IP string is a hostmask (rather than a netmask). + + Args: + ip_str: A string, the potential hostmask. + + Returns: + A boolean, True if the IP string is a hostmask. + + """ + bits = ip_str.split('.') + try: + parts = [int(x) for x in bits if int(x) in self._valid_mask_octets] + except ValueError: + return False + if len(parts) != len(bits): + return False + if parts[0] < parts[-1]: + return True + return False + @property def max_prefixlen(self): return self._max_prefixlen @@ -1213,9 +1265,6 @@ class IPv4Interface(IPv4Address): - # the valid octets for host and netmasks. only useful for IPv4. - _valid_mask_octets = set((255, 254, 252, 248, 240, 224, 192, 128, 0)) - def __init__(self, address): if isinstance(address, (bytes, int)): IPv4Address.__init__(self, address) @@ -1248,53 +1297,6 @@ def __hash__(self): return self._ip ^ self._prefixlen ^ int(self.network.network_address) - def _is_valid_netmask(self, netmask): - """Verify that the netmask is valid. - - Args: - netmask: A string, either a prefix or dotted decimal - netmask. - - Returns: - A boolean, True if the prefix represents a valid IPv4 - netmask. - - """ - mask = netmask.split('.') - if len(mask) == 4: - if [x for x in mask if int(x) not in self._valid_mask_octets]: - return False - if [y for idx, y in enumerate(mask) if idx > 0 and - y > mask[idx - 1]]: - return False - return True - try: - netmask = int(netmask) - except ValueError: - return False - return 0 <= netmask <= self._max_prefixlen - - def _is_hostmask(self, ip_str): - """Test if the IP string is a hostmask (rather than a netmask). - - Args: - ip_str: A string, the potential hostmask. - - Returns: - A boolean, True if the IP string is a hostmask. - - """ - bits = ip_str.split('.') - try: - parts = [int(x) for x in bits if int(x) in self._valid_mask_octets] - except ValueError: - return False - if len(parts) != len(bits): - return False - if parts[0] < parts[-1]: - return True - return False - @property def prefixlen(self): return self._prefixlen @@ -1334,9 +1336,6 @@ # TODO (ncoghlan): Investigate using IPv4Interface instead _address_class = IPv4Address - # the valid octets for host and netmasks. only useful for IPv4. - _valid_mask_octets = set((255, 254, 252, 248, 240, 224, 192, 128, 0)) - def __init__(self, address, strict=True): """Instantiate a new IPv4 network object. @@ -1443,58 +1442,6 @@ if self._prefixlen == (self._max_prefixlen - 1): self.hosts = self.__iter__ - @property - def packed(self): - """The binary representation of this address.""" - return v4_int_to_packed(self.network_address) - - def _is_valid_netmask(self, netmask): - """Verify that the netmask is valid. - - Args: - netmask: A string, either a prefix or dotted decimal - netmask. - - Returns: - A boolean, True if the prefix represents a valid IPv4 - netmask. - - """ - mask = netmask.split('.') - if len(mask) == 4: - if [x for x in mask if int(x) not in self._valid_mask_octets]: - return False - if [y for idx, y in enumerate(mask) if idx > 0 and - y > mask[idx - 1]]: - return False - return True - try: - netmask = int(netmask) - except ValueError: - return False - return 0 <= netmask <= self._max_prefixlen - - def _is_hostmask(self, ip_str): - """Test if the IP string is a hostmask (rather than a netmask). - - Args: - ip_str: A string, the potential hostmask. - - Returns: - A boolean, True if the IP string is a hostmask. - - """ - bits = ip_str.split('.') - try: - parts = [int(x) for x in bits if int(x) in self._valid_mask_octets] - except ValueError: - return False - if len(parts) != len(bits): - return False - if parts[0] < parts[-1]: - return True - return False - class _BaseV6: @@ -1675,7 +1622,7 @@ ValueError: The address is bigger than 128 bits of all ones. """ - if not ip_int and ip_int != 0: + if ip_int is None: ip_int = int(self._ip) if ip_int > self._ALL_ONES: @@ -1721,11 +1668,6 @@ return self._max_prefixlen @property - def packed(self): - """The binary representation of this address.""" - return v6_int_to_packed(self._ip) - - @property def version(self): return self._version @@ -1931,6 +1873,11 @@ self._ip = self._ip_int_from_string(addr_str) + @property + def packed(self): + """The binary representation of this address.""" + return v6_int_to_packed(self._ip) + class IPv6Interface(IPv6Address): diff --git a/Lib/test/test_ipaddress.py b/Lib/test/test_ipaddress.py --- a/Lib/test/test_ipaddress.py +++ b/Lib/test/test_ipaddress.py @@ -5,12 +5,13 @@ import unittest -import time import ipaddress + # Compatibility function to cast str to bytes objects _cb = lambda bytestr: bytes(bytestr, 'charmap') + class IpaddrUnitTest(unittest.TestCase): def setUp(self): @@ -133,6 +134,31 @@ '1.a.2.3') self.assertEqual(False, ipaddress.IPv4Interface(1)._is_hostmask( '1.a.2.3')) + self.assertRaises(ValueError, ipaddress.ip_interface, 'bogus') + self.assertRaises(ValueError, ipaddress.IPv4Address, '127.0.0.1/32') + self.assertRaises(ValueError, ipaddress.v4_int_to_packed, -1) + self.assertRaises(ValueError, ipaddress.v4_int_to_packed, + 2 ** ipaddress.IPV4LENGTH) + self.assertRaises(ValueError, ipaddress.v6_int_to_packed, -1) + self.assertRaises(ValueError, ipaddress.v6_int_to_packed, + 2 ** ipaddress.IPV6LENGTH) + + def testInternals(self): + first, last = ipaddress._find_address_range([ + ipaddress.IPv4Address('10.10.10.10'), + ipaddress.IPv4Address('10.10.10.12')]) + self.assertEqual(first, last) + self.assertEqual(0, ipaddress._get_prefix_length(2**32, 0, 32)) + self.assertEqual(128, ipaddress._count_righthand_zero_bits(0, 128)) + base_ip = ipaddress._BaseAddress('127.0.0.1') + try: + base_ip.version + self.fail('_BaseAddress.version didn\'t raise NotImplementedError') + except NotImplementedError: + pass + self.assertEqual("IPv4Network('1.2.3.0/24')", repr(self.ipv4_network)) + self.assertEqual('0x1020318', hex(self.ipv4_network)) + self.assertRaises(TypeError, self.ipv4_network.__eq__, object()) def testGetNetwork(self): self.assertEqual(int(self.ipv4_network.network_address), 16909056) @@ -188,6 +214,7 @@ self.assertEqual([v6addr, v6net], sorted([v6net, v6addr], key=ipaddress.get_mixed_type_key)) + self.assertEqual(NotImplemented, ipaddress.get_mixed_type_key(object)) def testIpFromInt(self): self.assertEqual(self.ipv4_interface._ip, @@ -209,9 +236,15 @@ ipaddress.IPv6Interface, 2**128) self.assertRaises(ipaddress.AddressValueError, ipaddress.IPv6Interface, -1) + self.assertRaises(ipaddress.AddressValueError, + ipaddress.IPv6Network, 2**128) + self.assertRaises(ipaddress.AddressValueError, + ipaddress.IPv6Network, -1) - self.assertEqual(ipaddress.ip_network(self.ipv4_address._ip).version, 4) - self.assertEqual(ipaddress.ip_network(self.ipv6_address._ip).version, 6) + self.assertEqual(ipaddress.ip_network(self.ipv4_address._ip).version, + 4) + self.assertEqual(ipaddress.ip_network(self.ipv6_address._ip).version, + 6) def testIpFromPacked(self): ip = ipaddress.ip_network @@ -255,12 +288,31 @@ self.assertEqual(int(ipv4_zero_netmask.network.netmask), 0) self.assertTrue(ipv4_zero_netmask.network._is_valid_netmask( str(0))) + self.assertTrue(ipv4_zero_netmask._is_valid_netmask('0')) + self.assertTrue(ipv4_zero_netmask._is_valid_netmask('0.0.0.0')) + self.assertFalse(ipv4_zero_netmask._is_valid_netmask('invalid')) ipv6_zero_netmask = ipaddress.IPv6Interface('::1/0') self.assertEqual(int(ipv6_zero_netmask.network.netmask), 0) self.assertTrue(ipv6_zero_netmask.network._is_valid_netmask( str(0))) + def testIPv4NetAndHostmasks(self): + net = self.ipv4_network + self.assertFalse(net._is_valid_netmask('invalid')) + self.assertTrue(net._is_valid_netmask('128.128.128.128')) + self.assertFalse(net._is_valid_netmask('128.128.128.127')) + self.assertFalse(net._is_valid_netmask('128.128.128.255')) + self.assertTrue(net._is_valid_netmask('255.128.128.128')) + + self.assertFalse(net._is_hostmask('invalid')) + self.assertTrue(net._is_hostmask('128.255.255.255')) + self.assertFalse(net._is_hostmask('255.255.255.255')) + self.assertFalse(net._is_hostmask('1.2.3.4')) + + net = ipaddress.IPv4Network('127.0.0.0/0.0.0.255') + self.assertEqual(24, net.prefixlen) + def testGetBroadcast(self): self.assertEqual(int(self.ipv4_network.broadcast_address), 16909311) self.assertEqual(str(self.ipv4_network.broadcast_address), '1.2.3.255') @@ -300,17 +352,25 @@ def testGetSupernet4(self): self.assertRaises(ValueError, self.ipv4_network.supernet, prefixlen_diff=2, new_prefix=1) - self.assertRaises(ValueError, self.ipv4_network.supernet, new_prefix=25) + self.assertRaises(ValueError, self.ipv4_network.supernet, + new_prefix=25) self.assertEqual(self.ipv4_network.supernet(prefixlen_diff=2), self.ipv4_network.supernet(new_prefix=22)) self.assertRaises(ValueError, self.ipv6_network.supernet, prefixlen_diff=2, new_prefix=1) - self.assertRaises(ValueError, self.ipv6_network.supernet, new_prefix=65) + self.assertRaises(ValueError, self.ipv6_network.supernet, + new_prefix=65) self.assertEqual(self.ipv6_network.supernet(prefixlen_diff=2), self.ipv6_network.supernet(new_prefix=62)) def testHosts(self): + hosts = list(self.ipv4_network.hosts()) + self.assertEqual(254, len(hosts)) + self.assertEqual(ipaddress.IPv4Address('1.2.3.1'), hosts[0]) + self.assertEqual(ipaddress.IPv4Address('1.2.3.254'), hosts[-1]) + + # special case where only 1 bit is left for address self.assertEqual([ipaddress.IPv4Address('2.0.0.0'), ipaddress.IPv4Address('2.0.0.1')], list(ipaddress.ip_network('2.0.0.0/31').hosts())) @@ -398,7 +458,8 @@ def testGetNum_Addresses(self): self.assertEqual(self.ipv4_network.num_addresses, 256) - self.assertEqual(list(self.ipv4_network.subnets())[0].num_addresses, 128) + self.assertEqual(list(self.ipv4_network.subnets())[0].num_addresses, + 128) self.assertEqual(self.ipv4_network.supernet().num_addresses, 512) self.assertEqual(self.ipv6_network.num_addresses, 18446744073709551616) @@ -431,6 +492,8 @@ self.assertRaises(ipaddress.AddressValueError, ipaddress.IPv4Interface, '1.2.3.4/32/24') self.assertRaises(ipaddress.AddressValueError, + ipaddress.IPv4Network, '1.2.3.4/32/24') + self.assertRaises(ipaddress.AddressValueError, ipaddress.IPv4Interface, '10/8') self.assertRaises(ipaddress.AddressValueError, ipaddress.IPv6Interface, '10/8') @@ -545,8 +608,9 @@ # check that addreses are subsumed properly. collapsed = ipaddress.collapse_addresses( [ip1, ip2, ip3, ip4, ip5, ip6]) - self.assertEqual(list(collapsed), [ipaddress.IPv4Network('1.1.1.0/30'), - ipaddress.IPv4Network('1.1.1.4/32')]) + self.assertEqual(list(collapsed), + [ipaddress.IPv4Network('1.1.1.0/30'), + ipaddress.IPv4Network('1.1.1.4/32')]) # test a mix of IP addresses and networks including some duplicates ip1 = ipaddress.IPv4Address('1.1.1.0') @@ -557,7 +621,8 @@ #ip6 = ipaddress.IPv4Interface('1.1.1.4/30') # check that addreses are subsumed properly. collapsed = ipaddress.collapse_addresses([ip1, ip2, ip3, ip4]) - self.assertEqual(list(collapsed), [ipaddress.IPv4Network('1.1.1.0/30')]) + self.assertEqual(list(collapsed), + [ipaddress.IPv4Network('1.1.1.0/30')]) # test only IP networks ip1 = ipaddress.IPv4Network('1.1.0.0/24') @@ -565,17 +630,20 @@ ip3 = ipaddress.IPv4Network('1.1.2.0/24') ip4 = ipaddress.IPv4Network('1.1.3.0/24') ip5 = ipaddress.IPv4Network('1.1.4.0/24') - # stored in no particular order b/c we want CollapseAddr to call [].sort + # stored in no particular order b/c we want CollapseAddr to call + # [].sort ip6 = ipaddress.IPv4Network('1.1.0.0/22') # check that addreses are subsumed properly. collapsed = ipaddress.collapse_addresses([ip1, ip2, ip3, ip4, ip5, ip6]) - self.assertEqual(list(collapsed), [ipaddress.IPv4Network('1.1.0.0/22'), - ipaddress.IPv4Network('1.1.4.0/24')]) + self.assertEqual(list(collapsed), + [ipaddress.IPv4Network('1.1.0.0/22'), + ipaddress.IPv4Network('1.1.4.0/24')]) # test that two addresses are supernet'ed properly collapsed = ipaddress.collapse_addresses([ip1, ip2]) - self.assertEqual(list(collapsed), [ipaddress.IPv4Network('1.1.0.0/23')]) + self.assertEqual(list(collapsed), + [ipaddress.IPv4Network('1.1.0.0/23')]) # test same IP networks ip_same1 = ip_same2 = ipaddress.IPv4Network('1.1.1.1/32') @@ -614,7 +682,20 @@ summarize = ipaddress.summarize_address_range ip1 = ipaddress.ip_address('1.1.1.0') ip2 = ipaddress.ip_address('1.1.1.255') - # test a /24 is sumamrized properly + + # summarize works only for IPv4 & IPv6 + class IPv7Address(ipaddress.IPv6Address): + @property + def version(self): + return 7 + ip_invalid1 = IPv7Address('::1') + ip_invalid2 = IPv7Address('::1') + self.assertRaises(ValueError, list, + summarize(ip_invalid1, ip_invalid2)) + # test that a summary over ip4 & ip6 fails + self.assertRaises(TypeError, list, + summarize(ip1, ipaddress.IPv6Address('::1'))) + # test a /24 is summarized properly self.assertEqual(list(summarize(ip1, ip2))[0], ipaddress.ip_network('1.1.1.0/24')) # test an IPv4 range that isn't on a network byte boundary @@ -622,6 +703,11 @@ self.assertEqual(list(summarize(ip1, ip2)), [ipaddress.ip_network('1.1.1.0/29'), ipaddress.ip_network('1.1.1.8')]) + # all! + ip1 = ipaddress.IPv4Address(0) + ip2 = ipaddress.IPv4Address(ipaddress.IPv4Address._ALL_ONES) + self.assertEqual([ipaddress.IPv4Network('0.0.0.0/0')], + list(summarize(ip1, ip2))) ip1 = ipaddress.ip_address('1::') ip2 = ipaddress.ip_address('1:ffff:ffff:ffff:ffff:ffff:ffff:ffff') @@ -663,15 +749,20 @@ def testNetworkComparison(self): # ip1 and ip2 have the same network address ip1 = ipaddress.IPv4Network('1.1.1.0/24') - ip2 = ipaddress.IPv4Network('1.1.1.1/32') + ip2 = ipaddress.IPv4Network('1.1.1.0/32') ip3 = ipaddress.IPv4Network('1.1.2.0/24') self.assertTrue(ip1 < ip3) self.assertTrue(ip3 > ip2) - #self.assertEqual(ip1.compare_networks(ip2), 0) - #self.assertTrue(ip1._get_networks_key() == ip2._get_networks_key()) + self.assertEqual(ip1.compare_networks(ip1), 0) + + # if addresses are the same, sort by netmask + self.assertEqual(ip1.compare_networks(ip2), -1) + self.assertEqual(ip2.compare_networks(ip1), 1) + self.assertEqual(ip1.compare_networks(ip3), -1) + self.assertEqual(ip3.compare_networks(ip1), 1) self.assertTrue(ip1._get_networks_key() < ip3._get_networks_key()) ip1 = ipaddress.IPv6Network('2001:2000::/96') @@ -685,6 +776,9 @@ # Test comparing different protocols. # Should always raise a TypeError. + self.assertRaises(TypeError, + self.ipv4_network.compare_networks, + self.ipv6_network) ipv6 = ipaddress.IPv6Interface('::/0') ipv4 = ipaddress.IPv4Interface('0.0.0.0/0') self.assertRaises(TypeError, ipv4.__lt__, ipv6) @@ -811,6 +905,7 @@ self.assertEqual(True, ipaddress.ip_interface( '224.1.1.1/31').is_multicast) self.assertEqual(False, ipaddress.ip_network('240.0.0.0').is_multicast) + self.assertEqual(True, ipaddress.ip_network('240.0.0.0').is_reserved) self.assertEqual(True, ipaddress.ip_interface( '192.168.1.1/17').is_private) @@ -818,9 +913,12 @@ self.assertEqual(True, ipaddress.ip_network( '10.255.255.255').is_private) self.assertEqual(False, ipaddress.ip_network('11.0.0.0').is_private) + self.assertEqual(False, ipaddress.ip_network('11.0.0.0').is_reserved) self.assertEqual(True, ipaddress.ip_network( '172.31.255.255').is_private) self.assertEqual(False, ipaddress.ip_network('172.32.0.0').is_private) + self.assertEqual(True, + ipaddress.ip_network('169.254.1.0/24').is_link_local) self.assertEqual(True, ipaddress.ip_interface( @@ -840,6 +938,9 @@ self.assertEqual(True, ipaddress.ip_address('0.0.0.0').is_unspecified) self.assertEqual(True, ipaddress.ip_address('224.1.1.1').is_multicast) self.assertEqual(False, ipaddress.ip_address('240.0.0.0').is_multicast) + self.assertEqual(True, ipaddress.ip_address('240.0.0.1').is_reserved) + self.assertEqual(False, + ipaddress.ip_address('239.255.255.255').is_reserved) self.assertEqual(True, ipaddress.ip_address('192.168.1.1').is_private) self.assertEqual(False, ipaddress.ip_address('192.169.0.0').is_private) @@ -851,9 +952,9 @@ self.assertEqual(False, ipaddress.ip_address('172.32.0.0').is_private) self.assertEqual(True, - ipaddress.ip_address('169.254.100.200').is_link_local) + ipaddress.ip_address('169.254.100.200').is_link_local) self.assertEqual(False, - ipaddress.ip_address('169.255.100.200').is_link_local) + ipaddress.ip_address('169.255.100.200').is_link_local) self.assertEqual(True, ipaddress.ip_address('127.100.200.254').is_loopback) @@ -864,7 +965,7 @@ def testReservedIpv6(self): self.assertEqual(True, ipaddress.ip_network('ffff::').is_multicast) - self.assertEqual(True, ipaddress.ip_network(2**128-1).is_multicast) + self.assertEqual(True, ipaddress.ip_network(2**128 - 1).is_multicast) self.assertEqual(True, ipaddress.ip_network('ff00::').is_multicast) self.assertEqual(False, ipaddress.ip_network('fdff::').is_multicast) @@ -899,7 +1000,7 @@ # test addresses self.assertEqual(True, ipaddress.ip_address('ffff::').is_multicast) - self.assertEqual(True, ipaddress.ip_address(2**128-1).is_multicast) + self.assertEqual(True, ipaddress.ip_address(2**128 - 1).is_multicast) self.assertEqual(True, ipaddress.ip_address('ff00::').is_multicast) self.assertEqual(False, ipaddress.ip_address('fdff::').is_multicast) @@ -935,8 +1036,9 @@ self.assertEqual(True, ipaddress.ip_network('4000::1/128').is_reserved) def testIpv4Mapped(self): - self.assertEqual(ipaddress.ip_address('::ffff:192.168.1.1').ipv4_mapped, - ipaddress.ip_address('192.168.1.1')) + self.assertEqual( + ipaddress.ip_address('::ffff:192.168.1.1').ipv4_mapped, + ipaddress.ip_address('192.168.1.1')) self.assertEqual(ipaddress.ip_address('::c0a8:101').ipv4_mapped, None) self.assertEqual(ipaddress.ip_address('::ffff:c0a8:101').ipv4_mapped, ipaddress.ip_address('192.168.1.1')) @@ -946,21 +1048,25 @@ addr2 = ipaddress.ip_network('10.1.1.0/26') addr3 = ipaddress.ip_network('10.2.1.0/24') addr4 = ipaddress.ip_address('10.1.1.0') + addr5 = ipaddress.ip_network('2001:db8::0/32') self.assertEqual(sorted(list(addr1.address_exclude(addr2))), [ipaddress.ip_network('10.1.1.64/26'), ipaddress.ip_network('10.1.1.128/25')]) self.assertRaises(ValueError, list, addr1.address_exclude(addr3)) self.assertRaises(TypeError, list, addr1.address_exclude(addr4)) + self.assertRaises(TypeError, list, addr1.address_exclude(addr5)) self.assertEqual(list(addr1.address_exclude(addr1)), []) def testHash(self): + self.assertEqual(hash(ipaddress.ip_interface('10.1.1.0/24')), + hash(ipaddress.ip_interface('10.1.1.0/24'))) self.assertEqual(hash(ipaddress.ip_network('10.1.1.0/24')), - hash(ipaddress.ip_network('10.1.1.0/24'))) + hash(ipaddress.ip_network('10.1.1.0/24'))) self.assertEqual(hash(ipaddress.ip_address('10.1.1.0')), - hash(ipaddress.ip_address('10.1.1.0'))) + hash(ipaddress.ip_address('10.1.1.0'))) # i70 self.assertEqual(hash(ipaddress.ip_address('1.2.3.4')), - hash(ipaddress.ip_address( + hash(ipaddress.ip_address( int(ipaddress.ip_address('1.2.3.4')._ip)))) ip1 = ipaddress.ip_address('10.1.1.0') ip2 = ipaddress.ip_address('1::') @@ -972,6 +1078,18 @@ self.assertTrue(self.ipv4_address in dummy) self.assertTrue(ip2 in dummy) + def testIPBases(self): + net = self.ipv4_network + self.assertEqual('1.2.3.0/24', net.compressed) + self.assertEqual( + net._ip_int_from_prefix(24), + net._ip_int_from_prefix(None)) + net = self.ipv6_network + self.assertRaises(ValueError, net._string_from_ip_int, 2**128 + 1) + self.assertEqual( + self.ipv6_address._string_from_ip_int(self.ipv6_address._ip), + self.ipv6_address._string_from_ip_int(None)) + def testIPv6NetworkHelpers(self): net = self.ipv6_network self.assertEqual('2001:658:22a:cafe::/64', net.with_prefixlen) @@ -1032,6 +1150,7 @@ addr1 = ipaddress.IPv6Interface('2001::1') addr2 = ipaddress.IPv6Address('2001:0:5ef5:79fd:0:59d:a0e5:ba1') addr3 = ipaddress.IPv6Network('2001::/96') + addr4 = ipaddress.IPv4Address('192.168.178.1') self.assertEqual('2001:0000:0000:0000:0000:0000:0000:0001/128', addr1.exploded) self.assertEqual('0000:0000:0000:0000:0000:0000:0000:0001/128', @@ -1041,6 +1160,7 @@ addr2.exploded) self.assertEqual('2001:0000:0000:0000:0000:0000:0000:0000/96', addr3.exploded) + self.assertEqual('192.168.178.1', addr4.exploded) def testIntRepresentation(self): self.assertEqual(16909060, int(self.ipv4_address)) @@ -1118,7 +1238,8 @@ # V6 - check we're cached self.assertTrue('broadcast_address' in self.ipv6_network._cache) self.assertTrue('hostmask' in self.ipv6_network._cache) - self.assertTrue('broadcast_address' in self.ipv6_interface.network._cache) + self.assertTrue( + 'broadcast_address' in self.ipv6_interface.network._cache) self.assertTrue('hostmask' in self.ipv6_interface.network._cache) def testTeredo(self): @@ -1139,7 +1260,6 @@ ipaddress.IPv4Address('95.26.244.94')), teredo_addr.teredo) - def testsixtofour(self): sixtofouraddr = ipaddress.ip_address('2002:ac1d:2d64::1') bad_addr = ipaddress.ip_address('2000:ac1d:2d64::1') -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 5 13:48:16 2012 From: python-checkins at python.org (victor.stinner) Date: Tue, 05 Jun 2012 13:48:16 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Close_=2311022=3A_TextIOWra?= =?utf8?q?pper_doesn=27t_call_locale=2Esetlocale=28=29_anymore?= Message-ID: http://hg.python.org/cpython/rev/2587328c7c9c changeset: 77361:2587328c7c9c user: Victor Stinner date: Tue Jun 05 13:43:22 2012 +0200 summary: Close #11022: TextIOWrapper doesn't call locale.setlocale() anymore open() and io.TextIOWrapper are now calling locale.getpreferredencoding(False) instead of locale.getpreferredencoding() in text mode if the encoding is not specified. Don't change temporary the locale encoding using locale.setlocale(), use the current locale encoding instead of the user preferred encoding. Explain also in open() documentation that locale.getpreferredencoding(False) is called if the encoding is not specified. files: Doc/library/functions.rst | 7 ++- Doc/library/io.rst | 8 ++++- Lib/_pyio.py | 4 +- Lib/test/test_builtin.py | 42 ++++++++++++++++++++------ Lib/test/test_io.py | 35 +++++++++++++++++----- Misc/NEWS | 6 +++ Modules/_io/_iomodule.c | 5 +- Modules/_io/textio.c | 4 +- 8 files changed, 83 insertions(+), 28 deletions(-) diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -800,9 +800,10 @@ already exists), ``'x'`` for exclusive creation and ``'a'`` for appending (which on *some* Unix systems, means that *all* writes append to the end of the file regardless of the current seek position). In text mode, if - *encoding* is not specified the encoding used is platform dependent. (For - reading and writing raw bytes use binary mode and leave *encoding* - unspecified.) The available modes are: + *encoding* is not specified the encoding used is platform dependent: + ``locale.getpreferredencoding(False)`` is called to get the current locale + encoding. (For reading and writing raw bytes use binary mode and leave + *encoding* unspecified.) The available modes are: ========= =============================================================== Character Meaning diff --git a/Doc/library/io.rst b/Doc/library/io.rst --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -752,7 +752,7 @@ It inherits :class:`TextIOBase`. *encoding* gives the name of the encoding that the stream will be decoded or - encoded with. It defaults to :func:`locale.getpreferredencoding`. + encoded with. It defaults to ``locale.getpreferredencoding(False)``. *errors* is an optional string that specifies how encoding and decoding errors are to be handled. Pass ``'strict'`` to raise a :exc:`ValueError` @@ -784,6 +784,12 @@ .. versionchanged:: 3.3 The *write_through* argument has been added. + .. versionchanged:: 3.3 + The default *encoding* is now ``locale.getpreferredencoding(False)`` + instead of ``locale.getpreferredencoding()``. Don't change temporary the + locale encoding using :func:`locale.setlocale`, use the current locale + encoding instead of the user preferred encoding. + :class:`TextIOWrapper` provides one attribute in addition to those of :class:`TextIOBase` and its parents: diff --git a/Lib/_pyio.py b/Lib/_pyio.py --- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -1448,7 +1448,7 @@ r"""Character and line based layer over a BufferedIOBase object, buffer. encoding gives the name of the encoding that the stream will be - decoded or encoded with. It defaults to locale.getpreferredencoding. + decoded or encoded with. It defaults to locale.getpreferredencoding(False). errors determines the strictness of encoding and decoding (see the codecs.register) and defaults to "strict". @@ -1487,7 +1487,7 @@ # Importing locale may fail if Python is being built encoding = "ascii" else: - encoding = locale.getpreferredencoding() + encoding = locale.getpreferredencoding(False) if not isinstance(encoding, str): raise ValueError("invalid encoding: %r" % encoding) diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -1,20 +1,21 @@ # Python test set -- built-in functions -import platform -import unittest -import sys -import warnings +import ast +import builtins import collections import io +import locale import os -import ast +import pickle +import platform +import random +import sys +import traceback import types -import builtins -import random -import traceback +import unittest +import warnings +from operator import neg from test.support import TESTFN, unlink, run_unittest, check_warnings -from operator import neg -import pickle try: import pty, signal except ImportError: @@ -961,6 +962,27 @@ fp.close() unlink(TESTFN) + def test_open_default_encoding(self): + old_environ = dict(os.environ) + try: + # try to get a user preferred encoding different than the current + # locale encoding to check that open() uses the current locale + # encoding and not the user preferred encoding + for key in ('LC_ALL', 'LANG', 'LC_CTYPE'): + if key in os.environ: + del os.environ[key] + + self.write_testfile() + current_locale_encoding = locale.getpreferredencoding(False) + fp = open(TESTFN, 'w') + try: + self.assertEqual(fp.encoding, current_locale_encoding) + finally: + fp.close() + finally: + os.environ.clear() + os.environ.update(old_environ) + def test_ord(self): self.assertEqual(ord(' '), 32) self.assertEqual(ord('A'), 65) diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -19,20 +19,21 @@ # test both implementations. This file has lots of examples. ################################################################################ +import abc +import array +import errno +import locale import os +import pickle +import random +import signal import sys import time -import array -import random import unittest +import warnings import weakref -import abc -import signal -import errno -import warnings -import pickle +from collections import deque from itertools import cycle, count -from collections import deque from test import support import codecs @@ -1881,6 +1882,24 @@ t.write("A\rB") self.assertEqual(r.getvalue(), b"XY\nZA\rB") + def test_default_encoding(self): + old_environ = dict(os.environ) + try: + # try to get a user preferred encoding different than the current + # locale encoding to check that TextIOWrapper() uses the current + # locale encoding and not the user preferred encoding + for key in ('LC_ALL', 'LANG', 'LC_CTYPE'): + if key in os.environ: + del os.environ[key] + + current_locale_encoding = locale.getpreferredencoding(False) + b = self.BytesIO() + t = self.TextIOWrapper(b) + self.assertEqual(t.encoding, current_locale_encoding) + finally: + os.environ.clear() + os.environ.update(old_environ) + def test_encoding(self): # Check the encoding attribute is always set, and valid b = self.BytesIO() diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,12 @@ Core and Builtins ----------------- +- Issue #11022: open() and io.TextIOWrapper are now calling + locale.getpreferredencoding(False) instead of locale.getpreferredencoding() + in text mode if the encoding is not specified. Don't change temporary the + locale encoding using locale.setlocale(), use the current locale encoding + instead of the user preferred encoding. + - Issue #14673: Add Eric Snow's sys.implementation implementation. Library diff --git a/Modules/_io/_iomodule.c b/Modules/_io/_iomodule.c --- a/Modules/_io/_iomodule.c +++ b/Modules/_io/_iomodule.c @@ -112,8 +112,9 @@ "'a' for appending (which on some Unix systems, means that all writes\n" "append to the end of the file regardless of the current seek position).\n" "In text mode, if encoding is not specified the encoding used is platform\n" -"dependent. (For reading and writing raw bytes use binary mode and leave\n" -"encoding unspecified.) The available modes are:\n" +"dependent: locale.getpreferredencoding(False) is called to get the\n" +"current locale encoding. (For reading and writing raw bytes use binary\n" +"mode and leave encoding unspecified.) The available modes are:\n" "\n" "========= ===============================================================\n" "Character Meaning\n" diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -630,7 +630,7 @@ "Character and line based layer over a BufferedIOBase object, buffer.\n" "\n" "encoding gives the name of the encoding that the stream will be\n" - "decoded or encoded with. It defaults to locale.getpreferredencoding.\n" + "decoded or encoded with. It defaults to locale.getpreferredencoding(False).\n" "\n" "errors determines the strictness of encoding and decoding (see the\n" "codecs.register) and defaults to \"strict\".\n" @@ -898,7 +898,7 @@ else { use_locale: self->encoding = _PyObject_CallMethodId( - state->locale_module, &PyId_getpreferredencoding, NULL); + state->locale_module, &PyId_getpreferredencoding, "O", Py_False); if (self->encoding == NULL) { catch_ImportError: /* -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 5 14:09:40 2012 From: python-checkins at python.org (nick.coghlan) Date: Tue, 05 Jun 2012 14:09:40 +0200 Subject: [Python-checkins] =?utf8?q?peps=3A_Add_PEP_422=3A_Dynamic_Class_D?= =?utf8?q?ecorators?= Message-ID: http://hg.python.org/peps/rev/0e3606500a26 changeset: 4449:0e3606500a26 user: Nick Coghlan date: Tue Jun 05 22:09:20 2012 +1000 summary: Add PEP 422: Dynamic Class Decorators files: pep-0422.txt | 168 +++++++++++++++++++++++++++++++++++++++ 1 files changed, 168 insertions(+), 0 deletions(-) diff --git a/pep-0422.txt b/pep-0422.txt new file mode 100644 --- /dev/null +++ b/pep-0422.txt @@ -0,0 +1,168 @@ +PEP: 422 +Title: Dynamic class decorators +Version: $Revision$ +Last-Modified: $Date$ +Author: Nick Coghlan +Status: Draft +Type: Standards Track +Content-Type: text/x-rst +Created: 5-Jun-2012 +Post-History: 5-Jun-2012 + + +Abstract +======== + +Classes currently support two mechanisms for modification of the class at +definition time: metaclasses and lexical decorators. + +Metaclasses can be awkward and challenging to use correctly in conjunction +with multiple inheritance and lexical decorators don't interact with class +inheritance at all. + +This PEP proposes a new mechanism for dynamic class decoration that +interacts more cleanly with class inheritance mechanisms. + + +Specification +============= + +This PEP proposes that a new step be added to the class creation process, +after the metaclass invocation to construct the class instance and before +the application of lexical decorators. + +This step will walk the class MRO in reverse order, looking for +``__decorators__`` entries in each class dictionary. These entries are +expected to be iterables that are also walked in reverse order to retrieve +class decorators that are automatically applied to the class being defined:: + + for entry in reversed(cls.mro()): + decorators = entry.__dict__.get("__decorators__", ()) + for deco in reversed(decorators): + cls = deco(cls) + +This step in the class creation process will be an implicit part of the +class statement and also part of the behaviour of ``types.new_class()``. + + +Rationale +========= + +When decorator support was added to classes, the lexical decoration syntax +was copied directly from function decorators:: + + @decorator + class Example: + # Subclasses will not be decorated automatically + pass + +This mechanism works well, so long as it is considered acceptable that the +decorator is *not* applied automatically to any subclasses. If it is +desired that the behaviour be inherited, it is currently necessary to +make the step up to defining a `custom metaclass`_:: + + class DynamicDecorators(type): + """Metaclass for dynamic decorator support + + Creates the class normally, then runs through the MRO looking for + __decorators__ attributes and applying the contained decorators to + the newly created class + """ + def __new__(meta, name, bases, ns): + cls = super(DynamicDecorators, meta).__new__(meta, name, bases, ns) + for entry in reversed(cls.mro()): + decorators = entry.__dict__.get("__decorators__", ()) + for deco in reversed(decorators): + cls = deco(cls) + return cls + + class Example(metaclass=DynamicDecorators): + # Subclasses *will* be decorated automatically + __decorators__ = [decorator] + +The main potential problem with this approach, is that it can place +significant constraints on the type heirarchy, as it requires that all +metaclasses used be well behaved with respect to multiple inheritance. + +By making dynamic decorators an inherent part of the class creation process, +many current use cases of metaclasses may be replaced with dynamic decorators +instead, greatly reducing the likelihood of metaclass conflicts, as well +as being substantially easier to write correctly in the first place. + + +Design Discussion +================= + + +Allowing metaclasses to override the dynamic decoration process +--------------------------------------------------------------- + +This PEP does not provide a mechanism that allows metaclasses to override the +dynamic decoration process. If this feature is deemed desirable in the +future, then it can be added by moving the functionality described in +this PEP into a new method on the metaclass (for example, ``__decorate__``), +with ``type`` providing a suitable default implementation that matches +the behaviour described here. + +This PEP chose the simplicity of the current approach, as lexical decorators +are currently outside the scope of metaclass control, so it seems reasonable +to pursue the simpler strategy in the absence of a solid use case for +making this behaviour configurable. + + +Iterating over decorator entries in reverse order +------------------------------------------------- + +This order was chosen to match the layout of lexical decorators when +converted to ordinary function calls. Just as the following are equivalent:: + + @deco2 + @deco1 + class C: + pass + + class C: + pass + C = deco2(deco1(C)) + +So too will the following be roughly equivalent (aside from inheritance):: + + class C: + __decorators__ = [deco2, deco1] + + class C: + pass + C = deco2(deco1(C)) + + +Iterating over the MRO in reverse order +--------------------------------------- + +The order of iteration over the MRO for decorator application was chosen to +match the order of actual call *evaluation* when using ``super`` to invoke +parent class implementations: the first method to run to completion is that +closest to the base of the class hierarchy. + + +References +========== + +.. _custom metaclass: + https://bitbucket.org/ncoghlan/misc/src/default/pep422.py + + +Copyright +========= + +This document has been placed in the public domain. + + +.. + Local Variables: + mode: indented-text + indent-tabs-mode: nil + sentence-end-double-space: t + fill-column: 70 + coding: utf-8 + End: + -- Repository URL: http://hg.python.org/peps From python-checkins at python.org Tue Jun 5 14:17:06 2012 From: python-checkins at python.org (nick.coghlan) Date: Tue, 05 Jun 2012 14:17:06 +0200 Subject: [Python-checkins] =?utf8?q?peps=3A_PEP_420_is_done?= Message-ID: http://hg.python.org/peps/rev/60d94f1eee0b changeset: 4450:60d94f1eee0b user: Nick Coghlan date: Tue Jun 05 22:16:52 2012 +1000 summary: PEP 420 is done files: pep-0420.txt | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/pep-0420.txt b/pep-0420.txt --- a/pep-0420.txt +++ b/pep-0420.txt @@ -3,7 +3,7 @@ Version: $Revision$ Last-Modified: $Date$ Author: Eric V. Smith -Status: Accepted +Status: Final Type: Standards Track Content-Type: text/x-rst Created: 19-Apr-2012 -- Repository URL: http://hg.python.org/peps From python-checkins at python.org Tue Jun 5 14:23:38 2012 From: python-checkins at python.org (richard.oudkerk) Date: Tue, 05 Jun 2012 14:23:38 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Add_test_for_multiprocessin?= =?utf8?q?g=2EConditon=2Ewait=28=29_and_changset_3baeb5e13dd2?= Message-ID: http://hg.python.org/cpython/rev/b2f86880517f changeset: 77362:b2f86880517f user: Richard Oudkerk date: Tue Jun 05 13:15:29 2012 +0100 summary: Add test for multiprocessing.Conditon.wait() and changset 3baeb5e13dd2 files: Lib/test/test_multiprocessing.py | 28 ++++++++++++++++++++ 1 files changed, 28 insertions(+), 0 deletions(-) diff --git a/Lib/test/test_multiprocessing.py b/Lib/test/test_multiprocessing.py --- a/Lib/test/test_multiprocessing.py +++ b/Lib/test/test_multiprocessing.py @@ -956,6 +956,34 @@ p.join(5) self.assertTrue(success.value) + @classmethod + def _test_wait_result(cls, c, pid): + with c: + c.notify() + time.sleep(1) + if pid is not None: + os.kill(pid, signal.SIGINT) + + def test_wait_result(self): + if isinstance(self, ProcessesMixin) and sys.platform != 'win32': + pid = os.getpid() + else: + pid = None + + c = self.Condition() + with c: + self.assertFalse(c.wait(0)) + self.assertFalse(c.wait(0.1)) + + p = self.Process(target=self._test_wait_result, args=(c, pid)) + p.start() + + self.assertTrue(c.wait(10)) + if pid is not None: + self.assertRaises(KeyboardInterrupt, c.wait, 10) + + p.join() + class _TestEvent(BaseTestCase): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 5 22:30:55 2012 From: python-checkins at python.org (gregory.p.smith) Date: Tue, 05 Jun 2012 22:30:55 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=283=2E2=29=3A_Fixes_issue_=23?= =?utf8?q?15000=3A_support_the_odd_x32_abi_on_posixsubprocess=27s_system_c?= =?utf8?q?all=2E?= Message-ID: http://hg.python.org/cpython/rev/aa9cfeea07ad changeset: 77363:aa9cfeea07ad branch: 3.2 parent: 77333:f3ce3e874a58 user: Gregory P. Smith date: Tue Jun 05 13:26:39 2012 -0700 summary: Fixes issue #15000: support the odd x32 abi on posixsubprocess's system call. files: Misc/NEWS | 2 ++ Modules/_posixsubprocess.c | 7 +++++++ 2 files changed, 9 insertions(+), 0 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -275,6 +275,8 @@ Extension Modules ----------------- +- Issue #15000: Support the "unique" x32 architecture in _posixsubprocess.c. + - Issue #9041: An issue in ctypes.c_longdouble, ctypes.c_double, and ctypes.c_float that caused an incorrect exception to be returned in the case of overflow has been fixed. diff --git a/Modules/_posixsubprocess.c b/Modules/_posixsubprocess.c --- a/Modules/_posixsubprocess.c +++ b/Modules/_posixsubprocess.c @@ -175,8 +175,15 @@ * chooses to break compatibility with all existing binaries. Highly Unlikely. */ struct linux_dirent { +#if defined(__x86_64__) && defined(__ILP32__) + /* Support the wacky x32 ABI (fake 32-bit userspace speaking to x86_64 + * kernel interfaces) - https://sites.google.com/site/x32abi/ */ + unsigned long long d_ino; + unsigned long long d_off; +#else unsigned long d_ino; /* Inode number */ unsigned long d_off; /* Offset to next linux_dirent */ +#endif unsigned short d_reclen; /* Length of this linux_dirent */ char d_name[256]; /* Filename (null-terminated) */ }; -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 5 22:30:56 2012 From: python-checkins at python.org (gregory.p.smith) Date: Tue, 05 Jun 2012 22:30:56 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=28merge_3=2E2_-=3E_default=29?= =?utf8?q?=3A_Fixes_issue_=2315000=3A_support_the_odd_x32_abi_on_posixsubp?= =?utf8?q?rocess=27s_system_call=2E?= Message-ID: http://hg.python.org/cpython/rev/5a4d5e714d08 changeset: 77364:5a4d5e714d08 parent: 77362:b2f86880517f parent: 77363:aa9cfeea07ad user: Gregory P. Smith date: Tue Jun 05 13:30:24 2012 -0700 summary: Fixes issue #15000: support the odd x32 abi on posixsubprocess's system call. files: Misc/NEWS | 5 +++++ Modules/_posixsubprocess.c | 7 +++++++ 2 files changed, 12 insertions(+), 0 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -55,6 +55,11 @@ - Issue #14963: Convert contextlib.ExitStack.__exit__ to use an iterative algorithm (Patch by Alon Horev) +Extension Modules +----------------- + +- Issue #15000: Support the "unique" x32 architecture in _posixsubprocess.c. + Tests ----- diff --git a/Modules/_posixsubprocess.c b/Modules/_posixsubprocess.c --- a/Modules/_posixsubprocess.c +++ b/Modules/_posixsubprocess.c @@ -177,8 +177,15 @@ * chooses to break compatibility with all existing binaries. Highly Unlikely. */ struct linux_dirent { +#if defined(__x86_64__) && defined(__ILP32__) + /* Support the wacky x32 ABI (fake 32-bit userspace speaking to x86_64 + * kernel interfaces) - https://sites.google.com/site/x32abi/ */ + unsigned long long d_ino; + unsigned long long d_off; +#else unsigned long d_ino; /* Inode number */ unsigned long d_off; /* Offset to next linux_dirent */ +#endif unsigned short d_reclen; /* Length of this linux_dirent */ char d_name[256]; /* Filename (null-terminated) */ }; -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 6 00:18:42 2012 From: python-checkins at python.org (kristjan.jonsson) Date: Wed, 06 Jun 2012 00:18:42 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Signal_condition_variables_?= =?utf8?q?with_the_mutex_held=2E__Destroy_condition_variables?= Message-ID: http://hg.python.org/cpython/rev/6d146e2ae9a9 changeset: 77365:6d146e2ae9a9 user: Kristj?n Valur J?nsson date: Tue Jun 05 22:17:42 2012 +0000 summary: Signal condition variables with the mutex held. Destroy condition variables before their mutexes. files: Python/ceval_gil.h | 9 +++++---- Python/thread_pthread.h | 15 +++++++++------ 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/Python/ceval_gil.h b/Python/ceval_gil.h --- a/Python/ceval_gil.h +++ b/Python/ceval_gil.h @@ -313,14 +313,15 @@ static void destroy_gil(void) { + /* some pthread-like implementations tie the mutex to the cond + * and must have the cond destroyed first. + */ + COND_FINI(gil_cond); MUTEX_FINI(gil_mutex); #ifdef FORCE_SWITCHING + COND_FINI(switch_cond); MUTEX_FINI(switch_mutex); #endif - COND_FINI(gil_cond); -#ifdef FORCE_SWITCHING - COND_FINI(switch_cond); -#endif _Py_atomic_store_explicit(&gil_locked, -1, _Py_memory_order_release); _Py_ANNOTATE_RWLOCK_DESTROY(&gil_locked); } diff --git a/Python/thread_pthread.h b/Python/thread_pthread.h --- a/Python/thread_pthread.h +++ b/Python/thread_pthread.h @@ -443,12 +443,15 @@ dprintf(("PyThread_free_lock(%p) called\n", lock)); + /* some pthread-like implementations tie the mutex to the cond + * and must have the cond destroyed first. + */ + status = pthread_cond_destroy( &thelock->lock_released ); + CHECK_STATUS("pthread_cond_destroy"); + status = pthread_mutex_destroy( &thelock->mut ); CHECK_STATUS("pthread_mutex_destroy"); - status = pthread_cond_destroy( &thelock->lock_released ); - CHECK_STATUS("pthread_cond_destroy"); - free((void *)thelock); } @@ -531,12 +534,12 @@ thelock->locked = 0; - status = pthread_mutex_unlock( &thelock->mut ); - CHECK_STATUS("pthread_mutex_unlock[3]"); - /* wake up someone (anyone, if any) waiting on the lock */ status = pthread_cond_signal( &thelock->lock_released ); CHECK_STATUS("pthread_cond_signal"); + + status = pthread_mutex_unlock( &thelock->mut ); + CHECK_STATUS("pthread_mutex_unlock[3]"); } #endif /* USE_SEMAPHORES */ -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 6 01:39:38 2012 From: python-checkins at python.org (victor.stinner) Date: Wed, 06 Jun 2012 01:39:38 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Close_=236203=3A_Document_t?= =?utf8?q?hat_Python_3_sets_LC=5FCTYPE_at_startup_to_the_user=27s?= Message-ID: http://hg.python.org/cpython/rev/113cdce4663c changeset: 77366:113cdce4663c user: Victor Stinner date: Wed Jun 06 01:37:37 2012 +0200 summary: Close #6203: Document that Python 3 sets LC_CTYPE at startup to the user's preferred locale encoding files: Doc/library/locale.rst | 7 +++++-- 1 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Doc/library/locale.rst b/Doc/library/locale.rst --- a/Doc/library/locale.rst +++ b/Doc/library/locale.rst @@ -475,8 +475,11 @@ locale somewhat painful to use correctly. Initially, when a program is started, the locale is the ``C`` locale, no matter -what the user's preferred locale is. The program must explicitly say that it -wants the user's preferred locale settings by calling ``setlocale(LC_ALL, '')``. +what the user's preferred locale is. There is one exception: the +:data:`LC_CTYPE` category is changed at startup to set the current locale +encoding to the user's preferred locale encoding. The program must explicitly +say that it wants the user's preferred locale settings for other categories by +calling ``setlocale(LC_ALL, '')``. It is generally a bad idea to call :func:`setlocale` in some library routine, since as a side effect it affects the entire program. Saving and restoring it -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 6 02:20:57 2012 From: python-checkins at python.org (brett.cannon) Date: Wed, 06 Jun 2012 02:20:57 +0200 Subject: [Python-checkins] =?utf8?q?peps=3A_Update_for_PEP_362_by_Yury_Sel?= =?utf8?q?ivanov_=28mostly=29_and_Larry_Hastings=2E?= Message-ID: http://hg.python.org/peps/rev/31ddc7f118d6 changeset: 4451:31ddc7f118d6 user: Brett Cannon date: Tue Jun 05 20:20:53 2012 -0400 summary: Update for PEP 362 by Yury Selivanov (mostly) and Larry Hastings. files: pep-0362.txt | 506 ++++++++++++++++++++------------------ 1 files changed, 261 insertions(+), 245 deletions(-) diff --git a/pep-0362.txt b/pep-0362.txt --- a/pep-0362.txt +++ b/pep-0362.txt @@ -2,269 +2,313 @@ Title: Function Signature Object Version: $Revision$ Last-Modified: $Date$ -Author: Brett Cannon , Jiwon Seo +Author: Brett Cannon , Jiwon Seo , + Yury Selivanov , Larry Hastings Status: Draft Type: Standards Track Content-Type: text/x-rst Created: 21-Aug-2006 -Python-Version: 2.6 -Post-History: 05-Sep-2007 +Python-Version: 3.3 +Post-History: 04-Jun-2012 Abstract ======== Python has always supported powerful introspection capabilities, -including that for functions and methods (for the rest of this PEP the -word "function" refers to both functions and methods). Taking a -function object, you can fully reconstruct the function's signature. -Unfortunately it is a little unruly having to look at all the -different attributes to pull together complete information for a -function's signature. +including introspecting functions and methods. (For the rest of +this PEP, "function" refers to both functions and methods). By +examining a function object you can fully reconstruct the function's +signature. Unfortunately this information is stored in an inconvenient +manner, and is spread across a half-dozen deeply nested attributes. -This PEP proposes an object representation for function signatures. -This should help facilitate introspection on functions for various -uses. The introspection information contains all possible information -about the parameters in a signature (including Python 3.0 features). +This PEP proposes a new representation for function signatures. +The new representation contains all necessary information about a function +and its parameters, and makes introspection easy and straightforward. -This object, though, is not meant to replace existing ways of -introspection on a function's signature. The current solutions are -there to make Python's execution work in an efficient manner. The -proposed object representation is only meant to help make application -code have an easier time to query a function on its signature. - - -Purpose -======= - -An object representation of a function's call signature should provide -an easy way to introspect what a function expects as arguments. It -does not need to be a "live" representation, though; the signature can -be inferred once and stored without changes to the signature object -representation affecting the function it represents (but this is an -`Open Issues`_). - -Indirection of signature introspection can also occur. If a -decorator took a decorated function's signature object and set it on -the decorating function then introspection could be redirected to what -is actually expected instead of the typical ``*args, **kwargs`` -signature of decorating functions. +However, this object does not replace the existing function +metadata, which is used by Python itself to execute those +functions. The new metadata object is intended solely to make +function introspection easier for Python programmers. Signature Object ================ -The overall signature of an object is represented by the Signature -object. This object is to store a `Parameter object`_ for each -parameter in the signature. It is also to store any information -about the function itself that is pertinent to the signature. +A Signature object represents the overall signature of a function. +It stores a `Parameter object`_ for each parameter accepted by the +function, as well as information specific to the function itself. -A Signature object has the following structure attributes: +A Signature object has the following public attributes and methods: * name : str - Name of the function. This is not fully qualified because - function objects for methods do not know the class they are - contained within. This makes functions and methods - indistinguishable from one another when passed to decorators, - preventing proper creation of a fully qualified name. -* var_args : str - Name of the variable positional parameter (i.e., ``*args``), if - present, or the empty string. -* var_kw_args : str - Name of the variable keyword parameter (i.e., ``**kwargs``), if - present, or the empty string. -* var_annotations: dict(str, object) - Dict that contains the annotations for the variable parameters. - The keys are of the variable parameter with values of the - annotation. If an annotation does not exist for a variable - parameter then the key does not exist in the dict. + Name of the function. +* qualname : str + Fully qualified name of the function. * return_annotation : object - If present, the attribute is set to the annotation for the return - type of the function. -* parameters : list(Parameter) - List of the parameters of the function as represented by - Parameter objects in the order of its definition (keyword-only - arguments are in the order listed by ``code.co_varnames``). -* bind(\*args, \*\*kwargs) -> dict(str, object) - Create a mapping from arguments to parameters. The keys are the - names of the parameter that an argument maps to with the value - being the value the parameter would have if this function was - called with the given arguments. + The annotation for the return type of the function if specified. + If the function has no annotation for its return type, this + attribute is not set. +* parameters : OrderedDict + An ordered mapping of parameters' names to the corresponding + Parameter objects (keyword-only arguments are in the same order + as listed in ``code.co_varnames``). +* bind(\*args, \*\*kwargs) -> BoundArguments + Creates a mapping from positional and keyword arguments to + parameters. -Signature objects also have the following methods: +Once a Signature object is created for a particular function, +it's cached in the ``__signature__`` attribute of that function. -* __getitem__(self, key : str) -> Parameter - Returns the Parameter object for the named parameter. -* __iter__(self) - Returns an iterator that returns Parameter objects in their - sequential order based on their 'position' attribute. - -The Signature object is stored in the ``__signature__`` attribute of -a function. When it is to be created is discussed in -`Open Issues`_. +Changes to the Signature object, or to any of its data members, +do not affect the function itself. Parameter Object ================ -A function's signature is made up of several parameters. Python's -different kinds of parameters is quite large and rich and continues to -grow. Parameter objects represent any possible parameter. - -Originally the plan was to represent parameters using a list of -parameter names on the Signature object along with various dicts keyed -on parameter names to disseminate the various pieces of information -one can know about a parameter. But the decision was made to -incorporate all information about a parameter in a single object so -as to make extending the information easier. This was originally put -forth by Talin and the preferred form of Guido (as discussed at the -2006 Google Sprint). +Python's expressive syntax means functions can accept many different +kinds of parameters with many subtle semantic differences. We +propose a rich Parameter object designed to represent any possible +function parameter. The structure of the Parameter object is: -* name : (str | tuple(str)) - The name of the parameter as a string if it is not a tuple. If - the argument is a tuple then a tuple of strings is used. -* position : int - The position of the parameter within the signature of the - function (zero-indexed). For keyword-only parameters the position - value is arbitrary while not conflicting with positional - parameters. The suggestion of setting the attribute to None or -1 - to represent keyword-only parameters was rejected to prevent - variable type usage and as a possible point of errors, - respectively. -* default_value : object - The default value for the parameter, if present, else the - attribute does not exist. -* keyword_only : bool +* name : str + The name of the parameter as a string. +* default : object + The default value for the parameter if specified. If the + parameter has no default value, this attribute is not set. +* annotation : object + The annotation for the parameter if specified. If the + parameter has no annotation, this attribute is not set. +* is_keyword_only : bool True if the parameter is keyword-only, else False. -* annotation - Set to the annotation for the parameter. If ``has_annotation`` is - False then the attribute does not exist to prevent accidental use. +* is_args : bool + True if the parameter accepts variable number of arguments + (``\*args``-like), else False. +* is_kwargs : bool + True if the parameter accepts variable number of keyword + arguments (``\*\*kwargs``-like), else False. +* is_implemented : bool + True if the parameter is implemented for use. Some platforms + implement functions but can't support specific parameters + (e.g. "mode" for os.mkdir). Passing in an unimplemented + parameter may result in the parameter being ignored, + or in NotImplementedError being raised. It is intended that + all conditions where ``is_implemented`` may be False be + thoroughly documented. + + +BoundArguments Object +===================== + +Result of a ``Signature.bind`` call. Holds the mapping of arguments +to the function's parameters. + +Has the following public attributes: + +* arguments : OrderedDict + An ordered mutable mapping of parameters' names to arguments' values. + Does not contain arguments' default values. +* args : tuple + Tuple of positional arguments values. Dynamically computed from + the 'arguments' attribute. +* kwargs : dict + Dict of keyword arguments values. Dynamically computed from + the 'arguments' attribute. + +The ``arguments`` attribute should be used in conjunction with +``Signature.parameters`` for any arguments processing purposes. + +``args`` and ``kwargs`` properties should be used to invoke functions: +:: + + def test(a, *, b): + ... + + sig = signature(test) + ba = sig.bind(10, b=20) + test(*ba.args, **ba.kwargs) Implementation ============== -An implementation can be found in Python's sandbox [#impl]_. -There is a function named ``signature()`` which -returns the value stored on the ``__signature__`` attribute if it -exists, else it creates the Signature object for the -function and sets ``__signature__``. For methods this is stored -directly on the im_func function object since that is what decorators -work with. +An implementation for Python 3.3 can be found here: [#impl]_. +A python issue was also created: [#issue]_. + +The implementation adds a new function ``signature()`` to the +``inspect`` module. ``signature()`` returns the value stored +on the ``__signature__`` attribute if it exists, otherwise it +creates the Signature object for the function and caches it in +the function's ``__signature__``. (For methods this is stored +directly in the ``__func__`` function object, since that is what +decorators work with.) Examples ======== +Function Signature Renderer +--------------------------- +:: + + def render_signature(signature): + '''Renders function definition by its signature. + + Example: + + >>> def test(a:'foo', *, b:'bar', c=True, **kwargs:None) -> 'spam': + ... pass + + >>> render_signature(inspect.signature(test)) + test(a:'foo', *, b:'bar', c=True, **kwargs:None) -> 'spam' + ''' + + result = [] + render_kw_only_separator = True + for param in signature.parameters.values(): + formatted = param.name + + # Add annotation and default value + if hasattr(param, 'annotation'): + formatted = '{}:{!r}'.format(formatted, param.annotation) + if hasattr(param, 'default'): + formatted = '{}={!r}'.format(formatted, param.default) + + # Handle *args and **kwargs -like parameters + if param.is_args: + formatted = '*' + formatted + elif param.is_kwargs: + formatted = '**' + formatted + + if param.is_args: + # OK, we have an '*args'-like parameter, so we won't need + # a '*' to separate keyword-only arguments + render_kw_only_separator = False + elif param.is_keyword_only and render_kw_only_separator: + # We have a keyword-only parameter to render and we haven't + # rendered an '*args'-like parameter before, so add a '*' + # separator to the parameters list ("foo(arg1, *, arg2)" case) + result.append('*') + # This condition should be only triggered once, so + # reset the flag + render_kw_only_separator = False + + result.append(formatted) + + rendered = '{}({})'.format(signature.name, ', '.join(result)) + + if hasattr(signature, 'return_annotation'): + rendered += ' -> {!r}'.format(signature.return_annotation) + + return rendered + + Annotation Checker ------------------ :: - def quack_check(fxn): - """Decorator to verify arguments and return value quack as they should. + import inspect + import functools - Positional arguments. - >>> @quack_check - ... def one_arg(x:int): pass - ... - >>> one_arg(42) - >>> one_arg('a') - Traceback (most recent call last): - ... - TypeError: 'a' does not quack like a + def checktypes(func): + '''Decorator to verify arguments and return types + Example: - *args - >>> @quack_check - ... def var_args(*args:int): pass - ... - >>> var_args(*[1,2,3]) - >>> var_args(*[1,'b',3]) - Traceback (most recent call last): - ... - TypeError: *args contains a a value that does not quack like a + >>> @checktypes + ... def test(a:int, b:str) -> int: + ... return int(a * b) - **kwargs - >>> @quack_check - ... def var_kw_args(**kwargs:int): pass - ... - >>> var_kw_args(**{'a': 1}) - >>> var_kw_args(**{'a': 'A'}) - Traceback (most recent call last): - ... - TypeError: **kwargs contains a value that does not quack like a + >>> test(10, '1') + 1111111111 - Return annotations. - >>> @quack_check - ... def returned(x) -> int: return x - ... - >>> returned(42) - 42 - >>> returned('a') - Traceback (most recent call last): - ... - TypeError: the return value 'a' does not quack like a + >>> test(10, 1) + Traceback (most recent call last): + ... + ValueError: foo: wrong type of 'b' argument, 'str' expected, got 'int' + ''' - """ - # Get the signature; only needs to be calculated once. - sig = Signature(fxn) - def check(*args, **kwargs): - # Find out the variable -> value bindings. - bindings = sig.bind(*args, **kwargs) - # Check *args for the proper quack. + sig = inspect.signature(func) + + types = {} + for param in sig.parameters.values(): + # Iterate through function's parameters and build the list of + # arguments types try: - duck = sig.var_annotations[sig.var_args] - except KeyError: + type_ = param.annotation + except AttributeError: + continue + else: + if not inspect.isclass(type_): + # Not a type, skip it + continue + + types[param.name] = type_ + + # If the argument has a type specified, let's check that its + # default value (if present) conforms with the type. + try: + default = param.default + except AttributeError: + continue + else: + if not isinstance(default, type_): + raise ValueError("{func}: wrong type of a default value for {arg!r}". \ + format(func=sig.qualname, arg=param.name)) + + def check_type(sig, arg_name, arg_type, arg_value): + # Internal function that incapsulates arguments type checking + if not isinstance(arg_value, arg_type): + raise ValueError("{func}: wrong type of {arg!r} argument, " \ + "{exp!r} expected, got {got!r}". \ + format(func=sig.qualname, arg=arg_name, + exp=arg_type.__name__, got=type(arg_value).__name__)) + + @functools.wraps(func) + def wrapper(*args, **kwargs): + # Let's bind the arguments + ba = sig.bind(*args, **kwargs) + for arg_name, arg in ba.arguments.items(): + # And iterate through the bound arguments + try: + type_ = types[arg_name] + except KeyError: + continue + else: + # OK, we have a type for the argument, lets get the corresponding + # parameter description from the signature object + param = sig.parameters[arg_name] + if param.is_args: + # If this parameter is a variable-argument parameter, + # then we need to check each of its values + for value in arg: + check_type(sig, arg_name, type_, value) + elif param.is_kwargs: + # If this parameter is a variable-keyword-argument parameter: + for subname, value in arg.items(): + check_type(sig, arg_name + ':' + subname, type_, value) + else: + # And, finally, if this parameter a regular one: + check_type(sig, arg_name, type_, arg) + + result = func(*ba.args, **ba.kwargs) + # The last bit - let's check that the result is correct + try: + return_type = sig.return_annotation + except AttributeError: + # Looks like we don't have any restriction on the return type pass else: - # Check every value in *args. - for value in bindings[sig.var_args]: - if not isinstance(value, duck): - raise TypeError("*%s contains a a value that does not " - "quack like a %r" % - (sig.var_args, duck)) - # Remove it from the bindings so as to not check it again. - del bindings[sig.var_args] - # **kwargs. - try: - duck = sig.var_annotations[sig.var_kw_args] - except (KeyError, AttributeError): - pass - else: - # Check every value in **kwargs. - for value in bindings[sig.var_kw_args].values(): - if not isinstance(value, duck): - raise TypeError("**%s contains a value that does not " - "quack like a %r" % - (sig.var_kw_args, duck)) - # Remove from bindings so as to not check again. - del bindings[sig.var_kw_args] - # For each remaining variable ... - for var, value in bindings.items(): - # See if an annotation was set. - try: - duck = sig[var].annotation - except AttributeError: - continue - # Check that the value quacks like it should. - if not isinstance(value, duck): - raise TypeError('%r does not quack like a %s' % (value, duck)) - else: - # All the ducks quack fine; let the call proceed. - returned = fxn(*args, **kwargs) - # Check the return value. - try: - if not isinstance(returned, sig.return_annotation): - raise TypeError('the return value %r does not quack like ' - 'a %r' % (returned, - sig.return_annotation)) - except AttributeError: - pass - return returned - # Full-featured version would set function metadata. - return check + if isinstance(return_type, type) and not isinstance(result, return_type): + raise ValueError('{func}: wrong return type, {exp} expected, got {got}'. \ + format(func=sig.qualname, exp=return_type.__name__, + got=type(result).__name__)) + return result + + return wrapper Open Issues @@ -280,54 +324,23 @@ Signature object and store it to ``__signature__`` if needed, and then return the value of ``__signature__``. +In the current implementation, signatures are created only on demand +("lazy"). -Should ``Signature.bind`` return Parameter objects as keys? ------------------------------------------------------------ -Instead of returning a dict with keys consisting of the name of the -parameters, would it be more useful to instead use Parameter -objects? The name of the argument can easily be retrieved from the -key (and the name would be used as the hash for a Parameter object). +Deprecate ``inspect.getfullargspec()`` and ``inspect.getcallargs()``? +--------------------------------------------------------------------- - -Have ``var_args`` and ``_var_kw_args`` default to ``None``? ------------------------------------------------------------- - -It has been suggested by Fred Drake that these two attributes have a -value of ``None`` instead of empty strings when they do not exist. -The answer to this question will influence what the defaults are for -other attributes as well. - - -Deprecate ``inspect.getargspec()`` and ``.formatargspec()``? -------------------------------------------------------------- - -Since the Signature object replicates the use of ``getargspec()`` -from the ``inspect`` module it might make sense to deprecate it in -2.6. ``formatargspec()`` could also go if Signature objects gained a -__str__ representation. - -Issue with that is types such as ``int``, when used as annotations, -do not lend themselves for output (e.g., ``""`` is the -string represenation for ``int``). The repr representation of types -would need to change in order to make this reasonable. - - -Have the objects be "live"? ---------------------------- - -Jim Jewett pointed out that Signature and Parameter objects could be -"live". That would mean requesting information would be done on the -fly instead of caching it on the objects. It would also allow for -mutating the function if the Signature or Parameter objects were -mutated. +Since the Signature object replicates the use of ``getfullargspec()`` +and ``getcallargs()`` from the ``inspect`` module it might make sense +to begin deprecating them in 3.3. References ========== -.. [#impl] pep362 directory in Python's sandbox - (http://svn.python.org/view/sandbox/trunk/pep362/) +.. [#impl] pep362 branch (https://bitbucket.org/1st1/cpython/overview) +.. [#issue] issue 15008 (http://bugs.python.org/issue15008) Copyright @@ -335,7 +348,6 @@ This document has been placed in the public domain. - .. Local Variables: -- Repository URL: http://hg.python.org/peps From solipsis at pitrou.net Wed Jun 6 05:47:53 2012 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Wed, 06 Jun 2012 05:47:53 +0200 Subject: [Python-checkins] Daily reference leaks (113cdce4663c): sum=0 Message-ID: results for 113cdce4663c on branch "default" -------------------------------------------- Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflog1ItS5Y', '-x'] From python-checkins at python.org Wed Jun 6 13:40:17 2012 From: python-checkins at python.org (nick.coghlan) Date: Wed, 06 Jun 2012 13:40:17 +0200 Subject: [Python-checkins] =?utf8?q?peps=3A_PEP_422_rewrite_to_present_an_?= =?utf8?q?idea_that_a=29_isn=27t_crazy_and_b=29_it_turns_out?= Message-ID: http://hg.python.org/peps/rev/8651e3c70755 changeset: 4452:8651e3c70755 user: Nick Coghlan date: Wed Jun 06 21:40:04 2012 +1000 summary: PEP 422 rewrite to present an idea that a) isn't crazy and b) it turns out Thomas Heller proposed back in 2001 files: pep-0422.txt | 334 +++++++++++++++++++++++++++----------- 1 files changed, 238 insertions(+), 96 deletions(-) diff --git a/pep-0422.txt b/pep-0422.txt --- a/pep-0422.txt +++ b/pep-0422.txt @@ -1,5 +1,5 @@ PEP: 422 -Title: Dynamic class decorators +Title: Simple class initialisation hook Version: $Revision$ Last-Modified: $Date$ Author: Nick Coghlan @@ -7,148 +7,287 @@ Type: Standards Track Content-Type: text/x-rst Created: 5-Jun-2012 +Python-Version: 3.4 Post-History: 5-Jun-2012 Abstract ======== -Classes currently support two mechanisms for modification of the class at -definition time: metaclasses and lexical decorators. +In Python 2, the body of a class definition could modify the way a class +was created (or simply arrange to run other code after the class was created) +by setting the ``__metaclass__`` attribute in the class body. While doing +this implicitly from called code required the use of an implementation detail +(specifically, ``sys._getframes()``), it could also be done explicitly in a +fully supported fashion (for example, by passing ``locals()`` to an +function that calculated a suitable ``__metaclass__`` value) -Metaclasses can be awkward and challenging to use correctly in conjunction -with multiple inheritance and lexical decorators don't interact with class -inheritance at all. +There is currently no corresponding mechanism in Python 3 that allows the +code executed in the class body to directly influence how the class object +is created. Instead, the class creation process is fully defined by the +class header, before the class body even begins executing. -This PEP proposes a new mechanism for dynamic class decoration that -interacts more cleanly with class inheritance mechanisms. +This PEP proposes a mechanism that will once again allow the body of a +class definition to more directly influence the way a class is created +(albeit in a more constrained fashion), as well as replacing some current +uses of metaclasses with a simpler, easier to understand alternative. -Specification -============= +Background +========== -This PEP proposes that a new step be added to the class creation process, -after the metaclass invocation to construct the class instance and before -the application of lexical decorators. +For an already created class ``cls``, the term "metaclass" has a clear +meaning: it is the value of ``type(cls)``. -This step will walk the class MRO in reverse order, looking for -``__decorators__`` entries in each class dictionary. These entries are -expected to be iterables that are also walked in reverse order to retrieve -class decorators that are automatically applied to the class being defined:: +*During* class creation, it has another meaning: it is also used to refer to +the metaclass hint that may be provided as part of the class definition. +While in many cases these two meanings end up referring to one and the same +object, there are two situations where that is not the case: - for entry in reversed(cls.mro()): - decorators = entry.__dict__.get("__decorators__", ()) - for deco in reversed(decorators): - cls = deco(cls) +* If the metaclass hint refers to an instance of ``type``, then it is + considered as a candidate metaclass along with the metaclasses of all of + the parents of the class being defined. If a more appropriate metaclass is + found amongst the candidates, then it will be used instead of the one + given in the metaclass hint. +* Otherwise, an explicit metaclass hint is assumed to be a factory function + and is called directly to create the class object. In this case, the final + metaclass will be determined by the factory function definition. In the + typical case (where the factory functions just calls ``type``, or, in + Python 3.3 or later, ``types.new_class``) the actual metaclass is then + determined based on the parent classes. -This step in the class creation process will be an implicit part of the -class statement and also part of the behaviour of ``types.new_class()``. +It is notable that only the actual metaclass is inherited - a factory +function used as a metaclass hook sees only the class currently being +defined, and is not invoked for any subclasses. +In Python 3, the metaclass hint is provided using the ``metaclass=Meta`` +keyword syntax in the class header. This allows the ``__prepare__`` method +on the metaclass to be used to create the ``locals()`` namespace used during +execution of the class body (for example, specifying the use of +``collections.OrderedDict`` instead of a regular ``dict``). -Rationale -========= +In Python 2, there was no ``__prepare__`` method (that API was added for +Python 3 by PEP 3115). Instead, a class body could set the ``__metaclass__`` +attribute, and the class creation process would extract that value from the +class namespace to use as the metaclass hint. There is `published code`_ that +makes use of this feature. -When decorator support was added to classes, the lexical decoration syntax -was copied directly from function decorators:: - @decorator - class Example: - # Subclasses will not be decorated automatically - pass +Proposal +======== -This mechanism works well, so long as it is considered acceptable that the -decorator is *not* applied automatically to any subclasses. If it is -desired that the behaviour be inherited, it is currently necessary to -make the step up to defining a `custom metaclass`_:: +This PEP proposes that a mechanism be added to Python 3 that meets the +following criteria: - class DynamicDecorators(type): - """Metaclass for dynamic decorator support +# Restores the ability for class namespaces to have some influence on the + class creation process (above and beyond populating the namespace itself), + but potentially without the full flexibility of the Python 2 style + ``__metaclass__`` hook +# Integrates nicely with class inheritance structures (including mixins and + multiple inheritance) +# Integrates nicely with the implicit ``__class__`` reference and + zero-argument ``super()`` syntax introduced by PEP 3135 +# Can be added to an existing base class without a significant risk of + introducing backwards compatibility problems - Creates the class normally, then runs through the MRO looking for - __decorators__ attributes and applying the contained decorators to - the newly created class - """ - def __new__(meta, name, bases, ns): - cls = super(DynamicDecorators, meta).__new__(meta, name, bases, ns) - for entry in reversed(cls.mro()): - decorators = entry.__dict__.get("__decorators__", ()) - for deco in reversed(decorators): - cls = deco(cls) +One mechanism that would achieve this goal is to add a new class +initialisation hook, modelled directly on the existing instance +initialisation hook. However, the signature would be constrained to ensure +that correctly supporting multiple inheritance is kept as simple as possible. + +Specifically, it is proposed that class definitions be able to provide a +class initialisation hook as follows:: + + class Example: + @classmethod + def __init_class__(cls): + # This is invoked after the class is created, but before any + # explicit decorators are called + # The usual super() mechanisms are used to correctly support + # multiple inheritance. The simple, decorator style invocation + # ensures that this is as simple as possible. + +If present on the created object, this new hook will be called by the class +creation machinery *after* the ``__class__`` reference has been initialised. +For ``types.new_class()``, it will be called as the last step before +returning the created class object. Calling the hook automatically from +``type.__init__`` unfortunately doesn't work, as it would mean the +``__init_class__`` method would be unable to call any methods that relied +on the ``__class__`` reference (or used the zero-argument form of +``super()``). + +If a metaclass wishes to block class initialisation for some reason, it +must arrange for ``cls.__init_class__`` to trigger ``AttributeError``. + +This general proposal is not a new idea (it was first suggested `more than +10 years ago`_), but I believe the situation has changed sufficiently in +that time that the idea is worth reconsidering. + + +Key Benefits +============ + + +Replaces dynamic setting of ``__metaclass__`` +--------------------------------------------- + +For use cases that didn't involve completely replacing the defined class, +Python 2 code that dynamically set ``__metaclass__`` can now dynamically +set ``__init_class__`` instead. For more advanced use cases, introduction of +an explicit metaclass will still be necessary in order to support Python 3. + + +Easier inheritance of definition time behaviour +----------------------------------------------- + +Understanding Python's metaclass system requires a deep understanding of +the type system and the class construction process. This is legitimately +seen as confusing, due to the need to keep multiple moving parts (the code, +the metaclass hint, the actual metaclass, the class object, instances of the +class object) clearly distinct in your mind. + +Understanding the proposed class initialisation hook requires understanding +decorators and ordinary method inheritance, which is a much simpler prospect. + + +Reduced chance of metaclass conflicts +------------------------------------- + +One of the big issues that makes library authors reluctant to use metaclasses +(even when it would be appropriate) is the risk of metaclass conflicts. +These occur whenever two unrelated metaclasses are used by the desired +parents of a class definition. This risk also makes it very difficult to +*add* a metaclass to a class that has previously been published without one. + +By contrast, adding an ``__init_class__`` method to an existing type poses +a similar level of risk to adding an ``__init__`` method: technically, there +is a risk of breaking poorly implemented subclasses, but when that occurs, +it is recognised as a bug in the subclass rather than the library author +breaching backwards compatibility guarantees. In fact, due to the constrained +signature, the risk in this case is actually even lower than in the case of +``__init__``. + + +Integrates cleanly with PEP 3135 +-------------------------------- + +Unlike code that runs as part of the metaclass, code that runs as part of +the new hook will be able to freely invoke class methods that rely on the +implicit ``__class__`` reference introduced by PEP 3135, including methods +that use the zero argument form of ``super()``. + + +Alternatives +============ + + +The Python 3 Status Quo +----------------------- + +The Python 3 status quo already offers a great deal of flexibility. For +changes which only affect a single class definition and which can be +specified at the time the code is written, then class decorators can be +used to modify a class explicitly. Class decorators largely ignore class +inheritance and can make full use of methods that rely on the ``__class__`` +reference being populated. + +Using a custom metaclass provides the same level of power as it did in +Python 2. However, it's notable that, unlike class decorators, a metaclass +cannot call any methods that rely on the ``__class__`` reference, as that +reference is not populated until after the metaclass constructor returns +control to the class creation code. + +One major use case for metaclasses actually closely resembles the use of +class decorators. It occurs whenever a metaclass has an implementation that +uses the following pattern:: + + class Metaclass(type): + def __new__(meta, *args, **kwds): + cls = super(Metaclass, meta).__new__(meta, *args, **kwds) + # Do something with cls return cls - class Example(metaclass=DynamicDecorators): - # Subclasses *will* be decorated automatically - __decorators__ = [decorator] +The key difference between this pattern and a class decorator is that it +is automatically inherited by subclasses. However, it also comes with a +major disadvantage: Python does not allow you to inherit from classes with +unrelated metaclasses. -The main potential problem with this approach, is that it can place -significant constraints on the type heirarchy, as it requires that all -metaclasses used be well behaved with respect to multiple inheritance. +Thus, the status quo requires that developers choose between the following +two alternatives: -By making dynamic decorators an inherent part of the class creation process, -many current use cases of metaclasses may be replaced with dynamic decorators -instead, greatly reducing the likelihood of metaclass conflicts, as well -as being substantially easier to write correctly in the first place. +* Use a class decorator, meaning that behaviour is not inherited and must be + requested explicitly on every subclass +* Use a metaclass, meaning that behaviour is inherited, but metaclass + conflicts may make integration with other libraries and frameworks more + difficult than it otherwise would be +If this PEP is ultimately rejected, then this is the existing design that +will remain in place by default. -Design Discussion -================= +Restoring the Python 2 metaclass hook +------------------------------------- -Allowing metaclasses to override the dynamic decoration process ---------------------------------------------------------------- +One simple alternative would be to restore support for a Python 2 style +``metaclass`` hook in the class body. This would be checked after the class +body was executed, potentially overwriting the metaclass hint provided in the +class header. -This PEP does not provide a mechanism that allows metaclasses to override the -dynamic decoration process. If this feature is deemed desirable in the -future, then it can be added by moving the functionality described in -this PEP into a new method on the metaclass (for example, ``__decorate__``), -with ``type`` providing a suitable default implementation that matches -the behaviour described here. +The main attraction of such an approach is that it would simplify porting +Python 2 applications that make use of this hook (especially those that do +so dynamically). -This PEP chose the simplicity of the current approach, as lexical decorators -are currently outside the scope of metaclass control, so it seems reasonable -to pursue the simpler strategy in the absence of a solid use case for -making this behaviour configurable. +However, this approach does nothing to simplify the process of adding +*inherited* class definition time behaviour, nor does it interoperate +cleanly with the PEP 3135 ``__class__`` and ``super()`` semantics (as with +any metaclass based solution, the ``__metaclass__`` hook would have to run +before the ``__class__`` reference has been populated. -Iterating over decorator entries in reverse order -------------------------------------------------- +Dynamic class decorators +------------------------ -This order was chosen to match the layout of lexical decorators when -converted to ordinary function calls. Just as the following are equivalent:: +The original version of this PEP was called "Dynamic class decorators" and +focused solely on a significantly more complicated proposal than that +presented in the current version. - @deco2 - @deco1 - class C: - pass +As with the current version, it proposed that a new step be added to the +class creation process, after the metaclass invocation to construct the +class instance and before the application of lexical decorators. However, +instead of a simple process of calling a single class method that relies +on normal inheritance mechanisms, it proposed a far more complicated +procedure that walked the class MRO looking for decorators stored in +iterable ``__decorators__`` attributes. - class C: - pass - C = deco2(deco1(C)) +Using the current version of the PEP, the scheme originally proposed could +be implemented as:: -So too will the following be roughly equivalent (aside from inheritance):: + class DynamicDecorators: + @classmethod + def __init_class__(cls): + super(DynamicDecorators, cls).__init_class__() + for entry in reversed(cls.mro()): + decorators = entry.__dict__.get("__decorators__", ()) + for deco in reversed(decorators): + cls = deco(cls) - class C: - __decorators__ = [deco2, deco1] +Any subclasses of this type would automatically have the contents of any +``__decorators__`` attributes processed and invoked. - class C: - pass - C = deco2(deco1(C)) +The mechanism in the current PEP is considered superior, as many issues +to do with ordering and the same decorator being invoked multiple times +simple go away, as that kind of thing is taken care of through the use of an +ordinary class method invocation. -Iterating over the MRO in reverse order ---------------------------------------- - -The order of iteration over the MRO for decorator application was chosen to -match the order of actual call *evaluation* when using ``super`` to invoke -parent class implementations: the first method to run to completion is that -closest to the base of the class hierarchy. - - References ========== -.. _custom metaclass: - https://bitbucket.org/ncoghlan/misc/src/default/pep422.py +.. _published code: + http://mail.python.org/pipermail/python-dev/2012-June/119878.html + +.. _more than 10 years ago: + http://mail.python.org/pipermail/python-dev/2001-November/018651.html Copyright -- Repository URL: http://hg.python.org/peps From python-checkins at python.org Wed Jun 6 13:44:13 2012 From: python-checkins at python.org (nick.coghlan) Date: Wed, 06 Jun 2012 13:44:13 +0200 Subject: [Python-checkins] =?utf8?q?peps=3A_Fix_numbered_bullet_points?= Message-ID: http://hg.python.org/peps/rev/c1bf52fbb073 changeset: 4453:c1bf52fbb073 user: Nick Coghlan date: Wed Jun 06 21:44:01 2012 +1000 summary: Fix numbered bullet points files: pep-0422.txt | 20 ++++++++++---------- 1 files changed, 10 insertions(+), 10 deletions(-) diff --git a/pep-0422.txt b/pep-0422.txt --- a/pep-0422.txt +++ b/pep-0422.txt @@ -79,16 +79,16 @@ This PEP proposes that a mechanism be added to Python 3 that meets the following criteria: -# Restores the ability for class namespaces to have some influence on the - class creation process (above and beyond populating the namespace itself), - but potentially without the full flexibility of the Python 2 style - ``__metaclass__`` hook -# Integrates nicely with class inheritance structures (including mixins and - multiple inheritance) -# Integrates nicely with the implicit ``__class__`` reference and - zero-argument ``super()`` syntax introduced by PEP 3135 -# Can be added to an existing base class without a significant risk of - introducing backwards compatibility problems +1. Restores the ability for class namespaces to have some influence on the + class creation process (above and beyond populating the namespace itself), + but potentially without the full flexibility of the Python 2 style + ``__metaclass__`` hook +2. Integrates nicely with class inheritance structures (including mixins and + multiple inheritance) +3. Integrates nicely with the implicit ``__class__`` reference and + zero-argument ``super()`` syntax introduced by PEP 3135 +4. Can be added to an existing base class without a significant risk of + introducing backwards compatibility problems One mechanism that would achieve this goal is to add a new class initialisation hook, modelled directly on the existing instance -- Repository URL: http://hg.python.org/peps From python-checkins at python.org Wed Jun 6 13:45:35 2012 From: python-checkins at python.org (nick.coghlan) Date: Wed, 06 Jun 2012 13:45:35 +0200 Subject: [Python-checkins] =?utf8?q?peps=3A_Reword_a_confusing_sentence?= Message-ID: http://hg.python.org/peps/rev/6858011a1c28 changeset: 4454:6858011a1c28 user: Nick Coghlan date: Wed Jun 06 21:45:26 2012 +1000 summary: Reword a confusing sentence files: pep-0422.txt | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pep-0422.txt b/pep-0422.txt --- a/pep-0422.txt +++ b/pep-0422.txt @@ -104,8 +104,8 @@ # This is invoked after the class is created, but before any # explicit decorators are called # The usual super() mechanisms are used to correctly support - # multiple inheritance. The simple, decorator style invocation - # ensures that this is as simple as possible. + # multiple inheritance. The decorator style invocation helps + # ensure that invoking the parent class is as simple as possible. If present on the created object, this new hook will be called by the class creation machinery *after* the ``__class__`` reference has been initialised. -- Repository URL: http://hg.python.org/peps From python-checkins at python.org Wed Jun 6 13:50:01 2012 From: python-checkins at python.org (nick.coghlan) Date: Wed, 06 Jun 2012 13:50:01 +0200 Subject: [Python-checkins] =?utf8?q?peps=3A_Eliminate_a_typo=2E_Also_wonde?= =?utf8?q?r_how_many_times_I_can_use_the_word_=27simple=27_or_a?= Message-ID: http://hg.python.org/peps/rev/2d3a63f8cd3f changeset: 4455:2d3a63f8cd3f user: Nick Coghlan date: Wed Jun 06 21:49:50 2012 +1000 summary: Eliminate a typo. Also wonder how many times I can use the word 'simple' or a derivative in one PEP. files: pep-0422.txt | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/pep-0422.txt b/pep-0422.txt --- a/pep-0422.txt +++ b/pep-0422.txt @@ -276,7 +276,7 @@ The mechanism in the current PEP is considered superior, as many issues to do with ordering and the same decorator being invoked multiple times -simple go away, as that kind of thing is taken care of through the use of an +just go away, as that kind of thing is taken care of through the use of an ordinary class method invocation. -- Repository URL: http://hg.python.org/peps From python-checkins at python.org Wed Jun 6 15:58:58 2012 From: python-checkins at python.org (stefan.krah) Date: Wed, 06 Jun 2012 15:58:58 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_1=29_Add_error_analysis_com?= =?utf8?b?bWVudHMgdG8gbXBkX3FsbjEwKCkgYW5kIF9tcGRfcWxuKCku?= Message-ID: http://hg.python.org/cpython/rev/36cd1cf5a160 changeset: 77367:36cd1cf5a160 user: Stefan Krah date: Wed Jun 06 15:57:18 2012 +0200 summary: 1) Add error analysis comments to mpd_qln10() and _mpd_qln(). 2) Simplify the precision adjustment code for values in [0.900, 1.15]. files: Modules/_decimal/libmpdec/mpdecimal.c | 131 ++++++++++--- 1 files changed, 98 insertions(+), 33 deletions(-) diff --git a/Modules/_decimal/libmpdec/mpdecimal.c b/Modules/_decimal/libmpdec/mpdecimal.c --- a/Modules/_decimal/libmpdec/mpdecimal.c +++ b/Modules/_decimal/libmpdec/mpdecimal.c @@ -4212,6 +4212,18 @@ *status |= workstatus; } +/* + * Schedule the optimal precision increase for the Newton iteration. + * v := input operand + * z_0 := initial approximation + * initprec := natural number such that abs(log(v) - z_0) < 10**-initprec + * maxprec := target precision + * + * For convenience the output klist contains the elements in reverse order: + * klist := [k_n-1, ..., k_0], where + * 1) k_0 <= initprec and + * 2) abs(log(v) - result) < 10**(-2*k_n-1 + 1) <= 10**-maxprec. + */ static inline int ln_schedule_prec(mpd_ssize_t klist[MPD_MAX_PREC_LOG2], mpd_ssize_t maxprec, mpd_ssize_t initprec) @@ -4231,6 +4243,7 @@ return i-1; } +/* The constants have been verified with both decimal.py and mpfr. */ #ifdef CONFIG_64 #if MPD_RDIGITS != 19 #error "mpdecimal.c: MPD_RDIGITS must be 19." @@ -4285,7 +4298,7 @@ (mpd_uint_t *)mpd_ln10_data }; -/* Set 'result' to ln(10), with 'prec' digits, using ROUND_HALF_EVEN. */ +/* Set 'result' to ln(10). ulp error: abs(result - log(10)) < ulp(log(10)) */ void mpd_qln10(mpd_t *result, mpd_ssize_t prec, uint32_t *status) { @@ -4320,7 +4333,7 @@ mpd_maxcontext(&varcontext); varcontext.round = MPD_ROUND_TRUNC; - i = ln_schedule_prec(klist, prec+2, result->digits); + i = ln_schedule_prec(klist, prec+2, -result->exp); for (; i >= 0; i--) { varcontext.prec = 2*klist[i]+3; result->flags ^= MPD_NEG; @@ -4339,7 +4352,18 @@ mpd_qfinalize(result, &maxcontext, status); } -/* Initial approximations for the ln() iteration */ +/* + * Initial approximations for the ln() iteration. The values have the + * following properties (established with both decimal.py and mpfr): + * + * Index 0 - 400, logarithms of x in [1.00, 5.00]: + * abs(lnapprox[i] * 10**-3 - log((i+100)/100)) < 10**-2 + * abs(lnapprox[i] * 10**-3 - log((i+1+100)/100)) < 10**-2 + * + * Index 401 - 899, logarithms of x in (0.500, 0.999]: + * abs(-lnapprox[i] * 10**-3 - log((i+100)/1000)) < 10**-2 + * abs(-lnapprox[i] * 10**-3 - log((i+1+100)/1000)) < 10**-2 + */ static const uint16_t lnapprox[900] = { /* index 0 - 400: log((i+100)/100) * 1000 */ 0, 10, 20, 30, 39, 49, 58, 68, 77, 86, 95, 104, 113, 122, 131, 140, 148, 157, @@ -4406,7 +4430,10 @@ 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 }; -/* Internal ln() function that does not check for specials, zero or one. */ +/* + * Internal ln() function that does not check for specials, zero or one. + * Relative error: abs(result - log(a)) < 0.1 * 10**-prec * abs(log(a)) + */ static void _mpd_qln(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status) @@ -4451,10 +4478,16 @@ mpd_setdigits(z); if (x <= 400) { + /* Reduce the input operand to 1.00 <= v <= 5.00. Let y = x + 100, + * so 100 <= y <= 500. Since y contains the most significant digits + * of v, y/100 <= v < (y+1)/100 and abs(z - log(v)) < 10**-2. */ v.exp = -(a_digits - 1); t = a_exp + a_digits - 1; } else { + /* Reduce the input operand to 0.500 < v <= 0.999. Let y = x + 100, + * so 500 < y <= 999. Since y contains the most significant digits + * of v, y/1000 <= v < (y+1)/1000 and abs(z - log(v)) < 10**-2. */ v.exp = -a_digits; t = a_exp + a_digits; mpd_set_negative(z); @@ -4465,37 +4498,46 @@ varcontext.round = MPD_ROUND_TRUNC; maxprec = ctx->prec + 2; - if (x <= 10 || x >= 805) { - /* v is close to 1: Estimate the magnitude of the logarithm. - * If v = 1 or ln(v) will underflow, skip the loop. Otherwise, - * adjust the precision upwards in order to obtain a sufficient - * number of significant digits. + if (t == 0 && (x <= 15 || x >= 800)) { + /* 0.900 <= v <= 1.15: Estimate the magnitude of the logarithm. + * If ln(v) will underflow, skip the loop. Otherwise, adjust the + * precision upwards in order to obtain a sufficient number of + * significant digits. * - * 1) x/(1+x) < ln(1+x) < x, for x > -1, x != 0 - * - * 2) (v-1)/v < ln(v) < v-1 + * Case v > 1: + * abs((v-1)/10) < abs((v-1)/v) < abs(ln(v)) < abs(v-1) + * Case v < 1: + * abs(v-1) < abs(ln(v)) < abs((v-1)/v) < abs((v-1)*10) */ - mpd_t *lower = &tmp; - mpd_t *upper = &vtmp; int cmp = _mpd_cmp(&v, &one); - varcontext.round = MPD_ROUND_CEILING; - varcontext.prec = maxprec; - mpd_qsub(upper, &v, &one, &varcontext, &varcontext.status); - varcontext.round = MPD_ROUND_FLOOR; - mpd_qdiv(lower, upper, &v, &varcontext, &varcontext.status); - varcontext.round = MPD_ROUND_TRUNC; + /* Upper bound (assume v > 1): abs(v-1), unrounded */ + _mpd_qsub(&tmp, &v, &one, &maxcontext, &maxcontext.status); + if (maxcontext.status & MPD_Errors) { + mpd_seterror(result, MPD_Malloc_error, status); + goto finish; + } if (cmp < 0) { - _mpd_ptrswap(&upper, &lower); - } - if (mpd_adjexp(upper) < mpd_etiny(ctx)) { - _settriple(z, (cmp<0), 1, mpd_etiny(ctx)-1); - goto postloop; - } - /* XXX optimization: t == 0 && mpd_adjexp(lower) < 0 */ - if (mpd_adjexp(lower) < 0) { - maxprec = maxprec - mpd_adjexp(lower); + /* v < 1: abs((v-1)*10) */ + tmp.exp += 1; + } + if (mpd_adjexp(&tmp) < mpd_etiny(ctx)) { + /* The upper bound is less than etiny: Underflow to zero */ + _settriple(result, (cmp<0), 1, mpd_etiny(ctx)-1); + goto finish; + } + /* Lower bound: abs((v-1)/10) or abs(v-1) */ + tmp.exp -= 1; + if (mpd_adjexp(&tmp) < 0) { + /* Absolute error of the loop: abs(z - log(v)) < 10**-p. If + * p = ctx->prec+2-adjexp(lower), then the relative error of + * the result is (using 10**adjexp(x) <= abs(x)): + * + * abs(z - log(v)) / abs(log(v)) < 10**-p / abs(log(v)) + * <= 10**(-ctx->prec-2) + */ + maxprec = maxprec - mpd_adjexp(&tmp); } } @@ -4523,14 +4565,37 @@ } } -postloop: - mpd_qln10(&v, maxprec+2, status); + /* + * Case t == 0: + * t * log(10) == 0, the result does not change and the analysis + * above applies. If v < 0.900 or v > 1.15, the relative error is + * less than 10**(-ctx.prec-1). + * Case t != 0: + * z := approx(log(v)) + * y := approx(log(10)) + * p := maxprec = ctx->prec + 2 + * Absolute errors: + * 1) abs(z - log(v)) < 10**-p + * 2) abs(y - log(10)) < 10**-p + * The multiplication is exact, so: + * 3) abs(t*y - t*log(10)) < t*10**-p + * The sum is exact, so: + * 4) abs((z + t*y) - (log(v) + t*log(10))) < (abs(t) + 1) * 10**-p + * Bounds for log(v) and log(10): + * 5) -7/10 < log(v) < 17/10 + * 6) 23/10 < log(10) < 24/10 + * Using 4), 5), 6) and t != 0, the relative error is: + * + * 7) relerr < ((abs(t) + 1)*10**-p) / abs(log(v) + t*log(10)) + * < 0.5 * 10**(-p + 1) = 0.5 * 10**(-ctx->prec-1) + */ + mpd_qln10(&v, maxprec+1, status); mpd_qmul_ssize(&tmp, &v, t, &maxcontext, status); - varcontext.prec = maxprec+2; - mpd_qadd(result, &tmp, z, &varcontext, status); + mpd_qadd(result, &tmp, z, &maxcontext, status); finish: + *status |= (MPD_Inexact|MPD_Rounded); mpd_del(&v); mpd_del(&vtmp); mpd_del(&tmp); -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 6 23:59:43 2012 From: python-checkins at python.org (kristjan.jonsson) Date: Wed, 06 Jun 2012 23:59:43 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Rearrange_code_to_beat_an_o?= =?utf8?q?ptimizer_bug_affecting_Release_x64_on_windows?= Message-ID: http://hg.python.org/cpython/rev/e1b950cb6b74 changeset: 77368:e1b950cb6b74 user: Kristj?n Valur J?nsson date: Wed Jun 06 21:58:08 2012 +0000 summary: Rearrange code to beat an optimizer bug affecting Release x64 on windows with VS2010sp1 files: Objects/unicodeobject.c | 22 ++++++++++------------ 1 files changed, 10 insertions(+), 12 deletions(-) diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -12038,16 +12038,23 @@ (categories Z* and C* except ASCII space) */ if (!Py_UNICODE_ISPRINTABLE(ch)) { + PyUnicode_WRITE(okind, odata, o++, '\\'); /* Map 8-bit characters to '\xhh' */ if (ch <= 0xff) { - PyUnicode_WRITE(okind, odata, o++, '\\'); PyUnicode_WRITE(okind, odata, o++, 'x'); PyUnicode_WRITE(okind, odata, o++, Py_hexdigits[(ch >> 4) & 0x000F]); PyUnicode_WRITE(okind, odata, o++, Py_hexdigits[ch & 0x000F]); } + /* Map 16-bit characters to '\uxxxx' */ + else if (ch <= 0xffff) { + PyUnicode_WRITE(okind, odata, o++, 'u'); + PyUnicode_WRITE(okind, odata, o++, Py_hexdigits[(ch >> 12) & 0xF]); + PyUnicode_WRITE(okind, odata, o++, Py_hexdigits[(ch >> 8) & 0xF]); + PyUnicode_WRITE(okind, odata, o++, Py_hexdigits[(ch >> 4) & 0xF]); + PyUnicode_WRITE(okind, odata, o++, Py_hexdigits[ch & 0xF]); + } /* Map 21-bit characters to '\U00xxxxxx' */ - else if (ch >= 0x10000) { - PyUnicode_WRITE(okind, odata, o++, '\\'); + else { PyUnicode_WRITE(okind, odata, o++, 'U'); PyUnicode_WRITE(okind, odata, o++, Py_hexdigits[(ch >> 28) & 0xF]); PyUnicode_WRITE(okind, odata, o++, Py_hexdigits[(ch >> 24) & 0xF]); @@ -12058,15 +12065,6 @@ PyUnicode_WRITE(okind, odata, o++, Py_hexdigits[(ch >> 4) & 0xF]); PyUnicode_WRITE(okind, odata, o++, Py_hexdigits[ch & 0xF]); } - /* Map 16-bit characters to '\uxxxx' */ - else { - PyUnicode_WRITE(okind, odata, o++, '\\'); - PyUnicode_WRITE(okind, odata, o++, 'u'); - PyUnicode_WRITE(okind, odata, o++, Py_hexdigits[(ch >> 12) & 0xF]); - PyUnicode_WRITE(okind, odata, o++, Py_hexdigits[(ch >> 8) & 0xF]); - PyUnicode_WRITE(okind, odata, o++, Py_hexdigits[(ch >> 4) & 0xF]); - PyUnicode_WRITE(okind, odata, o++, Py_hexdigits[ch & 0xF]); - } } /* Copy characters as-is */ else { -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Thu Jun 7 05:48:12 2012 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Thu, 07 Jun 2012 05:48:12 +0200 Subject: [Python-checkins] Daily reference leaks (e1b950cb6b74): sum=-1 Message-ID: results for e1b950cb6b74 on branch "default" -------------------------------------------- test_exceptions leaked [0, -1, 0] references, sum=-1 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogGc3eIB', '-x'] From python-checkins at python.org Thu Jun 7 14:08:54 2012 From: python-checkins at python.org (nick.coghlan) Date: Thu, 07 Jun 2012 14:08:54 +0200 Subject: [Python-checkins] =?utf8?q?peps=3A_Update_422_based_on_python-dev?= =?utf8?q?_feedback?= Message-ID: http://hg.python.org/peps/rev/d65ae0ea8b46 changeset: 4456:d65ae0ea8b46 user: Nick Coghlan date: Thu Jun 07 22:08:41 2012 +1000 summary: Update 422 based on python-dev feedback files: pep-0422.txt | 97 ++++++++++++++++++++++++++++++--------- 1 files changed, 73 insertions(+), 24 deletions(-) diff --git a/pep-0422.txt b/pep-0422.txt --- a/pep-0422.txt +++ b/pep-0422.txt @@ -44,7 +44,7 @@ While in many cases these two meanings end up referring to one and the same object, there are two situations where that is not the case: -* If the metaclass hint refers to an instance of ``type``, then it is +* If the metaclass hint refers to a subclass of ``type``, then it is considered as a candidate metaclass along with the metaclasses of all of the parents of the class being defined. If a more appropriate metaclass is found amongst the candidates, then it will be used instead of the one @@ -72,6 +72,16 @@ class namespace to use as the metaclass hint. There is `published code`_ that makes use of this feature. +Another new feature in Python 3 is the zero-argument form of the ``super()`` +builtin, introduced by PEP 3135. This feature uses an implicit ``__class__`` +reference to the class being defined to replace the "by name" references +required in Python 2. Just as code invoked during execution of a Python 2 +metaclass could not call methods that referenced the class by name (as the +name had not yet been bound in the containing scope), similarly, Python 3 +metaclasses cannot call methods that rely on the implicit ``__class__`` +reference (as it is not populated until after the metaclass has returned +control to the class creation machiner). + Proposal ======== @@ -90,10 +100,10 @@ 4. Can be added to an existing base class without a significant risk of introducing backwards compatibility problems -One mechanism that would achieve this goal is to add a new class +One mechanism that can achieve this goal is to add a new class initialisation hook, modelled directly on the existing instance -initialisation hook. However, the signature would be constrained to ensure -that correctly supporting multiple inheritance is kept as simple as possible. +initialisation hook, but with the signature constrained to match that +of an ordinary class decorator. Specifically, it is proposed that class definitions be able to provide a class initialisation hook as follows:: @@ -110,51 +120,57 @@ If present on the created object, this new hook will be called by the class creation machinery *after* the ``__class__`` reference has been initialised. For ``types.new_class()``, it will be called as the last step before -returning the created class object. Calling the hook automatically from -``type.__init__`` unfortunately doesn't work, as it would mean the -``__init_class__`` method would be unable to call any methods that relied -on the ``__class__`` reference (or used the zero-argument form of -``super()``). +returning the created class object. If a metaclass wishes to block class initialisation for some reason, it must arrange for ``cls.__init_class__`` to trigger ``AttributeError``. -This general proposal is not a new idea (it was first suggested `more than -10 years ago`_), but I believe the situation has changed sufficiently in -that time that the idea is worth reconsidering. +This general proposal is not a new idea (it was first suggested for +inclusion in the language definition `more than 10 years ago`_, and a +similar mechanism has long been supported by `Zope's ExtensionClass`_), +but I believe the situation has changed sufficiently in recent years that +the idea is worth reconsidering. Key Benefits ============ -Replaces dynamic setting of ``__metaclass__`` ---------------------------------------------- +Replaces many use cases for dynamic setting of ``__metaclass__`` +----------------------------------------------------------------- -For use cases that didn't involve completely replacing the defined class, +For use cases that don't involve completely replacing the defined class, Python 2 code that dynamically set ``__metaclass__`` can now dynamically set ``__init_class__`` instead. For more advanced use cases, introduction of -an explicit metaclass will still be necessary in order to support Python 3. +an explicit metaclass (possibly made available as a required base class) will +still be necessary in order to support Python 3. Easier inheritance of definition time behaviour ----------------------------------------------- -Understanding Python's metaclass system requires a deep understanding of +Understanding Python's metaclasses requires a deep understanding of the type system and the class construction process. This is legitimately -seen as confusing, due to the need to keep multiple moving parts (the code, +seen as challenging, due to the need to keep multiple moving parts (the code, the metaclass hint, the actual metaclass, the class object, instances of the -class object) clearly distinct in your mind. +class object) clearly distinct in your mind. Even when you know the rules, +it's still easy to make a mistake if you're not being extremely careful. +An earlier version of this PEP actually included such a mistake: it +stated "instance of type" for a constraint that is actually "subclass of +type". -Understanding the proposed class initialisation hook requires understanding -decorators and ordinary method inheritance, which is a much simpler prospect. +Understanding the proposed class initialisation hook only requires +understanding decorators and ordinary method inheritance, which isn't +quite as daunting a task. The new hook provides a more gradual path +towards understanding all of the phases involved in the class definition +process. Reduced chance of metaclass conflicts ------------------------------------- One of the big issues that makes library authors reluctant to use metaclasses -(even when it would be appropriate) is the risk of metaclass conflicts. +(even when they would be appropriate) is the risk of metaclass conflicts. These occur whenever two unrelated metaclasses are used by the desired parents of a class definition. This risk also makes it very difficult to *add* a metaclass to a class that has previously been published without one. @@ -164,12 +180,12 @@ is a risk of breaking poorly implemented subclasses, but when that occurs, it is recognised as a bug in the subclass rather than the library author breaching backwards compatibility guarantees. In fact, due to the constrained -signature, the risk in this case is actually even lower than in the case of -``__init__``. +signature of ``__init_class__``, the risk in this case is actually even +lower than in the case of ``__init__``. -Integrates cleanly with PEP 3135 --------------------------------- +Integrates cleanly with \PEP 3135 +--------------------------------- Unlike code that runs as part of the metaclass, code that runs as part of the new hook will be able to freely invoke class methods that rely on the @@ -280,6 +296,35 @@ ordinary class method invocation. +Automatic metaclass derivation +------------------------------ + +When no appropriate metaclass is found, it's theoretically possible to +automatically derive a metaclass for a new type based on the metaclass hint +and the metaclasses of the bases. + +While adding such a mechanism would reduce the risk of spurious metaclass +conflicts, it would do nothing to improve integration with PEP 3135, would +not help with porting Python 2 code that set ``__metaclass__`` dynamically +and would not provide a more straightforward inherited mechanism for invoking +additional operations after the class invocation is complete. + +In addition, there would still be a risk of metaclass conflicts in cases +where the base metaclasses were not written with multiple inheritance in +mind. In such situations, there's a chance of introducing latent defects +if one or more metaclasses are not invoked correctly. + + +Calling the new hook from ``type.__init__`` +------------------------------------------- + +Calling the new hook automatically from ``type.__init__``, would achieve most +of the goals of this PEP. However, using that approach would mean that +``__init_class__`` implementations would be unable to call any methods that +relied on the ``__class__`` reference (or used the zero-argument form of +``super()``), and could not make use of those features themselves. + + References ========== @@ -289,6 +334,8 @@ .. _more than 10 years ago: http://mail.python.org/pipermail/python-dev/2001-November/018651.html +.. _Zope's ExtensionClass: + http://docs.zope.org/zope_secrets/extensionclass.html Copyright ========= -- Repository URL: http://hg.python.org/peps From python-checkins at python.org Thu Jun 7 14:42:47 2012 From: python-checkins at python.org (nick.coghlan) Date: Thu, 07 Jun 2012 14:42:47 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=283=2E2=29=3A_Nudge_readers_t?= =?utf8?q?owards_a_more_accurate_mental_model_for_loop_else_clauses?= Message-ID: http://hg.python.org/cpython/rev/6e4ec47fba6a changeset: 77369:6e4ec47fba6a branch: 3.2 parent: 77363:aa9cfeea07ad user: Nick Coghlan date: Thu Jun 07 22:41:34 2012 +1000 summary: Nudge readers towards a more accurate mental model for loop else clauses files: Doc/tutorial/controlflow.rst | 7 +++++++ 1 files changed, 7 insertions(+), 0 deletions(-) diff --git a/Doc/tutorial/controlflow.rst b/Doc/tutorial/controlflow.rst --- a/Doc/tutorial/controlflow.rst +++ b/Doc/tutorial/controlflow.rst @@ -187,6 +187,13 @@ (Yes, this is the correct code. Look closely: the ``else`` clause belongs to the :keyword:`for` loop, **not** the :keyword:`if` statement.) +When used with a loop, the ``else`` clause has more in common with the +``else`` clause of a :keyword:`try` statement than it does that of +:keyword:`if` statements: a :keyword:`try` statement's ``else`` clause runs +when no exception occurs, and a loop's ``else`` clause runs when no ``break`` +occurs. For more on the :keyword:`try` statement and exceptions, see +:ref:`tut-handling`. + .. _tut-pass: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 7 14:42:47 2012 From: python-checkins at python.org (nick.coghlan) Date: Thu, 07 Jun 2012 14:42:47 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=28merge_3=2E2_-=3E_default=29?= =?utf8?q?=3A_Merge_from_3=2E2?= Message-ID: http://hg.python.org/cpython/rev/cedc68440a67 changeset: 77370:cedc68440a67 parent: 77368:e1b950cb6b74 parent: 77369:6e4ec47fba6a user: Nick Coghlan date: Thu Jun 07 22:42:29 2012 +1000 summary: Merge from 3.2 files: Doc/tutorial/controlflow.rst | 7 +++++++ 1 files changed, 7 insertions(+), 0 deletions(-) diff --git a/Doc/tutorial/controlflow.rst b/Doc/tutorial/controlflow.rst --- a/Doc/tutorial/controlflow.rst +++ b/Doc/tutorial/controlflow.rst @@ -187,6 +187,13 @@ (Yes, this is the correct code. Look closely: the ``else`` clause belongs to the :keyword:`for` loop, **not** the :keyword:`if` statement.) +When used with a loop, the ``else`` clause has more in common with the +``else`` clause of a :keyword:`try` statement than it does that of +:keyword:`if` statements: a :keyword:`try` statement's ``else`` clause runs +when no exception occurs, and a loop's ``else`` clause runs when no ``break`` +occurs. For more on the :keyword:`try` statement and exceptions, see +:ref:`tut-handling`. + .. _tut-pass: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 7 14:58:13 2012 From: python-checkins at python.org (nick.coghlan) Date: Thu, 07 Jun 2012 14:58:13 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=282=2E7=29=3A_Nudge_readers_t?= =?utf8?q?owards_a_more_accurate_mental_model_for_loop_else_clauses?= Message-ID: http://hg.python.org/cpython/rev/d79c837b6bf3 changeset: 77371:d79c837b6bf3 branch: 2.7 parent: 77315:477508efe4ab user: Nick Coghlan date: Thu Jun 07 22:57:35 2012 +1000 summary: Nudge readers towards a more accurate mental model for loop else clauses (Backport from 3.x) files: Doc/tutorial/controlflow.rst | 7 +++++++ 1 files changed, 7 insertions(+), 0 deletions(-) diff --git a/Doc/tutorial/controlflow.rst b/Doc/tutorial/controlflow.rst --- a/Doc/tutorial/controlflow.rst +++ b/Doc/tutorial/controlflow.rst @@ -159,6 +159,13 @@ (Yes, this is the correct code. Look closely: the ``else`` clause belongs to the :keyword:`for` loop, **not** the :keyword:`if` statement.) +When used with a loop, the ``else`` clause has more in common with the +``else`` clause of a :keyword:`try` statement than it does that of +:keyword:`if` statements: a :keyword:`try` statement's ``else`` clause runs +when no exception occurs, and a loop's ``else`` clause runs when no ``break`` +occurs. For more on the :keyword:`try` statement and exceptions, see +:ref:`tut-handling`. + .. _tut-pass: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 7 16:18:14 2012 From: python-checkins at python.org (brett.cannon) Date: Thu, 07 Jun 2012 16:18:14 +0200 Subject: [Python-checkins] =?utf8?q?peps=3A_Update_from_Yury_for_PEP_362?= =?utf8?q?=2E?= Message-ID: http://hg.python.org/peps/rev/f723e9fb778b changeset: 4457:f723e9fb778b user: Brett Cannon date: Thu Jun 07 10:18:08 2012 -0400 summary: Update from Yury for PEP 362. files: pep-0362.txt | 97 ++++++++++++++++++++++++--------------- 1 files changed, 60 insertions(+), 37 deletions(-) diff --git a/pep-0362.txt b/pep-0362.txt --- a/pep-0362.txt +++ b/pep-0362.txt @@ -55,10 +55,8 @@ as listed in ``code.co_varnames``). * bind(\*args, \*\*kwargs) -> BoundArguments Creates a mapping from positional and keyword arguments to - parameters. - -Once a Signature object is created for a particular function, -it's cached in the ``__signature__`` attribute of that function. + parameters. Raises a ``BindError`` if the passed arguments + do not match the signature. Changes to the Signature object, or to any of its data members, do not affect the function itself. @@ -86,19 +84,25 @@ True if the parameter is keyword-only, else False. * is_args : bool True if the parameter accepts variable number of arguments - (``\*args``-like), else False. + (``*args``-like), else False. * is_kwargs : bool True if the parameter accepts variable number of keyword - arguments (``\*\*kwargs``-like), else False. + arguments (``**kwargs``-like), else False. * is_implemented : bool True if the parameter is implemented for use. Some platforms implement functions but can't support specific parameters - (e.g. "mode" for os.mkdir). Passing in an unimplemented + (e.g. "mode" for ``os.mkdir``). Passing in an unimplemented parameter may result in the parameter being ignored, or in NotImplementedError being raised. It is intended that all conditions where ``is_implemented`` may be False be thoroughly documented. +Parameter objects support testing for equality. Two Parameter +objects are equal, when all their properties are equal. Those +who need to test if one signature has the same parameters as +another, can do a direct comparison of ``Signature.parameters`` +collections: ``signature(foo).parameters == signature(bar).parameters``. + BoundArguments Object ===================== @@ -135,16 +139,58 @@ Implementation ============== +The implementation adds a new function ``signature()`` to the ``inspect`` +module. The function is the preferred way of getting a ``Signature`` for +a callable object. + +The function implements the following algorithm: + + - If the object is not callable - raise a TypeError + + - If the object has a ``__signature__`` attribute and if it + is not ``None`` - return it + + - If it is ``None`` and the object is an instance of + ``BuiltinFunction``, raise a ``ValueError`` + + - If the object is a an instance of ``FunctionType``: + + - If it has a ``__wrapped__`` attribute, return + ``signature(object.__wrapped__)`` + + - Or else construct a new ``Signature`` object and return it + + - if the object is a method, construct and return a new ``Signature`` + object, with its first parameter (usually ``self``) removed + + - If the object is a class return ``signature(object.__init__)`` + + - Return ``signature(object.__call__)`` + +Note, that the ``Signature`` object is created in a lazy manner, and +is not automatically cached. + An implementation for Python 3.3 can be found here: [#impl]_. A python issue was also created: [#issue]_. -The implementation adds a new function ``signature()`` to the -``inspect`` module. ``signature()`` returns the value stored -on the ``__signature__`` attribute if it exists, otherwise it -creates the Signature object for the function and caches it in -the function's ``__signature__``. (For methods this is stored -directly in the ``__func__`` function object, since that is what -decorators work with.) + +Design Considerations +===================== + +No Implicit Caching of Signature Objects +---------------------------------------- + +The first PEP design had a provision for implicit caching of ``Signature`` +objects in the ``inspect.signature()`` function. However, this has the +following downsides: + + * If the ``Signature`` object is cached then any changes to the function + it describes will not be reflected in it. However, If the caching is + needed, it can be always done manually and explicitly + + * It is better to reserve the ``__signature__`` attribute for the cases + when there is a need to explicitly set to a ``Signature`` object that + is different from the actual one Examples @@ -311,31 +357,6 @@ return wrapper -Open Issues -=========== - -When to construct the Signature object? ---------------------------------------- - -The Signature object can either be created in an eager or lazy -fashion. In the eager situation, the object can be created during -creation of the function object. In the lazy situation, one would -pass a function object to a function and that would generate the -Signature object and store it to ``__signature__`` if -needed, and then return the value of ``__signature__``. - -In the current implementation, signatures are created only on demand -("lazy"). - - -Deprecate ``inspect.getfullargspec()`` and ``inspect.getcallargs()``? ---------------------------------------------------------------------- - -Since the Signature object replicates the use of ``getfullargspec()`` -and ``getcallargs()`` from the ``inspect`` module it might make sense -to begin deprecating them in 3.3. - - References ========== -- Repository URL: http://hg.python.org/peps From python-checkins at python.org Thu Jun 7 16:25:43 2012 From: python-checkins at python.org (brett.cannon) Date: Thu, 07 Jun 2012 16:25:43 +0200 Subject: [Python-checkins] =?utf8?q?peps=3A_Another_update_from_Yury_for_P?= =?utf8?q?EP_362=2E?= Message-ID: http://hg.python.org/peps/rev/13f360c76e50 changeset: 4458:13f360c76e50 user: Brett Cannon date: Thu Jun 07 10:25:42 2012 -0400 summary: Another update from Yury for PEP 362. files: pep-0362.txt | 9 +++++++-- 1 files changed, 7 insertions(+), 2 deletions(-) diff --git a/pep-0362.txt b/pep-0362.txt --- a/pep-0362.txt +++ b/pep-0362.txt @@ -160,11 +160,16 @@ - Or else construct a new ``Signature`` object and return it - - if the object is a method, construct and return a new ``Signature`` - object, with its first parameter (usually ``self``) removed + - if the object is a method or a classmethod, construct and return + a new ``Signature`` object, with its first parameter (usually + ``self`` or ``cls``) removed - If the object is a class return ``signature(object.__init__)`` + - If the object is an instance of ``functools.partial``, construct + a new ``Signature`` from its ``partial.func`` attribute, and + account for already bound ``partial.args`` and ``partial.kwargs`` + - Return ``signature(object.__call__)`` Note, that the ``Signature`` object is created in a lazy manner, and -- Repository URL: http://hg.python.org/peps From urban.dani+py at gmail.com Thu Jun 7 17:45:29 2012 From: urban.dani+py at gmail.com (Daniel Urban) Date: Thu, 7 Jun 2012 17:45:29 +0200 Subject: [Python-checkins] peps: Update 422 based on python-dev feedback In-Reply-To: References: Message-ID: On Thu, Jun 7, 2012 at 2:08 PM, nick.coghlan wrote: > -* If the metaclass hint refers to an instance of ``type``, then it is > +* If the metaclass hint refers to a subclass of ``type``, then it is > ? considered as a candidate metaclass along with the metaclasses of all of > ? the parents of the class being defined. If a more appropriate metaclass is > ? found amongst the candidates, then it will be used instead of the one I think here "instance" was correct (see http://hg.python.org/cpython/file/default/Lib/types.py#l76 and http://hg.python.org/cpython/file/cedc68440a67/Python/bltinmodule.c#l90). Daniel From python-checkins at python.org Thu Jun 7 17:50:40 2012 From: python-checkins at python.org (stefan.krah) Date: Thu, 07 Jun 2012 17:50:40 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_1=29_The_overflow_detection?= =?utf8?q?_in_mpd=5Fqln=28=29_has_a_surprising_number_of_case_splits=2E?= Message-ID: http://hg.python.org/cpython/rev/5588fe6874fa changeset: 77372:5588fe6874fa parent: 77370:cedc68440a67 user: Stefan Krah date: Thu Jun 07 17:48:47 2012 +0200 summary: 1) The overflow detection in mpd_qln() has a surprising number of case splits. List all of them in the comment. 2) Use the recently stated relative error of _mpd_qln() to generate the interval for the exact value of ln(x). See also the comment in mpd_qexp(). files: Modules/_decimal/libmpdec/mpdecimal.c | 26 ++++++++++---- 1 files changed, 19 insertions(+), 7 deletions(-) diff --git a/Modules/_decimal/libmpdec/mpdecimal.c b/Modules/_decimal/libmpdec/mpdecimal.c --- a/Modules/_decimal/libmpdec/mpdecimal.c +++ b/Modules/_decimal/libmpdec/mpdecimal.c @@ -4632,14 +4632,26 @@ _settriple(result, MPD_POS, 0, 0); return; } - /* Check if the result will overflow. + /* + * Check if the result will overflow (0 < x, x != 1): + * 1) log10(x) < 0 iff adjexp(x) < 0 + * 2) 0 < x /\ x <= y ==> adjexp(x) <= adjexp(y) + * 3) 0 < x /\ x != 1 ==> 2 * abs(log10(x)) < abs(log(x)) + * 4) adjexp(x) <= log10(x) < adjexp(x) + 1 * - * 1) adjexp(a) + 1 > log10(a) >= adjexp(a) + * Case adjexp(x) >= 0: + * 5) 2 * adjexp(x) < abs(log(x)) + * Case adjexp(x) > 0: + * 6) adjexp(2 * adjexp(x)) <= adjexp(abs(log(x))) + * Case adjexp(x) == 0: + * mpd_exp_digits(t)-1 == 0 <= emax (the shortcut is not triggered) * - * 2) |log10(a)| >= adjexp(a), if adjexp(a) >= 0 - * |log10(a)| > -adjexp(a)-1, if adjexp(a) < 0 - * - * 3) |log(a)| > 2*|log10(a)| + * Case adjexp(x) < 0: + * 7) 2 * (-adjexp(x) - 1) < abs(log(x)) + * Case adjexp(x) < -1: + * 8) adjexp(2 * (-adjexp(x) - 1)) <= adjexp(abs(log(x))) + * Case adjexp(x) == -1: + * mpd_exp_digits(t)-1 == 0 <= emax (the shortcut is not triggered) */ adjexp = mpd_adjexp(a); t = (adjexp < 0) ? -adjexp-1 : adjexp; @@ -4674,7 +4686,7 @@ workctx.prec = prec; _mpd_qln(result, a, &workctx, status); _ssettriple(&ulp, MPD_POS, 1, - result->exp + result->digits-workctx.prec-1); + result->exp + result->digits-workctx.prec); workctx.prec = ctx->prec; mpd_qadd(&t1, result, &ulp, &workctx, &workctx.status); -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 7 20:29:29 2012 From: python-checkins at python.org (alexander.belopolsky) Date: Thu, 07 Jun 2012 20:29:29 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Issue_=2311823=3A_disassemb?= =?utf8?q?ly_now_shows_argument_counts_on_calls_with_keyword_args?= Message-ID: http://hg.python.org/cpython/rev/22dc0a433b0e changeset: 77373:22dc0a433b0e user: Alexander Belopolsky date: Thu Jun 07 14:28:14 2012 -0400 summary: Issue #11823: disassembly now shows argument counts on calls with keyword args files: Lib/dis.py | 6 ++++++ Lib/opcode.py | 7 ++++++- Lib/test/test_dis.py | 6 +++--- Misc/NEWS | 2 ++ 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/Lib/dis.py b/Lib/dis.py --- a/Lib/dis.py +++ b/Lib/dis.py @@ -190,6 +190,9 @@ if free is None: free = co.co_cellvars + co.co_freevars print('(' + free[oparg] + ')', end=' ') + elif op in hasnargs: + print('(%d positional, %d keyword pair)' + % (code[i-2], code[i-1]), end=' ') print() def _disassemble_bytes(code, lasti=-1, varnames=None, names=None, @@ -229,6 +232,9 @@ print('(%d)' % oparg, end=' ') elif op in hascompare: print('(' + cmp_op[oparg] + ')', end=' ') + elif op in hasnargs: + print('(%d positional, %d keyword pair)' + % (code[i-2], code[i-1]), end=' ') print() def _disassemble_str(source): diff --git a/Lib/opcode.py b/Lib/opcode.py --- a/Lib/opcode.py +++ b/Lib/opcode.py @@ -6,7 +6,7 @@ __all__ = ["cmp_op", "hasconst", "hasname", "hasjrel", "hasjabs", "haslocal", "hascompare", "hasfree", "opname", "opmap", - "HAVE_ARGUMENT", "EXTENDED_ARG"] + "HAVE_ARGUMENT", "EXTENDED_ARG", "hasnargs"] cmp_op = ('<', '<=', '==', '!=', '>', '>=', 'in', 'not in', 'is', 'is not', 'exception match', 'BAD') @@ -18,6 +18,7 @@ haslocal = [] hascompare = [] hasfree = [] +hasnargs = [] opmap = {} opname = [''] * 256 @@ -152,6 +153,7 @@ def_op('RAISE_VARARGS', 130) # Number of raise arguments (1, 2, or 3) def_op('CALL_FUNCTION', 131) # #args + (#kwargs << 8) +hasnargs.append(131) def_op('MAKE_FUNCTION', 132) # Number of args with default values def_op('BUILD_SLICE', 133) # Number of items def_op('MAKE_CLOSURE', 134) @@ -165,8 +167,11 @@ hasfree.append(138) def_op('CALL_FUNCTION_VAR', 140) # #args + (#kwargs << 8) +hasnargs.append(140) def_op('CALL_FUNCTION_KW', 141) # #args + (#kwargs << 8) +hasnargs.append(141) def_op('CALL_FUNCTION_VAR_KW', 142) # #args + (#kwargs << 8) +hasnargs.append(142) jrel_op('SETUP_WITH', 143) diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -38,7 +38,7 @@ dis_f = """\ %-4d 0 LOAD_GLOBAL 0 (print) 3 LOAD_FAST 0 (a) - 6 CALL_FUNCTION 1 + 6 CALL_FUNCTION 1 (1 positional, 0 keyword pair) 9 POP_TOP %-4d 10 LOAD_CONST 1 (1) @@ -50,7 +50,7 @@ dis_f_co_code = """\ 0 LOAD_GLOBAL 0 (0) 3 LOAD_FAST 0 (0) - 6 CALL_FUNCTION 1 + 6 CALL_FUNCTION 1 (1 positional, 0 keyword pair) 9 POP_TOP 10 LOAD_CONST 1 (1) 13 RETURN_VALUE @@ -68,7 +68,7 @@ 6 LOAD_CONST 1 (1) %-4d 9 LOAD_CONST 2 (10) - 12 CALL_FUNCTION 2 + 12 CALL_FUNCTION 2 (2 positional, 0 keyword pair) 15 GET_ITER >> 16 FOR_ITER 6 (to 25) 19 STORE_FAST 0 (res) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -21,6 +21,8 @@ Library ------- +- Issue #11823: disassembly now shows argument counts on calls with keyword args. + - Issue #14711: os.stat_float_times() has been deprecated. - LZMAFile now accepts the modes "rb"/"wb"/"ab" as synonyms of "r"/"w"/"a". -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 7 21:42:02 2012 From: python-checkins at python.org (richard.oudkerk) Date: Thu, 07 Jun 2012 21:42:02 +0200 Subject: [Python-checkins] =?utf8?b?Y3B5dGhvbiAoMy4yKTogSXNzdWUgIzEyMTU3?= =?utf8?q?=3A_pool=2Emap=28=29_does_not_handle_empty_iterable_correctly?= Message-ID: http://hg.python.org/cpython/rev/1b3d4ffcb4d1 changeset: 77374:1b3d4ffcb4d1 branch: 3.2 parent: 77369:6e4ec47fba6a user: Richard Oudkerk date: Wed Jun 06 19:04:57 2012 +0100 summary: Issue #12157: pool.map() does not handle empty iterable correctly Initial patch by mouad files: Lib/multiprocessing/pool.py | 1 + Lib/test/test_multiprocessing.py | 18 +++++++++++++++--- Misc/NEWS | 3 +++ 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/Lib/multiprocessing/pool.py b/Lib/multiprocessing/pool.py --- a/Lib/multiprocessing/pool.py +++ b/Lib/multiprocessing/pool.py @@ -584,6 +584,7 @@ if chunksize <= 0: self._number_left = 0 self._ready = True + del cache[self._job] else: self._number_left = length//chunksize + bool(length % chunksize) diff --git a/Lib/test/test_multiprocessing.py b/Lib/test/test_multiprocessing.py --- a/Lib/test/test_multiprocessing.py +++ b/Lib/test/test_multiprocessing.py @@ -1178,6 +1178,18 @@ join() self.assertLess(join.elapsed, 0.5) + def test_empty_iterable(self): + # See Issue 12157 + p = self.Pool(1) + + self.assertEqual(p.map(sqr, []), []) + self.assertEqual(list(p.imap(sqr, [])), []) + self.assertEqual(list(p.imap_unordered(sqr, [])), []) + self.assertEqual(p.map_async(sqr, []).get(), []) + + p.close() + p.join() + def raising(): raise KeyError("key") @@ -2176,7 +2188,7 @@ 'Queue', 'Lock', 'RLock', 'Semaphore', 'BoundedSemaphore', 'Condition', 'Event', 'Value', 'Array', 'RawValue', 'RawArray', 'current_process', 'active_children', 'Pipe', - 'connection', 'JoinableQueue' + 'connection', 'JoinableQueue', 'Pool' ))) testcases_processes = create_test_cases(ProcessesMixin, type='processes') @@ -2190,7 +2202,7 @@ locals().update(get_attributes(manager, ( 'Queue', 'Lock', 'RLock', 'Semaphore', 'BoundedSemaphore', 'Condition', 'Event', 'Value', 'Array', 'list', 'dict', - 'Namespace', 'JoinableQueue' + 'Namespace', 'JoinableQueue', 'Pool' ))) testcases_manager = create_test_cases(ManagerMixin, type='manager') @@ -2204,7 +2216,7 @@ 'Queue', 'Lock', 'RLock', 'Semaphore', 'BoundedSemaphore', 'Condition', 'Event', 'Value', 'Array', 'current_process', 'active_children', 'Pipe', 'connection', 'dict', 'list', - 'Namespace', 'JoinableQueue' + 'Namespace', 'JoinableQueue', 'Pool' ))) testcases_threads = create_test_cases(ThreadsMixin, type='threads') diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -70,6 +70,9 @@ Library ------- +- Issue #12157: Make pool.map() empty iterables correctly. Initial + patch by mouad. + - Issue #14992: os.makedirs(path, exist_ok=True) would raise an OSError when the path existed and had the S_ISGID mode bit set when it was not explicitly asked for. This is no longer an exception as mkdir -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 7 21:42:03 2012 From: python-checkins at python.org (richard.oudkerk) Date: Thu, 07 Jun 2012 21:42:03 +0200 Subject: [Python-checkins] =?utf8?b?Y3B5dGhvbiAoMy4yKTogSXNzdWUgIzEzODU0?= =?utf8?q?=3A_Properly_handle_non-integer=2C_non-string_arg_to_SystemExit?= Message-ID: http://hg.python.org/cpython/rev/4346cba353b4 changeset: 77375:4346cba353b4 branch: 3.2 user: Richard Oudkerk date: Wed Jun 06 19:04:57 2012 +0100 summary: Issue #13854: Properly handle non-integer, non-string arg to SystemExit Previously multiprocessing only expected int or str. It also wrongly used an exit code of 1 when the argument was a string instead of zero. files: Lib/multiprocessing/process.py | 6 ++-- Lib/test/test_multiprocessing.py | 30 ++++++++++++++++++++ Misc/NEWS | 3 ++ 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/Lib/multiprocessing/process.py b/Lib/multiprocessing/process.py --- a/Lib/multiprocessing/process.py +++ b/Lib/multiprocessing/process.py @@ -271,11 +271,11 @@ except SystemExit as e: if not e.args: exitcode = 1 - elif type(e.args[0]) is int: + elif isinstance(e.args[0], int): exitcode = e.args[0] else: - sys.stderr.write(e.args[0] + '\n') - exitcode = 1 + sys.stderr.write(str(e.args[0]) + '\n') + exitcode = 0 if isinstance(e.args[0], str) else 1 except: exitcode = 1 import traceback diff --git a/Lib/test/test_multiprocessing.py b/Lib/test/test_multiprocessing.py --- a/Lib/test/test_multiprocessing.py +++ b/Lib/test/test_multiprocessing.py @@ -390,6 +390,36 @@ 1/0 # MARKER + @classmethod + def _test_sys_exit(cls, reason, testfn): + sys.stderr = open(testfn, 'w') + sys.exit(reason) + + def test_sys_exit(self): + # See Issue 13854 + if self.TYPE == 'threads': + return + + testfn = test.support.TESTFN + self.addCleanup(test.support.unlink, testfn) + + for reason, code in (([1, 2, 3], 1), ('ignore this', 0)): + p = self.Process(target=self._test_sys_exit, args=(reason, testfn)) + p.daemon = True + p.start() + p.join(5) + self.assertEqual(p.exitcode, code) + + with open(testfn, 'r') as f: + self.assertEqual(f.read().rstrip(), str(reason)) + + for reason in (True, False, 8): + p = self.Process(target=sys.exit, args=(reason,)) + p.daemon = True + p.start() + p.join(5) + self.assertEqual(p.exitcode, reason) + # # # diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -70,6 +70,9 @@ Library ------- +- Issue #13854: Make multiprocessing properly handle non-integer + non-string argument to SystemExit. + - Issue #12157: Make pool.map() empty iterables correctly. Initial patch by mouad. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 7 21:42:05 2012 From: python-checkins at python.org (richard.oudkerk) Date: Thu, 07 Jun 2012 21:42:05 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=28merge_3=2E2_-=3E_default=29?= =?utf8?q?=3A_Merge_fixes_for_=2313854_and_=2312157=2E?= Message-ID: http://hg.python.org/cpython/rev/3585cb1388f2 changeset: 77376:3585cb1388f2 parent: 77373:22dc0a433b0e parent: 77375:4346cba353b4 user: Richard Oudkerk date: Thu Jun 07 20:38:11 2012 +0100 summary: Merge fixes for #13854 and #12157. files: Lib/multiprocessing/pool.py | 1 + Lib/multiprocessing/process.py | 6 +- Lib/test/test_multiprocessing.py | 48 ++++++++++++++++++- Misc/NEWS | 6 ++ 4 files changed, 55 insertions(+), 6 deletions(-) diff --git a/Lib/multiprocessing/pool.py b/Lib/multiprocessing/pool.py --- a/Lib/multiprocessing/pool.py +++ b/Lib/multiprocessing/pool.py @@ -576,6 +576,7 @@ if chunksize <= 0: self._number_left = 0 self._event.set() + del cache[self._job] else: self._number_left = length//chunksize + bool(length % chunksize) diff --git a/Lib/multiprocessing/process.py b/Lib/multiprocessing/process.py --- a/Lib/multiprocessing/process.py +++ b/Lib/multiprocessing/process.py @@ -262,11 +262,11 @@ except SystemExit as e: if not e.args: exitcode = 1 - elif type(e.args[0]) is int: + elif isinstance(e.args[0], int): exitcode = e.args[0] else: - sys.stderr.write(e.args[0] + '\n') - exitcode = 1 + sys.stderr.write(str(e.args[0]) + '\n') + exitcode = 0 if isinstance(e.args[0], str) else 1 except: exitcode = 1 import traceback diff --git a/Lib/test/test_multiprocessing.py b/Lib/test/test_multiprocessing.py --- a/Lib/test/test_multiprocessing.py +++ b/Lib/test/test_multiprocessing.py @@ -439,6 +439,36 @@ 1/0 # MARKER + @classmethod + def _test_sys_exit(cls, reason, testfn): + sys.stderr = open(testfn, 'w') + sys.exit(reason) + + def test_sys_exit(self): + # See Issue 13854 + if self.TYPE == 'threads': + return + + testfn = test.support.TESTFN + self.addCleanup(test.support.unlink, testfn) + + for reason, code in (([1, 2, 3], 1), ('ignore this', 0)): + p = self.Process(target=self._test_sys_exit, args=(reason, testfn)) + p.daemon = True + p.start() + p.join(5) + self.assertEqual(p.exitcode, code) + + with open(testfn, 'r') as f: + self.assertEqual(f.read().rstrip(), str(reason)) + + for reason in (True, False, 8): + p = self.Process(target=sys.exit, args=(reason,)) + p.daemon = True + p.start() + p.join(5) + self.assertEqual(p.exitcode, reason) + # # # @@ -1342,6 +1372,18 @@ join() self.assertLess(join.elapsed, 0.5) + def test_empty_iterable(self): + # See Issue 12157 + p = self.Pool(1) + + self.assertEqual(p.map(sqr, []), []) + self.assertEqual(list(p.imap(sqr, [])), []) + self.assertEqual(list(p.imap_unordered(sqr, [])), []) + self.assertEqual(p.map_async(sqr, []).get(), []) + + p.close() + p.join() + def raising(): raise KeyError("key") @@ -2487,7 +2529,7 @@ 'Queue', 'Lock', 'RLock', 'Semaphore', 'BoundedSemaphore', 'Condition', 'Event', 'Value', 'Array', 'RawValue', 'RawArray', 'current_process', 'active_children', 'Pipe', - 'connection', 'JoinableQueue' + 'connection', 'JoinableQueue', 'Pool' ))) testcases_processes = create_test_cases(ProcessesMixin, type='processes') @@ -2501,7 +2543,7 @@ locals().update(get_attributes(manager, ( 'Queue', 'Lock', 'RLock', 'Semaphore', 'BoundedSemaphore', 'Condition', 'Event', 'Value', 'Array', 'list', 'dict', - 'Namespace', 'JoinableQueue' + 'Namespace', 'JoinableQueue', 'Pool' ))) testcases_manager = create_test_cases(ManagerMixin, type='manager') @@ -2515,7 +2557,7 @@ 'Queue', 'Lock', 'RLock', 'Semaphore', 'BoundedSemaphore', 'Condition', 'Event', 'Value', 'Array', 'current_process', 'active_children', 'Pipe', 'connection', 'dict', 'list', - 'Namespace', 'JoinableQueue' + 'Namespace', 'JoinableQueue', 'Pool' ))) testcases_threads = create_test_cases(ThreadsMixin, type='threads') diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -21,6 +21,12 @@ Library ------- +- Issue #13854: Make multiprocessing properly handle non-integer + non-string argument to SystemExit. + +- Issue #12157: Make pool.map() empty iterables correctly. Initial + patch by mouad. + - Issue #11823: disassembly now shows argument counts on calls with keyword args. - Issue #14711: os.stat_float_times() has been deprecated. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 7 21:42:06 2012 From: python-checkins at python.org (richard.oudkerk) Date: Thu, 07 Jun 2012 21:42:06 +0200 Subject: [Python-checkins] =?utf8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzEyMTU3?= =?utf8?q?=3A_pool=2Emap=28=29_does_not_handle_empty_iterable_correctly?= Message-ID: http://hg.python.org/cpython/rev/7ab7836894c4 changeset: 77377:7ab7836894c4 branch: 2.7 parent: 77371:d79c837b6bf3 user: Richard Oudkerk date: Wed Jun 06 17:52:18 2012 +0100 summary: Issue #12157: pool.map() does not handle empty iterable correctly Initial patch by mouad files: Lib/multiprocessing/pool.py | 1 + Lib/test/test_multiprocessing.py | 18 +++++++++++++++--- Misc/NEWS | 3 +++ 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/Lib/multiprocessing/pool.py b/Lib/multiprocessing/pool.py --- a/Lib/multiprocessing/pool.py +++ b/Lib/multiprocessing/pool.py @@ -576,6 +576,7 @@ if chunksize <= 0: self._number_left = 0 self._ready = True + del cache[self._job] else: self._number_left = length//chunksize + bool(length % chunksize) diff --git a/Lib/test/test_multiprocessing.py b/Lib/test/test_multiprocessing.py --- a/Lib/test/test_multiprocessing.py +++ b/Lib/test/test_multiprocessing.py @@ -1152,6 +1152,18 @@ join() self.assertTrue(join.elapsed < 0.2) + def test_empty_iterable(self): + # See Issue 12157 + p = self.Pool(1) + + self.assertEqual(p.map(sqr, []), []) + self.assertEqual(list(p.imap(sqr, [])), []) + self.assertEqual(list(p.imap_unordered(sqr, [])), []) + self.assertEqual(p.map_async(sqr, []).get(), []) + + p.close() + p.join() + def unpickleable_result(): return lambda: 42 @@ -2113,7 +2125,7 @@ 'Queue', 'Lock', 'RLock', 'Semaphore', 'BoundedSemaphore', 'Condition', 'Event', 'Value', 'Array', 'RawValue', 'RawArray', 'current_process', 'active_children', 'Pipe', - 'connection', 'JoinableQueue' + 'connection', 'JoinableQueue', 'Pool' ))) testcases_processes = create_test_cases(ProcessesMixin, type='processes') @@ -2127,7 +2139,7 @@ locals().update(get_attributes(manager, ( 'Queue', 'Lock', 'RLock', 'Semaphore', 'BoundedSemaphore', 'Condition', 'Event', 'Value', 'Array', 'list', 'dict', - 'Namespace', 'JoinableQueue' + 'Namespace', 'JoinableQueue', 'Pool' ))) testcases_manager = create_test_cases(ManagerMixin, type='manager') @@ -2141,7 +2153,7 @@ 'Queue', 'Lock', 'RLock', 'Semaphore', 'BoundedSemaphore', 'Condition', 'Event', 'Value', 'Array', 'current_process', 'active_children', 'Pipe', 'connection', 'dict', 'list', - 'Namespace', 'JoinableQueue' + 'Namespace', 'JoinableQueue', 'Pool' ))) testcases_threads = create_test_cases(ThreadsMixin, type='threads') diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -67,6 +67,9 @@ Library ------- +- Issue #12157: Make pool.map() empty iterables correctly. Initial + patch by mouad. + - Issue #14962: Update text coloring in IDLE shell window after changing options. Patch by Roger Serwy. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 7 21:42:07 2012 From: python-checkins at python.org (richard.oudkerk) Date: Thu, 07 Jun 2012 21:42:07 +0200 Subject: [Python-checkins] =?utf8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzEzODU0?= =?utf8?q?=3A_Properly_handle_non-integer=2C_non-string_arg_to_SystemExit?= Message-ID: http://hg.python.org/cpython/rev/da5b370f41a1 changeset: 77378:da5b370f41a1 branch: 2.7 user: Richard Oudkerk date: Wed Jun 06 19:01:14 2012 +0100 summary: Issue #13854: Properly handle non-integer, non-string arg to SystemExit Previously multiprocessing only expected int or str. It also wrongly used an exit code of 1 when the argument was a string instead of zero. files: Lib/multiprocessing/process.py | 6 ++-- Lib/test/test_multiprocessing.py | 30 ++++++++++++++++++++ Misc/NEWS | 3 ++ 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/Lib/multiprocessing/process.py b/Lib/multiprocessing/process.py --- a/Lib/multiprocessing/process.py +++ b/Lib/multiprocessing/process.py @@ -262,12 +262,12 @@ except SystemExit, e: if not e.args: exitcode = 1 - elif type(e.args[0]) is int: + elif isinstance(e.args[0], int): exitcode = e.args[0] else: - sys.stderr.write(e.args[0] + '\n') + sys.stderr.write(str(e.args[0]) + '\n') sys.stderr.flush() - exitcode = 1 + exitcode = 0 if isinstance(e.args[0], str) else 1 except: exitcode = 1 import traceback diff --git a/Lib/test/test_multiprocessing.py b/Lib/test/test_multiprocessing.py --- a/Lib/test/test_multiprocessing.py +++ b/Lib/test/test_multiprocessing.py @@ -325,6 +325,36 @@ ] self.assertEqual(result, expected) + @classmethod + def _test_sys_exit(cls, reason, testfn): + sys.stderr = open(testfn, 'w') + sys.exit(reason) + + def test_sys_exit(self): + # See Issue 13854 + if self.TYPE == 'threads': + return + + testfn = test_support.TESTFN + self.addCleanup(test_support.unlink, testfn) + + for reason, code in (([1, 2, 3], 1), ('ignore this', 0)): + p = self.Process(target=self._test_sys_exit, args=(reason, testfn)) + p.daemon = True + p.start() + p.join(5) + self.assertEqual(p.exitcode, code) + + with open(testfn, 'r') as f: + self.assertEqual(f.read().rstrip(), str(reason)) + + for reason in (True, False, 8): + p = self.Process(target=sys.exit, args=(reason,)) + p.daemon = True + p.start() + p.join(5) + self.assertEqual(p.exitcode, reason) + # # # diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -67,6 +67,9 @@ Library ------- +- Issue #13854: Make multiprocessing properly handle non-integer + non-string argument to SystemExit. + - Issue #12157: Make pool.map() empty iterables correctly. Initial patch by mouad. -- Repository URL: http://hg.python.org/cpython From tjreedy at udel.edu Thu Jun 7 21:47:01 2012 From: tjreedy at udel.edu (Terry Reedy) Date: Thu, 07 Jun 2012 15:47:01 -0400 Subject: [Python-checkins] peps: Update 422 based on python-dev feedback In-Reply-To: References: Message-ID: On 6/7/2012 11:45 AM, Daniel Urban wrote: > On Thu, Jun 7, 2012 at 2:08 PM, nick.coghlan wrote: >> -* If the metaclass hint refers to an instance of ``type``, then it is >> +* If the metaclass hint refers to a subclass of ``type``, then it is >> considered as a candidate metaclass along with the metaclasses of all of >> the parents of the class being defined. If a more appropriate metaclass is >> found amongst the candidates, then it will be used instead of the one > > I think here "instance" was correct (see > http://hg.python.org/cpython/file/default/Lib/types.py#l76 and > http://hg.python.org/cpython/file/cedc68440a67/Python/bltinmodule.c#l90). If so, then the behavior of the standard case of a type subclass is not obviously (to me) covered. -- Terry Jan Reedy From urban.dani+py at gmail.com Thu Jun 7 22:17:15 2012 From: urban.dani+py at gmail.com (Daniel Urban) Date: Thu, 7 Jun 2012 22:17:15 +0200 Subject: [Python-checkins] peps: Update 422 based on python-dev feedback In-Reply-To: References: Message-ID: On Thu, Jun 7, 2012 at 9:47 PM, Terry Reedy wrote: > On 6/7/2012 11:45 AM, Daniel Urban wrote: >> >> On Thu, Jun 7, 2012 at 2:08 PM, nick.coghlan >> ?wrote: >>> >>> -* If the metaclass hint refers to an instance of ``type``, then it is >>> +* If the metaclass hint refers to a subclass of ``type``, then it is >>> ? considered as a candidate metaclass along with the metaclasses of all >>> of >>> ? the parents of the class being defined. If a more appropriate metaclass >>> is >>> ? found amongst the candidates, then it will be used instead of the one >> >> >> I think here "instance" was correct (see >> http://hg.python.org/cpython/file/default/Lib/types.py#l76 and >> http://hg.python.org/cpython/file/cedc68440a67/Python/bltinmodule.c#l90). > > > If so, then the behavior of the standard case of a type subclass is not > obviously (to me) covered. A subclass of type is also necessarily an instance of type, so that is also covered by this case. Daniel From python-checkins at python.org Fri Jun 8 02:05:12 2012 From: python-checkins at python.org (terry.reedy) Date: Fri, 08 Jun 2012 02:05:12 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=282=2E7=29=3A_NEWS_fixes?= Message-ID: http://hg.python.org/cpython/rev/063f125dc0f7 changeset: 77379:063f125dc0f7 branch: 2.7 user: Terry Jan Reedy date: Thu Jun 07 19:50:30 2012 -0400 summary: NEWS fixes files: Lib/idlelib/NEWS.txt | 15 +++++++++++++-- Misc/NEWS | 2 +- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt --- a/Lib/idlelib/NEWS.txt +++ b/Lib/idlelib/NEWS.txt @@ -1,5 +1,16 @@ +What's New in IDLE 2.7.4? +========================= + +- Issue # 12510: Attempt to get certain tool tips no longer crashes IDLE. + +- Issue10365: File open dialog now works instead of crashing even when + parent window is closed while dialog is open. + +- Issue 14876: use user-selected font for highlight configuration. + + What's New in IDLE 2.7.3? -======================= +========================= - Issue #14409: IDLE now properly executes commands in the Shell window when it cannot read the normal config files on startup and @@ -11,7 +22,7 @@ What's New in IDLE 2.7.2? -======================= +========================= *Release date: 29-May-2011* diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -78,7 +78,7 @@ - Issue #10997: Prevent a duplicate entry in IDLE's "Recent Files" menu. -- Issue12510: Attempting to get invalid tooltip no longer closes Idle. +- Issue #12510: Attempting to get invalid tooltip no longer closes Idle. Original patch by Roger Serwy. - Issue #10365: File open dialog now works instead of crashing -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 8 02:05:13 2012 From: python-checkins at python.org (terry.reedy) Date: Fri, 08 Jun 2012 02:05:13 +0200 Subject: [Python-checkins] =?utf8?b?Y3B5dGhvbiAoMy4yKTogSXNzdWUgIzEyNTEw?= =?utf8?q?=3A_Revise_and_triple_=23_of_calltip_tests=2C_with_an_eye_to_uni?= =?utf8?q?ttest?= Message-ID: http://hg.python.org/cpython/rev/02b4c62ce393 changeset: 77380:02b4c62ce393 branch: 3.2 parent: 77375:4346cba353b4 user: Terry Jan Reedy date: Thu Jun 07 19:41:04 2012 -0400 summary: Issue #12510: Revise and triple # of calltip tests, with an eye to unittest use. Make the get_entity 'method' a module function as it did not use 'self'. Delete buggy _find_constructor function that is not needed, at least in 3.x. Revise get_argspec so all tests pass. Add and fix NEWS entries. files: Lib/idlelib/CallTips.py | 191 +++++++++++++++++---------- Lib/idlelib/NEWS.txt | 8 + Misc/NEWS | 17 +- 3 files changed, 139 insertions(+), 77 deletions(-) diff --git a/Lib/idlelib/CallTips.py b/Lib/idlelib/CallTips.py --- a/Lib/idlelib/CallTips.py +++ b/Lib/idlelib/CallTips.py @@ -100,52 +100,53 @@ return rpcclt.remotecall("exec", "get_the_calltip", (expression,), {}) else: - entity = self.get_entity(expression) - return get_argspec(entity) + return get_argspec(get_entity(expression)) - def get_entity(self, expression): - """Return the object corresponding to expression evaluated - in a namespace spanning sys.modules and __main.dict__. - """ - if expression: - namespace = sys.modules.copy() - namespace.update(__main__.__dict__) - try: - return eval(expression, namespace) - except BaseException: - # An uncaught exception closes idle, and eval can raise any - # exception, especially if user classes are involved. - return None +def get_entity(expression): + """Return the object corresponding to expression evaluated + in a namespace spanning sys.modules and __main.dict__. + """ + if expression: + namespace = sys.modules.copy() + namespace.update(__main__.__dict__) + try: + return eval(expression, namespace) + except BaseException: + # An uncaught exception closes idle, and eval can raise any + # exception, especially if user classes are involved. + return None -def _find_constructor(class_ob): - "Find the nearest __init__() in the class tree." - try: - return class_ob.__init__.__func__ - except AttributeError: - for base in class_ob.__bases__: - init = _find_constructor(base) - if init: - return init - return None +# The following are used in both get_argspec and tests +_self_pat = re.compile('self\,?\s*') +_default_callable_argspec = "No docstring, see docs." def get_argspec(ob): - """Get a string describing the arguments for the given object, - only if it is callable.""" + '''Return a string describing the arguments and return of a callable object. + + For Python-coded functions and methods, the first line is introspected. + Delete 'self' parameter for classes (.__init__) and bound methods. + The last line is the first line of the doc string. For builtins, this typically + includes the arguments in addition to the return value. + + ''' argspec = "" - if ob is not None and hasattr(ob, '__call__'): + if hasattr(ob, '__call__'): if isinstance(ob, type): - fob = _find_constructor(ob) - if fob is None: - fob = lambda: None - elif isinstance(ob, types.MethodType): - fob = ob.__func__ + fob = getattr(ob, '__init__', None) + elif isinstance(ob.__call__, types.MethodType): + fob = ob.__call__ else: fob = ob - if isinstance(fob, (types.FunctionType, types.LambdaType)): + if isinstance(fob, (types.FunctionType, types.MethodType)): argspec = inspect.formatargspec(*inspect.getfullargspec(fob)) - pat = re.compile('self\,?\s*') - argspec = pat.sub("", argspec) - doc = getattr(ob, "__doc__", "") + if (isinstance(ob, (type, types.MethodType)) or + isinstance(ob.__call__, types.MethodType)): + argspec = _self_pat.sub("", argspec) + + if isinstance(ob.__call__, types.MethodType): + doc = ob.__call__.__doc__ + else: + doc = getattr(ob, "__doc__", "") if doc: doc = doc.lstrip() pos = doc.find("\n") @@ -154,13 +155,16 @@ if argspec: argspec += "\n" argspec += doc[:pos] + if not argspec: + argspec = _default_callable_argspec return argspec ################################################# # -# Test code -# +# Test code tests CallTips.fetch_tip, get_entity, and get_argspec + def main(): + # Putting expected in docstrings results in doubled tips for test def t1(): "()" def t2(a, b=None): "(a, b=None)" def t3(a, *args): "(a, *args)" @@ -170,39 +174,88 @@ class TC(object): "(ai=None, *b)" - def __init__(self, ai=None, *b): "(ai=None, *b)" - def t1(self): "()" - def t2(self, ai, b=None): "(ai, b=None)" - def t3(self, ai, *args): "(ai, *args)" - def t4(self, *args): "(*args)" - def t5(self, ai, *args): "(ai, *args)" - def t6(self, ai, b=None, *args, **kw): "(ai, b=None, *args, **kw)" - - __main__.__dict__.update(locals()) - - def test(tests): - ct = CallTips() - failed=[] - for t in tests: - expected = t.__doc__ + "\n" + t.__doc__ - name = t.__name__ - # exercise fetch_tip(), not just get_argspec() - try: - qualified_name = "%s.%s" % (t.__self__.__class__.__name__, name) - except AttributeError: - qualified_name = name - argspec = ct.fetch_tip(qualified_name) - if argspec != expected: - failed.append(t) - fmt = "%s - expected %s, but got %s" - print(fmt % (t.__name__, expected, get_argspec(t))) - print("%d of %d tests failed" % (len(failed), len(tests))) + def __init__(self, ai=None, *b): "(self, ai=None, *b)" + def t1(self): "(self)" + def t2(self, ai, b=None): "(self, ai, b=None)" + def t3(self, ai, *args): "(self, ai, *args)" + def t4(self, *args): "(self, *args)" + def t5(self, ai, *args): "(self, ai, *args)" + def t6(self, ai, b=None, *args, **kw): "(self, ai, b=None, *args, **kw)" + @classmethod + def cm(cls, a): "(cls, a)" + @staticmethod + def sm(b): "(b)" + def __call__(self, ci): "(ci)" tc = TC() - tests = (t1, t2, t3, t4, t5, t6, - TC, tc.t1, tc.t2, tc.t3, tc.t4, tc.t5, tc.t6) - test(tests) + # Python classes that inherit builtin methods + class Int(int): "Int(x[, base]) -> integer" + class List(list): "List() -> new empty list" + # Simulate builtin with no docstring for default argspec test + class SB: __call__ = None + + __main__.__dict__.update(locals()) # required for get_entity eval() + + num_tests = num_fail = 0 + tip = CallTips().fetch_tip + + def test(expression, expected): + nonlocal num_tests, num_fail + num_tests += 1 + argspec = tip(expression) + if argspec != expected: + num_fail += 1 + fmt = "%s - expected\n%r\n - but got\n%r" + print(fmt % (expression, expected, argspec)) + + def test_builtins(): + # if first line of a possibly multiline compiled docstring changes, + # must change corresponding test string + test('int', "int(x[, base]) -> integer") + test('Int', Int.__doc__) + test('types.MethodType', "method(function, instance)") + test('list', "list() -> new empty list") + test('List', List.__doc__) + test('list.__new__', + 'T.__new__(S, ...) -> a new object with type S, a subtype of T') + test('list.__init__', + 'x.__init__(...) initializes x; see help(type(x)) for signature') + append_doc = "L.append(object) -> None -- append object to end" + test('list.append', append_doc) + test('[].append', append_doc) + test('List.append', append_doc) + test('SB()', _default_callable_argspec) + + def test_funcs(): + for func in (t1, t2, t3, t4, t5, t6, TC,): + fdoc = func.__doc__ + test(func.__name__, fdoc + "\n" + fdoc) + for func in (TC.t1, TC.t2, TC.t3, TC.t4, TC.t5, TC.t6, TC.cm, TC.sm): + fdoc = func.__doc__ + test('TC.'+func.__name__, fdoc + "\n" + fdoc) + + def test_methods(): + for func in (tc.t1, tc.t2, tc.t3, tc.t4, tc.t5, tc.t6): + fdoc = func.__doc__ + test('tc.'+func.__name__, _self_pat.sub("", fdoc) + "\n" + fdoc) + fdoc = tc.__call__.__doc__ + test('tc', fdoc + "\n" + fdoc) + + def test_non_callables(): + # expression evaluates, but not to a callable + for expr in ('0', '0.0' 'num_tests', b'num_tests', '[]', '{}'): + test(expr, '') + # expression does not evaluate, but raises an exception + for expr in ('1a', 'xyx', 'num_tests.xyz', '[int][1]', '{0:int}[1]'): + test(expr, '') + + test_builtins() + test_funcs() + test_non_callables() + test_methods() + + print("%d of %d tests failed" % (num_fail, num_tests)) if __name__ == '__main__': main() diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt --- a/Lib/idlelib/NEWS.txt +++ b/Lib/idlelib/NEWS.txt @@ -1,6 +1,14 @@ What's New in IDLE 3.2.4? ========================= +- Issue # 12510: Attempt to get certain tool tips no longer crashes IDLE. + Erroneous tool tips have been corrected. Default added for callables. + +- Issue10365: File open dialog now works instead of crashing even when + parent window is closed while dialog is open. + +- Issue 14876: use user-selected font for highlight configuration. + - Issue #14937: Perform auto-completion of filenames in strings even for non-ASCII filenames. Likewise for identifiers. diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -89,14 +89,15 @@ - Issue #14443: Tell rpmbuild to use the correct version of Python in bdist_rpm. Initial patch by Ross Lagerwall. -- Issue14929: Stop Idle 3.x from closing on Unicode decode errors when grepping. - Patch by Roger Serwy. - -- Issue12510: Attempting to get invalid tooltip no longer closes Idle. - Original patch by Roger Serwy. - -- Issue #10365: File open dialog now works instead of crashing - even when parent window is closed. Patch by Roger Serwy. +- Issue #14929: Stop Idle 3.x from closing on Unicode decode errors when + grepping. Patch by Roger Serwy. + +- Issue #12510: Attempting to get invalid tooltip no longer closes Idle. + Other tooltipss have been corrected or improved and the number of tests + has been tripled. Original patch by Roger Serwy. + +- Issue #10365: File open dialog now works instead of crashing even when + the parent window is closed before the dialog. Patch by Roger Serwy. - Issue #14876: Use user-selected font for highlight configuration. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 8 02:05:14 2012 From: python-checkins at python.org (terry.reedy) Date: Fri, 08 Jun 2012 02:05:14 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=28merge_3=2E2_-=3E_default=29?= =?utf8?q?=3A_Merge_from_3=2E2=2C_=2312510?= Message-ID: http://hg.python.org/cpython/rev/03b5f75ddac7 changeset: 77381:03b5f75ddac7 parent: 77376:3585cb1388f2 parent: 77380:02b4c62ce393 user: Terry Jan Reedy date: Thu Jun 07 20:04:17 2012 -0400 summary: Merge from 3.2, #12510 files: Lib/idlelib/CallTips.py | 191 +++++++++++++++++---------- Lib/idlelib/NEWS.txt | 8 + Misc/NEWS | 11 +- 3 files changed, 136 insertions(+), 74 deletions(-) diff --git a/Lib/idlelib/CallTips.py b/Lib/idlelib/CallTips.py --- a/Lib/idlelib/CallTips.py +++ b/Lib/idlelib/CallTips.py @@ -100,52 +100,53 @@ return rpcclt.remotecall("exec", "get_the_calltip", (expression,), {}) else: - entity = self.get_entity(expression) - return get_argspec(entity) + return get_argspec(get_entity(expression)) - def get_entity(self, expression): - """Return the object corresponding to expression evaluated - in a namespace spanning sys.modules and __main.dict__. - """ - if expression: - namespace = sys.modules.copy() - namespace.update(__main__.__dict__) - try: - return eval(expression, namespace) - except BaseException: - # An uncaught exception closes idle, and eval can raise any - # exception, especially if user classes are involved. - return None +def get_entity(expression): + """Return the object corresponding to expression evaluated + in a namespace spanning sys.modules and __main.dict__. + """ + if expression: + namespace = sys.modules.copy() + namespace.update(__main__.__dict__) + try: + return eval(expression, namespace) + except BaseException: + # An uncaught exception closes idle, and eval can raise any + # exception, especially if user classes are involved. + return None -def _find_constructor(class_ob): - "Find the nearest __init__() in the class tree." - try: - return class_ob.__init__.__func__ - except AttributeError: - for base in class_ob.__bases__: - init = _find_constructor(base) - if init: - return init - return None +# The following are used in both get_argspec and tests +_self_pat = re.compile('self\,?\s*') +_default_callable_argspec = "No docstring, see docs." def get_argspec(ob): - """Get a string describing the arguments for the given object, - only if it is callable.""" + '''Return a string describing the arguments and return of a callable object. + + For Python-coded functions and methods, the first line is introspected. + Delete 'self' parameter for classes (.__init__) and bound methods. + The last line is the first line of the doc string. For builtins, this typically + includes the arguments in addition to the return value. + + ''' argspec = "" - if ob is not None and hasattr(ob, '__call__'): + if hasattr(ob, '__call__'): if isinstance(ob, type): - fob = _find_constructor(ob) - if fob is None: - fob = lambda: None - elif isinstance(ob, types.MethodType): - fob = ob.__func__ + fob = getattr(ob, '__init__', None) + elif isinstance(ob.__call__, types.MethodType): + fob = ob.__call__ else: fob = ob - if isinstance(fob, (types.FunctionType, types.LambdaType)): + if isinstance(fob, (types.FunctionType, types.MethodType)): argspec = inspect.formatargspec(*inspect.getfullargspec(fob)) - pat = re.compile('self\,?\s*') - argspec = pat.sub("", argspec) - doc = getattr(ob, "__doc__", "") + if (isinstance(ob, (type, types.MethodType)) or + isinstance(ob.__call__, types.MethodType)): + argspec = _self_pat.sub("", argspec) + + if isinstance(ob.__call__, types.MethodType): + doc = ob.__call__.__doc__ + else: + doc = getattr(ob, "__doc__", "") if doc: doc = doc.lstrip() pos = doc.find("\n") @@ -154,13 +155,16 @@ if argspec: argspec += "\n" argspec += doc[:pos] + if not argspec: + argspec = _default_callable_argspec return argspec ################################################# # -# Test code -# +# Test code tests CallTips.fetch_tip, get_entity, and get_argspec + def main(): + # Putting expected in docstrings results in doubled tips for test def t1(): "()" def t2(a, b=None): "(a, b=None)" def t3(a, *args): "(a, *args)" @@ -170,39 +174,88 @@ class TC(object): "(ai=None, *b)" - def __init__(self, ai=None, *b): "(ai=None, *b)" - def t1(self): "()" - def t2(self, ai, b=None): "(ai, b=None)" - def t3(self, ai, *args): "(ai, *args)" - def t4(self, *args): "(*args)" - def t5(self, ai, *args): "(ai, *args)" - def t6(self, ai, b=None, *args, **kw): "(ai, b=None, *args, **kw)" - - __main__.__dict__.update(locals()) - - def test(tests): - ct = CallTips() - failed=[] - for t in tests: - expected = t.__doc__ + "\n" + t.__doc__ - name = t.__name__ - # exercise fetch_tip(), not just get_argspec() - try: - qualified_name = "%s.%s" % (t.__self__.__class__.__name__, name) - except AttributeError: - qualified_name = name - argspec = ct.fetch_tip(qualified_name) - if argspec != expected: - failed.append(t) - fmt = "%s - expected %s, but got %s" - print(fmt % (t.__name__, expected, get_argspec(t))) - print("%d of %d tests failed" % (len(failed), len(tests))) + def __init__(self, ai=None, *b): "(self, ai=None, *b)" + def t1(self): "(self)" + def t2(self, ai, b=None): "(self, ai, b=None)" + def t3(self, ai, *args): "(self, ai, *args)" + def t4(self, *args): "(self, *args)" + def t5(self, ai, *args): "(self, ai, *args)" + def t6(self, ai, b=None, *args, **kw): "(self, ai, b=None, *args, **kw)" + @classmethod + def cm(cls, a): "(cls, a)" + @staticmethod + def sm(b): "(b)" + def __call__(self, ci): "(ci)" tc = TC() - tests = (t1, t2, t3, t4, t5, t6, - TC, tc.t1, tc.t2, tc.t3, tc.t4, tc.t5, tc.t6) - test(tests) + # Python classes that inherit builtin methods + class Int(int): "Int(x[, base]) -> integer" + class List(list): "List() -> new empty list" + # Simulate builtin with no docstring for default argspec test + class SB: __call__ = None + + __main__.__dict__.update(locals()) # required for get_entity eval() + + num_tests = num_fail = 0 + tip = CallTips().fetch_tip + + def test(expression, expected): + nonlocal num_tests, num_fail + num_tests += 1 + argspec = tip(expression) + if argspec != expected: + num_fail += 1 + fmt = "%s - expected\n%r\n - but got\n%r" + print(fmt % (expression, expected, argspec)) + + def test_builtins(): + # if first line of a possibly multiline compiled docstring changes, + # must change corresponding test string + test('int', "int(x[, base]) -> integer") + test('Int', Int.__doc__) + test('types.MethodType', "method(function, instance)") + test('list', "list() -> new empty list") + test('List', List.__doc__) + test('list.__new__', + 'T.__new__(S, ...) -> a new object with type S, a subtype of T') + test('list.__init__', + 'x.__init__(...) initializes x; see help(type(x)) for signature') + append_doc = "L.append(object) -> None -- append object to end" + test('list.append', append_doc) + test('[].append', append_doc) + test('List.append', append_doc) + test('SB()', _default_callable_argspec) + + def test_funcs(): + for func in (t1, t2, t3, t4, t5, t6, TC,): + fdoc = func.__doc__ + test(func.__name__, fdoc + "\n" + fdoc) + for func in (TC.t1, TC.t2, TC.t3, TC.t4, TC.t5, TC.t6, TC.cm, TC.sm): + fdoc = func.__doc__ + test('TC.'+func.__name__, fdoc + "\n" + fdoc) + + def test_methods(): + for func in (tc.t1, tc.t2, tc.t3, tc.t4, tc.t5, tc.t6): + fdoc = func.__doc__ + test('tc.'+func.__name__, _self_pat.sub("", fdoc) + "\n" + fdoc) + fdoc = tc.__call__.__doc__ + test('tc', fdoc + "\n" + fdoc) + + def test_non_callables(): + # expression evaluates, but not to a callable + for expr in ('0', '0.0' 'num_tests', b'num_tests', '[]', '{}'): + test(expr, '') + # expression does not evaluate, but raises an exception + for expr in ('1a', 'xyx', 'num_tests.xyz', '[int][1]', '{0:int}[1]'): + test(expr, '') + + test_builtins() + test_funcs() + test_non_callables() + test_methods() + + print("%d of %d tests failed" % (num_fail, num_tests)) if __name__ == '__main__': main() diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt --- a/Lib/idlelib/NEWS.txt +++ b/Lib/idlelib/NEWS.txt @@ -1,6 +1,14 @@ What's New in IDLE 3.3.0? ========================= +- Issue # 12510: Attempt to get certain tool tips no longer crashes IDLE. + Erroneous tool tips have been corrected. Default added for callables. + +- Issue10365: File open dialog now works instead of crashing even when + parent window is closed while dialog is open. + +- Issue 14876: use user-selected font for highlight configuration. + - Issue #14937: Perform auto-completion of filenames in strings even for non-ASCII filenames. Likewise for identifiers. diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -144,14 +144,15 @@ - Issue #14443: Tell rpmbuild to use the correct version of Python in bdist_rpm. Initial patch by Ross Lagerwall. -- Issue #14929: Stop Idle 3.x from closing on Unicode decode errors when grepping. - Patch by Roger Serwy. +- Issue #14929: Stop Idle 3.x from closing on Unicode decode errors when + grepping. Patch by Roger Serwy. - Issue #12515: email now registers a defect if it gets to EOF while parsing a MIME part without seeing the closing MIME boundary. - Issue #12510: Attempting to get invalid tooltip no longer closes Idle. - Original patch by Roger Serwy. + Other tooltipss have been corrected or improved and the number of tests + has been tripled. Original patch by Roger Serwy. - Issue #1672568: email now always decodes base64 payloads, adding padding and ignoring non-base64-alphabet characters if needed, and registering defects @@ -161,8 +162,8 @@ is a missing header/body separator line. MalformedHeaderDefect, which the existing code would never actually generate, is deprecated. -- Issue #10365: File open dialog now works instead of crashing - even when parent window is closed. Patch by Roger Serwy. +- Issue #10365: File open dialog now works instead of crashing even when + the parent window is closed before the dialog. Patch by Roger Serwy. - Issue #8739: Updated smtpd to support RFC 5321, and added support for the RFC 1870 SIZE extension. -- Repository URL: http://hg.python.org/cpython From ncoghlan at gmail.com Fri Jun 8 03:10:26 2012 From: ncoghlan at gmail.com (Nick Coghlan) Date: Fri, 8 Jun 2012 11:10:26 +1000 Subject: [Python-checkins] peps: Update 422 based on python-dev feedback In-Reply-To: References: Message-ID: On Fri, Jun 8, 2012 at 1:45 AM, Daniel Urban wrote: > On Thu, Jun 7, 2012 at 2:08 PM, nick.coghlan wrote: >> -* If the metaclass hint refers to an instance of ``type``, then it is >> +* If the metaclass hint refers to a subclass of ``type``, then it is >> ? considered as a candidate metaclass along with the metaclasses of all of >> ? the parents of the class being defined. If a more appropriate metaclass is >> ? found amongst the candidates, then it will be used instead of the one > > I think here "instance" was correct (see > http://hg.python.org/cpython/file/default/Lib/types.py#l76 and > http://hg.python.org/cpython/file/cedc68440a67/Python/bltinmodule.c#l90). Hmm, thinking back on it, the REPL experiments that persuaded me Terry was right were flawed (I tried with object directly, but the signature of __new__/__init__ would have been wrong regardless in that case). Still, I'm kinda proving my point that I find it difficult to keep *all* the details of metaclass invocation straight in my head, even though I've been hacking on the type system for years. I've never had anything even close to that kind of problem with class methods :) Cheers, Nick. -- Nick Coghlan?? |?? ncoghlan at gmail.com?? |?? Brisbane, Australia From python-checkins at python.org Fri Jun 8 03:47:08 2012 From: python-checkins at python.org (r.david.murray) Date: Fri, 08 Jun 2012 03:47:08 +0200 Subject: [Python-checkins] =?utf8?b?Y3B5dGhvbiAoMi43KTogIzg2NTI6IHVwZGF0?= =?utf8?q?e_errors_tutorial=2E?= Message-ID: http://hg.python.org/cpython/rev/b873afe640e2 changeset: 77382:b873afe640e2 branch: 2.7 parent: 77379:063f125dc0f7 user: R David Murray date: Thu Jun 07 21:46:44 2012 -0400 summary: #8652: update errors tutorial. The tutorial had some outdated examples. The patch also adds a caution about the meaning of parens in the except statement. Patch by Marien Zwart. files: Doc/tutorial/errors.rst | 14 +++++++++++--- 1 files changed, 11 insertions(+), 3 deletions(-) diff --git a/Doc/tutorial/errors.rst b/Doc/tutorial/errors.rst --- a/Doc/tutorial/errors.rst +++ b/Doc/tutorial/errors.rst @@ -120,6 +120,14 @@ ... except (RuntimeError, TypeError, NameError): ... pass +Note that the parentheses around this tuple are required, because +``except ValueError, e:`` was the syntax used for what is normally +written as ``except ValueError as e:`` in modern Python (described +below). The old syntax is still supported for backwards compatibility. +This means ``except RuntimeError, TypeError`` is not equivalent to +``except (RuntimeError, TypeError):`` but to ``except RuntimeError as +TypeError:`` which is not what you want. + The last except clause may omit the exception name(s), to serve as a wildcard. Use this with extreme caution, since it is easy to mask a real programming error in this way! It can also be used to print an error message and then re-raise @@ -131,8 +139,8 @@ f = open('myfile.txt') s = f.readline() i = int(s.strip()) - except IOError as (errno, strerror): - print "I/O error({0}): {1}".format(errno, strerror) + except IOError as e: + print "I/O error({0}): {1}".format(e.errno, e.strerror) except ValueError: print "Could not convert data to an integer." except: @@ -177,7 +185,7 @@ ... print type(inst) # the exception instance ... print inst.args # arguments stored in .args ... print inst # __str__ allows args to printed directly - ... x, y = inst # __getitem__ allows args to be unpacked directly + ... x, y = inst.args ... print 'x =', x ... print 'y =', y ... -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Fri Jun 8 05:51:17 2012 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Fri, 08 Jun 2012 05:51:17 +0200 Subject: [Python-checkins] Daily reference leaks (03b5f75ddac7): sum=1 Message-ID: results for 03b5f75ddac7 on branch "default" -------------------------------------------- test_support leaked [1, 0, 0] references, sum=1 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflog4sufr0', '-x'] From python-checkins at python.org Fri Jun 8 15:22:02 2012 From: python-checkins at python.org (hynek.schlawack) Date: Fri, 08 Jun 2012 15:22:02 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_=2314814=3A_Remove_redundan?= =?utf8?q?t_code_from_ipaddress=2EIPv6Network?= Message-ID: http://hg.python.org/cpython/rev/4aeb5b9b62d7 changeset: 77383:4aeb5b9b62d7 parent: 77381:03b5f75ddac7 user: Hynek Schlawack date: Fri Jun 08 15:21:21 2012 +0200 summary: #14814: Remove redundant code from ipaddress.IPv6Network The strict checks and netmask computing don't make sense if constructed with a ALL_ONES mask based on addresses. Also fix a bug due to mis-indentation of a return statement in the same code block. files: Lib/ipaddress.py | 14 +------------- 1 files changed, 1 insertions(+), 13 deletions(-) diff --git a/Lib/ipaddress.py b/Lib/ipaddress.py --- a/Lib/ipaddress.py +++ b/Lib/ipaddress.py @@ -1990,12 +1990,6 @@ self.network_address = IPv6Address(address) self._prefixlen = self._max_prefixlen self.netmask = IPv6Address(self._ALL_ONES) - if strict: - if (IPv6Address(int(self.network_address) & - int(self.netmask)) != self.network_address): - raise ValueError('%s has host bits set' % str(self)) - self.network_address = IPv6Address(int(self.network_address) & - int(self.netmask)) return # Constructing from a packed address @@ -2004,13 +1998,7 @@ self.network_address = IPv6Address((tmp[0] << 64) | tmp[1]) self._prefixlen = self._max_prefixlen self.netmask = IPv6Address(self._ALL_ONES) - if strict: - if (IPv6Address(int(self.network_address) & - int(self.netmask)) != self.network_address): - raise ValueError('%s has host bits set' % str(self)) - self.network_address = IPv6Address(int(self.network_address) & - int(self.netmask)) - return + return # Assume input argument to be string or any object representation # which converts into a formatted IP prefix string. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 8 18:33:23 2012 From: python-checkins at python.org (alexander.belopolsky) Date: Fri, 08 Jun 2012 18:33:23 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Issue_=232736=3A_Added_date?= =?utf8?q?time=2Etimestamp=28=29_method=2E?= Message-ID: http://hg.python.org/cpython/rev/6671c5039e15 changeset: 77384:6671c5039e15 user: Alexander Belopolsky date: Fri Jun 08 12:33:09 2012 -0400 summary: Issue #2736: Added datetime.timestamp() method. files: Doc/library/datetime.rst | 44 +++++++++++++++++----- Lib/datetime.py | 11 +++++- Lib/test/datetimetester.py | 36 +++++++++++++++++++ Misc/NEWS | 2 + Modules/_datetimemodule.c | 49 ++++++++++++++++++++++++++ 5 files changed, 130 insertions(+), 12 deletions(-) diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst --- a/Doc/library/datetime.rst +++ b/Doc/library/datetime.rst @@ -752,17 +752,6 @@ datetime(1970, 1, 1) + timedelta(seconds=timestamp) - There is no method to obtain the timestamp from a :class:`datetime` - instance, but POSIX timestamp corresponding to a :class:`datetime` - instance ``dt`` can be easily calculated as follows. For a naive - ``dt``:: - - timestamp = (dt - datetime(1970, 1, 1)) / timedelta(seconds=1) - - And for an aware ``dt``:: - - timestamp = (dt - datetime(1970, 1, 1, tzinfo=timezone.utc)) / timedelta(seconds=1) - .. versionchanged:: 3.3 Raise :exc:`OverflowError` instead of :exc:`ValueError` if the timestamp is out of the range of values supported by the platform C @@ -1054,6 +1043,39 @@ Return the proleptic Gregorian ordinal of the date. The same as ``self.date().toordinal()``. +.. method:: datetime.timestamp() + + Return POSIX timestamp corresponding to the :class:`datetime` + instance. The return value is a :class:`float` similar to that + returned by :func:`time.time`. + + Naive :class:`datetime` instances are assumed to represent local + time and this method relies on the platform C :c:func:`mktime` + function to perform the conversion. Since :class:`datetime` + supports wider range of values than :c:func:`mktime` on many + platforms, this method may raise :exc:`OverflowError` for times far + in the past or far in the future. + + For aware :class:`datetime` instances, the return value is computed + as:: + + (dt - datetime(1970, 1, 1, tzinfo=timezone.utc)).total_seconds() + + .. versionadded:: 3.3 + + .. note:: + + There is no method to obtain the POSIX timestamp directly from a + naive :class:`datetime` instance representing UTC time. If your + application uses this convention and your system timezone is not + set to UTC, you can obtain the POSIX timestamp by supplying + ``tzinfo=timezone.utc``:: + + timestamp = dt.replace(tzinfo=timezone.utc).timestamp() + + or by calculating the timestamp directly:: + + timestamp = (dt - datetime(1970, 1, 1)) / timedelta(seconds=1) .. method:: datetime.weekday() diff --git a/Lib/datetime.py b/Lib/datetime.py --- a/Lib/datetime.py +++ b/Lib/datetime.py @@ -1434,6 +1434,15 @@ self.hour, self.minute, self.second, dst) + def timestamp(self): + "Return POSIX timestamp as float" + if self._tzinfo is None: + return _time.mktime((self.year, self.month, self.day, + self.hour, self.minute, self.second, + -1, -1, -1)) + self.microsecond / 1e6 + else: + return (self - _EPOCH).total_seconds() + def utctimetuple(self): "Return UTC time tuple compatible with time.gmtime()." offset = self.utcoffset() @@ -1889,7 +1898,7 @@ timezone.utc = timezone._create(timedelta(0)) timezone.min = timezone._create(timezone._minoffset) timezone.max = timezone._create(timezone._maxoffset) - +_EPOCH = datetime(1970, 1, 1, tzinfo=timezone.utc) """ Some time zone algebra. For a datetime x, let x.n = x stripped of its timezone -- its naive time. diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -1735,6 +1735,42 @@ got = self.theclass.utcfromtimestamp(ts) self.verify_field_equality(expected, got) + # Run with US-style DST rules: DST begins 2 a.m. on second Sunday in + # March (M3.2.0) and ends 2 a.m. on first Sunday in November (M11.1.0). + @support.run_with_tz('EST+05EDT,M3.2.0,M11.1.0') + def test_timestamp_naive(self): + t = self.theclass(1970, 1, 1) + self.assertEqual(t.timestamp(), 18000.0) + t = self.theclass(1970, 1, 1, 1, 2, 3, 4) + self.assertEqual(t.timestamp(), + 18000.0 + 3600 + 2*60 + 3 + 4*1e-6) + # Missing hour defaults to standard time + t = self.theclass(2012, 3, 11, 2, 30) + self.assertEqual(self.theclass.fromtimestamp(t.timestamp()), + t + timedelta(hours=1)) + # Ambiguous hour defaults to DST + t = self.theclass(2012, 11, 4, 1, 30) + self.assertEqual(self.theclass.fromtimestamp(t.timestamp()), t) + + # Timestamp may raise an overflow error on some platforms + for t in [self.theclass(1,1,1), self.theclass(9999,12,12)]: + try: + s = t.timestamp() + except OverflowError: + pass + else: + self.assertEqual(self.theclass.fromtimestamp(s), t) + + def test_timestamp_aware(self): + t = self.theclass(1970, 1, 1, tzinfo=timezone.utc) + self.assertEqual(t.timestamp(), 0.0) + t = self.theclass(1970, 1, 1, 1, 2, 3, 4, tzinfo=timezone.utc) + self.assertEqual(t.timestamp(), + 3600 + 2*60 + 3 + 4*1e-6) + t = self.theclass(1970, 1, 1, 1, 2, 3, 4, + tzinfo=timezone(timedelta(hours=-5), 'EST')) + self.assertEqual(t.timestamp(), + 18000 + 3600 + 2*60 + 3 + 4*1e-6) def test_microsecond_rounding(self): for fts in [self.theclass.fromtimestamp, self.theclass.utcfromtimestamp]: diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -21,6 +21,8 @@ Library ------- +- Issue #2736: Added datetime.timestamp() method. + - Issue #13854: Make multiprocessing properly handle non-integer non-string argument to SystemExit. diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -766,6 +766,8 @@ /* The interned UTC timezone instance */ static PyObject *PyDateTime_TimeZone_UTC; +/* The interned Epoch datetime instance */ +static PyObject *PyDateTime_Epoch; /* Create new timezone instance checking offset range. This function does not check the name argument. Caller must assure @@ -4748,6 +4750,44 @@ } static PyObject * +datetime_timestamp(PyDateTime_DateTime *self) +{ + PyObject *result; + + if (HASTZINFO(self) && self->tzinfo != Py_None) { + PyObject *delta; + delta = datetime_subtract((PyObject *)self, PyDateTime_Epoch); + if (delta == NULL) + return NULL; + result = delta_total_seconds(delta); + Py_DECREF(delta); + } + else { + struct tm time; + time_t timestamp; + memset((void *) &time, '\0', sizeof(struct tm)); + time.tm_year = GET_YEAR(self) - 1900; + time.tm_mon = GET_MONTH(self) - 1; + time.tm_mday = GET_DAY(self); + time.tm_hour = DATE_GET_HOUR(self); + time.tm_min = DATE_GET_MINUTE(self); + time.tm_sec = DATE_GET_SECOND(self); + time.tm_wday = -1; + time.tm_isdst = -1; + timestamp = mktime(&time); + /* Return value of -1 does not necessarily mean an error, but tm_wday + * cannot remain set to -1 if mktime succeeded. */ + if (timestamp == (time_t)(-1) && time.tm_wday == -1) { + PyErr_SetString(PyExc_OverflowError, + "timestamp out of range"); + return NULL; + } + result = PyFloat_FromDouble(timestamp + DATE_GET_MICROSECOND(self) / 1e6); + } + return result; +} + +static PyObject * datetime_getdate(PyDateTime_DateTime *self) { return new_date(GET_YEAR(self), @@ -4894,6 +4934,9 @@ {"timetuple", (PyCFunction)datetime_timetuple, METH_NOARGS, PyDoc_STR("Return time tuple, compatible with time.localtime().")}, + {"timestamp", (PyCFunction)datetime_timestamp, METH_NOARGS, + PyDoc_STR("Return POSIX timestamp as float.")}, + {"utctimetuple", (PyCFunction)datetime_utctimetuple, METH_NOARGS, PyDoc_STR("Return UTC time tuple, compatible with time.localtime().")}, @@ -5151,6 +5194,12 @@ return NULL; Py_DECREF(x); + /* Epoch */ + PyDateTime_Epoch = new_datetime(1970, 1, 1, 0, 0, 0, 0, + PyDateTime_TimeZone_UTC); + if (PyDateTime_Epoch == NULL) + return NULL; + /* module initialization */ PyModule_AddIntConstant(m, "MINYEAR", MINYEAR); PyModule_AddIntConstant(m, "MAXYEAR", MAXYEAR); -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 8 18:56:35 2012 From: python-checkins at python.org (stefan.krah) Date: Fri, 08 Jun 2012 18:56:35 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_1=29_List_relative_error_fo?= =?utf8?b?ciBfbXBkX3FsbjEwKCku?= Message-ID: http://hg.python.org/cpython/rev/87a8a209c6e1 changeset: 77385:87a8a209c6e1 parent: 77383:4aeb5b9b62d7 user: Stefan Krah date: Fri Jun 08 18:41:33 2012 +0200 summary: 1) List relative error for _mpd_qln10(). 2) Add rigorous error analysis to _mpd_qlog10 (ACL2 proofs exist). 3) Use the relative error as a basis for the interval generation in the correction loop (same as in _mpd_qln()). files: Modules/_decimal/libmpdec/mpdecimal.c | 38 +++++++++++--- 1 files changed, 29 insertions(+), 9 deletions(-) diff --git a/Modules/_decimal/libmpdec/mpdecimal.c b/Modules/_decimal/libmpdec/mpdecimal.c --- a/Modules/_decimal/libmpdec/mpdecimal.c +++ b/Modules/_decimal/libmpdec/mpdecimal.c @@ -4298,7 +4298,14 @@ (mpd_uint_t *)mpd_ln10_data }; -/* Set 'result' to ln(10). ulp error: abs(result - log(10)) < ulp(log(10)) */ +/* + * Set 'result' to log(10). + * Ulp error: abs(result - log(10)) < ulp(log(10)) + * Relative error : abs(result - log(10)) < 5 * 10**-prec * log(10) + * + * NOTE: The relative error is not derived from the ulp error, but + * calculated separately using the fact that 23/10 < log(10) < 24/10. + */ void mpd_qln10(mpd_t *result, mpd_ssize_t prec, uint32_t *status) { @@ -4712,21 +4719,34 @@ } } -/* Internal log10() function that does not check for specials, zero, ... */ +/* + * Internal log10() function that does not check for specials, zero or one. + * Case SKIP_FINALIZE: + * Relative error: abs(result - log10(a)) < 0.1 * 10**-prec * abs(log10(a)) + * Case DO_FINALIZE: + * Ulp error: abs(result - log10(a)) < ulp(log10(a)) + */ +enum {SKIP_FINALIZE, DO_FINALIZE}; static void -_mpd_qlog10(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, - uint32_t *status) +_mpd_qlog10(int action, mpd_t *result, const mpd_t *a, + const mpd_context_t *ctx, uint32_t *status) { mpd_context_t workctx; MPD_NEW_STATIC(ln10,0,0,0,0); mpd_maxcontext(&workctx); workctx.prec = ctx->prec + 3; + /* relative error: 0.1 * 10**(-p-3). The specific underflow shortcut + * in _mpd_qln() does not change the final result. */ _mpd_qln(result, a, &workctx, status); + /* relative error: 5 * 10**(-p-3) */ mpd_qln10(&ln10, workctx.prec, status); - workctx = *ctx; - workctx.round = MPD_ROUND_HALF_EVEN; + if (action == DO_FINALIZE) { + workctx = *ctx; + workctx.round = MPD_ROUND_HALF_EVEN; + } + /* SKIP_FINALIZE: relative error: 5 * 10**(-p-3) */ _mpd_qdiv(NO_IDEAL_EXP, result, result, &ln10, &workctx, status); mpd_del(&ln10); @@ -4807,9 +4827,9 @@ prec = ctx->prec + 3; while (1) { workctx.prec = prec; - _mpd_qlog10(result, a, &workctx, status); + _mpd_qlog10(SKIP_FINALIZE, result, a, &workctx, status); _ssettriple(&ulp, MPD_POS, 1, - result->exp + result->digits-workctx.prec-1); + result->exp + result->digits-workctx.prec); workctx.prec = ctx->prec; mpd_qadd(&t1, result, &ulp, &workctx, &workctx.status); @@ -4829,7 +4849,7 @@ mpd_del(&aa); } else { - _mpd_qlog10(result, a, &workctx, status); + _mpd_qlog10(DO_FINALIZE, result, a, &workctx, status); mpd_check_underflow(result, &workctx, status); } } -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 8 18:56:36 2012 From: python-checkins at python.org (stefan.krah) Date: Fri, 08 Jun 2012 18:56:36 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=28merge_default_-=3E_default?= =?utf8?b?KTogTWVyZ2Uu?= Message-ID: http://hg.python.org/cpython/rev/162a5508017a changeset: 77386:162a5508017a parent: 77385:87a8a209c6e1 parent: 77384:6671c5039e15 user: Stefan Krah date: Fri Jun 08 18:55:22 2012 +0200 summary: Merge. files: Doc/library/datetime.rst | 44 +++++++++++++++++----- Lib/datetime.py | 11 +++++- Lib/test/datetimetester.py | 36 +++++++++++++++++++ Misc/NEWS | 2 + Modules/_datetimemodule.c | 49 ++++++++++++++++++++++++++ 5 files changed, 130 insertions(+), 12 deletions(-) diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst --- a/Doc/library/datetime.rst +++ b/Doc/library/datetime.rst @@ -752,17 +752,6 @@ datetime(1970, 1, 1) + timedelta(seconds=timestamp) - There is no method to obtain the timestamp from a :class:`datetime` - instance, but POSIX timestamp corresponding to a :class:`datetime` - instance ``dt`` can be easily calculated as follows. For a naive - ``dt``:: - - timestamp = (dt - datetime(1970, 1, 1)) / timedelta(seconds=1) - - And for an aware ``dt``:: - - timestamp = (dt - datetime(1970, 1, 1, tzinfo=timezone.utc)) / timedelta(seconds=1) - .. versionchanged:: 3.3 Raise :exc:`OverflowError` instead of :exc:`ValueError` if the timestamp is out of the range of values supported by the platform C @@ -1054,6 +1043,39 @@ Return the proleptic Gregorian ordinal of the date. The same as ``self.date().toordinal()``. +.. method:: datetime.timestamp() + + Return POSIX timestamp corresponding to the :class:`datetime` + instance. The return value is a :class:`float` similar to that + returned by :func:`time.time`. + + Naive :class:`datetime` instances are assumed to represent local + time and this method relies on the platform C :c:func:`mktime` + function to perform the conversion. Since :class:`datetime` + supports wider range of values than :c:func:`mktime` on many + platforms, this method may raise :exc:`OverflowError` for times far + in the past or far in the future. + + For aware :class:`datetime` instances, the return value is computed + as:: + + (dt - datetime(1970, 1, 1, tzinfo=timezone.utc)).total_seconds() + + .. versionadded:: 3.3 + + .. note:: + + There is no method to obtain the POSIX timestamp directly from a + naive :class:`datetime` instance representing UTC time. If your + application uses this convention and your system timezone is not + set to UTC, you can obtain the POSIX timestamp by supplying + ``tzinfo=timezone.utc``:: + + timestamp = dt.replace(tzinfo=timezone.utc).timestamp() + + or by calculating the timestamp directly:: + + timestamp = (dt - datetime(1970, 1, 1)) / timedelta(seconds=1) .. method:: datetime.weekday() diff --git a/Lib/datetime.py b/Lib/datetime.py --- a/Lib/datetime.py +++ b/Lib/datetime.py @@ -1434,6 +1434,15 @@ self.hour, self.minute, self.second, dst) + def timestamp(self): + "Return POSIX timestamp as float" + if self._tzinfo is None: + return _time.mktime((self.year, self.month, self.day, + self.hour, self.minute, self.second, + -1, -1, -1)) + self.microsecond / 1e6 + else: + return (self - _EPOCH).total_seconds() + def utctimetuple(self): "Return UTC time tuple compatible with time.gmtime()." offset = self.utcoffset() @@ -1889,7 +1898,7 @@ timezone.utc = timezone._create(timedelta(0)) timezone.min = timezone._create(timezone._minoffset) timezone.max = timezone._create(timezone._maxoffset) - +_EPOCH = datetime(1970, 1, 1, tzinfo=timezone.utc) """ Some time zone algebra. For a datetime x, let x.n = x stripped of its timezone -- its naive time. diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -1735,6 +1735,42 @@ got = self.theclass.utcfromtimestamp(ts) self.verify_field_equality(expected, got) + # Run with US-style DST rules: DST begins 2 a.m. on second Sunday in + # March (M3.2.0) and ends 2 a.m. on first Sunday in November (M11.1.0). + @support.run_with_tz('EST+05EDT,M3.2.0,M11.1.0') + def test_timestamp_naive(self): + t = self.theclass(1970, 1, 1) + self.assertEqual(t.timestamp(), 18000.0) + t = self.theclass(1970, 1, 1, 1, 2, 3, 4) + self.assertEqual(t.timestamp(), + 18000.0 + 3600 + 2*60 + 3 + 4*1e-6) + # Missing hour defaults to standard time + t = self.theclass(2012, 3, 11, 2, 30) + self.assertEqual(self.theclass.fromtimestamp(t.timestamp()), + t + timedelta(hours=1)) + # Ambiguous hour defaults to DST + t = self.theclass(2012, 11, 4, 1, 30) + self.assertEqual(self.theclass.fromtimestamp(t.timestamp()), t) + + # Timestamp may raise an overflow error on some platforms + for t in [self.theclass(1,1,1), self.theclass(9999,12,12)]: + try: + s = t.timestamp() + except OverflowError: + pass + else: + self.assertEqual(self.theclass.fromtimestamp(s), t) + + def test_timestamp_aware(self): + t = self.theclass(1970, 1, 1, tzinfo=timezone.utc) + self.assertEqual(t.timestamp(), 0.0) + t = self.theclass(1970, 1, 1, 1, 2, 3, 4, tzinfo=timezone.utc) + self.assertEqual(t.timestamp(), + 3600 + 2*60 + 3 + 4*1e-6) + t = self.theclass(1970, 1, 1, 1, 2, 3, 4, + tzinfo=timezone(timedelta(hours=-5), 'EST')) + self.assertEqual(t.timestamp(), + 18000 + 3600 + 2*60 + 3 + 4*1e-6) def test_microsecond_rounding(self): for fts in [self.theclass.fromtimestamp, self.theclass.utcfromtimestamp]: diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -21,6 +21,8 @@ Library ------- +- Issue #2736: Added datetime.timestamp() method. + - Issue #13854: Make multiprocessing properly handle non-integer non-string argument to SystemExit. diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -766,6 +766,8 @@ /* The interned UTC timezone instance */ static PyObject *PyDateTime_TimeZone_UTC; +/* The interned Epoch datetime instance */ +static PyObject *PyDateTime_Epoch; /* Create new timezone instance checking offset range. This function does not check the name argument. Caller must assure @@ -4748,6 +4750,44 @@ } static PyObject * +datetime_timestamp(PyDateTime_DateTime *self) +{ + PyObject *result; + + if (HASTZINFO(self) && self->tzinfo != Py_None) { + PyObject *delta; + delta = datetime_subtract((PyObject *)self, PyDateTime_Epoch); + if (delta == NULL) + return NULL; + result = delta_total_seconds(delta); + Py_DECREF(delta); + } + else { + struct tm time; + time_t timestamp; + memset((void *) &time, '\0', sizeof(struct tm)); + time.tm_year = GET_YEAR(self) - 1900; + time.tm_mon = GET_MONTH(self) - 1; + time.tm_mday = GET_DAY(self); + time.tm_hour = DATE_GET_HOUR(self); + time.tm_min = DATE_GET_MINUTE(self); + time.tm_sec = DATE_GET_SECOND(self); + time.tm_wday = -1; + time.tm_isdst = -1; + timestamp = mktime(&time); + /* Return value of -1 does not necessarily mean an error, but tm_wday + * cannot remain set to -1 if mktime succeeded. */ + if (timestamp == (time_t)(-1) && time.tm_wday == -1) { + PyErr_SetString(PyExc_OverflowError, + "timestamp out of range"); + return NULL; + } + result = PyFloat_FromDouble(timestamp + DATE_GET_MICROSECOND(self) / 1e6); + } + return result; +} + +static PyObject * datetime_getdate(PyDateTime_DateTime *self) { return new_date(GET_YEAR(self), @@ -4894,6 +4934,9 @@ {"timetuple", (PyCFunction)datetime_timetuple, METH_NOARGS, PyDoc_STR("Return time tuple, compatible with time.localtime().")}, + {"timestamp", (PyCFunction)datetime_timestamp, METH_NOARGS, + PyDoc_STR("Return POSIX timestamp as float.")}, + {"utctimetuple", (PyCFunction)datetime_utctimetuple, METH_NOARGS, PyDoc_STR("Return UTC time tuple, compatible with time.localtime().")}, @@ -5151,6 +5194,12 @@ return NULL; Py_DECREF(x); + /* Epoch */ + PyDateTime_Epoch = new_datetime(1970, 1, 1, 0, 0, 0, 0, + PyDateTime_TimeZone_UTC); + if (PyDateTime_Epoch == NULL) + return NULL; + /* module initialization */ PyModule_AddIntConstant(m, "MINYEAR", MINYEAR); PyModule_AddIntConstant(m, "MAXYEAR", MAXYEAR); -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 8 19:00:55 2012 From: python-checkins at python.org (alexander.belopolsky) Date: Fri, 08 Jun 2012 19:00:55 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Relax_datetime=2Etimestamp?= =?utf8?q?=28=29_test_around_DST_change?= Message-ID: http://hg.python.org/cpython/rev/239ebc022479 changeset: 77387:239ebc022479 user: Alexander Belopolsky date: Fri Jun 08 12:58:31 2012 -0400 summary: Relax datetime.timestamp() test around DST change files: Lib/test/datetimetester.py | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -1744,10 +1744,10 @@ t = self.theclass(1970, 1, 1, 1, 2, 3, 4) self.assertEqual(t.timestamp(), 18000.0 + 3600 + 2*60 + 3 + 4*1e-6) - # Missing hour defaults to standard time + # Missing hour may produce platform-dependent result t = self.theclass(2012, 3, 11, 2, 30) - self.assertEqual(self.theclass.fromtimestamp(t.timestamp()), - t + timedelta(hours=1)) + self.assertIn(self.theclass.fromtimestamp(t.timestamp()), + [t, t + timedelta(hours=1)]) # Ambiguous hour defaults to DST t = self.theclass(2012, 11, 4, 1, 30) self.assertEqual(self.theclass.fromtimestamp(t.timestamp()), t) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 8 19:00:56 2012 From: python-checkins at python.org (alexander.belopolsky) Date: Fri, 08 Jun 2012 19:00:56 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Relax_datetime=2Etimestamp?= =?utf8?q?=28=29_test_around_DST_change?= Message-ID: http://hg.python.org/cpython/rev/e6b8202443b6 changeset: 77388:e6b8202443b6 user: Alexander Belopolsky date: Fri Jun 08 13:00:27 2012 -0400 summary: Relax datetime.timestamp() test around DST change files: Lib/test/datetimetester.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -1747,7 +1747,7 @@ # Missing hour may produce platform-dependent result t = self.theclass(2012, 3, 11, 2, 30) self.assertIn(self.theclass.fromtimestamp(t.timestamp()), - [t, t + timedelta(hours=1)]) + [t - timedelta(hours=1), t + timedelta(hours=1)]) # Ambiguous hour defaults to DST t = self.theclass(2012, 11, 4, 1, 30) self.assertEqual(self.theclass.fromtimestamp(t.timestamp()), t) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 8 22:24:46 2012 From: python-checkins at python.org (raymond.hettinger) Date: Fri, 08 Jun 2012 22:24:46 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=282=2E7=29=3A_Code_cleanups?= Message-ID: http://hg.python.org/cpython/rev/aca1a271c4fc changeset: 77389:aca1a271c4fc branch: 2.7 parent: 77382:b873afe640e2 user: Raymond Hettinger date: Fri Jun 08 13:24:12 2012 -0700 summary: Code cleanups files: Doc/library/collections.rst | 64 ++++++------ Lib/collections.py | 117 ++++++++++++++--------- 2 files changed, 105 insertions(+), 76 deletions(-) diff --git a/Doc/library/collections.rst b/Doc/library/collections.rst --- a/Doc/library/collections.rst +++ b/Doc/library/collections.rst @@ -601,47 +601,49 @@ >>> Point = namedtuple('Point', ['x', 'y'], verbose=True) class Point(tuple): - 'Point(x, y)' + 'Point(x, y)' - __slots__ = () + __slots__ = () - _fields = ('x', 'y') + _fields = ('x', 'y') - def __new__(_cls, x, y): - 'Create a new instance of Point(x, y)' - return _tuple.__new__(_cls, (x, y)) + def __new__(_cls, x, y): + 'Create a new instance of Point(x, y)' + return _tuple.__new__(_cls, (x, y)) - @classmethod - def _make(cls, iterable, new=tuple.__new__, len=len): - 'Make a new Point object from a sequence or iterable' - result = new(cls, iterable) - if len(result) != 2: - raise TypeError('Expected 2 arguments, got %d' % len(result)) - return result + @classmethod + def _make(cls, iterable, new=tuple.__new__, len=len): + 'Make a new Point object from a sequence or iterable' + result = new(cls, iterable) + if len(result) != 2: + raise TypeError('Expected 2 arguments, got %d' % len(result)) + return result - def __repr__(self): - 'Return a nicely formatted representation string' - return 'Point(x=%r, y=%r)' % self + def __repr__(self): + 'Return a nicely formatted representation string' + return 'Point(x=%r, y=%r)' % self - def _asdict(self): - 'Return a new OrderedDict which maps field names to their values' - return OrderedDict(zip(self._fields, self)) + def _asdict(self): + 'Return a new OrderedDict which maps field names to their values' + return OrderedDict(zip(self._fields, self)) - __dict__ = property(_asdict) + __dict__ = property(_asdict) - def _replace(_self, **kwds): - 'Return a new Point object replacing specified fields with new values' - result = _self._make(map(kwds.pop, ('x', 'y'), _self)) - if kwds: - raise ValueError('Got unexpected field names: %r' % kwds.keys()) - return result + def _replace(_self, **kwds): + 'Return a new Point object replacing specified fields with new values' + result = _self._make(map(kwds.pop, ('x', 'y'), _self)) + if kwds: + raise ValueError('Got unexpected field names: %r' % kwds.keys()) + return result - def __getnewargs__(self): - 'Return self as a plain tuple. Used by copy and pickle.' - return tuple(self) + def __getnewargs__(self): + 'Return self as a plain tuple. Used by copy and pickle.' + return tuple(self) - x = _property(_itemgetter(0), doc='Alias for field number 0') - y = _property(_itemgetter(1), doc='Alias for field number 1') + x = _property(_itemgetter(0), doc='Alias for field number 0') + + y = _property(_itemgetter(1), doc='Alias for field number 1') + >>> p = Point(11, y=22) # instantiate with positional or keyword arguments >>> p[0] + p[1] # indexable like the plain tuple (11, 22) diff --git a/Lib/collections.py b/Lib/collections.py --- a/Lib/collections.py +++ b/Lib/collections.py @@ -234,10 +234,60 @@ ### namedtuple ################################################################################ +_class_template = '''\ +class {typename}(tuple): + '{typename}({arg_list})' + + __slots__ = () + + _fields = {field_names!r} + + def __new__(_cls, {arg_list}): + 'Create new instance of {typename}({arg_list})' + return _tuple.__new__(_cls, ({arg_list})) + + @classmethod + def _make(cls, iterable, new=tuple.__new__, len=len): + 'Make a new {typename} object from a sequence or iterable' + result = new(cls, iterable) + if len(result) != {num_fields:d}: + raise TypeError('Expected {num_fields:d} arguments, got %d' % len(result)) + return result + + def __repr__(self): + 'Return a nicely formatted representation string' + return '{typename}({repr_fmt})' % self + + def _asdict(self): + 'Return a new OrderedDict which maps field names to their values' + return OrderedDict(zip(self._fields, self)) + + __dict__ = property(_asdict) + + def _replace(_self, **kwds): + 'Return a new {typename} object replacing specified fields with new values' + result = _self._make(map(kwds.pop, {field_names!r}, _self)) + if kwds: + raise ValueError('Got unexpected field names: %r' % kwds.keys()) + return result + + def __getnewargs__(self): + 'Return self as a plain tuple. Used by copy and pickle.' + return tuple(self) + +{field_defs} +''' + +_repr_template = '{name}=%r' + +_field_template = '''\ + {name} = _property(_itemgetter({index:d}), doc='Alias for field number {index:d}') +''' + def namedtuple(typename, field_names, verbose=False, rename=False): """Returns a new subclass of tuple with named fields. - >>> Point = namedtuple('Point', 'x y') + >>> Point = namedtuple('Point', ['x', 'y']) >>> Point.__doc__ # docstring for the new class 'Point(x, y)' >>> p = Point(11, y=22) # instantiate with positional args or keywords @@ -267,8 +317,11 @@ names = list(field_names) seen = set() for i, name in enumerate(names): - if (not all(c.isalnum() or c=='_' for c in name) or _iskeyword(name) - or not name or name[0].isdigit() or name.startswith('_') + if (not all(c.isalnum() or c=='_' for c in name) + or _iskeyword(name) + or not name + or name[0].isdigit() + or name.startswith('_') or name in seen): names[i] = '_%d' % i seen.add(name) @@ -280,60 +333,34 @@ raise ValueError('Type names and field names cannot be a keyword: %r' % name) if name[0].isdigit(): raise ValueError('Type names and field names cannot start with a number: %r' % name) - seen_names = set() + seen = set() for name in field_names: if name.startswith('_') and not rename: raise ValueError('Field names cannot start with an underscore: %r' % name) - if name in seen_names: + if name in seen: raise ValueError('Encountered duplicate field name: %r' % name) - seen_names.add(name) + seen.add(name) - # Create and fill-in the class template - numfields = len(field_names) - argtxt = repr(field_names).replace("'", "")[1:-1] # tuple repr without parens or quotes - reprtxt = ', '.join('%s=%%r' % name for name in field_names) - template = '''class %(typename)s(tuple): - '%(typename)s(%(argtxt)s)' \n - __slots__ = () \n - _fields = %(field_names)r \n - def __new__(_cls, %(argtxt)s): - 'Create new instance of %(typename)s(%(argtxt)s)' - return _tuple.__new__(_cls, (%(argtxt)s)) \n - @classmethod - def _make(cls, iterable, new=tuple.__new__, len=len): - 'Make a new %(typename)s object from a sequence or iterable' - result = new(cls, iterable) - if len(result) != %(numfields)d: - raise TypeError('Expected %(numfields)d arguments, got %%d' %% len(result)) - return result \n - def __repr__(self): - 'Return a nicely formatted representation string' - return '%(typename)s(%(reprtxt)s)' %% self \n - def _asdict(self): - 'Return a new OrderedDict which maps field names to their values' - return OrderedDict(zip(self._fields, self)) \n - __dict__ = property(_asdict) \n - def _replace(_self, **kwds): - 'Return a new %(typename)s object replacing specified fields with new values' - result = _self._make(map(kwds.pop, %(field_names)r, _self)) - if kwds: - raise ValueError('Got unexpected field names: %%r' %% kwds.keys()) - return result \n - def __getnewargs__(self): - 'Return self as a plain tuple. Used by copy and pickle.' - return tuple(self) \n\n''' % locals() - for i, name in enumerate(field_names): - template += " %s = _property(_itemgetter(%d), doc='Alias for field number %d')\n" % (name, i, i) + # Fill-in the class template + class_definition = _class_template.format( + typename = typename, + field_names = tuple(field_names), + num_fields = len(field_names), + arg_list = repr(tuple(field_names)).replace("'", "")[1:-1], + repr_fmt = ', '.join(_repr_template.format(name=name) for name in field_names), + field_defs = '\n'.join(_field_template.format(index=index, name=name) + for index, name in enumerate(field_names)) + ) if verbose: - print template + print class_definition # Execute the template string in a temporary namespace and # support tracing utilities by setting a value for frame.f_globals['__name__'] namespace = dict(_itemgetter=_itemgetter, __name__='namedtuple_%s' % typename, OrderedDict=OrderedDict, _property=property, _tuple=tuple) try: - exec template in namespace - except SyntaxError, e: + exec class_definition in namespace + except SyntaxError as e: raise SyntaxError(e.message + ':\n' + template) result = namespace[typename] -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 9 00:03:26 2012 From: python-checkins at python.org (stefan.krah) Date: Sat, 09 Jun 2012 00:03:26 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Enumerate_all_cases_in_the_?= =?utf8?q?overflow_detection_strategy_in_mpd=5Fqlog10=28=29=2E?= Message-ID: http://hg.python.org/cpython/rev/ace3a7eb10a8 changeset: 77390:ace3a7eb10a8 parent: 77388:e6b8202443b6 user: Stefan Krah date: Sat Jun 09 00:01:28 2012 +0200 summary: Enumerate all cases in the overflow detection strategy in mpd_qlog10(). files: Modules/_decimal/libmpdec/mpdecimal.c | 21 ++++++++++++-- 1 files changed, 17 insertions(+), 4 deletions(-) diff --git a/Modules/_decimal/libmpdec/mpdecimal.c b/Modules/_decimal/libmpdec/mpdecimal.c --- a/Modules/_decimal/libmpdec/mpdecimal.c +++ b/Modules/_decimal/libmpdec/mpdecimal.c @@ -4793,12 +4793,25 @@ mpd_qfinalize(result, &workctx, status); return; } - /* Check if the result will overflow. + /* + * Check if the result will overflow (0 < x, x != 1): + * 1) log10(x) < 0 iff adjexp(x) < 0 + * 2) 0 < x /\ x <= y ==> adjexp(x) <= adjexp(y) + * 3) adjexp(x) <= log10(x) < adjexp(x) + 1 * - * 1) adjexp(a) + 1 > log10(a) >= adjexp(a) + * Case adjexp(x) >= 0: + * 4) adjexp(x) <= abs(log10(x)) + * Case adjexp(x) > 0: + * 5) adjexp(adjexp(x)) <= adjexp(abs(log10(x))) + * Case adjexp(x) == 0: + * mpd_exp_digits(t)-1 == 0 <= emax (the shortcut is not triggered) * - * 2) |log10(a)| >= adjexp(a), if adjexp(a) >= 0 - * |log10(a)| > -adjexp(a)-1, if adjexp(a) < 0 + * Case adjexp(x) < 0: + * 6) -adjexp(x) - 1 < abs(log10(x)) + * Case adjexp(x) < -1: + * 7) adjexp(-adjexp(x) - 1) <= adjexp(abs(log(x))) + * Case adjexp(x) == -1: + * mpd_exp_digits(t)-1 == 0 <= emax (the shortcut is not triggered) */ adjexp = mpd_adjexp(a); t = (adjexp < 0) ? -adjexp-1 : adjexp; -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 9 04:46:33 2012 From: python-checkins at python.org (r.david.murray) Date: Sat, 09 Jun 2012 04:46:33 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Now_that_Defects_are_Except?= =?utf8?q?ion_subclasses=2C_call_super=2E?= Message-ID: http://hg.python.org/cpython/rev/10a8ad665749 changeset: 77391:10a8ad665749 user: R David Murray date: Fri Jun 08 22:45:46 2012 -0400 summary: Now that Defects are Exception subclasses, call super. The behavior of MessageDefect is legacy behavior. The chances anyone is actually using the undocumented 'line' attribute is low, but it costs little to retain backward compatibility. Although one of the costs is having to restore normal exception behavior in HeaderDefect. On the other hand, I'll probably add some specialized behavior there later. files: Lib/email/errors.py | 5 +++++ 1 files changed, 5 insertions(+), 0 deletions(-) diff --git a/Lib/email/errors.py b/Lib/email/errors.py --- a/Lib/email/errors.py +++ b/Lib/email/errors.py @@ -34,6 +34,8 @@ """Base class for a message defect.""" def __init__(self, line=None): + if line is not None: + super().__init__(line) self.line = line class NoBoundaryInMultipartDefect(MessageDefect): @@ -76,6 +78,9 @@ class HeaderDefect(MessageDefect): """Base class for a header defect.""" + def __init__(self, *args, **kw): + super().__init__(*args, **kw) + class InvalidHeaderDefect(HeaderDefect): """Header is not valid, message gives details.""" -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Sat Jun 9 05:47:45 2012 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Sat, 09 Jun 2012 05:47:45 +0200 Subject: [Python-checkins] Daily reference leaks (ace3a7eb10a8): sum=0 Message-ID: results for ace3a7eb10a8 on branch "default" -------------------------------------------- Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogH0a8vO', '-x'] From python-checkins at python.org Sat Jun 9 15:30:31 2012 From: python-checkins at python.org (stefan.krah) Date: Sat, 09 Jun 2012 15:30:31 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Add_one_extra_comparison_to?= =?utf8?q?_the_=5Fmpd=5Fshortmul=28=29_case_to_avoid_repetitive_code=2E?= Message-ID: http://hg.python.org/cpython/rev/08c474d33f82 changeset: 77392:08c474d33f82 user: Stefan Krah date: Sat Jun 09 15:28:36 2012 +0200 summary: Add one extra comparison to the _mpd_shortmul() case to avoid repetitive code. files: Modules/_decimal/libmpdec/mpdecimal.c | 26 +++++--------- 1 files changed, 9 insertions(+), 17 deletions(-) diff --git a/Modules/_decimal/libmpdec/mpdecimal.c b/Modules/_decimal/libmpdec/mpdecimal.c --- a/Modules/_decimal/libmpdec/mpdecimal.c +++ b/Modules/_decimal/libmpdec/mpdecimal.c @@ -5543,32 +5543,24 @@ if (small->len == 1) { - if ((rdata = mpd_calloc(rsize, sizeof *rdata)) == NULL) { - mpd_seterror(result, MPD_Malloc_error, status); - return; - } - _mpd_shortmul(rdata, big->data, big->len, small->data[0]); + rdata = mpd_calloc(rsize, sizeof *rdata); + if (rdata != NULL) { + _mpd_shortmul(rdata, big->data, big->len, small->data[0]); + } } else if (rsize <= 1024) { rdata = _mpd_kmul(big->data, small->data, big->len, small->len, &rsize); - if (rdata == NULL) { - mpd_seterror(result, MPD_Malloc_error, status); - return; - } } else if (rsize <= 3*MPD_MAXTRANSFORM_2N) { rdata = _mpd_fntmul(big->data, small->data, big->len, small->len, &rsize); - if (rdata == NULL) { - mpd_seterror(result, MPD_Malloc_error, status); - return; - } } else { rdata = _mpd_kmul_fnt(big->data, small->data, big->len, small->len, &rsize); - if (rdata == NULL) { - mpd_seterror(result, MPD_Malloc_error, status); /* GCOV_UNLIKELY */ - return; /* GCOV_UNLIKELY */ - } + } + + if (rdata == NULL) { + mpd_seterror(result, MPD_Malloc_error, status); + return; } if (mpd_isdynamic_data(result)) { -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 9 18:32:07 2012 From: python-checkins at python.org (michael.foord) Date: Sat, 09 Jun 2012 18:32:07 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Fix_exception_when_calling_?= =?utf8?q?reset=5Fmock_on_a_mock_created_with_autospec?= Message-ID: http://hg.python.org/cpython/rev/2059910e7d76 changeset: 77393:2059910e7d76 user: Michael Foord date: Sat Jun 09 17:31:59 2012 +0100 summary: Fix exception when calling reset_mock on a mock created with autospec files: Lib/unittest/mock.py | 3 +++ Lib/unittest/test/testmock/testhelpers.py | 7 +++++++ Lib/unittest/test/testmock/testmagicmethods.py | 8 ++++++++ 3 files changed, 18 insertions(+), 0 deletions(-) diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -510,6 +510,8 @@ self.method_calls = _CallList() for child in self._mock_children.values(): + if isinstance(child, _SpecState): + continue child.reset_mock() ret = self._mock_return_value @@ -664,6 +666,7 @@ # but not method calls _check_and_set_parent(self, value, None, name) setattr(type(self), name, value) + self._mock_children[name] = value elif name == '__class__': self._spec_class = value return diff --git a/Lib/unittest/test/testmock/testhelpers.py b/Lib/unittest/test/testmock/testhelpers.py --- a/Lib/unittest/test/testmock/testhelpers.py +++ b/Lib/unittest/test/testmock/testhelpers.py @@ -355,6 +355,13 @@ self.assertEqual(mock(), 'foo') + def test_autospec_reset_mock(self): + m = create_autospec(int) + int(m) + m.reset_mock() + self.assertEqual(m.__int__.call_count, 0) + + def test_mocking_unbound_methods(self): class Foo(object): def foo(self, foo): diff --git a/Lib/unittest/test/testmock/testmagicmethods.py b/Lib/unittest/test/testmock/testmagicmethods.py --- a/Lib/unittest/test/testmock/testmagicmethods.py +++ b/Lib/unittest/test/testmock/testmagicmethods.py @@ -345,6 +345,14 @@ self.assertEqual(mock[1][2][3], 3) + def test_magic_method_reset_mock(self): + mock = MagicMock() + str(mock) + self.assertTrue(mock.__str__.called) + mock.reset_mock() + self.assertFalse(mock.__str__.called) + + def test_dir(self): # overriding the default implementation for mock in Mock(), MagicMock(): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 9 22:04:47 2012 From: python-checkins at python.org (raymond.hettinger) Date: Sat, 09 Jun 2012 22:04:47 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=282=2E7=29=3A_Wrap_fat_lines_?= =?utf8?q?and_improve_some_variable_names=2E?= Message-ID: http://hg.python.org/cpython/rev/b15d5b2c9051 changeset: 77394:b15d5b2c9051 branch: 2.7 parent: 77389:aca1a271c4fc user: Raymond Hettinger date: Sat Jun 09 13:04:29 2012 -0700 summary: Wrap fat lines and improve some variable names. files: Lib/collections.py | 37 ++++++++++++++++++--------------- 1 files changed, 20 insertions(+), 17 deletions(-) diff --git a/Lib/collections.py b/Lib/collections.py --- a/Lib/collections.py +++ b/Lib/collections.py @@ -308,35 +308,37 @@ """ - # Parse and validate the field names. Validation serves two purposes, - # generating informative error messages and preventing template injection attacks. + # Validate the field names. At the user's option, either generate an error + # message or automatically replace the field name with a valid name. if isinstance(field_names, basestring): - field_names = field_names.replace(',', ' ').split() # names separated by whitespace and/or commas - field_names = tuple(map(str, field_names)) + field_names = field_names.replace(',', ' ').split() + field_names = map(str, field_names) if rename: - names = list(field_names) seen = set() - for i, name in enumerate(names): + for index, name in enumerate(field_names): if (not all(c.isalnum() or c=='_' for c in name) or _iskeyword(name) or not name or name[0].isdigit() or name.startswith('_') or name in seen): - names[i] = '_%d' % i + field_names[index] = '_%d' % index seen.add(name) - field_names = tuple(names) - for name in (typename,) + field_names: + for name in [typename] + field_names: if not all(c.isalnum() or c=='_' for c in name): - raise ValueError('Type names and field names can only contain alphanumeric characters and underscores: %r' % name) + raise ValueError('Type names and field names can only contain ' + 'alphanumeric characters and underscores: %r' % name) if _iskeyword(name): - raise ValueError('Type names and field names cannot be a keyword: %r' % name) + raise ValueError('Type names and field names cannot be a ' + 'keyword: %r' % name) if name[0].isdigit(): - raise ValueError('Type names and field names cannot start with a number: %r' % name) + raise ValueError('Type names and field names cannot start with ' + 'a number: %r' % name) seen = set() for name in field_names: if name.startswith('_') and not rename: - raise ValueError('Field names cannot start with an underscore: %r' % name) + raise ValueError('Field names cannot start with an underscore:' + '%r' % name) if name in seen: raise ValueError('Encountered duplicate field name: %r' % name) seen.add(name) @@ -347,21 +349,22 @@ field_names = tuple(field_names), num_fields = len(field_names), arg_list = repr(tuple(field_names)).replace("'", "")[1:-1], - repr_fmt = ', '.join(_repr_template.format(name=name) for name in field_names), + repr_fmt = ', '.join(_repr_template.format(name=name) + for name in field_names), field_defs = '\n'.join(_field_template.format(index=index, name=name) for index, name in enumerate(field_names)) ) if verbose: print class_definition - # Execute the template string in a temporary namespace and - # support tracing utilities by setting a value for frame.f_globals['__name__'] + # Execute the template string in a temporary namespace and support + # tracing utilities by setting a value for frame.f_globals['__name__'] namespace = dict(_itemgetter=_itemgetter, __name__='namedtuple_%s' % typename, OrderedDict=OrderedDict, _property=property, _tuple=tuple) try: exec class_definition in namespace except SyntaxError as e: - raise SyntaxError(e.message + ':\n' + template) + raise SyntaxError(e.message + ':\n' + class_definition) result = namespace[typename] # For pickling to work, the __module__ variable needs to be set to the frame -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 10 02:27:33 2012 From: python-checkins at python.org (raymond.hettinger) Date: Sun, 10 Jun 2012 02:27:33 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=282=2E7=29=3A_Minor_formattin?= =?utf8?q?g_fix=3Dup?= Message-ID: http://hg.python.org/cpython/rev/272e7dcffd30 changeset: 77395:272e7dcffd30 branch: 2.7 user: Raymond Hettinger date: Sat Jun 09 17:27:23 2012 -0700 summary: Minor formatting fix=up files: Lib/collections.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Lib/collections.py b/Lib/collections.py --- a/Lib/collections.py +++ b/Lib/collections.py @@ -337,7 +337,7 @@ seen = set() for name in field_names: if name.startswith('_') and not rename: - raise ValueError('Field names cannot start with an underscore:' + raise ValueError('Field names cannot start with an underscore: ' '%r' % name) if name in seen: raise ValueError('Encountered duplicate field name: %r' % name) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 10 03:46:56 2012 From: python-checkins at python.org (raymond.hettinger) Date: Sun, 10 Jun 2012 03:46:56 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Minor_reformatting_=28wrap_?= =?utf8?q?fat_lines=2C_etc=2E=29_and_create_an_=5F=5Fmain=5F=5F_file?= Message-ID: http://hg.python.org/cpython/rev/626e9d6210a0 changeset: 77396:626e9d6210a0 parent: 77393:2059910e7d76 user: Raymond Hettinger date: Sat Jun 09 18:46:45 2012 -0700 summary: Minor reformatting (wrap fat lines, etc.) and create an __main__ file files: Lib/collections/__init__.py | 66 +++++------------------- Lib/collections/__main__.py | 38 ++++++++++++++ 2 files changed, 53 insertions(+), 51 deletions(-) diff --git a/Lib/collections/__init__.py b/Lib/collections/__init__.py --- a/Lib/collections/__init__.py +++ b/Lib/collections/__init__.py @@ -315,10 +315,10 @@ """ - # Parse and validate the field names. Validation serves two purposes, - # generating informative error messages and preventing template injection attacks. + # Validate the field names. At the user's option, either generate an error + # message or automatically replace the field name with a valid name. if isinstance(field_names, str): - field_names = field_names.replace(',', ' ').split() # names separated by whitespace and/or commas + field_names = field_names.replace(',', ' ').split() field_names = list(map(str, field_names)) if rename: seen = set() @@ -333,15 +333,19 @@ seen.add(name) for name in [typename] + field_names: if not all(c.isalnum() or c=='_' for c in name): - raise ValueError('Type names and field names can only contain alphanumeric characters and underscores: %r' % name) + raise ValueError('Type names and field names can only contain ' + 'alphanumeric characters and underscores: %r' % name) if _iskeyword(name): - raise ValueError('Type names and field names cannot be a keyword: %r' % name) + raise ValueError('Type names and field names cannot be a ' + 'keyword: %r' % name) if name[0].isdigit(): - raise ValueError('Type names and field names cannot start with a number: %r' % name) + raise ValueError('Type names and field names cannot start with ' + 'a number: %r' % name) seen = set() for name in field_names: if name.startswith('_') and not rename: - raise ValueError('Field names cannot start with an underscore: %r' % name) + raise ValueError('Field names cannot start with an underscore: ' + '%r' % name) if name in seen: raise ValueError('Encountered duplicate field name: %r' % name) seen.add(name) @@ -352,13 +356,14 @@ field_names = tuple(field_names), num_fields = len(field_names), arg_list = repr(tuple(field_names)).replace("'", "")[1:-1], - repr_fmt = ', '.join(_repr_template.format(name=name) for name in field_names), + repr_fmt = ', '.join(_repr_template.format(name=name) + for name in field_names), field_defs = '\n'.join(_field_template.format(index=index, name=name) for index, name in enumerate(field_names)) ) - # Execute the template string in a temporary namespace and - # support tracing utilities by setting a value for frame.f_globals['__name__'] + # Execute the template string in a temporary namespace and support + # tracing utilities by setting a value for frame.f_globals['__name__'] namespace = dict(__name__='namedtuple_%s' % typename) try: exec(class_definition, namespace) @@ -1122,44 +1127,3 @@ return self.__class__(self.data.translate(*args)) def upper(self): return self.__class__(self.data.upper()) def zfill(self, width): return self.__class__(self.data.zfill(width)) - - - -################################################################################ -### Simple tests -################################################################################ - -if __name__ == '__main__': - # verify that instances can be pickled - from pickle import loads, dumps - Point = namedtuple('Point', 'x, y', True) - p = Point(x=10, y=20) - assert p == loads(dumps(p)) - - # test and demonstrate ability to override methods - class Point(namedtuple('Point', 'x y')): - __slots__ = () - @property - def hypot(self): - return (self.x ** 2 + self.y ** 2) ** 0.5 - def __str__(self): - return 'Point: x=%6.3f y=%6.3f hypot=%6.3f' % (self.x, self.y, self.hypot) - - for p in Point(3, 4), Point(14, 5/7.): - print (p) - - class Point(namedtuple('Point', 'x y')): - 'Point class with optimized _make() and _replace() without error-checking' - __slots__ = () - _make = classmethod(tuple.__new__) - def _replace(self, _map=map, **kwds): - return self._make(_map(kwds.get, ('x', 'y'), self)) - - print(Point(11, 22)._replace(x=100)) - - Point3D = namedtuple('Point3D', Point._fields + ('z',)) - print(Point3D.__doc__) - - import doctest - TestResults = namedtuple('TestResults', 'failed attempted') - print(TestResults(*doctest.testmod())) diff --git a/Lib/collections/__main__.py b/Lib/collections/__main__.py new file mode 100644 --- /dev/null +++ b/Lib/collections/__main__.py @@ -0,0 +1,38 @@ +################################################################################ +### Simple tests +################################################################################ + +# verify that instances can be pickled +from collections import namedtuple +from pickle import loads, dumps +Point = namedtuple('Point', 'x, y', True) +p = Point(x=10, y=20) +assert p == loads(dumps(p)) + +# test and demonstrate ability to override methods +class Point(namedtuple('Point', 'x y')): + __slots__ = () + @property + def hypot(self): + return (self.x ** 2 + self.y ** 2) ** 0.5 + def __str__(self): + return 'Point: x=%6.3f y=%6.3f hypot=%6.3f' % (self.x, self.y, self.hypot) + +for p in Point(3, 4), Point(14, 5/7.): + print (p) + +class Point(namedtuple('Point', 'x y')): + 'Point class with optimized _make() and _replace() without error-checking' + __slots__ = () + _make = classmethod(tuple.__new__) + def _replace(self, _map=map, **kwds): + return self._make(_map(kwds.get, ('x', 'y'), self)) + +print(Point(11, 22)._replace(x=100)) + +Point3D = namedtuple('Point3D', Point._fields + ('z',)) +print(Point3D.__doc__) + +import doctest, collections +TestResults = namedtuple('TestResults', 'failed attempted') +print(TestResults(*doctest.testmod(collections))) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 10 04:15:34 2012 From: python-checkins at python.org (raymond.hettinger) Date: Sun, 10 Jun 2012 04:15:34 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Note_that_the_=5Fasdict=28?= =?utf8?q?=29_method_is_outdated?= Message-ID: http://hg.python.org/cpython/rev/fecbcd5c3978 changeset: 77397:fecbcd5c3978 user: Raymond Hettinger date: Sat Jun 09 19:15:26 2012 -0700 summary: Note that the _asdict() method is outdated files: Doc/library/collections.rst | 937 ++++++++++++----------- 1 files changed, 469 insertions(+), 468 deletions(-) diff --git a/Doc/library/collections.rst b/Doc/library/collections.rst --- a/Doc/library/collections.rst +++ b/Doc/library/collections.rst @@ -2,15 +2,15 @@ ========================================== .. module:: collections - :synopsis: Container datatypes + :synopsis: Container datatypes .. moduleauthor:: Raymond Hettinger .. sectionauthor:: Raymond Hettinger .. testsetup:: * - from collections import * - import itertools - __name__ = '' + from collections import * + import itertools + __name__ = '' **Source code:** :source:`Lib/collections/__init__.py` @@ -33,9 +33,9 @@ ===================== ==================================================================== .. versionchanged:: 3.3 - Moved :ref:`collections-abstract-base-classes` to the :mod:`collections.abc` module. - For backwards compatibility, they continue to be visible in this module - as well. + Moved :ref:`collections-abstract-base-classes` to the :mod:`collections.abc` module. + For backwards compatibility, they continue to be visible in this module + as well. :class:`ChainMap` objects @@ -51,105 +51,105 @@ .. class:: ChainMap(*maps) - A :class:`ChainMap` groups multiple dicts or other mappings together to - create a single, updateable view. If no *maps* are specified, a single empty - dictionary is provided so that a new chain always has at least one mapping. + A :class:`ChainMap` groups multiple dicts or other mappings together to + create a single, updateable view. If no *maps* are specified, a single empty + dictionary is provided so that a new chain always has at least one mapping. - The underlying mappings are stored in a list. That list is public and can - accessed or updated using the *maps* attribute. There is no other state. + The underlying mappings are stored in a list. That list is public and can + accessed or updated using the *maps* attribute. There is no other state. - Lookups search the underlying mappings successively until a key is found. In - contrast, writes, updates, and deletions only operate on the first mapping. + Lookups search the underlying mappings successively until a key is found. In + contrast, writes, updates, and deletions only operate on the first mapping. - A :class:`ChainMap` incorporates the underlying mappings by reference. So, if - one of the underlying mappings gets updated, those changes will be reflected - in :class:`ChainMap`. + A :class:`ChainMap` incorporates the underlying mappings by reference. So, if + one of the underlying mappings gets updated, those changes will be reflected + in :class:`ChainMap`. - All of the usual dictionary methods are supported. In addition, there is a - *maps* attribute, a method for creating new subcontexts, and a property for - accessing all but the first mapping: + All of the usual dictionary methods are supported. In addition, there is a + *maps* attribute, a method for creating new subcontexts, and a property for + accessing all but the first mapping: - .. attribute:: maps + .. attribute:: maps - A user updateable list of mappings. The list is ordered from - first-searched to last-searched. It is the only stored state and can - be modified to change which mappings are searched. The list should - always contain at least one mapping. + A user updateable list of mappings. The list is ordered from + first-searched to last-searched. It is the only stored state and can + be modified to change which mappings are searched. The list should + always contain at least one mapping. - .. method:: new_child() + .. method:: new_child() - Returns a new :class:`ChainMap` containing a new :class:`dict` followed by - all of the maps in the current instance. A call to ``d.new_child()`` is - equivalent to: ``ChainMap({}, *d.maps)``. This method is used for - creating subcontexts that can be updated without altering values in any - of the parent mappings. + Returns a new :class:`ChainMap` containing a new :class:`dict` followed by + all of the maps in the current instance. A call to ``d.new_child()`` is + equivalent to: ``ChainMap({}, *d.maps)``. This method is used for + creating subcontexts that can be updated without altering values in any + of the parent mappings. - .. method:: parents() + .. method:: parents() - Returns a new :class:`ChainMap` containing all of the maps in the current - instance except the first one. This is useful for skipping the first map - in the search. The use-cases are similar to those for the - :keyword:`nonlocal` keyword used in :term:`nested scopes `. - The use-cases also parallel those for the builtin :func:`super` function. - A reference to ``d.parents`` is equivalent to: ``ChainMap(*d.maps[1:])``. + Returns a new :class:`ChainMap` containing all of the maps in the current + instance except the first one. This is useful for skipping the first map + in the search. The use-cases are similar to those for the + :keyword:`nonlocal` keyword used in :term:`nested scopes `. + The use-cases also parallel those for the builtin :func:`super` function. + A reference to ``d.parents`` is equivalent to: ``ChainMap(*d.maps[1:])``. - Example of simulating Python's internal lookup chain:: + Example of simulating Python's internal lookup chain:: - import builtins - pylookup = ChainMap(locals(), globals(), vars(builtins)) + import builtins + pylookup = ChainMap(locals(), globals(), vars(builtins)) - Example of letting user specified values take precedence over environment - variables which in turn take precedence over default values:: + Example of letting user specified values take precedence over environment + variables which in turn take precedence over default values:: - import os, argparse - defaults = {'color': 'red', 'user': guest} - parser = argparse.ArgumentParser() - parser.add_argument('-u', '--user') - parser.add_argument('-c', '--color') - user_specified = vars(parser.parse_args()) - combined = ChainMap(user_specified, os.environ, defaults) + import os, argparse + defaults = {'color': 'red', 'user': guest} + parser = argparse.ArgumentParser() + parser.add_argument('-u', '--user') + parser.add_argument('-c', '--color') + user_specified = vars(parser.parse_args()) + combined = ChainMap(user_specified, os.environ, defaults) - Example patterns for using the :class:`ChainMap` class to simulate nested - contexts:: + Example patterns for using the :class:`ChainMap` class to simulate nested + contexts:: - c = ChainMap() # Create root context - d = c.new_child() # Create nested child context - e = c.new_child() # Child of c, independent from d - e.maps[0] # Current context dictionary -- like Python's locals() - e.maps[-1] # Root context -- like Python's globals() - e.parents # Enclosing context chain -- like Python's nonlocals + c = ChainMap() # Create root context + d = c.new_child() # Create nested child context + e = c.new_child() # Child of c, independent from d + e.maps[0] # Current context dictionary -- like Python's locals() + e.maps[-1] # Root context -- like Python's globals() + e.parents # Enclosing context chain -- like Python's nonlocals - d['x'] # Get first key in the chain of contexts - d['x'] = 1 # Set value in current context - del['x'] # Delete from current context - list(d) # All nested values - k in d # Check all nested values - len(d) # Number of nested values - d.items() # All nested items - dict(d) # Flatten into a regular dictionary + d['x'] # Get first key in the chain of contexts + d['x'] = 1 # Set value in current context + del['x'] # Delete from current context + list(d) # All nested values + k in d # Check all nested values + len(d) # Number of nested values + d.items() # All nested items + dict(d) # Flatten into a regular dictionary - .. seealso:: + .. seealso:: - * The `MultiContext class - `_ - in the Enthought `CodeTools package - `_ has options to support - writing to any mapping in the chain. + * The `MultiContext class + `_ + in the Enthought `CodeTools package + `_ has options to support + writing to any mapping in the chain. - * Django's `Context class - `_ - for templating is a read-only chain of mappings. It also features - pushing and popping of contexts similar to the - :meth:`~collections.ChainMap.new_child` method and the - :meth:`~collections.ChainMap.parents` property. + * Django's `Context class + `_ + for templating is a read-only chain of mappings. It also features + pushing and popping of contexts similar to the + :meth:`~collections.ChainMap.new_child` method and the + :meth:`~collections.ChainMap.parents` property. - * The `Nested Contexts recipe - `_ has options to control - whether writes and other mutations apply only to the first mapping or to - any mapping in the chain. + * The `Nested Contexts recipe + `_ has options to control + whether writes and other mutations apply only to the first mapping or to + any mapping in the chain. - * A `greatly simplified read-only version of Chainmap - `_. + * A `greatly simplified read-only version of Chainmap + `_. :class:`Counter` objects @@ -174,85 +174,85 @@ .. class:: Counter([iterable-or-mapping]) - A :class:`Counter` is a :class:`dict` subclass for counting hashable objects. - It is an unordered collection where elements are stored as dictionary keys - and their counts are stored as dictionary values. Counts are allowed to be - any integer value including zero or negative counts. The :class:`Counter` - class is similar to bags or multisets in other languages. + A :class:`Counter` is a :class:`dict` subclass for counting hashable objects. + It is an unordered collection where elements are stored as dictionary keys + and their counts are stored as dictionary values. Counts are allowed to be + any integer value including zero or negative counts. The :class:`Counter` + class is similar to bags or multisets in other languages. - Elements are counted from an *iterable* or initialized from another - *mapping* (or counter): + Elements are counted from an *iterable* or initialized from another + *mapping* (or counter): >>> c = Counter() # a new, empty counter >>> c = Counter('gallahad') # a new counter from an iterable >>> c = Counter({'red': 4, 'blue': 2}) # a new counter from a mapping >>> c = Counter(cats=4, dogs=8) # a new counter from keyword args - Counter objects have a dictionary interface except that they return a zero - count for missing items instead of raising a :exc:`KeyError`: + Counter objects have a dictionary interface except that they return a zero + count for missing items instead of raising a :exc:`KeyError`: >>> c = Counter(['eggs', 'ham']) >>> c['bacon'] # count of a missing element is zero 0 - Setting a count to zero does not remove an element from a counter. - Use ``del`` to remove it entirely: + Setting a count to zero does not remove an element from a counter. + Use ``del`` to remove it entirely: >>> c['sausage'] = 0 # counter entry with a zero count >>> del c['sausage'] # del actually removes the entry - .. versionadded:: 3.1 + .. versionadded:: 3.1 - Counter objects support three methods beyond those available for all - dictionaries: + Counter objects support three methods beyond those available for all + dictionaries: - .. method:: elements() + .. method:: elements() - Return an iterator over elements repeating each as many times as its - count. Elements are returned in arbitrary order. If an element's count - is less than one, :meth:`elements` will ignore it. + Return an iterator over elements repeating each as many times as its + count. Elements are returned in arbitrary order. If an element's count + is less than one, :meth:`elements` will ignore it. >>> c = Counter(a=4, b=2, c=0, d=-2) >>> list(c.elements()) ['a', 'a', 'a', 'a', 'b', 'b'] - .. method:: most_common([n]) + .. method:: most_common([n]) - Return a list of the *n* most common elements and their counts from the - most common to the least. If *n* is not specified, :func:`most_common` - returns *all* elements in the counter. Elements with equal counts are - ordered arbitrarily: + Return a list of the *n* most common elements and their counts from the + most common to the least. If *n* is not specified, :func:`most_common` + returns *all* elements in the counter. Elements with equal counts are + ordered arbitrarily: >>> Counter('abracadabra').most_common(3) [('a', 5), ('r', 2), ('b', 2)] - .. method:: subtract([iterable-or-mapping]) + .. method:: subtract([iterable-or-mapping]) - Elements are subtracted from an *iterable* or from another *mapping* - (or counter). Like :meth:`dict.update` but subtracts counts instead - of replacing them. Both inputs and outputs may be zero or negative. + Elements are subtracted from an *iterable* or from another *mapping* + (or counter). Like :meth:`dict.update` but subtracts counts instead + of replacing them. Both inputs and outputs may be zero or negative. >>> c = Counter(a=4, b=2, c=0, d=-2) >>> d = Counter(a=1, b=2, c=3, d=4) >>> c.subtract(d) Counter({'a': 3, 'b': 0, 'c': -3, 'd': -6}) - .. versionadded:: 3.2 + .. versionadded:: 3.2 - The usual dictionary methods are available for :class:`Counter` objects - except for two which work differently for counters. + The usual dictionary methods are available for :class:`Counter` objects + except for two which work differently for counters. - .. method:: fromkeys(iterable) + .. method:: fromkeys(iterable) - This class method is not implemented for :class:`Counter` objects. + This class method is not implemented for :class:`Counter` objects. - .. method:: update([iterable-or-mapping]) + .. method:: update([iterable-or-mapping]) - Elements are counted from an *iterable* or added-in from another - *mapping* (or counter). Like :meth:`dict.update` but adds counts - instead of replacing them. Also, the *iterable* is expected to be a - sequence of elements, not a sequence of ``(key, value)`` pairs. + Elements are counted from an *iterable* or added-in from another + *mapping* (or counter). Like :meth:`dict.update` but adds counts + instead of replacing them. Also, the *iterable* is expected to be a + sequence of elements, not a sequence of ``(key, value)`` pairs. Common patterns for working with :class:`Counter` objects:: @@ -294,57 +294,57 @@ Counter({'b': 4}) .. versionadded:: 3.3 - Added support for unary plus, unary minus, and in-place multiset operations. + Added support for unary plus, unary minus, and in-place multiset operations. .. note:: - Counters were primarily designed to work with positive integers to represent - running counts; however, care was taken to not unnecessarily preclude use - cases needing other types or negative values. To help with those use cases, - this section documents the minimum range and type restrictions. + Counters were primarily designed to work with positive integers to represent + running counts; however, care was taken to not unnecessarily preclude use + cases needing other types or negative values. To help with those use cases, + this section documents the minimum range and type restrictions. - * The :class:`Counter` class itself is a dictionary subclass with no - restrictions on its keys and values. The values are intended to be numbers - representing counts, but you *could* store anything in the value field. + * The :class:`Counter` class itself is a dictionary subclass with no + restrictions on its keys and values. The values are intended to be numbers + representing counts, but you *could* store anything in the value field. - * The :meth:`most_common` method requires only that the values be orderable. + * The :meth:`most_common` method requires only that the values be orderable. - * For in-place operations such as ``c[key] += 1``, the value type need only - support addition and subtraction. So fractions, floats, and decimals would - work and negative values are supported. The same is also true for - :meth:`update` and :meth:`subtract` which allow negative and zero values - for both inputs and outputs. + * For in-place operations such as ``c[key] += 1``, the value type need only + support addition and subtraction. So fractions, floats, and decimals would + work and negative values are supported. The same is also true for + :meth:`update` and :meth:`subtract` which allow negative and zero values + for both inputs and outputs. - * The multiset methods are designed only for use cases with positive values. - The inputs may be negative or zero, but only outputs with positive values - are created. There are no type restrictions, but the value type needs to - support addition, subtraction, and comparison. + * The multiset methods are designed only for use cases with positive values. + The inputs may be negative or zero, but only outputs with positive values + are created. There are no type restrictions, but the value type needs to + support addition, subtraction, and comparison. - * The :meth:`elements` method requires integer counts. It ignores zero and - negative counts. + * The :meth:`elements` method requires integer counts. It ignores zero and + negative counts. .. seealso:: * `Counter class `_ - adapted for Python 2.5 and an early `Bag recipe - `_ for Python 2.4. + adapted for Python 2.5 and an early `Bag recipe + `_ for Python 2.4. * `Bag class `_ - in Smalltalk. + in Smalltalk. * Wikipedia entry for `Multisets `_. * `C++ multisets `_ - tutorial with examples. + tutorial with examples. * For mathematical operations on multisets and their use cases, see - *Knuth, Donald. The Art of Computer Programming Volume II, - Section 4.6.3, Exercise 19*. + *Knuth, Donald. The Art of Computer Programming Volume II, + Section 4.6.3, Exercise 19*. * To enumerate all distinct multisets of a given size over a given set of - elements, see :func:`itertools.combinations_with_replacement`. + elements, see :func:`itertools.combinations_with_replacement`. - map(Counter, combinations_with_replacement('ABC', 2)) --> AA AB AC BB BC CC + map(Counter, combinations_with_replacement('ABC', 2)) --> AA AB AC BB BC CC :class:`deque` objects @@ -352,105 +352,105 @@ .. class:: deque([iterable, [maxlen]]) - Returns a new deque object initialized left-to-right (using :meth:`append`) with - data from *iterable*. If *iterable* is not specified, the new deque is empty. + Returns a new deque object initialized left-to-right (using :meth:`append`) with + data from *iterable*. If *iterable* is not specified, the new deque is empty. - Deques are a generalization of stacks and queues (the name is pronounced "deck" - and is short for "double-ended queue"). Deques support thread-safe, memory - efficient appends and pops from either side of the deque with approximately the - same O(1) performance in either direction. + Deques are a generalization of stacks and queues (the name is pronounced "deck" + and is short for "double-ended queue"). Deques support thread-safe, memory + efficient appends and pops from either side of the deque with approximately the + same O(1) performance in either direction. - Though :class:`list` objects support similar operations, they are optimized for - fast fixed-length operations and incur O(n) memory movement costs for - ``pop(0)`` and ``insert(0, v)`` operations which change both the size and - position of the underlying data representation. + Though :class:`list` objects support similar operations, they are optimized for + fast fixed-length operations and incur O(n) memory movement costs for + ``pop(0)`` and ``insert(0, v)`` operations which change both the size and + position of the underlying data representation. - If *maxlen* is not specified or is *None*, deques may grow to an - arbitrary length. Otherwise, the deque is bounded to the specified maximum - length. Once a bounded length deque is full, when new items are added, a - corresponding number of items are discarded from the opposite end. Bounded - length deques provide functionality similar to the ``tail`` filter in - Unix. They are also useful for tracking transactions and other pools of data - where only the most recent activity is of interest. + If *maxlen* is not specified or is *None*, deques may grow to an + arbitrary length. Otherwise, the deque is bounded to the specified maximum + length. Once a bounded length deque is full, when new items are added, a + corresponding number of items are discarded from the opposite end. Bounded + length deques provide functionality similar to the ``tail`` filter in + Unix. They are also useful for tracking transactions and other pools of data + where only the most recent activity is of interest. - Deque objects support the following methods: + Deque objects support the following methods: - .. method:: append(x) + .. method:: append(x) - Add *x* to the right side of the deque. + Add *x* to the right side of the deque. - .. method:: appendleft(x) + .. method:: appendleft(x) - Add *x* to the left side of the deque. + Add *x* to the left side of the deque. - .. method:: clear() + .. method:: clear() - Remove all elements from the deque leaving it with length 0. + Remove all elements from the deque leaving it with length 0. - .. method:: count(x) + .. method:: count(x) - Count the number of deque elements equal to *x*. + Count the number of deque elements equal to *x*. - .. versionadded:: 3.2 + .. versionadded:: 3.2 - .. method:: extend(iterable) + .. method:: extend(iterable) - Extend the right side of the deque by appending elements from the iterable - argument. + Extend the right side of the deque by appending elements from the iterable + argument. - .. method:: extendleft(iterable) + .. method:: extendleft(iterable) - Extend the left side of the deque by appending elements from *iterable*. - Note, the series of left appends results in reversing the order of - elements in the iterable argument. + Extend the left side of the deque by appending elements from *iterable*. + Note, the series of left appends results in reversing the order of + elements in the iterable argument. - .. method:: pop() + .. method:: pop() - Remove and return an element from the right side of the deque. If no - elements are present, raises an :exc:`IndexError`. + Remove and return an element from the right side of the deque. If no + elements are present, raises an :exc:`IndexError`. - .. method:: popleft() + .. method:: popleft() - Remove and return an element from the left side of the deque. If no - elements are present, raises an :exc:`IndexError`. + Remove and return an element from the left side of the deque. If no + elements are present, raises an :exc:`IndexError`. - .. method:: remove(value) + .. method:: remove(value) - Removed the first occurrence of *value*. If not found, raises a - :exc:`ValueError`. + Removed the first occurrence of *value*. If not found, raises a + :exc:`ValueError`. - .. method:: reverse() + .. method:: reverse() - Reverse the elements of the deque in-place and then return ``None``. + Reverse the elements of the deque in-place and then return ``None``. - .. versionadded:: 3.2 + .. versionadded:: 3.2 - .. method:: rotate(n) + .. method:: rotate(n) - Rotate the deque *n* steps to the right. If *n* is negative, rotate to - the left. Rotating one step to the right is equivalent to: - ``d.appendleft(d.pop())``. + Rotate the deque *n* steps to the right. If *n* is negative, rotate to + the left. Rotating one step to the right is equivalent to: + ``d.appendleft(d.pop())``. - Deque objects also provide one read-only attribute: + Deque objects also provide one read-only attribute: - .. attribute:: maxlen + .. attribute:: maxlen - Maximum size of a deque or *None* if unbounded. + Maximum size of a deque or *None* if unbounded. - .. versionadded:: 3.1 + .. versionadded:: 3.1 In addition to the above, deques support iteration, pickling, ``len(d)``, @@ -463,56 +463,56 @@ .. doctest:: - >>> from collections import deque - >>> d = deque('ghi') # make a new deque with three items - >>> for elem in d: # iterate over the deque's elements - ... print(elem.upper()) - G - H - I + >>> from collections import deque + >>> d = deque('ghi') # make a new deque with three items + >>> for elem in d: # iterate over the deque's elements + ... print(elem.upper()) + G + H + I - >>> d.append('j') # add a new entry to the right side - >>> d.appendleft('f') # add a new entry to the left side - >>> d # show the representation of the deque - deque(['f', 'g', 'h', 'i', 'j']) + >>> d.append('j') # add a new entry to the right side + >>> d.appendleft('f') # add a new entry to the left side + >>> d # show the representation of the deque + deque(['f', 'g', 'h', 'i', 'j']) - >>> d.pop() # return and remove the rightmost item - 'j' - >>> d.popleft() # return and remove the leftmost item - 'f' - >>> list(d) # list the contents of the deque - ['g', 'h', 'i'] - >>> d[0] # peek at leftmost item - 'g' - >>> d[-1] # peek at rightmost item - 'i' + >>> d.pop() # return and remove the rightmost item + 'j' + >>> d.popleft() # return and remove the leftmost item + 'f' + >>> list(d) # list the contents of the deque + ['g', 'h', 'i'] + >>> d[0] # peek at leftmost item + 'g' + >>> d[-1] # peek at rightmost item + 'i' - >>> list(reversed(d)) # list the contents of a deque in reverse - ['i', 'h', 'g'] - >>> 'h' in d # search the deque - True - >>> d.extend('jkl') # add multiple elements at once - >>> d - deque(['g', 'h', 'i', 'j', 'k', 'l']) - >>> d.rotate(1) # right rotation - >>> d - deque(['l', 'g', 'h', 'i', 'j', 'k']) - >>> d.rotate(-1) # left rotation - >>> d - deque(['g', 'h', 'i', 'j', 'k', 'l']) + >>> list(reversed(d)) # list the contents of a deque in reverse + ['i', 'h', 'g'] + >>> 'h' in d # search the deque + True + >>> d.extend('jkl') # add multiple elements at once + >>> d + deque(['g', 'h', 'i', 'j', 'k', 'l']) + >>> d.rotate(1) # right rotation + >>> d + deque(['l', 'g', 'h', 'i', 'j', 'k']) + >>> d.rotate(-1) # left rotation + >>> d + deque(['g', 'h', 'i', 'j', 'k', 'l']) - >>> deque(reversed(d)) # make a new deque in reverse order - deque(['l', 'k', 'j', 'i', 'h', 'g']) - >>> d.clear() # empty the deque - >>> d.pop() # cannot pop from an empty deque - Traceback (most recent call last): - File "", line 1, in -toplevel- - d.pop() - IndexError: pop from an empty deque + >>> deque(reversed(d)) # make a new deque in reverse order + deque(['l', 'k', 'j', 'i', 'h', 'g']) + >>> d.clear() # empty the deque + >>> d.pop() # cannot pop from an empty deque + Traceback (most recent call last): + File "", line 1, in -toplevel- + d.pop() + IndexError: pop from an empty deque - >>> d.extendleft('abc') # extendleft() reverses the input order - >>> d - deque(['c', 'b', 'a']) + >>> d.extendleft('abc') # extendleft() reverses the input order + >>> d + deque(['c', 'b', 'a']) :class:`deque` Recipes @@ -523,10 +523,10 @@ Bounded length deques provide functionality similar to the ``tail`` filter in Unix:: - def tail(filename, n=10): - 'Return the last n lines of a file' - with open(filename) as f: - return deque(f, n) + def tail(filename, n=10): + 'Return the last n lines of a file' + with open(filename) as f: + return deque(f, n) Another approach to using deques is to maintain a sequence of recently added elements by appending to the right and popping to the left:: @@ -547,10 +547,10 @@ deletion. For example, a pure Python implementation of ``del d[n]`` relies on the :meth:`rotate` method to position elements to be popped:: - def delete_nth(d, n): - d.rotate(-n) - d.popleft() - d.rotate(n) + def delete_nth(d, n): + d.rotate(-n) + d.popleft() + d.rotate(n) To implement :class:`deque` slicing, use a similar approach applying :meth:`rotate` to bring a target element to the left side of the deque. Remove @@ -566,50 +566,50 @@ .. class:: defaultdict([default_factory[, ...]]) - Returns a new dictionary-like object. :class:`defaultdict` is a subclass of the - built-in :class:`dict` class. It overrides one method and adds one writable - instance variable. The remaining functionality is the same as for the - :class:`dict` class and is not documented here. + Returns a new dictionary-like object. :class:`defaultdict` is a subclass of the + built-in :class:`dict` class. It overrides one method and adds one writable + instance variable. The remaining functionality is the same as for the + :class:`dict` class and is not documented here. - The first argument provides the initial value for the :attr:`default_factory` - attribute; it defaults to ``None``. All remaining arguments are treated the same - as if they were passed to the :class:`dict` constructor, including keyword - arguments. + The first argument provides the initial value for the :attr:`default_factory` + attribute; it defaults to ``None``. All remaining arguments are treated the same + as if they were passed to the :class:`dict` constructor, including keyword + arguments. - :class:`defaultdict` objects support the following method in addition to the - standard :class:`dict` operations: + :class:`defaultdict` objects support the following method in addition to the + standard :class:`dict` operations: - .. method:: __missing__(key) + .. method:: __missing__(key) - If the :attr:`default_factory` attribute is ``None``, this raises a - :exc:`KeyError` exception with the *key* as argument. + If the :attr:`default_factory` attribute is ``None``, this raises a + :exc:`KeyError` exception with the *key* as argument. - If :attr:`default_factory` is not ``None``, it is called without arguments - to provide a default value for the given *key*, this value is inserted in - the dictionary for the *key*, and returned. + If :attr:`default_factory` is not ``None``, it is called without arguments + to provide a default value for the given *key*, this value is inserted in + the dictionary for the *key*, and returned. - If calling :attr:`default_factory` raises an exception this exception is - propagated unchanged. + If calling :attr:`default_factory` raises an exception this exception is + propagated unchanged. - This method is called by the :meth:`__getitem__` method of the - :class:`dict` class when the requested key is not found; whatever it - returns or raises is then returned or raised by :meth:`__getitem__`. + This method is called by the :meth:`__getitem__` method of the + :class:`dict` class when the requested key is not found; whatever it + returns or raises is then returned or raised by :meth:`__getitem__`. - Note that :meth:`__missing__` is *not* called for any operations besides - :meth:`__getitem__`. This means that :meth:`get` will, like normal - dictionaries, return ``None`` as a default rather than using - :attr:`default_factory`. + Note that :meth:`__missing__` is *not* called for any operations besides + :meth:`__getitem__`. This means that :meth:`get` will, like normal + dictionaries, return ``None`` as a default rather than using + :attr:`default_factory`. - :class:`defaultdict` objects support the following instance variable: + :class:`defaultdict` objects support the following instance variable: - .. attribute:: default_factory + .. attribute:: default_factory - This attribute is used by the :meth:`__missing__` method; it is - initialized from the first argument to the constructor, if present, or to - ``None``, if absent. + This attribute is used by the :meth:`__missing__` method; it is + initialized from the first argument to the constructor, if present, or to + ``None``, if absent. :class:`defaultdict` Examples @@ -618,13 +618,13 @@ Using :class:`list` as the :attr:`default_factory`, it is easy to group a sequence of key-value pairs into a dictionary of lists: - >>> s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)] - >>> d = defaultdict(list) - >>> for k, v in s: - ... d[k].append(v) - ... - >>> list(d.items()) - [('blue', [2, 4]), ('red', [1]), ('yellow', [1, 3])] + >>> s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)] + >>> d = defaultdict(list) + >>> for k, v in s: + ... d[k].append(v) + ... + >>> list(d.items()) + [('blue', [2, 4]), ('red', [1]), ('yellow', [1, 3])] When each key is encountered for the first time, it is not already in the mapping; so an entry is automatically created using the :attr:`default_factory` @@ -634,24 +634,24 @@ :meth:`list.append` operation adds another value to the list. This technique is simpler and faster than an equivalent technique using :meth:`dict.setdefault`: - >>> d = {} - >>> for k, v in s: - ... d.setdefault(k, []).append(v) - ... - >>> list(d.items()) - [('blue', [2, 4]), ('red', [1]), ('yellow', [1, 3])] + >>> d = {} + >>> for k, v in s: + ... d.setdefault(k, []).append(v) + ... + >>> list(d.items()) + [('blue', [2, 4]), ('red', [1]), ('yellow', [1, 3])] Setting the :attr:`default_factory` to :class:`int` makes the :class:`defaultdict` useful for counting (like a bag or multiset in other languages): - >>> s = 'mississippi' - >>> d = defaultdict(int) - >>> for k in s: - ... d[k] += 1 - ... - >>> list(d.items()) - [('i', 4), ('p', 2), ('s', 4), ('m', 1)] + >>> s = 'mississippi' + >>> d = defaultdict(int) + >>> for k in s: + ... d[k] += 1 + ... + >>> list(d.items()) + [('i', 4), ('p', 2), ('s', 4), ('m', 1)] When a letter is first encountered, it is missing from the mapping, so the :attr:`default_factory` function calls :func:`int` to supply a default count of @@ -662,23 +662,23 @@ is to use a lambda function which can supply any constant value (not just zero): - >>> def constant_factory(value): - ... return lambda: value - >>> d = defaultdict(constant_factory('')) - >>> d.update(name='John', action='ran') - >>> '%(name)s %(action)s to %(object)s' % d - 'John ran to ' + >>> def constant_factory(value): + ... return lambda: value + >>> d = defaultdict(constant_factory('')) + >>> d.update(name='John', action='ran') + >>> '%(name)s %(action)s to %(object)s' % d + 'John ran to ' Setting the :attr:`default_factory` to :class:`set` makes the :class:`defaultdict` useful for building a dictionary of sets: - >>> s = [('red', 1), ('blue', 2), ('red', 3), ('blue', 4), ('red', 1), ('blue', 4)] - >>> d = defaultdict(set) - >>> for k, v in s: - ... d[k].add(v) - ... - >>> list(d.items()) - [('blue', {2, 4}), ('red', {1, 3})] + >>> s = [('red', 1), ('blue', 2), ('red', 3), ('blue', 4), ('red', 1), ('blue', 4)] + >>> d = defaultdict(set) + >>> for k, v in s: + ... d[k].add(v) + ... + >>> list(d.items()) + [('blue', {2, 4}), ('red', {1, 3})] :func:`namedtuple` Factory Function for Tuples with Named Fields @@ -690,69 +690,69 @@ .. function:: namedtuple(typename, field_names, verbose=False, rename=False) - Returns a new tuple subclass named *typename*. The new subclass is used to - create tuple-like objects that have fields accessible by attribute lookup as - well as being indexable and iterable. Instances of the subclass also have a - helpful docstring (with typename and field_names) and a helpful :meth:`__repr__` - method which lists the tuple contents in a ``name=value`` format. + Returns a new tuple subclass named *typename*. The new subclass is used to + create tuple-like objects that have fields accessible by attribute lookup as + well as being indexable and iterable. Instances of the subclass also have a + helpful docstring (with typename and field_names) and a helpful :meth:`__repr__` + method which lists the tuple contents in a ``name=value`` format. - The *field_names* are a single string with each fieldname separated by whitespace - and/or commas, for example ``'x y'`` or ``'x, y'``. Alternatively, *field_names* - can be a sequence of strings such as ``['x', 'y']``. + The *field_names* are a single string with each fieldname separated by whitespace + and/or commas, for example ``'x y'`` or ``'x, y'``. Alternatively, *field_names* + can be a sequence of strings such as ``['x', 'y']``. - Any valid Python identifier may be used for a fieldname except for names - starting with an underscore. Valid identifiers consist of letters, digits, - and underscores but do not start with a digit or underscore and cannot be - a :mod:`keyword` such as *class*, *for*, *return*, *global*, *pass*, - or *raise*. + Any valid Python identifier may be used for a fieldname except for names + starting with an underscore. Valid identifiers consist of letters, digits, + and underscores but do not start with a digit or underscore and cannot be + a :mod:`keyword` such as *class*, *for*, *return*, *global*, *pass*, + or *raise*. - If *rename* is true, invalid fieldnames are automatically replaced - with positional names. For example, ``['abc', 'def', 'ghi', 'abc']`` is - converted to ``['abc', '_1', 'ghi', '_3']``, eliminating the keyword - ``def`` and the duplicate fieldname ``abc``. + If *rename* is true, invalid fieldnames are automatically replaced + with positional names. For example, ``['abc', 'def', 'ghi', 'abc']`` is + converted to ``['abc', '_1', 'ghi', '_3']``, eliminating the keyword + ``def`` and the duplicate fieldname ``abc``. - If *verbose* is true, the class definition is printed after it is - built. This option is outdated; instead, it is simpler to print the - :attr:`_source` attribute. + If *verbose* is true, the class definition is printed after it is + built. This option is outdated; instead, it is simpler to print the + :attr:`_source` attribute. - Named tuple instances do not have per-instance dictionaries, so they are - lightweight and require no more memory than regular tuples. + Named tuple instances do not have per-instance dictionaries, so they are + lightweight and require no more memory than regular tuples. - .. versionchanged:: 3.1 - Added support for *rename*. + .. versionchanged:: 3.1 + Added support for *rename*. .. doctest:: - :options: +NORMALIZE_WHITESPACE + :options: +NORMALIZE_WHITESPACE - >>> # Basic example - >>> Point = namedtuple('Point', ['x', 'y']) - >>> p = Point(11, y=22) # instantiate with positional or keyword arguments - >>> p[0] + p[1] # indexable like the plain tuple (11, 22) - 33 - >>> x, y = p # unpack like a regular tuple - >>> x, y - (11, 22) - >>> p.x + p.y # fields also accessible by name - 33 - >>> p # readable __repr__ with a name=value style - Point(x=11, y=22) + >>> # Basic example + >>> Point = namedtuple('Point', ['x', 'y']) + >>> p = Point(11, y=22) # instantiate with positional or keyword arguments + >>> p[0] + p[1] # indexable like the plain tuple (11, 22) + 33 + >>> x, y = p # unpack like a regular tuple + >>> x, y + (11, 22) + >>> p.x + p.y # fields also accessible by name + 33 + >>> p # readable __repr__ with a name=value style + Point(x=11, y=22) Named tuples are especially useful for assigning field names to result tuples returned by the :mod:`csv` or :mod:`sqlite3` modules:: - EmployeeRecord = namedtuple('EmployeeRecord', 'name, age, title, department, paygrade') + EmployeeRecord = namedtuple('EmployeeRecord', 'name, age, title, department, paygrade') - import csv - for emp in map(EmployeeRecord._make, csv.reader(open("employees.csv", "rb"))): - print(emp.name, emp.title) + import csv + for emp in map(EmployeeRecord._make, csv.reader(open("employees.csv", "rb"))): + print(emp.name, emp.title) - import sqlite3 - conn = sqlite3.connect('/companydata') - cursor = conn.cursor() - cursor.execute('SELECT name, age, title, department, paygrade FROM employees') - for emp in map(EmployeeRecord._make, cursor.fetchall()): - print(emp.name, emp.title) + import sqlite3 + conn = sqlite3.connect('/companydata') + cursor = conn.cursor() + cursor.execute('SELECT name, age, title, department, paygrade FROM employees') + for emp in map(EmployeeRecord._make, cursor.fetchall()): + print(emp.name, emp.title) In addition to the methods inherited from tuples, named tuples support three additional methods and two attributes. To prevent conflicts with @@ -760,62 +760,63 @@ .. classmethod:: somenamedtuple._make(iterable) - Class method that makes a new instance from an existing sequence or iterable. + Class method that makes a new instance from an existing sequence or iterable. .. doctest:: - >>> t = [11, 22] - >>> Point._make(t) - Point(x=11, y=22) + >>> t = [11, 22] + >>> Point._make(t) + Point(x=11, y=22) .. method:: somenamedtuple._asdict() - Return a new :class:`OrderedDict` which maps field names to their corresponding - values:: + Return a new :class:`OrderedDict` which maps field names to their corresponding + values. Note, this method is no longer needed now that the same effect can + be achieved by using the built-in :func:`vars` function:: - >>> p._asdict() - OrderedDict([('x', 11), ('y', 22)]) + >>> vars(p) + OrderedDict([('x', 11), ('y', 22)]) - .. versionchanged:: 3.1 - Returns an :class:`OrderedDict` instead of a regular :class:`dict`. + .. versionchanged:: 3.1 + Returns an :class:`OrderedDict` instead of a regular :class:`dict`. .. method:: somenamedtuple._replace(kwargs) - Return a new instance of the named tuple replacing specified fields with new - values: + Return a new instance of the named tuple replacing specified fields with new + values: :: - >>> p = Point(x=11, y=22) - >>> p._replace(x=33) - Point(x=33, y=22) + >>> p = Point(x=11, y=22) + >>> p._replace(x=33) + Point(x=33, y=22) - >>> for partnum, record in inventory.items(): - ... inventory[partnum] = record._replace(price=newprices[partnum], timestamp=time.now()) + >>> for partnum, record in inventory.items(): + ... inventory[partnum] = record._replace(price=newprices[partnum], timestamp=time.now()) .. attribute:: somenamedtuple._source - A string with the pure Python source code used to create the named - tuple class. The source makes the named tuple self-documenting. - It can be printed, executed using :func:`exec`, or saved to a file - and imported. + A string with the pure Python source code used to create the named + tuple class. The source makes the named tuple self-documenting. + It can be printed, executed using :func:`exec`, or saved to a file + and imported. - .. versionadded:: 3.3 + .. versionadded:: 3.3 .. attribute:: somenamedtuple._fields - Tuple of strings listing the field names. Useful for introspection - and for creating new named tuple types from existing named tuples. + Tuple of strings listing the field names. Useful for introspection + and for creating new named tuple types from existing named tuples. .. doctest:: - >>> p._fields # view the field names - ('x', 'y') + >>> p._fields # view the field names + ('x', 'y') - >>> Color = namedtuple('Color', 'red green blue') - >>> Pixel = namedtuple('Pixel', Point._fields + Color._fields) - >>> Pixel(11, 22, 128, 255, 0) - Pixel(x=11, y=22, red=128, green=255, blue=0) + >>> Color = namedtuple('Color', 'red green blue') + >>> Pixel = namedtuple('Pixel', Point._fields + Color._fields) + >>> Pixel(11, 22, 128, 255, 0) + Pixel(x=11, y=22, red=128, green=255, blue=0) To retrieve a field whose name is stored in a string, use the :func:`getattr` function: @@ -826,24 +827,24 @@ To convert a dictionary to a named tuple, use the double-star-operator (as described in :ref:`tut-unpacking-arguments`): - >>> d = {'x': 11, 'y': 22} - >>> Point(**d) - Point(x=11, y=22) + >>> d = {'x': 11, 'y': 22} + >>> Point(**d) + Point(x=11, y=22) Since a named tuple is a regular Python class, it is easy to add or change functionality with a subclass. Here is how to add a calculated field and a fixed-width print format: >>> class Point(namedtuple('Point', 'x y')): - __slots__ = () - @property - def hypot(self): - return (self.x ** 2 + self.y ** 2) ** 0.5 - def __str__(self): - return 'Point: x=%6.3f y=%6.3f hypot=%6.3f' % (self.x, self.y, self.hypot) + __slots__ = () + @property + def hypot(self): + return (self.x ** 2 + self.y ** 2) ** 0.5 + def __str__(self): + return 'Point: x=%6.3f y=%6.3f hypot=%6.3f' % (self.x, self.y, self.hypot) >>> for p in Point(3, 4), Point(14, 5/7): - print(p) + print(p) Point: x= 3.000 y= 4.000 hypot= 5.000 Point: x=14.000 y= 0.714 hypot=14.018 @@ -870,19 +871,19 @@ >>> Status.open, Status.pending, Status.closed (0, 1, 2) >>> class Status: - open, pending, closed = range(3) + open, pending, closed = range(3) .. seealso:: - * `Named tuple recipe `_ - adapted for Python 2.4. + * `Named tuple recipe `_ + adapted for Python 2.4. - * `Recipe for named tuple abstract base class with a metaclass mix-in - `_ - by Jan Kaliszewski. Besides providing an :term:`abstract base class` for - named tuples, it also supports an alternate :term:`metaclass`-based - constructor that is convenient for use cases where named tuples are being - subclassed. + * `Recipe for named tuple abstract base class with a metaclass mix-in + `_ + by Jan Kaliszewski. Besides providing an :term:`abstract base class` for + named tuples, it also supports an alternate :term:`metaclass`-based + constructor that is convenient for use cases where named tuples are being + subclassed. :class:`OrderedDict` objects @@ -894,36 +895,36 @@ .. class:: OrderedDict([items]) - Return an instance of a dict subclass, supporting the usual :class:`dict` - methods. An *OrderedDict* is a dict that remembers the order that keys - were first inserted. If a new entry overwrites an existing entry, the - original insertion position is left unchanged. Deleting an entry and - reinserting it will move it to the end. + Return an instance of a dict subclass, supporting the usual :class:`dict` + methods. An *OrderedDict* is a dict that remembers the order that keys + were first inserted. If a new entry overwrites an existing entry, the + original insertion position is left unchanged. Deleting an entry and + reinserting it will move it to the end. - .. versionadded:: 3.1 + .. versionadded:: 3.1 - .. method:: popitem(last=True) + .. method:: popitem(last=True) - The :meth:`popitem` method for ordered dictionaries returns and removes a - (key, value) pair. The pairs are returned in LIFO order if *last* is true - or FIFO order if false. + The :meth:`popitem` method for ordered dictionaries returns and removes a + (key, value) pair. The pairs are returned in LIFO order if *last* is true + or FIFO order if false. - .. method:: move_to_end(key, last=True) + .. method:: move_to_end(key, last=True) - Move an existing *key* to either end of an ordered dictionary. The item - is moved to the right end if *last* is true (the default) or to the - beginning if *last* is false. Raises :exc:`KeyError` if the *key* does - not exist:: + Move an existing *key* to either end of an ordered dictionary. The item + is moved to the right end if *last* is true (the default) or to the + beginning if *last* is false. Raises :exc:`KeyError` if the *key* does + not exist:: - >>> d = OrderedDict.fromkeys('abcde') - >>> d.move_to_end('b') - >>> ''.join(d.keys()) - 'acdeb' - >>> d.move_to_end('b', last=False) - >>> ''.join(d.keys()) - 'bacde' + >>> d = OrderedDict.fromkeys('abcde') + >>> d.move_to_end('b') + >>> ''.join(d.keys()) + 'acdeb' + >>> d.move_to_end('b', last=False) + >>> ''.join(d.keys()) + 'bacde' - .. versionadded:: 3.2 + .. versionadded:: 3.2 In addition to the usual mapping methods, ordered dictionaries also support reverse iteration using :func:`reversed`. @@ -941,8 +942,8 @@ .. seealso:: - `Equivalent OrderedDict recipe `_ - that runs on Python 2.4 or later. + `Equivalent OrderedDict recipe `_ + that runs on Python 2.4 or later. :class:`OrderedDict` Examples and Recipes ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -985,7 +986,7 @@ An ordered dictionary can be combined with the :class:`Counter` class so that the counter remembers the order elements are first encountered:: - class OrderedCounter(Counter, OrderedDict): + class OrderedCounter(Counter, OrderedDict): 'Counter that remembers the order elements are first encountered' def __repr__(self): @@ -1006,19 +1007,19 @@ .. class:: UserDict([initialdata]) - Class that simulates a dictionary. The instance's contents are kept in a - regular dictionary, which is accessible via the :attr:`data` attribute of - :class:`UserDict` instances. If *initialdata* is provided, :attr:`data` is - initialized with its contents; note that a reference to *initialdata* will not - be kept, allowing it be used for other purposes. + Class that simulates a dictionary. The instance's contents are kept in a + regular dictionary, which is accessible via the :attr:`data` attribute of + :class:`UserDict` instances. If *initialdata* is provided, :attr:`data` is + initialized with its contents; note that a reference to *initialdata* will not + be kept, allowing it be used for other purposes. - In addition to supporting the methods and operations of mappings, - :class:`UserDict` instances provide the following attribute: + In addition to supporting the methods and operations of mappings, + :class:`UserDict` instances provide the following attribute: - .. attribute:: data + .. attribute:: data - A real dictionary used to store the contents of the :class:`UserDict` - class. + A real dictionary used to store the contents of the :class:`UserDict` + class. @@ -1036,19 +1037,19 @@ .. class:: UserList([list]) - Class that simulates a list. The instance's contents are kept in a regular - list, which is accessible via the :attr:`data` attribute of :class:`UserList` - instances. The instance's contents are initially set to a copy of *list*, - defaulting to the empty list ``[]``. *list* can be any iterable, for - example a real Python list or a :class:`UserList` object. + Class that simulates a list. The instance's contents are kept in a regular + list, which is accessible via the :attr:`data` attribute of :class:`UserList` + instances. The instance's contents are initially set to a copy of *list*, + defaulting to the empty list ``[]``. *list* can be any iterable, for + example a real Python list or a :class:`UserList` object. - In addition to supporting the methods and operations of mutable sequences, - :class:`UserList` instances provide the following attribute: + In addition to supporting the methods and operations of mutable sequences, + :class:`UserList` instances provide the following attribute: - .. attribute:: data + .. attribute:: data - A real :class:`list` object used to store the contents of the - :class:`UserList` class. + A real :class:`list` object used to store the contents of the + :class:`UserList` class. **Subclassing requirements:** Subclasses of :class:`UserList` are expect to offer a constructor which can be called with either no arguments or one @@ -1073,10 +1074,10 @@ .. class:: UserString([sequence]) - Class that simulates a string or a Unicode string object. The instance's - content is kept in a regular string object, which is accessible via the - :attr:`data` attribute of :class:`UserString` instances. The instance's - contents are initially set to a copy of *sequence*. The *sequence* can - be an instance of :class:`bytes`, :class:`str`, :class:`UserString` (or a - subclass) or an arbitrary sequence which can be converted into a string using - the built-in :func:`str` function. + Class that simulates a string or a Unicode string object. The instance's + content is kept in a regular string object, which is accessible via the + :attr:`data` attribute of :class:`UserString` instances. The instance's + contents are initially set to a copy of *sequence*. The *sequence* can + be an instance of :class:`bytes`, :class:`str`, :class:`UserString` (or a + subclass) or an arbitrary sequence which can be converted into a string using + the built-in :func:`str` function. -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Sun Jun 10 05:49:17 2012 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Sun, 10 Jun 2012 05:49:17 +0200 Subject: [Python-checkins] Daily reference leaks (2059910e7d76): sum=-2 Message-ID: results for 2059910e7d76 on branch "default" -------------------------------------------- test_exceptions leaked [0, -1, 0] references, sum=-1 test_support leaked [0, 0, -1] references, sum=-1 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogyYDqQt', '-x'] From python-checkins at python.org Sun Jun 10 07:52:08 2012 From: python-checkins at python.org (raymond.hettinger) Date: Sun, 10 Jun 2012 07:52:08 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Small_cleanup_and_optimizat?= =?utf8?q?ion?= Message-ID: http://hg.python.org/cpython/rev/743cf3319862 changeset: 77398:743cf3319862 user: Raymond Hettinger date: Sat Jun 09 22:51:39 2012 -0700 summary: Small cleanup and optimization files: Lib/collections/__init__.py | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/collections/__init__.py b/Lib/collections/__init__.py --- a/Lib/collections/__init__.py +++ b/Lib/collections/__init__.py @@ -8,7 +8,7 @@ __all__ += collections.abc.__all__ from _collections import deque, defaultdict -from operator import itemgetter as _itemgetter +from operator import itemgetter as _itemgetter, eq as _eq from keyword import iskeyword as _iskeyword import sys as _sys import heapq as _heapq @@ -229,7 +229,7 @@ ''' if isinstance(other, OrderedDict): return len(self)==len(other) and \ - all(p==q for p, q in zip(self.items(), other.items())) + all(map(_eq, self.items(), other.items())) return dict.__eq__(self, other) -- Repository URL: http://hg.python.org/cpython From ncoghlan at gmail.com Sun Jun 10 15:41:55 2012 From: ncoghlan at gmail.com (Nick Coghlan) Date: Sun, 10 Jun 2012 23:41:55 +1000 Subject: [Python-checkins] cpython: Note that the _asdict() method is outdated In-Reply-To: References: Message-ID: On Sun, Jun 10, 2012 at 12:15 PM, raymond.hettinger wrote: > http://hg.python.org/cpython/rev/fecbcd5c3978 > changeset: ? 77397:fecbcd5c3978 > user: ? ? ? ?Raymond Hettinger > date: ? ? ? ?Sat Jun 09 19:15:26 2012 -0700 > summary: > ?Note that the _asdict() method is outdated This checkin changed a lot of the indentation in the collections docs. Did you mean to do that? Cheers, Nick. -- Nick Coghlan?? |?? ncoghlan at gmail.com?? |?? Brisbane, Australia From python-checkins at python.org Sun Jun 10 16:52:11 2012 From: python-checkins at python.org (stefan.krah) Date: Sun, 10 Jun 2012 16:52:11 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_1=29_State_restrictions_for?= =?utf8?q?_the_transform_length=2E?= Message-ID: http://hg.python.org/cpython/rev/27b9ab483c59 changeset: 77399:27b9ab483c59 user: Stefan Krah date: Sun Jun 10 16:50:55 2012 +0200 summary: 1) State restrictions for the transform length. 2) Switch argument order to match the function signature of mpd_calloc() (cosmetic change, since the order is irrelevant). files: Modules/_decimal/libmpdec/mpdecimal.c | 15 ++++++++++----- 1 files changed, 10 insertions(+), 5 deletions(-) diff --git a/Modules/_decimal/libmpdec/mpdecimal.c b/Modules/_decimal/libmpdec/mpdecimal.c --- a/Modules/_decimal/libmpdec/mpdecimal.c +++ b/Modules/_decimal/libmpdec/mpdecimal.c @@ -5158,7 +5158,11 @@ } -/* Determine the minimum length for the number theoretic transform. */ +/* + * Determine the minimum length for the number theoretic transform. Valid + * transform lengths are 2**n or 3*2**n, where 2**n <= MPD_MAXTRANSFORM_2N. + * The function finds the shortest length m such that rsize <= m. + */ static inline mpd_size_t _mpd_get_transform_len(mpd_size_t rsize) { @@ -5169,6 +5173,7 @@ log2rsize = mpd_bsr(rsize); if (rsize <= 1024) { + /* 2**n is faster in this range. */ x = ((mpd_size_t)1)< http://hg.python.org/cpython/rev/b65c1f21369d changeset: 77400:b65c1f21369d user: Raymond Hettinger date: Sun Jun 10 11:39:44 2012 -0700 summary: Expand examples for ChainMap(). Improve markup. files: Doc/library/collections.rst | 108 ++++++++++++++++-------- 1 files changed, 72 insertions(+), 36 deletions(-) diff --git a/Doc/library/collections.rst b/Doc/library/collections.rst --- a/Doc/library/collections.rst +++ b/Doc/library/collections.rst @@ -93,13 +93,44 @@ The use-cases also parallel those for the builtin :func:`super` function. A reference to ``d.parents`` is equivalent to: ``ChainMap(*d.maps[1:])``. - Example of simulating Python's internal lookup chain:: + +.. seealso:: + + * The `MultiContext class + `_ + in the Enthought `CodeTools package + `_ has options to support + writing to any mapping in the chain. + + * Django's `Context class + `_ + for templating is a read-only chain of mappings. It also features + pushing and popping of contexts similar to the + :meth:`~collections.ChainMap.new_child` method and the + :meth:`~collections.ChainMap.parents` property. + + * The `Nested Contexts recipe + `_ has options to control + whether writes and other mutations apply only to the first mapping or to + any mapping in the chain. + + * A `greatly simplified read-only version of Chainmap + `_. + + +:class:`ChainMap` Examples and Recipes +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This section shows various approaches to working with chained maps. + + +Example of simulating Python's internal lookup chain:: import builtins pylookup = ChainMap(locals(), globals(), vars(builtins)) - Example of letting user specified values take precedence over environment - variables which in turn take precedence over default values:: +Example of letting user specified values take precedence over environment +variables which in turn take precedence over default values:: import os, argparse defaults = {'color': 'red', 'user': guest} @@ -109,8 +140,8 @@ user_specified = vars(parser.parse_args()) combined = ChainMap(user_specified, os.environ, defaults) - Example patterns for using the :class:`ChainMap` class to simulate nested - contexts:: +Example patterns for using the :class:`ChainMap` class to simulate nested +contexts:: c = ChainMap() # Create root context d = c.new_child() # Create nested child context @@ -128,28 +159,33 @@ d.items() # All nested items dict(d) # Flatten into a regular dictionary - .. seealso:: +The :class:`ChainMap` class only makes updates (writes and deletions) to the +first mapping in the chain while lookups will search the full chain. However, +if deep writes and deletions are desired, it is easy to make a subclass that +updates keys found deeper in the chain:: - * The `MultiContext class - `_ - in the Enthought `CodeTools package - `_ has options to support - writing to any mapping in the chain. + class DeepChainMap(ChainMap): + 'Variant of ChainMap that allows direct updates to inner scopes' - * Django's `Context class - `_ - for templating is a read-only chain of mappings. It also features - pushing and popping of contexts similar to the - :meth:`~collections.ChainMap.new_child` method and the - :meth:`~collections.ChainMap.parents` property. + def __setitem__(self, key, value): + for mapping in self.maps: + if key in mapping: + mapping[key] = value + return + self.maps[0][key] = value - * The `Nested Contexts recipe - `_ has options to control - whether writes and other mutations apply only to the first mapping or to - any mapping in the chain. + def __delitem__(self, key): + for mapping in self.maps: + if key in mapping: + del mapping[key] + return + raise KeyError(key) - * A `greatly simplified read-only version of Chainmap - `_. + >>> d = DeepChainMap({'zebra': 'black'}, {'elephant' : 'blue'}, {'lion' : 'yellow'}) + >>> d['lion'] = 'orange' # update an existing key two levels down + >>> d['snake'] = 'red' # new keys get added to the topmost dict + >>> del d['elephant'] # remove an existing key one level down + DeepChainMap({'zebra': 'black', 'snake': 'red'}, {}, {'lion': 'orange'}) :class:`Counter` objects @@ -326,23 +362,23 @@ .. seealso:: * `Counter class `_ - adapted for Python 2.5 and an early `Bag recipe - `_ for Python 2.4. + adapted for Python 2.5 and an early `Bag recipe + `_ for Python 2.4. * `Bag class `_ - in Smalltalk. + in Smalltalk. * Wikipedia entry for `Multisets `_. * `C++ multisets `_ - tutorial with examples. + tutorial with examples. * For mathematical operations on multisets and their use cases, see - *Knuth, Donald. The Art of Computer Programming Volume II, - Section 4.6.3, Exercise 19*. + *Knuth, Donald. The Art of Computer Programming Volume II, + Section 4.6.3, Exercise 19*. * To enumerate all distinct multisets of a given size over a given set of - elements, see :func:`itertools.combinations_with_replacement`. + elements, see :func:`itertools.combinations_with_replacement`. map(Counter, combinations_with_replacement('ABC', 2)) --> AA AB AC BB BC CC @@ -876,14 +912,14 @@ .. seealso:: * `Named tuple recipe `_ - adapted for Python 2.4. + adapted for Python 2.4. * `Recipe for named tuple abstract base class with a metaclass mix-in - `_ - by Jan Kaliszewski. Besides providing an :term:`abstract base class` for - named tuples, it also supports an alternate :term:`metaclass`-based - constructor that is convenient for use cases where named tuples are being - subclassed. + `_ + by Jan Kaliszewski. Besides providing an :term:`abstract base class` for + named tuples, it also supports an alternate :term:`metaclass`-based + constructor that is convenient for use cases where named tuples are being + subclassed. :class:`OrderedDict` objects -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 10 21:36:41 2012 From: python-checkins at python.org (michael.foord) Date: Sun, 10 Jun 2012 21:36:41 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Adding_patch=2Estopall_meth?= =?utf8?q?od_to_unittest=2Emock?= Message-ID: http://hg.python.org/cpython/rev/4d28666c54f2 changeset: 77401:4d28666c54f2 user: Michael Foord date: Sun Jun 10 20:36:32 2012 +0100 summary: Adding patch.stopall method to unittest.mock files: Doc/library/unittest.mock.rst | 8 +++- Lib/unittest/mock.py | 22 +++++++++++- Lib/unittest/test/testmock/testpatch.py | 18 ++++++++++ 3 files changed, 44 insertions(+), 4 deletions(-) diff --git a/Doc/library/unittest.mock.rst b/Doc/library/unittest.mock.rst --- a/Doc/library/unittest.mock.rst +++ b/Doc/library/unittest.mock.rst @@ -1354,8 +1354,12 @@ As an added bonus you no longer need to keep a reference to the `patcher` object. -In fact `start` and `stop` are just aliases for the context manager -`__enter__` and `__exit__` methods. +It is also possible to stop all patches which have been started by using +`patch.stopall`. + +.. function:: patch.stopall + + Stop all active patches. TEST_PREFIX diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -1002,6 +1002,7 @@ class _patch(object): attribute_name = None + _active_patches = set() def __init__( self, getter, attribute, new, spec, create, @@ -1270,8 +1271,18 @@ if _is_started(patcher): patcher.__exit__(*exc_info) - start = __enter__ - stop = __exit__ + + def start(self): + """Activate a patch, returning any created mock.""" + result = self.__enter__() + self._active_patches.add(self) + return result + + + def stop(self): + """Stop an active patch.""" + self._active_patches.discard(self) + return self.__exit__() @@ -1562,9 +1573,16 @@ del in_dict[key] +def _patch_stopall(): + """Stop all active patches.""" + for patch in list(_patch._active_patches): + patch.stop() + + patch.object = _patch_object patch.dict = _patch_dict patch.multiple = _patch_multiple +patch.stopall = _patch_stopall patch.TEST_PREFIX = 'test' magic_methods = ( diff --git a/Lib/unittest/test/testmock/testpatch.py b/Lib/unittest/test/testmock/testpatch.py --- a/Lib/unittest/test/testmock/testpatch.py +++ b/Lib/unittest/test/testmock/testpatch.py @@ -1762,6 +1762,24 @@ p.stop() + def test_patch_stopall(self): + unlink = os.unlink + chdir = os.chdir + path = os.path + patch('os.unlink', something).start() + patch('os.chdir', something_else).start() + + @patch('os.path') + def patched(mock_path): + patch.stopall() + self.assertIs(os.path, mock_path) + self.assertIs(os.unlink, unlink) + self.assertIs(os.chdir, chdir) + + patched() + self.assertIs(os.path, path) + + if __name__ == '__main__': unittest.main() -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 10 21:38:54 2012 From: python-checkins at python.org (michael.foord) Date: Sun, 10 Jun 2012 21:38:54 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_unittest=2Emock_minor_doc_u?= =?utf8?q?pdate?= Message-ID: http://hg.python.org/cpython/rev/5399a7d792d5 changeset: 77402:5399a7d792d5 user: Michael Foord date: Sun Jun 10 20:38:54 2012 +0100 summary: unittest.mock minor doc update files: Doc/library/unittest.mock.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/library/unittest.mock.rst b/Doc/library/unittest.mock.rst --- a/Doc/library/unittest.mock.rst +++ b/Doc/library/unittest.mock.rst @@ -1359,7 +1359,7 @@ .. function:: patch.stopall - Stop all active patches. + Stop all active patches. Only stops patches started with `start`. TEST_PREFIX -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 11 05:03:50 2012 From: python-checkins at python.org (brian.quinlan) Date: Mon, 11 Jun 2012 05:03:50 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_=2315015=3A_Fix_accessing_a?= =?utf8?q?n_non-existing_attribute=2E?= Message-ID: http://hg.python.org/cpython/rev/68d7c0be5890 changeset: 77403:68d7c0be5890 user: Brian Quinlan date: Mon Jun 11 12:59:07 2012 +1000 summary: #15015: Fix accessing an non-existing attribute. files: Lib/concurrent/futures/_base.py | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/concurrent/futures/_base.py b/Lib/concurrent/futures/_base.py --- a/Lib/concurrent/futures/_base.py +++ b/Lib/concurrent/futures/_base.py @@ -470,8 +470,8 @@ return True else: LOGGER.critical('Future %s in unexpected state: %s', - id(self.future), - self.future._state) + id(self), + self._state) raise RuntimeError('Future in unexpected state') def set_result(self, result): -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Mon Jun 11 05:50:18 2012 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Mon, 11 Jun 2012 05:50:18 +0200 Subject: [Python-checkins] Daily reference leaks (5399a7d792d5): sum=2 Message-ID: results for 5399a7d792d5 on branch "default" -------------------------------------------- test_dbm leaked [2, 0, 0] references, sum=2 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogtL3Nub', '-x'] From python-checkins at python.org Mon Jun 11 08:58:56 2012 From: python-checkins at python.org (stefan.krah) Date: Mon, 11 Jun 2012 08:58:56 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_1=29_Replace_long-winded_ab?= =?utf8?q?ort=28=29_construct_by_assert=28=29=2E?= Message-ID: http://hg.python.org/cpython/rev/8a222aac951b changeset: 77404:8a222aac951b user: Stefan Krah date: Mon Jun 11 08:57:17 2012 +0200 summary: 1) Replace long-winded abort() construct by assert(). 2) Remove micro optimization (inline checking for NaN before calling mpd_qcheck_nans()) that probably has no benefit in this case. files: Modules/_decimal/libmpdec/mpdecimal.c | 63 +++++++------- 1 files changed, 30 insertions(+), 33 deletions(-) diff --git a/Modules/_decimal/libmpdec/mpdecimal.c b/Modules/_decimal/libmpdec/mpdecimal.c --- a/Modules/_decimal/libmpdec/mpdecimal.c +++ b/Modules/_decimal/libmpdec/mpdecimal.c @@ -5713,30 +5713,28 @@ mpd_qnext_minus(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status) { - mpd_context_t workctx; /* function context */ + mpd_context_t workctx; MPD_NEW_CONST(tiny,MPD_POS,mpd_etiny(ctx)-1,1,1,1,1); if (mpd_isspecial(a)) { if (mpd_qcheck_nan(result, a, ctx, status)) { return; } - if (mpd_isinfinite(a)) { - if (mpd_isnegative(a)) { - mpd_qcopy(result, a, status); + + assert(mpd_isinfinite(a)); + if (mpd_isnegative(a)) { + mpd_qcopy(result, a, status); + return; + } + else { + mpd_clear_flags(result); + mpd_qmaxcoeff(result, ctx, status); + if (mpd_isnan(result)) { return; } - else { - mpd_clear_flags(result); - mpd_qmaxcoeff(result, ctx, status); - if (mpd_isnan(result)) { - return; - } - result->exp = ctx->emax - ctx->prec + 1; - return; - } - } - /* debug */ - abort(); /* GCOV_NOT_REACHED */ + result->exp = mpd_etop(ctx); + return; + } } mpd_workcontext(&workctx, ctx); @@ -5769,21 +5767,21 @@ if (mpd_qcheck_nan(result, a, ctx, status)) { return; } - if (mpd_isinfinite(a)) { - if (mpd_ispositive(a)) { - mpd_qcopy(result, a, status); + + assert(mpd_isinfinite(a)); + if (mpd_ispositive(a)) { + mpd_qcopy(result, a, status); + } + else { + mpd_clear_flags(result); + mpd_qmaxcoeff(result, ctx, status); + if (mpd_isnan(result)) { + return; } - else { - mpd_clear_flags(result); - mpd_qmaxcoeff(result, ctx, status); - if (mpd_isnan(result)) { - return; - } - mpd_set_flags(result, MPD_NEG); - result->exp = mpd_etop(ctx); - } - return; - } + mpd_set_flags(result, MPD_NEG); + result->exp = mpd_etop(ctx); + } + return; } mpd_workcontext(&workctx, ctx); @@ -5814,9 +5812,8 @@ { int c; - if (mpd_isnan(a) || mpd_isnan(b)) { - if (mpd_qcheck_nans(result, a, b, ctx, status)) - return; + if (mpd_qcheck_nans(result, a, b, ctx, status)) { + return; } c = _mpd_cmp(a, b); -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 11 09:42:27 2012 From: python-checkins at python.org (raymond.hettinger) Date: Mon, 11 Jun 2012 09:42:27 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Fix_indentation_of_method_a?= =?utf8?q?nd_attribute_examples=2E?= Message-ID: http://hg.python.org/cpython/rev/dce47c04d3ab changeset: 77405:dce47c04d3ab parent: 77400:b65c1f21369d user: Raymond Hettinger date: Mon Jun 11 00:38:14 2012 -0700 summary: Fix indentation of method and attribute examples. files: Doc/library/collections.rst | 36 +++++++++++------------- 1 files changed, 17 insertions(+), 19 deletions(-) diff --git a/Doc/library/collections.rst b/Doc/library/collections.rst --- a/Doc/library/collections.rst +++ b/Doc/library/collections.rst @@ -798,11 +798,11 @@ Class method that makes a new instance from an existing sequence or iterable. -.. doctest:: + .. doctest:: - >>> t = [11, 22] - >>> Point._make(t) - Point(x=11, y=22) + >>> t = [11, 22] + >>> Point._make(t) + Point(x=11, y=22) .. method:: somenamedtuple._asdict() @@ -819,16 +819,14 @@ .. method:: somenamedtuple._replace(kwargs) Return a new instance of the named tuple replacing specified fields with new - values: + values:: -:: + >>> p = Point(x=11, y=22) + >>> p._replace(x=33) + Point(x=33, y=22) - >>> p = Point(x=11, y=22) - >>> p._replace(x=33) - Point(x=33, y=22) - - >>> for partnum, record in inventory.items(): - ... inventory[partnum] = record._replace(price=newprices[partnum], timestamp=time.now()) + >>> for partnum, record in inventory.items(): + ... inventory[partnum] = record._replace(price=newprices[partnum], timestamp=time.now()) .. attribute:: somenamedtuple._source @@ -844,15 +842,15 @@ Tuple of strings listing the field names. Useful for introspection and for creating new named tuple types from existing named tuples. -.. doctest:: + .. doctest:: - >>> p._fields # view the field names - ('x', 'y') + >>> p._fields # view the field names + ('x', 'y') - >>> Color = namedtuple('Color', 'red green blue') - >>> Pixel = namedtuple('Pixel', Point._fields + Color._fields) - >>> Pixel(11, 22, 128, 255, 0) - Pixel(x=11, y=22, red=128, green=255, blue=0) + >>> Color = namedtuple('Color', 'red green blue') + >>> Pixel = namedtuple('Pixel', Point._fields + Color._fields) + >>> Pixel(11, 22, 128, 255, 0) + Pixel(x=11, y=22, red=128, green=255, blue=0) To retrieve a field whose name is stored in a string, use the :func:`getattr` function: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 11 09:42:28 2012 From: python-checkins at python.org (raymond.hettinger) Date: Mon, 11 Jun 2012 09:42:28 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=28merge_default_-=3E_default?= =?utf8?q?=29=3A_merge?= Message-ID: http://hg.python.org/cpython/rev/224a2d256f7c changeset: 77406:224a2d256f7c parent: 77405:dce47c04d3ab parent: 77404:8a222aac951b user: Raymond Hettinger date: Mon Jun 11 00:42:17 2012 -0700 summary: merge files: Doc/library/unittest.mock.rst | 8 +- Lib/concurrent/futures/_base.py | 4 +- Lib/unittest/mock.py | 22 ++++- Lib/unittest/test/testmock/testpatch.py | 18 +++ Modules/_decimal/libmpdec/mpdecimal.c | 63 ++++++------ 5 files changed, 76 insertions(+), 39 deletions(-) diff --git a/Doc/library/unittest.mock.rst b/Doc/library/unittest.mock.rst --- a/Doc/library/unittest.mock.rst +++ b/Doc/library/unittest.mock.rst @@ -1354,8 +1354,12 @@ As an added bonus you no longer need to keep a reference to the `patcher` object. -In fact `start` and `stop` are just aliases for the context manager -`__enter__` and `__exit__` methods. +It is also possible to stop all patches which have been started by using +`patch.stopall`. + +.. function:: patch.stopall + + Stop all active patches. Only stops patches started with `start`. TEST_PREFIX diff --git a/Lib/concurrent/futures/_base.py b/Lib/concurrent/futures/_base.py --- a/Lib/concurrent/futures/_base.py +++ b/Lib/concurrent/futures/_base.py @@ -470,8 +470,8 @@ return True else: LOGGER.critical('Future %s in unexpected state: %s', - id(self.future), - self.future._state) + id(self), + self._state) raise RuntimeError('Future in unexpected state') def set_result(self, result): diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -1002,6 +1002,7 @@ class _patch(object): attribute_name = None + _active_patches = set() def __init__( self, getter, attribute, new, spec, create, @@ -1270,8 +1271,18 @@ if _is_started(patcher): patcher.__exit__(*exc_info) - start = __enter__ - stop = __exit__ + + def start(self): + """Activate a patch, returning any created mock.""" + result = self.__enter__() + self._active_patches.add(self) + return result + + + def stop(self): + """Stop an active patch.""" + self._active_patches.discard(self) + return self.__exit__() @@ -1562,9 +1573,16 @@ del in_dict[key] +def _patch_stopall(): + """Stop all active patches.""" + for patch in list(_patch._active_patches): + patch.stop() + + patch.object = _patch_object patch.dict = _patch_dict patch.multiple = _patch_multiple +patch.stopall = _patch_stopall patch.TEST_PREFIX = 'test' magic_methods = ( diff --git a/Lib/unittest/test/testmock/testpatch.py b/Lib/unittest/test/testmock/testpatch.py --- a/Lib/unittest/test/testmock/testpatch.py +++ b/Lib/unittest/test/testmock/testpatch.py @@ -1762,6 +1762,24 @@ p.stop() + def test_patch_stopall(self): + unlink = os.unlink + chdir = os.chdir + path = os.path + patch('os.unlink', something).start() + patch('os.chdir', something_else).start() + + @patch('os.path') + def patched(mock_path): + patch.stopall() + self.assertIs(os.path, mock_path) + self.assertIs(os.unlink, unlink) + self.assertIs(os.chdir, chdir) + + patched() + self.assertIs(os.path, path) + + if __name__ == '__main__': unittest.main() diff --git a/Modules/_decimal/libmpdec/mpdecimal.c b/Modules/_decimal/libmpdec/mpdecimal.c --- a/Modules/_decimal/libmpdec/mpdecimal.c +++ b/Modules/_decimal/libmpdec/mpdecimal.c @@ -5713,30 +5713,28 @@ mpd_qnext_minus(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status) { - mpd_context_t workctx; /* function context */ + mpd_context_t workctx; MPD_NEW_CONST(tiny,MPD_POS,mpd_etiny(ctx)-1,1,1,1,1); if (mpd_isspecial(a)) { if (mpd_qcheck_nan(result, a, ctx, status)) { return; } - if (mpd_isinfinite(a)) { - if (mpd_isnegative(a)) { - mpd_qcopy(result, a, status); + + assert(mpd_isinfinite(a)); + if (mpd_isnegative(a)) { + mpd_qcopy(result, a, status); + return; + } + else { + mpd_clear_flags(result); + mpd_qmaxcoeff(result, ctx, status); + if (mpd_isnan(result)) { return; } - else { - mpd_clear_flags(result); - mpd_qmaxcoeff(result, ctx, status); - if (mpd_isnan(result)) { - return; - } - result->exp = ctx->emax - ctx->prec + 1; - return; - } - } - /* debug */ - abort(); /* GCOV_NOT_REACHED */ + result->exp = mpd_etop(ctx); + return; + } } mpd_workcontext(&workctx, ctx); @@ -5769,21 +5767,21 @@ if (mpd_qcheck_nan(result, a, ctx, status)) { return; } - if (mpd_isinfinite(a)) { - if (mpd_ispositive(a)) { - mpd_qcopy(result, a, status); + + assert(mpd_isinfinite(a)); + if (mpd_ispositive(a)) { + mpd_qcopy(result, a, status); + } + else { + mpd_clear_flags(result); + mpd_qmaxcoeff(result, ctx, status); + if (mpd_isnan(result)) { + return; } - else { - mpd_clear_flags(result); - mpd_qmaxcoeff(result, ctx, status); - if (mpd_isnan(result)) { - return; - } - mpd_set_flags(result, MPD_NEG); - result->exp = mpd_etop(ctx); - } - return; - } + mpd_set_flags(result, MPD_NEG); + result->exp = mpd_etop(ctx); + } + return; } mpd_workcontext(&workctx, ctx); @@ -5814,9 +5812,8 @@ { int c; - if (mpd_isnan(a) || mpd_isnan(b)) { - if (mpd_qcheck_nans(result, a, b, ctx, status)) - return; + if (mpd_qcheck_nans(result, a, b, ctx, status)) { + return; } c = _mpd_cmp(a, b); -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 11 15:08:33 2012 From: python-checkins at python.org (nick.coghlan) Date: Mon, 11 Jun 2012 15:08:33 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Close_=2313857=3A_Added_tex?= =?utf8?q?twrap=2Eindent=28=29_function_=28initial_patch_by_Ezra?= Message-ID: http://hg.python.org/cpython/rev/6f7afe25d681 changeset: 77407:6f7afe25d681 user: Nick Coghlan date: Mon Jun 11 23:07:51 2012 +1000 summary: Close #13857: Added textwrap.indent() function (initial patch by Ezra Berch) files: Doc/library/textwrap.rst | 35 +++++- Doc/whatsnew/3.3.rst | 8 + Lib/test/test_textwrap.py | 140 +++++++++++++++++++++++++- Lib/textwrap.py | 21 +++- Misc/ACKS | 1 + Misc/NEWS | 3 + 6 files changed, 201 insertions(+), 7 deletions(-) diff --git a/Doc/library/textwrap.rst b/Doc/library/textwrap.rst --- a/Doc/library/textwrap.rst +++ b/Doc/library/textwrap.rst @@ -12,7 +12,7 @@ The :mod:`textwrap` module provides two convenience functions, :func:`wrap` and :func:`fill`, as well as :class:`TextWrapper`, the class that does all the work, -and a utility function :func:`dedent`. If you're just wrapping or filling one +and two utility functions, :func:`dedent` and :func:`indent`. If you're just wrapping or filling one or two text strings, the convenience functions should be good enough; otherwise, you should use an instance of :class:`TextWrapper` for efficiency. @@ -45,9 +45,10 @@ hyphenated words; only then will long words be broken if necessary, unless :attr:`TextWrapper.break_long_words` is set to false. -An additional utility function, :func:`dedent`, is provided to remove -indentation from strings that have unwanted whitespace to the left of the text. - +Two additional utility function, :func:`dedent` and :func:`indent`, are +provided to remove indentation from strings that have unwanted whitespace +to the left of the text and to add an arbitrary prefix to selected lines +in a block of text. .. function:: dedent(text) @@ -72,6 +73,32 @@ print(repr(dedent(s))) # prints 'hello\n world\n' +.. function:: indent(text, prefix, predicate=None) + + Add *prefix* to the beginning of selected lines in *text*. + + Lines are separated by calling ``text.splitlines(True)``. + + By default, *prefix* is added to all lines that do not consist + solely of whitespace (including any line endings). + + For example:: + + >>> s = 'hello\n\n \nworld' + >>> indent(s, ' ') + ' hello\n\n \n world' + + The optional *predicate* argument can be used to control which lines + are indented. For example, it is easy to add *prefix* to even empty + and whitespace-only lines:: + + >>> print(indent(s, '+ ', lambda line: True)) + + hello + + + + + + world + + .. class:: TextWrapper(**kwargs) The :class:`TextWrapper` constructor accepts a number of optional keyword diff --git a/Doc/whatsnew/3.3.rst b/Doc/whatsnew/3.3.rst --- a/Doc/whatsnew/3.3.rst +++ b/Doc/whatsnew/3.3.rst @@ -1406,6 +1406,14 @@ (:issue:`11223`) +textwrap +-------- + +* The :mod:`textwrap` module has a new :func:`~textwrap.indent` that makes + it straightforward to add a common prefix to selected lines in a block + of text. + + (:issue:`13857`) time ---- diff --git a/Lib/test/test_textwrap.py b/Lib/test/test_textwrap.py --- a/Lib/test/test_textwrap.py +++ b/Lib/test/test_textwrap.py @@ -11,7 +11,7 @@ import unittest from test import support -from textwrap import TextWrapper, wrap, fill, dedent +from textwrap import TextWrapper, wrap, fill, dedent, indent class BaseTestCase(unittest.TestCase): @@ -594,11 +594,147 @@ self.assertEqual(expect, dedent(text)) +# Test textwrap.indent +class IndentTestCase(unittest.TestCase): + # The examples used for tests. If any of these change, the expected + # results in the various test cases must also be updated. + # The roundtrip cases are separate, because textwrap.dedent doesn't + # handle Windows line endings + ROUNDTRIP_CASES = ( + # Basic test case + "Hi.\nThis is a test.\nTesting.", + # Include a blank line + "Hi.\nThis is a test.\n\nTesting.", + # Include leading and trailing blank lines + "\nHi.\nThis is a test.\nTesting.\n", + ) + CASES = ROUNDTRIP_CASES + ( + # Use Windows line endings + "Hi.\r\nThis is a test.\r\nTesting.\r\n", + # Pathological case + "\nHi.\r\nThis is a test.\n\r\nTesting.\r\n\n", + ) + + def test_indent_nomargin_default(self): + # indent should do nothing if 'prefix' is empty. + for text in self.CASES: + self.assertEqual(indent(text, ''), text) + + def test_indent_nomargin_explicit_default(self): + # The same as test_indent_nomargin, but explicitly requesting + # the default behaviour by passing None as the predicate + for text in self.CASES: + self.assertEqual(indent(text, '', None), text) + + def test_indent_nomargin_all_lines(self): + # The same as test_indent_nomargin, but using the optional + # predicate argument + predicate = lambda line: True + for text in self.CASES: + self.assertEqual(indent(text, '', predicate), text) + + def test_indent_no_lines(self): + # Explicitly skip indenting any lines + predicate = lambda line: False + for text in self.CASES: + self.assertEqual(indent(text, ' ', predicate), text) + + def test_roundtrip_spaces(self): + # A whitespace prefix should roundtrip with dedent + for text in self.ROUNDTRIP_CASES: + self.assertEqual(dedent(indent(text, ' ')), text) + + def test_roundtrip_tabs(self): + # A whitespace prefix should roundtrip with dedent + for text in self.ROUNDTRIP_CASES: + self.assertEqual(dedent(indent(text, '\t\t')), text) + + def test_roundtrip_mixed(self): + # A whitespace prefix should roundtrip with dedent + for text in self.ROUNDTRIP_CASES: + self.assertEqual(dedent(indent(text, ' \t \t ')), text) + + def test_indent_default(self): + # Test default indenting of lines that are not whitespace only + prefix = ' ' + expected = ( + # Basic test case + " Hi.\n This is a test.\n Testing.", + # Include a blank line + " Hi.\n This is a test.\n\n Testing.", + # Include leading and trailing blank lines + "\n Hi.\n This is a test.\n Testing.\n", + # Use Windows line endings + " Hi.\r\n This is a test.\r\n Testing.\r\n", + # Pathological case + "\n Hi.\r\n This is a test.\n\r\n Testing.\r\n\n", + ) + for text, expect in zip(self.CASES, expected): + self.assertEqual(indent(text, prefix), expect) + + def test_indent_explicit_default(self): + # Test default indenting of lines that are not whitespace only + prefix = ' ' + expected = ( + # Basic test case + " Hi.\n This is a test.\n Testing.", + # Include a blank line + " Hi.\n This is a test.\n\n Testing.", + # Include leading and trailing blank lines + "\n Hi.\n This is a test.\n Testing.\n", + # Use Windows line endings + " Hi.\r\n This is a test.\r\n Testing.\r\n", + # Pathological case + "\n Hi.\r\n This is a test.\n\r\n Testing.\r\n\n", + ) + for text, expect in zip(self.CASES, expected): + self.assertEqual(indent(text, prefix, None), expect) + + def test_indent_all_lines(self): + # Add 'prefix' to all lines, including whitespace-only ones. + prefix = ' ' + expected = ( + # Basic test case + " Hi.\n This is a test.\n Testing.", + # Include a blank line + " Hi.\n This is a test.\n \n Testing.", + # Include leading and trailing blank lines + " \n Hi.\n This is a test.\n Testing.\n", + # Use Windows line endings + " Hi.\r\n This is a test.\r\n Testing.\r\n", + # Pathological case + " \n Hi.\r\n This is a test.\n \r\n Testing.\r\n \n", + ) + predicate = lambda line: True + for text, expect in zip(self.CASES, expected): + self.assertEqual(indent(text, prefix, predicate), expect) + + def test_indent_empty_lines(self): + # Add 'prefix' solely to whitespace-only lines. + prefix = ' ' + expected = ( + # Basic test case + "Hi.\nThis is a test.\nTesting.", + # Include a blank line + "Hi.\nThis is a test.\n \nTesting.", + # Include leading and trailing blank lines + " \nHi.\nThis is a test.\nTesting.\n", + # Use Windows line endings + "Hi.\r\nThis is a test.\r\nTesting.\r\n", + # Pathological case + " \nHi.\r\nThis is a test.\n \r\nTesting.\r\n \n", + ) + predicate = lambda line: not line.strip() + for text, expect in zip(self.CASES, expected): + self.assertEqual(indent(text, prefix, predicate), expect) + + def test_main(): support.run_unittest(WrapTestCase, LongWordTestCase, IndentTestCases, - DedentTestCase) + DedentTestCase, + IndentTestCase) if __name__ == '__main__': test_main() diff --git a/Lib/textwrap.py b/Lib/textwrap.py --- a/Lib/textwrap.py +++ b/Lib/textwrap.py @@ -7,7 +7,7 @@ import re -__all__ = ['TextWrapper', 'wrap', 'fill', 'dedent'] +__all__ = ['TextWrapper', 'wrap', 'fill', 'dedent', 'indent'] # Hardcode the recognized whitespace characters to the US-ASCII # whitespace characters. The main reason for doing this is that in @@ -386,6 +386,25 @@ text = re.sub(r'(?m)^' + margin, '', text) return text + +def indent(text, prefix, predicate=None): + """Adds 'prefix' to the beginning of selected lines in 'text'. + + If 'predicate' is provided, 'prefix' will only be added to the lines + where 'predicate(line)' is True. If 'predicate' is not provided, + it will default to adding 'prefix' to all non-empty lines that do not + consist solely of whitespace characters. + """ + if predicate is None: + def predicate(line): + return line.strip() + + def prefixed_lines(): + for line in text.splitlines(True): + yield (prefix + line if predicate(line) else line) + return ''.join(prefixed_lines()) + + if __name__ == "__main__": #print dedent("\tfoo\n\tbar") #print dedent(" \thello there\n \t how are you?") diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -82,6 +82,7 @@ Eli Bendersky Andrew Bennetts Andy Bensky +Ezra Berch Michel Van den Bergh Julian Berman Brice Berna diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -21,6 +21,9 @@ Library ------- +- Issue #13857: Added textwrap.indent() function (initial patch by Ezra + Berch) + - Issue #2736: Added datetime.timestamp() method. - Issue #13854: Make multiprocessing properly handle non-integer -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 11 16:19:53 2012 From: python-checkins at python.org (richard.oudkerk) Date: Mon, 11 Jun 2012 16:19:53 +0200 Subject: [Python-checkins] =?utf8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzEwMTMz?= =?utf8?q?=3A_Make_multiprocessing_deallocate_buffer_if_socket_read_fails?= =?utf8?q?=2E?= Message-ID: http://hg.python.org/cpython/rev/60a7b704de5c changeset: 77408:60a7b704de5c branch: 2.7 parent: 77395:272e7dcffd30 user: Richard Oudkerk date: Mon Jun 11 15:11:35 2012 +0100 summary: Issue #10133: Make multiprocessing deallocate buffer if socket read fails. Patch by Hallvard B Furuseth. files: Misc/NEWS | 3 + Modules/_multiprocessing/socket_connection.c | 29 +++++---- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -67,6 +67,9 @@ Library ------- +- Issue #10133: Make multiprocessing deallocate buffer if socket read + fails. Patch by Hallvard B Furuseth. + - Issue #13854: Make multiprocessing properly handle non-integer non-string argument to SystemExit. diff --git a/Modules/_multiprocessing/socket_connection.c b/Modules/_multiprocessing/socket_connection.c --- a/Modules/_multiprocessing/socket_connection.c +++ b/Modules/_multiprocessing/socket_connection.c @@ -117,7 +117,7 @@ conn_recv_string(ConnectionObject *conn, char *buffer, size_t buflength, char **newbuffer, size_t maxlength) { - int res; + Py_ssize_t res; UINT32 ulength; *newbuffer = NULL; @@ -132,20 +132,23 @@ if (ulength > maxlength) return MP_BAD_MESSAGE_LENGTH; - if (ulength <= buflength) { - Py_BEGIN_ALLOW_THREADS - res = _conn_recvall(conn->handle, buffer, (size_t)ulength); - Py_END_ALLOW_THREADS - return res < 0 ? res : ulength; - } else { - *newbuffer = PyMem_Malloc((size_t)ulength); - if (*newbuffer == NULL) + if (ulength > buflength) { + *newbuffer = buffer = PyMem_Malloc((size_t)ulength); + if (buffer == NULL) return MP_MEMORY_ERROR; - Py_BEGIN_ALLOW_THREADS - res = _conn_recvall(conn->handle, *newbuffer, (size_t)ulength); - Py_END_ALLOW_THREADS - return res < 0 ? (Py_ssize_t)res : (Py_ssize_t)ulength; } + + Py_BEGIN_ALLOW_THREADS + res = _conn_recvall(conn->handle, buffer, (size_t)ulength); + Py_END_ALLOW_THREADS + + if (res >= 0) { + res = (Py_ssize_t)ulength; + } else if (*newbuffer != NULL) { + PyMem_Free(*newbuffer); + *newbuffer = NULL; + } + return res; } /* -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 11 16:19:54 2012 From: python-checkins at python.org (richard.oudkerk) Date: Mon, 11 Jun 2012 16:19:54 +0200 Subject: [Python-checkins] =?utf8?b?Y3B5dGhvbiAoMy4yKTogSXNzdWUgIzEwMTMz?= =?utf8?q?=3A_Make_multiprocessing_deallocate_buffer_if_socket_read_fails?= =?utf8?q?=2E?= Message-ID: http://hg.python.org/cpython/rev/5643697070c0 changeset: 77409:5643697070c0 branch: 3.2 parent: 77380:02b4c62ce393 user: Richard Oudkerk date: Mon Jun 11 15:12:12 2012 +0100 summary: Issue #10133: Make multiprocessing deallocate buffer if socket read fails. Patch by Hallvard B Furuseth. files: Misc/NEWS | 3 + Modules/_multiprocessing/socket_connection.c | 29 +++++---- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -70,6 +70,9 @@ Library ------- +- Issue #10133: Make multiprocessing deallocate buffer if socket read + fails. Patch by Hallvard B Furuseth. + - Issue #13854: Make multiprocessing properly handle non-integer non-string argument to SystemExit. diff --git a/Modules/_multiprocessing/socket_connection.c b/Modules/_multiprocessing/socket_connection.c --- a/Modules/_multiprocessing/socket_connection.c +++ b/Modules/_multiprocessing/socket_connection.c @@ -117,7 +117,7 @@ conn_recv_string(ConnectionObject *conn, char *buffer, size_t buflength, char **newbuffer, size_t maxlength) { - int res; + Py_ssize_t res; UINT32 ulength; *newbuffer = NULL; @@ -132,20 +132,23 @@ if (ulength > maxlength) return MP_BAD_MESSAGE_LENGTH; - if (ulength <= buflength) { - Py_BEGIN_ALLOW_THREADS - res = _conn_recvall(conn->handle, buffer, (size_t)ulength); - Py_END_ALLOW_THREADS - return res < 0 ? res : ulength; - } else { - *newbuffer = PyMem_Malloc((size_t)ulength); - if (*newbuffer == NULL) + if (ulength > buflength) { + *newbuffer = buffer = PyMem_Malloc((size_t)ulength); + if (buffer == NULL) return MP_MEMORY_ERROR; - Py_BEGIN_ALLOW_THREADS - res = _conn_recvall(conn->handle, *newbuffer, (size_t)ulength); - Py_END_ALLOW_THREADS - return res < 0 ? (Py_ssize_t)res : (Py_ssize_t)ulength; } + + Py_BEGIN_ALLOW_THREADS + res = _conn_recvall(conn->handle, buffer, (size_t)ulength); + Py_END_ALLOW_THREADS + + if (res >= 0) { + res = (Py_ssize_t)ulength; + } else if (*newbuffer != NULL) { + PyMem_Free(*newbuffer); + *newbuffer = NULL; + } + return res; } /* -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 11 16:19:54 2012 From: python-checkins at python.org (richard.oudkerk) Date: Mon, 11 Jun 2012 16:19:54 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=28merge_3=2E2_-=3E_default=29?= =?utf8?q?=3A_Dummy_merge?= Message-ID: http://hg.python.org/cpython/rev/d9d382b7670a changeset: 77410:d9d382b7670a parent: 77407:6f7afe25d681 parent: 77409:5643697070c0 user: Richard Oudkerk date: Mon Jun 11 15:16:56 2012 +0100 summary: Dummy merge files: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 11 18:57:49 2012 From: python-checkins at python.org (richard.oudkerk) Date: Mon, 11 Jun 2012 18:57:49 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Issue_=233518=3A_Remove_ref?= =?utf8?q?erences_to_non-existent_BaseManager=2Efrom=5Faddress=28=29?= Message-ID: http://hg.python.org/cpython/rev/c2910971eb86 changeset: 77411:c2910971eb86 user: Richard Oudkerk date: Mon Jun 11 17:56:08 2012 +0100 summary: Issue #3518: Remove references to non-existent BaseManager.from_address() method files: Doc/library/multiprocessing.rst | 7 ++++--- Lib/multiprocessing/managers.py | 4 ---- Misc/NEWS | 3 +++ 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Doc/library/multiprocessing.rst b/Doc/library/multiprocessing.rst --- a/Doc/library/multiprocessing.rst +++ b/Doc/library/multiprocessing.rst @@ -1236,9 +1236,10 @@ type of shared object. This must be a string. *callable* is a callable used for creating objects for this type - identifier. If a manager instance will be created using the - :meth:`from_address` classmethod or if the *create_method* argument is - ``False`` then this can be left as ``None``. + identifier. If a manager instance will be connected to the + server using the :meth:`connect` method, or if the + *create_method* argument is ``False`` then this can be left as + ``None``. *proxytype* is a subclass of :class:`BaseProxy` which is used to create proxies for shared objects with this *typeid*. If ``None`` then a proxy diff --git a/Lib/multiprocessing/managers.py b/Lib/multiprocessing/managers.py --- a/Lib/multiprocessing/managers.py +++ b/Lib/multiprocessing/managers.py @@ -455,10 +455,6 @@ self._serializer = serializer self._Listener, self._Client = listener_client[serializer] - def __reduce__(self): - return type(self).from_address, \ - (self._address, self._authkey, self._serializer) - def get_server(self): ''' Return server object with serve_forever() method and address attribute diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -21,6 +21,9 @@ Library ------- +- Issue #3518: Remove references to non-existent BaseManager.from_address() + method. + - Issue #13857: Added textwrap.indent() function (initial patch by Ezra Berch) -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Tue Jun 12 05:47:18 2012 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Tue, 12 Jun 2012 05:47:18 +0200 Subject: [Python-checkins] Daily reference leaks (c2910971eb86): sum=0 Message-ID: results for c2910971eb86 on branch "default" -------------------------------------------- Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogydpjuP', '-x'] From python-checkins at python.org Tue Jun 12 21:06:54 2012 From: python-checkins at python.org (stefan.krah) Date: Tue, 12 Jun 2012 21:06:54 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_1=29_Fix_signature_of_=5Fmp?= =?utf8?q?d=5Fqpow=5Fuint=28=29=3A_contrary_to_the_comment_base_is_constan?= =?utf8?q?t=2E?= Message-ID: http://hg.python.org/cpython/rev/8f88718fe2c4 changeset: 77412:8f88718fe2c4 user: Stefan Krah date: Tue Jun 12 21:06:06 2012 +0200 summary: 1) Fix signature of _mpd_qpow_uint(): contrary to the comment base is constant. 2) Abort the loop for all specials, not only infinity. 3) Make the function more general and distinguish between zero clamping and folding down the exponent. The latter case is currently handled by setting context->clamp to 0 before calling the function. files: Modules/_decimal/libmpdec/mpdecimal.c | 16 ++++++++------ 1 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Modules/_decimal/libmpdec/mpdecimal.c b/Modules/_decimal/libmpdec/mpdecimal.c --- a/Modules/_decimal/libmpdec/mpdecimal.c +++ b/Modules/_decimal/libmpdec/mpdecimal.c @@ -107,8 +107,9 @@ const mpd_context_t *ctx, uint32_t *status); static void _mpd_base_ndivmod(mpd_t *q, mpd_t *r, const mpd_t *a, const mpd_t *b, uint32_t *status); -static inline void _mpd_qpow_uint(mpd_t *result, mpd_t *base, mpd_uint_t exp, - uint8_t resultsign, const mpd_context_t *ctx, uint32_t *status); +static inline void _mpd_qpow_uint(mpd_t *result, const mpd_t *base, + mpd_uint_t exp, uint8_t resultsign, + const mpd_context_t *ctx, uint32_t *status); mpd_uint_t mpd_qsshiftr(mpd_t *result, const mpd_t *a, mpd_ssize_t n); @@ -5841,12 +5842,12 @@ } /* - * Internal function: Integer power with mpd_uint_t exponent, base is modified! - * Function can fail with MPD_Malloc_error. + * Internal function: Integer power with mpd_uint_t exponent. The function + * can fail with MPD_Malloc_error. */ static inline void -_mpd_qpow_uint(mpd_t *result, mpd_t *base, mpd_uint_t exp, uint8_t resultsign, - const mpd_context_t *ctx, uint32_t *status) +_mpd_qpow_uint(mpd_t *result, const mpd_t *base, mpd_uint_t exp, + uint8_t resultsign, const mpd_context_t *ctx, uint32_t *status) { uint32_t workstatus = 0; mpd_uint_t n; @@ -5866,7 +5867,8 @@ if (exp & n) { mpd_qmul(result, result, base, ctx, &workstatus); } - if (workstatus & (MPD_Overflow|MPD_Clamped)) { + if (mpd_isspecial(result) || + (mpd_iszerocoeff(result) && (workstatus & MPD_Clamped))) { break; } } -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 12 22:14:32 2012 From: python-checkins at python.org (alexander.belopolsky) Date: Tue, 12 Jun 2012 22:14:32 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Fixed_a_typo_in_time=5Floca?= =?utf8?q?ltime=28=29?= Message-ID: http://hg.python.org/cpython/rev/a6c53396c2c3 changeset: 77413:a6c53396c2c3 user: Alexander Belopolsky date: Tue Jun 12 16:14:17 2012 -0400 summary: Fixed a typo in time_localtime() files: Modules/timemodule.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Modules/timemodule.c b/Modules/timemodule.c --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -401,7 +401,7 @@ if (!parse_time_t_args(args, "|O:localtime", &when)) return NULL; - if (pylocaltime(&when, &buf) == 1) + if (pylocaltime(&when, &buf) == -1) return NULL; return tmtotuple(&buf); } -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 12 22:18:09 2012 From: python-checkins at python.org (victor.stinner) Date: Tue, 12 Jun 2012 22:18:09 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_time=2Eget=5Fclock=5Finfo?= =?utf8?q?=28=29_uses_a_namespace_instead_of_structseq?= Message-ID: http://hg.python.org/cpython/rev/0011536f2a06 changeset: 77414:0011536f2a06 user: Victor Stinner date: Tue Jun 12 22:11:44 2012 +0200 summary: time.get_clock_info() uses a namespace instead of structseq files: Doc/library/time.rst | 36 ++++++-------------- Modules/timemodule.c | 57 +++++++++++-------------------- 2 files changed, 31 insertions(+), 62 deletions(-) diff --git a/Doc/library/time.rst b/Doc/library/time.rst --- a/Doc/library/time.rst +++ b/Doc/library/time.rst @@ -160,30 +160,6 @@ .. versionadded:: 3.3 -.. class:: clock_info - - Clock information object returned by :func:`get_clock_info`. - - .. attribute:: implementation - - The name of the underlying C function used to get the clock value. - - .. attribute:: monotonic - - ``True`` if the clock cannot go backward, ``False`` otherwise. - - .. attribute:: adjusted - - ``True`` if the clock can be adjusted (e.g. by a NTP daemon), ``False`` - otherwise. - - .. attribute:: resolution - - The resolution of the clock in seconds (:class:`float`). - - .. versionadded:: 3.3 - - .. function:: clock_settime(clk_id, time) Set the time of the specified clock *clk_id*. @@ -267,7 +243,7 @@ .. function:: get_clock_info(name) - Get information on the specified clock as a :class:`clock_info` object. + Get information on the specified clock as a namespace object. Supported clock names and the corresponding functions to read their value are: @@ -277,6 +253,16 @@ * ``'process_time'``: :func:`time.process_time` * ``'time'``: :func:`time.time` + The result has the following attributes: + + - *adjusted*: ``True`` if the clock can be adjusted (e.g. by a NTP daemon), + ``False`` otherwise + - *implementation*: The name of the underlying C function used to get + the clock value + - *monotonic*: ``True`` if the clock cannot go backward, + ``False`` otherwise + - *resolution*: The resolution of the clock in seconds (:class:`float`) + .. versionadded:: 3.3 diff --git a/Modules/timemodule.c b/Modules/timemodule.c --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -1124,35 +1124,12 @@ Process time for profiling: sum of the kernel and user-space CPU time."); -static PyTypeObject ClockInfoType; - -PyDoc_STRVAR(ClockInfo_docstring, - "Clock information"); - -static PyStructSequence_Field ClockInfo_fields[] = { - {"implementation", "name of the underlying C function " - "used to get the clock value"}, - {"monotonic", "True if the clock cannot go backward, False otherwise"}, - {"adjusted", "True if the clock can be adjusted " - "(e.g. by a NTP daemon), False otherwise"}, - {"resolution", "resolution of the clock in seconds"}, - {NULL, NULL} -}; - -static PyStructSequence_Desc ClockInfo_desc = { - "time.clock_info", - ClockInfo_docstring, - ClockInfo_fields, - 4, -}; - static PyObject * time_get_clock_info(PyObject *self, PyObject *args) { char *name; - PyObject *obj; _Py_clock_info_t info; - PyObject *result; + PyObject *obj = NULL, *dict, *ns; if (!PyArg_ParseTuple(args, "s:get_clock_info", &name)) return NULL; @@ -1191,39 +1168,50 @@ return NULL; Py_DECREF(obj); - result = PyStructSequence_New(&ClockInfoType); - if (result == NULL) + dict = PyDict_New(); + if (dict == NULL) return NULL; assert(info.implementation != NULL); obj = PyUnicode_FromString(info.implementation); if (obj == NULL) goto error; - PyStructSequence_SET_ITEM(result, 0, obj); + if (PyDict_SetItemString(dict, "implementation", obj) == -1) + goto error; + Py_CLEAR(obj); assert(info.monotonic != -1); obj = PyBool_FromLong(info.monotonic); if (obj == NULL) goto error; - PyStructSequence_SET_ITEM(result, 1, obj); + if (PyDict_SetItemString(dict, "monotonic", obj) == -1) + goto error; + Py_CLEAR(obj); assert(info.adjusted != -1); obj = PyBool_FromLong(info.adjusted); if (obj == NULL) goto error; - PyStructSequence_SET_ITEM(result, 2, obj); + if (PyDict_SetItemString(dict, "adjusted", obj) == -1) + goto error; + Py_CLEAR(obj); assert(info.resolution > 0.0); assert(info.resolution <= 1.0); obj = PyFloat_FromDouble(info.resolution); if (obj == NULL) goto error; - PyStructSequence_SET_ITEM(result, 3, obj); + if (PyDict_SetItemString(dict, "resolution", obj) == -1) + goto error; + Py_CLEAR(obj); - return result; + ns = _PyNamespace_New(dict); + Py_DECREF(dict); + return ns; error: - Py_DECREF(result); + Py_DECREF(dict); + Py_XDECREF(obj); return NULL; } @@ -1451,11 +1439,6 @@ PyStructSequence_InitType(&StructTimeType, &struct_time_type_desc); - /* initialize ClockInfoType */ - PyStructSequence_InitType(&ClockInfoType, &ClockInfo_desc); - Py_INCREF(&ClockInfoType); - PyModule_AddObject(m, "clock_info", (PyObject*)&ClockInfoType); - #ifdef MS_WINDOWS winver.dwOSVersionInfoSize = sizeof(winver); if (!GetVersionEx((OSVERSIONINFO*)&winver)) { -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 12 22:50:50 2012 From: python-checkins at python.org (victor.stinner) Date: Tue, 12 Jun 2012 22:50:50 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_PEP_418=3A_Rename_adjusted_?= =?utf8?q?attribute_to_adjustable_in_time=2Eget=5Fclock=5Finfo=28=29_resul?= =?utf8?q?t?= Message-ID: http://hg.python.org/cpython/rev/0e46e0cd368f changeset: 77415:0e46e0cd368f user: Victor Stinner date: Tue Jun 12 22:46:37 2012 +0200 summary: PEP 418: Rename adjusted attribute to adjustable in time.get_clock_info() result Fix also its value on Windows and Linux according to its documentation: "adjustable" indicates if the clock *can be* adjusted, not if it is or was adjusted. In most cases, it is not possible to indicate if a clock is or was adjusted. files: Doc/library/time.rst | 4 +- Include/pytime.h | 2 +- Lib/test/test_time.py | 12 +++++----- Misc/NEWS | 2 + Modules/timemodule.c | 36 ++++++++++++------------------ Python/pytime.c | 11 +++------ 6 files changed, 30 insertions(+), 37 deletions(-) diff --git a/Doc/library/time.rst b/Doc/library/time.rst --- a/Doc/library/time.rst +++ b/Doc/library/time.rst @@ -255,8 +255,8 @@ The result has the following attributes: - - *adjusted*: ``True`` if the clock can be adjusted (e.g. by a NTP daemon), - ``False`` otherwise + - *adjustable*: ``True`` if the clock can be changed automatically (e.g. by + a NTP daemon) or manually by the system administrator, ``False`` otherwise - *implementation*: The name of the underlying C function used to get the clock value - *monotonic*: ``True`` if the clock cannot go backward, diff --git a/Include/pytime.h b/Include/pytime.h --- a/Include/pytime.h +++ b/Include/pytime.h @@ -26,7 +26,7 @@ typedef struct { const char *implementation; int monotonic; - int adjusted; + int adjustable; double resolution; } _Py_clock_info_t; diff --git a/Lib/test/test_time.py b/Lib/test/test_time.py --- a/Lib/test/test_time.py +++ b/Lib/test/test_time.py @@ -32,14 +32,14 @@ info = time.get_clock_info('time') self.assertFalse(info.monotonic) if sys.platform != 'win32': - self.assertTrue(info.adjusted) + self.assertTrue(info.adjustable) def test_clock(self): time.clock() info = time.get_clock_info('clock') self.assertTrue(info.monotonic) - self.assertFalse(info.adjusted) + self.assertFalse(info.adjustable) @unittest.skipUnless(hasattr(time, 'clock_gettime'), 'need time.clock_gettime()') @@ -372,9 +372,9 @@ info = time.get_clock_info('monotonic') self.assertTrue(info.monotonic) if sys.platform == 'linux': - self.assertTrue(info.adjusted) + self.assertTrue(info.adjustable) else: - self.assertFalse(info.adjusted) + self.assertFalse(info.adjustable) def test_perf_counter(self): time.perf_counter() @@ -390,7 +390,7 @@ info = time.get_clock_info('process_time') self.assertTrue(info.monotonic) - self.assertFalse(info.adjusted) + self.assertFalse(info.adjustable) @unittest.skipUnless(hasattr(time, 'monotonic'), 'need time.monotonic') @@ -441,7 +441,7 @@ # 0.0 < resolution <= 1.0 self.assertGreater(info.resolution, 0.0) self.assertLessEqual(info.resolution, 1.0) - self.assertIsInstance(info.adjusted, bool) + self.assertIsInstance(info.adjustable, bool) self.assertRaises(ValueError, time.get_clock_info, 'xxx') diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -21,6 +21,8 @@ Library ------- +- Rename adjusted attribute to adjustable in time.get_clock_info() result. + - Issue #3518: Remove references to non-existent BaseManager.from_address() method. diff --git a/Modules/timemodule.c b/Modules/timemodule.c --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -96,7 +96,7 @@ info->implementation = "clock()"; info->resolution = 1.0 / (double)CLOCKS_PER_SEC; info->monotonic = 1; - info->adjusted = 0; + info->adjustable = 0; } return PyFloat_FromDouble((double)value / CLOCKS_PER_SEC); } @@ -132,7 +132,7 @@ info->implementation = "QueryPerformanceCounter()"; info->resolution = 1.0 / (double)cpu_frequency; info->monotonic = 1; - info->adjusted = 0; + info->adjustable = 0; } *result = PyFloat_FromDouble(diff / (double)cpu_frequency); return 0; @@ -882,7 +882,7 @@ return NULL; } info->resolution = timeIncrement * 1e-7; - info->adjusted = 0; + info->adjustable = 0; } return PyFloat_FromDouble(result); @@ -903,7 +903,7 @@ info->implementation = "mach_absolute_time()"; info->resolution = (double)timebase.numer / timebase.denom * 1e-9; info->monotonic = 1; - info->adjusted = 0; + info->adjustable = 0; } return PyFloat_FromDouble(secs); @@ -926,13 +926,7 @@ struct timespec res; info->monotonic = 1; info->implementation = function; -#if (defined(linux) || defined(__linux) || defined(__linux__)) \ - && !defined(CLOCK_HIGHRES) - /* CLOCK_MONOTONIC is adjusted on Linux */ - info->adjusted = 1; -#else - info->adjusted = 0; -#endif + info->adjustable = 0; if (clock_getres(clk_id, &res) == 0) info->resolution = res.tv_sec + res.tv_nsec * 1e-9; else @@ -1024,7 +1018,7 @@ info->implementation = "GetProcessTimes()"; info->resolution = 1e-7; info->monotonic = 1; - info->adjusted = 0; + info->adjustable = 0; } return PyFloat_FromDouble(total * 1e-7); #else @@ -1053,7 +1047,7 @@ struct timespec res; info->implementation = function; info->monotonic = 1; - info->adjusted = 0; + info->adjustable = 0; if (clock_getres(clk_id, &res) == 0) info->resolution = res.tv_sec + res.tv_nsec * 1e-9; else @@ -1071,7 +1065,7 @@ if (info) { info->implementation = "getrusage(RUSAGE_SELF)"; info->monotonic = 1; - info->adjusted = 0; + info->adjustable = 0; info->resolution = 1e-6; } return PyFloat_FromDouble(total); @@ -1100,7 +1094,7 @@ if (info) { info->implementation = "times()"; info->monotonic = 1; - info->adjusted = 0; + info->adjustable = 0; info->resolution = 1.0 / ticks_per_second; } return PyFloat_FromDouble(total); @@ -1137,12 +1131,12 @@ #ifdef Py_DEBUG info.implementation = NULL; info.monotonic = -1; - info.adjusted = -1; + info.adjustable = -1; info.resolution = -1.0; #else info.implementation = ""; info.monotonic = 0; - info.adjusted = 0; + info.adjustable = 0; info.resolution = 1.0; #endif @@ -1188,11 +1182,11 @@ goto error; Py_CLEAR(obj); - assert(info.adjusted != -1); - obj = PyBool_FromLong(info.adjusted); + assert(info.adjustable != -1); + obj = PyBool_FromLong(info.adjustable); if (obj == NULL) goto error; - if (PyDict_SetItemString(dict, "adjusted", obj) == -1) + if (PyDict_SetItemString(dict, "adjustable", obj) == -1) goto error; Py_CLEAR(obj); @@ -1471,7 +1465,7 @@ struct timespec res; info->implementation = "clock_gettime(CLOCK_REALTIME)"; info->monotonic = 0; - info->adjusted = 1; + info->adjustable = 1; if (clock_getres(CLOCK_REALTIME, &res) == 0) info->resolution = res.tv_sec + res.tv_nsec * 1e-9; else diff --git a/Python/pytime.c b/Python/pytime.c --- a/Python/pytime.c +++ b/Python/pytime.c @@ -44,10 +44,7 @@ (void) GetSystemTimeAdjustment(&timeAdjustment, &timeIncrement, &isTimeAdjustmentDisabled); info->resolution = timeIncrement * 1e-7; - if (isTimeAdjustmentDisabled) - info->adjusted = 0; - else - info->adjusted = 1; + info->adjustable = 1; } #else /* There are three ways to get the time: @@ -71,7 +68,7 @@ info->implementation = "gettimeofday()"; info->resolution = 1e-6; info->monotonic = 0; - info->adjusted = 1; + info->adjustable = 1; } return; } @@ -87,7 +84,7 @@ info->implementation = "ftime()"; info->resolution = 1e-3; info->monotonic = 0; - info->adjusted = 1; + info->adjustable = 1; } } #else /* !HAVE_FTIME */ @@ -97,7 +94,7 @@ info->implementation = "time()"; info->resolution = 1.0; info->monotonic = 0; - info->adjusted = 1; + info->adjustable = 1; } #endif /* !HAVE_FTIME */ -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 12 23:08:05 2012 From: python-checkins at python.org (victor.stinner) Date: Tue, 12 Jun 2012 23:08:05 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Fix_test=5Ftime_for_adjuste?= =?utf8?q?d/adjustable_changes?= Message-ID: http://hg.python.org/cpython/rev/26e2ee402a0b changeset: 77416:26e2ee402a0b user: Victor Stinner date: Tue Jun 12 23:04:11 2012 +0200 summary: Fix test_time for adjusted/adjustable changes files: Lib/test/test_time.py | 8 ++------ 1 files changed, 2 insertions(+), 6 deletions(-) diff --git a/Lib/test/test_time.py b/Lib/test/test_time.py --- a/Lib/test/test_time.py +++ b/Lib/test/test_time.py @@ -31,8 +31,7 @@ time.time() info = time.get_clock_info('time') self.assertFalse(info.monotonic) - if sys.platform != 'win32': - self.assertTrue(info.adjustable) + self.assertTrue(info.adjustable) def test_clock(self): time.clock() @@ -371,10 +370,7 @@ info = time.get_clock_info('monotonic') self.assertTrue(info.monotonic) - if sys.platform == 'linux': - self.assertTrue(info.adjustable) - else: - self.assertFalse(info.adjustable) + self.assertFalse(info.adjustable) def test_perf_counter(self): time.perf_counter() -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Wed Jun 13 05:47:09 2012 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Wed, 13 Jun 2012 05:47:09 +0200 Subject: [Python-checkins] Daily reference leaks (26e2ee402a0b): sum=2 Message-ID: results for 26e2ee402a0b on branch "default" -------------------------------------------- test_support leaked [0, -1, 1] references, sum=0 test_dbm leaked [2, 0, 0] references, sum=2 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogkYVGEW', '-x'] From python-checkins at python.org Wed Jun 13 23:59:35 2012 From: python-checkins at python.org (sandro.tosi) Date: Wed, 13 Jun 2012 23:59:35 +0200 Subject: [Python-checkins] =?utf8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzE1MDYw?= =?utf8?q?=3A_fix_typo_in_socket_doc=3B_Patch_by_anatoly_techtonik?= Message-ID: http://hg.python.org/cpython/rev/744fb52ffdf0 changeset: 77417:744fb52ffdf0 branch: 2.7 parent: 77408:60a7b704de5c user: Sandro Tosi date: Wed Jun 13 23:58:35 2012 +0200 summary: Issue #15060: fix typo in socket doc; Patch by anatoly techtonik files: Doc/library/socket.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -38,7 +38,7 @@ :const:`AF_UNIX` address family. A pair ``(host, port)`` is used for the :const:`AF_INET` address family, where *host* is a string representing either a hostname in Internet domain notation like ``'daring.cwi.nl'`` or an IPv4 address -like ``'100.50.200.5'``, and *port* is an integral port number. For +like ``'100.50.200.5'``, and *port* is an integer port number. For :const:`AF_INET6` address family, a four-tuple ``(host, port, flowinfo, scopeid)`` is used, where *flowinfo* and *scopeid* represents ``sin6_flowinfo`` and ``sin6_scope_id`` member in :const:`struct sockaddr_in6` in C. For -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 13 23:59:36 2012 From: python-checkins at python.org (sandro.tosi) Date: Wed, 13 Jun 2012 23:59:36 +0200 Subject: [Python-checkins] =?utf8?b?Y3B5dGhvbiAoMy4yKTogSXNzdWUgIzE1MDYw?= =?utf8?q?=3A_fix_typo_in_socket_doc=3B_Patch_by_anatoly_techtonik?= Message-ID: http://hg.python.org/cpython/rev/4d755a711823 changeset: 77418:4d755a711823 branch: 3.2 parent: 77409:5643697070c0 user: Sandro Tosi date: Wed Jun 13 23:58:54 2012 +0200 summary: Issue #15060: fix typo in socket doc; Patch by anatoly techtonik files: Doc/library/socket.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -47,7 +47,7 @@ - A pair ``(host, port)`` is used for the :const:`AF_INET` address family, where *host* is a string representing either a hostname in Internet domain notation like ``'daring.cwi.nl'`` or an IPv4 address like ``'100.50.200.5'``, - and *port* is an integral port number. + and *port* is an integer port number. - For :const:`AF_INET6` address family, a four-tuple ``(host, port, flowinfo, scopeid)`` is used, where *flowinfo* and *scopeid* represent the ``sin6_flowinfo`` -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 13 23:59:37 2012 From: python-checkins at python.org (sandro.tosi) Date: Wed, 13 Jun 2012 23:59:37 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=28merge_3=2E2_-=3E_default=29?= =?utf8?q?=3A_Issue_=2315060=3A_merge_with_3=2E2?= Message-ID: http://hg.python.org/cpython/rev/29fb36928433 changeset: 77419:29fb36928433 parent: 77416:26e2ee402a0b parent: 77418:4d755a711823 user: Sandro Tosi date: Wed Jun 13 23:59:21 2012 +0200 summary: Issue #15060: merge with 3.2 files: Doc/library/socket.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -61,7 +61,7 @@ - A pair ``(host, port)`` is used for the :const:`AF_INET` address family, where *host* is a string representing either a hostname in Internet domain notation like ``'daring.cwi.nl'`` or an IPv4 address like ``'100.50.200.5'``, - and *port* is an integral port number. + and *port* is an integer port number. - For :const:`AF_INET6` address family, a four-tuple ``(host, port, flowinfo, scopeid)`` is used, where *flowinfo* and *scopeid* represent the ``sin6_flowinfo`` -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 14 00:37:37 2012 From: python-checkins at python.org (sandro.tosi) Date: Thu, 14 Jun 2012 00:37:37 +0200 Subject: [Python-checkins] =?utf8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzE1MDYw?= =?utf8?q?=3A_better_fix=2C_thanks_to_review_on_=23python-dev?= Message-ID: http://hg.python.org/cpython/rev/412c7daed0db changeset: 77420:412c7daed0db branch: 2.7 parent: 77417:744fb52ffdf0 user: Sandro Tosi date: Thu Jun 14 00:36:54 2012 +0200 summary: Issue #15060: better fix, thanks to review on #python-dev files: Doc/library/socket.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -38,7 +38,7 @@ :const:`AF_UNIX` address family. A pair ``(host, port)`` is used for the :const:`AF_INET` address family, where *host* is a string representing either a hostname in Internet domain notation like ``'daring.cwi.nl'`` or an IPv4 address -like ``'100.50.200.5'``, and *port* is an integer port number. For +like ``'100.50.200.5'``, and *port* is an integer. For :const:`AF_INET6` address family, a four-tuple ``(host, port, flowinfo, scopeid)`` is used, where *flowinfo* and *scopeid* represents ``sin6_flowinfo`` and ``sin6_scope_id`` member in :const:`struct sockaddr_in6` in C. For -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 14 00:37:39 2012 From: python-checkins at python.org (sandro.tosi) Date: Thu, 14 Jun 2012 00:37:39 +0200 Subject: [Python-checkins] =?utf8?b?Y3B5dGhvbiAoMy4yKTogSXNzdWUgIzE1MDYw?= =?utf8?q?=3A_better_fix=2C_thanks_to_review_on_=23python-dev?= Message-ID: http://hg.python.org/cpython/rev/e616985284cd changeset: 77421:e616985284cd branch: 3.2 parent: 77418:4d755a711823 user: Sandro Tosi date: Thu Jun 14 00:37:09 2012 +0200 summary: Issue #15060: better fix, thanks to review on #python-dev files: Doc/library/socket.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -47,7 +47,7 @@ - A pair ``(host, port)`` is used for the :const:`AF_INET` address family, where *host* is a string representing either a hostname in Internet domain notation like ``'daring.cwi.nl'`` or an IPv4 address like ``'100.50.200.5'``, - and *port* is an integer port number. + and *port* is an integer. - For :const:`AF_INET6` address family, a four-tuple ``(host, port, flowinfo, scopeid)`` is used, where *flowinfo* and *scopeid* represent the ``sin6_flowinfo`` -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 14 00:37:41 2012 From: python-checkins at python.org (sandro.tosi) Date: Thu, 14 Jun 2012 00:37:41 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=28merge_3=2E2_-=3E_default=29?= =?utf8?q?=3A_Issue_=2315060=3A_merge_with_3=2E2?= Message-ID: http://hg.python.org/cpython/rev/d16065304bbd changeset: 77422:d16065304bbd parent: 77419:29fb36928433 parent: 77421:e616985284cd user: Sandro Tosi date: Thu Jun 14 00:37:25 2012 +0200 summary: Issue #15060: merge with 3.2 files: Doc/library/socket.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -61,7 +61,7 @@ - A pair ``(host, port)`` is used for the :const:`AF_INET` address family, where *host* is a string representing either a hostname in Internet domain notation like ``'daring.cwi.nl'`` or an IPv4 address like ``'100.50.200.5'``, - and *port* is an integer port number. + and *port* is an integer. - For :const:`AF_INET6` address family, a four-tuple ``(host, port, flowinfo, scopeid)`` is used, where *flowinfo* and *scopeid* represent the ``sin6_flowinfo`` -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 14 04:15:07 2012 From: python-checkins at python.org (brett.cannon) Date: Thu, 14 Jun 2012 04:15:07 +0200 Subject: [Python-checkins] =?utf8?q?peps=3A_Update_to_PEP_362_from_Yury=2E?= Message-ID: http://hg.python.org/peps/rev/882f0a5ba45e changeset: 4459:882f0a5ba45e user: Brett Cannon date: Wed Jun 13 22:15:01 2012 -0400 summary: Update to PEP 362 from Yury. files: pep-0362.txt | 140 ++++++++++++++++++++------------------ 1 files changed, 75 insertions(+), 65 deletions(-) diff --git a/pep-0362.txt b/pep-0362.txt --- a/pep-0362.txt +++ b/pep-0362.txt @@ -16,7 +16,7 @@ ======== Python has always supported powerful introspection capabilities, -including introspecting functions and methods. (For the rest of +including introspecting functions and methods (for the rest of this PEP, "function" refers to both functions and methods). By examining a function object you can fully reconstruct the function's signature. Unfortunately this information is stored in an inconvenient @@ -35,16 +35,12 @@ Signature Object ================ -A Signature object represents the overall signature of a function. -It stores a `Parameter object`_ for each parameter accepted by the -function, as well as information specific to the function itself. +A Signature object represents the call signature of a function and +its return annotation. For each parameter accepted by the function +it stores a `Parameter object`_ in its ``parameters`` collection. A Signature object has the following public attributes and methods: -* name : str - Name of the function. -* qualname : str - Fully qualified name of the function. * return_annotation : object The annotation for the return type of the function if specified. If the function has no annotation for its return type, this @@ -55,8 +51,22 @@ as listed in ``code.co_varnames``). * bind(\*args, \*\*kwargs) -> BoundArguments Creates a mapping from positional and keyword arguments to - parameters. Raises a ``BindError`` if the passed arguments - do not match the signature. + parameters. Raises a ``BindError`` (subclass of ``TypeError``) + if the passed arguments do not match the signature. +* bind_partial(\*args, \*\*kwargs) -> BoundArguments + Works the same way as ``bind()``, but allows the omission + of some required arguments (mimics ``functools.partial`` + behavior.) +* format(...) -> str + Formats the Signature object to a string. Optional arguments allow + for custom render functions for parameter names, + annotations and default values, along with custom separators. + +Signature implements the ``__str__`` method, which fallbacks to the +``Signature.format()`` call. + +It's possible to test Signatures for equality. Two signatures +are equal when they have equal parameters and return annotations. Changes to the Signature object, or to any of its data members, do not affect the function itself. @@ -75,7 +85,7 @@ * name : str The name of the parameter as a string. * default : object - The default value for the parameter if specified. If the + The default value for the parameter, if specified. If the parameter has no default value, this attribute is not set. * annotation : object The annotation for the parameter if specified. If the @@ -97,11 +107,7 @@ all conditions where ``is_implemented`` may be False be thoroughly documented. -Parameter objects support testing for equality. Two Parameter -objects are equal, when all their properties are equal. Those -who need to test if one signature has the same parameters as -another, can do a direct comparison of ``Signature.parameters`` -collections: ``signature(foo).parameters == signature(bar).parameters``. +Two parameters are equal when all their attributes are equal. BoundArguments Object @@ -113,7 +119,7 @@ Has the following public attributes: * arguments : OrderedDict - An ordered mutable mapping of parameters' names to arguments' values. + An ordered, mutable mapping of parameters' names to arguments' values. Does not contain arguments' default values. * args : tuple Tuple of positional arguments values. Dynamically computed from @@ -125,7 +131,7 @@ The ``arguments`` attribute should be used in conjunction with ``Signature.parameters`` for any arguments processing purposes. -``args`` and ``kwargs`` properties should be used to invoke functions: +``args`` and ``kwargs`` properties can be used to invoke functions: :: def test(a, *, b): @@ -148,7 +154,7 @@ - If the object is not callable - raise a TypeError - If the object has a ``__signature__`` attribute and if it - is not ``None`` - return it + is not ``None`` - return a deepcopy of it - If it is ``None`` and the object is an instance of ``BuiltinFunction``, raise a ``ValueError`` @@ -160,29 +166,43 @@ - Or else construct a new ``Signature`` object and return it - - if the object is a method or a classmethod, construct and return + - If the object is a method or a classmethod, construct and return a new ``Signature`` object, with its first parameter (usually ``self`` or ``cls``) removed - - If the object is a class return ``signature(object.__init__)`` + - If the object is a staticmethod, construct and return + a new ``Signature`` object - If the object is an instance of ``functools.partial``, construct a new ``Signature`` from its ``partial.func`` attribute, and account for already bound ``partial.args`` and ``partial.kwargs`` + - If the object is a class or metaclass: + + - If the object's type has a ``__call__`` method defined in + its MRO, return a Signature for it + + - If the object has a ``__new__`` method defined in its class, + return a Signature object for it + + - If the object has a ``__init__`` method defined in its class, + return a Signature object for it + - Return ``signature(object.__call__)`` Note, that the ``Signature`` object is created in a lazy manner, and -is not automatically cached. +is not automatically cached. If, however, the Signature object was +explicitly cached by the user, ``signature()`` returns a new deepcopy +of it on each invocation. -An implementation for Python 3.3 can be found here: [#impl]_. -A python issue was also created: [#issue]_. +An implementation for Python 3.3 can be found at [#impl]_. +The python issue tracking the patch is [#issue]_. Design Considerations ===================== -No Implicit Caching of Signature Objects +No implicit caching of Signature objects ---------------------------------------- The first PEP design had a provision for implicit caching of ``Signature`` @@ -201,60 +221,49 @@ Examples ======== -Function Signature Renderer ---------------------------- +Visualizing Callable Objects' Signature +--------------------------------------- :: - def render_signature(signature): - '''Renders function definition by its signature. + from inspect import signature + from functools import partial, wraps - Example: - >>> def test(a:'foo', *, b:'bar', c=True, **kwargs:None) -> 'spam': - ... pass + class FooMeta(type): + def __new__(mcls, name, bases, dct, *, bar:bool=False): + return super().__new__(mcls, name, bases, dct) - >>> render_signature(inspect.signature(test)) - test(a:'foo', *, b:'bar', c=True, **kwargs:None) -> 'spam' - ''' + def __init__(cls, name, bases, dct, **kwargs): + return super().__init__(name, bases, dct) - result = [] - render_kw_only_separator = True - for param in signature.parameters.values(): - formatted = param.name - # Add annotation and default value - if hasattr(param, 'annotation'): - formatted = '{}:{!r}'.format(formatted, param.annotation) - if hasattr(param, 'default'): - formatted = '{}={!r}'.format(formatted, param.default) + class Foo(metaclass=FooMeta): + def __init__(self, spam:int=42): + self.spam = spam - # Handle *args and **kwargs -like parameters - if param.is_args: - formatted = '*' + formatted - elif param.is_kwargs: - formatted = '**' + formatted + def __call__(self, a, b, *, c) -> tuple: + return a, b, c - if param.is_args: - # OK, we have an '*args'-like parameter, so we won't need - # a '*' to separate keyword-only arguments - render_kw_only_separator = False - elif param.is_keyword_only and render_kw_only_separator: - # We have a keyword-only parameter to render and we haven't - # rendered an '*args'-like parameter before, so add a '*' - # separator to the parameters list ("foo(arg1, *, arg2)" case) - result.append('*') - # This condition should be only triggered once, so - # reset the flag - render_kw_only_separator = False - result.append(formatted) + print('FooMeta >', str(signature(FooMeta))) + print('Foo >', str(signature(Foo))) + print('Foo.__call__ >', str(signature(Foo.__call__))) + print('Foo().__call__ >', str(signature(Foo().__call__))) + print('partial(Foo().__call__, 1, c=3) >', + str(signature(partial(Foo().__call__, 1, c=3)))) + print('partial(partial(Foo().__call__, 1, c=3), 2, c=20) >', + str(signature(partial(partial(Foo().__call__, 1, c=3), 2, c=20)))) - rendered = '{}({})'.format(signature.name, ', '.join(result)) - if hasattr(signature, 'return_annotation'): - rendered += ' -> {!r}'.format(signature.return_annotation) +The script will output: +:: - return rendered + FooMeta > (name, bases, dct, *, bar:bool=False) + Foo > (spam:int=42) + Foo.__call__ > (self, a, b, *, c) -> tuple + Foo().__call__ > (a, b, *, c) -> tuple + partial(Foo().__call__, 1, c=3) > (b, *, c=3) -> tuple + partial(partial(Foo().__call__, 1, c=3), 2, c=20) > (*, c=20) -> tuple Annotation Checker -- Repository URL: http://hg.python.org/peps From python-checkins at python.org Thu Jun 14 04:16:08 2012 From: python-checkins at python.org (alexander.belopolsky) Date: Thu, 14 Jun 2012 04:16:08 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Issue_=231667546=3A_On_plat?= =?utf8?q?forms_supporting_tm=5Fzone_and_tm=5Fgmtoff_fields?= Message-ID: http://hg.python.org/cpython/rev/3b5545ba6432 changeset: 77423:3b5545ba6432 user: Alexander Belopolsky date: Wed Jun 13 22:15:26 2012 -0400 summary: Issue #1667546: On platforms supporting tm_zone and tm_gmtoff fields in struct tm, time.struct_time objects returned by time.gmtime(), time.localtime() and time.strptime() functions now have tm_zone and tm_gmtoff attributes. Original patch by Paul Boddie. files: Doc/library/time.rst | 23 ++++++++++- Lib/_strptime.py | 6 +- Lib/test/test_structseq.py | 5 +- Lib/test/test_time.py | 51 ++++++++++++++++++++++++++ Misc/NEWS | 5 ++ Modules/timemodule.c | 36 +++++++++++++++++- 6 files changed, 117 insertions(+), 9 deletions(-) diff --git a/Doc/library/time.rst b/Doc/library/time.rst --- a/Doc/library/time.rst +++ b/Doc/library/time.rst @@ -77,6 +77,12 @@ See :class:`struct_time` for a description of these objects. + .. versionchanged:: 3.3 + + The :class:`struct_time` type was extended to provide the + :attr:`tm_gmtoff` and :attr:`tm_zone` attributes when platform + supports corresponding ``struct tm`` members. + * Use the following functions to convert between time representations: +-------------------------+-------------------------+-------------------------+ @@ -336,7 +342,6 @@ .. versionadded:: 3.3 - .. function:: sleep(secs) Suspend execution for the given number of seconds. The argument may be a @@ -433,6 +438,12 @@ | ``%Y`` | Year with century as a decimal number. | | | | | | +-----------+------------------------------------------------+-------+ + | ``%z`` | Time zone offset indicating a positive or | | + | | negative time difference from UTC/GMT of the | | + | | form +HHMM or -HHMM, where H represents decimal| | + | | hour digits and M represents decimal minute | | + | | digits [-23:59, +23:59]. | | + +-----------+------------------------------------------------+-------+ | ``%Z`` | Time zone name (no characters if no time zone | | | | exists). | | +-----------+------------------------------------------------+-------+ @@ -532,6 +543,10 @@ +-------+-------------------+---------------------------------+ | 8 | :attr:`tm_isdst` | 0, 1 or -1; see below | +-------+-------------------+---------------------------------+ + | N/A | :attr:`tm_zone` | abbreviation of timezone name | + +-------+-------------------+---------------------------------+ + | N/A | :attr:`tm_gmtoff` | offset from UTC in seconds | + +-------+-------------------+---------------------------------+ Note that unlike the C structure, the month value is a range of [1, 12], not [0, 11]. A ``-1`` argument as the daylight @@ -542,6 +557,11 @@ :class:`struct_time`, or having elements of the wrong type, a :exc:`TypeError` is raised. + .. versionchanged:: 3.3 + + :attr:`tm_gmtoff` and :attr:`tm_zone` attributes are avaliable on + platforms with C library supporting the corresponding fields in + ``struct tm``. .. function:: time() @@ -552,7 +572,6 @@ lower value than a previous call if the system clock has been set back between the two calls. - .. data:: timezone The offset of the local (non-DST) timezone, in seconds west of UTC (negative in diff --git a/Lib/_strptime.py b/Lib/_strptime.py --- a/Lib/_strptime.py +++ b/Lib/_strptime.py @@ -486,19 +486,19 @@ return (year, month, day, hour, minute, second, - weekday, julian, tz, gmtoff, tzname), fraction + weekday, julian, tz, tzname, gmtoff), fraction def _strptime_time(data_string, format="%a %b %d %H:%M:%S %Y"): """Return a time struct based on the input string and the format string.""" tt = _strptime(data_string, format)[0] - return time.struct_time(tt[:9]) + return time.struct_time(tt[:time._STRUCT_TM_ITEMS]) def _strptime_datetime(cls, data_string, format="%a %b %d %H:%M:%S %Y"): """Return a class cls instance based on the input string and the format string.""" tt, fraction = _strptime(data_string, format) - gmtoff, tzname = tt[-2:] + tzname, gmtoff = tt[-2:] args = tt[:6] + (fraction,) if gmtoff is not None: tzdelta = datetime_timedelta(seconds=gmtoff) diff --git a/Lib/test/test_structseq.py b/Lib/test/test_structseq.py --- a/Lib/test/test_structseq.py +++ b/Lib/test/test_structseq.py @@ -78,8 +78,9 @@ def test_fields(self): t = time.gmtime() - self.assertEqual(len(t), t.n_fields) - self.assertEqual(t.n_fields, t.n_sequence_fields+t.n_unnamed_fields) + self.assertEqual(len(t), t.n_sequence_fields) + self.assertEqual(t.n_unnamed_fields, 0) + self.assertEqual(t.n_fields, time._STRUCT_TM_ITEMS) def test_constructor(self): t = time.struct_time diff --git a/Lib/test/test_time.py b/Lib/test/test_time.py --- a/Lib/test/test_time.py +++ b/Lib/test/test_time.py @@ -620,7 +620,58 @@ for invalid in self.invalid_values: self.assertRaises(OverflowError, pytime_object_to_timespec, invalid) + @unittest.skipUnless(time._STRUCT_TM_ITEMS == 11, "needs tm_zone support") + def test_localtime_timezone(self): + # Get the localtime and examine it for the offset and zone. + lt = time.localtime() + self.assertTrue(hasattr(lt, "tm_gmtoff")) + self.assertTrue(hasattr(lt, "tm_zone")) + + # See if the offset and zone are similar to the module + # attributes. + if lt.tm_gmtoff is None: + self.assertTrue(not hasattr(time, "timezone")) + else: + self.assertEqual(lt.tm_gmtoff, -[time.timezone, time.altzone][lt.tm_isdst]) + if lt.tm_zone is None: + self.assertTrue(not hasattr(time, "tzname")) + else: + self.assertEqual(lt.tm_zone, time.tzname[lt.tm_isdst]) + + # Try and make UNIX times from the localtime and a 9-tuple + # created from the localtime. Test to see that the times are + # the same. + t = time.mktime(lt); t9 = time.mktime(lt[:9]) + self.assertEqual(t, t9) + + # Make localtimes from the UNIX times and compare them to + # the original localtime, thus making a round trip. + new_lt = time.localtime(t); new_lt9 = time.localtime(t9) + self.assertEqual(new_lt, lt) + self.assertEqual(new_lt.tm_gmtoff, lt.tm_gmtoff) + self.assertEqual(new_lt.tm_zone, lt.tm_zone) + self.assertEqual(new_lt9, lt) + self.assertEqual(new_lt.tm_gmtoff, lt.tm_gmtoff) + self.assertEqual(new_lt9.tm_zone, lt.tm_zone) + + @unittest.skipUnless(time._STRUCT_TM_ITEMS == 11, "needs tm_zone support") + def test_strptime_timezone(self): + t = time.strptime("UTC", "%Z") + self.assertEqual(t.tm_zone, 'UTC') + t = time.strptime("+0500", "%z") + self.assertEqual(t.tm_gmtoff, 5 * 3600) + + @unittest.skipUnless(time._STRUCT_TM_ITEMS == 11, "needs tm_zone support") + def test_short_times(self): + + import pickle + + # Load a short time structure using pickle. + st = b"ctime\nstruct_time\np0\n((I2007\nI8\nI11\nI1\nI24\nI49\nI5\nI223\nI1\ntp1\n(dp2\ntp3\nRp4\n." + lt = pickle.loads(st) + self.assertIs(lt.tm_gmtoff, None) + self.assertIs(lt.tm_zone, None) def test_main(): support.run_unittest( diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -21,6 +21,11 @@ Library ------- +- Issue #1667546: On platforms supporting tm_zone and tm_gmtoff fields + in struct tm, time.struct_time objects returned by time.gmtime(), + time.localtime() and time.strptime() functions now have tm_zone and + tm_gmtoff attributes. Original patch by Paul Boddie. + - Rename adjusted attribute to adjustable in time.get_clock_info() result. - Issue #3518: Remove references to non-existent BaseManager.from_address() diff --git a/Modules/timemodule.c b/Modules/timemodule.c --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -275,6 +275,10 @@ {"tm_wday", "day of week, range [0, 6], Monday is 0"}, {"tm_yday", "day of year, range [1, 366]"}, {"tm_isdst", "1 if summer time is in effect, 0 if not, and -1 if unknown"}, +#ifdef HAVE_STRUCT_TM_TM_ZONE + {"tm_zone", "abbreviation of timezone name"}, + {"tm_gmtoff", "offset from UTC in seconds"}, +#endif /* HAVE_STRUCT_TM_TM_ZONE */ {0} }; @@ -294,6 +298,7 @@ static int initialized; static PyTypeObject StructTimeType; + static PyObject * tmtotuple(struct tm *p) { @@ -312,6 +317,11 @@ SET(6, (p->tm_wday + 6) % 7); /* Want Monday == 0 */ SET(7, p->tm_yday + 1); /* Want January, 1 == 1 */ SET(8, p->tm_isdst); +#ifdef HAVE_STRUCT_TM_TM_ZONE + PyStructSequence_SET_ITEM(v, 9, + PyUnicode_DecodeLocale(p->tm_zone, "surrogateescape")); + SET(10, p->tm_gmtoff); +#endif /* HAVE_STRUCT_TM_TM_ZONE */ #undef SET if (PyErr_Occurred()) { Py_XDECREF(v); @@ -371,7 +381,10 @@ tm_sec, tm_wday, tm_yday, tm_isdst)\n\ \n\ Convert seconds since the Epoch to a time tuple expressing UTC (a.k.a.\n\ -GMT). When 'seconds' is not passed in, convert the current time instead."); +GMT). When 'seconds' is not passed in, convert the current time instead.\n\ +\n\ +If the platform supports the tm_gmtoff and tm_zone, they are available as\n\ +attributes only."); static int pylocaltime(time_t *timep, struct tm *result) @@ -438,6 +451,17 @@ p->tm_mon--; p->tm_wday = (p->tm_wday + 1) % 7; p->tm_yday--; +#ifdef HAVE_STRUCT_TM_TM_ZONE + if (Py_TYPE(args) == &StructTimeType) { + PyObject *item; + item = PyTuple_GET_ITEM(args, 9); + p->tm_zone = item == Py_None ? NULL : _PyUnicode_AsString(item); + item = PyTuple_GET_ITEM(args, 10); + p->tm_gmtoff = item == Py_None ? 0 : PyLong_AsLong(item); + if (PyErr_Occurred()) + return 0; + } +#endif /* HAVE_STRUCT_TM_TM_ZONE */ return 1; } @@ -778,7 +802,10 @@ PyDoc_STRVAR(mktime_doc, "mktime(tuple) -> floating point number\n\ \n\ -Convert a time tuple in local time to seconds since the Epoch."); +Convert a time tuple in local time to seconds since the Epoch.\n\ +Note that mktime(gmtime(0)) will not generally return zero for most\n\ +time zones; instead the returned value will either be equal to that\n\ +of the timezone or altzone attributes on the time module."); #endif /* HAVE_MKTIME */ #ifdef HAVE_WORKING_TZSET @@ -1443,6 +1470,11 @@ #endif } Py_INCREF(&StructTimeType); +#ifdef HAVE_STRUCT_TM_TM_ZONE + PyModule_AddIntConstant(m, "_STRUCT_TM_ITEMS", 11); +#else + PyModule_AddIntConstant(m, "_STRUCT_TM_ITEMS", 9); +#endif PyModule_AddObject(m, "struct_time", (PyObject*) &StructTimeType); initialized = 1; return m; -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Thu Jun 14 05:48:16 2012 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Thu, 14 Jun 2012 05:48:16 +0200 Subject: [Python-checkins] Daily reference leaks (d16065304bbd): sum=0 Message-ID: results for d16065304bbd on branch "default" -------------------------------------------- Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflognI8H8y', '-x'] From python-checkins at python.org Thu Jun 14 15:49:08 2012 From: python-checkins at python.org (martin.v.loewis) Date: Thu, 14 Jun 2012 15:49:08 +0200 Subject: [Python-checkins] =?utf8?b?Y3B5dGhvbiAoMy4yKTogSXNzdWUgIzE0OTM3?= =?utf8?q?=3A_Fix_typo=2E_Patch_by_Roger_Serwy=2E?= Message-ID: http://hg.python.org/cpython/rev/62030ebb2b01 changeset: 77424:62030ebb2b01 branch: 3.2 parent: 77421:e616985284cd user: Martin v. L?wis date: Thu Jun 14 15:37:21 2012 +0200 summary: Issue #14937: Fix typo. Patch by Roger Serwy. files: Lib/idlelib/AutoComplete.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Lib/idlelib/AutoComplete.py b/Lib/idlelib/AutoComplete.py --- a/Lib/idlelib/AutoComplete.py +++ b/Lib/idlelib/AutoComplete.py @@ -143,7 +143,7 @@ elif hp.is_in_code() and (not mode or mode==COMPLETE_ATTRIBUTES): self._remove_autocomplete_window() mode = COMPLETE_ATTRIBUTES - while i and curline[i-1] in ID_CHARS or ord(curline[i-1]) > 127: + while i and (curline[i-1] in ID_CHARS or ord(curline[i-1]) > 127): i -= 1 comp_start = curline[i:j] if i and curline[i-1] == '.': -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 14 15:49:11 2012 From: python-checkins at python.org (martin.v.loewis) Date: Thu, 14 Jun 2012 15:49:11 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=28merge_3=2E2_-=3E_default=29?= =?utf8?q?=3A_merge_3=2E2?= Message-ID: http://hg.python.org/cpython/rev/82fc6ff7aa3e changeset: 77425:82fc6ff7aa3e parent: 77423:3b5545ba6432 parent: 77424:62030ebb2b01 user: Martin v. L?wis date: Thu Jun 14 15:37:53 2012 +0200 summary: merge 3.2 files: Lib/idlelib/AutoComplete.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Lib/idlelib/AutoComplete.py b/Lib/idlelib/AutoComplete.py --- a/Lib/idlelib/AutoComplete.py +++ b/Lib/idlelib/AutoComplete.py @@ -140,7 +140,7 @@ elif hp.is_in_code() and (not mode or mode==COMPLETE_ATTRIBUTES): self._remove_autocomplete_window() mode = COMPLETE_ATTRIBUTES - while i and curline[i-1] in ID_CHARS or ord(curline[i-1]) > 127: + while i and (curline[i-1] in ID_CHARS or ord(curline[i-1]) > 127): i -= 1 comp_start = curline[i:j] if i and curline[i-1] == '.': -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 14 16:09:37 2012 From: python-checkins at python.org (martin.v.loewis) Date: Thu, 14 Jun 2012 16:09:37 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Issue_=2314936=3A_curses=5F?= =?utf8?q?panel_was_converted_to_PEP_3121_API=2E?= Message-ID: http://hg.python.org/cpython/rev/9a6b45a83dec changeset: 77426:9a6b45a83dec user: Martin v. L?wis date: Thu Jun 14 16:00:24 2012 +0200 summary: Issue #14936: curses_panel was converted to PEP 3121 API. Patch by Robin Schreiber. files: Misc/NEWS | 3 + Modules/_curses_panel.c | 54 ++++++++++++++++++++++------ 2 files changed, 45 insertions(+), 12 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -21,6 +21,9 @@ Library ------- +- Issue #14936: curses_panel was converted to PEP 3121 API. + Patch by Robin Schreiber. + - Issue #1667546: On platforms supporting tm_zone and tm_gmtoff fields in struct tm, time.struct_time objects returned by time.gmtime(), time.localtime() and time.strptime() functions now have tm_zone and diff --git a/Modules/_curses_panel.c b/Modules/_curses_panel.c --- a/Modules/_curses_panel.c +++ b/Modules/_curses_panel.c @@ -16,8 +16,38 @@ #include -static PyObject *PyCursesError; +typedef struct { + PyObject *PyCursesError; +} _curses_panelstate; +#define _curses_panelstate(o) ((_curses_panelstate *)PyModule_GetState(o)) + +/*static PyObject *PyCursesError;*/ + +static int +_curses_panel_clear(PyObject *m) +{ + Py_CLEAR(_curses_panelstate(m)->PyCursesError); + return 0; +} + +static int +_curses_panel_traverse(PyObject *m, visitproc visit, void *arg) +{ + Py_VISIT(_curses_panelstate(m)->PyCursesError); + return 0; +} + +static void +_curses_panel_free(void *m) +{ + _curses_panel_clear((PyObject *) m); +} + +static struct PyModuleDef _curses_panelmodule; + +#define _curses_panelstate_global \ +((_curses_panelstate *) PyModule_GetState(PyState_FindModule(&_curses_panelmodule))) /* Utility Functions */ @@ -34,9 +64,9 @@ return Py_None; } else { if (fname == NULL) { - PyErr_SetString(PyCursesError, catchall_ERR); + PyErr_SetString(_curses_panelstate_global->PyCursesError, catchall_ERR); } else { - PyErr_Format(PyCursesError, "%s() returned ERR", fname); + PyErr_Format(_curses_panelstate_global->PyCursesError, "%s() returned ERR", fname); } return NULL; } @@ -280,7 +310,7 @@ rtn = replace_panel(self->pan, temp->win); if (rtn == ERR) { - PyErr_SetString(PyCursesError, "replace_panel() returned ERR"); + PyErr_SetString(_curses_panelstate_global->PyCursesError, "replace_panel() returned ERR"); return NULL; } Py_DECREF(po->wo); @@ -305,7 +335,7 @@ PyCursesInitialised; obj = (PyObject *) panel_userptr(self->pan); if (obj == NULL) { - PyErr_SetString(PyCursesError, "no userptr set"); + PyErr_SetString(_curses_panelstate_global->PyCursesError, "no userptr set"); return NULL; } @@ -405,7 +435,7 @@ return NULL; pan = new_panel(win->win); if (pan == NULL) { - PyErr_SetString(PyCursesError, catchall_NULL); + PyErr_SetString(_curses_panelstate_global->PyCursesError, catchall_NULL); return NULL; } return (PyObject *)PyCursesPanel_New(pan, win); @@ -467,12 +497,12 @@ PyModuleDef_HEAD_INIT, "_curses_panel", NULL, - -1, + sizeof(_curses_panelstate), PyCurses_methods, NULL, - NULL, - NULL, - NULL + _curses_panel_traverse, + _curses_panel_clear, + _curses_panel_free }; PyMODINIT_FUNC @@ -493,8 +523,8 @@ d = PyModule_GetDict(m); /* For exception _curses_panel.error */ - PyCursesError = PyErr_NewException("_curses_panel.error", NULL, NULL); - PyDict_SetItemString(d, "error", PyCursesError); + _curses_panelstate(m)->PyCursesError = PyErr_NewException("_curses_panel.error", NULL, NULL); + PyDict_SetItemString(d, "error", _curses_panelstate(m)->PyCursesError); /* Make the version available */ v = PyUnicode_FromString(PyCursesVersion); -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 14 16:09:38 2012 From: python-checkins at python.org (martin.v.loewis) Date: Thu, 14 Jun 2012 16:09:38 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Issue_=2314936=3A_curses=5F?= =?utf8?q?panel_was_converted_to_PEP_3121_and_PEP_384_API=2E?= Message-ID: http://hg.python.org/cpython/rev/6eb21c1d3099 changeset: 77427:6eb21c1d3099 user: Martin v. L?wis date: Thu Jun 14 16:01:23 2012 +0200 summary: Issue #14936: curses_panel was converted to PEP 3121 and PEP 384 API. Patch by Robin Schreiber. files: Misc/NEWS | 2 +- Modules/_curses_panel.c | 72 +++++++++++----------------- 2 files changed, 30 insertions(+), 44 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -21,7 +21,7 @@ Library ------- -- Issue #14936: curses_panel was converted to PEP 3121 API. +- Issue #14936: curses_panel was converted to PEP 3121 and PEP 384 API. Patch by Robin Schreiber. - Issue #1667546: On platforms supporting tm_zone and tm_gmtoff fields diff --git a/Modules/_curses_panel.c b/Modules/_curses_panel.c --- a/Modules/_curses_panel.c +++ b/Modules/_curses_panel.c @@ -18,12 +18,11 @@ typedef struct { PyObject *PyCursesError; + PyObject *PyCursesPanel_Type; } _curses_panelstate; #define _curses_panelstate(o) ((_curses_panelstate *)PyModule_GetState(o)) -/*static PyObject *PyCursesError;*/ - static int _curses_panel_clear(PyObject *m) { @@ -84,9 +83,8 @@ PyCursesWindowObject *wo; /* for reference counts */ } PyCursesPanelObject; -PyTypeObject PyCursesPanel_Type; - -#define PyCursesPanel_Check(v) (Py_TYPE(v) == &PyCursesPanel_Type) +#define PyCursesPanel_Check(v) \ + (Py_TYPE(v) == _curses_panelstate_global->PyCursesPanel_Type) /* Some helper functions. The problem is that there's always a window associated with a panel. To ensure that Python's GC doesn't pull @@ -205,7 +203,8 @@ { PyCursesPanelObject *po; - po = PyObject_NEW(PyCursesPanelObject, &PyCursesPanel_Type); + po = PyObject_NEW(PyCursesPanelObject, + (PyTypeObject *)(_curses_panelstate_global)->PyCursesPanel_Type); if (po == NULL) return NULL; po->pan = pan; if (insert_lop(po) < 0) { @@ -364,36 +363,18 @@ /* -------------------------------------------------------*/ -PyTypeObject PyCursesPanel_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "_curses_panel.curses panel", /*tp_name*/ - sizeof(PyCursesPanelObject), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - /* methods */ - (destructor)PyCursesPanel_Dealloc, /*tp_dealloc*/ - 0, /*tp_print*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_reserved*/ - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash*/ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT, /*tp_flags*/ - 0, /*tp_doc*/ - 0, /*tp_traverse*/ - 0, /*tp_clear*/ - 0, /*tp_richcompare*/ - 0, /*tp_weaklistoffset*/ - 0, /*tp_iter*/ - 0, /*tp_iternext*/ - PyCursesPanel_Methods, /*tp_methods*/ +static PyType_Slot PyCursesPanel_Type_slots[] = { + {Py_tp_dealloc, PyCursesPanel_Dealloc}, + {Py_tp_methods, PyCursesPanel_Methods}, + {0, 0}, +}; + +static PyType_Spec PyCursesPanel_Type_spec = { + "_curses_panel.curses panel", + sizeof(PyCursesPanelObject), + 0, + Py_TPFLAGS_DEFAULT, + PyCursesPanel_Type_slots }; /* Wrapper for panel_above(NULL). This function returns the bottom @@ -510,18 +491,20 @@ { PyObject *m, *d, *v; - /* Initialize object type */ - if (PyType_Ready(&PyCursesPanel_Type) < 0) - return NULL; - - import_curses(); - /* Create the module and add the functions */ m = PyModule_Create(&_curses_panelmodule); if (m == NULL) - return NULL; + goto fail; d = PyModule_GetDict(m); + /* Initialize object type */ + _curses_panelstate(m)->PyCursesPanel_Type = \ + PyType_FromSpec(&PyCursesPanel_Type_spec); + if (_curses_panelstate(m)->PyCursesPanel_Type == NULL) + goto fail; + + import_curses(); + /* For exception _curses_panel.error */ _curses_panelstate(m)->PyCursesError = PyErr_NewException("_curses_panel.error", NULL, NULL); PyDict_SetItemString(d, "error", _curses_panelstate(m)->PyCursesError); @@ -532,4 +515,7 @@ PyDict_SetItemString(d, "__version__", v); Py_DECREF(v); return m; + fail: + Py_XDECREF(m); + return NULL; } -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 14 16:14:46 2012 From: python-checkins at python.org (brett.cannon) Date: Thu, 14 Jun 2012 16:14:46 +0200 Subject: [Python-checkins] =?utf8?q?peps=3A_Fix_a_spelling_error=2E?= Message-ID: http://hg.python.org/peps/rev/ea71020acbad changeset: 4460:ea71020acbad user: Brett Cannon date: Thu Jun 14 10:10:28 2012 -0400 summary: Fix a spelling error. files: pep-0362.txt | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/pep-0362.txt b/pep-0362.txt --- a/pep-0362.txt +++ b/pep-0362.txt @@ -320,7 +320,7 @@ format(func=sig.qualname, arg=param.name)) def check_type(sig, arg_name, arg_type, arg_value): - # Internal function that incapsulates arguments type checking + # Internal function that encapsulates arguments type checking if not isinstance(arg_value, arg_type): raise ValueError("{func}: wrong type of {arg!r} argument, " \ "{exp!r} expected, got {got!r}". \ -- Repository URL: http://hg.python.org/peps From python-checkins at python.org Thu Jun 14 16:49:08 2012 From: python-checkins at python.org (richard.oudkerk) Date: Thu, 14 Jun 2012 16:49:08 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Issue_=2313841=3A_Make_chil?= =?utf8?q?d_processes_exit_using_sys=2Eexit=28=29_on_Windows?= Message-ID: http://hg.python.org/cpython/rev/d31e83497c5a changeset: 77428:d31e83497c5a user: Richard Oudkerk date: Thu Jun 14 15:30:10 2012 +0100 summary: Issue #13841: Make child processes exit using sys.exit() on Windows files: Lib/multiprocessing/forking.py | 6 +- Lib/multiprocessing/managers.py | 67 ++++++++----------- Lib/multiprocessing/util.py | 27 ++++--- Lib/test/support.py | 2 +- Lib/test/test_multiprocessing.py | 5 + Misc/NEWS | 2 + 6 files changed, 54 insertions(+), 55 deletions(-) diff --git a/Lib/multiprocessing/forking.py b/Lib/multiprocessing/forking.py --- a/Lib/multiprocessing/forking.py +++ b/Lib/multiprocessing/forking.py @@ -13,7 +13,7 @@ from multiprocessing import util, process -__all__ = ['Popen', 'assert_spawning', 'exit', 'duplicate', 'close', 'ForkingPickler'] +__all__ = ['Popen', 'assert_spawning', 'duplicate', 'close', 'ForkingPickler'] # # Check that the current thread is spawning a child process @@ -75,7 +75,6 @@ # if sys.platform != 'win32': - exit = os._exit duplicate = os.dup close = os.close @@ -168,7 +167,6 @@ WINEXE = (sys.platform == 'win32' and getattr(sys, 'frozen', False)) WINSERVICE = sys.executable.lower().endswith("pythonservice.exe") - exit = _winapi.ExitProcess close = _winapi.CloseHandle # @@ -349,7 +347,7 @@ from_parent.close() exitcode = self._bootstrap() - exit(exitcode) + sys.exit(exitcode) def get_preparation_data(name): diff --git a/Lib/multiprocessing/managers.py b/Lib/multiprocessing/managers.py --- a/Lib/multiprocessing/managers.py +++ b/Lib/multiprocessing/managers.py @@ -22,7 +22,7 @@ from traceback import format_exc from multiprocessing import Process, current_process, active_children, Pool, util, connection from multiprocessing.process import AuthenticationString -from multiprocessing.forking import exit, Popen, ForkingPickler +from multiprocessing.forking import Popen, ForkingPickler from time import time as _time # @@ -140,28 +140,38 @@ self.id_to_obj = {'0': (None, ())} self.id_to_refcount = {} self.mutex = threading.RLock() - self.stop = 0 def serve_forever(self): ''' Run the server forever ''' + self.stop_event = threading.Event() current_process()._manager_server = self try: + accepter = threading.Thread(target=self.accepter) + accepter.daemon = True + accepter.start() try: - while 1: - try: - c = self.listener.accept() - except (OSError, IOError): - continue - t = threading.Thread(target=self.handle_request, args=(c,)) - t.daemon = True - t.start() + while not self.stop_event.is_set(): + self.stop_event.wait(1) except (KeyboardInterrupt, SystemExit): pass finally: - self.stop = 999 - self.listener.close() + if sys.stdout != sys.__stdout__: + util.debug('resetting stdout, stderr') + sys.stdout = sys.__stdout__ + sys.stderr = sys.__stderr__ + sys.exit(0) + + def accepter(self): + while True: + try: + c = self.listener.accept() + except (OSError, IOError): + continue + t = threading.Thread(target=self.handle_request, args=(c,)) + t.daemon = True + t.start() def handle_request(self, c): ''' @@ -208,7 +218,7 @@ send = conn.send id_to_obj = self.id_to_obj - while not self.stop: + while not self.stop_event.is_set(): try: methodname = obj = None @@ -318,32 +328,13 @@ Shutdown this process ''' try: - try: - util.debug('manager received shutdown message') - c.send(('#RETURN', None)) - - if sys.stdout != sys.__stdout__: - util.debug('resetting stdout, stderr') - sys.stdout = sys.__stdout__ - sys.stderr = sys.__stderr__ - - util._run_finalizers(0) - - for p in active_children(): - util.debug('terminating a child process of manager') - p.terminate() - - for p in active_children(): - util.debug('terminating a child process of manager') - p.join() - - util._run_finalizers() - util.info('manager exiting with exitcode 0') - except: - import traceback - traceback.print_exc() + util.debug('manager received shutdown message') + c.send(('#RETURN', None)) + except: + import traceback + traceback.print_exc() finally: - exit(0) + self.stop_event.set() def create(self, c, typeid, *args, **kwds): ''' diff --git a/Lib/multiprocessing/util.py b/Lib/multiprocessing/util.py --- a/Lib/multiprocessing/util.py +++ b/Lib/multiprocessing/util.py @@ -269,21 +269,24 @@ def _exit_function(): global _exiting - info('process shutting down') - debug('running all "atexit" finalizers with priority >= 0') - _run_finalizers(0) + if not _exiting: + _exiting = True - for p in active_children(): - if p._daemonic: - info('calling terminate() for daemon %s', p.name) - p._popen.terminate() + info('process shutting down') + debug('running all "atexit" finalizers with priority >= 0') + _run_finalizers(0) - for p in active_children(): - info('calling join() for process %s', p.name) - p.join() + for p in active_children(): + if p._daemonic: + info('calling terminate() for daemon %s', p.name) + p._popen.terminate() - debug('running the remaining "atexit" finalizers') - _run_finalizers() + for p in active_children(): + info('calling join() for process %s', p.name) + p.join() + + debug('running the remaining "atexit" finalizers') + _run_finalizers() atexit.register(_exit_function) diff --git a/Lib/test/support.py b/Lib/test/support.py --- a/Lib/test/support.py +++ b/Lib/test/support.py @@ -1593,7 +1593,7 @@ This will typically be run on the result of the communicate() method of a subprocess.Popen object. """ - stderr = re.sub(br"\[\d+ refs\]\r?\n?$", b"", stderr).strip() + stderr = re.sub(br"\[\d+ refs\]\r?\n?", b"", stderr).strip() return stderr def args_from_interpreter_flags(): diff --git a/Lib/test/test_multiprocessing.py b/Lib/test/test_multiprocessing.py --- a/Lib/test/test_multiprocessing.py +++ b/Lib/test/test_multiprocessing.py @@ -1564,6 +1564,11 @@ manager.shutdown() + # If the manager process exited cleanly then the exitcode + # will be zero. Otherwise (after a short timeout) + # terminate() is used, resulting in an exitcode of -SIGTERM. + self.assertEqual(manager._process.exitcode, 0) + # # Test of connecting to a remote server and using xmlrpclib for serialization # diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -21,6 +21,8 @@ Library ------- +- Issue #13841: Make child processes exit using sys.exit() on Windows. + - Issue #14936: curses_panel was converted to PEP 3121 and PEP 384 API. Patch by Robin Schreiber. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 14 21:54:18 2012 From: python-checkins at python.org (antoine.pitrou) Date: Thu, 14 Jun 2012 21:54:18 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Issue_=2315070=3A_fix_VS9?= =?utf8?q?=2E0_build_regression?= Message-ID: http://hg.python.org/cpython/rev/ccbf6f970943 changeset: 77429:ccbf6f970943 user: Antoine Pitrou date: Thu Jun 14 21:51:12 2012 +0200 summary: Issue #15070: fix VS9.0 build regression files: PC/VS9.0/pythoncore.vcproj | 8 ++++++++ 1 files changed, 8 insertions(+), 0 deletions(-) diff --git a/PC/VS9.0/pythoncore.vcproj b/PC/VS9.0/pythoncore.vcproj --- a/PC/VS9.0/pythoncore.vcproj +++ b/PC/VS9.0/pythoncore.vcproj @@ -803,6 +803,10 @@ > + + @@ -1563,6 +1567,10 @@ > + + -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 14 21:57:28 2012 From: python-checkins at python.org (antoine.pitrou) Date: Thu, 14 Jun 2012 21:57:28 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Update_=2Ehgignore_for_VS9?= =?utf8?q?=2E0-generated_files?= Message-ID: http://hg.python.org/cpython/rev/1c792a3e4763 changeset: 77430:1c792a3e4763 user: Antoine Pitrou date: Thu Jun 14 21:54:24 2012 +0200 summary: Update .hgignore for VS9.0-generated files files: .hgignore | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-) diff --git a/.hgignore b/.hgignore --- a/.hgignore +++ b/.hgignore @@ -55,6 +55,8 @@ PC/pythonnt_rc*.h PC/*.obj PC/*.exe +PC/*/*.exe +PC/*/*.pdb PC/*/*.user PC/*/*.ncb PC/*/*.suo -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Fri Jun 15 05:47:04 2012 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Fri, 15 Jun 2012 05:47:04 +0200 Subject: [Python-checkins] Daily reference leaks (1c792a3e4763): sum=0 Message-ID: results for 1c792a3e4763 on branch "default" -------------------------------------------- Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflog19SXNK', '-x'] From python-checkins at python.org Fri Jun 15 06:44:20 2012 From: python-checkins at python.org (eli.bendersky) Date: Fri, 15 Jun 2012 06:44:20 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Replace_the_iter/itertext_m?= =?utf8?q?ethods_of_Element_in_=5Felementtree_with_true_C?= Message-ID: http://hg.python.org/cpython/rev/652d148bdc1d changeset: 77431:652d148bdc1d user: Eli Bendersky date: Fri Jun 15 07:42:50 2012 +0300 summary: Replace the iter/itertext methods of Element in _elementtree with true C implementations, instead of the bootstrapped Python code. In addition to being cleaner (removing the last remains of the bootstrapping code in _elementtree), this gives a 10x performance boost for iter() on large documents. Also reorganized the tests a bit to be more robust. files: Lib/test/test_xml_etree.py | 247 ++++++++------ Lib/test/test_xml_etree_c.py | 28 +- Lib/xml/etree/ElementTree.py | 6 +- Modules/_elementtree.c | 362 +++++++++++++++++----- 4 files changed, 414 insertions(+), 229 deletions(-) diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py --- a/Lib/test/test_xml_etree.py +++ b/Lib/test/test_xml_etree.py @@ -23,7 +23,8 @@ from test import support from test.support import findfile, import_fresh_module, gc_collect -pyET = import_fresh_module('xml.etree.ElementTree', blocked=['_elementtree']) +pyET = None +ET = None SIMPLE_XMLFILE = findfile("simple.xml", subdir="xmltestdata") try: @@ -209,10 +210,8 @@ These methods return an iterable. See bug 6472. - >>> check_method(element.iter("tag").__next__) >>> check_method(element.iterfind("tag").__next__) >>> check_method(element.iterfind("*").__next__) - >>> check_method(tree.iter("tag").__next__) >>> check_method(tree.iterfind("tag").__next__) >>> check_method(tree.iterfind("*").__next__) @@ -291,42 +290,6 @@ 'hello' """ -# Only with Python implementation -def simplefind(): - """ - Test find methods using the elementpath fallback. - - >>> ElementTree = pyET - - >>> CurrentElementPath = ElementTree.ElementPath - >>> ElementTree.ElementPath = ElementTree._SimpleElementPath() - >>> elem = ElementTree.XML(SAMPLE_XML) - >>> elem.find("tag").tag - 'tag' - >>> ElementTree.ElementTree(elem).find("tag").tag - 'tag' - >>> elem.findtext("tag") - 'text' - >>> elem.findtext("tog") - >>> elem.findtext("tog", "default") - 'default' - >>> ElementTree.ElementTree(elem).findtext("tag") - 'text' - >>> summarize_list(elem.findall("tag")) - ['tag', 'tag'] - >>> summarize_list(elem.findall(".//tag")) - ['tag', 'tag', 'tag'] - - Path syntax doesn't work in this case. - - >>> elem.find("section/tag") - >>> elem.findtext("section/tag") - >>> summarize_list(elem.findall("section/tag")) - [] - - >>> ElementTree.ElementPath = CurrentElementPath - """ - def find(): """ Test find methods (including xpath syntax). @@ -1002,36 +965,6 @@ '1 < 2\n' """ -def iterators(): - """ - Test iterators. - - >>> e = ET.XML("this is a paragraph...") - >>> summarize_list(e.iter()) - ['html', 'body', 'i'] - >>> summarize_list(e.find("body").iter()) - ['body', 'i'] - >>> summarize(next(e.iter())) - 'html' - >>> "".join(e.itertext()) - 'this is a paragraph...' - >>> "".join(e.find("body").itertext()) - 'this is a paragraph.' - >>> next(e.itertext()) - 'this is a ' - - Method iterparse should return an iterator. See bug 6472. - - >>> sourcefile = serialize(e, to_string=False) - >>> next(ET.iterparse(sourcefile)) # doctest: +ELLIPSIS - ('end', ) - - >>> tree = ET.ElementTree(None) - >>> tree.iter() - Traceback (most recent call last): - AttributeError: 'NoneType' object has no attribute 'iter' - """ - ENTITY_XML = """\ @@ -1339,6 +1272,7 @@ """.format(html.escape(SIMPLE_XMLFILE, True)) + def xinclude_loader(href, parse="xml", encoding=None): try: data = XINCLUDE[href] @@ -1411,22 +1345,6 @@ >>> # print(serialize(document)) # C5 """ -def xinclude_default(): - """ - >>> from xml.etree import ElementInclude - - >>> document = xinclude_loader("default.xml") - >>> ElementInclude.include(document) - >>> print(serialize(document)) # default - -

Example.

- - text - texttail - - -
- """ # # badly formatted xi:include tags @@ -1917,9 +1835,8 @@ self.assertIsInstance(ET.QName, type) self.assertIsInstance(ET.ElementTree, type) self.assertIsInstance(ET.Element, type) - # XXX issue 14128 with C ElementTree - # self.assertIsInstance(ET.TreeBuilder, type) - # self.assertIsInstance(ET.XMLParser, type) + self.assertIsInstance(ET.TreeBuilder, type) + self.assertIsInstance(ET.XMLParser, type) def test_Element_subclass_trivial(self): class MyElement(ET.Element): @@ -1953,6 +1870,73 @@ self.assertEqual(mye.newmethod(), 'joe') +class ElementIterTest(unittest.TestCase): + def _ilist(self, elem, tag=None): + return summarize_list(elem.iter(tag)) + + def test_basic(self): + doc = ET.XML("this is a paragraph...") + self.assertEqual(self._ilist(doc), ['html', 'body', 'i']) + self.assertEqual(self._ilist(doc.find('body')), ['body', 'i']) + self.assertEqual(next(doc.iter()).tag, 'html') + self.assertEqual(''.join(doc.itertext()), 'this is a paragraph...') + self.assertEqual(''.join(doc.find('body').itertext()), + 'this is a paragraph.') + self.assertEqual(next(doc.itertext()), 'this is a ') + + # iterparse should return an iterator + sourcefile = serialize(doc, to_string=False) + self.assertEqual(next(ET.iterparse(sourcefile))[0], 'end') + + tree = ET.ElementTree(None) + self.assertRaises(AttributeError, tree.iter) + + def test_corners(self): + # single root, no subelements + a = ET.Element('a') + self.assertEqual(self._ilist(a), ['a']) + + # one child + b = ET.SubElement(a, 'b') + self.assertEqual(self._ilist(a), ['a', 'b']) + + # one child and one grandchild + c = ET.SubElement(b, 'c') + self.assertEqual(self._ilist(a), ['a', 'b', 'c']) + + # two children, only first with grandchild + d = ET.SubElement(a, 'd') + self.assertEqual(self._ilist(a), ['a', 'b', 'c', 'd']) + + # replace first child by second + a[0] = a[1] + del a[1] + self.assertEqual(self._ilist(a), ['a', 'd']) + + def test_iter_by_tag(self): + doc = ET.XML(''' + + + bedroom1 + bedroom2 + + nothing here + + + bedroom8 + + ''') + + self.assertEqual(self._ilist(doc, 'room'), ['room'] * 3) + self.assertEqual(self._ilist(doc, 'house'), ['house'] * 2) + + # make sure both tag=None and tag='*' return all tags + all_tags = ['document', 'house', 'room', 'room', + 'shed', 'house', 'room'] + self.assertEqual(self._ilist(doc), all_tags) + self.assertEqual(self._ilist(doc, '*'), all_tags) + + class TreeBuilderTest(unittest.TestCase): sample1 = (' +

Example.

+ + text + texttail + + +''') class XMLParserTest(unittest.TestCase): sample1 = '22' sample2 = ('>> cElementTree = cET - >>> e = cElementTree.Element('a') - >>> getattr(e, '\uD800') # doctest: +ELLIPSIS - Traceback (most recent call last): - ... - UnicodeEncodeError: ... - - >>> p = cElementTree.XMLParser() - >>> p.version.split()[0] - 'Expat' - >>> getattr(p, '\uD800') - Traceback (most recent call last): - ... - AttributeError: 'XMLParser' object has no attribute '\ud800' - """ - - class MiscTests(unittest.TestCase): # Issue #8651. @support.bigmemtest(size=support._2G + 100, memuse=1) @@ -46,6 +21,7 @@ finally: data = None + @unittest.skipUnless(cET, 'requires _elementtree') class TestAliasWorking(unittest.TestCase): # Test that the cET alias module is alive @@ -53,6 +29,7 @@ e = cET_alias.Element('foo') self.assertEqual(e.tag, 'foo') + @unittest.skipUnless(cET, 'requires _elementtree') class TestAcceleratorImported(unittest.TestCase): # Test that the C accelerator was imported, as expected @@ -67,7 +44,6 @@ from test import test_xml_etree, test_xml_etree_c # Run the tests specific to the C implementation - support.run_doctest(test_xml_etree_c, verbosity=True) support.run_unittest( MiscTests, TestAliasWorking, diff --git a/Lib/xml/etree/ElementTree.py b/Lib/xml/etree/ElementTree.py --- a/Lib/xml/etree/ElementTree.py +++ b/Lib/xml/etree/ElementTree.py @@ -916,11 +916,7 @@ _raise_serialization_error(qname) # populate qname and namespaces table - try: - iterate = elem.iter - except AttributeError: - iterate = elem.getiterator # cET compatibility - for elem in iterate(): + for elem in elem.iter(): tag = elem.tag if isinstance(tag, QName): if tag.text not in qnames: diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c --- a/Modules/_elementtree.c +++ b/Modules/_elementtree.c @@ -103,8 +103,6 @@ /* glue functions (see the init function for details) */ static PyObject* elementtree_parseerror_obj; static PyObject* elementtree_deepcopy_obj; -static PyObject* elementtree_iter_obj; -static PyObject* elementtree_itertext_obj; static PyObject* elementpath_obj; /* helpers */ @@ -1109,67 +1107,32 @@ return list; } -static PyObject* -element_iter(ElementObject* self, PyObject* args) + +static PyObject * +create_elementiter(ElementObject *self, PyObject *tag, int gettext); + + +static PyObject * +element_iter(ElementObject *self, PyObject *args) { - PyObject* result; - PyObject* tag = Py_None; if (!PyArg_ParseTuple(args, "|O:iter", &tag)) return NULL; - if (!elementtree_iter_obj) { - PyErr_SetString( - PyExc_RuntimeError, - "iter helper not found" - ); - return NULL; - } - - args = PyTuple_New(2); - if (!args) - return NULL; - - Py_INCREF(self); PyTuple_SET_ITEM(args, 0, (PyObject*) self); - Py_INCREF(tag); PyTuple_SET_ITEM(args, 1, (PyObject*) tag); - - result = PyObject_CallObject(elementtree_iter_obj, args); - - Py_DECREF(args); - - return result; + return create_elementiter(self, tag, 0); } static PyObject* element_itertext(ElementObject* self, PyObject* args) { - PyObject* result; - if (!PyArg_ParseTuple(args, ":itertext")) return NULL; - if (!elementtree_itertext_obj) { - PyErr_SetString( - PyExc_RuntimeError, - "itertext helper not found" - ); - return NULL; - } - - args = PyTuple_New(1); - if (!args) - return NULL; - - Py_INCREF(self); PyTuple_SET_ITEM(args, 0, (PyObject*) self); - - result = PyObject_CallObject(elementtree_itertext_obj, args); - - Py_DECREF(args); - - return result; + return create_elementiter(self, Py_None, 1); } + static PyObject* element_getitem(PyObject* self_, Py_ssize_t index) { @@ -1790,6 +1753,267 @@ 0, /* tp_free */ }; +/******************************* Element iterator ****************************/ + +/* ElementIterObject represents the iteration state over an XML element in + * pre-order traversal. To keep track of which sub-element should be returned + * next, a stack of parents is maintained. This is a standard stack-based + * iterative pre-order traversal of a tree. + * The stack is managed using a single-linked list starting at parent_stack. + * Each stack node contains the saved parent to which we should return after + * the current one is exhausted, and the next child to examine in that parent. + */ +typedef struct ParentLocator_t { + ElementObject *parent; + Py_ssize_t child_index; + struct ParentLocator_t *next; +} ParentLocator; + +typedef struct { + PyObject_HEAD + ParentLocator *parent_stack; + ElementObject *root_element; + PyObject *sought_tag; + int root_done; + int gettext; +} ElementIterObject; + + +static void +elementiter_dealloc(ElementIterObject *it) +{ + ParentLocator *p = it->parent_stack; + while (p) { + ParentLocator *temp = p; + Py_XDECREF(p->parent); + p = p->next; + PyObject_Free(temp); + } + + Py_XDECREF(it->sought_tag); + Py_XDECREF(it->root_element); + + PyObject_GC_UnTrack(it); + PyObject_GC_Del(it); +} + +static int +elementiter_traverse(ElementIterObject *it, visitproc visit, void *arg) +{ + ParentLocator *p = it->parent_stack; + while (p) { + Py_VISIT(p->parent); + p = p->next; + } + + Py_VISIT(it->root_element); + Py_VISIT(it->sought_tag); + return 0; +} + +/* Helper function for elementiter_next. Add a new parent to the parent stack. + */ +static ParentLocator * +parent_stack_push_new(ParentLocator *stack, ElementObject *parent) +{ + ParentLocator *new_node = PyObject_Malloc(sizeof(ParentLocator)); + if (new_node) { + new_node->parent = parent; + Py_INCREF(parent); + new_node->child_index = 0; + new_node->next = stack; + } + return new_node; +} + +static PyObject * +elementiter_next(ElementIterObject *it) +{ + /* Sub-element iterator. + * + * A short note on gettext: this function serves both the iter() and + * itertext() methods to avoid code duplication. However, there are a few + * small differences in the way these iterations work. Namely: + * - itertext() only yields text from nodes that have it, and continues + * iterating when a node doesn't have text (so it doesn't return any + * node like iter()) + * - itertext() also has to handle tail, after finishing with all the + * children of a node. + */ + + while (1) { + /* Handle the case reached in the beginning and end of iteration, where + * the parent stack is empty. The root_done flag gives us indication + * whether we've just started iterating (so root_done is 0), in which + * case the root is returned. If root_done is 1 and we're here, the + * iterator is exhausted. + */ + if (!it->parent_stack->parent) { + if (it->root_done) { + PyErr_SetNone(PyExc_StopIteration); + return NULL; + } else { + it->parent_stack = parent_stack_push_new(it->parent_stack, + it->root_element); + if (!it->parent_stack) { + PyErr_NoMemory(); + return NULL; + } + + it->root_done = 1; + if (it->sought_tag == Py_None || + PyObject_RichCompareBool(it->root_element->tag, + it->sought_tag, Py_EQ) == 1) { + if (it->gettext) { + PyObject *text = JOIN_OBJ(it->root_element->text); + if (PyObject_IsTrue(text)) { + Py_INCREF(text); + return text; + } + } else { + Py_INCREF(it->root_element); + return (PyObject *)it->root_element; + } + } + } + } + + /* See if there are children left to traverse in the current parent. If + * yes, visit the next child. If not, pop the stack and try again. + */ + ElementObject *cur_parent = it->parent_stack->parent; + Py_ssize_t child_index = it->parent_stack->child_index; + if (cur_parent->extra && child_index < cur_parent->extra->length) { + ElementObject *child = (ElementObject *) + cur_parent->extra->children[child_index]; + it->parent_stack->child_index++; + it->parent_stack = parent_stack_push_new(it->parent_stack, + child); + if (!it->parent_stack) { + PyErr_NoMemory(); + return NULL; + } + + if (it->gettext) { + PyObject *text = JOIN_OBJ(child->text); + if (PyObject_IsTrue(text)) { + Py_INCREF(text); + return text; + } + } else if (it->sought_tag == Py_None || + PyObject_RichCompareBool(child->tag, + it->sought_tag, Py_EQ) == 1) { + Py_INCREF(child); + return (PyObject *)child; + } + else + continue; + } + else { + PyObject *tail = it->gettext ? JOIN_OBJ(cur_parent->tail) : Py_None; + ParentLocator *next = it->parent_stack->next; + Py_XDECREF(it->parent_stack->parent); + PyObject_Free(it->parent_stack); + it->parent_stack = next; + + /* Note that extra condition on it->parent_stack->parent here; + * this is because itertext() is supposed to only return *inner* + * text, not text following the element it began iteration with. + */ + if (it->parent_stack->parent && PyObject_IsTrue(tail)) { + Py_INCREF(tail); + return tail; + } + } + } + + return NULL; +} + + +static PyTypeObject ElementIter_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "_elementtree._element_iterator", /* tp_name */ + sizeof(ElementIterObject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)elementiter_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ + 0, /* tp_doc */ + (traverseproc)elementiter_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)elementiter_next, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + + +static PyObject * +create_elementiter(ElementObject *self, PyObject *tag, int gettext) +{ + ElementIterObject *it; + PyObject *star = NULL; + + it = PyObject_GC_New(ElementIterObject, &ElementIter_Type); + if (!it) + return NULL; + if (!(it->parent_stack = PyObject_Malloc(sizeof(ParentLocator)))) { + PyObject_GC_Del(it); + return NULL; + } + + it->parent_stack->parent = NULL; + it->parent_stack->child_index = 0; + it->parent_stack->next = NULL; + + if (PyUnicode_Check(tag)) + star = PyUnicode_FromString("*"); + else if (PyBytes_Check(tag)) + star = PyBytes_FromString("*"); + + if (star && PyObject_RichCompareBool(tag, star, Py_EQ) == 1) + tag = Py_None; + + Py_XDECREF(star); + it->sought_tag = tag; + it->root_done = 0; + it->gettext = gettext; + it->root_element = self; + + Py_INCREF(self); + Py_INCREF(tag); + + PyObject_GC_Track(it); + return (PyObject *)it; +} + + /* ==================================================================== */ /* the tree builder type */ @@ -3238,8 +3462,7 @@ PyMODINIT_FUNC PyInit__elementtree(void) { - PyObject *m, *g, *temp; - char* bootstrap; + PyObject *m, *temp; /* Initialize object types */ if (PyType_Ready(&TreeBuilder_Type) < 0) @@ -3255,44 +3478,6 @@ if (!m) return NULL; - /* The code below requires that the module gets already added - to sys.modules. */ - PyDict_SetItemString(PyImport_GetModuleDict(), - _elementtreemodule.m_name, - m); - - /* python glue code */ - - g = PyDict_New(); - if (!g) - return NULL; - - PyDict_SetItemString(g, "__builtins__", PyEval_GetBuiltins()); - - bootstrap = ( - "def iter(node, tag=None):\n" /* helper */ - " if tag == '*':\n" - " tag = None\n" - " if tag is None or node.tag == tag:\n" - " yield node\n" - " for node in node:\n" - " for node in iter(node, tag):\n" - " yield node\n" - - "def itertext(node):\n" /* helper */ - " if node.text:\n" - " yield node.text\n" - " for e in node:\n" - " for s in e.itertext():\n" - " yield s\n" - " if e.tail:\n" - " yield e.tail\n" - - ); - - if (!PyRun_String(bootstrap, Py_file_input, g, NULL)) - return NULL; - if (!(temp = PyImport_ImportModule("copy"))) return NULL; elementtree_deepcopy_obj = PyObject_GetAttrString(temp, "deepcopy"); @@ -3301,9 +3486,6 @@ if (!(elementpath_obj = PyImport_ImportModule("xml.etree.ElementPath"))) return NULL; - elementtree_iter_obj = PyDict_GetItemString(g, "iter"); - elementtree_itertext_obj = PyDict_GetItemString(g, "itertext"); - /* link against pyexpat */ expat_capi = PyCapsule_Import(PyExpat_CAPSULE_NAME, 0); if (expat_capi) { -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 15 06:54:18 2012 From: python-checkins at python.org (eli.bendersky) Date: Fri, 15 Jun 2012 06:54:18 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Fix_windows_compilation_pro?= =?utf8?q?blems_caused_by_previous_commit=2E?= Message-ID: http://hg.python.org/cpython/rev/3c089edbf02c changeset: 77432:3c089edbf02c user: Eli Bendersky date: Fri Jun 15 07:52:49 2012 +0300 summary: Fix windows compilation problems caused by previous commit. files: Modules/_elementtree.c | 6 ++++-- 1 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c --- a/Modules/_elementtree.c +++ b/Modules/_elementtree.c @@ -1840,6 +1840,8 @@ * - itertext() also has to handle tail, after finishing with all the * children of a node. */ + ElementObject *cur_parent; + Py_ssize_t child_index; while (1) { /* Handle the case reached in the beginning and end of iteration, where @@ -1881,8 +1883,8 @@ /* See if there are children left to traverse in the current parent. If * yes, visit the next child. If not, pop the stack and try again. */ - ElementObject *cur_parent = it->parent_stack->parent; - Py_ssize_t child_index = it->parent_stack->child_index; + cur_parent = it->parent_stack->parent; + child_index = it->parent_stack->child_index; if (cur_parent->extra && child_index < cur_parent->extra->length) { ElementObject *child = (ElementObject *) cur_parent->extra->children[child_index]; -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 15 07:38:37 2012 From: python-checkins at python.org (eli.bendersky) Date: Fri, 15 Jun 2012 07:38:37 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_mark_problematic_test_as_ex?= =?utf8?q?pected_failure_-_investigating?= Message-ID: http://hg.python.org/cpython/rev/40f2fdae6d22 changeset: 77433:40f2fdae6d22 user: Eli Bendersky date: Fri Jun 15 08:37:08 2012 +0300 summary: mark problematic test as expected failure - investigating files: Lib/test/test_xml_etree.py | 8 ++++++-- 1 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py --- a/Lib/test/test_xml_etree.py +++ b/Lib/test/test_xml_etree.py @@ -2010,7 +2010,9 @@ ('html', '-//W3C//DTD XHTML 1.0 Transitional//EN', 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd')) + class XincludeTest(unittest.TestCase): + @unittest.expectedFailure def test_xinclude_default(self): from xml.etree import ElementInclude doc = xinclude_loader('default.xml') @@ -2024,6 +2026,8 @@ ''') + + class XMLParserTest(unittest.TestCase): sample1 = '22' sample2 = (' http://hg.python.org/cpython/rev/ea0dc4338987 changeset: 77434:ea0dc4338987 user: Eli Bendersky date: Fri Jun 15 09:03:19 2012 +0300 summary: Removed _SimpleElementPath and its flaky test. The test monkey-patches the module, which causes other failures and fails itself depending on the order tests are run. files: Lib/test/test_xml_etree.py | 23 --------------------- Lib/xml/etree/ElementTree.py | 26 +----------------------- 2 files changed, 1 insertions(+), 48 deletions(-) diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py --- a/Lib/test/test_xml_etree.py +++ b/Lib/test/test_xml_etree.py @@ -2234,28 +2234,6 @@ self.assertEqual(pyET.Element.__module__, 'xml.etree.ElementTree') self.assertEqual(pyET.SubElement.__module__, 'xml.etree.ElementTree') - -class ElementPathFallbackTest(unittest.TestCase): - def test_fallback(self): - current_ElementPath = ET.ElementPath - ET.ElementPath = ET._SimpleElementPath() - elem = ET.XML(SAMPLE_XML) - self.assertEqual(elem.find('tag').tag, 'tag') - self.assertEqual(ET.ElementTree(elem).find('tag').tag, 'tag') - self.assertEqual(elem.findtext('tag'), 'text') - self.assertIsNone(elem.findtext('tog')) - self.assertEqual(elem.findtext('tog', 'default'), 'default') - self.assertEqual(ET.ElementTree(elem).findtext('tag'), 'text') - self.assertEqual(summarize_list(elem.findall('tag')), ['tag', 'tag']) - self.assertEqual(summarize_list(elem.findall('.//tag')), - ['tag', 'tag', 'tag']) - - #self.assertIsNone(elem.find('section/tag')) - #self.assertIsNone(elem.findtext('section/tag')) - self.assertEqual(summarize_list(elem.findall('section/tag')), []) - - ET.ElementPath = current_ElementPath - # -------------------------------------------------------------------- @@ -2328,7 +2306,6 @@ if pyET: test_classes.extend([ NoAcceleratorTest, - ElementPathFallbackTest, ]) support.run_unittest(*test_classes) diff --git a/Lib/xml/etree/ElementTree.py b/Lib/xml/etree/ElementTree.py --- a/Lib/xml/etree/ElementTree.py +++ b/Lib/xml/etree/ElementTree.py @@ -101,32 +101,8 @@ import re import warnings -class _SimpleElementPath: - # emulate pre-1.2 find/findtext/findall behaviour - def find(self, element, tag, namespaces=None): - for elem in element: - if elem.tag == tag: - return elem - return None - def findtext(self, element, tag, default=None, namespaces=None): - elem = self.find(element, tag) - if elem is None: - return default - return elem.text or "" - def iterfind(self, element, tag, namespaces=None): - if tag[:3] == ".//": - for elem in element.iter(tag[3:]): - yield elem - for elem in element: - if elem.tag == tag: - yield elem - def findall(self, element, tag, namespaces=None): - return list(self.iterfind(element, tag, namespaces)) +from . import ElementPath -try: - from . import ElementPath -except ImportError: - ElementPath = _SimpleElementPath() ## # Parser error. This is a subclass of SyntaxError. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 15 08:42:15 2012 From: python-checkins at python.org (eli.bendersky) Date: Fri, 15 Jun 2012 08:42:15 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Skip_XincludeTest_entirely_?= =?utf8?q?instead_of_just_ignoring_failures=2C_because_it_may?= Message-ID: http://hg.python.org/cpython/rev/40eeab0f7fc2 changeset: 77435:40eeab0f7fc2 user: Eli Bendersky date: Fri Jun 15 09:40:44 2012 +0300 summary: Skip XincludeTest entirely instead of just ignoring failures, because it may segfault, depending on the order of running tests files: Lib/test/test_xml_etree.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py --- a/Lib/test/test_xml_etree.py +++ b/Lib/test/test_xml_etree.py @@ -2011,8 +2011,8 @@ 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd')) + at unittest.skip('Unstable due to module monkeypatching') class XincludeTest(unittest.TestCase): - @unittest.expectedFailure def test_xinclude_default(self): from xml.etree import ElementInclude doc = xinclude_loader('default.xml') -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 15 09:15:17 2012 From: python-checkins at python.org (martin.v.loewis) Date: Fri, 15 Jun 2012 09:15:17 +0200 (CEST) Subject: [Python-checkins] r88976 - tracker/roundup-src/roundup/cgi/client.py Message-ID: <3WDCdd5YGNzNtW@mail.python.org> Author: martin.v.loewis Date: Fri Jun 15 09:15:17 2012 New Revision: 88976 Log: Fake a list property to prevent "Error: not indexable". Modified: tracker/roundup-src/roundup/cgi/client.py Modified: tracker/roundup-src/roundup/cgi/client.py ============================================================================== --- tracker/roundup-src/roundup/cgi/client.py (original) +++ tracker/roundup-src/roundup/cgi/client.py Fri Jun 15 09:15:17 2012 @@ -305,6 +305,10 @@ # see if we need to re-parse the environment for the form (eg Zope) if form is None: self.form = cgi.FieldStorage(fp=request.rfile, environ=env) + # In some case (e.g. content-type application/xml), cgi + # will not parse anything. Fake a list property in this case + if self.form.list is None: + self.form.list = [] else: self.form = form From python-checkins at python.org Fri Jun 15 13:02:16 2012 From: python-checkins at python.org (martin.v.loewis) Date: Fri, 15 Jun 2012 13:02:16 +0200 (CEST) Subject: [Python-checkins] r88977 - tracker/instances/python-dev/detectors/no_texthtml.py Message-ID: <3WDJgX3pYMzNv9@mail.python.org> Author: martin.v.loewis Date: Fri Jun 15 13:02:16 2012 New Revision: 88977 Log: Add text/x-html and html to black list. Modified: tracker/instances/python-dev/detectors/no_texthtml.py Modified: tracker/instances/python-dev/detectors/no_texthtml.py ============================================================================== --- tracker/instances/python-dev/detectors/no_texthtml.py (original) +++ tracker/instances/python-dev/detectors/no_texthtml.py Fri Jun 15 13:02:16 2012 @@ -1,8 +1,8 @@ def audit_html_files(db, cl, nodeid, newvalues): - if newvalues.has_key('type') and newvalues['type'] == 'text/html': + if newvalues.has_key('type') and newvalues['type'] in ('text/html', 'html', 'text/x-html'): newvalues['type'] = 'text/plain' - + def init(db): db.file.audit('set', audit_html_files) From python-checkins at python.org Fri Jun 15 13:02:57 2012 From: python-checkins at python.org (martin.v.loewis) Date: Fri, 15 Jun 2012 13:02:57 +0200 (CEST) Subject: [Python-checkins] r88978 - tracker/instances/jython/detectors/no_texthtml.py Message-ID: <3WDJhK74ytzNv9@mail.python.org> Author: martin.v.loewis Date: Fri Jun 15 13:02:57 2012 New Revision: 88978 Log: Add text/x-html and html to black list. Modified: tracker/instances/jython/detectors/no_texthtml.py Modified: tracker/instances/jython/detectors/no_texthtml.py ============================================================================== --- tracker/instances/jython/detectors/no_texthtml.py (original) +++ tracker/instances/jython/detectors/no_texthtml.py Fri Jun 15 13:02:57 2012 @@ -1,8 +1,8 @@ def audit_html_files(db, cl, nodeid, newvalues): - if newvalues.has_key('type') and newvalues['type'] == 'text/html': + if newvalues.has_key('type') and newvalues['type'] in ('text/html', 'html', 'text/x-html'): newvalues['type'] = 'text/plain' - + def init(db): db.file.audit('set', audit_html_files) From python-checkins at python.org Fri Jun 15 13:14:47 2012 From: python-checkins at python.org (nick.coghlan) Date: Fri, 15 Jun 2012 13:14:47 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Issue_=2315061=3A_Don=27t_o?= =?utf8?q?versell_the_capabilities_of_the_new_non-shortcircuiting?= Message-ID: http://hg.python.org/cpython/rev/f36af3766a20 changeset: 77436:f36af3766a20 user: Nick Coghlan date: Fri Jun 15 21:14:08 2012 +1000 summary: Issue #15061: Don't oversell the capabilities of the new non-shortcircuiting comparison function in hmac files: Doc/library/hmac.rst | 41 +++++++++++++++++----------- Lib/hmac.py | 26 +++++++++--------- Lib/test/test_hmac.py | 44 ++++++++++++++++-------------- Misc/NEWS | 5 +++ 4 files changed, 66 insertions(+), 50 deletions(-) diff --git a/Doc/library/hmac.rst b/Doc/library/hmac.rst --- a/Doc/library/hmac.rst +++ b/Doc/library/hmac.rst @@ -42,8 +42,8 @@ When comparing the output of :meth:`digest` to an externally-supplied digest during a verification routine, it is recommended to use the - :func:`hmac.secure_compare` function instead of the ``==`` operator - to avoid potential timing attacks. + :func:`compare_digest` function instead of the ``==`` operator + to reduce the vulnerability to timing attacks. .. method:: HMAC.hexdigest() @@ -54,10 +54,11 @@ .. warning:: - When comparing the output of :meth:`hexdigest` to an externally-supplied - digest during a verification routine, it is recommended to use the - :func:`hmac.secure_compare` function instead of the ``==`` operator - to avoid potential timing attacks. + The output of :meth:`hexdigest` should not be compared directly to an + externally-supplied digest during a verification routine. Instead, the + externally supplied digest should be converted to a :class:`bytes` + value and compared to the output of :meth:`digest` with + :func:`compare_digest`. .. method:: HMAC.copy() @@ -68,20 +69,28 @@ This module also provides the following helper function: -.. function:: secure_compare(a, b) +.. function:: compare_digest(a, b) - Returns the equivalent of ``a == b``, but using a time-independent - comparison method. Comparing the full lengths of the inputs *a* and *b*, - instead of short-circuiting the comparison upon the first unequal byte, - prevents leaking information about the inputs being compared and mitigates - potential timing attacks. The inputs must be either :class:`str` or - :class:`bytes` instances. + Returns the equivalent of ``a == b``, but avoids content based + short circuiting behaviour to reduce the vulnerability to timing + analysis. The inputs must be :class:`bytes` instances. + + Using a short circuiting comparison (that is, one that terminates as soon + as it finds any difference between the values) to check digests for + correctness can be problematic, as it introduces a potential + vulnerability when an attacker can control both the message to be checked + *and* the purported signature value. By keeping the plaintext consistent + and supplying different signature values, an attacker may be able to use + timing variations to search the signature space for the expected value in + O(n) time rather than the desired O(2**n). .. note:: - While the :func:`hmac.secure_compare` function prevents leaking the - contents of the inputs via a timing attack, it does leak the length - of the inputs. However, this generally is not a security risk. + While this function reduces the likelihood of leaking the contents of + the expected digest via a timing attack, it still uses short circuiting + behaviour based on the *length* of the inputs. It is assumed that the + expected length of the digest is not a secret, as it is typically + published as part of a file format, network protocol or API definition. .. versionadded:: 3.3 diff --git a/Lib/hmac.py b/Lib/hmac.py --- a/Lib/hmac.py +++ b/Lib/hmac.py @@ -13,24 +13,24 @@ digest_size = None -def secure_compare(a, b): - """Returns the equivalent of 'a == b', but using a time-independent - comparison method to prevent timing attacks.""" - if not ((isinstance(a, str) and isinstance(b, str)) or - (isinstance(a, bytes) and isinstance(b, bytes))): - raise TypeError("inputs must be strings or bytes") +def compare_digest(a, b): + """Returns the equivalent of 'a == b', but avoids content based short + circuiting to reduce the vulnerability to timing attacks.""" + # Consistent timing matters more here than data type flexibility + if not (isinstance(a, bytes) and isinstance(b, bytes)): + raise TypeError("inputs must be bytes instances") + # We assume the length of the expected digest is public knowledge, + # thus this early return isn't leaking anything an attacker wouldn't + # already know if len(a) != len(b): return False + # We assume that integers in the bytes range are all cached, + # thus timing shouldn't vary much due to integer object creation result = 0 - if isinstance(a, bytes): - for x, y in zip(a, b): - result |= x ^ y - else: - for x, y in zip(a, b): - result |= ord(x) ^ ord(y) - + for x, y in zip(a, b): + result |= x ^ y return result == 0 diff --git a/Lib/test/test_hmac.py b/Lib/test/test_hmac.py --- a/Lib/test/test_hmac.py +++ b/Lib/test/test_hmac.py @@ -302,40 +302,42 @@ self.assertEqual(h1.hexdigest(), h2.hexdigest(), "Hexdigest of copy doesn't match original hexdigest.") -class SecureCompareTestCase(unittest.TestCase): +class CompareDigestTestCase(unittest.TestCase): def test_compare(self): # Testing input type exception handling a, b = 100, 200 - self.assertRaises(TypeError, hmac.secure_compare, a, b) - a, b = 100, "foobar" - self.assertRaises(TypeError, hmac.secure_compare, a, b) + self.assertRaises(TypeError, hmac.compare_digest, a, b) + a, b = 100, b"foobar" + self.assertRaises(TypeError, hmac.compare_digest, a, b) + a, b = b"foobar", 200 + self.assertRaises(TypeError, hmac.compare_digest, a, b) a, b = "foobar", b"foobar" - self.assertRaises(TypeError, hmac.secure_compare, a, b) + self.assertRaises(TypeError, hmac.compare_digest, a, b) + a, b = b"foobar", "foobar" + self.assertRaises(TypeError, hmac.compare_digest, a, b) + a, b = "foobar", "foobar" + self.assertRaises(TypeError, hmac.compare_digest, a, b) + a, b = bytearray(b"foobar"), bytearray(b"foobar") + self.assertRaises(TypeError, hmac.compare_digest, a, b) - # Testing str/bytes of different lengths - a, b = "foobar", "foo" - self.assertFalse(hmac.secure_compare(a, b)) + # Testing bytes of different lengths a, b = b"foobar", b"foo" - self.assertFalse(hmac.secure_compare(a, b)) + self.assertFalse(hmac.compare_digest(a, b)) a, b = b"\xde\xad\xbe\xef", b"\xde\xad" - self.assertFalse(hmac.secure_compare(a, b)) + self.assertFalse(hmac.compare_digest(a, b)) - # Testing str/bytes of same lengths, different values - a, b = "foobar", "foobaz" - self.assertFalse(hmac.secure_compare(a, b)) + # Testing bytes of same lengths, different values a, b = b"foobar", b"foobaz" - self.assertFalse(hmac.secure_compare(a, b)) + self.assertFalse(hmac.compare_digest(a, b)) a, b = b"\xde\xad\xbe\xef", b"\xab\xad\x1d\xea" - self.assertFalse(hmac.secure_compare(a, b)) + self.assertFalse(hmac.compare_digest(a, b)) - # Testing str/bytes of same lengths, same values - a, b = "foobar", "foobar" - self.assertTrue(hmac.secure_compare(a, b)) + # Testing bytes of same lengths, same values a, b = b"foobar", b"foobar" - self.assertTrue(hmac.secure_compare(a, b)) + self.assertTrue(hmac.compare_digest(a, b)) a, b = b"\xde\xad\xbe\xef", b"\xde\xad\xbe\xef" - self.assertTrue(hmac.secure_compare(a, b)) + self.assertTrue(hmac.compare_digest(a, b)) def test_main(): support.run_unittest( @@ -343,7 +345,7 @@ ConstructorTestCase, SanityTestCase, CopyTestCase, - SecureCompareTestCase + CompareDigestTestCase ) if __name__ == "__main__": diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -21,6 +21,11 @@ Library ------- +- Issue #15061: The inappropriately named hmac.secure_compare has been + renamed to hash.compare_digest, restricted to operating on bytes inputs + only and had its documentation updated to more acurrately reflect both its + intent and its limitations + - Issue #13841: Make child processes exit using sys.exit() on Windows. - Issue #14936: curses_panel was converted to PEP 3121 and PEP 384 API. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 15 13:16:37 2012 From: python-checkins at python.org (nick.coghlan) Date: Fri, 15 Jun 2012 13:16:37 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Fix_typos_in_NEWS_entry?= Message-ID: http://hg.python.org/cpython/rev/593418586945 changeset: 77437:593418586945 user: Nick Coghlan date: Fri Jun 15 21:16:25 2012 +1000 summary: Fix typos in NEWS entry files: Misc/NEWS | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -22,8 +22,8 @@ ------- - Issue #15061: The inappropriately named hmac.secure_compare has been - renamed to hash.compare_digest, restricted to operating on bytes inputs - only and had its documentation updated to more acurrately reflect both its + renamed to hmac.compare_digest, restricted to operating on bytes inputs + only and had its documentation updated to more accurately reflect both its intent and its limitations - Issue #13841: Make child processes exit using sys.exit() on Windows. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 15 15:22:33 2012 From: python-checkins at python.org (martin.v.loewis) Date: Fri, 15 Jun 2012 15:22:33 +0200 (CEST) Subject: [Python-checkins] r88979 - tracker/instances/python-dev/extensions/openid_login.py Message-ID: <3WDMnP0pg3zNyM@mail.python.org> Author: martin.v.loewis Date: Fri Jun 15 15:22:32 2012 New Revision: 88979 Log: Catch discovery errors. Modified: tracker/instances/python-dev/extensions/openid_login.py Modified: tracker/instances/python-dev/extensions/openid_login.py ============================================================================== --- tracker/instances/python-dev/extensions/openid_login.py (original) +++ tracker/instances/python-dev/extensions/openid_login.py Fri Jun 15 15:22:32 2012 @@ -160,7 +160,10 @@ # results. However, the risk of login breaking if a provider does change # its service URL outweighs the cost of another HTTP request to perform # the discovery during login. - result = openid2rp.discover(provider_id) + try: + result = openid2rp.discover(provider_id) + except Exception: + result = None if result is None: self.client.error_message.append('Provider %s appears to be down' % providers[provider][0]) return From python-checkins at python.org Fri Jun 15 18:36:55 2012 From: python-checkins at python.org (antoine.pitrou) Date: Fri, 15 Jun 2012 18:36:55 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=282=2E7=29=3A_Skip_test=5Fbig?= =?utf8?q?mem=2Etest=5Funicode=5Frepr=5Foflw=2C_since_it_crashes_=28issue_?= =?utf8?b?IzE0OTA0KS4=?= Message-ID: http://hg.python.org/cpython/rev/1c9635109079 changeset: 77438:1c9635109079 branch: 2.7 parent: 77420:412c7daed0db user: Antoine Pitrou date: Fri Jun 15 18:33:48 2012 +0200 summary: Skip test_bigmem.test_unicode_repr_oflw, since it crashes (issue #14904). files: Lib/test/test_bigmem.py | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/Lib/test/test_bigmem.py b/Lib/test/test_bigmem.py --- a/Lib/test/test_bigmem.py +++ b/Lib/test/test_bigmem.py @@ -124,6 +124,7 @@ @precisionbigmemtest(size=_4G // 5, memuse=6+2) def test_unicode_repr_oflw(self, size): + self.skipTest("test crashes - see issue #14904") try: s = u"\uAAAA"*size r = repr(s) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 15 19:15:57 2012 From: python-checkins at python.org (antoine.pitrou) Date: Fri, 15 Jun 2012 19:15:57 +0200 Subject: [Python-checkins] =?utf8?b?Y3B5dGhvbiAoMy4yKTogSXNzdWUgIzE0OTMz?= =?utf8?q?=3A_fix_misleading_doc_about_weakref_support_in_extension_types?= =?utf8?q?=2E?= Message-ID: http://hg.python.org/cpython/rev/69177ff1a643 changeset: 77439:69177ff1a643 branch: 3.2 parent: 77424:62030ebb2b01 user: Antoine Pitrou date: Fri Jun 15 19:11:31 2012 +0200 summary: Issue #14933: fix misleading doc about weakref support in extension types. files: Doc/extending/newtypes.rst | 5 ++--- 1 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Doc/extending/newtypes.rst b/Doc/extending/newtypes.rst --- a/Doc/extending/newtypes.rst +++ b/Doc/extending/newtypes.rst @@ -1459,9 +1459,8 @@ } The only further addition is that the destructor needs to call the weak -reference manager to clear any weak references. This should be done before any -other parts of the destruction have occurred, but is only required if the weak -reference list is non-*NULL*:: +reference manager to clear any weak references. This is only required if the +weak reference list is non-*NULL*:: static void instance_dealloc(PyInstanceObject *inst) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 15 19:15:58 2012 From: python-checkins at python.org (antoine.pitrou) Date: Fri, 15 Jun 2012 19:15:58 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=28merge_3=2E2_-=3E_default=29?= =?utf8?q?=3A_Issue_=2314933=3A_fix_misleading_doc_about_weakref_support_i?= =?utf8?q?n_extension_types=2E?= Message-ID: http://hg.python.org/cpython/rev/b17c8005e08a changeset: 77440:b17c8005e08a parent: 77437:593418586945 parent: 77439:69177ff1a643 user: Antoine Pitrou date: Fri Jun 15 19:12:04 2012 +0200 summary: Issue #14933: fix misleading doc about weakref support in extension types. files: Doc/extending/newtypes.rst | 5 ++--- 1 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Doc/extending/newtypes.rst b/Doc/extending/newtypes.rst --- a/Doc/extending/newtypes.rst +++ b/Doc/extending/newtypes.rst @@ -1437,9 +1437,8 @@ } The only further addition is that the destructor needs to call the weak -reference manager to clear any weak references. This should be done before any -other parts of the destruction have occurred, but is only required if the weak -reference list is non-*NULL*:: +reference manager to clear any weak references. This is only required if the +weak reference list is non-*NULL*:: static void instance_dealloc(PyInstanceObject *inst) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 15 19:15:59 2012 From: python-checkins at python.org (antoine.pitrou) Date: Fri, 15 Jun 2012 19:15:59 +0200 Subject: [Python-checkins] =?utf8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzE0OTMz?= =?utf8?q?=3A_fix_misleading_doc_about_weakref_support_in_extension_types?= =?utf8?q?=2E?= Message-ID: http://hg.python.org/cpython/rev/0ac1f90954dc changeset: 77441:0ac1f90954dc branch: 2.7 parent: 77438:1c9635109079 user: Antoine Pitrou date: Fri Jun 15 19:11:31 2012 +0200 summary: Issue #14933: fix misleading doc about weakref support in extension types. files: Doc/extending/newtypes.rst | 5 ++--- 1 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Doc/extending/newtypes.rst b/Doc/extending/newtypes.rst --- a/Doc/extending/newtypes.rst +++ b/Doc/extending/newtypes.rst @@ -1521,9 +1521,8 @@ } The only further addition is that the destructor needs to call the weak -reference manager to clear any weak references. This should be done before any -other parts of the destruction have occurred, but is only required if the weak -reference list is non-*NULL*:: +reference manager to clear any weak references. This is only required if the +weak reference list is non-*NULL*:: static void instance_dealloc(PyInstanceObject *inst) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 15 19:33:01 2012 From: python-checkins at python.org (richard.oudkerk) Date: Fri, 15 Jun 2012 19:33:01 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Issue_=2314059=3A_Implement?= =?utf8?q?_multiprocessing=2EBarrier?= Message-ID: http://hg.python.org/cpython/rev/2d2f206d040e changeset: 77442:2d2f206d040e parent: 77440:b17c8005e08a user: Richard Oudkerk date: Fri Jun 15 18:26:07 2012 +0100 summary: Issue #14059: Implement multiprocessing.Barrier files: Doc/library/multiprocessing.rst | 23 +- Lib/multiprocessing/__init__.py | 11 +- Lib/multiprocessing/dummy/__init__.py | 4 +- Lib/multiprocessing/managers.py | 21 + Lib/multiprocessing/synchronize.py | 40 + Lib/test/test_multiprocessing.py | 337 +++++++++++++- Misc/NEWS | 2 + 7 files changed, 426 insertions(+), 12 deletions(-) diff --git a/Doc/library/multiprocessing.rst b/Doc/library/multiprocessing.rst --- a/Doc/library/multiprocessing.rst +++ b/Doc/library/multiprocessing.rst @@ -226,11 +226,11 @@ holds Python objects and allows other processes to manipulate them using proxies. - A manager returned by :func:`Manager` will support types :class:`list`, - :class:`dict`, :class:`Namespace`, :class:`Lock`, :class:`RLock`, - :class:`Semaphore`, :class:`BoundedSemaphore`, :class:`Condition`, - :class:`Event`, :class:`Queue`, :class:`Value` and :class:`Array`. For - example, :: + A manager returned by :func:`Manager` will support types + :class:`list`, :class:`dict`, :class:`Namespace`, :class:`Lock`, + :class:`RLock`, :class:`Semaphore`, :class:`BoundedSemaphore`, + :class:`Condition`, :class:`Event`, :class:`Barrier`, + :class:`Queue`, :class:`Value` and :class:`Array`. For example, :: from multiprocessing import Process, Manager @@ -885,6 +885,12 @@ Note that one can also create synchronization primitives by using a manager object -- see :ref:`multiprocessing-managers`. +.. class:: Barrier(parties[, action[, timeout]]) + + A barrier object: a clone of :class:`threading.Barrier`. + + .. versionadded:: 3.3 + .. class:: BoundedSemaphore([value]) A bounded semaphore object: a clone of :class:`threading.BoundedSemaphore`. @@ -1280,6 +1286,13 @@ It also supports creation of shared lists and dictionaries. + .. method:: Barrier(parties[, action[, timeout]]) + + Create a shared :class:`threading.Barrier` object and return a + proxy for it. + + .. versionadded:: 3.3 + .. method:: BoundedSemaphore([value]) Create a shared :class:`threading.BoundedSemaphore` object and return a diff --git a/Lib/multiprocessing/__init__.py b/Lib/multiprocessing/__init__.py --- a/Lib/multiprocessing/__init__.py +++ b/Lib/multiprocessing/__init__.py @@ -23,8 +23,8 @@ 'Manager', 'Pipe', 'cpu_count', 'log_to_stderr', 'get_logger', 'allow_connection_pickling', 'BufferTooShort', 'TimeoutError', 'Lock', 'RLock', 'Semaphore', 'BoundedSemaphore', 'Condition', - 'Event', 'Queue', 'SimpleQueue', 'JoinableQueue', 'Pool', 'Value', 'Array', - 'RawValue', 'RawArray', 'SUBDEBUG', 'SUBWARNING', + 'Event', 'Barrier', 'Queue', 'SimpleQueue', 'JoinableQueue', 'Pool', + 'Value', 'Array', 'RawValue', 'RawArray', 'SUBDEBUG', 'SUBWARNING', ] __author__ = 'R. Oudkerk (r.m.oudkerk at gmail.com)' @@ -186,6 +186,13 @@ from multiprocessing.synchronize import Event return Event() +def Barrier(parties, action=None, timeout=None): + ''' + Returns a barrier object + ''' + from multiprocessing.synchronize import Barrier + return Barrier(parties, action, timeout) + def Queue(maxsize=0): ''' Returns a queue object diff --git a/Lib/multiprocessing/dummy/__init__.py b/Lib/multiprocessing/dummy/__init__.py --- a/Lib/multiprocessing/dummy/__init__.py +++ b/Lib/multiprocessing/dummy/__init__.py @@ -35,7 +35,7 @@ __all__ = [ 'Process', 'current_process', 'active_children', 'freeze_support', 'Lock', 'RLock', 'Semaphore', 'BoundedSemaphore', 'Condition', - 'Event', 'Queue', 'Manager', 'Pipe', 'Pool', 'JoinableQueue' + 'Event', 'Barrier', 'Queue', 'Manager', 'Pipe', 'Pool', 'JoinableQueue' ] # @@ -49,7 +49,7 @@ from multiprocessing.dummy.connection import Pipe from threading import Lock, RLock, Semaphore, BoundedSemaphore -from threading import Event, Condition +from threading import Event, Condition, Barrier from queue import Queue # diff --git a/Lib/multiprocessing/managers.py b/Lib/multiprocessing/managers.py --- a/Lib/multiprocessing/managers.py +++ b/Lib/multiprocessing/managers.py @@ -993,6 +993,26 @@ def wait(self, timeout=None): return self._callmethod('wait', (timeout,)) + +class BarrierProxy(BaseProxy): + _exposed_ = ('__getattribute__', 'wait', 'abort', 'reset') + def wait(self, timeout=None): + return self._callmethod('wait', (timeout,)) + def abort(self): + return self._callmethod('abort') + def reset(self): + return self._callmethod('reset') + @property + def parties(self): + return self._callmethod('__getattribute__', ('parties',)) + @property + def n_waiting(self): + return self._callmethod('__getattribute__', ('n_waiting',)) + @property + def broken(self): + return self._callmethod('__getattribute__', ('broken',)) + + class NamespaceProxy(BaseProxy): _exposed_ = ('__getattribute__', '__setattr__', '__delattr__') def __getattr__(self, key): @@ -1084,6 +1104,7 @@ SyncManager.register('BoundedSemaphore', threading.BoundedSemaphore, AcquirerProxy) SyncManager.register('Condition', threading.Condition, ConditionProxy) +SyncManager.register('Barrier', threading.Barrier, BarrierProxy) SyncManager.register('Pool', Pool, PoolProxy) SyncManager.register('list', list, ListProxy) SyncManager.register('dict', dict, DictProxy) diff --git a/Lib/multiprocessing/synchronize.py b/Lib/multiprocessing/synchronize.py --- a/Lib/multiprocessing/synchronize.py +++ b/Lib/multiprocessing/synchronize.py @@ -333,3 +333,43 @@ return False finally: self._cond.release() + +# +# Barrier +# + +class Barrier(threading.Barrier): + + def __init__(self, parties, action=None, timeout=None): + import struct + from multiprocessing.heap import BufferWrapper + wrapper = BufferWrapper(struct.calcsize('i') * 2) + cond = Condition() + self.__setstate__((parties, action, timeout, cond, wrapper)) + self._state = 0 + self._count = 0 + + def __setstate__(self, state): + (self._parties, self._action, self._timeout, + self._cond, self._wrapper) = state + self._array = self._wrapper.create_memoryview().cast('i') + + def __getstate__(self): + return (self._parties, self._action, self._timeout, + self._cond, self._wrapper) + + @property + def _state(self): + return self._array[0] + + @_state.setter + def _state(self, value): + self._array[0] = value + + @property + def _count(self): + return self._array[1] + + @_count.setter + def _count(self, value): + self._array[1] = value diff --git a/Lib/test/test_multiprocessing.py b/Lib/test/test_multiprocessing.py --- a/Lib/test/test_multiprocessing.py +++ b/Lib/test/test_multiprocessing.py @@ -18,6 +18,7 @@ import socket import random import logging +import struct import test.support @@ -1057,6 +1058,336 @@ self.assertEqual(wait(), True) # +# Tests for Barrier - adapted from tests in test/lock_tests.py +# + +# Many of the tests for threading.Barrier use a list as an atomic +# counter: a value is appended to increment the counter, and the +# length of the list gives the value. We use the class DummyList +# for the same purpose. + +class _DummyList(object): + + def __init__(self): + wrapper = multiprocessing.heap.BufferWrapper(struct.calcsize('i')) + lock = multiprocessing.Lock() + self.__setstate__((wrapper, lock)) + self._lengthbuf[0] = 0 + + def __setstate__(self, state): + (self._wrapper, self._lock) = state + self._lengthbuf = self._wrapper.create_memoryview().cast('i') + + def __getstate__(self): + return (self._wrapper, self._lock) + + def append(self, _): + with self._lock: + self._lengthbuf[0] += 1 + + def __len__(self): + with self._lock: + return self._lengthbuf[0] + +def _wait(): + # A crude wait/yield function not relying on synchronization primitives. + time.sleep(0.01) + + +class Bunch(object): + """ + A bunch of threads. + """ + def __init__(self, namespace, f, args, n, wait_before_exit=False): + """ + Construct a bunch of `n` threads running the same function `f`. + If `wait_before_exit` is True, the threads won't terminate until + do_finish() is called. + """ + self.f = f + self.args = args + self.n = n + self.started = namespace.DummyList() + self.finished = namespace.DummyList() + self._can_exit = namespace.Value('i', not wait_before_exit) + for i in range(n): + namespace.Process(target=self.task).start() + + def task(self): + pid = os.getpid() + self.started.append(pid) + try: + self.f(*self.args) + finally: + self.finished.append(pid) + while not self._can_exit.value: + _wait() + + def wait_for_started(self): + while len(self.started) < self.n: + _wait() + + def wait_for_finished(self): + while len(self.finished) < self.n: + _wait() + + def do_finish(self): + self._can_exit.value = True + + +class AppendTrue(object): + def __init__(self, obj): + self.obj = obj + def __call__(self): + self.obj.append(True) + + +class _TestBarrier(BaseTestCase): + """ + Tests for Barrier objects. + """ + N = 5 + defaultTimeout = 10.0 # XXX Slow Windows buildbots need generous timeout + + def setUp(self): + self.barrier = self.Barrier(self.N, timeout=self.defaultTimeout) + + def tearDown(self): + self.barrier.abort() + self.barrier = None + + def DummyList(self): + if self.TYPE == 'threads': + return [] + elif self.TYPE == 'manager': + return self.manager.list() + else: + return _DummyList() + + def run_threads(self, f, args): + b = Bunch(self, f, args, self.N-1) + f(*args) + b.wait_for_finished() + + @classmethod + def multipass(cls, barrier, results, n): + m = barrier.parties + assert m == cls.N + for i in range(n): + results[0].append(True) + assert len(results[1]) == i * m + barrier.wait() + results[1].append(True) + assert len(results[0]) == (i + 1) * m + barrier.wait() + try: + assert barrier.n_waiting == 0 + except NotImplementedError: + pass + assert not barrier.broken + + def test_barrier(self, passes=1): + """ + Test that a barrier is passed in lockstep + """ + results = [self.DummyList(), self.DummyList()] + self.run_threads(self.multipass, (self.barrier, results, passes)) + + def test_barrier_10(self): + """ + Test that a barrier works for 10 consecutive runs + """ + return self.test_barrier(10) + + @classmethod + def _test_wait_return_f(cls, barrier, queue): + res = barrier.wait() + queue.put(res) + + def test_wait_return(self): + """ + test the return value from barrier.wait + """ + queue = self.Queue() + self.run_threads(self._test_wait_return_f, (self.barrier, queue)) + results = [queue.get() for i in range(self.N)] + self.assertEqual(results.count(0), 1) + + @classmethod + def _test_action_f(cls, barrier, results): + barrier.wait() + if len(results) != 1: + raise RuntimeError + + def test_action(self): + """ + Test the 'action' callback + """ + results = self.DummyList() + barrier = self.Barrier(self.N, action=AppendTrue(results)) + self.run_threads(self._test_action_f, (barrier, results)) + self.assertEqual(len(results), 1) + + @classmethod + def _test_abort_f(cls, barrier, results1, results2): + try: + i = barrier.wait() + if i == cls.N//2: + raise RuntimeError + barrier.wait() + results1.append(True) + except threading.BrokenBarrierError: + results2.append(True) + except RuntimeError: + barrier.abort() + + def test_abort(self): + """ + Test that an abort will put the barrier in a broken state + """ + results1 = self.DummyList() + results2 = self.DummyList() + self.run_threads(self._test_abort_f, + (self.barrier, results1, results2)) + self.assertEqual(len(results1), 0) + self.assertEqual(len(results2), self.N-1) + self.assertTrue(self.barrier.broken) + + @classmethod + def _test_reset_f(cls, barrier, results1, results2, results3): + i = barrier.wait() + if i == cls.N//2: + # Wait until the other threads are all in the barrier. + while barrier.n_waiting < cls.N-1: + time.sleep(0.001) + barrier.reset() + else: + try: + barrier.wait() + results1.append(True) + except threading.BrokenBarrierError: + results2.append(True) + # Now, pass the barrier again + barrier.wait() + results3.append(True) + + def test_reset(self): + """ + Test that a 'reset' on a barrier frees the waiting threads + """ + results1 = self.DummyList() + results2 = self.DummyList() + results3 = self.DummyList() + self.run_threads(self._test_reset_f, + (self.barrier, results1, results2, results3)) + self.assertEqual(len(results1), 0) + self.assertEqual(len(results2), self.N-1) + self.assertEqual(len(results3), self.N) + + @classmethod + def _test_abort_and_reset_f(cls, barrier, barrier2, + results1, results2, results3): + try: + i = barrier.wait() + if i == cls.N//2: + raise RuntimeError + barrier.wait() + results1.append(True) + except threading.BrokenBarrierError: + results2.append(True) + except RuntimeError: + barrier.abort() + # Synchronize and reset the barrier. Must synchronize first so + # that everyone has left it when we reset, and after so that no + # one enters it before the reset. + if barrier2.wait() == cls.N//2: + barrier.reset() + barrier2.wait() + barrier.wait() + results3.append(True) + + def test_abort_and_reset(self): + """ + Test that a barrier can be reset after being broken. + """ + results1 = self.DummyList() + results2 = self.DummyList() + results3 = self.DummyList() + barrier2 = self.Barrier(self.N) + + self.run_threads(self._test_abort_and_reset_f, + (self.barrier, barrier2, results1, results2, results3)) + self.assertEqual(len(results1), 0) + self.assertEqual(len(results2), self.N-1) + self.assertEqual(len(results3), self.N) + + @classmethod + def _test_timeout_f(cls, barrier, results): + i = barrier.wait(20) + if i == cls.N//2: + # One thread is late! + time.sleep(4.0) + try: + barrier.wait(0.5) + except threading.BrokenBarrierError: + results.append(True) + + def test_timeout(self): + """ + Test wait(timeout) + """ + results = self.DummyList() + self.run_threads(self._test_timeout_f, (self.barrier, results)) + self.assertEqual(len(results), self.barrier.parties) + + @classmethod + def _test_default_timeout_f(cls, barrier, results): + i = barrier.wait(20) + if i == cls.N//2: + # One thread is later than the default timeout + time.sleep(4.0) + try: + barrier.wait() + except threading.BrokenBarrierError: + results.append(True) + + def test_default_timeout(self): + """ + Test the barrier's default timeout + """ + barrier = self.Barrier(self.N, timeout=1.0) + results = self.DummyList() + self.run_threads(self._test_default_timeout_f, (barrier, results)) + self.assertEqual(len(results), barrier.parties) + + def test_single_thread(self): + b = self.Barrier(1) + b.wait() + b.wait() + + @classmethod + def _test_thousand_f(cls, barrier, passes, conn, lock): + for i in range(passes): + barrier.wait() + with lock: + conn.send(i) + + def test_thousand(self): + if self.TYPE == 'manager': + return + passes = 1000 + lock = self.Lock() + conn, child_conn = self.Pipe(False) + for j in range(self.N): + p = self.Process(target=self._test_thousand_f, + args=(self.barrier, passes, child_conn, lock)) + p.start() + + for i in range(passes): + for j in range(self.N): + self.assertEqual(conn.recv(), i) + +# # # @@ -2532,7 +2863,7 @@ Process = multiprocessing.Process locals().update(get_attributes(multiprocessing, ( 'Queue', 'Lock', 'RLock', 'Semaphore', 'BoundedSemaphore', - 'Condition', 'Event', 'Value', 'Array', 'RawValue', + 'Condition', 'Event', 'Barrier', 'Value', 'Array', 'RawValue', 'RawArray', 'current_process', 'active_children', 'Pipe', 'connection', 'JoinableQueue', 'Pool' ))) @@ -2547,7 +2878,7 @@ manager = object.__new__(multiprocessing.managers.SyncManager) locals().update(get_attributes(manager, ( 'Queue', 'Lock', 'RLock', 'Semaphore', 'BoundedSemaphore', - 'Condition', 'Event', 'Value', 'Array', 'list', 'dict', + 'Condition', 'Event', 'Barrier', 'Value', 'Array', 'list', 'dict', 'Namespace', 'JoinableQueue', 'Pool' ))) @@ -2560,7 +2891,7 @@ Process = multiprocessing.dummy.Process locals().update(get_attributes(multiprocessing.dummy, ( 'Queue', 'Lock', 'RLock', 'Semaphore', 'BoundedSemaphore', - 'Condition', 'Event', 'Value', 'Array', 'current_process', + 'Condition', 'Event', 'Barrier', 'Value', 'Array', 'current_process', 'active_children', 'Pipe', 'connection', 'dict', 'list', 'Namespace', 'JoinableQueue', 'Pool' ))) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -21,6 +21,8 @@ Library ------- +- Issue #14059: Implement multiprocessing.Barrier. + - Issue #15061: The inappropriately named hmac.secure_compare has been renamed to hmac.compare_digest, restricted to operating on bytes inputs only and had its documentation updated to more accurately reflect both its -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 15 19:56:27 2012 From: python-checkins at python.org (brett.cannon) Date: Fri, 15 Jun 2012 19:56:27 +0200 Subject: [Python-checkins] =?utf8?q?peps=3A_Update_from_Yury=2E?= Message-ID: http://hg.python.org/peps/rev/c1f693b39292 changeset: 4461:c1f693b39292 user: Brett Cannon date: Fri Jun 15 13:56:20 2012 -0400 summary: Update from Yury. files: pep-0362.txt | 164 +++++++++++++++++++++++++++++--------- 1 files changed, 126 insertions(+), 38 deletions(-) diff --git a/pep-0362.txt b/pep-0362.txt --- a/pep-0362.txt +++ b/pep-0362.txt @@ -51,12 +51,13 @@ as listed in ``code.co_varnames``). * bind(\*args, \*\*kwargs) -> BoundArguments Creates a mapping from positional and keyword arguments to - parameters. Raises a ``BindError`` (subclass of ``TypeError``) - if the passed arguments do not match the signature. + parameters. Raises a ``TypeError`` if the passed arguments do + not match the signature. * bind_partial(\*args, \*\*kwargs) -> BoundArguments Works the same way as ``bind()``, but allows the omission of some required arguments (mimics ``functools.partial`` - behavior.) + behavior.) Raises a ``TypeError`` if the passed arguments do + not match the signature. * format(...) -> str Formats the Signature object to a string. Optional arguments allow for custom render functions for parameter names, @@ -84,27 +85,53 @@ * name : str The name of the parameter as a string. + * default : object The default value for the parameter, if specified. If the parameter has no default value, this attribute is not set. + * annotation : object The annotation for the parameter if specified. If the parameter has no annotation, this attribute is not set. -* is_keyword_only : bool - True if the parameter is keyword-only, else False. -* is_args : bool - True if the parameter accepts variable number of arguments - (``*args``-like), else False. -* is_kwargs : bool - True if the parameter accepts variable number of keyword - arguments (``**kwargs``-like), else False. -* is_implemented : bool + +* kind : str + Describes how argument values are bound to the parameter. + Possible values: + + * ``Parameter.POSITIONAL_ONLY`` - value must be supplied + as a positional argument. + + Python has no explicit syntax for defining positional-only + parameters, but many builtin and extension module functions + (especially those that accept only one or two parameters) + accept them. + + * ``Parameter.POSITIONAL_OR_KEYWORD`` - value may be + supplied as either a keyword or positional argument + (this is the standard binding behaviour for functions + implemented in Python.) + + * ``Parameter.KEYWORD_ONLY`` - value must be supplied + as a keyword argument. Keyword only parameters are those + which appear after a "*" or "\*args" entry in a Python + function definition. + + * ``Parameter.VAR_POSITIONAL`` - a tuple of positional + arguments that aren't bound to any other parameter. + This corresponds to a "\*args" parameter in a Python + function definition. + + * ``Parameter.VAR_KEYWORD`` - a dict of keyword arguments + that aren't bound to any other parameter. This corresponds + to a "\*\*kwds" parameter in a Python function definition. + +* implemented : bool True if the parameter is implemented for use. Some platforms implement functions but can't support specific parameters (e.g. "mode" for ``os.mkdir``). Passing in an unimplemented parameter may result in the parameter being ignored, or in NotImplementedError being raised. It is intended that - all conditions where ``is_implemented`` may be False be + all conditions where ``implemented`` may be False be thoroughly documented. Two parameters are equal when all their attributes are equal. @@ -159,12 +186,11 @@ - If it is ``None`` and the object is an instance of ``BuiltinFunction``, raise a ``ValueError`` - - If the object is a an instance of ``FunctionType``: + - If it has a ``__wrapped__`` attribute, return + ``signature(object.__wrapped__)`` - - If it has a ``__wrapped__`` attribute, return - ``signature(object.__wrapped__)`` - - - Or else construct a new ``Signature`` object and return it + - If the object is a an instance of ``FunctionType`` construct + and return a new ``Signature`` for it - If the object is a method or a classmethod, construct and return a new ``Signature`` object, with its first parameter (usually @@ -223,6 +249,9 @@ Visualizing Callable Objects' Signature --------------------------------------- + +Let's define some classes and functions: + :: from inspect import signature @@ -245,25 +274,62 @@ return a, b, c - print('FooMeta >', str(signature(FooMeta))) - print('Foo >', str(signature(Foo))) - print('Foo.__call__ >', str(signature(Foo.__call__))) - print('Foo().__call__ >', str(signature(Foo().__call__))) - print('partial(Foo().__call__, 1, c=3) >', - str(signature(partial(Foo().__call__, 1, c=3)))) - print('partial(partial(Foo().__call__, 1, c=3), 2, c=20) >', - str(signature(partial(partial(Foo().__call__, 1, c=3), 2, c=20)))) + def shared_vars(*shared_args): + """Decorator factory that defines shared variables that are + passed to every invocation of the function""" + def decorator(f): + @wraps(f) + def wrapper(*args, **kwds): + full_args = shared_args + args + return f(*full_args, **kwds) + # Override signature + sig = wrapper.__signature__ = signature(f) + for __ in shared_args: + sig.parameters.popitem(last=False) + return wrapper + return decorator -The script will output: + + @shared_vars({}) + def example(_state, a, b, c): + return _state, a, b, c + + + def format_signature(obj): + return str(signature(obj)) + + +Now, in the python REPL: + :: - FooMeta > (name, bases, dct, *, bar:bool=False) - Foo > (spam:int=42) - Foo.__call__ > (self, a, b, *, c) -> tuple - Foo().__call__ > (a, b, *, c) -> tuple - partial(Foo().__call__, 1, c=3) > (b, *, c=3) -> tuple - partial(partial(Foo().__call__, 1, c=3), 2, c=20) > (*, c=20) -> tuple + >>> format_signature(FooMeta) + '(name, bases, dct, *, bar:bool=False)' + + >>> format_signature(Foo) + '(spam:int=42)' + + >>> format_signature(Foo.__call__) + '(self, a, b, *, c) -> tuple' + + >>> format_signature(Foo().__call__) + '(a, b, *, c) -> tuple' + + >>> format_signature(partial(Foo().__call__, 1, c=3)) + '(b, *, c=3) -> tuple' + + >>> format_signature(partial(partial(Foo().__call__, 1, c=3), 2, c=20)) + '(*, c=20) -> tuple' + + >>> format_signature(example) + '(a, b, c)' + + >>> format_signature(partial(example, 1, 2)) + '(c)' + + >>> format_signature(partial(partial(example, 1, b=2), c=3)) + '(b=2, c=3)' Annotation Checker @@ -317,14 +383,14 @@ else: if not isinstance(default, type_): raise ValueError("{func}: wrong type of a default value for {arg!r}". \ - format(func=sig.qualname, arg=param.name)) + format(func=func.__qualname__, arg=param.name)) def check_type(sig, arg_name, arg_type, arg_value): # Internal function that encapsulates arguments type checking if not isinstance(arg_value, arg_type): raise ValueError("{func}: wrong type of {arg!r} argument, " \ "{exp!r} expected, got {got!r}". \ - format(func=sig.qualname, arg=arg_name, + format(func=func.__qualname__, arg=arg_name, exp=arg_type.__name__, got=type(arg_value).__name__)) @functools.wraps(func) @@ -341,12 +407,12 @@ # OK, we have a type for the argument, lets get the corresponding # parameter description from the signature object param = sig.parameters[arg_name] - if param.is_args: + if param.kind == param.VAR_POSITIONAL: # If this parameter is a variable-argument parameter, # then we need to check each of its values for value in arg: check_type(sig, arg_name, type_, value) - elif param.is_kwargs: + elif param.kind == param.VAR_KEYWORD: # If this parameter is a variable-keyword-argument parameter: for subname, value in arg.items(): check_type(sig, arg_name + ':' + subname, type_, value) @@ -364,13 +430,35 @@ else: if isinstance(return_type, type) and not isinstance(result, return_type): raise ValueError('{func}: wrong return type, {exp} expected, got {got}'. \ - format(func=sig.qualname, exp=return_type.__name__, + format(func=func.__qualname__, exp=return_type.__name__, got=type(result).__name__)) return result return wrapper +Render Function Signature to HTML +--------------------------------- + +:: + + import inspect + + def format_to_html(func): + sig = inspect.signature(func) + + html = sig.format(token_params_separator=',', + token_colon=':', + token_eq='=', + token_return_annotation='->', + token_left_paren='(', + token_right_paren=')', + token_kwonly_separator='*', + format_name=lambda name: ''+name+'') + + return '{}'.format(html) + + References ========== -- Repository URL: http://hg.python.org/peps From python-checkins at python.org Fri Jun 15 20:04:00 2012 From: python-checkins at python.org (petri.lehtinen) Date: Fri, 15 Jun 2012 20:04:00 +0200 Subject: [Python-checkins] =?utf8?b?Y3B5dGhvbiAoMi43KTogIzE1MDM2OiBNYWtl?= =?utf8?q?_a_repeated_changes_and_flushes_work_with_single-file_mailboxes?= Message-ID: http://hg.python.org/cpython/rev/0add70dd3c43 changeset: 77443:0add70dd3c43 branch: 2.7 parent: 77441:0ac1f90954dc user: Petri Lehtinen date: Fri Jun 15 20:50:51 2012 +0300 summary: #15036: Make a repeated changes and flushes work with single-file mailboxes files: Lib/mailbox.py | 1 + Lib/test/test_mailbox.py | 11 +++++++++++ Misc/NEWS | 4 ++++ 3 files changed, 16 insertions(+), 0 deletions(-) diff --git a/Lib/mailbox.py b/Lib/mailbox.py --- a/Lib/mailbox.py +++ b/Lib/mailbox.py @@ -649,6 +649,7 @@ new_file.write(buffer) new_toc[key] = (new_start, new_file.tell()) self._post_message_hook(new_file) + self._file_length = new_file.tell() except: new_file.close() os.remove(new_file.name) diff --git a/Lib/test/test_mailbox.py b/Lib/test/test_mailbox.py --- a/Lib/test/test_mailbox.py +++ b/Lib/test/test_mailbox.py @@ -386,6 +386,17 @@ # Write changes to disk self._test_flush_or_close(self._box.flush, True) + def test_popitem_and_flush_twice(self): + # See #15036. + self._box.add(self._template % 0) + self._box.add(self._template % 1) + self._box.flush() + + self._box.popitem() + self._box.flush() + self._box.popitem() + self._box.flush() + def test_lock_unlock(self): # Lock and unlock the mailbox self.assertFalse(os.path.exists(self._get_lock_path())) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -67,6 +67,10 @@ Library ------- +- Issue #15036: Allow removing or changing multiple items in + single-file mailboxes (mbox, MMDF, Babyl) flushing the mailbox + between the changes. + - Issue #10133: Make multiprocessing deallocate buffer if socket read fails. Patch by Hallvard B Furuseth. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 15 20:04:01 2012 From: python-checkins at python.org (petri.lehtinen) Date: Fri, 15 Jun 2012 20:04:01 +0200 Subject: [Python-checkins] =?utf8?b?Y3B5dGhvbiAoMy4yKTogIzE1MDM2OiBNYWtl?= =?utf8?q?_a_repeated_changes_and_flushes_work_with_single-file_mailboxes?= Message-ID: http://hg.python.org/cpython/rev/714b8f91f3d4 changeset: 77444:714b8f91f3d4 branch: 3.2 parent: 77439:69177ff1a643 user: Petri Lehtinen date: Fri Jun 15 20:50:51 2012 +0300 summary: #15036: Make a repeated changes and flushes work with single-file mailboxes files: Lib/mailbox.py | 1 + Lib/test/test_mailbox.py | 11 +++++++++++ Misc/NEWS | 4 ++++ 3 files changed, 16 insertions(+), 0 deletions(-) diff --git a/Lib/mailbox.py b/Lib/mailbox.py --- a/Lib/mailbox.py +++ b/Lib/mailbox.py @@ -675,6 +675,7 @@ new_file.write(buffer) new_toc[key] = (new_start, new_file.tell()) self._post_message_hook(new_file) + self._file_length = new_file.tell() except: new_file.close() os.remove(new_file.name) diff --git a/Lib/test/test_mailbox.py b/Lib/test/test_mailbox.py --- a/Lib/test/test_mailbox.py +++ b/Lib/test/test_mailbox.py @@ -500,6 +500,17 @@ # Write changes to disk self._test_flush_or_close(self._box.flush, True) + def test_popitem_and_flush_twice(self): + # See #15036. + self._box.add(self._template % 0) + self._box.add(self._template % 1) + self._box.flush() + + self._box.popitem() + self._box.flush() + self._box.popitem() + self._box.flush() + def test_lock_unlock(self): # Lock and unlock the mailbox self.assertFalse(os.path.exists(self._get_lock_path())) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -70,6 +70,10 @@ Library ------- +- Issue #15036: Allow removing or changing multiple items in + single-file mailboxes (mbox, MMDF, Babyl) flushing the mailbox + between the changes. + - Issue #10133: Make multiprocessing deallocate buffer if socket read fails. Patch by Hallvard B Furuseth. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 15 20:04:02 2012 From: python-checkins at python.org (petri.lehtinen) Date: Fri, 15 Jun 2012 20:04:02 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=28merge_3=2E2_-=3E_default=29?= =?utf8?q?=3A_=2315036=3A_Make_a_repeated_changes_and_flushes_work_with_si?= =?utf8?q?ngle-file_mailboxes?= Message-ID: http://hg.python.org/cpython/rev/87d119117560 changeset: 77445:87d119117560 parent: 77442:2d2f206d040e parent: 77444:714b8f91f3d4 user: Petri Lehtinen date: Fri Jun 15 21:01:39 2012 +0300 summary: #15036: Make a repeated changes and flushes work with single-file mailboxes files: Lib/mailbox.py | 1 + Lib/test/test_mailbox.py | 11 +++++++++++ Misc/NEWS | 4 ++++ 3 files changed, 16 insertions(+), 0 deletions(-) diff --git a/Lib/mailbox.py b/Lib/mailbox.py --- a/Lib/mailbox.py +++ b/Lib/mailbox.py @@ -675,6 +675,7 @@ new_file.write(buffer) new_toc[key] = (new_start, new_file.tell()) self._post_message_hook(new_file) + self._file_length = new_file.tell() except: new_file.close() os.remove(new_file.name) diff --git a/Lib/test/test_mailbox.py b/Lib/test/test_mailbox.py --- a/Lib/test/test_mailbox.py +++ b/Lib/test/test_mailbox.py @@ -504,6 +504,17 @@ # Write changes to disk self._test_flush_or_close(self._box.flush, True) + def test_popitem_and_flush_twice(self): + # See #15036. + self._box.add(self._template % 0) + self._box.add(self._template % 1) + self._box.flush() + + self._box.popitem() + self._box.flush() + self._box.popitem() + self._box.flush() + def test_lock_unlock(self): # Lock and unlock the mailbox self.assertFalse(os.path.exists(self._get_lock_path())) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -21,6 +21,10 @@ Library ------- +- Issue #15036: Allow removing or changing multiple items in + single-file mailboxes (mbox, MMDF, Babyl) flushing the mailbox + between the changes. + - Issue #14059: Implement multiprocessing.Barrier. - Issue #15061: The inappropriately named hmac.secure_compare has been -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 15 20:24:14 2012 From: python-checkins at python.org (richard.oudkerk) Date: Fri, 15 Jun 2012 20:24:14 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Fix_for_2d2f206d040e_so_tha?= =?utf8?q?t_test=5Fmultiprocessing_does_not_depend_on_ctypes?= Message-ID: http://hg.python.org/cpython/rev/31b17246e959 changeset: 77446:31b17246e959 user: Richard Oudkerk date: Fri Jun 15 19:18:30 2012 +0100 summary: Fix for 2d2f206d040e so that test_multiprocessing does not depend on ctypes files: Lib/test/test_multiprocessing.py | 14 +++++++++----- 1 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_multiprocessing.py b/Lib/test/test_multiprocessing.py --- a/Lib/test/test_multiprocessing.py +++ b/Lib/test/test_multiprocessing.py @@ -1109,9 +1109,13 @@ self.n = n self.started = namespace.DummyList() self.finished = namespace.DummyList() - self._can_exit = namespace.Value('i', not wait_before_exit) + self._can_exit = namespace.Event() + if not wait_before_exit: + self._can_exit.set() for i in range(n): - namespace.Process(target=self.task).start() + p = namespace.Process(target=self.task) + p.daemon = True + p.start() def task(self): pid = os.getpid() @@ -1120,8 +1124,8 @@ self.f(*self.args) finally: self.finished.append(pid) - while not self._can_exit.value: - _wait() + self._can_exit.wait(30) + assert self._can_exit.is_set() def wait_for_started(self): while len(self.started) < self.n: @@ -1132,7 +1136,7 @@ _wait() def do_finish(self): - self._can_exit.value = True + self._can_exit.set() class AppendTrue(object): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 15 21:13:52 2012 From: python-checkins at python.org (richard.oudkerk) Date: Fri, 15 Jun 2012 21:13:52 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Increase_timeout_used_when_?= =?utf8?q?waiting_for_manager_to_shutdown_cleanly?= Message-ID: http://hg.python.org/cpython/rev/831ae71d0bdc changeset: 77447:831ae71d0bdc user: Richard Oudkerk date: Fri Jun 15 20:08:29 2012 +0100 summary: Increase timeout used when waiting for manager to shutdown cleanly before resorting to terminate() files: Lib/multiprocessing/managers.py | 2 +- Lib/test/test_multiprocessing.py | 5 +++++ 2 files changed, 6 insertions(+), 1 deletions(-) diff --git a/Lib/multiprocessing/managers.py b/Lib/multiprocessing/managers.py --- a/Lib/multiprocessing/managers.py +++ b/Lib/multiprocessing/managers.py @@ -582,7 +582,7 @@ except Exception: pass - process.join(timeout=0.2) + process.join(timeout=1.0) if process.is_alive(): util.info('manager still alive') if hasattr(process, 'terminate'): diff --git a/Lib/test/test_multiprocessing.py b/Lib/test/test_multiprocessing.py --- a/Lib/test/test_multiprocessing.py +++ b/Lib/test/test_multiprocessing.py @@ -1820,6 +1820,11 @@ # run after all the other tests for the manager. It tests that # there have been no "reference leaks" for the manager's shared # objects. Note the comment in _TestPool.test_terminate(). + + # If some other test using ManagerMixin.manager fails, then the + # raised exception may keep alive a frame which holds a reference + # to a managed object. This will cause test_number_of_objects to + # also fail. ALLOWED_TYPES = ('manager',) def test_number_of_objects(self): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 15 22:18:31 2012 From: python-checkins at python.org (antoine.pitrou) Date: Fri, 15 Jun 2012 22:18:31 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Issue_=2315026=3A_utf-16_en?= =?utf8?q?coding_is_now_significantly_faster_=28up_to_10x=29=2E?= Message-ID: http://hg.python.org/cpython/rev/acca141fda80 changeset: 77448:acca141fda80 user: Antoine Pitrou date: Fri Jun 15 22:15:23 2012 +0200 summary: Issue #15026: utf-16 encoding is now significantly faster (up to 10x). Patch by Serhiy Storchaka. files: Include/unicodeobject.h | 4 +- Misc/NEWS | 3 + Objects/stringlib/codecs.h | 64 +++++++++++++++++++ Objects/unicodeobject.c | 86 ++++++++++--------------- 4 files changed, 105 insertions(+), 52 deletions(-) diff --git a/Include/unicodeobject.h b/Include/unicodeobject.h --- a/Include/unicodeobject.h +++ b/Include/unicodeobject.h @@ -188,9 +188,9 @@ (((((Py_UCS4)(high) & 0x03FF) << 10) | \ ((Py_UCS4)(low) & 0x03FF)) + 0x10000) /* high surrogate = top 10 bits added to D800 */ -#define Py_UNICODE_HIGH_SURROGATE(ch) (0xD800 | (((ch) - 0x10000) >> 10)) +#define Py_UNICODE_HIGH_SURROGATE(ch) (0xD800 - (0x10000 >> 10) + ((ch) >> 10)) /* low surrogate = bottom 10 bits added to DC00 */ -#define Py_UNICODE_LOW_SURROGATE(ch) (0xDC00 | (((ch) - 0x10000) & 0x3FF)) +#define Py_UNICODE_LOW_SURROGATE(ch) (0xDC00 + ((ch) & 0x3FF)) /* Check if substring matches at given offset. The offset must be valid, and the substring must not be empty. */ diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,9 @@ Core and Builtins ----------------- +- Issue #15026: utf-16 encoding is now significantly faster (up to 10x). + Patch by Serhiy Storchaka. + - Issue #11022: open() and io.TextIOWrapper are now calling locale.getpreferredencoding(False) instead of locale.getpreferredencoding() in text mode if the encoding is not specified. Don't change temporary the diff --git a/Objects/stringlib/codecs.h b/Objects/stringlib/codecs.h --- a/Objects/stringlib/codecs.h +++ b/Objects/stringlib/codecs.h @@ -562,4 +562,68 @@ #undef STRIPPED_MASK #undef SWAB #undef LONG_PTR_MASK + + +Py_LOCAL_INLINE(void) +STRINGLIB(utf16_encode)(unsigned short *out, + const STRINGLIB_CHAR *in, + Py_ssize_t len, + int native_ordering) +{ + const STRINGLIB_CHAR *end = in + len; +#if STRINGLIB_SIZEOF_CHAR == 1 +# define SWAB2(CH) ((CH) << 8) +#else +# define SWAB2(CH) (((CH) << 8) | ((CH) >> 8)) +#endif +#if STRINGLIB_MAX_CHAR < 0x10000 + if (native_ordering) { +# if STRINGLIB_SIZEOF_CHAR == 2 + Py_MEMCPY(out, in, 2 * len); +# else + _PyUnicode_CONVERT_BYTES(STRINGLIB_CHAR, unsigned short, in, end, out); +# endif + } else { + const STRINGLIB_CHAR *unrolled_end = in + (len & ~ (Py_ssize_t) 3); + while (in < unrolled_end) { + out[0] = SWAB2(in[0]); + out[1] = SWAB2(in[1]); + out[2] = SWAB2(in[2]); + out[3] = SWAB2(in[3]); + in += 4; out += 4; + } + while (in < end) { + *out++ = SWAB2(*in); + ++in; + } + } +#else + if (native_ordering) { + while (in < end) { + Py_UCS4 ch = *in++; + if (ch < 0x10000) + *out++ = ch; + else { + out[0] = Py_UNICODE_HIGH_SURROGATE(ch); + out[1] = Py_UNICODE_LOW_SURROGATE(ch); + out += 2; + } + } + } else { + while (in < end) { + Py_UCS4 ch = *in++; + if (ch < 0x10000) + *out++ = SWAB2((Py_UCS2)ch); + else { + Py_UCS2 ch1 = Py_UNICODE_HIGH_SURROGATE(ch); + Py_UCS2 ch2 = Py_UNICODE_LOW_SURROGATE(ch); + out[0] = SWAB2(ch1); + out[1] = SWAB2(ch2); + out += 2; + } + } + } +#endif +#undef SWAB2 +} #endif /* STRINGLIB_IS_UNICODE */ diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -5359,26 +5359,18 @@ const char *errors, int byteorder) { - int kind; - void *data; + enum PyUnicode_Kind kind; + const void *data; Py_ssize_t len; PyObject *v; - unsigned char *p; - Py_ssize_t nsize, bytesize; - Py_ssize_t i, pairs; - /* Offsets from p for storing byte pairs in the right order. */ -#ifdef BYTEORDER_IS_LITTLE_ENDIAN - int ihi = 1, ilo = 0; + unsigned short *out; + Py_ssize_t bytesize; + Py_ssize_t pairs; +#ifdef WORDS_BIGENDIAN + int native_ordering = byteorder >= 0; #else - int ihi = 0, ilo = 1; -#endif - -#define STORECHAR(CH) \ - do { \ - p[ihi] = ((CH) >> 8) & 0xff; \ - p[ilo] = (CH) & 0xff; \ - p += 2; \ - } while(0) + int native_ordering = byteorder <= 0; +#endif if (!PyUnicode_Check(str)) { PyErr_BadArgument(); @@ -5391,53 +5383,47 @@ len = PyUnicode_GET_LENGTH(str); pairs = 0; - if (kind == PyUnicode_4BYTE_KIND) - for (i = 0; i < len; i++) - if (PyUnicode_READ(kind, data, i) >= 0x10000) + if (kind == PyUnicode_4BYTE_KIND) { + const Py_UCS4 *in = (const Py_UCS4 *)data; + const Py_UCS4 *end = in + len; + while (in < end) + if (*in++ >= 0x10000) pairs++; - /* 2 * (len + pairs + (byteorder == 0)) */ - if (len > PY_SSIZE_T_MAX - pairs - (byteorder == 0)) + } + if (len > PY_SSIZE_T_MAX / 2 - pairs - (byteorder == 0)) return PyErr_NoMemory(); - nsize = len + pairs + (byteorder == 0); - bytesize = nsize * 2; - if (bytesize / 2 != nsize) - return PyErr_NoMemory(); + bytesize = (len + pairs + (byteorder == 0)) * 2; v = PyBytes_FromStringAndSize(NULL, bytesize); if (v == NULL) return NULL; - p = (unsigned char *)PyBytes_AS_STRING(v); + /* output buffer is 2-bytes aligned */ + assert(((Py_uintptr_t)PyBytes_AS_STRING(v) & 1) == 0); + out = (unsigned short *)PyBytes_AS_STRING(v); if (byteorder == 0) - STORECHAR(0xFEFF); + *out++ = 0xFEFF; if (len == 0) goto done; - if (byteorder == -1) { - /* force LE */ - ihi = 1; - ilo = 0; - } - else if (byteorder == 1) { - /* force BE */ - ihi = 0; - ilo = 1; - } - - for (i = 0; i < len; i++) { - Py_UCS4 ch = PyUnicode_READ(kind, data, i); - Py_UCS4 ch2 = 0; - if (ch >= 0x10000) { - ch2 = Py_UNICODE_LOW_SURROGATE(ch); - ch = Py_UNICODE_HIGH_SURROGATE(ch); - } - STORECHAR(ch); - if (ch2) - STORECHAR(ch2); + switch (kind) { + case PyUnicode_1BYTE_KIND: { + ucs1lib_utf16_encode(out, (const Py_UCS1 *)data, len, native_ordering); + break; + } + case PyUnicode_2BYTE_KIND: { + ucs2lib_utf16_encode(out, (const Py_UCS2 *)data, len, native_ordering); + break; + } + case PyUnicode_4BYTE_KIND: { + ucs4lib_utf16_encode(out, (const Py_UCS4 *)data, len, native_ordering); + break; + } + default: + assert(0); } done: return v; -#undef STORECHAR } PyObject * -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 15 22:25:25 2012 From: python-checkins at python.org (antoine.pitrou) Date: Fri, 15 Jun 2012 22:25:25 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Mention_the_UTF-16_encoding?= =?utf8?q?_speedup_in_the_whatsnew_=28issue_=2315026=29=2E?= Message-ID: http://hg.python.org/cpython/rev/35667fc5f785 changeset: 77449:35667fc5f785 user: Antoine Pitrou date: Fri Jun 15 22:22:18 2012 +0200 summary: Mention the UTF-16 encoding speedup in the whatsnew (issue #15026). files: Doc/whatsnew/3.3.rst | 6 ++++-- 1 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Doc/whatsnew/3.3.rst b/Doc/whatsnew/3.3.rst --- a/Doc/whatsnew/3.3.rst +++ b/Doc/whatsnew/3.3.rst @@ -1484,9 +1484,11 @@ * repeating a single ASCII letter and getting a substring of a ASCII strings is 4 times faster -* UTF-8 and UTF-16 decoding is now 2x to 4x faster. +* UTF-8 and UTF-16 decoding is now 2x to 4x faster. UTF-16 encoding is now + up to 10x faster. - (contributed by Serhiy Storchaka, :issue:`14624` and :issue:`14738`.) + (contributed by Serhiy Storchaka, :issue:`14624`, :issue:`14738` and + :issue:`15026`.) Build and C API Changes -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 15 23:00:21 2012 From: python-checkins at python.org (richard.oudkerk) Date: Fri, 15 Jun 2012 23:00:21 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Fix_=5FTestListener=2EALLOW?= =?utf8?q?ED=5FTYPES_and_add_sanity_check?= Message-ID: http://hg.python.org/cpython/rev/4c704dc97496 changeset: 77450:4c704dc97496 user: Richard Oudkerk date: Fri Jun 15 21:53:34 2012 +0100 summary: Fix _TestListener.ALLOWED_TYPES and add sanity check files: Lib/test/test_multiprocessing.py | 4 +++- 1 files changed, 3 insertions(+), 1 deletions(-) diff --git a/Lib/test/test_multiprocessing.py b/Lib/test/test_multiprocessing.py --- a/Lib/test/test_multiprocessing.py +++ b/Lib/test/test_multiprocessing.py @@ -2268,7 +2268,7 @@ class _TestListener(BaseTestCase): - ALLOWED_TYPES = ('processes') + ALLOWED_TYPES = ('processes',) def test_multiple_bind(self): for family in self.connection.families: @@ -2850,10 +2850,12 @@ result = {} glob = globals() Type = type.capitalize() + ALL_TYPES = {'processes', 'threads', 'manager'} for name in list(glob.keys()): if name.startswith('_Test'): base = glob[name] + assert set(base.ALLOWED_TYPES) <= ALL_TYPES, set(base.ALLOWED_TYPES) if type in base.ALLOWED_TYPES: newname = 'With' + Type + name[1:] class Temp(base, unittest.TestCase, Mixin): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 16 00:15:40 2012 From: python-checkins at python.org (alexander.belopolsky) Date: Sat, 16 Jun 2012 00:15:40 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Removed_redundant_code?= Message-ID: http://hg.python.org/cpython/rev/14028bdd80bc changeset: 77451:14028bdd80bc user: Alexander Belopolsky date: Fri Jun 15 18:15:25 2012 -0400 summary: Removed redundant code files: Lib/datetime.py | 6 ++---- 1 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Lib/datetime.py b/Lib/datetime.py --- a/Lib/datetime.py +++ b/Lib/datetime.py @@ -1670,10 +1670,8 @@ if mytz is ottz: base_compare = True else: - if mytz is not None: - myoff = self.utcoffset() - if ottz is not None: - otoff = other.utcoffset() + myoff = self.utcoffset() + otoff = other.utcoffset() base_compare = myoff == otoff if base_compare: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 16 01:05:32 2012 From: python-checkins at python.org (brett.cannon) Date: Sat, 16 Jun 2012 01:05:32 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Make_a_test_easier_to_read?= =?utf8?q?=2E?= Message-ID: http://hg.python.org/cpython/rev/b03dcd25ced8 changeset: 77452:b03dcd25ced8 parent: 77410:d9d382b7670a user: Brett Cannon date: Mon Jun 11 11:02:36 2012 -0400 summary: Make a test easier to read. files: Lib/test/test_reprlib.py | 7 +++---- 1 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_reprlib.py b/Lib/test/test_reprlib.py --- a/Lib/test/test_reprlib.py +++ b/Lib/test/test_reprlib.py @@ -251,13 +251,12 @@ def test_module(self): self._check_path_limitations(self.pkgname) - eq = self.assertEqual create_empty_file(os.path.join(self.subpkgname, self.pkgname + '.py')) importlib.invalidate_caches() from areallylongpackageandmodulenametotestreprtruncation.areallylongpackageandmodulenametotestreprtruncation import areallylongpackageandmodulenametotestreprtruncation - eq(repr(areallylongpackageandmodulenametotestreprtruncation), - "" % (areallylongpackageandmodulenametotestreprtruncation.__name__, areallylongpackageandmodulenametotestreprtruncation.__file__)) - eq(repr(sys), "") + module = areallylongpackageandmodulenametotestreprtruncation + self.assertEqual(repr(module), "" % (module.__name__, module.__file__)) + self.assertEqual(repr(sys), "") def test_type(self): self._check_path_limitations('foo') -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 16 01:05:33 2012 From: python-checkins at python.org (brett.cannon) Date: Sat, 16 Jun 2012 01:05:33 +0200 Subject: [Python-checkins] =?utf8?q?cpython=3A_Update_importlib=2Eh_by_tou?= =?utf8?q?ching_Lib/importlib/=5Fbootstrap=2Epy=2E?= Message-ID: http://hg.python.org/cpython/rev/0cb3a518116c changeset: 77453:0cb3a518116c user: Brett Cannon date: Mon Jun 11 11:02:53 2012 -0400 summary: Update importlib.h by touching Lib/importlib/_bootstrap.py. files: Python/importlib.h | Bin 1 files changed, 0 insertions(+), 0 deletions(-) diff --git a/Python/importlib.h b/Python/importlib.h index 224e28c029a7dc4b51e87b6f07a9f213f8d2bcf2..15d0ba4bbecf831fd80c35507509ed2721568f19 GIT binary patch [stripped] -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 16 01:05:34 2012 From: python-checkins at python.org (brett.cannon) Date: Sat, 16 Jun 2012 01:05:34 +0200 Subject: [Python-checkins] =?utf8?q?cpython_=28merge_default_-=3E_default?= =?utf8?q?=29=3A_Merge?= Message-ID: http://hg.python.org/cpython/rev/7b8d3b7fd606 changeset: 77454:7b8d3b7fd606 parent: 77453:0cb3a518116c parent: 77451:14028bdd80bc user: Brett Cannon date: Fri Jun 15 19:04:29 2012 -0400 summary: Merge files: .hgignore | 2 + Doc/extending/newtypes.rst | 5 +- Doc/library/hmac.rst | 41 +- Doc/library/multiprocessing.rst | 30 +- Doc/library/socket.rst | 2 +- Doc/library/time.rst | 59 +- Doc/whatsnew/3.3.rst | 6 +- Include/pytime.h | 2 +- Include/unicodeobject.h | 4 +- Lib/_strptime.py | 6 +- Lib/datetime.py | 6 +- Lib/hmac.py | 26 +- Lib/idlelib/AutoComplete.py | 2 +- Lib/mailbox.py | 1 + Lib/multiprocessing/__init__.py | 11 +- Lib/multiprocessing/dummy/__init__.py | 4 +- Lib/multiprocessing/forking.py | 6 +- Lib/multiprocessing/managers.py | 94 +- Lib/multiprocessing/synchronize.py | 40 + Lib/multiprocessing/util.py | 27 +- Lib/test/support.py | 2 +- Lib/test/test_hmac.py | 44 +- Lib/test/test_mailbox.py | 11 + Lib/test/test_multiprocessing.py | 355 +++++++++++++- Lib/test/test_structseq.py | 5 +- Lib/test/test_time.py | 65 ++- Lib/test/test_xml_etree.py | 226 ++++---- Lib/test/test_xml_etree_c.py | 28 +- Lib/xml/etree/ElementTree.py | 32 +- Misc/NEWS | 29 + Modules/_curses_panel.c | 124 ++-- Modules/_decimal/libmpdec/mpdecimal.c | 16 +- Modules/_elementtree.c | 364 ++++++++++--- Modules/timemodule.c | 129 ++-- Objects/stringlib/codecs.h | 64 ++ Objects/unicodeobject.c | 86 +- PC/VS9.0/pythoncore.vcproj | 8 + Python/pytime.c | 11 +- 38 files changed, 1361 insertions(+), 612 deletions(-) diff --git a/.hgignore b/.hgignore --- a/.hgignore +++ b/.hgignore @@ -55,6 +55,8 @@ PC/pythonnt_rc*.h PC/*.obj PC/*.exe +PC/*/*.exe +PC/*/*.pdb PC/*/*.user PC/*/*.ncb PC/*/*.suo diff --git a/Doc/extending/newtypes.rst b/Doc/extending/newtypes.rst --- a/Doc/extending/newtypes.rst +++ b/Doc/extending/newtypes.rst @@ -1437,9 +1437,8 @@ } The only further addition is that the destructor needs to call the weak -reference manager to clear any weak references. This should be done before any -other parts of the destruction have occurred, but is only required if the weak -reference list is non-*NULL*:: +reference manager to clear any weak references. This is only required if the +weak reference list is non-*NULL*:: static void instance_dealloc(PyInstanceObject *inst) diff --git a/Doc/library/hmac.rst b/Doc/library/hmac.rst --- a/Doc/library/hmac.rst +++ b/Doc/library/hmac.rst @@ -42,8 +42,8 @@ When comparing the output of :meth:`digest` to an externally-supplied digest during a verification routine, it is recommended to use the - :func:`hmac.secure_compare` function instead of the ``==`` operator - to avoid potential timing attacks. + :func:`compare_digest` function instead of the ``==`` operator + to reduce the vulnerability to timing attacks. .. method:: HMAC.hexdigest() @@ -54,10 +54,11 @@ .. warning:: - When comparing the output of :meth:`hexdigest` to an externally-supplied - digest during a verification routine, it is recommended to use the - :func:`hmac.secure_compare` function instead of the ``==`` operator - to avoid potential timing attacks. + The output of :meth:`hexdigest` should not be compared directly to an + externally-supplied digest during a verification routine. Instead, the + externally supplied digest should be converted to a :class:`bytes` + value and compared to the output of :meth:`digest` with + :func:`compare_digest`. .. method:: HMAC.copy() @@ -68,20 +69,28 @@ This module also provides the following helper function: -.. function:: secure_compare(a, b) +.. function:: compare_digest(a, b) - Returns the equivalent of ``a == b``, but using a time-independent - comparison method. Comparing the full lengths of the inputs *a* and *b*, - instead of short-circuiting the comparison upon the first unequal byte, - prevents leaking information about the inputs being compared and mitigates - potential timing attacks. The inputs must be either :class:`str` or - :class:`bytes` instances. + Returns the equivalent of ``a == b``, but avoids content based + short circuiting behaviour to reduce the vulnerability to timing + analysis. The inputs must be :class:`bytes` instances. + + Using a short circuiting comparison (that is, one that terminates as soon + as it finds any difference between the values) to check digests for + correctness can be problematic, as it introduces a potential + vulnerability when an attacker can control both the message to be checked + *and* the purported signature value. By keeping the plaintext consistent + and supplying different signature values, an attacker may be able to use + timing variations to search the signature space for the expected value in + O(n) time rather than the desired O(2**n). .. note:: - While the :func:`hmac.secure_compare` function prevents leaking the - contents of the inputs via a timing attack, it does leak the length - of the inputs. However, this generally is not a security risk. + While this function reduces the likelihood of leaking the contents of + the expected digest via a timing attack, it still uses short circuiting + behaviour based on the *length* of the inputs. It is assumed that the + expected length of the digest is not a secret, as it is typically + published as part of a file format, network protocol or API definition. .. versionadded:: 3.3 diff --git a/Doc/library/multiprocessing.rst b/Doc/library/multiprocessing.rst --- a/Doc/library/multiprocessing.rst +++ b/Doc/library/multiprocessing.rst @@ -226,11 +226,11 @@ holds Python objects and allows other processes to manipulate them using proxies. - A manager returned by :func:`Manager` will support types :class:`list`, - :class:`dict`, :class:`Namespace`, :class:`Lock`, :class:`RLock`, - :class:`Semaphore`, :class:`BoundedSemaphore`, :class:`Condition`, - :class:`Event`, :class:`Queue`, :class:`Value` and :class:`Array`. For - example, :: + A manager returned by :func:`Manager` will support types + :class:`list`, :class:`dict`, :class:`Namespace`, :class:`Lock`, + :class:`RLock`, :class:`Semaphore`, :class:`BoundedSemaphore`, + :class:`Condition`, :class:`Event`, :class:`Barrier`, + :class:`Queue`, :class:`Value` and :class:`Array`. For example, :: from multiprocessing import Process, Manager @@ -885,6 +885,12 @@ Note that one can also create synchronization primitives by using a manager object -- see :ref:`multiprocessing-managers`. +.. class:: Barrier(parties[, action[, timeout]]) + + A barrier object: a clone of :class:`threading.Barrier`. + + .. versionadded:: 3.3 + .. class:: BoundedSemaphore([value]) A bounded semaphore object: a clone of :class:`threading.BoundedSemaphore`. @@ -1236,9 +1242,10 @@ type of shared object. This must be a string. *callable* is a callable used for creating objects for this type - identifier. If a manager instance will be created using the - :meth:`from_address` classmethod or if the *create_method* argument is - ``False`` then this can be left as ``None``. + identifier. If a manager instance will be connected to the + server using the :meth:`connect` method, or if the + *create_method* argument is ``False`` then this can be left as + ``None``. *proxytype* is a subclass of :class:`BaseProxy` which is used to create proxies for shared objects with this *typeid*. If ``None`` then a proxy @@ -1279,6 +1286,13 @@ It also supports creation of shared lists and dictionaries. + .. method:: Barrier(parties[, action[, timeout]]) + + Create a shared :class:`threading.Barrier` object and return a + proxy for it. + + .. versionadded:: 3.3 + .. method:: BoundedSemaphore([value]) Create a shared :class:`threading.BoundedSemaphore` object and return a diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -61,7 +61,7 @@ - A pair ``(host, port)`` is used for the :const:`AF_INET` address family, where *host* is a string representing either a hostname in Internet domain notation like ``'daring.cwi.nl'`` or an IPv4 address like ``'100.50.200.5'``, - and *port* is an integral port number. + and *port* is an integer. - For :const:`AF_INET6` address family, a four-tuple ``(host, port, flowinfo, scopeid)`` is used, where *flowinfo* and *scopeid* represent the ``sin6_flowinfo`` diff --git a/Doc/library/time.rst b/Doc/library/time.rst --- a/Doc/library/time.rst +++ b/Doc/library/time.rst @@ -77,6 +77,12 @@ See :class:`struct_time` for a description of these objects. + .. versionchanged:: 3.3 + + The :class:`struct_time` type was extended to provide the + :attr:`tm_gmtoff` and :attr:`tm_zone` attributes when platform + supports corresponding ``struct tm`` members. + * Use the following functions to convert between time representations: +-------------------------+-------------------------+-------------------------+ @@ -160,30 +166,6 @@ .. versionadded:: 3.3 -.. class:: clock_info - - Clock information object returned by :func:`get_clock_info`. - - .. attribute:: implementation - - The name of the underlying C function used to get the clock value. - - .. attribute:: monotonic - - ``True`` if the clock cannot go backward, ``False`` otherwise. - - .. attribute:: adjusted - - ``True`` if the clock can be adjusted (e.g. by a NTP daemon), ``False`` - otherwise. - - .. attribute:: resolution - - The resolution of the clock in seconds (:class:`float`). - - .. versionadded:: 3.3 - - .. function:: clock_settime(clk_id, time) Set the time of the specified clock *clk_id*. @@ -267,7 +249,7 @@ .. function:: get_clock_info(name) - Get information on the specified clock as a :class:`clock_info` object. + Get information on the specified clock as a namespace object. Supported clock names and the corresponding functions to read their value are: @@ -277,6 +259,16 @@ * ``'process_time'``: :func:`time.process_time` * ``'time'``: :func:`time.time` + The result has the following attributes: + + - *adjustable*: ``True`` if the clock can be changed automatically (e.g. by + a NTP daemon) or manually by the system administrator, ``False`` otherwise + - *implementation*: The name of the underlying C function used to get + the clock value + - *monotonic*: ``True`` if the clock cannot go backward, + ``False`` otherwise + - *resolution*: The resolution of the clock in seconds (:class:`float`) + .. versionadded:: 3.3 @@ -350,7 +342,6 @@ .. versionadded:: 3.3 - .. function:: sleep(secs) Suspend execution for the given number of seconds. The argument may be a @@ -447,6 +438,12 @@ | ``%Y`` | Year with century as a decimal number. | | | | | | +-----------+------------------------------------------------+-------+ + | ``%z`` | Time zone offset indicating a positive or | | + | | negative time difference from UTC/GMT of the | | + | | form +HHMM or -HHMM, where H represents decimal| | + | | hour digits and M represents decimal minute | | + | | digits [-23:59, +23:59]. | | + +-----------+------------------------------------------------+-------+ | ``%Z`` | Time zone name (no characters if no time zone | | | | exists). | | +-----------+------------------------------------------------+-------+ @@ -546,6 +543,10 @@ +-------+-------------------+---------------------------------+ | 8 | :attr:`tm_isdst` | 0, 1 or -1; see below | +-------+-------------------+---------------------------------+ + | N/A | :attr:`tm_zone` | abbreviation of timezone name | + +-------+-------------------+---------------------------------+ + | N/A | :attr:`tm_gmtoff` | offset from UTC in seconds | + +-------+-------------------+---------------------------------+ Note that unlike the C structure, the month value is a range of [1, 12], not [0, 11]. A ``-1`` argument as the daylight @@ -556,6 +557,11 @@ :class:`struct_time`, or having elements of the wrong type, a :exc:`TypeError` is raised. + .. versionchanged:: 3.3 + + :attr:`tm_gmtoff` and :attr:`tm_zone` attributes are avaliable on + platforms with C library supporting the corresponding fields in + ``struct tm``. .. function:: time() @@ -566,7 +572,6 @@ lower value than a previous call if the system clock has been set back between the two calls. - .. data:: timezone The offset of the local (non-DST) timezone, in seconds west of UTC (negative in diff --git a/Doc/whatsnew/3.3.rst b/Doc/whatsnew/3.3.rst --- a/Doc/whatsnew/3.3.rst +++ b/Doc/whatsnew/3.3.rst @@ -1484,9 +1484,11 @@ * repeating a single ASCII letter and getting a substring of a ASCII strings is 4 times faster -* UTF-8 and UTF-16 decoding is now 2x to 4x faster. +* UTF-8 and UTF-16 decoding is now 2x to 4x faster. UTF-16 encoding is now + up to 10x faster. - (contributed by Serhiy Storchaka, :issue:`14624` and :issue:`14738`.) + (contributed by Serhiy Storchaka, :issue:`14624`, :issue:`14738` and + :issue:`15026`.) Build and C API Changes diff --git a/Include/pytime.h b/Include/pytime.h --- a/Include/pytime.h +++ b/Include/pytime.h @@ -26,7 +26,7 @@ typedef struct { const char *implementation; int monotonic; - int adjusted; + int adjustable; double resolution; } _Py_clock_info_t; diff --git a/Include/unicodeobject.h b/Include/unicodeobject.h --- a/Include/unicodeobject.h +++ b/Include/unicodeobject.h @@ -188,9 +188,9 @@ (((((Py_UCS4)(high) & 0x03FF) << 10) | \ ((Py_UCS4)(low) & 0x03FF)) + 0x10000) /* high surrogate = top 10 bits added to D800 */ -#define Py_UNICODE_HIGH_SURROGATE(ch) (0xD800 | (((ch) - 0x10000) >> 10)) +#define Py_UNICODE_HIGH_SURROGATE(ch) (0xD800 - (0x10000 >> 10) + ((ch) >> 10)) /* low surrogate = bottom 10 bits added to DC00 */ -#define Py_UNICODE_LOW_SURROGATE(ch) (0xDC00 | (((ch) - 0x10000) & 0x3FF)) +#define Py_UNICODE_LOW_SURROGATE(ch) (0xDC00 + ((ch) & 0x3FF)) /* Check if substring matches at given offset. The offset must be valid, and the substring must not be empty. */ diff --git a/Lib/_strptime.py b/Lib/_strptime.py --- a/Lib/_strptime.py +++ b/Lib/_strptime.py @@ -486,19 +486,19 @@ return (year, month, day, hour, minute, second, - weekday, julian, tz, gmtoff, tzname), fraction + weekday, julian, tz, tzname, gmtoff), fraction def _strptime_time(data_string, format="%a %b %d %H:%M:%S %Y"): """Return a time struct based on the input string and the format string.""" tt = _strptime(data_string, format)[0] - return time.struct_time(tt[:9]) + return time.struct_time(tt[:time._STRUCT_TM_ITEMS]) def _strptime_datetime(cls, data_string, format="%a %b %d %H:%M:%S %Y"): """Return a class cls instance based on the input string and the format string.""" tt, fraction = _strptime(data_string, format) - gmtoff, tzname = tt[-2:] + tzname, gmtoff = tt[-2:] args = tt[:6] + (fraction,) if gmtoff is not None: tzdelta = datetime_timedelta(seconds=gmtoff) diff --git a/Lib/datetime.py b/Lib/datetime.py --- a/Lib/datetime.py +++ b/Lib/datetime.py @@ -1670,10 +1670,8 @@ if mytz is ottz: base_compare = True else: - if mytz is not None: - myoff = self.utcoffset() - if ottz is not None: - otoff = other.utcoffset() + myoff = self.utcoffset() + otoff = other.utcoffset() base_compare = myoff == otoff if base_compare: diff --git a/Lib/hmac.py b/Lib/hmac.py --- a/Lib/hmac.py +++ b/Lib/hmac.py @@ -13,24 +13,24 @@ digest_size = None -def secure_compare(a, b): - """Returns the equivalent of 'a == b', but using a time-independent - comparison method to prevent timing attacks.""" - if not ((isinstance(a, str) and isinstance(b, str)) or - (isinstance(a, bytes) and isinstance(b, bytes))): - raise TypeError("inputs must be strings or bytes") +def compare_digest(a, b): + """Returns the equivalent of 'a == b', but avoids content based short + circuiting to reduce the vulnerability to timing attacks.""" + # Consistent timing matters more here than data type flexibility + if not (isinstance(a, bytes) and isinstance(b, bytes)): + raise TypeError("inputs must be bytes instances") + # We assume the length of the expected digest is public knowledge, + # thus this early return isn't leaking anything an attacker wouldn't + # already know if len(a) != len(b): return False + # We assume that integers in the bytes range are all cached, + # thus timing shouldn't vary much due to integer object creation result = 0 - if isinstance(a, bytes): - for x, y in zip(a, b): - result |= x ^ y - else: - for x, y in zip(a, b): - result |= ord(x) ^ ord(y) - + for x, y in zip(a, b): + result |= x ^ y return result == 0 diff --git a/Lib/idlelib/AutoComplete.py b/Lib/idlelib/AutoComplete.py --- a/Lib/idlelib/AutoComplete.py +++ b/Lib/idlelib/AutoComplete.py @@ -140,7 +140,7 @@ elif hp.is_in_code() and (not mode or mode==COMPLETE_ATTRIBUTES): self._remove_autocomplete_window() mode = COMPLETE_ATTRIBUTES - while i and curline[i-1] in ID_CHARS or ord(curline[i-1]) > 127: + while i and (curline[i-1] in ID_CHARS or ord(curline[i-1]) > 127): i -= 1 comp_start = curline[i:j] if i and curline[i-1] == '.': diff --git a/Lib/mailbox.py b/Lib/mailbox.py --- a/Lib/mailbox.py +++ b/Lib/mailbox.py @@ -675,6 +675,7 @@ new_file.write(buffer) new_toc[key] = (new_start, new_file.tell()) self._post_message_hook(new_file) + self._file_length = new_file.tell() except: new_file.close() os.remove(new_file.name) diff --git a/Lib/multiprocessing/__init__.py b/Lib/multiprocessing/__init__.py --- a/Lib/multiprocessing/__init__.py +++ b/Lib/multiprocessing/__init__.py @@ -23,8 +23,8 @@ 'Manager', 'Pipe', 'cpu_count', 'log_to_stderr', 'get_logger', 'allow_connection_pickling', 'BufferTooShort', 'TimeoutError', 'Lock', 'RLock', 'Semaphore', 'BoundedSemaphore', 'Condition', - 'Event', 'Queue', 'SimpleQueue', 'JoinableQueue', 'Pool', 'Value', 'Array', - 'RawValue', 'RawArray', 'SUBDEBUG', 'SUBWARNING', + 'Event', 'Barrier', 'Queue', 'SimpleQueue', 'JoinableQueue', 'Pool', + 'Value', 'Array', 'RawValue', 'RawArray', 'SUBDEBUG', 'SUBWARNING', ] __author__ = 'R. Oudkerk (r.m.oudkerk at gmail.com)' @@ -186,6 +186,13 @@ from multiprocessing.synchronize import Event return Event() +def Barrier(parties, action=None, timeout=None): + ''' + Returns a barrier object + ''' + from multiprocessing.synchronize import Barrier + return Barrier(parties, action, timeout) + def Queue(maxsize=0): ''' Returns a queue object diff --git a/Lib/multiprocessing/dummy/__init__.py b/Lib/multiprocessing/dummy/__init__.py --- a/Lib/multiprocessing/dummy/__init__.py +++ b/Lib/multiprocessing/dummy/__init__.py @@ -35,7 +35,7 @@ __all__ = [ 'Process', 'current_process', 'active_children', 'freeze_support', 'Lock', 'RLock', 'Semaphore', 'BoundedSemaphore', 'Condition', - 'Event', 'Queue', 'Manager', 'Pipe', 'Pool', 'JoinableQueue' + 'Event', 'Barrier', 'Queue', 'Manager', 'Pipe', 'Pool', 'JoinableQueue' ] # @@ -49,7 +49,7 @@ from multiprocessing.dummy.connection import Pipe from threading import Lock, RLock, Semaphore, BoundedSemaphore -from threading import Event, Condition +from threading import Event, Condition, Barrier from queue import Queue # diff --git a/Lib/multiprocessing/forking.py b/Lib/multiprocessing/forking.py --- a/Lib/multiprocessing/forking.py +++ b/Lib/multiprocessing/forking.py @@ -13,7 +13,7 @@ from multiprocessing import util, process -__all__ = ['Popen', 'assert_spawning', 'exit', 'duplicate', 'close', 'ForkingPickler'] +__all__ = ['Popen', 'assert_spawning', 'duplicate', 'close', 'ForkingPickler'] # # Check that the current thread is spawning a child process @@ -75,7 +75,6 @@ # if sys.platform != 'win32': - exit = os._exit duplicate = os.dup close = os.close @@ -168,7 +167,6 @@ WINEXE = (sys.platform == 'win32' and getattr(sys, 'frozen', False)) WINSERVICE = sys.executable.lower().endswith("pythonservice.exe") - exit = _winapi.ExitProcess close = _winapi.CloseHandle # @@ -349,7 +347,7 @@ from_parent.close() exitcode = self._bootstrap() - exit(exitcode) + sys.exit(exitcode) def get_preparation_data(name): diff --git a/Lib/multiprocessing/managers.py b/Lib/multiprocessing/managers.py --- a/Lib/multiprocessing/managers.py +++ b/Lib/multiprocessing/managers.py @@ -22,7 +22,7 @@ from traceback import format_exc from multiprocessing import Process, current_process, active_children, Pool, util, connection from multiprocessing.process import AuthenticationString -from multiprocessing.forking import exit, Popen, ForkingPickler +from multiprocessing.forking import Popen, ForkingPickler from time import time as _time # @@ -140,28 +140,38 @@ self.id_to_obj = {'0': (None, ())} self.id_to_refcount = {} self.mutex = threading.RLock() - self.stop = 0 def serve_forever(self): ''' Run the server forever ''' + self.stop_event = threading.Event() current_process()._manager_server = self try: + accepter = threading.Thread(target=self.accepter) + accepter.daemon = True + accepter.start() try: - while 1: - try: - c = self.listener.accept() - except (OSError, IOError): - continue - t = threading.Thread(target=self.handle_request, args=(c,)) - t.daemon = True - t.start() + while not self.stop_event.is_set(): + self.stop_event.wait(1) except (KeyboardInterrupt, SystemExit): pass finally: - self.stop = 999 - self.listener.close() + if sys.stdout != sys.__stdout__: + util.debug('resetting stdout, stderr') + sys.stdout = sys.__stdout__ + sys.stderr = sys.__stderr__ + sys.exit(0) + + def accepter(self): + while True: + try: + c = self.listener.accept() + except (OSError, IOError): + continue + t = threading.Thread(target=self.handle_request, args=(c,)) + t.daemon = True + t.start() def handle_request(self, c): ''' @@ -208,7 +218,7 @@ send = conn.send id_to_obj = self.id_to_obj - while not self.stop: + while not self.stop_event.is_set(): try: methodname = obj = None @@ -318,32 +328,13 @@ Shutdown this process ''' try: - try: - util.debug('manager received shutdown message') - c.send(('#RETURN', None)) - - if sys.stdout != sys.__stdout__: - util.debug('resetting stdout, stderr') - sys.stdout = sys.__stdout__ - sys.stderr = sys.__stderr__ - - util._run_finalizers(0) - - for p in active_children(): - util.debug('terminating a child process of manager') - p.terminate() - - for p in active_children(): - util.debug('terminating a child process of manager') - p.join() - - util._run_finalizers() - util.info('manager exiting with exitcode 0') - except: - import traceback - traceback.print_exc() + util.debug('manager received shutdown message') + c.send(('#RETURN', None)) + except: + import traceback + traceback.print_exc() finally: - exit(0) + self.stop_event.set() def create(self, c, typeid, *args, **kwds): ''' @@ -455,10 +446,6 @@ self._serializer = serializer self._Listener, self._Client = listener_client[serializer] - def __reduce__(self): - return type(self).from_address, \ - (self._address, self._authkey, self._serializer) - def get_server(self): ''' Return server object with serve_forever() method and address attribute @@ -595,7 +582,7 @@ except Exception: pass - process.join(timeout=0.2) + process.join(timeout=1.0) if process.is_alive(): util.info('manager still alive') if hasattr(process, 'terminate'): @@ -1006,6 +993,26 @@ def wait(self, timeout=None): return self._callmethod('wait', (timeout,)) + +class BarrierProxy(BaseProxy): + _exposed_ = ('__getattribute__', 'wait', 'abort', 'reset') + def wait(self, timeout=None): + return self._callmethod('wait', (timeout,)) + def abort(self): + return self._callmethod('abort') + def reset(self): + return self._callmethod('reset') + @property + def parties(self): + return self._callmethod('__getattribute__', ('parties',)) + @property + def n_waiting(self): + return self._callmethod('__getattribute__', ('n_waiting',)) + @property + def broken(self): + return self._callmethod('__getattribute__', ('broken',)) + + class NamespaceProxy(BaseProxy): _exposed_ = ('__getattribute__', '__setattr__', '__delattr__') def __getattr__(self, key): @@ -1097,6 +1104,7 @@ SyncManager.register('BoundedSemaphore', threading.BoundedSemaphore, AcquirerProxy) SyncManager.register('Condition', threading.Condition, ConditionProxy) +SyncManager.register('Barrier', threading.Barrier, BarrierProxy) SyncManager.register('Pool', Pool, PoolProxy) SyncManager.register('list', list, ListProxy) SyncManager.register('dict', dict, DictProxy) diff --git a/Lib/multiprocessing/synchronize.py b/Lib/multiprocessing/synchronize.py --- a/Lib/multiprocessing/synchronize.py +++ b/Lib/multiprocessing/synchronize.py @@ -333,3 +333,43 @@ return False finally: self._cond.release() + +# +# Barrier +# + +class Barrier(threading.Barrier): + + def __init__(self, parties, action=None, timeout=None): + import struct + from multiprocessing.heap import BufferWrapper + wrapper = BufferWrapper(struct.calcsize('i') * 2) + cond = Condition() + self.__setstate__((parties, action, timeout, cond, wrapper)) + self._state = 0 + self._count = 0 + + def __setstate__(self, state): + (self._parties, self._action, self._timeout, + self._cond, self._wrapper) = state + self._array = self._wrapper.create_memoryview().cast('i') + + def __getstate__(self): + return (self._parties, self._action, self._timeout, + self._cond, self._wrapper) + + @property + def _state(self): + return self._array[0] + + @_state.setter + def _state(self, value): + self._array[0] = value + + @property + def _count(self): + return self._array[1] + + @_count.setter + def _count(self, value): + self._array[1] = value diff --git a/Lib/multiprocessing/util.py b/Lib/multiprocessing/util.py --- a/Lib/multiprocessing/util.py +++ b/Lib/multiprocessing/util.py @@ -269,21 +269,24 @@ def _exit_function(): global _exiting - info('process shutting down') - debug('running all "atexit" finalizers with priority >= 0') - _run_finalizers(0) + if not _exiting: + _exiting = True - for p in active_children(): - if p._daemonic: - info('calling terminate() for daemon %s', p.name) - p._popen.terminate() + info('process shutting down') + debug('running all "atexit" finalizers with priority >= 0') + _run_finalizers(0) - for p in active_children(): - info('calling join() for process %s', p.name) - p.join() + for p in active_children(): + if p._daemonic: + info('calling terminate() for daemon %s', p.name) + p._popen.terminate() - debug('running the remaining "atexit" finalizers') - _run_finalizers() + for p in active_children(): + info('calling join() for process %s', p.name) + p.join() + + debug('running the remaining "atexit" finalizers') + _run_finalizers() atexit.register(_exit_function) diff --git a/Lib/test/support.py b/Lib/test/support.py --- a/Lib/test/support.py +++ b/Lib/test/support.py @@ -1593,7 +1593,7 @@ This will typically be run on the result of the communicate() method of a subprocess.Popen object. """ - stderr = re.sub(br"\[\d+ refs\]\r?\n?$", b"", stderr).strip() + stderr = re.sub(br"\[\d+ refs\]\r?\n?", b"", stderr).strip() return stderr def args_from_interpreter_flags(): diff --git a/Lib/test/test_hmac.py b/Lib/test/test_hmac.py --- a/Lib/test/test_hmac.py +++ b/Lib/test/test_hmac.py @@ -302,40 +302,42 @@ self.assertEqual(h1.hexdigest(), h2.hexdigest(), "Hexdigest of copy doesn't match original hexdigest.") -class SecureCompareTestCase(unittest.TestCase): +class CompareDigestTestCase(unittest.TestCase): def test_compare(self): # Testing input type exception handling a, b = 100, 200 - self.assertRaises(TypeError, hmac.secure_compare, a, b) - a, b = 100, "foobar" - self.assertRaises(TypeError, hmac.secure_compare, a, b) + self.assertRaises(TypeError, hmac.compare_digest, a, b) + a, b = 100, b"foobar" + self.assertRaises(TypeError, hmac.compare_digest, a, b) + a, b = b"foobar", 200 + self.assertRaises(TypeError, hmac.compare_digest, a, b) a, b = "foobar", b"foobar" - self.assertRaises(TypeError, hmac.secure_compare, a, b) + self.assertRaises(TypeError, hmac.compare_digest, a, b) + a, b = b"foobar", "foobar" + self.assertRaises(TypeError, hmac.compare_digest, a, b) + a, b = "foobar", "foobar" + self.assertRaises(TypeError, hmac.compare_digest, a, b) + a, b = bytearray(b"foobar"), bytearray(b"foobar") + self.assertRaises(TypeError, hmac.compare_digest, a, b) - # Testing str/bytes of different lengths - a, b = "foobar", "foo" - self.assertFalse(hmac.secure_compare(a, b)) + # Testing bytes of different lengths a, b = b"foobar", b"foo" - self.assertFalse(hmac.secure_compare(a, b)) + self.assertFalse(hmac.compare_digest(a, b)) a, b = b"\xde\xad\xbe\xef", b"\xde\xad" - self.assertFalse(hmac.secure_compare(a, b)) + self.assertFalse(hmac.compare_digest(a, b)) - # Testing str/bytes of same lengths, different values - a, b = "foobar", "foobaz" - self.assertFalse(hmac.secure_compare(a, b)) + # Testing bytes of same lengths, different values a, b = b"foobar", b"foobaz" - self.assertFalse(hmac.secure_compare(a, b)) + self.assertFalse(hmac.compare_digest(a, b)) a, b = b"\xde\xad\xbe\xef", b"\xab\xad\x1d\xea" - self.assertFalse(hmac.secure_compare(a, b)) + self.assertFalse(hmac.compare_digest(a, b)) - # Testing str/bytes of same lengths, same values - a, b = "foobar", "foobar" - self.assertTrue(hmac.secure_compare(a, b)) + # Testing bytes of same lengths, same values a, b = b"foobar", b"foobar" - self.assertTrue(hmac.secure_compare(a, b)) + self.assertTrue(hmac.compare_digest(a, b)) a, b = b"\xde\xad\xbe\xef", b"\xde\xad\xbe\xef" - self.assertTrue(hmac.secure_compare(a, b)) + self.assertTrue(hmac.compare_digest(a, b)) def test_main(): support.run_unittest( @@ -343,7 +345,7 @@ ConstructorTestCase, SanityTestCase, CopyTestCase, - SecureCompareTestCase + CompareDigestTestCase ) if __name__ == "__main__": diff --git a/Lib/test/test_mailbox.py b/Lib/test/test_mailbox.py --- a/Lib/test/test_mailbox.py +++ b/Lib/test/test_mailbox.py @@ -504,6 +504,17 @@ # Write changes to disk self._test_flush_or_close(self._box.flush, True) + def test_popitem_and_flush_twice(self): + # See #15036. + self._box.add(self._template % 0) + self._box.add(self._template % 1) + self._box.flush() + + self._box.popitem() + self._box.flush() + self._box.popitem() + self._box.flush() + def test_lock_unlock(self): # Lock and unlock the mailbox self.assertFalse(os.path.exists(self._get_lock_path())) diff --git a/Lib/test/test_multiprocessing.py b/Lib/test/test_multiprocessing.py --- a/Lib/test/test_multiprocessing.py +++ b/Lib/test/test_multiprocessing.py @@ -18,6 +18,7 @@ import socket import random import logging +import struct import test.support @@ -1057,6 +1058,340 @@ self.assertEqual(wait(), True) # +# Tests for Barrier - adapted from tests in test/lock_tests.py +# + +# Many of the tests for threading.Barrier use a list as an atomic +# counter: a value is appended to increment the counter, and the +# length of the list gives the value. We use the class DummyList +# for the same purpose. + +class _DummyList(object): + + def __init__(self): + wrapper = multiprocessing.heap.BufferWrapper(struct.calcsize('i')) + lock = multiprocessing.Lock() + self.__setstate__((wrapper, lock)) + self._lengthbuf[0] = 0 + + def __setstate__(self, state): + (self._wrapper, self._lock) = state + self._lengthbuf = self._wrapper.create_memoryview().cast('i') + + def __getstate__(self): + return (self._wrapper, self._lock) + + def append(self, _): + with self._lock: + self._lengthbuf[0] += 1 + + def __len__(self): + with self._lock: + return self._lengthbuf[0] + +def _wait(): + # A crude wait/yield function not relying on synchronization primitives. + time.sleep(0.01) + + +class Bunch(object): + """ + A bunch of threads. + """ + def __init__(self, namespace, f, args, n, wait_before_exit=False): + """ + Construct a bunch of `n` threads running the same function `f`. + If `wait_before_exit` is True, the threads won't terminate until + do_finish() is called. + """ + self.f = f + self.args = args + self.n = n + self.started = namespace.DummyList() + self.finished = namespace.DummyList() + self._can_exit = namespace.Event() + if not wait_before_exit: + self._can_exit.set() + for i in range(n): + p = namespace.Process(target=self.task) + p.daemon = True + p.start() + + def task(self): + pid = os.getpid() + self.started.append(pid) + try: + self.f(*self.args) + finally: + self.finished.append(pid) + self._can_exit.wait(30) + assert self._can_exit.is_set() + + def wait_for_started(self): + while len(self.started) < self.n: + _wait() + + def wait_for_finished(self): + while len(self.finished) < self.n: + _wait() + + def do_finish(self): + self._can_exit.set() + + +class AppendTrue(object): + def __init__(self, obj): + self.obj = obj + def __call__(self): + self.obj.append(True) + + +class _TestBarrier(BaseTestCase): + """ + Tests for Barrier objects. + """ + N = 5 + defaultTimeout = 10.0 # XXX Slow Windows buildbots need generous timeout + + def setUp(self): + self.barrier = self.Barrier(self.N, timeout=self.defaultTimeout) + + def tearDown(self): + self.barrier.abort() + self.barrier = None + + def DummyList(self): + if self.TYPE == 'threads': + return [] + elif self.TYPE == 'manager': + return self.manager.list() + else: + return _DummyList() + + def run_threads(self, f, args): + b = Bunch(self, f, args, self.N-1) + f(*args) + b.wait_for_finished() + + @classmethod + def multipass(cls, barrier, results, n): + m = barrier.parties + assert m == cls.N + for i in range(n): + results[0].append(True) + assert len(results[1]) == i * m + barrier.wait() + results[1].append(True) + assert len(results[0]) == (i + 1) * m + barrier.wait() + try: + assert barrier.n_waiting == 0 + except NotImplementedError: + pass + assert not barrier.broken + + def test_barrier(self, passes=1): + """ + Test that a barrier is passed in lockstep + """ + results = [self.DummyList(), self.DummyList()] + self.run_threads(self.multipass, (self.barrier, results, passes)) + + def test_barrier_10(self): + """ + Test that a barrier works for 10 consecutive runs + """ + return self.test_barrier(10) + + @classmethod + def _test_wait_return_f(cls, barrier, queue): + res = barrier.wait() + queue.put(res) + + def test_wait_return(self): + """ + test the return value from barrier.wait + """ + queue = self.Queue() + self.run_threads(self._test_wait_return_f, (self.barrier, queue)) + results = [queue.get() for i in range(self.N)] + self.assertEqual(results.count(0), 1) + + @classmethod + def _test_action_f(cls, barrier, results): + barrier.wait() + if len(results) != 1: + raise RuntimeError + + def test_action(self): + """ + Test the 'action' callback + """ + results = self.DummyList() + barrier = self.Barrier(self.N, action=AppendTrue(results)) + self.run_threads(self._test_action_f, (barrier, results)) + self.assertEqual(len(results), 1) + + @classmethod + def _test_abort_f(cls, barrier, results1, results2): + try: + i = barrier.wait() + if i == cls.N//2: + raise RuntimeError + barrier.wait() + results1.append(True) + except threading.BrokenBarrierError: + results2.append(True) + except RuntimeError: + barrier.abort() + + def test_abort(self): + """ + Test that an abort will put the barrier in a broken state + """ + results1 = self.DummyList() + results2 = self.DummyList() + self.run_threads(self._test_abort_f, + (self.barrier, results1, results2)) + self.assertEqual(len(results1), 0) + self.assertEqual(len(results2), self.N-1) + self.assertTrue(self.barrier.broken) + + @classmethod + def _test_reset_f(cls, barrier, results1, results2, results3): + i = barrier.wait() + if i == cls.N//2: + # Wait until the other threads are all in the barrier. + while barrier.n_waiting < cls.N-1: + time.sleep(0.001) + barrier.reset() + else: + try: + barrier.wait() + results1.append(True) + except threading.BrokenBarrierError: + results2.append(True) + # Now, pass the barrier again + barrier.wait() + results3.append(True) + + def test_reset(self): + """ + Test that a 'reset' on a barrier frees the waiting threads + """ + results1 = self.DummyList() + results2 = self.DummyList() + results3 = self.DummyList() + self.run_threads(self._test_reset_f, + (self.barrier, results1, results2, results3)) + self.assertEqual(len(results1), 0) + self.assertEqual(len(results2), self.N-1) + self.assertEqual(len(results3), self.N) + + @classmethod + def _test_abort_and_reset_f(cls, barrier, barrier2, + results1, results2, results3): + try: + i = barrier.wait() + if i == cls.N//2: + raise RuntimeError + barrier.wait() + results1.append(True) + except threading.BrokenBarrierError: + results2.append(True) + except RuntimeError: + barrier.abort() + # Synchronize and reset the barrier. Must synchronize first so + # that everyone has left it when we reset, and after so that no + # one enters it before the reset. + if barrier2.wait() == cls.N//2: + barrier.reset() + barrier2.wait() + barrier.wait() + results3.append(True) + + def test_abort_and_reset(self): + """ + Test that a barrier can be reset after being broken. + """ + results1 = self.DummyList() + results2 = self.DummyList() + results3 = self.DummyList() + barrier2 = self.Barrier(self.N) + + self.run_threads(self._test_abort_and_reset_f, + (self.barrier, barrier2, results1, results2, results3)) + self.assertEqual(len(results1), 0) + self.assertEqual(len(results2), self.N-1) + self.assertEqual(len(results3), self.N) + + @classmethod + def _test_timeout_f(cls, barrier, results): + i = barrier.wait(20) + if i == cls.N//2: + # One thread is late! + time.sleep(4.0) + try: + barrier.wait(0.5) + except threading.BrokenBarrierError: + results.append(True) + + def test_timeout(self): + """ + Test wait(timeout) + """ + results = self.DummyList() + self.run_threads(self._test_timeout_f, (self.barrier, results)) + self.assertEqual(len(results), self.barrier.parties) + + @classmethod + def _test_default_timeout_f(cls, barrier, results): + i = barrier.wait(20) + if i == cls.N//2: + # One thread is later than the default timeout + time.sleep(4.0) + try: + barrier.wait() + except threading.BrokenBarrierError: + results.append(True) + + def test_default_timeout(self): + """ + Test the barrier's default timeout + """ + barrier = self.Barrier(self.N, timeout=1.0) + results = self.DummyList() + self.run_threads(self._test_default_timeout_f, (barrier, results)) + self.assertEqual(len(results), barrier.parties) + + def test_single_thread(self): + b = self.Barrier(1) + b.wait() + b.wait() + + @classmethod + def _test_thousand_f(cls, barrier, passes, conn, lock): + for i in range(passes): + barrier.wait() + with lock: + conn.send(i) + + def test_thousand(self): + if self.TYPE == 'manager': + return + passes = 1000 + lock = self.Lock() + conn, child_conn = self.Pipe(False) + for j in range(self.N): + p = self.Process(target=self._test_thousand_f, + args=(self.barrier, passes, child_conn, lock)) + p.start() + + for i in range(passes): + for j in range(self.N): + self.assertEqual(conn.recv(), i) + +# # # @@ -1485,6 +1820,11 @@ # run after all the other tests for the manager. It tests that # there have been no "reference leaks" for the manager's shared # objects. Note the comment in _TestPool.test_terminate(). + + # If some other test using ManagerMixin.manager fails, then the + # raised exception may keep alive a frame which holds a reference + # to a managed object. This will cause test_number_of_objects to + # also fail. ALLOWED_TYPES = ('manager',) def test_number_of_objects(self): @@ -1564,6 +1904,11 @@ manager.shutdown() + # If the manager process exited cleanly then the exitcode + # will be zero. Otherwise (after a short timeout) + # terminate() is used, resulting in an exitcode of -SIGTERM. + self.assertEqual(manager._process.exitcode, 0) + # # Test of connecting to a remote server and using xmlrpclib for serialization # @@ -1923,7 +2268,7 @@ class _TestListener(BaseTestCase): - ALLOWED_TYPES = ('processes') + ALLOWED_TYPES = ('processes',) def test_multiple_bind(self): for family in self.connection.families: @@ -2505,10 +2850,12 @@ result = {} glob = globals() Type = type.capitalize() + ALL_TYPES = {'processes', 'threads', 'manager'} for name in list(glob.keys()): if name.startswith('_Test'): base = glob[name] + assert set(base.ALLOWED_TYPES) <= ALL_TYPES, set(base.ALLOWED_TYPES) if type in base.ALLOWED_TYPES: newname = 'With' + Type + name[1:] class Temp(base, unittest.TestCase, Mixin): @@ -2527,7 +2874,7 @@ Process = multiprocessing.Process locals().update(get_attributes(multiprocessing, ( 'Queue', 'Lock', 'RLock', 'Semaphore', 'BoundedSemaphore', - 'Condition', 'Event', 'Value', 'Array', 'RawValue', + 'Condition', 'Event', 'Barrier', 'Value', 'Array', 'RawValue', 'RawArray', 'current_process', 'active_children', 'Pipe', 'connection', 'JoinableQueue', 'Pool' ))) @@ -2542,7 +2889,7 @@ manager = object.__new__(multiprocessing.managers.SyncManager) locals().update(get_attributes(manager, ( 'Queue', 'Lock', 'RLock', 'Semaphore', 'BoundedSemaphore', - 'Condition', 'Event', 'Value', 'Array', 'list', 'dict', + 'Condition', 'Event', 'Barrier', 'Value', 'Array', 'list', 'dict', 'Namespace', 'JoinableQueue', 'Pool' ))) @@ -2555,7 +2902,7 @@ Process = multiprocessing.dummy.Process locals().update(get_attributes(multiprocessing.dummy, ( 'Queue', 'Lock', 'RLock', 'Semaphore', 'BoundedSemaphore', - 'Condition', 'Event', 'Value', 'Array', 'current_process', + 'Condition', 'Event', 'Barrier', 'Value', 'Array', 'current_process', 'active_children', 'Pipe', 'connection', 'dict', 'list', 'Namespace', 'JoinableQueue', 'Pool' ))) diff --git a/Lib/test/test_structseq.py b/Lib/test/test_structseq.py --- a/Lib/test/test_structseq.py +++ b/Lib/test/test_structseq.py @@ -78,8 +78,9 @@ def test_fields(self): t = time.gmtime() - self.assertEqual(len(t), t.n_fields) - self.assertEqual(t.n_fields, t.n_sequence_fields+t.n_unnamed_fields) + self.assertEqual(len(t), t.n_sequence_fields) + self.assertEqual(t.n_unnamed_fields, 0) + self.assertEqual(t.n_fields, time._STRUCT_TM_ITEMS) def test_constructor(self): t = time.struct_time diff --git a/Lib/test/test_time.py b/Lib/test/test_time.py --- a/Lib/test/test_time.py +++ b/Lib/test/test_time.py @@ -31,15 +31,14 @@ time.time() info = time.get_clock_info('time') self.assertFalse(info.monotonic) - if sys.platform != 'win32': - self.assertTrue(info.adjusted) + self.assertTrue(info.adjustable) def test_clock(self): time.clock() info = time.get_clock_info('clock') self.assertTrue(info.monotonic) - self.assertFalse(info.adjusted) + self.assertFalse(info.adjustable) @unittest.skipUnless(hasattr(time, 'clock_gettime'), 'need time.clock_gettime()') @@ -371,10 +370,7 @@ info = time.get_clock_info('monotonic') self.assertTrue(info.monotonic) - if sys.platform == 'linux': - self.assertTrue(info.adjusted) - else: - self.assertFalse(info.adjusted) + self.assertFalse(info.adjustable) def test_perf_counter(self): time.perf_counter() @@ -390,7 +386,7 @@ info = time.get_clock_info('process_time') self.assertTrue(info.monotonic) - self.assertFalse(info.adjusted) + self.assertFalse(info.adjustable) @unittest.skipUnless(hasattr(time, 'monotonic'), 'need time.monotonic') @@ -441,7 +437,7 @@ # 0.0 < resolution <= 1.0 self.assertGreater(info.resolution, 0.0) self.assertLessEqual(info.resolution, 1.0) - self.assertIsInstance(info.adjusted, bool) + self.assertIsInstance(info.adjustable, bool) self.assertRaises(ValueError, time.get_clock_info, 'xxx') @@ -624,7 +620,58 @@ for invalid in self.invalid_values: self.assertRaises(OverflowError, pytime_object_to_timespec, invalid) + @unittest.skipUnless(time._STRUCT_TM_ITEMS == 11, "needs tm_zone support") + def test_localtime_timezone(self): + # Get the localtime and examine it for the offset and zone. + lt = time.localtime() + self.assertTrue(hasattr(lt, "tm_gmtoff")) + self.assertTrue(hasattr(lt, "tm_zone")) + + # See if the offset and zone are similar to the module + # attributes. + if lt.tm_gmtoff is None: + self.assertTrue(not hasattr(time, "timezone")) + else: + self.assertEqual(lt.tm_gmtoff, -[time.timezone, time.altzone][lt.tm_isdst]) + if lt.tm_zone is None: + self.assertTrue(not hasattr(time, "tzname")) + else: + self.assertEqual(lt.tm_zone, time.tzname[lt.tm_isdst]) + + # Try and make UNIX times from the localtime and a 9-tuple + # created from the localtime. Test to see that the times are + # the same. + t = time.mktime(lt); t9 = time.mktime(lt[:9]) + self.assertEqual(t, t9) + + # Make localtimes from the UNIX times and compare them to + # the original localtime, thus making a round trip. + new_lt = time.localtime(t); new_lt9 = time.localtime(t9) + self.assertEqual(new_lt, lt) + self.assertEqual(new_lt.tm_gmtoff, lt.tm_gmtoff) + self.assertEqual(new_lt.tm_zone, lt.tm_zone) + self.assertEqual(new_lt9, lt) + self.assertEqual(new_lt.tm_gmtoff, lt.tm_gmtoff) + self.assertEqual(new_lt9.tm_zone, lt.tm_zone) + + @unittest.skipUnless(time._STRUCT_TM_ITEMS == 11, "needs tm_zone support") + def test_strptime_timezone(self): + t = time.strptime("UTC", "%Z") + self.assertEqual(t.tm_zone, 'UTC') + t = time.strptime("+0500", "%z") + self.assertEqual(t.tm_gmtoff, 5 * 3600) + + @unittest.skipUnless(time._STRUCT_TM_ITEMS == 11, "needs tm_zone support") + def test_short_times(self): + + import pickle + + # Load a short time structure using pickle. + st = b"ctime\nstruct_time\np0\n((I2007\nI8\nI11\nI1\nI24\nI49\nI5\nI223\nI1\ntp1\n(dp2\ntp3\nRp4\n." + lt = pickle.loads(st) + self.assertIs(lt.tm_gmtoff, None) + self.assertIs(lt.tm_zone, None) def test_main(): support.run_unittest( diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py --- a/Lib/test/test_xml_etree.py +++ b/Lib/test/test_xml_etree.py @@ -23,7 +23,8 @@ from test import support from test.support import findfile, import_fresh_module, gc_collect -pyET = import_fresh_module('xml.etree.ElementTree', blocked=['_elementtree']) +pyET = None +ET = None SIMPLE_XMLFILE = findfile("simple.xml", subdir="xmltestdata") try: @@ -209,10 +210,8 @@ These methods return an iterable. See bug 6472. - >>> check_method(element.iter("tag").__next__) >>> check_method(element.iterfind("tag").__next__) >>> check_method(element.iterfind("*").__next__) - >>> check_method(tree.iter("tag").__next__) >>> check_method(tree.iterfind("tag").__next__) >>> check_method(tree.iterfind("*").__next__) @@ -291,42 +290,6 @@ 'hello' """ -# Only with Python implementation -def simplefind(): - """ - Test find methods using the elementpath fallback. - - >>> ElementTree = pyET - - >>> CurrentElementPath = ElementTree.ElementPath - >>> ElementTree.ElementPath = ElementTree._SimpleElementPath() - >>> elem = ElementTree.XML(SAMPLE_XML) - >>> elem.find("tag").tag - 'tag' - >>> ElementTree.ElementTree(elem).find("tag").tag - 'tag' - >>> elem.findtext("tag") - 'text' - >>> elem.findtext("tog") - >>> elem.findtext("tog", "default") - 'default' - >>> ElementTree.ElementTree(elem).findtext("tag") - 'text' - >>> summarize_list(elem.findall("tag")) - ['tag', 'tag'] - >>> summarize_list(elem.findall(".//tag")) - ['tag', 'tag', 'tag'] - - Path syntax doesn't work in this case. - - >>> elem.find("section/tag") - >>> elem.findtext("section/tag") - >>> summarize_list(elem.findall("section/tag")) - [] - - >>> ElementTree.ElementPath = CurrentElementPath - """ - def find(): """ Test find methods (including xpath syntax). @@ -1002,36 +965,6 @@ '1 < 2\n' """ -def iterators(): - """ - Test iterators. - - >>> e = ET.XML("this is a paragraph...") - >>> summarize_list(e.iter()) - ['html', 'body', 'i'] - >>> summarize_list(e.find("body").iter()) - ['body', 'i'] - >>> summarize(next(e.iter())) - 'html' - >>> "".join(e.itertext()) - 'this is a paragraph...' - >>> "".join(e.find("body").itertext()) - 'this is a paragraph.' - >>> next(e.itertext()) - 'this is a ' - - Method iterparse should return an iterator. See bug 6472. - - >>> sourcefile = serialize(e, to_string=False) - >>> next(ET.iterparse(sourcefile)) # doctest: +ELLIPSIS - ('end', ) - - >>> tree = ET.ElementTree(None) - >>> tree.iter() - Traceback (most recent call last): - AttributeError: 'NoneType' object has no attribute 'iter' - """ - ENTITY_XML = """\ @@ -1339,6 +1272,7 @@ """.format(html.escape(SIMPLE_XMLFILE, True)) + def xinclude_loader(href, parse="xml", encoding=None): try: data = XINCLUDE[href] @@ -1411,22 +1345,6 @@ >>> # print(serialize(document)) # C5 """ -def xinclude_default(): - """ - >>> from xml.etree import ElementInclude - - >>> document = xinclude_loader("default.xml") - >>> ElementInclude.include(document) - >>> print(serialize(document)) # default - -

Example.

- - text - texttail - - -
- """ # # badly formatted xi:include tags @@ -1917,9 +1835,8 @@ self.assertIsInstance(ET.QName, type) self.assertIsInstance(ET.ElementTree, type) self.assertIsInstance(ET.Element, type) - # XXX issue 14128 with C ElementTree - # self.assertIsInstance(ET.TreeBuilder, type) - # self.assertIsInstance(ET.XMLParser, type) + self.assertIsInstance(ET.TreeBuilder, type) + self.assertIsInstance(ET.XMLParser, type) def test_Element_subclass_trivial(self): class MyElement(ET.Element): @@ -1953,6 +1870,73 @@ self.assertEqual(mye.newmethod(), 'joe') +class ElementIterTest(unittest.TestCase): + def _ilist(self, elem, tag=None): + return summarize_list(elem.iter(tag)) + + def test_basic(self): + doc = ET.XML("this is a paragraph...") + self.assertEqual(self._ilist(doc), ['html', 'body', 'i']) + self.assertEqual(self._ilist(doc.find('body')), ['body', 'i']) + self.assertEqual(next(doc.iter()).tag, 'html') + self.assertEqual(''.join(doc.itertext()), 'this is a paragraph...') + self.assertEqual(''.join(doc.find('body').itertext()), + 'this is a paragraph.') + self.assertEqual(next(doc.itertext()), 'this is a ') + + # iterparse should return an iterator + sourcefile = serialize(doc, to_string=False) + self.assertEqual(next(ET.iterparse(sourcefile))[0], 'end') + + tree = ET.ElementTree(None) + self.assertRaises(AttributeError, tree.iter) + + def test_corners(self): + # single root, no subelements + a = ET.Element('a') + self.assertEqual(self._ilist(a), ['a']) + + # one child + b = ET.SubElement(a, 'b') + self.assertEqual(self._ilist(a), ['a', 'b']) + + # one child and one grandchild + c = ET.SubElement(b, 'c') + self.assertEqual(self._ilist(a), ['a', 'b', 'c']) + + # two children, only first with grandchild + d = ET.SubElement(a, 'd') + self.assertEqual(self._ilist(a), ['a', 'b', 'c', 'd']) + + # replace first child by second + a[0] = a[1] + del a[1] + self.assertEqual(self._ilist(a), ['a', 'd']) + + def test_iter_by_tag(self): + doc = ET.XML(''' + + + bedroom1 + bedroom2 + + nothing here + + + bedroom8 + + ''') + + self.assertEqual(self._ilist(doc, 'room'), ['room'] * 3) + self.assertEqual(self._ilist(doc, 'house'), ['house'] * 2) + + # make sure both tag=None and tag='*' return all tags + all_tags = ['document', 'house', 'room', 'room', + 'shed', 'house', 'room'] + self.assertEqual(self._ilist(doc), all_tags) + self.assertEqual(self._ilist(doc, '*'), all_tags) + + class TreeBuilderTest(unittest.TestCase): sample1 = (' +

Example.

+ + text + texttail + + +''') + + class XMLParserTest(unittest.TestCase): sample1 = '22' sample2 = ('>> cElementTree = cET - >>> e = cElementTree.Element('a') - >>> getattr(e, '\uD800') # doctest: +ELLIPSIS - Traceback (most recent call last): - ... - UnicodeEncodeError: ... - - >>> p = cElementTree.XMLParser() - >>> p.version.split()[0] - 'Expat' - >>> getattr(p, '\uD800') - Traceback (most recent call last): - ... - AttributeError: 'XMLParser' object has no attribute '\ud800' - """ - - class MiscTests(unittest.TestCase): # Issue #8651. @support.bigmemtest(size=support._2G + 100, memuse=1) @@ -46,6 +21,7 @@ finally: data = None + @unittest.skipUnless(cET, 'requires _elementtree') class TestAliasWorking(unittest.TestCase): # Test that the cET alias module is alive @@ -53,6 +29,7 @@ e = cET_alias.Element('foo') self.assertEqual(e.tag, 'foo') + @unittest.skipUnless(cET, 'requires _elementtree') class TestAcceleratorImported(unittest.TestCase): # Test that the C accelerator was imported, as expected @@ -67,7 +44,6 @@ from test import test_xml_etree, test_xml_etree_c # Run the tests specific to the C implementation - support.run_doctest(test_xml_etree_c, verbosity=True) support.run_unittest( MiscTests, TestAliasWorking, diff --git a/Lib/xml/etree/ElementTree.py b/Lib/xml/etree/ElementTree.py --- a/Lib/xml/etree/ElementTree.py +++ b/Lib/xml/etree/ElementTree.py @@ -101,32 +101,8 @@ import re import warnings -class _SimpleElementPath: - # emulate pre-1.2 find/findtext/findall behaviour - def find(self, element, tag, namespaces=None): - for elem in element: - if elem.tag == tag: - return elem - return None - def findtext(self, element, tag, default=None, namespaces=None): - elem = self.find(element, tag) - if elem is None: - return default - return elem.text or "" - def iterfind(self, element, tag, namespaces=None): - if tag[:3] == ".//": - for elem in element.iter(tag[3:]): - yield elem - for elem in element: - if elem.tag == tag: - yield elem - def findall(self, element, tag, namespaces=None): - return list(self.iterfind(element, tag, namespaces)) +from . import ElementPath -try: - from . import ElementPath -except ImportError: - ElementPath = _SimpleElementPath() ## # Parser error. This is a subclass of SyntaxError. @@ -916,11 +892,7 @@ _raise_serialization_error(qname) # populate qname and namespaces table - try: - iterate = elem.iter - except AttributeError: - iterate = elem.getiterator # cET compatibility - for elem in iterate(): + for elem in elem.iter(): tag = elem.tag if isinstance(tag, QName): if tag.text not in qnames: diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,9 @@ Core and Builtins ----------------- +- Issue #15026: utf-16 encoding is now significantly faster (up to 10x). + Patch by Serhiy Storchaka. + - Issue #11022: open() and io.TextIOWrapper are now calling locale.getpreferredencoding(False) instead of locale.getpreferredencoding() in text mode if the encoding is not specified. Don't change temporary the @@ -21,6 +24,32 @@ Library ------- +- Issue #15036: Allow removing or changing multiple items in + single-file mailboxes (mbox, MMDF, Babyl) flushing the mailbox + between the changes. + +- Issue #14059: Implement multiprocessing.Barrier. + +- Issue #15061: The inappropriately named hmac.secure_compare has been + renamed to hmac.compare_digest, restricted to operating on bytes inputs + only and had its documentation updated to more accurately reflect both its + intent and its limitations + +- Issue #13841: Make child processes exit using sys.exit() on Windows. + +- Issue #14936: curses_panel was converted to PEP 3121 and PEP 384 API. + Patch by Robin Schreiber. + +- Issue #1667546: On platforms supporting tm_zone and tm_gmtoff fields + in struct tm, time.struct_time objects returned by time.gmtime(), + time.localtime() and time.strptime() functions now have tm_zone and + tm_gmtoff attributes. Original patch by Paul Boddie. + +- Rename adjusted attribute to adjustable in time.get_clock_info() result. + +- Issue #3518: Remove references to non-existent BaseManager.from_address() + method. + - Issue #13857: Added textwrap.indent() function (initial patch by Ezra Berch) diff --git a/Modules/_curses_panel.c b/Modules/_curses_panel.c --- a/Modules/_curses_panel.c +++ b/Modules/_curses_panel.c @@ -16,8 +16,37 @@ #include -static PyObject *PyCursesError; +typedef struct { + PyObject *PyCursesError; + PyObject *PyCursesPanel_Type; +} _curses_panelstate; +#define _curses_panelstate(o) ((_curses_panelstate *)PyModule_GetState(o)) + +static int +_curses_panel_clear(PyObject *m) +{ + Py_CLEAR(_curses_panelstate(m)->PyCursesError); + return 0; +} + +static int +_curses_panel_traverse(PyObject *m, visitproc visit, void *arg) +{ + Py_VISIT(_curses_panelstate(m)->PyCursesError); + return 0; +} + +static void +_curses_panel_free(void *m) +{ + _curses_panel_clear((PyObject *) m); +} + +static struct PyModuleDef _curses_panelmodule; + +#define _curses_panelstate_global \ +((_curses_panelstate *) PyModule_GetState(PyState_FindModule(&_curses_panelmodule))) /* Utility Functions */ @@ -34,9 +63,9 @@ return Py_None; } else { if (fname == NULL) { - PyErr_SetString(PyCursesError, catchall_ERR); + PyErr_SetString(_curses_panelstate_global->PyCursesError, catchall_ERR); } else { - PyErr_Format(PyCursesError, "%s() returned ERR", fname); + PyErr_Format(_curses_panelstate_global->PyCursesError, "%s() returned ERR", fname); } return NULL; } @@ -54,9 +83,8 @@ PyCursesWindowObject *wo; /* for reference counts */ } PyCursesPanelObject; -PyTypeObject PyCursesPanel_Type; - -#define PyCursesPanel_Check(v) (Py_TYPE(v) == &PyCursesPanel_Type) +#define PyCursesPanel_Check(v) \ + (Py_TYPE(v) == _curses_panelstate_global->PyCursesPanel_Type) /* Some helper functions. The problem is that there's always a window associated with a panel. To ensure that Python's GC doesn't pull @@ -175,7 +203,8 @@ { PyCursesPanelObject *po; - po = PyObject_NEW(PyCursesPanelObject, &PyCursesPanel_Type); + po = PyObject_NEW(PyCursesPanelObject, + (PyTypeObject *)(_curses_panelstate_global)->PyCursesPanel_Type); if (po == NULL) return NULL; po->pan = pan; if (insert_lop(po) < 0) { @@ -280,7 +309,7 @@ rtn = replace_panel(self->pan, temp->win); if (rtn == ERR) { - PyErr_SetString(PyCursesError, "replace_panel() returned ERR"); + PyErr_SetString(_curses_panelstate_global->PyCursesError, "replace_panel() returned ERR"); return NULL; } Py_DECREF(po->wo); @@ -305,7 +334,7 @@ PyCursesInitialised; obj = (PyObject *) panel_userptr(self->pan); if (obj == NULL) { - PyErr_SetString(PyCursesError, "no userptr set"); + PyErr_SetString(_curses_panelstate_global->PyCursesError, "no userptr set"); return NULL; } @@ -334,36 +363,18 @@ /* -------------------------------------------------------*/ -PyTypeObject PyCursesPanel_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "_curses_panel.curses panel", /*tp_name*/ - sizeof(PyCursesPanelObject), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - /* methods */ - (destructor)PyCursesPanel_Dealloc, /*tp_dealloc*/ - 0, /*tp_print*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_reserved*/ - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash*/ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT, /*tp_flags*/ - 0, /*tp_doc*/ - 0, /*tp_traverse*/ - 0, /*tp_clear*/ - 0, /*tp_richcompare*/ - 0, /*tp_weaklistoffset*/ - 0, /*tp_iter*/ - 0, /*tp_iternext*/ - PyCursesPanel_Methods, /*tp_methods*/ +static PyType_Slot PyCursesPanel_Type_slots[] = { + {Py_tp_dealloc, PyCursesPanel_Dealloc}, + {Py_tp_methods, PyCursesPanel_Methods}, + {0, 0}, +}; + +static PyType_Spec PyCursesPanel_Type_spec = { + "_curses_panel.curses panel", + sizeof(PyCursesPanelObject), + 0, + Py_TPFLAGS_DEFAULT, + PyCursesPanel_Type_slots }; /* Wrapper for panel_above(NULL). This function returns the bottom @@ -405,7 +416,7 @@ return NULL; pan = new_panel(win->win); if (pan == NULL) { - PyErr_SetString(PyCursesError, catchall_NULL); + PyErr_SetString(_curses_panelstate_global->PyCursesError, catchall_NULL); return NULL; } return (PyObject *)PyCursesPanel_New(pan, win); @@ -467,12 +478,12 @@ PyModuleDef_HEAD_INIT, "_curses_panel", NULL, - -1, + sizeof(_curses_panelstate), PyCurses_methods, NULL, - NULL, - NULL, - NULL + _curses_panel_traverse, + _curses_panel_clear, + _curses_panel_free }; PyMODINIT_FUNC @@ -480,21 +491,23 @@ { PyObject *m, *d, *v; + /* Create the module and add the functions */ + m = PyModule_Create(&_curses_panelmodule); + if (m == NULL) + goto fail; + d = PyModule_GetDict(m); + /* Initialize object type */ - if (PyType_Ready(&PyCursesPanel_Type) < 0) - return NULL; + _curses_panelstate(m)->PyCursesPanel_Type = \ + PyType_FromSpec(&PyCursesPanel_Type_spec); + if (_curses_panelstate(m)->PyCursesPanel_Type == NULL) + goto fail; import_curses(); - /* Create the module and add the functions */ - m = PyModule_Create(&_curses_panelmodule); - if (m == NULL) - return NULL; - d = PyModule_GetDict(m); - /* For exception _curses_panel.error */ - PyCursesError = PyErr_NewException("_curses_panel.error", NULL, NULL); - PyDict_SetItemString(d, "error", PyCursesError); + _curses_panelstate(m)->PyCursesError = PyErr_NewException("_curses_panel.error", NULL, NULL); + PyDict_SetItemString(d, "error", _curses_panelstate(m)->PyCursesError); /* Make the version available */ v = PyUnicode_FromString(PyCursesVersion); @@ -502,4 +515,7 @@ PyDict_SetItemString(d, "__version__", v); Py_DECREF(v); return m; + fail: + Py_XDECREF(m); + return NULL; } diff --git a/Modules/_decimal/libmpdec/mpdecimal.c b/Modules/_decimal/libmpdec/mpdecimal.c --- a/Modules/_decimal/libmpdec/mpdecimal.c +++ b/Modules/_decimal/libmpdec/mpdecimal.c @@ -107,8 +107,9 @@ const mpd_context_t *ctx, uint32_t *status); static void _mpd_base_ndivmod(mpd_t *q, mpd_t *r, const mpd_t *a, const mpd_t *b, uint32_t *status); -static inline void _mpd_qpow_uint(mpd_t *result, mpd_t *base, mpd_uint_t exp, - uint8_t resultsign, const mpd_context_t *ctx, uint32_t *status); +static inline void _mpd_qpow_uint(mpd_t *result, const mpd_t *base, + mpd_uint_t exp, uint8_t resultsign, + const mpd_context_t *ctx, uint32_t *status); mpd_uint_t mpd_qsshiftr(mpd_t *result, const mpd_t *a, mpd_ssize_t n); @@ -5841,12 +5842,12 @@ } /* - * Internal function: Integer power with mpd_uint_t exponent, base is modified! - * Function can fail with MPD_Malloc_error. + * Internal function: Integer power with mpd_uint_t exponent. The function + * can fail with MPD_Malloc_error. */ static inline void -_mpd_qpow_uint(mpd_t *result, mpd_t *base, mpd_uint_t exp, uint8_t resultsign, - const mpd_context_t *ctx, uint32_t *status) +_mpd_qpow_uint(mpd_t *result, const mpd_t *base, mpd_uint_t exp, + uint8_t resultsign, const mpd_context_t *ctx, uint32_t *status) { uint32_t workstatus = 0; mpd_uint_t n; @@ -5866,7 +5867,8 @@ if (exp & n) { mpd_qmul(result, result, base, ctx, &workstatus); } - if (workstatus & (MPD_Overflow|MPD_Clamped)) { + if (mpd_isspecial(result) || + (mpd_iszerocoeff(result) && (workstatus & MPD_Clamped))) { break; } } diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c --- a/Modules/_elementtree.c +++ b/Modules/_elementtree.c @@ -103,8 +103,6 @@ /* glue functions (see the init function for details) */ static PyObject* elementtree_parseerror_obj; static PyObject* elementtree_deepcopy_obj; -static PyObject* elementtree_iter_obj; -static PyObject* elementtree_itertext_obj; static PyObject* elementpath_obj; /* helpers */ @@ -1109,67 +1107,32 @@ return list; } -static PyObject* -element_iter(ElementObject* self, PyObject* args) + +static PyObject * +create_elementiter(ElementObject *self, PyObject *tag, int gettext); + + +static PyObject * +element_iter(ElementObject *self, PyObject *args) { - PyObject* result; - PyObject* tag = Py_None; if (!PyArg_ParseTuple(args, "|O:iter", &tag)) return NULL; - if (!elementtree_iter_obj) { - PyErr_SetString( - PyExc_RuntimeError, - "iter helper not found" - ); - return NULL; - } - - args = PyTuple_New(2); - if (!args) - return NULL; - - Py_INCREF(self); PyTuple_SET_ITEM(args, 0, (PyObject*) self); - Py_INCREF(tag); PyTuple_SET_ITEM(args, 1, (PyObject*) tag); - - result = PyObject_CallObject(elementtree_iter_obj, args); - - Py_DECREF(args); - - return result; + return create_elementiter(self, tag, 0); } static PyObject* element_itertext(ElementObject* self, PyObject* args) { - PyObject* result; - if (!PyArg_ParseTuple(args, ":itertext")) return NULL; - if (!elementtree_itertext_obj) { - PyErr_SetString( - PyExc_RuntimeError, - "itertext helper not found" - ); - return NULL; - } - - args = PyTuple_New(1); - if (!args) - return NULL; - - Py_INCREF(self); PyTuple_SET_ITEM(args, 0, (PyObject*) self); - - result = PyObject_CallObject(elementtree_itertext_obj, args); - - Py_DECREF(args); - - return result; + return create_elementiter(self, Py_None, 1); } + static PyObject* element_getitem(PyObject* self_, Py_ssize_t index) { @@ -1790,6 +1753,269 @@ 0, /* tp_free */ }; +/******************************* Element iterator ****************************/ + +/* ElementIterObject represents the iteration state over an XML element in + * pre-order traversal. To keep track of which sub-element should be returned + * next, a stack of parents is maintained. This is a standard stack-based + * iterative pre-order traversal of a tree. + * The stack is managed using a single-linked list starting at parent_stack. + * Each stack node contains the saved parent to which we should return after + * the current one is exhausted, and the next child to examine in that parent. + */ +typedef struct ParentLocator_t { + ElementObject *parent; + Py_ssize_t child_index; + struct ParentLocator_t *next; +} ParentLocator; + +typedef struct { + PyObject_HEAD + ParentLocator *parent_stack; + ElementObject *root_element; + PyObject *sought_tag; + int root_done; + int gettext; +} ElementIterObject; + + +static void +elementiter_dealloc(ElementIterObject *it) +{ + ParentLocator *p = it->parent_stack; + while (p) { + ParentLocator *temp = p; + Py_XDECREF(p->parent); + p = p->next; + PyObject_Free(temp); + } + + Py_XDECREF(it->sought_tag); + Py_XDECREF(it->root_element); + + PyObject_GC_UnTrack(it); + PyObject_GC_Del(it); +} + +static int +elementiter_traverse(ElementIterObject *it, visitproc visit, void *arg) +{ + ParentLocator *p = it->parent_stack; + while (p) { + Py_VISIT(p->parent); + p = p->next; + } + + Py_VISIT(it->root_element); + Py_VISIT(it->sought_tag); + return 0; +} + +/* Helper function for elementiter_next. Add a new parent to the parent stack. + */ +static ParentLocator * +parent_stack_push_new(ParentLocator *stack, ElementObject *parent) +{ + ParentLocator *new_node = PyObject_Malloc(sizeof(ParentLocator)); + if (new_node) { + new_node->parent = parent; + Py_INCREF(parent); + new_node->child_index = 0; + new_node->next = stack; + } + return new_node; +} + +static PyObject * +elementiter_next(ElementIterObject *it) +{ + /* Sub-element iterator. + * + * A short note on gettext: this function serves both the iter() and + * itertext() methods to avoid code duplication. However, there are a few + * small differences in the way these iterations work. Namely: + * - itertext() only yields text from nodes that have it, and continues + * iterating when a node doesn't have text (so it doesn't return any + * node like iter()) + * - itertext() also has to handle tail, after finishing with all the + * children of a node. + */ + ElementObject *cur_parent; + Py_ssize_t child_index; + + while (1) { + /* Handle the case reached in the beginning and end of iteration, where + * the parent stack is empty. The root_done flag gives us indication + * whether we've just started iterating (so root_done is 0), in which + * case the root is returned. If root_done is 1 and we're here, the + * iterator is exhausted. + */ + if (!it->parent_stack->parent) { + if (it->root_done) { + PyErr_SetNone(PyExc_StopIteration); + return NULL; + } else { + it->parent_stack = parent_stack_push_new(it->parent_stack, + it->root_element); + if (!it->parent_stack) { + PyErr_NoMemory(); + return NULL; + } + + it->root_done = 1; + if (it->sought_tag == Py_None || + PyObject_RichCompareBool(it->root_element->tag, + it->sought_tag, Py_EQ) == 1) { + if (it->gettext) { + PyObject *text = JOIN_OBJ(it->root_element->text); + if (PyObject_IsTrue(text)) { + Py_INCREF(text); + return text; + } + } else { + Py_INCREF(it->root_element); + return (PyObject *)it->root_element; + } + } + } + } + + /* See if there are children left to traverse in the current parent. If + * yes, visit the next child. If not, pop the stack and try again. + */ + cur_parent = it->parent_stack->parent; + child_index = it->parent_stack->child_index; + if (cur_parent->extra && child_index < cur_parent->extra->length) { + ElementObject *child = (ElementObject *) + cur_parent->extra->children[child_index]; + it->parent_stack->child_index++; + it->parent_stack = parent_stack_push_new(it->parent_stack, + child); + if (!it->parent_stack) { + PyErr_NoMemory(); + return NULL; + } + + if (it->gettext) { + PyObject *text = JOIN_OBJ(child->text); + if (PyObject_IsTrue(text)) { + Py_INCREF(text); + return text; + } + } else if (it->sought_tag == Py_None || + PyObject_RichCompareBool(child->tag, + it->sought_tag, Py_EQ) == 1) { + Py_INCREF(child); + return (PyObject *)child; + } + else + continue; + } + else { + PyObject *tail = it->gettext ? JOIN_OBJ(cur_parent->tail) : Py_None; + ParentLocator *next = it->parent_stack->next; + Py_XDECREF(it->parent_stack->parent); + PyObject_Free(it->parent_stack); + it->parent_stack = next; + + /* Note that extra condition on it->parent_stack->parent here; + * this is because itertext() is supposed to only return *inner* + * text, not text following the element it began iteration with. + */ + if (it->parent_stack->parent && PyObject_IsTrue(tail)) { + Py_INCREF(tail); + return tail; + } + } + } + + return NULL; +} + + +static PyTypeObject ElementIter_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "_elementtree._element_iterator", /* tp_name */ + sizeof(ElementIterObject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)elementiter_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ + 0, /* tp_doc */ + (traverseproc)elementiter_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)elementiter_next, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + + +static PyObject * +create_elementiter(ElementObject *self, PyObject *tag, int gettext) +{ + ElementIterObject *it; + PyObject *star = NULL; + + it = PyObject_GC_New(ElementIterObject, &ElementIter_Type); + if (!it) + return NULL; + if (!(it->parent_stack = PyObject_Malloc(sizeof(ParentLocator)))) { + PyObject_GC_Del(it); + return NULL; + } + + it->parent_stack->parent = NULL; + it->parent_stack->child_index = 0; + it->parent_stack->next = NULL; + + if (PyUnicode_Check(tag)) + star = PyUnicode_FromString("*"); + else if (PyBytes_Check(tag)) + star = PyBytes_FromString("*"); + + if (star && PyObject_RichCompareBool(tag, star, Py_EQ) == 1) + tag = Py_None; + + Py_XDECREF(star); + it->sought_tag = tag; + it->root_done = 0; + it->gettext = gettext;