From python-checkins at python.org Sat Jun 1 00:03:29 2013 From: python-checkins at python.org (brett.cannon) Date: Sat, 1 Jun 2013 00:03:29 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Add_a_reference_to_module?= =?utf-8?q?=5Fto=5Fload?= Message-ID: <3bMfmx0hMLz7LjN@mail.python.org> http://hg.python.org/cpython/rev/ea3af5f5e6bc changeset: 83996:ea3af5f5e6bc parent: 83994:ebd11a19d830 user: Brett Cannon date: Fri May 31 18:00:56 2013 -0400 summary: Add a reference to module_to_load files: Doc/library/importlib.rst | 3 +-- 1 files changed, 1 insertions(+), 2 deletions(-) diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst --- a/Doc/library/importlib.rst +++ b/Doc/library/importlib.rst @@ -242,8 +242,7 @@ from the import. If the loader inserted a module and the load fails, it must be removed by the loader from :data:`sys.modules`; modules already in :data:`sys.modules` before the loader began execution should be left - alone. The :func:`importlib.util.module_for_loader` decorator handles - all of these details. + alone (see :func:`importlib.util.module_to_load`). The loader should set several attributes on the module. (Note that some of these attributes can change when a module is -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 1 00:03:30 2013 From: python-checkins at python.org (brett.cannon) Date: Sat, 1 Jun 2013 00:03:30 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Update_What=27s_New_for_im?= =?utf-8?q?portlib=2Eutil=2Emodule=5Fto=5Fload_name_change?= Message-ID: <3bMfmy2Rb2z7M05@mail.python.org> http://hg.python.org/cpython/rev/16598c43d3e8 changeset: 83997:16598c43d3e8 user: Brett Cannon date: Fri May 31 18:02:11 2013 -0400 summary: Update What's New for importlib.util.module_to_load name change files: Doc/whatsnew/3.4.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/whatsnew/3.4.rst b/Doc/whatsnew/3.4.rst --- a/Doc/whatsnew/3.4.rst +++ b/Doc/whatsnew/3.4.rst @@ -255,4 +255,4 @@ * :meth:`importlib.util.module_for_loader` now sets ``__loader__`` and ``__package__`` unconditionally to properly support reloading. If this is not desired then you will need to set these attributes manually. You can use - :class:`importlib.util.ModuleManager` for module management. + :func:`importlib.util.module_to_load` for module management. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 1 00:11:26 2013 From: python-checkins at python.org (brett.cannon) Date: Sat, 1 Jun 2013 00:11:26 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Add_a_reset=5Fname_argumen?= =?utf-8?q?t_to_importlib=2Eutil=2Emodule=5Fto=5Fload_in_order_to?= Message-ID: <3bMfy630Xkz7LpQ@mail.python.org> http://hg.python.org/cpython/rev/39cc1b04713e changeset: 83998:39cc1b04713e user: Brett Cannon date: Fri May 31 18:11:17 2013 -0400 summary: Add a reset_name argument to importlib.util.module_to_load in order to control whether to reset the module's __name__ attribute in case a reload is being done. files: Doc/library/importlib.rst | 6 +++++- Lib/importlib/_bootstrap.py | 14 +++++++++++++- Lib/test/test_importlib/test_util.py | 12 ++++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst --- a/Doc/library/importlib.rst +++ b/Doc/library/importlib.rst @@ -788,7 +788,7 @@ .. versionadded:: 3.3 -.. function:: module_to_load(name) +.. function:: module_to_load(name, *, reset_name=True) Returns a :term:`context manager` which provides the module to load. The module will either come from :attr:`sys.modules` in the case of reloading or @@ -796,6 +796,10 @@ :attr:`sys.modules` occurs if the module was new and an exception was raised. + If **reset_name** is true and the module requested is being reloaded then + the module's :attr:`__name__` attribute will + be reset to **name**, else it will be left untouched. + .. versionadded:: 3.4 .. decorator:: module_for_loader diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py --- a/Lib/importlib/_bootstrap.py +++ b/Lib/importlib/_bootstrap.py @@ -493,8 +493,14 @@ """ - def __init__(self, name): + def __init__(self, name, *, reset_name=True): + """Prepare the context manager. + + The reset_name argument specifies whether to unconditionally reset + the __name__ attribute if the module is found to be a reload. + """ self._name = name + self._reset_name = reset_name def __enter__(self): self._module = sys.modules.get(self._name) @@ -508,6 +514,12 @@ # (otherwise an optimization shortcut in import.c becomes wrong) self._module.__initializing__ = True sys.modules[self._name] = self._module + elif self._reset_name: + try: + self._module.__name__ = self._name + except AttributeError: + pass + return self._module def __exit__(self, *args): diff --git a/Lib/test/test_importlib/test_util.py b/Lib/test/test_importlib/test_util.py --- a/Lib/test/test_importlib/test_util.py +++ b/Lib/test/test_importlib/test_util.py @@ -55,6 +55,18 @@ else: self.fail('importlib.util.module_to_load swallowed an exception') + def test_reset_name(self): + # If reset_name is true then module.__name__ = name, else leave it be. + odd_name = 'not your typical name' + created_module = imp.new_module(self.module_name) + created_module.__name__ = odd_name + sys.modules[self.module_name] = created_module + with util.module_to_load(self.module_name) as module: + self.assertEqual(module.__name__, self.module_name) + created_module.__name__ = odd_name + with util.module_to_load(self.module_name, reset_name=False) as module: + self.assertEqual(module.__name__, odd_name) + class ModuleForLoaderTests(unittest.TestCase): -- Repository URL: http://hg.python.org/cpython From brett at python.org Sat Jun 1 00:37:13 2013 From: brett at python.org (Brett Cannon) Date: Fri, 31 May 2013 18:37:13 -0400 Subject: [Python-checkins] cpython: Add a reset_name argument to importlib.util.module_to_load in order to In-Reply-To: <3bMfy630Xkz7LpQ@mail.python.org> References: <3bMfy630Xkz7LpQ@mail.python.org> Message-ID: I realize this broke the buildbots. Missed part of a diff in the commit. I'm trying to split a massive CL into reasonable commit sizes, so please be patient. On Fri, May 31, 2013 at 6:11 PM, brett.cannon wrote: > http://hg.python.org/cpython/rev/39cc1b04713e > changeset: 83998:39cc1b04713e > user: Brett Cannon > date: Fri May 31 18:11:17 2013 -0400 > summary: > Add a reset_name argument to importlib.util.module_to_load in order to > control whether to reset the module's __name__ attribute in case a > reload is being done. > > files: > Doc/library/importlib.rst | 6 +++++- > Lib/importlib/_bootstrap.py | 14 +++++++++++++- > Lib/test/test_importlib/test_util.py | 12 ++++++++++++ > 3 files changed, 30 insertions(+), 2 deletions(-) > > > diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst > --- a/Doc/library/importlib.rst > +++ b/Doc/library/importlib.rst > @@ -788,7 +788,7 @@ > > .. versionadded:: 3.3 > > -.. function:: module_to_load(name) > +.. function:: module_to_load(name, *, reset_name=True) > > Returns a :term:`context manager` which provides the module to load. > The > module will either come from :attr:`sys.modules` in the case of > reloading or > @@ -796,6 +796,10 @@ > :attr:`sys.modules` occurs if the module was new and an exception was > raised. > > + If **reset_name** is true and the module requested is being reloaded > then > + the module's :attr:`__name__` attribute will > + be reset to **name**, else it will be left untouched. > + > .. versionadded:: 3.4 > > .. decorator:: module_for_loader > diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py > --- a/Lib/importlib/_bootstrap.py > +++ b/Lib/importlib/_bootstrap.py > @@ -493,8 +493,14 @@ > > """ > > - def __init__(self, name): > + def __init__(self, name, *, reset_name=True): > + """Prepare the context manager. > + > + The reset_name argument specifies whether to unconditionally reset > + the __name__ attribute if the module is found to be a reload. > + """ > self._name = name > + self._reset_name = reset_name > > def __enter__(self): > self._module = sys.modules.get(self._name) > @@ -508,6 +514,12 @@ > # (otherwise an optimization shortcut in import.c becomes > wrong) > self._module.__initializing__ = True > sys.modules[self._name] = self._module > + elif self._reset_name: > + try: > + self._module.__name__ = self._name > + except AttributeError: > + pass > + > return self._module > > def __exit__(self, *args): > diff --git a/Lib/test/test_importlib/test_util.py > b/Lib/test/test_importlib/test_util.py > --- a/Lib/test/test_importlib/test_util.py > +++ b/Lib/test/test_importlib/test_util.py > @@ -55,6 +55,18 @@ > else: > self.fail('importlib.util.module_to_load swallowed an > exception') > > + def test_reset_name(self): > + # If reset_name is true then module.__name__ = name, else leave > it be. > + odd_name = 'not your typical name' > + created_module = imp.new_module(self.module_name) > + created_module.__name__ = odd_name > + sys.modules[self.module_name] = created_module > + with util.module_to_load(self.module_name) as module: > + self.assertEqual(module.__name__, self.module_name) > + created_module.__name__ = odd_name > + with util.module_to_load(self.module_name, reset_name=False) as > module: > + self.assertEqual(module.__name__, odd_name) > + > > class ModuleForLoaderTests(unittest.TestCase): > > > -- > 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 python-checkins at python.org Sat Jun 1 00:57:56 2013 From: python-checkins at python.org (brett.cannon) Date: Sat, 1 Jun 2013 00:57:56 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Fix_for_last_commit_on_add?= =?utf-8?q?ing_reset=5Fname_to_module=5Fto=5Fload?= Message-ID: <3bMgzm2W71z7Lnn@mail.python.org> http://hg.python.org/cpython/rev/130dfd6b3428 changeset: 83999:130dfd6b3428 user: Brett Cannon date: Fri May 31 18:37:44 2013 -0400 summary: Fix for last commit on adding reset_name to module_to_load files: Lib/importlib/_bootstrap.py | 9 ++++++--- 1 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py --- a/Lib/importlib/_bootstrap.py +++ b/Lib/importlib/_bootstrap.py @@ -529,10 +529,13 @@ del sys.modules[self._name] -def module_to_load(name): - """Return a context manager which provides the module object to load.""" +def module_to_load(name, *, reset_name=True): + """Return a context manager which provides the module object to load. + + If reset_name is true, reset the module's __name__ to 'name'. + """ # Hiding _ModuleManager behind a function for better naming. - return _ModuleManager(name) + return _ModuleManager(name, reset_name=reset_name) def set_package(fxn): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 1 00:57:57 2013 From: python-checkins at python.org (brett.cannon) Date: Sat, 1 Jun 2013 00:57:57 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Docstring_cleanup?= Message-ID: <3bMgzn4Djmz7M17@mail.python.org> http://hg.python.org/cpython/rev/42a3d182d906 changeset: 84000:42a3d182d906 user: Brett Cannon date: Fri May 31 18:39:07 2013 -0400 summary: Docstring cleanup files: Lib/importlib/abc.py | 5 +++-- 1 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Lib/importlib/abc.py b/Lib/importlib/abc.py --- a/Lib/importlib/abc.py +++ b/Lib/importlib/abc.py @@ -71,7 +71,7 @@ a possible part of a namespace. The fullname is a str. Returns a 2-tuple of (Loader, portion) where portion is a sequence of file system locations contributing to part of - a namespace package. The sequence may be empty and the loader may be + a namespace package. The sequence may be empty and the loader may be None. """ return None, [] @@ -108,7 +108,8 @@ def module_repr(self, module): """Return a module's repr. - Used by the module type when implemented without raising an exception. + Used by the module type when the method does not raise + NotImplementedError. """ raise NotImplementedError -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 1 00:57:59 2013 From: python-checkins at python.org (brett.cannon) Date: Sat, 1 Jun 2013 00:57:59 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issues_=2318088=2C_18089?= =?utf-8?q?=3A_Introduce?= Message-ID: <3bMgzq2q03z7M11@mail.python.org> http://hg.python.org/cpython/rev/e873f2e67353 changeset: 84001:e873f2e67353 user: Brett Cannon date: Fri May 31 18:56:47 2013 -0400 summary: Issues #18088, 18089: Introduce importlib.abc.Loader.init_module_attrs() and implement importlib.abc.InspectLoader.load_module(). The importlib.abc.Loader.init_module_attrs() method sets the various attributes on the module being loaded. It is done unconditionally to support reloading. Typically people used importlib.util.module_for_loader, but since that's a decorator there was no way to override it's actions, so init_module_attrs() came into existence to allow for overriding. This is also why module_for_loader is now pending deprecation (having its other use replaced by importlib.util.module_to_load). All of this allowed for importlib.abc.InspectLoader.load_module() to be implemented. At this point you can now implement a loader with nothing more than get_code() (which only requires get_source(); package support requires is_package()). Thanks to init_module_attrs() the implementation of load_module() is basically a context manager containing 2 methods calls, a call to exec(), and a return statement. files: Doc/library/importlib.rst | 61 +- Doc/whatsnew/3.4.rst | 5 +- Lib/importlib/_bootstrap.py | 125 +- Lib/importlib/abc.py | 32 +- Lib/importlib/util.py | 45 +- Lib/test/test_importlib/source/test_file_loader.py | 33 +- Lib/test/test_importlib/test_abc.py | 202 + Lib/test/test_importlib/test_util.py | 24 +- Misc/NEWS | 8 +- Python/importlib.h | 7034 ++++----- 10 files changed, 3933 insertions(+), 3636 deletions(-) diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst --- a/Doc/library/importlib.rst +++ b/Doc/library/importlib.rst @@ -246,7 +246,7 @@ The loader should set several attributes on the module. (Note that some of these attributes can change when a module is - reloaded.) + reloaded; see :meth:`init_module_attrs`): - :attr:`__name__` The name of the module. @@ -289,6 +289,17 @@ .. versionchanged:: 3.4 Made optional instead of an abstractmethod. + .. method:: init_module_attrs(module) + + Set the :attr:`__loader__` attribute on the module. + + Subclasses overriding this method should set whatever appropriate + attributes it can, getting the module's name from :attr:`__name__` when + needed. All values should also be overridden so that reloading works as + expected. + + .. versionadded:: 3.4 + .. class:: ResourceLoader @@ -363,6 +374,18 @@ .. versionadded:: 3.4 + .. method:: init_module_attrs(module) + + Set the :attr:`__package__` attribute and :attr:`__path__` attribute to + the empty list if appropriate along with what + :meth:`importlib.abc.Loader.init_module_attrs` sets. + + .. versionadded:: 3.4 + + .. method:: load_module(fullname) + + Implementation of :meth:`Loader.load_module`. + .. class:: ExecutionLoader @@ -383,6 +406,15 @@ .. versionchanged:: 3.4 Raises :exc:`ImportError` instead of :exc:`NotImplementedError`. + .. method:: init_module_attrs(module) + + Set :attr:`__file__` and if initializing a package then set + :attr:`__path__` to ``[os.path.dirname(__file__)]`` along with + all attributes set by + :meth:`importlib.abc.InspectLoader.init_module_attrs`. + + .. versionadded:: 3.4 + .. class:: FileLoader(fullname, path) @@ -500,6 +532,14 @@ ``__init__`` when the file extension is removed **and** the module name itself does not end in ``__init__``. + .. method:: init_module_attr(module) + + Set :attr:`__cached__` using :func:`imp.cache_from_source`. Other + attributes set by + :meth:`importlib.abc.ExecutionLoader.init_module_attrs`. + + .. versionadded:: 3.4 + :mod:`importlib.machinery` -- Importers and path hooks ------------------------------------------------------ @@ -826,17 +866,18 @@ module from being in left in :data:`sys.modules`. If the module was already in :data:`sys.modules` then it is left alone. - .. note:: - :func:`module_to_load` subsumes the module management aspect of this - decorator. - .. versionchanged:: 3.3 :attr:`__loader__` and :attr:`__package__` are automatically set (when possible). .. versionchanged:: 3.4 - Set :attr:`__loader__` :attr:`__package__` unconditionally to support - reloading. + Set :attr:`__name__`, :attr:`__loader__` :attr:`__package__` + unconditionally to support reloading. + + .. deprecated:: 3.4 + For the benefit of :term:`loader` subclasses, please use + :func:`module_to_load` and + :meth:`importlib.abc.Loader.init_module_attrs` instead. .. decorator:: set_loader @@ -849,7 +890,8 @@ .. note:: As this decorator sets :attr:`__loader__` after loading the module, it is - recommended to use :func:`module_for_loader` instead when appropriate. + recommended to use :meth:`importlib.abc.Loader.init_module_attrs` instead + when appropriate. .. versionchanged:: 3.4 Set ``__loader__`` if set to ``None``, as if the attribute does not @@ -862,4 +904,5 @@ .. note:: As this decorator sets :attr:`__package__` after loading the module, it is - recommended to use :func:`module_for_loader` instead when appropriate. + recommended to use :meth:`importlib.abc.Loader.init_module_attrs` instead + when appropriate. diff --git a/Doc/whatsnew/3.4.rst b/Doc/whatsnew/3.4.rst --- a/Doc/whatsnew/3.4.rst +++ b/Doc/whatsnew/3.4.rst @@ -232,7 +232,10 @@ Deprecated features ------------------- -* None yet. +* :func:`importlib.util.module_for_loader` is pending deprecation. Using + :func:`importlib.util.module_to_load` and + :meth:`importlib.abc.Loader.init_module_attrs` allows subclasses of a loader + to more easily customize module loading. Porting to Python 3.4 diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py --- a/Lib/importlib/_bootstrap.py +++ b/Lib/importlib/_bootstrap.py @@ -538,6 +538,32 @@ return _ModuleManager(name, reset_name=reset_name) +def _init_package_attrs(loader, module): + """Set __package__ and __path__ based on what loader.is_package() says.""" + name = module.__name__ + try: + is_package = loader.is_package(name) + except ImportError: + pass + else: + if is_package: + module.__package__ = name + module.__path__ = [] + else: + module.__package__ = name.rpartition('.')[0] + + +def _init_file_attrs(loader, module): + """Set __file__ and __path__ based on loader.get_filename().""" + try: + module.__file__ = loader.get_filename(module.__name__) + except ImportError: + pass + else: + if module.__name__ == module.__package__: + module.__path__.append(_path_split(module.__file__)[0]) + + def set_package(fxn): """Set __package__ on the returned module.""" def set_package_wrapper(*args, **kwargs): @@ -562,42 +588,6 @@ return set_loader_wrapper -def module_for_loader(fxn): - """Decorator to handle selecting the proper module for loaders. - - The decorated function is passed the module to use instead of the module - name. The module passed in to the function is either from sys.modules if - it already exists or is a new module. If the module is new, then __name__ - is set the first argument to the method, __loader__ is set to self, and - __package__ is set accordingly (if self.is_package() is defined) will be set - before it is passed to the decorated function (if self.is_package() does - not work for the module it will be set post-load). - - If an exception is raised and the decorator created the module it is - subsequently removed from sys.modules. - - The decorator assumes that the decorated function takes the module name as - the second argument. - - """ - def module_for_loader_wrapper(self, fullname, *args, **kwargs): - with module_to_load(fullname) as module: - module.__loader__ = self - try: - is_package = self.is_package(fullname) - except (ImportError, AttributeError): - pass - else: - if is_package: - module.__package__ = fullname - else: - module.__package__ = fullname.rpartition('.')[0] - # If __package__ was not set above, __import__() will do it later. - return fxn(self, module, *args, **kwargs) - _wrap(module_for_loader_wrapper, fxn) - return module_for_loader_wrapper - - def _check_name(method): """Decorator to verify that the module being requested matches the one the loader can handle. @@ -904,25 +894,32 @@ tail_name = fullname.rpartition('.')[2] return filename_base == '__init__' and tail_name != '__init__' - @module_for_loader - def _load_module(self, module, *, sourceless=False): - """Helper for load_module able to handle either source or sourceless - loading.""" - name = module.__name__ - code_object = self.get_code(name) - module.__file__ = self.get_filename(name) - if not sourceless: + def init_module_attrs(self, module): + """Set various attributes on the module. + + ExecutionLoader.init_module_attrs() is used to set __loader__, + __package__, __file__, and optionally __path__. The __cached__ attribute + is set using imp.cache_from_source() and __file__. + """ + module.__loader__ = self # Loader + _init_package_attrs(self, module) # InspectLoader + _init_file_attrs(self, module) # ExecutionLoader + if hasattr(module, '__file__'): # SourceLoader try: module.__cached__ = cache_from_source(module.__file__) except NotImplementedError: - module.__cached__ = module.__file__ - else: - module.__cached__ = module.__file__ - if self.is_package(name): - module.__path__ = [_path_split(module.__file__)[0]] - # __package__ and __loader set by @module_for_loader. - _call_with_frames_removed(exec, code_object, module.__dict__) - return module + pass + + def load_module(self, fullname): + """Load the specified module into sys.modules and return it.""" + with module_to_load(fullname) as module: + self.init_module_attrs(module) + code = self.get_code(fullname) + if code is None: + raise ImportError('cannot load module {!r} when get_code() ' + 'returns None'.format(fullname)) + _call_with_frames_removed(exec, code, module.__dict__) + return module class SourceLoader(_LoaderBasics): @@ -1046,16 +1043,6 @@ pass return code_object - def load_module(self, fullname): - """Concrete implementation of Loader.load_module. - - Requires ExecutionLoader.get_filename and ResourceLoader.get_data to be - implemented to load source code. Use of bytecode is dictated by whether - get_code uses/writes bytecode. - - """ - return self._load_module(fullname) - class FileLoader: @@ -1133,8 +1120,9 @@ """Loader which handles sourceless file imports.""" - def load_module(self, fullname): - return self._load_module(fullname, sourceless=True) + def init_module_attrs(self, module): + super().init_module_attrs(module) + module.__cached__ = module.__file__ def get_code(self, fullname): path = self.get_filename(fullname) @@ -1259,12 +1247,13 @@ def module_repr(cls, module): return "".format(module.__name__) - @module_for_loader - def load_module(self, module): + def load_module(self, fullname): """Load a namespace module.""" _verbose_message('namespace module loaded with path {!r}', self._path) - module.__path__ = self._path - return module + with module_to_load(fullname) as module: + module.__path__ = self._path + module.__package__ = fullname + return module # Finders ##################################################################### diff --git a/Lib/importlib/abc.py b/Lib/importlib/abc.py --- a/Lib/importlib/abc.py +++ b/Lib/importlib/abc.py @@ -8,11 +8,6 @@ raise _frozen_importlib = None import abc -import imp -import marshal -import sys -import tokenize -import warnings def _register(abstract_cls, *classes): @@ -113,6 +108,10 @@ """ raise NotImplementedError + def init_module_attrs(self, module): + """Set the module's __loader__ attribute.""" + module.__loader__ = self + class ResourceLoader(Loader): @@ -177,6 +176,17 @@ argument should be where the data was retrieved (when applicable).""" return compile(data, path, 'exec', dont_inherit=True) + def init_module_attrs(self, module): + """Initialize the __loader__ and __package__ attributes of the module. + + The name of the module is gleaned from module.__name__. The __package__ + attribute is set based on self.is_package(). + """ + super().init_module_attrs(module) + _bootstrap._init_package_attrs(self, module) + + load_module = _bootstrap._LoaderBasics.load_module + _register(InspectLoader, machinery.BuiltinImporter, machinery.FrozenImporter, machinery.ExtensionFileLoader) @@ -215,6 +225,18 @@ else: return self.source_to_code(source, path) + def init_module_attrs(self, module): + """Initialize the module's attributes. + + It is assumed that the module's name has been set on module.__name__. + It is also assumed that any path returned by self.get_filename() uses + (one of) the operating system's path separator(s) to separate filenames + from directories in order to set __path__ intelligently. + InspectLoader.init_module_attrs() sets __loader__ and __package__. + """ + super().init_module_attrs(module) + _bootstrap._init_file_attrs(self, module) + class FileLoader(_bootstrap.FileLoader, ResourceLoader, ExecutionLoader): diff --git a/Lib/importlib/util.py b/Lib/importlib/util.py --- a/Lib/importlib/util.py +++ b/Lib/importlib/util.py @@ -1,11 +1,13 @@ """Utility code for constructing importers, etc.""" from ._bootstrap import module_to_load -from ._bootstrap import module_for_loader from ._bootstrap import set_loader from ._bootstrap import set_package from ._bootstrap import _resolve_name +import functools +import warnings + def resolve_name(name, package): """Resolve a relative module name to an absolute one.""" @@ -20,3 +22,44 @@ break level += 1 return _resolve_name(name[level:], package, level) + + +def module_for_loader(fxn): + """Decorator to handle selecting the proper module for loaders. + + The decorated function is passed the module to use instead of the module + name. The module passed in to the function is either from sys.modules if + it already exists or is a new module. If the module is new, then __name__ + is set the first argument to the method, __loader__ is set to self, and + __package__ is set accordingly (if self.is_package() is defined) will be set + before it is passed to the decorated function (if self.is_package() does + not work for the module it will be set post-load). + + If an exception is raised and the decorator created the module it is + subsequently removed from sys.modules. + + The decorator assumes that the decorated function takes the module name as + the second argument. + + """ + warnings.warn('To make it easier for subclasses, please use ' + 'importlib.util.module_to_load() and ' + 'importlib.abc.Loader.init_module_attrs()', + PendingDeprecationWarning, stacklevel=2) + @functools.wraps(fxn) + def module_for_loader_wrapper(self, fullname, *args, **kwargs): + with module_to_load(fullname) as module: + module.__loader__ = self + try: + is_package = self.is_package(fullname) + except (ImportError, AttributeError): + pass + else: + if is_package: + module.__package__ = fullname + else: + module.__package__ = fullname.rpartition('.')[0] + # If __package__ was not set above, __import__() will do it later. + return fxn(self, module, *args, **kwargs) + + return module_for_loader_wrapper \ No newline at end of file diff --git a/Lib/test/test_importlib/source/test_file_loader.py b/Lib/test/test_importlib/source/test_file_loader.py --- a/Lib/test/test_importlib/source/test_file_loader.py +++ b/Lib/test/test_importlib/source/test_file_loader.py @@ -15,7 +15,7 @@ import sys import unittest -from test.support import make_legacy_pyc +from test.support import make_legacy_pyc, unload class SimpleTest(unittest.TestCase): @@ -26,23 +26,13 @@ """ def test_load_module_API(self): - # If fullname is not specified that assume self.name is desired. - class TesterMixin(importlib.abc.Loader): - def load_module(self, fullname): return fullname - def module_repr(self, module): return '' + class Tester(importlib.abc.FileLoader): + def get_source(self, _): return 'attr = 42' + def is_package(self, _): return False - class Tester(importlib.abc.FileLoader, TesterMixin): - def get_code(self, _): pass - def get_source(self, _): pass - def is_package(self, _): pass - - name = 'mod_name' - loader = Tester(name, 'some_path') - self.assertEqual(name, loader.load_module()) - self.assertEqual(name, loader.load_module(None)) - self.assertEqual(name, loader.load_module(name)) - with self.assertRaises(ImportError): - loader.load_module(loader.name + 'XXX') + loader = Tester('blah', 'blah.py') + self.addCleanup(unload, 'blah') + module = loader.load_module() # Should not raise an exception. def test_get_filename_API(self): # If fullname is not set then assume self.path is desired. @@ -473,13 +463,6 @@ self._test_non_code_marshal(del_source=True) -def test_main(): - from test.support import run_unittest - run_unittest(SimpleTest, - SourceLoaderBadBytecodeTest, - SourcelessLoaderBadBytecodeTest - ) - if __name__ == '__main__': - test_main() + unittest.main() diff --git a/Lib/test/test_importlib/test_abc.py b/Lib/test/test_importlib/test_abc.py --- a/Lib/test/test_importlib/test_abc.py +++ b/Lib/test/test_importlib/test_abc.py @@ -2,12 +2,14 @@ from importlib import abc from importlib import machinery +import contextlib import imp import inspect import io import marshal import os import sys +from test import support import unittest from unittest import mock @@ -198,6 +200,15 @@ with self.assertRaises(ImportError): self.ins.get_filename('blah') +##### Loader concrete methods ################################################## +class LoaderConcreteMethodTests(unittest.TestCase): + + def test_init_module_attrs(self): + loader = LoaderSubclass() + module = imp.new_module('blah') + loader.init_module_attrs(module) + self.assertEqual(module.__loader__, loader) + ##### InspectLoader concrete methods ########################################### class InspectLoaderSourceToCodeTests(unittest.TestCase): @@ -269,6 +280,93 @@ loader.get_code('blah') +class InspectLoaderInitModuleTests(unittest.TestCase): + + @staticmethod + def mock_is_package(return_value): + return mock.patch.object(InspectLoaderSubclass, 'is_package', + return_value=return_value) + + def init_module_attrs(self, name): + loader = InspectLoaderSubclass() + module = imp.new_module(name) + loader.init_module_attrs(module) + self.assertEqual(module.__loader__, loader) + return module + + def test_package(self): + # If a package, then __package__ == __name__, __path__ == [] + with self.mock_is_package(True): + name = 'blah' + module = self.init_module_attrs(name) + self.assertEqual(module.__package__, name) + self.assertEqual(module.__path__, []) + + def test_toplevel(self): + # If a module is top-level, __package__ == '' + with self.mock_is_package(False): + name = 'blah' + module = self.init_module_attrs(name) + self.assertEqual(module.__package__, '') + + def test_submodule(self): + # If a module is contained within a package then set __package__ to the + # package name. + with self.mock_is_package(False): + name = 'pkg.mod' + module = self.init_module_attrs(name) + self.assertEqual(module.__package__, 'pkg') + + def test_is_package_ImportError(self): + # If is_package() raises ImportError, __package__ should be None and + # __path__ should not be set. + with self.mock_is_package(False) as mocked_method: + mocked_method.side_effect = ImportError + name = 'mod' + module = self.init_module_attrs(name) + self.assertIsNone(module.__package__) + self.assertFalse(hasattr(module, '__path__')) + + +class InspectLoaderLoadModuleTests(unittest.TestCase): + + """Test InspectLoader.load_module().""" + + module_name = 'blah' + + def setUp(self): + support.unload(self.module_name) + self.addCleanup(support.unload, self.module_name) + + def mock_get_code(self): + return mock.patch.object(InspectLoaderSubclass, 'get_code') + + def test_get_code_ImportError(self): + # If get_code() raises ImportError, it should propagate. + with self.mock_get_code() as mocked_get_code: + mocked_get_code.side_effect = ImportError + with self.assertRaises(ImportError): + loader = InspectLoaderSubclass() + loader.load_module(self.module_name) + + def test_get_code_None(self): + # If get_code() returns None, raise ImportError. + with self.mock_get_code() as mocked_get_code: + mocked_get_code.return_value = None + with self.assertRaises(ImportError): + loader = InspectLoaderSubclass() + loader.load_module(self.module_name) + + def test_module_returned(self): + # The loaded module should be returned. + code = compile('attr = 42', '', 'exec') + with self.mock_get_code() as mocked_get_code: + mocked_get_code.return_value = code + loader = InspectLoaderSubclass() + module = loader.load_module(self.module_name) + self.assertEqual(module, sys.modules[self.module_name]) + + ##### ExecutionLoader concrete methods ######################################### class ExecutionLoaderGetCodeTests(unittest.TestCase): @@ -327,6 +425,69 @@ self.assertEqual(module.attr, 42) +class ExecutionLoaderInitModuleTests(unittest.TestCase): + + @staticmethod + @contextlib.contextmanager + def mock_methods(is_package, filename): + is_package_manager = InspectLoaderInitModuleTests.mock_is_package(is_package) + get_filename_manager = mock.patch.object(ExecutionLoaderSubclass, + 'get_filename', return_value=filename) + with is_package_manager as mock_is_package: + with get_filename_manager as mock_get_filename: + yield {'is_package': mock_is_package, + 'get_filename': mock_get_filename} + + def test_toplevel(self): + # Verify __loader__, __file__, and __package__; no __path__. + name = 'blah' + path = os.path.join('some', 'path', '{}.py'.format(name)) + with self.mock_methods(False, path): + loader = ExecutionLoaderSubclass() + module = imp.new_module(name) + loader.init_module_attrs(module) + self.assertIs(module.__loader__, loader) + self.assertEqual(module.__file__, path) + self.assertEqual(module.__package__, '') + self.assertFalse(hasattr(module, '__path__')) + + def test_package(self): + # Verify __loader__, __file__, __package__, and __path__. + name = 'pkg' + path = os.path.join('some', 'pkg', '__init__.py') + with self.mock_methods(True, path): + loader = ExecutionLoaderSubclass() + module = imp.new_module(name) + loader.init_module_attrs(module) + self.assertIs(module.__loader__, loader) + self.assertEqual(module.__file__, path) + self.assertEqual(module.__package__, 'pkg') + self.assertEqual(module.__path__, [os.path.dirname(path)]) + + def test_submodule(self): + # Verify __package__ and not __path__; test_toplevel() takes care of + # other attributes. + name = 'pkg.submodule' + path = os.path.join('some', 'pkg', 'submodule.py') + with self.mock_methods(False, path): + loader = ExecutionLoaderSubclass() + module = imp.new_module(name) + loader.init_module_attrs(module) + self.assertEqual(module.__package__, 'pkg') + self.assertEqual(module.__file__, path) + self.assertFalse(hasattr(module, '__path__')) + + def test_get_filename_ImportError(self): + # If get_filename() raises ImportError, don't set __file__. + name = 'blah' + path = 'blah.py' + with self.mock_methods(False, path) as mocked_methods: + mocked_methods['get_filename'].side_effect = ImportError + loader = ExecutionLoaderSubclass() + module = imp.new_module(name) + loader.init_module_attrs(module) + self.assertFalse(hasattr(module, '__file__')) + ##### SourceLoader concrete methods ############################################ class SourceOnlyLoaderMock(abc.SourceLoader): @@ -621,6 +782,47 @@ self.assertEqual(mock.get_source(name), expect) +class SourceLoaderInitModuleAttrTests(unittest.TestCase): + + """Tests for importlib.abc.SourceLoader.init_module_attrs().""" + + def test_init_module_attrs(self): + # If __file__ set, __cached__ == imp.cached_from_source(__file__). + name = 'blah' + path = 'blah.py' + loader = SourceOnlyLoaderMock(path) + module = imp.new_module(name) + loader.init_module_attrs(module) + self.assertEqual(module.__loader__, loader) + self.assertEqual(module.__package__, '') + self.assertEqual(module.__file__, path) + self.assertEqual(module.__cached__, imp.cache_from_source(path)) + + @mock.patch('importlib._bootstrap.cache_from_source') + def test_cache_from_source_NotImplementedError(self, mock_cache_from_source): + # If imp.cache_from_source() raises NotImplementedError don't set + # __cached__. + mock_cache_from_source.side_effect = NotImplementedError + name = 'blah' + path = 'blah.py' + loader = SourceOnlyLoaderMock(path) + module = imp.new_module(name) + loader.init_module_attrs(module) + self.assertEqual(module.__file__, path) + self.assertFalse(hasattr(module, '__cached__')) + + def test_no_get_filename(self): + # No __file__, no __cached__. + with mock.patch.object(SourceOnlyLoaderMock, 'get_filename') as mocked: + mocked.side_effect = ImportError + name = 'blah' + loader = SourceOnlyLoaderMock('blah.py') + module = imp.new_module(name) + loader.init_module_attrs(module) + self.assertFalse(hasattr(module, '__file__')) + self.assertFalse(hasattr(module, '__cached__')) + + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_importlib/test_util.py b/Lib/test/test_importlib/test_util.py --- a/Lib/test/test_importlib/test_util.py +++ b/Lib/test/test_importlib/test_util.py @@ -5,6 +5,7 @@ from test import support import types import unittest +import warnings class ModuleToLoadTests(unittest.TestCase): @@ -72,14 +73,27 @@ """Tests for importlib.util.module_for_loader.""" + @staticmethod + def module_for_loader(func): + with warnings.catch_warnings(): + warnings.simplefilter('ignore', PendingDeprecationWarning) + return util.module_for_loader(func) + + def test_warning(self): + # Should raise a PendingDeprecationWarning when used. + with warnings.catch_warnings(): + warnings.simplefilter('error', PendingDeprecationWarning) + with self.assertRaises(PendingDeprecationWarning): + func = util.module_for_loader(lambda x: x) + def return_module(self, name): - fxn = util.module_for_loader(lambda self, module: module) + fxn = self.module_for_loader(lambda self, module: module) return fxn(self, name) def raise_exception(self, name): def to_wrap(self, module): raise ImportError - fxn = util.module_for_loader(to_wrap) + fxn = self.module_for_loader(to_wrap) try: fxn(self, name) except ImportError: @@ -100,7 +114,7 @@ class FakeLoader: def is_package(self, name): return True - @util.module_for_loader + @self.module_for_loader def load_module(self, module): return module name = 'a.b.c' @@ -134,7 +148,7 @@ def test_decorator_attrs(self): def fxn(self, module): pass - wrapped = util.module_for_loader(fxn) + wrapped = self.module_for_loader(fxn) self.assertEqual(wrapped.__name__, fxn.__name__) self.assertEqual(wrapped.__qualname__, fxn.__qualname__) @@ -160,7 +174,7 @@ self._pkg = is_package def is_package(self, name): return self._pkg - @util.module_for_loader + @self.module_for_loader def load_module(self, module): return module diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -97,6 +97,12 @@ Library ------- +- Issue #18089: Implement importlib.abc.InspectLoader.load_module. + +- Issue #18088: Introduce importlib.abc.Loader.init_module_attrs for setting + module attributes. Leads to the pending deprecation of + importlib.util.module_for_loader. + - Issue #17403: urllib.parse.robotparser normalizes the urls before adding to ruleline. This helps in handling certain types invalid urls in a conservative manner. Patch contributed by Mher Movsisyan. @@ -104,7 +110,7 @@ - Issue #18070: Have importlib.util.module_for_loader() set attributes unconditionally in order to properly support reloading. -- Add importlib.util.module_to_load to return a context manager to provide the +- Added importlib.util.module_to_load to return a context manager to provide the proper module object to load. - Issue #18025: Fixed a segfault in io.BufferedIOBase.readinto() when raw diff --git a/Python/importlib.h b/Python/importlib.h --- a/Python/importlib.h +++ b/Python/importlib.h [stripped] -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 1 00:58:00 2013 From: python-checkins at python.org (brett.cannon) Date: Sat, 1 Jun 2013 00:58:00 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_fix_whitespace?= Message-ID: <3bMgzr4Ww2z7M11@mail.python.org> http://hg.python.org/cpython/rev/6a90817fb437 changeset: 84002:6a90817fb437 user: Brett Cannon date: Fri May 31 18:57:45 2013 -0400 summary: fix whitespace files: Lib/importlib/util.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Lib/importlib/util.py b/Lib/importlib/util.py --- a/Lib/importlib/util.py +++ b/Lib/importlib/util.py @@ -62,4 +62,4 @@ # If __package__ was not set above, __import__() will do it later. return fxn(self, module, *args, **kwargs) - return module_for_loader_wrapper \ No newline at end of file + return module_for_loader_wrapper -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 1 05:18:49 2013 From: python-checkins at python.org (brett.cannon) Date: Sat, 1 Jun 2013 05:18:49 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2318065=3A_For_froz?= =?utf-8?b?ZW4gcGFja2FnZXMgc2V0IF9fcGF0aF9fIHRvIFtdLg==?= Message-ID: <3bMnmn3r0dz7LlV@mail.python.org> http://hg.python.org/cpython/rev/82db02a2e023 changeset: 84003:82db02a2e023 user: Brett Cannon date: Fri May 31 23:18:39 2013 -0400 summary: Issue #18065: For frozen packages set __path__ to []. Previously __path__ was set to [__name__], but that could lead to bad results if someone managed to circumvent the frozen importer and somehow ended up with a finder that thought __name__ was a legit directory/location. files: Doc/whatsnew/3.4.rst | 7 +++++++ Lib/test/test_importlib/frozen/test_loader.py | 2 +- Misc/NEWS | 6 ++++++ Python/import.c | 6 ++---- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/Doc/whatsnew/3.4.rst b/Doc/whatsnew/3.4.rst --- a/Doc/whatsnew/3.4.rst +++ b/Doc/whatsnew/3.4.rst @@ -259,3 +259,10 @@ ``__package__`` unconditionally to properly support reloading. If this is not desired then you will need to set these attributes manually. You can use :func:`importlib.util.module_to_load` for module management. + +* Import now resets relevant attributes (e.g. ``__name__``, ``__loader__``, + ``__package__``, ``__file__``, ``__cached__``) unconditionally when reloading. + +* Frozen packages no longer set ``__path__`` to a list containg the package name + but an empty list instead. Determing if a module is a package should be done + using ``hasattr(module, '__path__')``. diff --git a/Lib/test/test_importlib/frozen/test_loader.py b/Lib/test/test_importlib/frozen/test_loader.py --- a/Lib/test/test_importlib/frozen/test_loader.py +++ b/Lib/test/test_importlib/frozen/test_loader.py @@ -24,7 +24,7 @@ module = machinery.FrozenImporter.load_module('__phello__') check = {'__name__': '__phello__', '__package__': '__phello__', - '__path__': ['__phello__'], + '__path__': [], '__loader__': machinery.FrozenImporter, } for attr, value in check.items(): diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,12 @@ Core and Builtins ----------------- +- Issue #18065: Don't set __path__ to the package name for frozen packages. + +- Issue #18088: When reloading a module, unconditionally reset all relevant + attributes on the module (e.g. __name__, __loader__, __package__, __file__, + __cached__). + - Issue #17937: Try harder to collect cyclic garbage at shutdown. - Issue #12370: Prevent class bodies from interfering with the __class__ diff --git a/Python/import.c b/Python/import.c --- a/Python/import.c +++ b/Python/import.c @@ -1107,19 +1107,17 @@ goto err_return; } if (ispackage) { - /* Set __path__ to the package name */ + /* Set __path__ to the empty list */ PyObject *d, *l; int err; m = PyImport_AddModuleObject(name); if (m == NULL) goto err_return; d = PyModule_GetDict(m); - l = PyList_New(1); + l = PyList_New(0); if (l == NULL) { goto err_return; } - Py_INCREF(name); - PyList_SET_ITEM(l, 0, name); err = PyDict_SetItemString(d, "__path__", l); Py_DECREF(l); if (err != 0) -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Sat Jun 1 05:51:08 2013 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Sat, 01 Jun 2013 05:51:08 +0200 Subject: [Python-checkins] Daily reference leaks (6a90817fb437): sum=0 Message-ID: results for 6a90817fb437 on branch "default" -------------------------------------------- Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/refloge5rj8r', '-x'] From python-checkins at python.org Sat Jun 1 07:18:37 2013 From: python-checkins at python.org (benjamin.peterson) Date: Sat, 1 Jun 2013 07:18:37 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_frozen_modules_now_apparen?= =?utf-8?q?tly_have_empty_=5F=5Fpath=5F=5F?= Message-ID: <3bMrR11Z6Vz7Ll2@mail.python.org> http://hg.python.org/cpython/rev/35da9e3ba697 changeset: 84004:35da9e3ba697 user: Benjamin Peterson date: Fri May 31 22:18:26 2013 -0700 summary: frozen modules now apparently have empty __path__ files: Lib/test/test_frozen.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Lib/test/test_frozen.py b/Lib/test/test_frozen.py --- a/Lib/test/test_frozen.py +++ b/Lib/test/test_frozen.py @@ -36,7 +36,7 @@ else: expect.add('spam') self.assertEqual(set(dir(__phello__)), expect) - self.assertEqual(__phello__.__path__, [__phello__.__name__]) + self.assertEqual(__phello__.__path__, []) self.assertEqual(stdout.getvalue(), 'Hello world!\n') with captured_stdout() as stdout: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 1 16:59:25 2013 From: python-checkins at python.org (senthil.kumaran) Date: Sat, 1 Jun 2013 16:59:25 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogRml4ICMxNjQ1MCB0?= =?utf-8?q?est=5Fmissing=5Flocalfile_testcase_fails_on_misconfigured_hostn?= =?utf-8?q?ame=2E?= Message-ID: <3bN5K9251PzShL@mail.python.org> http://hg.python.org/cpython/rev/60c195e89c88 changeset: 84005:60c195e89c88 branch: 2.7 parent: 83995:6ceb5bf24da8 user: Senthil Kumaran date: Sat Jun 01 07:59:10 2013 -0700 summary: Fix #16450 test_missing_localfile testcase fails on misconfigured hostname. Refactor test to accomodate that and exercise the needed functionality. files: Lib/test/test_urllib.py | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_urllib.py b/Lib/test/test_urllib.py --- a/Lib/test/test_urllib.py +++ b/Lib/test/test_urllib.py @@ -227,13 +227,13 @@ 'file://localhost/a/missing/file.py') fd, tmp_file = tempfile.mkstemp() tmp_fileurl = 'file://localhost/' + tmp_file.replace(os.path.sep, '/') + self.assertTrue(os.path.exists(tmp_file)) try: - self.assertTrue(os.path.exists(tmp_file)) fp = urllib.urlopen(tmp_fileurl) + fp.close() finally: os.close(fd) - fp.close() - os.unlink(tmp_file) + os.unlink(tmp_file) self.assertFalse(os.path.exists(tmp_file)) self.assertRaises(IOError, urllib.urlopen, tmp_fileurl) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 1 17:28:00 2013 From: python-checkins at python.org (senthil.kumaran) Date: Sat, 1 Jun 2013 17:28:00 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogRml4ICMxNzk2Nzog?= =?utf-8?q?For_ftp_urls_CWD_to_target_instead_of_hopping_to_each_directory?= Message-ID: <3bN5y81h4XzT0K@mail.python.org> http://hg.python.org/cpython/rev/0a544bb539e6 changeset: 84006:0a544bb539e6 branch: 2.7 user: Senthil Kumaran date: Sat Jun 01 08:24:31 2013 -0700 summary: Fix #17967: For ftp urls CWD to target instead of hopping to each directory towards target. This fixes a bug where target is accessible, but parent directories are restricted. files: Lib/urllib.py | 3 +-- 1 files changed, 1 insertions(+), 2 deletions(-) diff --git a/Lib/urllib.py b/Lib/urllib.py --- a/Lib/urllib.py +++ b/Lib/urllib.py @@ -870,8 +870,7 @@ self.ftp = ftplib.FTP() self.ftp.connect(self.host, self.port, self.timeout) self.ftp.login(self.user, self.passwd) - for dir in self.dirs: - self.ftp.cwd(dir) + self.ftp.cwd(os.path.join(*self.dirs)) def retrfile(self, file, type): import ftplib -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 1 17:28:01 2013 From: python-checkins at python.org (senthil.kumaran) Date: Sat, 1 Jun 2013 17:28:01 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogRml4ICMxNzk2Nzog?= =?utf-8?q?For_ftp_urls_CWD_to_target_instead_of_hopping_to_each_directory?= Message-ID: <3bN5y93ZPmz7Ljd@mail.python.org> http://hg.python.org/cpython/rev/dbfbdf2b5c19 changeset: 84007:dbfbdf2b5c19 branch: 3.3 parent: 83993:81c02d2c830d user: Senthil Kumaran date: Sat Jun 01 08:27:06 2013 -0700 summary: Fix #17967: For ftp urls CWD to target instead of hopping to each directory towards target. This fixes a bug where target is accessible, but parent directories are restricted. files: Lib/urllib/request.py | 3 +-- 1 files changed, 1 insertions(+), 2 deletions(-) diff --git a/Lib/urllib/request.py b/Lib/urllib/request.py --- a/Lib/urllib/request.py +++ b/Lib/urllib/request.py @@ -2273,8 +2273,7 @@ self.ftp = ftplib.FTP() self.ftp.connect(self.host, self.port, self.timeout) self.ftp.login(self.user, self.passwd) - for dir in self.dirs: - self.ftp.cwd(dir) + self.ftp.cwd(os.path.join(*self.dirs)) def retrfile(self, file, type): import ftplib -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 1 17:28:02 2013 From: python-checkins at python.org (senthil.kumaran) Date: Sat, 1 Jun 2013 17:28:02 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_merge_from_3=2E3?= Message-ID: <3bN5yB5Q6Dz7Ljq@mail.python.org> http://hg.python.org/cpython/rev/c1101f0d6c29 changeset: 84008:c1101f0d6c29 parent: 84004:35da9e3ba697 parent: 84007:dbfbdf2b5c19 user: Senthil Kumaran date: Sat Jun 01 08:27:53 2013 -0700 summary: merge from 3.3 Fix #17967: For ftp urls CWD to target instead of hopping to each directory towards target. This fixes a bug where target is accessible, but parent directories are restricted. files: Lib/urllib/request.py | 3 +-- 1 files changed, 1 insertions(+), 2 deletions(-) diff --git a/Lib/urllib/request.py b/Lib/urllib/request.py --- a/Lib/urllib/request.py +++ b/Lib/urllib/request.py @@ -2303,8 +2303,7 @@ self.ftp = ftplib.FTP() self.ftp.connect(self.host, self.port, self.timeout) self.ftp.login(self.user, self.passwd) - for dir in self.dirs: - self.ftp.cwd(dir) + self.ftp.cwd(os.path.join(*self.dirs)) def retrfile(self, file, type): import ftplib -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 1 19:53:47 2013 From: python-checkins at python.org (andrew.kuchling) Date: Sat, 1 Jun 2013 19:53:47 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_=2318066=3A_remove_vestigi?= =?utf-8?q?al_code_depending_on_the_sgi_module?= Message-ID: <3bN9BM0cTdzQ8b@mail.python.org> http://hg.python.org/cpython/rev/a678f139510b changeset: 84009:a678f139510b user: Andrew Kuchling date: Sat Jun 01 13:52:30 2013 -0400 summary: #18066: remove vestigial code depending on the sgi module files: Lib/pty.py | 13 +------------ 1 files changed, 1 insertions(+), 12 deletions(-) diff --git a/Lib/pty.py b/Lib/pty.py --- a/Lib/pty.py +++ b/Lib/pty.py @@ -47,18 +47,7 @@ return _open_terminal() def _open_terminal(): - """Open pty master and return (master_fd, tty_name). - SGI and generic BSD version, for when openpty() fails.""" - try: - import sgi - except ImportError: - pass - else: - try: - tty_name, master_fd = sgi._getpty(os.O_RDWR, 0o666, 0) - except OSError as msg: - raise OSError(msg) - return master_fd, tty_name + """Open pty master and return (master_fd, tty_name).""" for x in 'pqrstuvwxyzPQRST': for y in '0123456789abcdef': pty_name = '/dev/pty' + x + y -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 1 20:12:58 2013 From: python-checkins at python.org (senthil.kumaran) Date: Sat, 1 Jun 2013 20:12:58 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_Fix_thishost_h?= =?utf-8?q?elper_funtion_in_urllib=2E_Returns_the_ipaddress_of_localhost_w?= =?utf-8?q?hen?= Message-ID: <3bN9cV3Z2kz7LjQ@mail.python.org> http://hg.python.org/cpython/rev/4657d0eebe42 changeset: 84010:4657d0eebe42 branch: 2.7 parent: 84006:0a544bb539e6 user: Senthil Kumaran date: Sat Jun 01 11:11:30 2013 -0700 summary: Fix thishost helper funtion in urllib. Returns the ipaddress of localhost when hostname is resolvable by socket.gethostname for local machine. This all fixes certain freebsd builtbot failures. files: Lib/urllib.py | 5 ++++- 1 files changed, 4 insertions(+), 1 deletions(-) diff --git a/Lib/urllib.py b/Lib/urllib.py --- a/Lib/urllib.py +++ b/Lib/urllib.py @@ -819,7 +819,10 @@ """Return the IP address of the current host.""" global _thishost if _thishost is None: - _thishost = socket.gethostbyname(socket.gethostname()) + try: + _thishost = socket.gethostbyname(socket.gethostname()) + except socket.gaierror: + _thishost = socket.gethostbyname('localhost') return _thishost _ftperrors = None -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 1 20:12:59 2013 From: python-checkins at python.org (senthil.kumaran) Date: Sat, 1 Jun 2013 20:12:59 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E3=29=3A_Fix_thishost_h?= =?utf-8?q?elper_funtion_in_urllib=2E_Returns_the_ipaddress_of_localhost_w?= =?utf-8?q?hen?= Message-ID: <3bN9cW5Svvz7LjQ@mail.python.org> http://hg.python.org/cpython/rev/b6464827bddb changeset: 84011:b6464827bddb branch: 3.3 parent: 84007:dbfbdf2b5c19 user: Senthil Kumaran date: Sat Jun 01 11:12:17 2013 -0700 summary: Fix thishost helper funtion in urllib. Returns the ipaddress of localhost when hostname is resolvable by socket.gethostname for local machine. This all fixes certain freebsd builtbot failures. files: Lib/urllib/request.py | 5 ++++- 1 files changed, 4 insertions(+), 1 deletions(-) diff --git a/Lib/urllib/request.py b/Lib/urllib/request.py --- a/Lib/urllib/request.py +++ b/Lib/urllib/request.py @@ -2229,7 +2229,10 @@ """Return the IP addresses of the current host.""" global _thishost if _thishost is None: - _thishost = tuple(socket.gethostbyname_ex(socket.gethostname())[2]) + try: + _thishost = tuple(socket.gethostbyname_ex(socket.gethostname())[2]) + except socket.gaierror: + _thishost = tuple(socket.gethostbyname_ex('localhost')[2]) return _thishost _ftperrors = None -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 1 20:13:01 2013 From: python-checkins at python.org (senthil.kumaran) Date: Sat, 1 Jun 2013 20:13:01 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_merge_from_3=2E3?= Message-ID: <3bN9cY0LhWz7LjY@mail.python.org> http://hg.python.org/cpython/rev/25450fff5b90 changeset: 84012:25450fff5b90 parent: 84009:a678f139510b parent: 84011:b6464827bddb user: Senthil Kumaran date: Sat Jun 01 11:12:52 2013 -0700 summary: merge from 3.3 Fix thishost helper funtion in urllib. Returns the ipaddress of localhost when hostname is resolvable by socket.gethostname for local machine. This all fixes certain freebsd builtbot failures. files: Lib/urllib/request.py | 5 ++++- 1 files changed, 4 insertions(+), 1 deletions(-) diff --git a/Lib/urllib/request.py b/Lib/urllib/request.py --- a/Lib/urllib/request.py +++ b/Lib/urllib/request.py @@ -2259,7 +2259,10 @@ """Return the IP addresses of the current host.""" global _thishost if _thishost is None: - _thishost = tuple(socket.gethostbyname_ex(socket.gethostname())[2]) + try: + _thishost = tuple(socket.gethostbyname_ex(socket.gethostname())[2]) + except socket.gaierror: + _thishost = tuple(socket.gethostbyname_ex('localhost')[2]) return _thishost _ftperrors = None -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 1 21:21:39 2013 From: python-checkins at python.org (antoine.pitrou) Date: Sat, 1 Jun 2013 21:21:39 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?devguide=3A_Don=27t_ask_contributors_?= =?utf-8?q?to_add_themselves_to_Misc/ACKS=2C_it_is_the_core?= Message-ID: <3bNC7l0Dwpz7Lk5@mail.python.org> http://hg.python.org/devguide/rev/51cbc121628e changeset: 621:51cbc121628e user: Antoine Pitrou date: Sat Jun 01 21:21:31 2013 +0200 summary: Don't ask contributors to add themselves to Misc/ACKS, it is the core developer's job. (as discussed on core-mentorship, with Nick's and Terry's approval) files: patch.rst | 38 +++++++++++++++++++++++++++----------- 1 files changed, 27 insertions(+), 11 deletions(-) diff --git a/patch.rst b/patch.rst --- a/patch.rst +++ b/patch.rst @@ -74,17 +74,6 @@ Fifth, proper :ref:`documentation ` additions/changes should be included. -Sixth, if you are not already in the ``Misc/ACKS`` file then add your name. If -you have taken the time to diagnose a problem, invent a solution, code it up, -and submit a patch you deserve to be recognized as having contributed to -Python. This also means you need to fill out a `contributor form`_ which -allows the `Python Software Foundation`_ to license your code for use with -Python (you retain the copyright). - - -.. _contributor form: http://www.python.org/psf/contrib/ -.. _Python Software Foundation: http://www.python.org/psf/ - .. _patch-generation: @@ -113,6 +102,24 @@ **single, condensed** patch rather than a series of several changesets. +Licensing +--------- + +For non-trivial changes, we must have your formal approval for distributing +your work under the `PSF license`_. Therefore, you need to fill out a +`contributor form`_ which allows the `Python Software Foundation`_ to +license your code for use with Python (you retain the copyright). + +.. note:: + You only have to sign this document once, it will then apply to all + your further contributions to Python. + + +.. _PSF license: http://docs.python.org/3.4/license.html#terms-and-conditions-for-accessing-or-otherwise-using-python +.. _contributor form: http://www.python.org/psf/contrib/ +.. _Python Software Foundation: http://www.python.org/psf/ + + Submitting ---------- @@ -166,3 +173,12 @@ with the next major release of Python. It may also be backported to older versions of Python as a bugfix if the core developer doing the commit believes it is warranted. + + +Crediting +--------- + +Non-trivial contributions are credited in the ``Misc/ACKS`` file (and, most +often, in a contribution's ``Misc/NEWS`` entry as well). This is something +the core developer will do when committing your patch, you don't have to +propose the addition by yourself. -- Repository URL: http://hg.python.org/devguide From solipsis at pitrou.net Sun Jun 2 05:49:50 2013 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Sun, 02 Jun 2013 05:49:50 +0200 Subject: [Python-checkins] Daily reference leaks (25450fff5b90): sum=4 Message-ID: results for 25450fff5b90 on branch "default" -------------------------------------------- test_support leaked [-1, 1, 0] references, sum=0 test_support leaked [-1, 3, 2] memory blocks, sum=4 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/refloghWGbq0', '-x'] From python-checkins at python.org Sun Jun 2 19:05:16 2013 From: python-checkins at python.org (raymond.hettinger) Date: Sun, 2 Jun 2013 19:05:16 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E3=29=3A_Clarify_which_?= =?utf-8?q?dictionaries_are_updateable?= Message-ID: <3bNm3w3sSLz7LmK@mail.python.org> http://hg.python.org/cpython/rev/4c8426acd2cf changeset: 84013:4c8426acd2cf branch: 3.3 parent: 84011:b6464827bddb user: Raymond Hettinger date: Sun Jun 02 10:03:05 2013 -0700 summary: Clarify which dictionaries are updateable by using the wording from the Py2.7 docs. files: Doc/library/functions.rst | 20 ++++++++++---------- 1 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -1355,14 +1355,18 @@ .. function:: vars([object]) - Without an argument, act like :func:`locals`. + Return the :attr:`__dict__` attribute for a module, class, instance, + or any other object with a :attr:`__dict__` attribute. - With a module, class or class instance object as argument (or anything else that - has a :attr:`__dict__` attribute), return that attribute. + Objects such as modules and instances have an updateable :attr:`__dict__` + attribute; however, other objects may have write restrictions on their + :attr:`__dict__` attributes (for example, classes use a + dictproxy to prevent direct dictionary updates). - .. note:: - The returned dictionary should not be modified: - the effects on the corresponding symbol table are undefined. [#]_ + Without an argument, :func:`vars` acts like :func:`locals`. Note, the + locals dictionary is only useful for reads since updates to the locals + dictionary are ignored. + .. function:: zip(*iterables) @@ -1481,7 +1485,3 @@ .. [#] Note that the parser only accepts the Unix-style end of line convention. If you are reading the code from a file, make sure to use newline conversion mode to convert Windows or Mac-style newlines. - -.. [#] In the current implementation, local variable bindings cannot normally be - affected this way, but variables retrieved from other scopes (such as modules) - can be. This may change. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 2 19:05:17 2013 From: python-checkins at python.org (raymond.hettinger) Date: Sun, 2 Jun 2013 19:05:17 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_merge?= Message-ID: <3bNm3x5jLvz7Llm@mail.python.org> http://hg.python.org/cpython/rev/49bcd1af0f49 changeset: 84014:49bcd1af0f49 parent: 84012:25450fff5b90 parent: 84013:4c8426acd2cf user: Raymond Hettinger date: Sun Jun 02 10:04:59 2013 -0700 summary: merge files: Doc/library/functions.rst | 20 ++++++++++---------- 1 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -1365,14 +1365,18 @@ .. function:: vars([object]) - Without an argument, act like :func:`locals`. + Return the :attr:`__dict__` attribute for a module, class, instance, + or any other object with a :attr:`__dict__` attribute. - With a module, class or class instance object as argument (or anything else that - has a :attr:`__dict__` attribute), return that attribute. + Objects such as modules and instances have an updateable :attr:`__dict__` + attribute; however, other objects may have write restrictions on their + :attr:`__dict__` attributes (for example, classes use a + dictproxy to prevent direct dictionary updates). - .. note:: - The returned dictionary should not be modified: - the effects on the corresponding symbol table are undefined. [#]_ + Without an argument, :func:`vars` acts like :func:`locals`. Note, the + locals dictionary is only useful for reads since updates to the locals + dictionary are ignored. + .. function:: zip(*iterables) @@ -1491,7 +1495,3 @@ .. [#] Note that the parser only accepts the Unix-style end of line convention. If you are reading the code from a file, make sure to use newline conversion mode to convert Windows or Mac-style newlines. - -.. [#] In the current implementation, local variable bindings cannot normally be - affected this way, but variables retrieved from other scopes (such as modules) - can be. This may change. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 2 21:00:52 2013 From: python-checkins at python.org (senthil.kumaran) Date: Sun, 2 Jun 2013 21:00:52 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogRml4ICMxNzk2NyAt?= =?utf-8?q?_Fix_related_to_regression_on_Windows=2E?= Message-ID: <3bNpdJ0b45zS9f@mail.python.org> http://hg.python.org/cpython/rev/e9d0fb934b46 changeset: 84015:e9d0fb934b46 branch: 2.7 parent: 84010:4657d0eebe42 user: Senthil Kumaran date: Sun Jun 02 11:59:09 2013 -0700 summary: Fix #17967 - Fix related to regression on Windows. os.path.join(*self.dirs) produces an invalid path on windows. ftp paths are always forward-slash seperated like this. /pub/dir. files: Lib/urllib.py | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-) diff --git a/Lib/urllib.py b/Lib/urllib.py --- a/Lib/urllib.py +++ b/Lib/urllib.py @@ -873,7 +873,8 @@ self.ftp = ftplib.FTP() self.ftp.connect(self.host, self.port, self.timeout) self.ftp.login(self.user, self.passwd) - self.ftp.cwd(os.path.join(*self.dirs)) + _target = '/'.join(self.dirs) + self.ftp.cwd(_target) def retrfile(self, file, type): import ftplib -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 2 21:00:53 2013 From: python-checkins at python.org (senthil.kumaran) Date: Sun, 2 Jun 2013 21:00:53 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogRml4ICMxNzk2NyAt?= =?utf-8?q?_Fix_related_to_regression_on_Windows=2E?= Message-ID: <3bNpdK2WWjzRZ6@mail.python.org> http://hg.python.org/cpython/rev/f5906026a7e9 changeset: 84016:f5906026a7e9 branch: 3.3 parent: 84013:4c8426acd2cf user: Senthil Kumaran date: Sun Jun 02 11:59:47 2013 -0700 summary: Fix #17967 - Fix related to regression on Windows. os.path.join(*self.dirs) produces an invalid path on windows. ftp paths are always forward-slash seperated like this. /pub/dir. files: Lib/urllib/request.py | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-) diff --git a/Lib/urllib/request.py b/Lib/urllib/request.py --- a/Lib/urllib/request.py +++ b/Lib/urllib/request.py @@ -2276,7 +2276,8 @@ self.ftp = ftplib.FTP() self.ftp.connect(self.host, self.port, self.timeout) self.ftp.login(self.user, self.passwd) - self.ftp.cwd(os.path.join(*self.dirs)) + _target = '/'.join(self.dirs) + self.ftp.cwd(_target) def retrfile(self, file, type): import ftplib -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 2 21:00:54 2013 From: python-checkins at python.org (senthil.kumaran) Date: Sun, 2 Jun 2013 21:00:54 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_merge_from_3=2E3?= Message-ID: <3bNpdL4MBRz7Ln0@mail.python.org> http://hg.python.org/cpython/rev/adfec512fb32 changeset: 84017:adfec512fb32 parent: 84014:49bcd1af0f49 parent: 84016:f5906026a7e9 user: Senthil Kumaran date: Sun Jun 02 12:00:45 2013 -0700 summary: merge from 3.3 Fix #17967 - Fix related to regression on Windows. os.path.join(*self.dirs) produces an invalid path on windows. ftp paths are always forward-slash seperated like this. /pub/dir. files: Lib/urllib/request.py | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-) diff --git a/Lib/urllib/request.py b/Lib/urllib/request.py --- a/Lib/urllib/request.py +++ b/Lib/urllib/request.py @@ -2306,7 +2306,8 @@ self.ftp = ftplib.FTP() self.ftp.connect(self.host, self.port, self.timeout) self.ftp.login(self.user, self.passwd) - self.ftp.cwd(os.path.join(*self.dirs)) + _target = '/'.join(self.dirs) + self.ftp.cwd(_target) def retrfile(self, file, type): import ftplib -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Mon Jun 3 05:53:05 2013 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Mon, 03 Jun 2013 05:53:05 +0200 Subject: [Python-checkins] Daily reference leaks (adfec512fb32): sum=0 Message-ID: results for adfec512fb32 on branch "default" -------------------------------------------- Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/refloglA7A2l', '-x'] From python-checkins at python.org Mon Jun 3 19:26:03 2013 From: python-checkins at python.org (phillip.eby) Date: Mon, 3 Jun 2013 19:26:03 +0200 (CEST) Subject: [Python-checkins] r89000 - sandbox/branches/setuptools-0.6/setuptools/ssl_support.py Message-ID: <3bPNTR1c5NzRbh@mail.python.org> Author: phillip.eby Date: Mon Jun 3 19:26:03 2013 New Revision: 89000 Log: Fix missing import Modified: sandbox/branches/setuptools-0.6/setuptools/ssl_support.py Modified: sandbox/branches/setuptools-0.6/setuptools/ssl_support.py ============================================================================== --- sandbox/branches/setuptools-0.6/setuptools/ssl_support.py (original) +++ sandbox/branches/setuptools-0.6/setuptools/ssl_support.py Mon Jun 3 19:26:03 2013 @@ -1,4 +1,4 @@ -import sys, os, socket, urllib2, atexit +import sys, os, socket, urllib2, atexit, re from pkg_resources import ResolutionError, ExtractionError, resource_filename try: From python-checkins at python.org Mon Jun 3 19:26:16 2013 From: python-checkins at python.org (phillip.eby) Date: Mon, 3 Jun 2013 19:26:16 +0200 (CEST) Subject: [Python-checkins] r89001 - sandbox/trunk/setuptools/setuptools/ssl_support.py Message-ID: <3bPNTh2jvXzSMx@mail.python.org> Author: phillip.eby Date: Mon Jun 3 19:26:16 2013 New Revision: 89001 Log: Fix missing import Modified: sandbox/trunk/setuptools/setuptools/ssl_support.py Modified: sandbox/trunk/setuptools/setuptools/ssl_support.py ============================================================================== --- sandbox/trunk/setuptools/setuptools/ssl_support.py (original) +++ sandbox/trunk/setuptools/setuptools/ssl_support.py Mon Jun 3 19:26:16 2013 @@ -1,4 +1,4 @@ -import sys, os, socket, urllib2, atexit +import sys, os, socket, urllib2, atexit, re from pkg_resources import ResolutionError, ExtractionError, resource_filename try: From python-checkins at python.org Mon Jun 3 22:15:06 2013 From: python-checkins at python.org (victor.stinner) Date: Mon, 3 Jun 2013 22:15:06 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Fix_compilater_warnings_on?= =?utf-8?q?_Windows_64-bit?= Message-ID: <3bPSDV45trz7LkJ@mail.python.org> http://hg.python.org/cpython/rev/46d8fea24490 changeset: 84018:46d8fea24490 user: Victor Stinner date: Fri May 17 00:19:59 2013 +0200 summary: Fix compilater warnings on Windows 64-bit files: Modules/_sqlite/util.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Modules/_sqlite/util.c b/Modules/_sqlite/util.c --- a/Modules/_sqlite/util.c +++ b/Modules/_sqlite/util.c @@ -132,7 +132,7 @@ } # endif #endif - return PyLong_FromLong(value); + return PyLong_FromLong(Py_SAFE_DOWNCAST(value, sqlite_int64, long)); } sqlite_int64 -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 3 22:15:07 2013 From: python-checkins at python.org (victor.stinner) Date: Mon, 3 Jun 2013 22:15:07 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogQ2xvc2UgIzE4MTA5?= =?utf-8?q?=3A_os=2Euname=28=29_now_decodes_fields_from_the_locale_encodin?= =?utf-8?q?g=2C_and?= Message-ID: <3bPSDW6PKlz7Lkw@mail.python.org> http://hg.python.org/cpython/rev/ffdee6b36305 changeset: 84019:ffdee6b36305 branch: 3.3 parent: 84016:f5906026a7e9 user: Victor Stinner date: Mon Jun 03 22:07:27 2013 +0200 summary: Close #18109: os.uname() now decodes fields from the locale encoding, and socket.gethostname() now decodes the hostname from the locale encoding, instead of using the UTF-8 encoding in strict mode. files: Misc/NEWS | 6 +++++- Modules/posixmodule.c | 2 +- Modules/socketmodule.c | 16 ++++++++-------- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -24,6 +24,10 @@ Library ------- +- Issue #18109: os.uname() now decodes fields from the locale encoding, and + socket.gethostname() now decodes the hostname from the locale encoding, + instead of using the UTF-8 encoding in strict mode. + - Issue #17403: urllib.parse.robotparser normalizes the urls before adding to ruleline. This helps in handling certain types invalid urls in a conservative manner. @@ -69,7 +73,7 @@ - Issue #15392: Create a unittest framework for IDLE. Initial patch by Rajagopalasarma Jayakrishnan. - + - Issue #14146: Highlight source line while debugging on Windows. - Issue #17532: Always include Options menu for IDLE on OS X. diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -4514,7 +4514,7 @@ #define SET(i, field) \ { \ - PyObject *o = PyUnicode_DecodeASCII(field, strlen(field), NULL); \ + PyObject *o = PyUnicode_DecodeFSDefault(field); \ if (!o) { \ Py_DECREF(value); \ return NULL; \ diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -1702,7 +1702,7 @@ return 0; } #endif - + #ifdef PF_SYSTEM case PF_SYSTEM: switch (s->sock_proto) { @@ -1710,10 +1710,10 @@ case SYSPROTO_CONTROL: { struct sockaddr_ctl *addr; - + addr = (struct sockaddr_ctl *)addr_ret; addr->sc_family = AF_SYSTEM; - addr->ss_sysaddr = AF_SYS_CONTROL; + addr->ss_sysaddr = AF_SYS_CONTROL; if (PyUnicode_Check(args)) { struct ctl_info info; @@ -1739,17 +1739,17 @@ "cannot find kernel control with provided name"); return 0; } - + addr->sc_id = info.ctl_id; addr->sc_unit = 0; } else if (!PyArg_ParseTuple(args, "II", &(addr->sc_id), &(addr->sc_unit))) { PyErr_SetString(PyExc_TypeError, "getsockaddrarg: " "expected str or tuple of two ints"); - + return 0; } - + *len_ret = sizeof(*addr); return 1; } @@ -1866,7 +1866,7 @@ return 1; } #endif - + #ifdef PF_SYSTEM case PF_SYSTEM: switch(s->sock_proto) { @@ -4111,7 +4111,7 @@ if (res < 0) return set_error(); buf[sizeof buf - 1] = '\0'; - return PyUnicode_FromString(buf); + return PyUnicode_DecodeFSDefault(buf); #endif } -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 3 22:15:09 2013 From: python-checkins at python.org (victor.stinner) Date: Mon, 3 Jun 2013 22:15:09 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?b?KTogKE1lcmdlIDMuMykgQ2xvc2UgIzE4MTA5OiBvcy51bmFtZSgpIG5vdyBk?= =?utf-8?q?ecodes_fields_from_the_locale?= Message-ID: <3bPSDY1fTgz7LmX@mail.python.org> http://hg.python.org/cpython/rev/2472603af83e changeset: 84020:2472603af83e parent: 84018:46d8fea24490 parent: 84019:ffdee6b36305 user: Victor Stinner date: Mon Jun 03 22:09:14 2013 +0200 summary: (Merge 3.3) Close #18109: os.uname() now decodes fields from the locale encoding, and socket.gethostname() now decodes the hostname from the locale encoding, instead of using the UTF-8 encoding in strict mode. files: Misc/NEWS | 6 +++++- Modules/posixmodule.c | 2 +- Modules/socketmodule.c | 16 ++++++++-------- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -103,6 +103,10 @@ Library ------- +- Issue #18109: os.uname() now decodes fields from the locale encoding, and + socket.gethostname() now decodes the hostname from the locale encoding, + instead of using the UTF-8 encoding in strict mode. + - Issue #18089: Implement importlib.abc.InspectLoader.load_module. - Issue #18088: Introduce importlib.abc.Loader.init_module_attrs for setting @@ -416,7 +420,7 @@ - Issue #15392: Create a unittest framework for IDLE. Initial patch by Rajagopalasarma Jayakrishnan. - + - Issue #14146: Highlight source line while debugging on Windows. - Issue #17838: Allow sys.stdin to be reassigned. diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -4257,7 +4257,7 @@ #define SET(i, field) \ { \ - PyObject *o = PyUnicode_DecodeASCII(field, strlen(field), NULL); \ + PyObject *o = PyUnicode_DecodeFSDefault(field); \ if (!o) { \ Py_DECREF(value); \ return NULL; \ diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -1644,7 +1644,7 @@ return 0; } #endif - + #ifdef PF_SYSTEM case PF_SYSTEM: switch (s->sock_proto) { @@ -1652,10 +1652,10 @@ case SYSPROTO_CONTROL: { struct sockaddr_ctl *addr; - + addr = (struct sockaddr_ctl *)addr_ret; addr->sc_family = AF_SYSTEM; - addr->ss_sysaddr = AF_SYS_CONTROL; + addr->ss_sysaddr = AF_SYS_CONTROL; if (PyUnicode_Check(args)) { struct ctl_info info; @@ -1681,17 +1681,17 @@ "cannot find kernel control with provided name"); return 0; } - + addr->sc_id = info.ctl_id; addr->sc_unit = 0; } else if (!PyArg_ParseTuple(args, "II", &(addr->sc_id), &(addr->sc_unit))) { PyErr_SetString(PyExc_TypeError, "getsockaddrarg: " "expected str or tuple of two ints"); - + return 0; } - + *len_ret = sizeof(*addr); return 1; } @@ -1808,7 +1808,7 @@ return 1; } #endif - + #ifdef PF_SYSTEM case PF_SYSTEM: switch(s->sock_proto) { @@ -4048,7 +4048,7 @@ if (res < 0) return set_error(); buf[sizeof buf - 1] = '\0'; - return PyUnicode_FromString(buf); + return PyUnicode_DecodeFSDefault(buf); #endif } -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Tue Jun 4 05:51:18 2013 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Tue, 04 Jun 2013 05:51:18 +0200 Subject: [Python-checkins] Daily reference leaks (2472603af83e): sum=0 Message-ID: results for 2472603af83e on branch "default" -------------------------------------------- Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogIOah9N', '-x'] From python-checkins at python.org Tue Jun 4 23:03:11 2013 From: python-checkins at python.org (victor.stinner) Date: Tue, 4 Jun 2013 23:03:11 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Close_=2317932=3A_Fix_an_i?= =?utf-8?q?nteger_overflow_issue_on_Windows_64-bit_in_iterators=3A?= Message-ID: <3bQ5FW4xQ4z7Ljc@mail.python.org> http://hg.python.org/cpython/rev/757a121a27c2 changeset: 84021:757a121a27c2 user: Victor Stinner date: Tue Jun 04 23:02:46 2013 +0200 summary: Close #17932: Fix an integer overflow issue on Windows 64-bit in iterators: change the C type of seqiterobject.it_index from long to Py_ssize_t. files: Misc/NEWS | 3 +++ Objects/iterobject.c | 2 +- 2 files changed, 4 insertions(+), 1 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,9 @@ Core and Builtins ----------------- +- Issue #17932: Fix an integer overflow issue on Windows 64-bit in iterators: + change the C type of seqiterobject.it_index from long to Py_ssize_t. + - Issue #18065: Don't set __path__ to the package name for frozen packages. - Issue #18088: When reloading a module, unconditionally reset all relevant diff --git a/Objects/iterobject.c b/Objects/iterobject.c --- a/Objects/iterobject.c +++ b/Objects/iterobject.c @@ -4,7 +4,7 @@ typedef struct { PyObject_HEAD - long it_index; + Py_ssize_t it_index; PyObject *it_seq; /* Set to NULL when iterator is exhausted */ } seqiterobject; -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 4 23:15:41 2013 From: python-checkins at python.org (victor.stinner) Date: Tue, 4 Jun 2013 23:15:41 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Reuse_Py=5FMIN_and_Py=5FMA?= =?utf-8?q?X_macros=3A_remove_duplicate_MIN/MAX_macros?= Message-ID: <3bQ5Wx3t8zzSyS@mail.python.org> http://hg.python.org/cpython/rev/41a2cbe23349 changeset: 84022:41a2cbe23349 user: Victor Stinner date: Tue Jun 04 23:14:37 2013 +0200 summary: Reuse Py_MIN and Py_MAX macros: remove duplicate MIN/MAX macros multiprocessing.h: remove unused MIN and MAX macros files: Modules/_bz2module.c | 12 +++----- Modules/_cursesmodule.c | 14 +++------ Modules/_multiprocessing/multiprocessing.h | 9 ------ Modules/md5module.c | 6 +--- Modules/sha1module.c | 6 +--- Modules/socketmodule.c | 7 +--- Objects/floatobject.c | 13 ++------ Objects/frameobject.c | 11 ++----- Objects/longobject.c | 13 ++------ 9 files changed, 25 insertions(+), 66 deletions(-) diff --git a/Modules/_bz2module.c b/Modules/_bz2module.c --- a/Modules/_bz2module.c +++ b/Modules/_bz2module.c @@ -36,8 +36,6 @@ #define RELEASE_LOCK(obj) #endif -#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y)) - typedef struct { PyObject_HEAD @@ -157,7 +155,7 @@ /* On a 64-bit system, len might not fit in avail_in (an unsigned int). Do compression in chunks of no more than UINT_MAX bytes each. */ if (c->bzs.avail_in == 0 && len > 0) { - c->bzs.avail_in = MIN(len, UINT_MAX); + c->bzs.avail_in = Py_MIN(len, UINT_MAX); len -= c->bzs.avail_in; } @@ -173,7 +171,7 @@ c->bzs.next_out = PyBytes_AS_STRING(result) + data_size; buffer_left = PyBytes_GET_SIZE(result) - data_size; } - c->bzs.avail_out = MIN(buffer_left, UINT_MAX); + c->bzs.avail_out = Py_MIN(buffer_left, UINT_MAX); } Py_BEGIN_ALLOW_THREADS @@ -370,7 +368,7 @@ d->bzs.next_in = data; /* On a 64-bit system, len might not fit in avail_in (an unsigned int). Do decompression in chunks of no more than UINT_MAX bytes each. */ - d->bzs.avail_in = MIN(len, UINT_MAX); + d->bzs.avail_in = Py_MIN(len, UINT_MAX); len -= d->bzs.avail_in; d->bzs.next_out = PyBytes_AS_STRING(result); d->bzs.avail_out = PyBytes_GET_SIZE(result); @@ -399,7 +397,7 @@ if (d->bzs.avail_in == 0) { if (len == 0) break; - d->bzs.avail_in = MIN(len, UINT_MAX); + d->bzs.avail_in = Py_MIN(len, UINT_MAX); len -= d->bzs.avail_in; } if (d->bzs.avail_out == 0) { @@ -410,7 +408,7 @@ d->bzs.next_out = PyBytes_AS_STRING(result) + data_size; buffer_left = PyBytes_GET_SIZE(result) - data_size; } - d->bzs.avail_out = MIN(buffer_left, UINT_MAX); + d->bzs.avail_out = Py_MIN(buffer_left, UINT_MAX); } } if (data_size != PyBytes_GET_SIZE(result)) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -168,10 +168,6 @@ "must call start_color() first"); \ return 0; } -#ifndef MIN -#define MIN(x,y) ((x) < (y) ? (x) : (y)) -#endif - /* Utility Functions */ /* @@ -1212,7 +1208,7 @@ if (!PyArg_ParseTuple(args,"i;n", &n)) return NULL; Py_BEGIN_ALLOW_THREADS - rtn2 = wgetnstr(self->win,rtn,MIN(n, 1023)); + rtn2 = wgetnstr(self->win, rtn, Py_MIN(n, 1023)); Py_END_ALLOW_THREADS break; case 2: @@ -1232,11 +1228,11 @@ #ifdef STRICT_SYSV_CURSES Py_BEGIN_ALLOW_THREADS rtn2 = wmove(self->win,y,x)==ERR ? ERR : - wgetnstr(self->win, rtn, MIN(n, 1023)); + wgetnstr(self->win, rtn, Py_MIN(n, 1023)); Py_END_ALLOW_THREADS #else Py_BEGIN_ALLOW_THREADS - rtn2 = mvwgetnstr(self->win, y, x, rtn, MIN(n, 1023)); + rtn2 = mvwgetnstr(self->win, y, x, rtn, Py_MIN(n, 1023)); Py_END_ALLOW_THREADS #endif break; @@ -1374,7 +1370,7 @@ case 1: if (!PyArg_ParseTuple(args,"i;n", &n)) return NULL; - rtn2 = winnstr(self->win,rtn,MIN(n,1023)); + rtn2 = winnstr(self->win, rtn, Py_MIN(n, 1023)); break; case 2: if (!PyArg_ParseTuple(args,"ii;y,x",&y,&x)) @@ -1384,7 +1380,7 @@ case 3: if (!PyArg_ParseTuple(args, "iii;y,x,n", &y, &x, &n)) return NULL; - rtn2 = mvwinnstr(self->win, y, x, rtn, MIN(n,1023)); + rtn2 = mvwinnstr(self->win, y, x, rtn, Py_MIN(n,1023)); break; default: PyErr_SetString(PyExc_TypeError, "instr requires 0 or 3 arguments"); diff --git a/Modules/_multiprocessing/multiprocessing.h b/Modules/_multiprocessing/multiprocessing.h --- a/Modules/_multiprocessing/multiprocessing.h +++ b/Modules/_multiprocessing/multiprocessing.h @@ -99,13 +99,4 @@ extern PyTypeObject _PyMp_SemLockType; -/* - * Miscellaneous - */ - -#ifndef MIN -# define MIN(x, y) ((x) < (y) ? x : y) -# define MAX(x, y) ((x) > (y) ? x : y) -#endif - #endif /* MULTIPROCESSING_H */ diff --git a/Modules/md5module.c b/Modules/md5module.c --- a/Modules/md5module.c +++ b/Modules/md5module.c @@ -91,10 +91,6 @@ (y)[3] = (unsigned char)(((x)>>24)&255); (y)[2] = (unsigned char)(((x)>>16)&255); \ (y)[1] = (unsigned char)(((x)>>8)&255); (y)[0] = (unsigned char)((x)&255); } -#ifndef MIN - #define MIN(x, y) ( ((x)<(y))?(x):(y) ) -#endif - /* MD5 macros */ @@ -244,7 +240,7 @@ in += MD5_BLOCKSIZE; inlen -= MD5_BLOCKSIZE; } else { - n = MIN(inlen, (Py_ssize_t)(MD5_BLOCKSIZE - md5->curlen)); + n = Py_MIN(inlen, (Py_ssize_t)(MD5_BLOCKSIZE - md5->curlen)); memcpy(md5->buf + md5->curlen, in, (size_t)n); md5->curlen += (MD5_INT32)n; in += n; diff --git a/Modules/sha1module.c b/Modules/sha1module.c --- a/Modules/sha1module.c +++ b/Modules/sha1module.c @@ -92,10 +92,6 @@ (y)[4] = (unsigned char)(((x)>>24)&255); (y)[5] = (unsigned char)(((x)>>16)&255); \ (y)[6] = (unsigned char)(((x)>>8)&255); (y)[7] = (unsigned char)((x)&255); } -#ifndef MIN - #define MIN(x, y) ( ((x)<(y))?(x):(y) ) -#endif - /* SHA1 macros */ @@ -220,7 +216,7 @@ in += SHA1_BLOCKSIZE; inlen -= SHA1_BLOCKSIZE; } else { - n = MIN(inlen, (Py_ssize_t)(SHA1_BLOCKSIZE - sha1->curlen)); + n = Py_MIN(inlen, (Py_ssize_t)(SHA1_BLOCKSIZE - sha1->curlen)); memcpy(sha1->buf + sha1->curlen, in, (size_t)n); sha1->curlen += (SHA1_INT32)n; in += n; diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -95,9 +95,6 @@ #include "Python.h" #include "structmember.h" -#undef MAX -#define MAX(x, y) ((x) < (y) ? (y) : (x)) - /* Socket object documentation */ PyDoc_STRVAR(sock_doc, "socket([family[, type[, proto]]]) -> socket object\n\ @@ -4819,7 +4816,7 @@ char* ip; int retval; #ifdef ENABLE_IPV6 - char packed[MAX(sizeof(struct in_addr), sizeof(struct in6_addr))]; + char packed[Py_MAX(sizeof(struct in_addr), sizeof(struct in6_addr))]; #else char packed[sizeof(struct in_addr)]; #endif @@ -4870,7 +4867,7 @@ int len; const char* retval; #ifdef ENABLE_IPV6 - char ip[MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) + 1]; + char ip[Py_MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) + 1]; #else char ip[INET_ADDRSTRLEN + 1]; #endif diff --git a/Objects/floatobject.c b/Objects/floatobject.c --- a/Objects/floatobject.c +++ b/Objects/floatobject.c @@ -9,11 +9,6 @@ #include #include -#undef MAX -#undef MIN -#define MAX(x, y) ((x) < (y) ? (y) : (x)) -#define MIN(x, y) ((x) < (y) ? (x) : (y)) - /* Special free list free_list is a singly-linked list of available PyFloatObjects, linked @@ -1131,7 +1126,7 @@ } m = frexp(fabs(x), &e); - shift = 1 - MAX(DBL_MIN_EXP - e, 0); + shift = 1 - Py_MAX(DBL_MIN_EXP - e, 0); m = ldexp(m, shift); e -= shift; @@ -1285,8 +1280,8 @@ fdigits = coeff_end - s_store; if (ndigits == 0) goto parse_error; - if (ndigits > MIN(DBL_MIN_EXP - DBL_MANT_DIG - LONG_MIN/2, - LONG_MAX/2 + 1 - DBL_MAX_EXP)/4) + if (ndigits > Py_MIN(DBL_MIN_EXP - DBL_MANT_DIG - LONG_MIN/2, + LONG_MAX/2 + 1 - DBL_MAX_EXP)/4) goto insane_length_error; /* [p ] */ @@ -1342,7 +1337,7 @@ /* lsb = exponent of least significant bit of the *rounded* value. This is top_exp - DBL_MANT_DIG unless result is subnormal. */ - lsb = MAX(top_exp, (long)DBL_MIN_EXP) - DBL_MANT_DIG; + lsb = Py_MAX(top_exp, (long)DBL_MIN_EXP) - DBL_MANT_DIG; x = 0.0; if (exp >= lsb) { diff --git a/Objects/frameobject.c b/Objects/frameobject.c --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -7,11 +7,6 @@ #include "opcode.h" #include "structmember.h" -#undef MIN -#undef MAX -#define MIN(a, b) ((a) < (b) ? (a) : (b)) -#define MAX(a, b) ((a) > (b) ? (a) : (b)) - #define OFF(x) offsetof(PyFrameObject, x) static PyMemberDef frame_memberlist[] = { @@ -160,8 +155,8 @@ /* We're now ready to look at the bytecode. */ PyBytes_AsStringAndSize(f->f_code->co_code, (char **)&code, &code_len); - min_addr = MIN(new_lasti, f->f_lasti); - max_addr = MAX(new_lasti, f->f_lasti); + min_addr = Py_MIN(new_lasti, f->f_lasti); + max_addr = Py_MAX(new_lasti, f->f_lasti); /* You can't jump onto a line with an 'except' statement on it - * they expect to have an exception on the top of the stack, which @@ -293,7 +288,7 @@ break; } - min_delta_iblock = MIN(min_delta_iblock, delta_iblock); + min_delta_iblock = Py_MIN(min_delta_iblock, delta_iblock); if (op >= HAVE_ARGUMENT) { addr += 2; diff --git a/Objects/longobject.c b/Objects/longobject.c --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -89,11 +89,6 @@ */ #define FIVEARY_CUTOFF 8 -#undef MIN -#undef MAX -#define MAX(x, y) ((x) < (y) ? (y) : (x)) -#define MIN(x, y) ((x) > (y) ? (y) : (x)) - #define SIGCHECK(PyTryBlock) \ do { \ if (PyErr_CheckSignals()) PyTryBlock \ @@ -3029,7 +3024,7 @@ Py_ssize_t size_lo, size_hi; const Py_ssize_t size_n = ABS(Py_SIZE(n)); - size_lo = MIN(size_n, size); + size_lo = Py_MIN(size_n, size); size_hi = size_n - size_lo; if ((hi = _PyLong_New(size_hi)) == NULL) @@ -3300,7 +3295,7 @@ nbdone = 0; while (bsize > 0) { PyLongObject *product; - const Py_ssize_t nbtouse = MIN(bsize, asize); + const Py_ssize_t nbtouse = Py_MIN(bsize, asize); /* Multiply the next slice of b by a. */ memcpy(bslice->ob_digit, b->ob_digit + nbdone, @@ -3591,7 +3586,7 @@ goto underflow_or_zero; /* Choose value for shift; see comments for step 1 above. */ - shift = MAX(diff, DBL_MIN_EXP) - DBL_MANT_DIG - 2; + shift = Py_MAX(diff, DBL_MIN_EXP) - DBL_MANT_DIG - 2; inexact = 0; @@ -3662,7 +3657,7 @@ x_bits = (x_size-1)*PyLong_SHIFT+bits_in_digit(x->ob_digit[x_size-1]); /* The number of extra bits that have to be rounded away. */ - extra_bits = MAX(x_bits, DBL_MIN_EXP - shift) - DBL_MANT_DIG; + extra_bits = Py_MAX(x_bits, DBL_MIN_EXP - shift) - DBL_MANT_DIG; assert(extra_bits == 2 || extra_bits == 3); /* Round by directly modifying the low digit of x. */ -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 4 23:36:16 2013 From: python-checkins at python.org (brett.cannon) Date: Tue, 4 Jun 2013 23:36:16 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Tweak_at_the_suggestion_of?= =?utf-8?q?_Ezio_Melotti_for_exception_messages_when?= Message-ID: <3bQ5zh1q7PzSRc@mail.python.org> http://hg.python.org/cpython/rev/9e833c1edeb6 changeset: 84023:9e833c1edeb6 parent: 84020:2472603af83e user: Brett Cannon date: Tue Jun 04 17:34:49 2013 -0400 summary: Tweak at the suggestion of Ezio Melotti for exception messages when EOF is hit while trying to read the header of a bytecode file. files: Lib/importlib/_bootstrap.py | 4 +- Misc/NEWS | 3 + Python/importlib.h | 4610 +++++++++++----------- 3 files changed, 2311 insertions(+), 2306 deletions(-) diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py --- a/Lib/importlib/_bootstrap.py +++ b/Lib/importlib/_bootstrap.py @@ -668,11 +668,11 @@ _verbose_message(message) raise ImportError(message, **exc_details) elif len(raw_timestamp) != 4: - message = 'incomplete timestamp in {!r}'.format(name) + message = 'reached EOF while reading magic number in {!r}'.format(name) _verbose_message(message) raise EOFError(message) elif len(raw_size) != 4: - message = 'incomplete size in {!r}'.format(name) + message = 'reached EOF while reading size in {!r}'.format(name) _verbose_message(message) raise EOFError(message) if source_stats is not None: diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,9 @@ Core and Builtins ----------------- +- Tweak the exception message when the magic number or size value in a bytecode + file is truncated. + - Issue #18065: Don't set __path__ to the package name for frozen packages. - Issue #18088: When reloading a module, unconditionally reset all relevant diff --git a/Python/importlib.h b/Python/importlib.h --- a/Python/importlib.h +++ b/Python/importlib.h [stripped] -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 4 23:36:17 2013 From: python-checkins at python.org (brett.cannon) Date: Tue, 4 Jun 2013 23:36:17 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_default_-=3E_default?= =?utf-8?q?=29=3A_merge?= Message-ID: <3bQ5zj5ZTRzSVJ@mail.python.org> http://hg.python.org/cpython/rev/b9af38d33606 changeset: 84024:b9af38d33606 parent: 84023:9e833c1edeb6 parent: 84022:41a2cbe23349 user: Brett Cannon date: Tue Jun 04 17:36:07 2013 -0400 summary: merge files: Misc/NEWS | 3 ++ Modules/_bz2module.c | 12 +++----- Modules/_cursesmodule.c | 14 +++------ Modules/_multiprocessing/multiprocessing.h | 9 ------ Modules/md5module.c | 6 +--- Modules/sha1module.c | 6 +--- Modules/socketmodule.c | 7 +--- Objects/floatobject.c | 13 ++------ Objects/frameobject.c | 11 ++----- Objects/iterobject.c | 2 +- Objects/longobject.c | 13 ++------ 11 files changed, 29 insertions(+), 67 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -13,6 +13,9 @@ - Tweak the exception message when the magic number or size value in a bytecode file is truncated. +- Issue #17932: Fix an integer overflow issue on Windows 64-bit in iterators: + change the C type of seqiterobject.it_index from long to Py_ssize_t. + - Issue #18065: Don't set __path__ to the package name for frozen packages. - Issue #18088: When reloading a module, unconditionally reset all relevant diff --git a/Modules/_bz2module.c b/Modules/_bz2module.c --- a/Modules/_bz2module.c +++ b/Modules/_bz2module.c @@ -36,8 +36,6 @@ #define RELEASE_LOCK(obj) #endif -#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y)) - typedef struct { PyObject_HEAD @@ -157,7 +155,7 @@ /* On a 64-bit system, len might not fit in avail_in (an unsigned int). Do compression in chunks of no more than UINT_MAX bytes each. */ if (c->bzs.avail_in == 0 && len > 0) { - c->bzs.avail_in = MIN(len, UINT_MAX); + c->bzs.avail_in = Py_MIN(len, UINT_MAX); len -= c->bzs.avail_in; } @@ -173,7 +171,7 @@ c->bzs.next_out = PyBytes_AS_STRING(result) + data_size; buffer_left = PyBytes_GET_SIZE(result) - data_size; } - c->bzs.avail_out = MIN(buffer_left, UINT_MAX); + c->bzs.avail_out = Py_MIN(buffer_left, UINT_MAX); } Py_BEGIN_ALLOW_THREADS @@ -370,7 +368,7 @@ d->bzs.next_in = data; /* On a 64-bit system, len might not fit in avail_in (an unsigned int). Do decompression in chunks of no more than UINT_MAX bytes each. */ - d->bzs.avail_in = MIN(len, UINT_MAX); + d->bzs.avail_in = Py_MIN(len, UINT_MAX); len -= d->bzs.avail_in; d->bzs.next_out = PyBytes_AS_STRING(result); d->bzs.avail_out = PyBytes_GET_SIZE(result); @@ -399,7 +397,7 @@ if (d->bzs.avail_in == 0) { if (len == 0) break; - d->bzs.avail_in = MIN(len, UINT_MAX); + d->bzs.avail_in = Py_MIN(len, UINT_MAX); len -= d->bzs.avail_in; } if (d->bzs.avail_out == 0) { @@ -410,7 +408,7 @@ d->bzs.next_out = PyBytes_AS_STRING(result) + data_size; buffer_left = PyBytes_GET_SIZE(result) - data_size; } - d->bzs.avail_out = MIN(buffer_left, UINT_MAX); + d->bzs.avail_out = Py_MIN(buffer_left, UINT_MAX); } } if (data_size != PyBytes_GET_SIZE(result)) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -168,10 +168,6 @@ "must call start_color() first"); \ return 0; } -#ifndef MIN -#define MIN(x,y) ((x) < (y) ? (x) : (y)) -#endif - /* Utility Functions */ /* @@ -1212,7 +1208,7 @@ if (!PyArg_ParseTuple(args,"i;n", &n)) return NULL; Py_BEGIN_ALLOW_THREADS - rtn2 = wgetnstr(self->win,rtn,MIN(n, 1023)); + rtn2 = wgetnstr(self->win, rtn, Py_MIN(n, 1023)); Py_END_ALLOW_THREADS break; case 2: @@ -1232,11 +1228,11 @@ #ifdef STRICT_SYSV_CURSES Py_BEGIN_ALLOW_THREADS rtn2 = wmove(self->win,y,x)==ERR ? ERR : - wgetnstr(self->win, rtn, MIN(n, 1023)); + wgetnstr(self->win, rtn, Py_MIN(n, 1023)); Py_END_ALLOW_THREADS #else Py_BEGIN_ALLOW_THREADS - rtn2 = mvwgetnstr(self->win, y, x, rtn, MIN(n, 1023)); + rtn2 = mvwgetnstr(self->win, y, x, rtn, Py_MIN(n, 1023)); Py_END_ALLOW_THREADS #endif break; @@ -1374,7 +1370,7 @@ case 1: if (!PyArg_ParseTuple(args,"i;n", &n)) return NULL; - rtn2 = winnstr(self->win,rtn,MIN(n,1023)); + rtn2 = winnstr(self->win, rtn, Py_MIN(n, 1023)); break; case 2: if (!PyArg_ParseTuple(args,"ii;y,x",&y,&x)) @@ -1384,7 +1380,7 @@ case 3: if (!PyArg_ParseTuple(args, "iii;y,x,n", &y, &x, &n)) return NULL; - rtn2 = mvwinnstr(self->win, y, x, rtn, MIN(n,1023)); + rtn2 = mvwinnstr(self->win, y, x, rtn, Py_MIN(n,1023)); break; default: PyErr_SetString(PyExc_TypeError, "instr requires 0 or 3 arguments"); diff --git a/Modules/_multiprocessing/multiprocessing.h b/Modules/_multiprocessing/multiprocessing.h --- a/Modules/_multiprocessing/multiprocessing.h +++ b/Modules/_multiprocessing/multiprocessing.h @@ -99,13 +99,4 @@ extern PyTypeObject _PyMp_SemLockType; -/* - * Miscellaneous - */ - -#ifndef MIN -# define MIN(x, y) ((x) < (y) ? x : y) -# define MAX(x, y) ((x) > (y) ? x : y) -#endif - #endif /* MULTIPROCESSING_H */ diff --git a/Modules/md5module.c b/Modules/md5module.c --- a/Modules/md5module.c +++ b/Modules/md5module.c @@ -91,10 +91,6 @@ (y)[3] = (unsigned char)(((x)>>24)&255); (y)[2] = (unsigned char)(((x)>>16)&255); \ (y)[1] = (unsigned char)(((x)>>8)&255); (y)[0] = (unsigned char)((x)&255); } -#ifndef MIN - #define MIN(x, y) ( ((x)<(y))?(x):(y) ) -#endif - /* MD5 macros */ @@ -244,7 +240,7 @@ in += MD5_BLOCKSIZE; inlen -= MD5_BLOCKSIZE; } else { - n = MIN(inlen, (Py_ssize_t)(MD5_BLOCKSIZE - md5->curlen)); + n = Py_MIN(inlen, (Py_ssize_t)(MD5_BLOCKSIZE - md5->curlen)); memcpy(md5->buf + md5->curlen, in, (size_t)n); md5->curlen += (MD5_INT32)n; in += n; diff --git a/Modules/sha1module.c b/Modules/sha1module.c --- a/Modules/sha1module.c +++ b/Modules/sha1module.c @@ -92,10 +92,6 @@ (y)[4] = (unsigned char)(((x)>>24)&255); (y)[5] = (unsigned char)(((x)>>16)&255); \ (y)[6] = (unsigned char)(((x)>>8)&255); (y)[7] = (unsigned char)((x)&255); } -#ifndef MIN - #define MIN(x, y) ( ((x)<(y))?(x):(y) ) -#endif - /* SHA1 macros */ @@ -220,7 +216,7 @@ in += SHA1_BLOCKSIZE; inlen -= SHA1_BLOCKSIZE; } else { - n = MIN(inlen, (Py_ssize_t)(SHA1_BLOCKSIZE - sha1->curlen)); + n = Py_MIN(inlen, (Py_ssize_t)(SHA1_BLOCKSIZE - sha1->curlen)); memcpy(sha1->buf + sha1->curlen, in, (size_t)n); sha1->curlen += (SHA1_INT32)n; in += n; diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -95,9 +95,6 @@ #include "Python.h" #include "structmember.h" -#undef MAX -#define MAX(x, y) ((x) < (y) ? (y) : (x)) - /* Socket object documentation */ PyDoc_STRVAR(sock_doc, "socket([family[, type[, proto]]]) -> socket object\n\ @@ -4819,7 +4816,7 @@ char* ip; int retval; #ifdef ENABLE_IPV6 - char packed[MAX(sizeof(struct in_addr), sizeof(struct in6_addr))]; + char packed[Py_MAX(sizeof(struct in_addr), sizeof(struct in6_addr))]; #else char packed[sizeof(struct in_addr)]; #endif @@ -4870,7 +4867,7 @@ int len; const char* retval; #ifdef ENABLE_IPV6 - char ip[MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) + 1]; + char ip[Py_MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) + 1]; #else char ip[INET_ADDRSTRLEN + 1]; #endif diff --git a/Objects/floatobject.c b/Objects/floatobject.c --- a/Objects/floatobject.c +++ b/Objects/floatobject.c @@ -9,11 +9,6 @@ #include #include -#undef MAX -#undef MIN -#define MAX(x, y) ((x) < (y) ? (y) : (x)) -#define MIN(x, y) ((x) < (y) ? (x) : (y)) - /* Special free list free_list is a singly-linked list of available PyFloatObjects, linked @@ -1131,7 +1126,7 @@ } m = frexp(fabs(x), &e); - shift = 1 - MAX(DBL_MIN_EXP - e, 0); + shift = 1 - Py_MAX(DBL_MIN_EXP - e, 0); m = ldexp(m, shift); e -= shift; @@ -1285,8 +1280,8 @@ fdigits = coeff_end - s_store; if (ndigits == 0) goto parse_error; - if (ndigits > MIN(DBL_MIN_EXP - DBL_MANT_DIG - LONG_MIN/2, - LONG_MAX/2 + 1 - DBL_MAX_EXP)/4) + if (ndigits > Py_MIN(DBL_MIN_EXP - DBL_MANT_DIG - LONG_MIN/2, + LONG_MAX/2 + 1 - DBL_MAX_EXP)/4) goto insane_length_error; /* [p ] */ @@ -1342,7 +1337,7 @@ /* lsb = exponent of least significant bit of the *rounded* value. This is top_exp - DBL_MANT_DIG unless result is subnormal. */ - lsb = MAX(top_exp, (long)DBL_MIN_EXP) - DBL_MANT_DIG; + lsb = Py_MAX(top_exp, (long)DBL_MIN_EXP) - DBL_MANT_DIG; x = 0.0; if (exp >= lsb) { diff --git a/Objects/frameobject.c b/Objects/frameobject.c --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -7,11 +7,6 @@ #include "opcode.h" #include "structmember.h" -#undef MIN -#undef MAX -#define MIN(a, b) ((a) < (b) ? (a) : (b)) -#define MAX(a, b) ((a) > (b) ? (a) : (b)) - #define OFF(x) offsetof(PyFrameObject, x) static PyMemberDef frame_memberlist[] = { @@ -160,8 +155,8 @@ /* We're now ready to look at the bytecode. */ PyBytes_AsStringAndSize(f->f_code->co_code, (char **)&code, &code_len); - min_addr = MIN(new_lasti, f->f_lasti); - max_addr = MAX(new_lasti, f->f_lasti); + min_addr = Py_MIN(new_lasti, f->f_lasti); + max_addr = Py_MAX(new_lasti, f->f_lasti); /* You can't jump onto a line with an 'except' statement on it - * they expect to have an exception on the top of the stack, which @@ -293,7 +288,7 @@ break; } - min_delta_iblock = MIN(min_delta_iblock, delta_iblock); + min_delta_iblock = Py_MIN(min_delta_iblock, delta_iblock); if (op >= HAVE_ARGUMENT) { addr += 2; diff --git a/Objects/iterobject.c b/Objects/iterobject.c --- a/Objects/iterobject.c +++ b/Objects/iterobject.c @@ -4,7 +4,7 @@ typedef struct { PyObject_HEAD - long it_index; + Py_ssize_t it_index; PyObject *it_seq; /* Set to NULL when iterator is exhausted */ } seqiterobject; diff --git a/Objects/longobject.c b/Objects/longobject.c --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -89,11 +89,6 @@ */ #define FIVEARY_CUTOFF 8 -#undef MIN -#undef MAX -#define MAX(x, y) ((x) < (y) ? (y) : (x)) -#define MIN(x, y) ((x) > (y) ? (y) : (x)) - #define SIGCHECK(PyTryBlock) \ do { \ if (PyErr_CheckSignals()) PyTryBlock \ @@ -3029,7 +3024,7 @@ Py_ssize_t size_lo, size_hi; const Py_ssize_t size_n = ABS(Py_SIZE(n)); - size_lo = MIN(size_n, size); + size_lo = Py_MIN(size_n, size); size_hi = size_n - size_lo; if ((hi = _PyLong_New(size_hi)) == NULL) @@ -3300,7 +3295,7 @@ nbdone = 0; while (bsize > 0) { PyLongObject *product; - const Py_ssize_t nbtouse = MIN(bsize, asize); + const Py_ssize_t nbtouse = Py_MIN(bsize, asize); /* Multiply the next slice of b by a. */ memcpy(bslice->ob_digit, b->ob_digit + nbdone, @@ -3591,7 +3586,7 @@ goto underflow_or_zero; /* Choose value for shift; see comments for step 1 above. */ - shift = MAX(diff, DBL_MIN_EXP) - DBL_MANT_DIG - 2; + shift = Py_MAX(diff, DBL_MIN_EXP) - DBL_MANT_DIG - 2; inexact = 0; @@ -3662,7 +3657,7 @@ x_bits = (x_size-1)*PyLong_SHIFT+bits_in_digit(x->ob_digit[x_size-1]); /* The number of extra bits that have to be rounded away. */ - extra_bits = MAX(x_bits, DBL_MIN_EXP - shift) - DBL_MANT_DIG; + extra_bits = Py_MAX(x_bits, DBL_MIN_EXP - shift) - DBL_MANT_DIG; assert(extra_bits == 2 || extra_bits == 3); /* Round by directly modifying the low digit of x. */ -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 4 23:57:56 2013 From: python-checkins at python.org (victor.stinner) Date: Tue, 4 Jun 2013 23:57:56 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=239566=3A_Fix_compi?= =?utf-8?q?ler_warning_on_Windows_64-bit_in_=5Fbz2module=2Ec?= Message-ID: <3bQ6Sh6kNPzSsq@mail.python.org> http://hg.python.org/cpython/rev/46401ce03547 changeset: 84025:46401ce03547 user: Victor Stinner date: Tue Jun 04 23:18:48 2013 +0200 summary: Issue #9566: Fix compiler warning on Windows 64-bit in _bz2module.c files: Modules/_bz2module.c | 14 +++++++------- 1 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Modules/_bz2module.c b/Modules/_bz2module.c --- a/Modules/_bz2module.c +++ b/Modules/_bz2module.c @@ -147,7 +147,7 @@ c->bzs.next_in = data; c->bzs.avail_in = 0; c->bzs.next_out = PyBytes_AS_STRING(result); - c->bzs.avail_out = PyBytes_GET_SIZE(result); + c->bzs.avail_out = SMALLCHUNK; for (;;) { char *this_out; int bzerror; @@ -155,7 +155,7 @@ /* On a 64-bit system, len might not fit in avail_in (an unsigned int). Do compression in chunks of no more than UINT_MAX bytes each. */ if (c->bzs.avail_in == 0 && len > 0) { - c->bzs.avail_in = Py_MIN(len, UINT_MAX); + c->bzs.avail_in = (unsigned int)Py_MIN(len, UINT_MAX); len -= c->bzs.avail_in; } @@ -171,7 +171,7 @@ c->bzs.next_out = PyBytes_AS_STRING(result) + data_size; buffer_left = PyBytes_GET_SIZE(result) - data_size; } - c->bzs.avail_out = Py_MIN(buffer_left, UINT_MAX); + c->bzs.avail_out = (unsigned int)Py_MIN(buffer_left, UINT_MAX); } Py_BEGIN_ALLOW_THREADS @@ -368,10 +368,10 @@ d->bzs.next_in = data; /* On a 64-bit system, len might not fit in avail_in (an unsigned int). Do decompression in chunks of no more than UINT_MAX bytes each. */ - d->bzs.avail_in = Py_MIN(len, UINT_MAX); + d->bzs.avail_in = (unsigned int)Py_MIN(len, UINT_MAX); len -= d->bzs.avail_in; d->bzs.next_out = PyBytes_AS_STRING(result); - d->bzs.avail_out = PyBytes_GET_SIZE(result); + d->bzs.avail_out = SMALLCHUNK; for (;;) { char *this_out; int bzerror; @@ -397,7 +397,7 @@ if (d->bzs.avail_in == 0) { if (len == 0) break; - d->bzs.avail_in = Py_MIN(len, UINT_MAX); + d->bzs.avail_in = (unsigned int)Py_MIN(len, UINT_MAX); len -= d->bzs.avail_in; } if (d->bzs.avail_out == 0) { @@ -408,7 +408,7 @@ d->bzs.next_out = PyBytes_AS_STRING(result) + data_size; buffer_left = PyBytes_GET_SIZE(result) - data_size; } - d->bzs.avail_out = Py_MIN(buffer_left, UINT_MAX); + d->bzs.avail_out = (unsigned int)Py_MIN(buffer_left, UINT_MAX); } } if (data_size != PyBytes_GET_SIZE(result)) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 4 23:57:58 2013 From: python-checkins at python.org (victor.stinner) Date: Tue, 4 Jun 2013 23:57:58 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Close_=2317931=3A_Fix_PyLo?= =?utf-8?q?ng=5FFromPid=28=29_on_Windows_64-bit=3A_processes_are_identifie?= =?utf-8?q?d?= Message-ID: <3bQ6Sk2fjkzSsq@mail.python.org> http://hg.python.org/cpython/rev/2298bcba6ec9 changeset: 84026:2298bcba6ec9 user: Victor Stinner date: Tue Jun 04 23:56:38 2013 +0200 summary: Close #17931: Fix PyLong_FromPid() on Windows 64-bit: processes are identified by their HANDLE which is a pointer (and not a long, which is smaller). files: Include/pyport.h | 4 ---- Misc/NEWS | 4 ++++ PC/pyconfig.h | 3 +++ 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Include/pyport.h b/Include/pyport.h --- a/Include/pyport.h +++ b/Include/pyport.h @@ -219,10 +219,6 @@ /* Smallest negative value of type Py_ssize_t. */ #define PY_SSIZE_T_MIN (-PY_SSIZE_T_MAX-1) -#if SIZEOF_PID_T > SIZEOF_LONG -# error "Python doesn't support sizeof(pid_t) > sizeof(long)" -#endif - /* PY_FORMAT_SIZE_T is a platform-specific modifier for use in a printf * format to convert an argument with the width of a size_t or Py_ssize_t. * C99 introduced "z" for this purpose, but not all platforms support that; diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,10 @@ Core and Builtins ----------------- +- Issue #17931: Fix PyLong_FromPid() on Windows 64-bit: processes are + identified by their HANDLE which is a pointer (and not a long, which is + smaller). + - Tweak the exception message when the magic number or size value in a bytecode file is truncated. diff --git a/PC/pyconfig.h b/PC/pyconfig.h --- a/PC/pyconfig.h +++ b/PC/pyconfig.h @@ -723,6 +723,9 @@ /* The size of `wchar_t', as computed by sizeof. */ #define SIZEOF_WCHAR_T 2 +/* The size of `pid_t' (HANDLE). */ +#define SIZEOF_PID_T SIZEOF_VOID_P + /* Define if you have the dl library (-ldl). */ /* #undef HAVE_LIBDL */ -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 5 00:14:05 2013 From: python-checkins at python.org (victor.stinner) Date: Wed, 5 Jun 2013 00:14:05 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issuse_=2317932=3A_Fix_an_?= =?utf-8?q?integer_overflow_issue_on_Windows_64-bit_in_tuple?= Message-ID: <3bQ6qK2qnSzSTg@mail.python.org> http://hg.python.org/cpython/rev/52075f60719e changeset: 84027:52075f60719e user: Victor Stinner date: Wed Jun 05 00:11:34 2013 +0200 summary: Issuse #17932: Fix an integer overflow issue on Windows 64-bit in tuple iterators: change the C type of tupleiterobject.it_index from long to Py_ssize_t. files: Objects/tupleobject.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c --- a/Objects/tupleobject.c +++ b/Objects/tupleobject.c @@ -930,7 +930,7 @@ typedef struct { PyObject_HEAD - long it_index; + Py_ssize_t it_index; PyTupleObject *it_seq; /* Set to NULL when iterator is exhausted */ } tupleiterobject; -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 5 00:14:06 2013 From: python-checkins at python.org (victor.stinner) Date: Wed, 5 Jun 2013 00:14:06 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=239566=3A_Fix_a_com?= =?utf-8?q?piler_warning_on_Windows_64-bit_in_namespace=5Finit=28=29?= Message-ID: <3bQ6qL52HfzSmY@mail.python.org> http://hg.python.org/cpython/rev/93f4b32fc95c changeset: 84028:93f4b32fc95c user: Victor Stinner date: Wed Jun 05 00:13:51 2013 +0200 summary: Issue #9566: Fix a compiler warning on Windows 64-bit in namespace_init() The result type is int, return -1 to avoid a compiler warning (cast Py_ssize_t to int). PyObject_Size() can only fail with -1, and anyway a constructor should return -1 on error, not an arbitrary negative number. files: Objects/namespaceobject.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Objects/namespaceobject.c b/Objects/namespaceobject.c --- a/Objects/namespaceobject.c +++ b/Objects/namespaceobject.c @@ -44,7 +44,7 @@ if (args != NULL) { Py_ssize_t argcount = PyObject_Size(args); if (argcount < 0) - return argcount; + return -1; else if (argcount > 0) { PyErr_Format(PyExc_TypeError, "no positional arguments expected"); return -1; -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 5 00:25:15 2013 From: python-checkins at python.org (victor.stinner) Date: Wed, 5 Jun 2013 00:25:15 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=239566=3A_Fix_compi?= =?utf-8?q?ler_warning_on_Windows_64-bit?= Message-ID: <3bQ74C3LRjzQWm@mail.python.org> http://hg.python.org/cpython/rev/5dcbd5d8d004 changeset: 84029:5dcbd5d8d004 user: Victor Stinner date: Wed Jun 05 00:21:31 2013 +0200 summary: Issue #9566: Fix compiler warning on Windows 64-bit files: Objects/unicodeobject.c | 8 +++++--- 1 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -6725,7 +6725,8 @@ /* each step cannot decode more than 1 character, but a character can be represented as a surrogate pair */ wchar_t buffer[2], *startout, *out; - int insize, outsize; + int insize; + Py_ssize_t outsize; PyObject *errorHandler = NULL; PyObject *exc = NULL; PyObject *encoding_obj = NULL; @@ -6995,10 +6996,11 @@ Py_DECREF(substring); return -1; } + assert(size <= INT_MAX); /* First get the size of the result */ outsize = WideCharToMultiByte(code_page, flags, - p, size, + p, (int)size, NULL, 0, NULL, pusedDefaultChar); if (outsize <= 0) @@ -7035,7 +7037,7 @@ /* Do the conversion */ outsize = WideCharToMultiByte(code_page, flags, - p, size, + p, (int)size, out, outsize, NULL, pusedDefaultChar); Py_CLEAR(substring); -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 5 00:25:16 2013 From: python-checkins at python.org (victor.stinner) Date: Wed, 5 Jun 2013 00:25:16 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=239566=3A_Fix_compi?= =?utf-8?q?ler_warning_on_Windows_64-bit?= Message-ID: <3bQ74D5QVNzQq2@mail.python.org> http://hg.python.org/cpython/rev/41b8be55b160 changeset: 84030:41b8be55b160 user: Victor Stinner date: Wed Jun 05 00:22:34 2013 +0200 summary: Issue #9566: Fix compiler warning on Windows 64-bit files: PC/getpathp.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/PC/getpathp.c b/PC/getpathp.c --- a/PC/getpathp.c +++ b/PC/getpathp.c @@ -434,7 +434,7 @@ char * p = fgets(buffer, MAXPATHLEN*2, env_file); wchar_t tmpbuffer[MAXPATHLEN*2+1]; PyObject * decoded; - int n; + size_t n; if (p == NULL) break; -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 5 00:36:05 2013 From: python-checkins at python.org (victor.stinner) Date: Wed, 5 Jun 2013 00:36:05 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2313772=3A_Fix_comp?= =?utf-8?q?iler_warnings_on_Windows?= Message-ID: <3bQ7Jj4RkFzRBB@mail.python.org> http://hg.python.org/cpython/rev/f431cd0edd85 changeset: 84031:f431cd0edd85 user: Victor Stinner date: Wed Jun 05 00:35:54 2013 +0200 summary: Issue #13772: Fix compiler warnings on Windows files: Modules/posixmodule.c | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -6765,7 +6765,7 @@ *ptr = 0; } -int _is_absW(WCHAR *path) { +int _is_absW(const WCHAR *path) { /* Is this path absolute? */ return path[0] == L'\\' || path[0] == L'/' || path[1] == L':'; @@ -6781,7 +6781,7 @@ void _joinW(WCHAR *dest_path, const WCHAR *root, const WCHAR *rest) { /* join root and rest with a backslash */ - int root_len; + size_t root_len; if(_is_absW(rest)) { wcscpy(dest_path, rest); @@ -6800,7 +6800,7 @@ void _joinA(char *dest_path, const char *root, const char *rest) { /* join root and rest with a backslash */ - int root_len; + size_t root_len; if(_is_absA(rest)) { strcpy(dest_path, rest); -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 5 00:37:26 2013 From: python-checkins at python.org (victor.stinner) Date: Wed, 5 Jun 2013 00:37:26 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2313772=3A_fix_=5Fc?= =?utf-8?b?aGVja19kaXJBKCk6IGNhbGwgKkEoKSBmdW5jdGlvbnMsIG5vdCAqVygpIGZ1?= =?utf-8?q?nctions?= Message-ID: <3bQ7LG3q4JzRBB@mail.python.org> http://hg.python.org/cpython/rev/c351591f1f63 changeset: 84032:c351591f1f63 user: Victor Stinner date: Wed Jun 05 00:37:12 2013 +0200 summary: Issue #13772: fix _check_dirA(): call *A() functions, not *W() functions files: Modules/posixmodule.c | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -6844,9 +6844,9 @@ /* dest_parent = os.path.dirname(dest) */ strcpy(dest_parent, dest); - _dirnameW(dest_parent); + _dirnameA(dest_parent); /* src_resolved = os.path.join(dest_parent, src) */ - _joinW(src_resolved, dest_parent, src); + _joinA(src_resolved, dest_parent, src); return ( GetFileAttributesExA(src_resolved, GetFileExInfoStandard, &src_info) && src_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 5 01:18:32 2013 From: python-checkins at python.org (victor.stinner) Date: Wed, 5 Jun 2013 01:18:32 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=239566=3A_Fix_compi?= =?utf-8?q?ler_warning_on_Windows_64-bit?= Message-ID: <3bQ8Fh2Xbfz7Ljh@mail.python.org> http://hg.python.org/cpython/rev/36c35a1893fe changeset: 84033:36c35a1893fe user: Victor Stinner date: Wed Jun 05 00:44:00 2013 +0200 summary: Issue #9566: Fix compiler warning on Windows 64-bit files: Parser/tokenizer.c | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-) diff --git a/Parser/tokenizer.c b/Parser/tokenizer.c --- a/Parser/tokenizer.c +++ b/Parser/tokenizer.c @@ -660,7 +660,8 @@ static char * translate_newlines(const char *s, int exec_input, struct tok_state *tok) { - int skip_next_lf = 0, needed_length = strlen(s) + 2, final_length; + int skip_next_lf = 0; + size_t needed_length = strlen(s) + 2, final_length; char *buf, *current; char c = '\0'; buf = PyMem_MALLOC(needed_length); -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 5 01:18:33 2013 From: python-checkins at python.org (victor.stinner) Date: Wed, 5 Jun 2013 01:18:33 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=239566=3A_Fix_compi?= =?utf-8?q?ler_warning_on_Windows_64-bit?= Message-ID: <3bQ8Fj4h0vz7Ljp@mail.python.org> http://hg.python.org/cpython/rev/88a21c5a97ef changeset: 84034:88a21c5a97ef user: Victor Stinner date: Wed Jun 05 00:46:29 2013 +0200 summary: Issue #9566: Fix compiler warning on Windows 64-bit files: Python/fileutils.c | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-) diff --git a/Python/fileutils.c b/Python/fileutils.c --- a/Python/fileutils.c +++ b/Python/fileutils.c @@ -707,7 +707,8 @@ _Py_wgetcwd(wchar_t *buf, size_t size) { #ifdef MS_WINDOWS - return _wgetcwd(buf, size); + int isize = (int)Py_MIN(size, INT_MAX); + return _wgetcwd(buf, isize); #else char fname[PATH_MAX]; wchar_t *wname; -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 5 01:18:34 2013 From: python-checkins at python.org (victor.stinner) Date: Wed, 5 Jun 2013 01:18:34 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=239566=3A_Fix_compi?= =?utf-8?q?later_warnings_on_Windows_64-bit?= Message-ID: <3bQ8Fk6lXYz7LkR@mail.python.org> http://hg.python.org/cpython/rev/aeebbae8c74c changeset: 84035:aeebbae8c74c user: Victor Stinner date: Wed Jun 05 01:18:13 2013 +0200 summary: Issue #9566: Fix compilater warnings on Windows 64-bit files: Python/getargs.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Python/getargs.c b/Python/getargs.c --- a/Python/getargs.c +++ b/Python/getargs.c @@ -570,7 +570,7 @@ "size does not fit in an int"); \ return converterr("", arg, msgbuf, bufsize); \ } \ - *q=s; \ + *q = (int)s; \ } #define BUFFER_LEN ((flags & FLAG_SIZE_T) ? *q2:*q) #define RETURN_ERR_OCCURRED return msgbuf -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 5 01:59:31 2013 From: python-checkins at python.org (victor.stinner) Date: Wed, 5 Jun 2013 01:59:31 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2313772=3A_Fix_a_co?= =?utf-8?q?mpiler_warning_on_Windows?= Message-ID: <3bQ98z0M51zQNg@mail.python.org> http://hg.python.org/cpython/rev/e024236ea253 changeset: 84036:e024236ea253 user: Victor Stinner date: Wed Jun 05 01:30:25 2013 +0200 summary: Issue #13772: Fix a compiler warning on Windows files: Modules/posixmodule.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -6772,7 +6772,7 @@ } -int _is_absA(char *path) { +int _is_absA(const char *path) { /* Is this path absolute? */ return path[0] == '\\' || path[0] == '/' || path[1] == ':'; -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 5 01:59:32 2013 From: python-checkins at python.org (victor.stinner) Date: Wed, 5 Jun 2013 01:59:32 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2313772=3A_Mark_hel?= =?utf-8?q?per_functions_as_private_=28static=29?= Message-ID: <3bQ9903l7Qz7Lk5@mail.python.org> http://hg.python.org/cpython/rev/d9f3ea27f826 changeset: 84037:d9f3ea27f826 user: Victor Stinner date: Wed Jun 05 01:49:17 2013 +0200 summary: Issue #13772: Mark helper functions as private (static) Cleanup also the code to follow the Python coding style (PEP 7). files: Modules/posixmodule.c | 79 ++++++++++++++++-------------- 1 files changed, 43 insertions(+), 36 deletions(-) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -6720,8 +6720,9 @@ /* Grab CreateSymbolicLinkW dynamically from kernel32 */ static DWORD (CALLBACK *Py_CreateSymbolicLinkW)(LPWSTR, LPWSTR, DWORD) = NULL; static DWORD (CALLBACK *Py_CreateSymbolicLinkA)(LPSTR, LPSTR, DWORD) = NULL; + static int -check_CreateSymbolicLink() +check_CreateSymbolicLink(void) { HINSTANCE hKernel32; /* only recheck */ @@ -6735,55 +6736,57 @@ return (Py_CreateSymbolicLinkW && Py_CreateSymbolicLinkA); } -void _dirnameW(WCHAR *path) { - /* Remove the last portion of the path */ - +/* Remove the last portion of the path */ +static void +_dirnameW(WCHAR *path) +{ WCHAR *ptr; /* walk the path from the end until a backslash is encountered */ - for(ptr = path + wcslen(path); ptr != path; ptr--) - { - if(*ptr == *L"\\" || *ptr == *L"/") { + for(ptr = path + wcslen(path); ptr != path; ptr--) { + if (*ptr == *L"\\" || *ptr == *L"/") break; - } } *ptr = 0; } -void _dirnameA(char *path) { - /* Remove the last portion of the path */ - +/* Remove the last portion of the path */ +static void +_dirnameA(char *path) +{ char *ptr; /* walk the path from the end until a backslash is encountered */ - for(ptr = path + strlen(path); ptr != path; ptr--) - { - if(*ptr == '\\' || *ptr == '/') { + for(ptr = path + strlen(path); ptr != path; ptr--) { + if (*ptr == '\\' || *ptr == '/') break; - } } *ptr = 0; } -int _is_absW(const WCHAR *path) { - /* Is this path absolute? */ - +/* Is this path absolute? */ +static int +_is_absW(const WCHAR *path) +{ return path[0] == L'\\' || path[0] == L'/' || path[1] == L':'; } -int _is_absA(const char *path) { - /* Is this path absolute? */ - +/* Is this path absolute? */ +static int +_is_absA(const char *path) +{ return path[0] == '\\' || path[0] == '/' || path[1] == ':'; } -void _joinW(WCHAR *dest_path, const WCHAR *root, const WCHAR *rest) { - /* join root and rest with a backslash */ +/* join root and rest with a backslash */ +static void +_joinW(WCHAR *dest_path, const WCHAR *root, const WCHAR *rest) +{ size_t root_len; - if(_is_absW(rest)) { + if (_is_absW(rest)) { wcscpy(dest_path, rest); return; } @@ -6792,17 +6795,19 @@ wcscpy(dest_path, root); if(root_len) { - dest_path[root_len] = *L"\\"; - root_len += 1; + dest_path[root_len] = L'\\'; + root_len++; } wcscpy(dest_path+root_len, rest); } -void _joinA(char *dest_path, const char *root, const char *rest) { - /* join root and rest with a backslash */ +/* join root and rest with a backslash */ +static void +_joinA(char *dest_path, const char *root, const char *rest) +{ size_t root_len; - if(_is_absA(rest)) { + if (_is_absA(rest)) { strcpy(dest_path, rest); return; } @@ -6812,14 +6817,15 @@ strcpy(dest_path, root); if(root_len) { dest_path[root_len] = '\\'; - root_len += 1; + root_len++; } strcpy(dest_path+root_len, rest); } -int _check_dirW(WCHAR *src, WCHAR *dest) -{ - /* Return True if the path at src relative to dest is a directory */ +/* Return True if the path at src relative to dest is a directory */ +static int +_check_dirW(WCHAR *src, WCHAR *dest) +{ WIN32_FILE_ATTRIBUTE_DATA src_info; WCHAR dest_parent[MAX_PATH]; WCHAR src_resolved[MAX_PATH] = L""; @@ -6835,9 +6841,10 @@ ); } -int _check_dirA(char *src, char *dest) -{ - /* Return True if the path at src relative to dest is a directory */ +/* Return True if the path at src relative to dest is a directory */ +static int +_check_dirA(char *src, char *dest) +{ WIN32_FILE_ATTRIBUTE_DATA src_info; char dest_parent[MAX_PATH]; char src_resolved[MAX_PATH] = ""; -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 5 02:08:02 2013 From: python-checkins at python.org (victor.stinner) Date: Wed, 5 Jun 2013 02:08:02 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2313772=3A_Use_synt?= =?utf-8?q?ax_for_literal_wchar=5Ft_character?= Message-ID: <3bQ9Lp24xmzSgY@mail.python.org> http://hg.python.org/cpython/rev/c8212fca8747 changeset: 84038:c8212fca8747 user: Victor Stinner date: Wed Jun 05 02:07:46 2013 +0200 summary: Issue #13772: Use syntax for literal wchar_t character files: Modules/posixmodule.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -6744,7 +6744,7 @@ /* walk the path from the end until a backslash is encountered */ for(ptr = path + wcslen(path); ptr != path; ptr--) { - if (*ptr == *L"\\" || *ptr == *L"/") + if (*ptr == L'\\' || *ptr == L'/') break; } *ptr = 0; -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 5 03:29:57 2013 From: python-checkins at python.org (guido.van.rossum) Date: Wed, 5 Jun 2013 03:29:57 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?peps=3A_Accept_PEP_443=2C_making_a_fe?= =?utf-8?q?w_minor_edits_to_improve_text_flow=2E?= Message-ID: <3bQC9K4JXRzQQ2@mail.python.org> http://hg.python.org/peps/rev/38826efc5fe4 changeset: 4921:38826efc5fe4 user: Guido van Rossum date: Tue Jun 04 18:29:49 2013 -0700 summary: Accept PEP 443, making a few minor edits to improve text flow. files: pep-0443.txt | 27 +++++++++++++++------------ 1 files changed, 15 insertions(+), 12 deletions(-) diff --git a/pep-0443.txt b/pep-0443.txt --- a/pep-0443.txt +++ b/pep-0443.txt @@ -4,7 +4,7 @@ Last-Modified: $Date$ Author: ?ukasz Langa Discussions-To: Python-Dev -Status: Draft +Status: Accepted Type: Standards Track Content-Type: text/x-rst Created: 22-May-2013 @@ -44,11 +44,14 @@ In addition, it is currently a common anti-pattern for Python code to inspect the types of received arguments, in order to decide what to do -with the objects. For example, code may wish to accept either an object +with the objects. + +For example, code may wish to accept either an object of some type, or a sequence of objects of that type. +Currently, the "obvious way" to do this is by type inspection, but this +is brittle and closed to extension. -Currently, the "obvious way" to do this is by type inspection, but this -is brittle and closed to extension. Abstract Base Classes make it easier +Abstract Base Classes make it easier to discover present behaviour, but don't help adding new behaviour. A developer using an already-written library may be unable to change how their objects are treated by such code, especially if the objects they @@ -63,7 +66,7 @@ To define a generic function, decorate it with the ``@singledispatch`` decorator. Note that the dispatch happens on the type of the first -argument, create your function accordingly:: +argument. Create your function accordingly:: >>> from functools import singledispatch >>> @singledispatch @@ -73,7 +76,7 @@ ... print(arg) To add overloaded implementations to the function, use the -``register()`` attribute of the generic function. It is a decorator, +``register()`` attribute of the generic function. This is a decorator, taking a type parameter and decorating a function implementing the operation for that type:: @@ -98,7 +101,7 @@ ... >>> fun.register(type(None), nothing) -The ``register()`` attribute returns the undecorated function which +The ``register()`` attribute returns the undecorated function. This enables decorator stacking, pickling, as well as creating unit tests for each variant independently:: @@ -172,12 +175,12 @@ reference implementation is available on hg.python.org [#ref-impl]_. The dispatch type is specified as a decorator argument. An alternative -form using function annotations has been considered but its inclusion -has been deferred. As of May 2013, this usage pattern is out of scope -for the standard library [#pep-0008]_ and the best practices for +form using function annotations was considered but its inclusion +has been rejected. As of May 2013, this usage pattern is out of scope +for the standard library [#pep-0008]_, and the best practices for annotation usage are still debated. -Based on the current ``pkgutil.simplegeneric`` implementation and +Based on the current ``pkgutil.simplegeneric`` implementation, and following the convention on registering virtual subclasses on Abstract Base Classes, the dispatch registry will not be thread-safe. @@ -278,7 +281,7 @@ This PEP proposes extending behaviour only of functions specifically marked as generic. Just as a base class method may be overridden by -a subclass, so too may a function be overloaded to provide custom +a subclass, so too a function may be overloaded to provide custom functionality for a given type. Universal overloading does not equal *arbitrary* overloading, in the -- Repository URL: http://hg.python.org/peps From solipsis at pitrou.net Wed Jun 5 05:51:46 2013 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Wed, 05 Jun 2013 05:51:46 +0200 Subject: [Python-checkins] Daily reference leaks (c8212fca8747): sum=0 Message-ID: results for c8212fca8747 on branch "default" -------------------------------------------- Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogD2huOK', '-x'] From python-checkins at python.org Wed Jun 5 12:20:49 2013 From: python-checkins at python.org (lukasz.langa) Date: Wed, 5 Jun 2013 12:20:49 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Add_reference_implementati?= =?utf-8?q?on_for_PEP_443?= Message-ID: <3bQQxs6YX7zRbZ@mail.python.org> http://hg.python.org/cpython/rev/dfcb64f51f7b changeset: 84039:dfcb64f51f7b user: ?ukasz Langa date: Wed Jun 05 12:20:24 2013 +0200 summary: Add reference implementation for PEP 443 PEP accepted: http://mail.python.org/pipermail/python-dev/2013-June/126734.html files: Doc/library/functools.rst | 110 +++++++ Lib/functools.py | 128 ++++++++- Lib/pkgutil.py | 52 +--- Lib/test/test_functools.py | 374 ++++++++++++++++++++++++- Misc/NEWS | 3 + Modules/Setup.dist | 2 +- 6 files changed, 614 insertions(+), 55 deletions(-) diff --git a/Doc/library/functools.rst b/Doc/library/functools.rst --- a/Doc/library/functools.rst +++ b/Doc/library/functools.rst @@ -6,6 +6,7 @@ .. moduleauthor:: Peter Harris .. moduleauthor:: Raymond Hettinger .. moduleauthor:: Nick Coghlan +.. moduleauthor:: ?ukasz Langa .. sectionauthor:: Peter Harris **Source code:** :source:`Lib/functools.py` @@ -186,6 +187,115 @@ *sequence* contains only one item, the first item is returned. +.. decorator:: singledispatch(default) + + Transforms a function into a single-dispatch generic function. A **generic + function** is composed of multiple functions implementing the same operation + for different types. Which implementation should be used during a call is + determined by the dispatch algorithm. When the implementation is chosen + based on the type of a single argument, this is known as **single + dispatch**. + + To define a generic function, decorate it with the ``@singledispatch`` + decorator. Note that the dispatch happens on the type of the first argument, + create your function accordingly:: + + >>> from functools import singledispatch + >>> @singledispatch + ... def fun(arg, verbose=False): + ... if verbose: + ... print("Let me just say,", end=" ") + ... print(arg) + + To add overloaded implementations to the function, use the :func:`register` + attribute of the generic function. It is a decorator, taking a type + parameter and decorating a function implementing the operation for that + type:: + + >>> @fun.register(int) + ... def _(arg, verbose=False): + ... if verbose: + ... print("Strength in numbers, eh?", end=" ") + ... print(arg) + ... + >>> @fun.register(list) + ... def _(arg, verbose=False): + ... if verbose: + ... print("Enumerate this:") + ... for i, elem in enumerate(arg): + ... print(i, elem) + + To enable registering lambdas and pre-existing functions, the + :func:`register` attribute can be used in a functional form:: + + >>> def nothing(arg, verbose=False): + ... print("Nothing.") + ... + >>> fun.register(type(None), nothing) + + The :func:`register` attribute returns the undecorated function which + enables decorator stacking, pickling, as well as creating unit tests for + each variant independently:: + + >>> @fun.register(float) + ... @fun.register(Decimal) + ... def fun_num(arg, verbose=False): + ... if verbose: + ... print("Half of your number:", end=" ") + ... print(arg / 2) + ... + >>> fun_num is fun + False + + When called, the generic function dispatches on the type of the first + argument:: + + >>> fun("Hello, world.") + Hello, world. + >>> fun("test.", verbose=True) + Let me just say, test. + >>> fun(42, verbose=True) + Strength in numbers, eh? 42 + >>> fun(['spam', 'spam', 'eggs', 'spam'], verbose=True) + Enumerate this: + 0 spam + 1 spam + 2 eggs + 3 spam + >>> fun(None) + Nothing. + >>> fun(1.23) + 0.615 + + Where there is no registered implementation for a specific type, its + method resolution order is used to find a more generic implementation. + The original function decorated with ``@singledispatch`` is registered + for the base ``object`` type, which means it is used if no better + implementation is found. + + To check which implementation will the generic function choose for + a given type, use the ``dispatch()`` attribute:: + + >>> fun.dispatch(float) + + >>> fun.dispatch(dict) # note: default implementation + + + To access all registered implementations, use the read-only ``registry`` + attribute:: + + >>> fun.registry.keys() + dict_keys([, , , + , , + ]) + >>> fun.registry[float] + + >>> fun.registry[object] + + + .. versionadded:: 3.4 + + .. function:: update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES) Update a *wrapper* function to look like the *wrapped* function. The optional diff --git a/Lib/functools.py b/Lib/functools.py --- a/Lib/functools.py +++ b/Lib/functools.py @@ -3,19 +3,24 @@ # Python module wrapper for _functools C module # to allow utilities written in Python to be added # to the functools module. -# Written by Nick Coghlan -# and Raymond Hettinger -# Copyright (C) 2006-2010 Python Software Foundation. +# Written by Nick Coghlan , +# Raymond Hettinger , +# and ?ukasz Langa . +# Copyright (C) 2006-2013 Python Software Foundation. # See C source code for _functools credits/copyright __all__ = ['update_wrapper', 'wraps', 'WRAPPER_ASSIGNMENTS', 'WRAPPER_UPDATES', - 'total_ordering', 'cmp_to_key', 'lru_cache', 'reduce', 'partial'] + 'total_ordering', 'cmp_to_key', 'lru_cache', 'reduce', 'partial', + 'singledispatch'] try: from _functools import reduce except ImportError: pass +from abc import get_cache_token from collections import namedtuple +from types import MappingProxyType +from weakref import WeakKeyDictionary try: from _thread import RLock except: @@ -354,3 +359,118 @@ return update_wrapper(wrapper, user_function) return decorating_function + + +################################################################################ +### singledispatch() - single-dispatch generic function decorator +################################################################################ + +def _compose_mro(cls, haystack): + """Calculates the MRO for a given class `cls`, including relevant abstract + base classes from `haystack`. + + """ + bases = set(cls.__mro__) + mro = list(cls.__mro__) + for needle in haystack: + if (needle in bases or not hasattr(needle, '__mro__') + or not issubclass(cls, needle)): + continue # either present in the __mro__ already or unrelated + for index, base in enumerate(mro): + if not issubclass(base, needle): + break + if base in bases and not issubclass(needle, base): + # Conflict resolution: put classes present in __mro__ and their + # subclasses first. See test_mro_conflicts() in test_functools.py + # for examples. + index += 1 + mro.insert(index, needle) + return mro + +def _find_impl(cls, registry): + """Returns the best matching implementation for the given class `cls` in + `registry`. Where there is no registered implementation for a specific + type, its method resolution order is used to find a more generic + implementation. + + Note: if `registry` does not contain an implementation for the base + `object` type, this function may return None. + + """ + mro = _compose_mro(cls, registry.keys()) + match = None + for t in mro: + if match is not None: + # If `match` is an ABC but there is another unrelated, equally + # matching ABC. Refuse the temptation to guess. + if (t in registry and not issubclass(match, t) + and match not in cls.__mro__): + raise RuntimeError("Ambiguous dispatch: {} or {}".format( + match, t)) + break + if t in registry: + match = t + return registry.get(match) + +def singledispatch(func): + """Single-dispatch generic function decorator. + + Transforms a function into a generic function, which can have different + behaviours depending upon the type of its first argument. The decorated + function acts as the default implementation, and additional + implementations can be registered using the 'register()' attribute of + the generic function. + + """ + registry = {} + dispatch_cache = WeakKeyDictionary() + cache_token = None + + def dispatch(typ): + """generic_func.dispatch(type) -> + + Runs the dispatch algorithm to return the best available implementation + for the given `type` registered on `generic_func`. + + """ + nonlocal cache_token + if cache_token is not None: + current_token = get_cache_token() + if cache_token != current_token: + dispatch_cache.clear() + cache_token = current_token + try: + impl = dispatch_cache[typ] + except KeyError: + try: + impl = registry[typ] + except KeyError: + impl = _find_impl(typ, registry) + dispatch_cache[typ] = impl + return impl + + def register(typ, func=None): + """generic_func.register(type, func) -> func + + Registers a new implementation for the given `type` on a `generic_func`. + + """ + nonlocal cache_token + if func is None: + return lambda f: register(typ, f) + registry[typ] = func + if cache_token is None and hasattr(typ, '__abstractmethods__'): + cache_token = get_cache_token() + dispatch_cache.clear() + return func + + def wrapper(*args, **kw): + return dispatch(args[0].__class__)(*args, **kw) + + registry[object] = func + wrapper.register = register + wrapper.dispatch = dispatch + wrapper.registry = MappingProxyType(registry) + wrapper._clear_cache = dispatch_cache.clear + update_wrapper(wrapper, func) + return wrapper diff --git a/Lib/pkgutil.py b/Lib/pkgutil.py --- a/Lib/pkgutil.py +++ b/Lib/pkgutil.py @@ -1,12 +1,13 @@ """Utilities to support packages.""" +from functools import singledispatch as simplegeneric +import imp +import importlib import os +import os.path import sys -import importlib -import imp -import os.path +from types import ModuleType from warnings import warn -from types import ModuleType __all__ = [ 'get_importer', 'iter_importers', 'get_loader', 'find_loader', @@ -27,46 +28,6 @@ return marshal.load(stream) -def simplegeneric(func): - """Make a trivial single-dispatch generic function""" - registry = {} - def wrapper(*args, **kw): - ob = args[0] - try: - cls = ob.__class__ - except AttributeError: - cls = type(ob) - try: - mro = cls.__mro__ - except AttributeError: - try: - class cls(cls, object): - pass - mro = cls.__mro__[1:] - except TypeError: - mro = object, # must be an ExtensionClass or some such :( - for t in mro: - if t in registry: - return registry[t](*args, **kw) - else: - return func(*args, **kw) - try: - wrapper.__name__ = func.__name__ - except (TypeError, AttributeError): - pass # Python 2.3 doesn't allow functions to be renamed - - def register(typ, func=None): - if func is None: - return lambda f: register(typ, f) - registry[typ] = func - return func - - wrapper.__dict__ = func.__dict__ - wrapper.__doc__ = func.__doc__ - wrapper.register = register - return wrapper - - def walk_packages(path=None, prefix='', onerror=None): """Yields (module_loader, name, ispkg) for all modules recursively on path, or, if path is None, all accessible modules. @@ -148,13 +109,12 @@ yield i, name, ispkg -#@simplegeneric + at simplegeneric def iter_importer_modules(importer, prefix=''): if not hasattr(importer, 'iter_modules'): return [] return importer.iter_modules(prefix) -iter_importer_modules = simplegeneric(iter_importer_modules) # Implement a file walker for the normal importlib path hook def _iter_file_finder_modules(importer, prefix=''): diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py --- a/Lib/test/test_functools.py +++ b/Lib/test/test_functools.py @@ -1,24 +1,30 @@ import collections -import sys -import unittest -from test import support -from weakref import proxy +from itertools import permutations import pickle from random import choice +import sys +from test import support +import unittest +from weakref import proxy import functools py_functools = support.import_fresh_module('functools', blocked=['_functools']) c_functools = support.import_fresh_module('functools', fresh=['_functools']) +decimal = support.import_fresh_module('decimal', fresh=['_decimal']) + + def capture(*args, **kw): """capture all positional and keyword arguments""" return args, kw + def signature(part): """ return the signature of a partial object """ return (part.func, part.args, part.keywords, part.__dict__) + class TestPartial: def test_basic_examples(self): @@ -138,6 +144,7 @@ join = self.partial(''.join) self.assertEqual(join(data), '0123456789') + @unittest.skipUnless(c_functools, 'requires the C _functools module') class TestPartialC(TestPartial, unittest.TestCase): if c_functools: @@ -194,18 +201,22 @@ "new style getargs format but argument is not a tuple", f.__setstate__, BadSequence()) + class TestPartialPy(TestPartial, unittest.TestCase): partial = staticmethod(py_functools.partial) + if c_functools: class PartialSubclass(c_functools.partial): pass + @unittest.skipUnless(c_functools, 'requires the C _functools module') class TestPartialCSubclass(TestPartialC): if c_functools: partial = PartialSubclass + class TestUpdateWrapper(unittest.TestCase): def check_wrapper(self, wrapper, wrapped, @@ -312,6 +323,7 @@ self.assertTrue(wrapper.__doc__.startswith('max(')) self.assertEqual(wrapper.__annotations__, {}) + class TestWraps(TestUpdateWrapper): def _default_update(self): @@ -372,6 +384,7 @@ self.assertEqual(wrapper.attr, 'This is a different test') self.assertEqual(wrapper.dict_attr, f.dict_attr) + class TestReduce(unittest.TestCase): func = functools.reduce @@ -452,6 +465,7 @@ d = {"one": 1, "two": 2, "three": 3} self.assertEqual(self.func(add, d), "".join(d.keys())) + class TestCmpToKey: def test_cmp_to_key(self): @@ -534,14 +548,17 @@ self.assertRaises(TypeError, hash, k) self.assertNotIsInstance(k, collections.Hashable) + @unittest.skipUnless(c_functools, 'requires the C _functools module') class TestCmpToKeyC(TestCmpToKey, unittest.TestCase): if c_functools: cmp_to_key = c_functools.cmp_to_key + class TestCmpToKeyPy(TestCmpToKey, unittest.TestCase): cmp_to_key = staticmethod(py_functools.cmp_to_key) + class TestTotalOrdering(unittest.TestCase): def test_total_ordering_lt(self): @@ -642,6 +659,7 @@ with self.assertRaises(TypeError): TestTO(8) <= () + class TestLRU(unittest.TestCase): def test_lru(self): @@ -834,6 +852,353 @@ DoubleEq(2)) # Verify the correct return value +class TestSingleDispatch(unittest.TestCase): + def test_simple_overloads(self): + @functools.singledispatch + def g(obj): + return "base" + def g_int(i): + return "integer" + g.register(int, g_int) + self.assertEqual(g("str"), "base") + self.assertEqual(g(1), "integer") + self.assertEqual(g([1,2,3]), "base") + + def test_mro(self): + @functools.singledispatch + def g(obj): + return "base" + class C: + pass + class D(C): + pass + def g_C(c): + return "C" + g.register(C, g_C) + self.assertEqual(g(C()), "C") + self.assertEqual(g(D()), "C") + + def test_classic_classes(self): + @functools.singledispatch + def g(obj): + return "base" + class C: + pass + class D(C): + pass + def g_C(c): + return "C" + g.register(C, g_C) + self.assertEqual(g(C()), "C") + self.assertEqual(g(D()), "C") + + def test_register_decorator(self): + @functools.singledispatch + def g(obj): + return "base" + @g.register(int) + def g_int(i): + return "int %s" % (i,) + self.assertEqual(g(""), "base") + self.assertEqual(g(12), "int 12") + self.assertIs(g.dispatch(int), g_int) + self.assertIs(g.dispatch(object), g.dispatch(str)) + # Note: in the assert above this is not g. + # @singledispatch returns the wrapper. + + def test_wrapping_attributes(self): + @functools.singledispatch + def g(obj): + "Simple test" + return "Test" + self.assertEqual(g.__name__, "g") + self.assertEqual(g.__doc__, "Simple test") + + @unittest.skipUnless(decimal, 'requires _decimal') + @support.cpython_only + def test_c_classes(self): + @functools.singledispatch + def g(obj): + return "base" + @g.register(decimal.DecimalException) + def _(obj): + return obj.args + subn = decimal.Subnormal("Exponent < Emin") + rnd = decimal.Rounded("Number got rounded") + self.assertEqual(g(subn), ("Exponent < Emin",)) + self.assertEqual(g(rnd), ("Number got rounded",)) + @g.register(decimal.Subnormal) + def _(obj): + return "Too small to care." + self.assertEqual(g(subn), "Too small to care.") + self.assertEqual(g(rnd), ("Number got rounded",)) + + def test_compose_mro(self): + c = collections + mro = functools._compose_mro + bases = [c.Sequence, c.MutableMapping, c.Mapping, c.Set] + for haystack in permutations(bases): + m = mro(dict, haystack) + self.assertEqual(m, [dict, c.MutableMapping, c.Mapping, object]) + bases = [c.Container, c.Mapping, c.MutableMapping, c.OrderedDict] + for haystack in permutations(bases): + m = mro(c.ChainMap, haystack) + self.assertEqual(m, [c.ChainMap, c.MutableMapping, c.Mapping, + c.Sized, c.Iterable, c.Container, object]) + # Note: The MRO order below depends on haystack ordering. + m = mro(c.defaultdict, [c.Sized, c.Container, str]) + self.assertEqual(m, [c.defaultdict, dict, c.Container, c.Sized, object]) + m = mro(c.defaultdict, [c.Container, c.Sized, str]) + self.assertEqual(m, [c.defaultdict, dict, c.Sized, c.Container, object]) + + def test_register_abc(self): + c = collections + d = {"a": "b"} + l = [1, 2, 3] + s = {object(), None} + f = frozenset(s) + t = (1, 2, 3) + @functools.singledispatch + def g(obj): + return "base" + self.assertEqual(g(d), "base") + self.assertEqual(g(l), "base") + self.assertEqual(g(s), "base") + self.assertEqual(g(f), "base") + self.assertEqual(g(t), "base") + g.register(c.Sized, lambda obj: "sized") + self.assertEqual(g(d), "sized") + self.assertEqual(g(l), "sized") + self.assertEqual(g(s), "sized") + self.assertEqual(g(f), "sized") + self.assertEqual(g(t), "sized") + g.register(c.MutableMapping, lambda obj: "mutablemapping") + self.assertEqual(g(d), "mutablemapping") + self.assertEqual(g(l), "sized") + self.assertEqual(g(s), "sized") + self.assertEqual(g(f), "sized") + self.assertEqual(g(t), "sized") + g.register(c.ChainMap, lambda obj: "chainmap") + self.assertEqual(g(d), "mutablemapping") # irrelevant ABCs registered + self.assertEqual(g(l), "sized") + self.assertEqual(g(s), "sized") + self.assertEqual(g(f), "sized") + self.assertEqual(g(t), "sized") + g.register(c.MutableSequence, lambda obj: "mutablesequence") + self.assertEqual(g(d), "mutablemapping") + self.assertEqual(g(l), "mutablesequence") + self.assertEqual(g(s), "sized") + self.assertEqual(g(f), "sized") + self.assertEqual(g(t), "sized") + g.register(c.MutableSet, lambda obj: "mutableset") + self.assertEqual(g(d), "mutablemapping") + self.assertEqual(g(l), "mutablesequence") + self.assertEqual(g(s), "mutableset") + self.assertEqual(g(f), "sized") + self.assertEqual(g(t), "sized") + g.register(c.Mapping, lambda obj: "mapping") + self.assertEqual(g(d), "mutablemapping") # not specific enough + self.assertEqual(g(l), "mutablesequence") + self.assertEqual(g(s), "mutableset") + self.assertEqual(g(f), "sized") + self.assertEqual(g(t), "sized") + g.register(c.Sequence, lambda obj: "sequence") + self.assertEqual(g(d), "mutablemapping") + self.assertEqual(g(l), "mutablesequence") + self.assertEqual(g(s), "mutableset") + self.assertEqual(g(f), "sized") + self.assertEqual(g(t), "sequence") + g.register(c.Set, lambda obj: "set") + self.assertEqual(g(d), "mutablemapping") + self.assertEqual(g(l), "mutablesequence") + self.assertEqual(g(s), "mutableset") + self.assertEqual(g(f), "set") + self.assertEqual(g(t), "sequence") + g.register(dict, lambda obj: "dict") + self.assertEqual(g(d), "dict") + self.assertEqual(g(l), "mutablesequence") + self.assertEqual(g(s), "mutableset") + self.assertEqual(g(f), "set") + self.assertEqual(g(t), "sequence") + g.register(list, lambda obj: "list") + self.assertEqual(g(d), "dict") + self.assertEqual(g(l), "list") + self.assertEqual(g(s), "mutableset") + self.assertEqual(g(f), "set") + self.assertEqual(g(t), "sequence") + g.register(set, lambda obj: "concrete-set") + self.assertEqual(g(d), "dict") + self.assertEqual(g(l), "list") + self.assertEqual(g(s), "concrete-set") + self.assertEqual(g(f), "set") + self.assertEqual(g(t), "sequence") + g.register(frozenset, lambda obj: "frozen-set") + self.assertEqual(g(d), "dict") + self.assertEqual(g(l), "list") + self.assertEqual(g(s), "concrete-set") + self.assertEqual(g(f), "frozen-set") + self.assertEqual(g(t), "sequence") + g.register(tuple, lambda obj: "tuple") + self.assertEqual(g(d), "dict") + self.assertEqual(g(l), "list") + self.assertEqual(g(s), "concrete-set") + self.assertEqual(g(f), "frozen-set") + self.assertEqual(g(t), "tuple") + + def test_mro_conflicts(self): + c = collections + + @functools.singledispatch + def g(arg): + return "base" + + class O(c.Sized): + def __len__(self): + return 0 + + o = O() + self.assertEqual(g(o), "base") + g.register(c.Iterable, lambda arg: "iterable") + g.register(c.Container, lambda arg: "container") + g.register(c.Sized, lambda arg: "sized") + g.register(c.Set, lambda arg: "set") + self.assertEqual(g(o), "sized") + c.Iterable.register(O) + self.assertEqual(g(o), "sized") # because it's explicitly in __mro__ + c.Container.register(O) + self.assertEqual(g(o), "sized") # see above: Sized is in __mro__ + + class P: + pass + + p = P() + self.assertEqual(g(p), "base") + c.Iterable.register(P) + self.assertEqual(g(p), "iterable") + c.Container.register(P) + with self.assertRaises(RuntimeError) as re: + g(p) + self.assertEqual( + str(re), + ("Ambiguous dispatch: " + "or "), + ) + + class Q(c.Sized): + def __len__(self): + return 0 + + q = Q() + self.assertEqual(g(q), "sized") + c.Iterable.register(Q) + self.assertEqual(g(q), "sized") # because it's explicitly in __mro__ + c.Set.register(Q) + self.assertEqual(g(q), "set") # because c.Set is a subclass of + # c.Sized which is explicitly in + # __mro__ + + def test_cache_invalidation(self): + from collections import UserDict + class TracingDict(UserDict): + def __init__(self, *args, **kwargs): + super(TracingDict, self).__init__(*args, **kwargs) + self.set_ops = [] + self.get_ops = [] + def __getitem__(self, key): + result = self.data[key] + self.get_ops.append(key) + return result + def __setitem__(self, key, value): + self.set_ops.append(key) + self.data[key] = value + def clear(self): + self.data.clear() + _orig_wkd = functools.WeakKeyDictionary + td = TracingDict() + functools.WeakKeyDictionary = lambda: td + c = collections + @functools.singledispatch + def g(arg): + return "base" + d = {} + l = [] + self.assertEqual(len(td), 0) + self.assertEqual(g(d), "base") + self.assertEqual(len(td), 1) + self.assertEqual(td.get_ops, []) + self.assertEqual(td.set_ops, [dict]) + self.assertEqual(td.data[dict], g.registry[object]) + self.assertEqual(g(l), "base") + self.assertEqual(len(td), 2) + self.assertEqual(td.get_ops, []) + self.assertEqual(td.set_ops, [dict, list]) + self.assertEqual(td.data[dict], g.registry[object]) + self.assertEqual(td.data[list], g.registry[object]) + self.assertEqual(td.data[dict], td.data[list]) + self.assertEqual(g(l), "base") + self.assertEqual(g(d), "base") + self.assertEqual(td.get_ops, [list, dict]) + self.assertEqual(td.set_ops, [dict, list]) + g.register(list, lambda arg: "list") + self.assertEqual(td.get_ops, [list, dict]) + self.assertEqual(len(td), 0) + self.assertEqual(g(d), "base") + self.assertEqual(len(td), 1) + self.assertEqual(td.get_ops, [list, dict]) + self.assertEqual(td.set_ops, [dict, list, dict]) + self.assertEqual(td.data[dict], + functools._find_impl(dict, g.registry)) + self.assertEqual(g(l), "list") + self.assertEqual(len(td), 2) + self.assertEqual(td.get_ops, [list, dict]) + self.assertEqual(td.set_ops, [dict, list, dict, list]) + self.assertEqual(td.data[list], + functools._find_impl(list, g.registry)) + class X: + pass + c.MutableMapping.register(X) # Will not invalidate the cache, + # not using ABCs yet. + self.assertEqual(g(d), "base") + self.assertEqual(g(l), "list") + self.assertEqual(td.get_ops, [list, dict, dict, list]) + self.assertEqual(td.set_ops, [dict, list, dict, list]) + g.register(c.Sized, lambda arg: "sized") + self.assertEqual(len(td), 0) + self.assertEqual(g(d), "sized") + self.assertEqual(len(td), 1) + self.assertEqual(td.get_ops, [list, dict, dict, list]) + self.assertEqual(td.set_ops, [dict, list, dict, list, dict]) + self.assertEqual(g(l), "list") + self.assertEqual(len(td), 2) + self.assertEqual(td.get_ops, [list, dict, dict, list]) + self.assertEqual(td.set_ops, [dict, list, dict, list, dict, list]) + self.assertEqual(g(l), "list") + self.assertEqual(g(d), "sized") + self.assertEqual(td.get_ops, [list, dict, dict, list, list, dict]) + self.assertEqual(td.set_ops, [dict, list, dict, list, dict, list]) + g.dispatch(list) + g.dispatch(dict) + self.assertEqual(td.get_ops, [list, dict, dict, list, list, dict, + list, dict]) + self.assertEqual(td.set_ops, [dict, list, dict, list, dict, list]) + c.MutableSet.register(X) # Will invalidate the cache. + self.assertEqual(len(td), 2) # Stale cache. + self.assertEqual(g(l), "list") + self.assertEqual(len(td), 1) + g.register(c.MutableMapping, lambda arg: "mutablemapping") + self.assertEqual(len(td), 0) + self.assertEqual(g(d), "mutablemapping") + self.assertEqual(len(td), 1) + self.assertEqual(g(l), "list") + self.assertEqual(len(td), 2) + g.register(dict, lambda arg: "dict") + self.assertEqual(g(d), "dict") + self.assertEqual(g(l), "list") + g._clear_cache() + self.assertEqual(len(td), 0) + functools.WeakKeyDictionary = _orig_wkd + + def test_main(verbose=None): test_classes = ( TestPartialC, @@ -846,6 +1211,7 @@ TestWraps, TestReduce, TestLRU, + TestSingleDispatch, ) support.run_unittest(*test_classes) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -344,6 +344,9 @@ the default for linking if LDSHARED is not also overriden. This restores Distutils behavior introduced in 3.2.3 and inadvertently dropped in 3.3.0. +- Implement PEP 443 "Single-dispatch generic functions". + + Tests ----- diff --git a/Modules/Setup.dist b/Modules/Setup.dist --- a/Modules/Setup.dist +++ b/Modules/Setup.dist @@ -116,6 +116,7 @@ _operator _operator.c # operator.add() and similar goodies _collections _collectionsmodule.c # Container types itertools itertoolsmodule.c # Functions creating iterators for efficient looping +atexit atexitmodule.c # Register functions to be run at interpreter-shutdown # access to ISO C locale support _locale _localemodule.c # -lintl @@ -170,7 +171,6 @@ #_weakref _weakref.c # basic weak reference support #_testcapi _testcapimodule.c # Python C API test module #_random _randommodule.c # Random number generator -#atexit atexitmodule.c # Register functions to be run at interpreter-shutdown #_elementtree -I$(srcdir)/Modules/expat -DHAVE_EXPAT_CONFIG_H -DUSE_PYEXPAT_CAPI _elementtree.c # elementtree accelerator #_pickle _pickle.c # pickle accelerator #_datetime _datetimemodule.c # datetime accelerator -- Repository URL: http://hg.python.org/cpython From brett at python.org Wed Jun 5 16:31:04 2013 From: brett at python.org (Brett Cannon) Date: Wed, 5 Jun 2013 10:31:04 -0400 Subject: [Python-checkins] cpython: Add reference implementation for PEP 443 In-Reply-To: <3bQQxs6YX7zRbZ@mail.python.org> References: <3bQQxs6YX7zRbZ@mail.python.org> Message-ID: Any chance you could move your definitions for "generic function" and "single dispatch" to the glossary and just link to them here? On Wed, Jun 5, 2013 at 6:20 AM, lukasz.langa wrote: > http://hg.python.org/cpython/rev/dfcb64f51f7b > changeset: 84039:dfcb64f51f7b > user: ?ukasz Langa > date: Wed Jun 05 12:20:24 2013 +0200 > summary: > Add reference implementation for PEP 443 > > PEP accepted: > http://mail.python.org/pipermail/python-dev/2013-June/126734.html > > files: > Doc/library/functools.rst | 110 +++++++ > Lib/functools.py | 128 ++++++++- > Lib/pkgutil.py | 52 +--- > Lib/test/test_functools.py | 374 ++++++++++++++++++++++++- > Misc/NEWS | 3 + > Modules/Setup.dist | 2 +- > 6 files changed, 614 insertions(+), 55 deletions(-) > > > diff --git a/Doc/library/functools.rst b/Doc/library/functools.rst > --- a/Doc/library/functools.rst > +++ b/Doc/library/functools.rst > @@ -6,6 +6,7 @@ > .. moduleauthor:: Peter Harris > .. moduleauthor:: Raymond Hettinger > .. moduleauthor:: Nick Coghlan > +.. moduleauthor:: ?ukasz Langa > .. sectionauthor:: Peter Harris > > **Source code:** :source:`Lib/functools.py` > @@ -186,6 +187,115 @@ > *sequence* contains only one item, the first item is returned. > > > +.. decorator:: singledispatch(default) > + > + Transforms a function into a single-dispatch generic function. A > **generic > + function** is composed of multiple functions implementing the same > operation > + for different types. Which implementation should be used during a call > is > + determined by the dispatch algorithm. When the implementation is > chosen > + based on the type of a single argument, this is known as **single > + dispatch**. > + > + To define a generic function, decorate it with the ``@singledispatch`` > + decorator. Note that the dispatch happens on the type of the first > argument, > + create your function accordingly:: > + > + >>> from functools import singledispatch > + >>> @singledispatch > + ... def fun(arg, verbose=False): > + ... if verbose: > + ... print("Let me just say,", end=" ") > + ... print(arg) > + > + To add overloaded implementations to the function, use the > :func:`register` > + attribute of the generic function. It is a decorator, taking a type > + parameter and decorating a function implementing the operation for that > + type:: > + > + >>> @fun.register(int) > + ... def _(arg, verbose=False): > + ... if verbose: > + ... print("Strength in numbers, eh?", end=" ") > + ... print(arg) > + ... > + >>> @fun.register(list) > + ... def _(arg, verbose=False): > + ... if verbose: > + ... print("Enumerate this:") > + ... for i, elem in enumerate(arg): > + ... print(i, elem) > + > + To enable registering lambdas and pre-existing functions, the > + :func:`register` attribute can be used in a functional form:: > + > + >>> def nothing(arg, verbose=False): > + ... print("Nothing.") > + ... > + >>> fun.register(type(None), nothing) > + > + The :func:`register` attribute returns the undecorated function which > + enables decorator stacking, pickling, as well as creating unit tests > for > + each variant independently:: > + > + >>> @fun.register(float) > + ... @fun.register(Decimal) > + ... def fun_num(arg, verbose=False): > + ... if verbose: > + ... print("Half of your number:", end=" ") > + ... print(arg / 2) > + ... > + >>> fun_num is fun > + False > + > + When called, the generic function dispatches on the type of the first > + argument:: > + > + >>> fun("Hello, world.") > + Hello, world. > + >>> fun("test.", verbose=True) > + Let me just say, test. > + >>> fun(42, verbose=True) > + Strength in numbers, eh? 42 > + >>> fun(['spam', 'spam', 'eggs', 'spam'], verbose=True) > + Enumerate this: > + 0 spam > + 1 spam > + 2 eggs > + 3 spam > + >>> fun(None) > + Nothing. > + >>> fun(1.23) > + 0.615 > + > + Where there is no registered implementation for a specific type, its > + method resolution order is used to find a more generic implementation. > + The original function decorated with ``@singledispatch`` is registered > + for the base ``object`` type, which means it is used if no better > + implementation is found. > + > + To check which implementation will the generic function choose for > + a given type, use the ``dispatch()`` attribute:: > + > + >>> fun.dispatch(float) > + > + >>> fun.dispatch(dict) # note: default implementation > + > + > + To access all registered implementations, use the read-only > ``registry`` > + attribute:: > + > + >>> fun.registry.keys() > + dict_keys([, , , > + , , > + ]) > + >>> fun.registry[float] > + > + >>> fun.registry[object] > + > + > + .. versionadded:: 3.4 > + > + > .. function:: update_wrapper(wrapper, wrapped, > assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES) > > Update a *wrapper* function to look like the *wrapped* function. The > optional > diff --git a/Lib/functools.py b/Lib/functools.py > --- a/Lib/functools.py > +++ b/Lib/functools.py > @@ -3,19 +3,24 @@ > # Python module wrapper for _functools C module > # to allow utilities written in Python to be added > # to the functools module. > -# Written by Nick Coghlan > -# and Raymond Hettinger > -# Copyright (C) 2006-2010 Python Software Foundation. > +# Written by Nick Coghlan , > +# Raymond Hettinger , > +# and ?ukasz Langa . > +# Copyright (C) 2006-2013 Python Software Foundation. > # See C source code for _functools credits/copyright > > __all__ = ['update_wrapper', 'wraps', 'WRAPPER_ASSIGNMENTS', > 'WRAPPER_UPDATES', > - 'total_ordering', 'cmp_to_key', 'lru_cache', 'reduce', > 'partial'] > + 'total_ordering', 'cmp_to_key', 'lru_cache', 'reduce', > 'partial', > + 'singledispatch'] > > try: > from _functools import reduce > except ImportError: > pass > +from abc import get_cache_token > from collections import namedtuple > +from types import MappingProxyType > +from weakref import WeakKeyDictionary > try: > from _thread import RLock > except: > @@ -354,3 +359,118 @@ > return update_wrapper(wrapper, user_function) > > return decorating_function > + > + > > +################################################################################ > +### singledispatch() - single-dispatch generic function decorator > > +################################################################################ > + > +def _compose_mro(cls, haystack): > + """Calculates the MRO for a given class `cls`, including relevant > abstract > + base classes from `haystack`. > + > + """ > + bases = set(cls.__mro__) > + mro = list(cls.__mro__) > + for needle in haystack: > + if (needle in bases or not hasattr(needle, '__mro__') > + or not issubclass(cls, needle)): > + continue # either present in the __mro__ already or > unrelated > + for index, base in enumerate(mro): > + if not issubclass(base, needle): > + break > + if base in bases and not issubclass(needle, base): > + # Conflict resolution: put classes present in __mro__ and > their > + # subclasses first. See test_mro_conflicts() in > test_functools.py > + # for examples. > + index += 1 > + mro.insert(index, needle) > + return mro > + > +def _find_impl(cls, registry): > + """Returns the best matching implementation for the given class `cls` > in > + `registry`. Where there is no registered implementation for a specific > + type, its method resolution order is used to find a more generic > + implementation. > + > + Note: if `registry` does not contain an implementation for the base > + `object` type, this function may return None. > + > + """ > + mro = _compose_mro(cls, registry.keys()) > + match = None > + for t in mro: > + if match is not None: > + # If `match` is an ABC but there is another unrelated, equally > + # matching ABC. Refuse the temptation to guess. > + if (t in registry and not issubclass(match, t) > + and match not in cls.__mro__): > + raise RuntimeError("Ambiguous dispatch: {} or {}".format( > + match, t)) > + break > + if t in registry: > + match = t > + return registry.get(match) > + > +def singledispatch(func): > + """Single-dispatch generic function decorator. > + > + Transforms a function into a generic function, which can have > different > + behaviours depending upon the type of its first argument. The > decorated > + function acts as the default implementation, and additional > + implementations can be registered using the 'register()' attribute of > + the generic function. > + > + """ > + registry = {} > + dispatch_cache = WeakKeyDictionary() > + cache_token = None > + > + def dispatch(typ): > + """generic_func.dispatch(type) -> > + > + Runs the dispatch algorithm to return the best available > implementation > + for the given `type` registered on `generic_func`. > + > + """ > + nonlocal cache_token > + if cache_token is not None: > + current_token = get_cache_token() > + if cache_token != current_token: > + dispatch_cache.clear() > + cache_token = current_token > + try: > + impl = dispatch_cache[typ] > + except KeyError: > + try: > + impl = registry[typ] > + except KeyError: > + impl = _find_impl(typ, registry) > + dispatch_cache[typ] = impl > + return impl > + > + def register(typ, func=None): > + """generic_func.register(type, func) -> func > + > + Registers a new implementation for the given `type` on a > `generic_func`. > + > + """ > + nonlocal cache_token > + if func is None: > + return lambda f: register(typ, f) > + registry[typ] = func > + if cache_token is None and hasattr(typ, '__abstractmethods__'): > + cache_token = get_cache_token() > + dispatch_cache.clear() > + return func > + > + def wrapper(*args, **kw): > + return dispatch(args[0].__class__)(*args, **kw) > + > + registry[object] = func > + wrapper.register = register > + wrapper.dispatch = dispatch > + wrapper.registry = MappingProxyType(registry) > + wrapper._clear_cache = dispatch_cache.clear > + update_wrapper(wrapper, func) > + return wrapper > diff --git a/Lib/pkgutil.py b/Lib/pkgutil.py > --- a/Lib/pkgutil.py > +++ b/Lib/pkgutil.py > @@ -1,12 +1,13 @@ > """Utilities to support packages.""" > > +from functools import singledispatch as simplegeneric > +import imp > +import importlib > import os > +import os.path > import sys > -import importlib > -import imp > -import os.path > +from types import ModuleType > from warnings import warn > -from types import ModuleType > > __all__ = [ > 'get_importer', 'iter_importers', 'get_loader', 'find_loader', > @@ -27,46 +28,6 @@ > return marshal.load(stream) > > > -def simplegeneric(func): > - """Make a trivial single-dispatch generic function""" > - registry = {} > - def wrapper(*args, **kw): > - ob = args[0] > - try: > - cls = ob.__class__ > - except AttributeError: > - cls = type(ob) > - try: > - mro = cls.__mro__ > - except AttributeError: > - try: > - class cls(cls, object): > - pass > - mro = cls.__mro__[1:] > - except TypeError: > - mro = object, # must be an ExtensionClass or some such > :( > - for t in mro: > - if t in registry: > - return registry[t](*args, **kw) > - else: > - return func(*args, **kw) > - try: > - wrapper.__name__ = func.__name__ > - except (TypeError, AttributeError): > - pass # Python 2.3 doesn't allow functions to be renamed > - > - def register(typ, func=None): > - if func is None: > - return lambda f: register(typ, f) > - registry[typ] = func > - return func > - > - wrapper.__dict__ = func.__dict__ > - wrapper.__doc__ = func.__doc__ > - wrapper.register = register > - return wrapper > - > - > def walk_packages(path=None, prefix='', onerror=None): > """Yields (module_loader, name, ispkg) for all modules recursively > on path, or, if path is None, all accessible modules. > @@ -148,13 +109,12 @@ > yield i, name, ispkg > > > -#@simplegeneric > + at simplegeneric > def iter_importer_modules(importer, prefix=''): > if not hasattr(importer, 'iter_modules'): > return [] > return importer.iter_modules(prefix) > > -iter_importer_modules = simplegeneric(iter_importer_modules) > > # Implement a file walker for the normal importlib path hook > def _iter_file_finder_modules(importer, prefix=''): > diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py > --- a/Lib/test/test_functools.py > +++ b/Lib/test/test_functools.py > @@ -1,24 +1,30 @@ > import collections > -import sys > -import unittest > -from test import support > -from weakref import proxy > +from itertools import permutations > import pickle > from random import choice > +import sys > +from test import support > +import unittest > +from weakref import proxy > > import functools > > py_functools = support.import_fresh_module('functools', > blocked=['_functools']) > c_functools = support.import_fresh_module('functools', > fresh=['_functools']) > > +decimal = support.import_fresh_module('decimal', fresh=['_decimal']) > + > + > def capture(*args, **kw): > """capture all positional and keyword arguments""" > return args, kw > > + > def signature(part): > """ return the signature of a partial object """ > return (part.func, part.args, part.keywords, part.__dict__) > > + > class TestPartial: > > def test_basic_examples(self): > @@ -138,6 +144,7 @@ > join = self.partial(''.join) > self.assertEqual(join(data), '0123456789') > > + > @unittest.skipUnless(c_functools, 'requires the C _functools module') > class TestPartialC(TestPartial, unittest.TestCase): > if c_functools: > @@ -194,18 +201,22 @@ > "new style getargs format but argument is not a tuple", > f.__setstate__, BadSequence()) > > + > class TestPartialPy(TestPartial, unittest.TestCase): > partial = staticmethod(py_functools.partial) > > + > if c_functools: > class PartialSubclass(c_functools.partial): > pass > > + > @unittest.skipUnless(c_functools, 'requires the C _functools module') > class TestPartialCSubclass(TestPartialC): > if c_functools: > partial = PartialSubclass > > + > class TestUpdateWrapper(unittest.TestCase): > > def check_wrapper(self, wrapper, wrapped, > @@ -312,6 +323,7 @@ > self.assertTrue(wrapper.__doc__.startswith('max(')) > self.assertEqual(wrapper.__annotations__, {}) > > + > class TestWraps(TestUpdateWrapper): > > def _default_update(self): > @@ -372,6 +384,7 @@ > self.assertEqual(wrapper.attr, 'This is a different test') > self.assertEqual(wrapper.dict_attr, f.dict_attr) > > + > class TestReduce(unittest.TestCase): > func = functools.reduce > > @@ -452,6 +465,7 @@ > d = {"one": 1, "two": 2, "three": 3} > self.assertEqual(self.func(add, d), "".join(d.keys())) > > + > class TestCmpToKey: > > def test_cmp_to_key(self): > @@ -534,14 +548,17 @@ > self.assertRaises(TypeError, hash, k) > self.assertNotIsInstance(k, collections.Hashable) > > + > @unittest.skipUnless(c_functools, 'requires the C _functools module') > class TestCmpToKeyC(TestCmpToKey, unittest.TestCase): > if c_functools: > cmp_to_key = c_functools.cmp_to_key > > + > class TestCmpToKeyPy(TestCmpToKey, unittest.TestCase): > cmp_to_key = staticmethod(py_functools.cmp_to_key) > > + > class TestTotalOrdering(unittest.TestCase): > > def test_total_ordering_lt(self): > @@ -642,6 +659,7 @@ > with self.assertRaises(TypeError): > TestTO(8) <= () > > + > class TestLRU(unittest.TestCase): > > def test_lru(self): > @@ -834,6 +852,353 @@ > DoubleEq(2)) # Verify the correct > return value > > > +class TestSingleDispatch(unittest.TestCase): > + def test_simple_overloads(self): > + @functools.singledispatch > + def g(obj): > + return "base" > + def g_int(i): > + return "integer" > + g.register(int, g_int) > + self.assertEqual(g("str"), "base") > + self.assertEqual(g(1), "integer") > + self.assertEqual(g([1,2,3]), "base") > + > + def test_mro(self): > + @functools.singledispatch > + def g(obj): > + return "base" > + class C: > + pass > + class D(C): > + pass > + def g_C(c): > + return "C" > + g.register(C, g_C) > + self.assertEqual(g(C()), "C") > + self.assertEqual(g(D()), "C") > + > + def test_classic_classes(self): > + @functools.singledispatch > + def g(obj): > + return "base" > + class C: > + pass > + class D(C): > + pass > + def g_C(c): > + return "C" > + g.register(C, g_C) > + self.assertEqual(g(C()), "C") > + self.assertEqual(g(D()), "C") > + > + def test_register_decorator(self): > + @functools.singledispatch > + def g(obj): > + return "base" > + @g.register(int) > + def g_int(i): > + return "int %s" % (i,) > + self.assertEqual(g(""), "base") > + self.assertEqual(g(12), "int 12") > + self.assertIs(g.dispatch(int), g_int) > + self.assertIs(g.dispatch(object), g.dispatch(str)) > + # Note: in the assert above this is not g. > + # @singledispatch returns the wrapper. > + > + def test_wrapping_attributes(self): > + @functools.singledispatch > + def g(obj): > + "Simple test" > + return "Test" > + self.assertEqual(g.__name__, "g") > + self.assertEqual(g.__doc__, "Simple test") > + > + @unittest.skipUnless(decimal, 'requires _decimal') > + @support.cpython_only > + def test_c_classes(self): > + @functools.singledispatch > + def g(obj): > + return "base" > + @g.register(decimal.DecimalException) > + def _(obj): > + return obj.args > + subn = decimal.Subnormal("Exponent < Emin") > + rnd = decimal.Rounded("Number got rounded") > + self.assertEqual(g(subn), ("Exponent < Emin",)) > + self.assertEqual(g(rnd), ("Number got rounded",)) > + @g.register(decimal.Subnormal) > + def _(obj): > + return "Too small to care." > + self.assertEqual(g(subn), "Too small to care.") > + self.assertEqual(g(rnd), ("Number got rounded",)) > + > + def test_compose_mro(self): > + c = collections > + mro = functools._compose_mro > + bases = [c.Sequence, c.MutableMapping, c.Mapping, c.Set] > + for haystack in permutations(bases): > + m = mro(dict, haystack) > + self.assertEqual(m, [dict, c.MutableMapping, c.Mapping, > object]) > + bases = [c.Container, c.Mapping, c.MutableMapping, c.OrderedDict] > + for haystack in permutations(bases): > + m = mro(c.ChainMap, haystack) > + self.assertEqual(m, [c.ChainMap, c.MutableMapping, c.Mapping, > + c.Sized, c.Iterable, c.Container, > object]) > + # Note: The MRO order below depends on haystack ordering. > + m = mro(c.defaultdict, [c.Sized, c.Container, str]) > + self.assertEqual(m, [c.defaultdict, dict, c.Container, c.Sized, > object]) > + m = mro(c.defaultdict, [c.Container, c.Sized, str]) > + self.assertEqual(m, [c.defaultdict, dict, c.Sized, c.Container, > object]) > + > + def test_register_abc(self): > + c = collections > + d = {"a": "b"} > + l = [1, 2, 3] > + s = {object(), None} > + f = frozenset(s) > + t = (1, 2, 3) > + @functools.singledispatch > + def g(obj): > + return "base" > + self.assertEqual(g(d), "base") > + self.assertEqual(g(l), "base") > + self.assertEqual(g(s), "base") > + self.assertEqual(g(f), "base") > + self.assertEqual(g(t), "base") > + g.register(c.Sized, lambda obj: "sized") > + self.assertEqual(g(d), "sized") > + self.assertEqual(g(l), "sized") > + self.assertEqual(g(s), "sized") > + self.assertEqual(g(f), "sized") > + self.assertEqual(g(t), "sized") > + g.register(c.MutableMapping, lambda obj: "mutablemapping") > + self.assertEqual(g(d), "mutablemapping") > + self.assertEqual(g(l), "sized") > + self.assertEqual(g(s), "sized") > + self.assertEqual(g(f), "sized") > + self.assertEqual(g(t), "sized") > + g.register(c.ChainMap, lambda obj: "chainmap") > + self.assertEqual(g(d), "mutablemapping") # irrelevant ABCs > registered > + self.assertEqual(g(l), "sized") > + self.assertEqual(g(s), "sized") > + self.assertEqual(g(f), "sized") > + self.assertEqual(g(t), "sized") > + g.register(c.MutableSequence, lambda obj: "mutablesequence") > + self.assertEqual(g(d), "mutablemapping") > + self.assertEqual(g(l), "mutablesequence") > + self.assertEqual(g(s), "sized") > + self.assertEqual(g(f), "sized") > + self.assertEqual(g(t), "sized") > + g.register(c.MutableSet, lambda obj: "mutableset") > + self.assertEqual(g(d), "mutablemapping") > + self.assertEqual(g(l), "mutablesequence") > + self.assertEqual(g(s), "mutableset") > + self.assertEqual(g(f), "sized") > + self.assertEqual(g(t), "sized") > + g.register(c.Mapping, lambda obj: "mapping") > + self.assertEqual(g(d), "mutablemapping") # not specific enough > + self.assertEqual(g(l), "mutablesequence") > + self.assertEqual(g(s), "mutableset") > + self.assertEqual(g(f), "sized") > + self.assertEqual(g(t), "sized") > + g.register(c.Sequence, lambda obj: "sequence") > + self.assertEqual(g(d), "mutablemapping") > + self.assertEqual(g(l), "mutablesequence") > + self.assertEqual(g(s), "mutableset") > + self.assertEqual(g(f), "sized") > + self.assertEqual(g(t), "sequence") > + g.register(c.Set, lambda obj: "set") > + self.assertEqual(g(d), "mutablemapping") > + self.assertEqual(g(l), "mutablesequence") > + self.assertEqual(g(s), "mutableset") > + self.assertEqual(g(f), "set") > + self.assertEqual(g(t), "sequence") > + g.register(dict, lambda obj: "dict") > + self.assertEqual(g(d), "dict") > + self.assertEqual(g(l), "mutablesequence") > + self.assertEqual(g(s), "mutableset") > + self.assertEqual(g(f), "set") > + self.assertEqual(g(t), "sequence") > + g.register(list, lambda obj: "list") > + self.assertEqual(g(d), "dict") > + self.assertEqual(g(l), "list") > + self.assertEqual(g(s), "mutableset") > + self.assertEqual(g(f), "set") > + self.assertEqual(g(t), "sequence") > + g.register(set, lambda obj: "concrete-set") > + self.assertEqual(g(d), "dict") > + self.assertEqual(g(l), "list") > + self.assertEqual(g(s), "concrete-set") > + self.assertEqual(g(f), "set") > + self.assertEqual(g(t), "sequence") > + g.register(frozenset, lambda obj: "frozen-set") > + self.assertEqual(g(d), "dict") > + self.assertEqual(g(l), "list") > + self.assertEqual(g(s), "concrete-set") > + self.assertEqual(g(f), "frozen-set") > + self.assertEqual(g(t), "sequence") > + g.register(tuple, lambda obj: "tuple") > + self.assertEqual(g(d), "dict") > + self.assertEqual(g(l), "list") > + self.assertEqual(g(s), "concrete-set") > + self.assertEqual(g(f), "frozen-set") > + self.assertEqual(g(t), "tuple") > + > + def test_mro_conflicts(self): > + c = collections > + > + @functools.singledispatch > + def g(arg): > + return "base" > + > + class O(c.Sized): > + def __len__(self): > + return 0 > + > + o = O() > + self.assertEqual(g(o), "base") > + g.register(c.Iterable, lambda arg: "iterable") > + g.register(c.Container, lambda arg: "container") > + g.register(c.Sized, lambda arg: "sized") > + g.register(c.Set, lambda arg: "set") > + self.assertEqual(g(o), "sized") > + c.Iterable.register(O) > + self.assertEqual(g(o), "sized") # because it's explicitly in > __mro__ > + c.Container.register(O) > + self.assertEqual(g(o), "sized") # see above: Sized is in __mro__ > + > + class P: > + pass > + > + p = P() > + self.assertEqual(g(p), "base") > + c.Iterable.register(P) > + self.assertEqual(g(p), "iterable") > + c.Container.register(P) > + with self.assertRaises(RuntimeError) as re: > + g(p) > + self.assertEqual( > + str(re), > + ("Ambiguous dispatch: > " > + "or "), > + ) > + > + class Q(c.Sized): > + def __len__(self): > + return 0 > + > + q = Q() > + self.assertEqual(g(q), "sized") > + c.Iterable.register(Q) > + self.assertEqual(g(q), "sized") # because it's explicitly in > __mro__ > + c.Set.register(Q) > + self.assertEqual(g(q), "set") # because c.Set is a subclass of > + # c.Sized which is explicitly in > + # __mro__ > + > + def test_cache_invalidation(self): > + from collections import UserDict > + class TracingDict(UserDict): > + def __init__(self, *args, **kwargs): > + super(TracingDict, self).__init__(*args, **kwargs) > + self.set_ops = [] > + self.get_ops = [] > + def __getitem__(self, key): > + result = self.data[key] > + self.get_ops.append(key) > + return result > + def __setitem__(self, key, value): > + self.set_ops.append(key) > + self.data[key] = value > + def clear(self): > + self.data.clear() > + _orig_wkd = functools.WeakKeyDictionary > + td = TracingDict() > + functools.WeakKeyDictionary = lambda: td > + c = collections > + @functools.singledispatch > + def g(arg): > + return "base" > + d = {} > + l = [] > + self.assertEqual(len(td), 0) > + self.assertEqual(g(d), "base") > + self.assertEqual(len(td), 1) > + self.assertEqual(td.get_ops, []) > + self.assertEqual(td.set_ops, [dict]) > + self.assertEqual(td.data[dict], g.registry[object]) > + self.assertEqual(g(l), "base") > + self.assertEqual(len(td), 2) > + self.assertEqual(td.get_ops, []) > + self.assertEqual(td.set_ops, [dict, list]) > + self.assertEqual(td.data[dict], g.registry[object]) > + self.assertEqual(td.data[list], g.registry[object]) > + self.assertEqual(td.data[dict], td.data[list]) > + self.assertEqual(g(l), "base") > + self.assertEqual(g(d), "base") > + self.assertEqual(td.get_ops, [list, dict]) > + self.assertEqual(td.set_ops, [dict, list]) > + g.register(list, lambda arg: "list") > + self.assertEqual(td.get_ops, [list, dict]) > + self.assertEqual(len(td), 0) > + self.assertEqual(g(d), "base") > + self.assertEqual(len(td), 1) > + self.assertEqual(td.get_ops, [list, dict]) > + self.assertEqual(td.set_ops, [dict, list, dict]) > + self.assertEqual(td.data[dict], > + functools._find_impl(dict, g.registry)) > + self.assertEqual(g(l), "list") > + self.assertEqual(len(td), 2) > + self.assertEqual(td.get_ops, [list, dict]) > + self.assertEqual(td.set_ops, [dict, list, dict, list]) > + self.assertEqual(td.data[list], > + functools._find_impl(list, g.registry)) > + class X: > + pass > + c.MutableMapping.register(X) # Will not invalidate the cache, > + # not using ABCs yet. > + self.assertEqual(g(d), "base") > + self.assertEqual(g(l), "list") > + self.assertEqual(td.get_ops, [list, dict, dict, list]) > + self.assertEqual(td.set_ops, [dict, list, dict, list]) > + g.register(c.Sized, lambda arg: "sized") > + self.assertEqual(len(td), 0) > + self.assertEqual(g(d), "sized") > + self.assertEqual(len(td), 1) > + self.assertEqual(td.get_ops, [list, dict, dict, list]) > + self.assertEqual(td.set_ops, [dict, list, dict, list, dict]) > + self.assertEqual(g(l), "list") > + self.assertEqual(len(td), 2) > + self.assertEqual(td.get_ops, [list, dict, dict, list]) > + self.assertEqual(td.set_ops, [dict, list, dict, list, dict, list]) > + self.assertEqual(g(l), "list") > + self.assertEqual(g(d), "sized") > + self.assertEqual(td.get_ops, [list, dict, dict, list, list, dict]) > + self.assertEqual(td.set_ops, [dict, list, dict, list, dict, list]) > + g.dispatch(list) > + g.dispatch(dict) > + self.assertEqual(td.get_ops, [list, dict, dict, list, list, dict, > + list, dict]) > + self.assertEqual(td.set_ops, [dict, list, dict, list, dict, list]) > + c.MutableSet.register(X) # Will invalidate the cache. > + self.assertEqual(len(td), 2) # Stale cache. > + self.assertEqual(g(l), "list") > + self.assertEqual(len(td), 1) > + g.register(c.MutableMapping, lambda arg: "mutablemapping") > + self.assertEqual(len(td), 0) > + self.assertEqual(g(d), "mutablemapping") > + self.assertEqual(len(td), 1) > + self.assertEqual(g(l), "list") > + self.assertEqual(len(td), 2) > + g.register(dict, lambda arg: "dict") > + self.assertEqual(g(d), "dict") > + self.assertEqual(g(l), "list") > + g._clear_cache() > + self.assertEqual(len(td), 0) > + functools.WeakKeyDictionary = _orig_wkd > + > + > def test_main(verbose=None): > test_classes = ( > TestPartialC, > @@ -846,6 +1211,7 @@ > TestWraps, > TestReduce, > TestLRU, > + TestSingleDispatch, > ) > support.run_unittest(*test_classes) > > diff --git a/Misc/NEWS b/Misc/NEWS > --- a/Misc/NEWS > +++ b/Misc/NEWS > @@ -344,6 +344,9 @@ > the default for linking if LDSHARED is not also overriden. This > restores > Distutils behavior introduced in 3.2.3 and inadvertently dropped in > 3.3.0. > > +- Implement PEP 443 "Single-dispatch generic functions". > + > + > Tests > ----- > > diff --git a/Modules/Setup.dist b/Modules/Setup.dist > --- a/Modules/Setup.dist > +++ b/Modules/Setup.dist > @@ -116,6 +116,7 @@ > _operator _operator.c # operator.add() and similar goodies > _collections _collectionsmodule.c # Container types > itertools itertoolsmodule.c # Functions creating iterators for > efficient looping > +atexit atexitmodule.c # Register functions to be run at > interpreter-shutdown > > # access to ISO C locale support > _locale _localemodule.c # -lintl > @@ -170,7 +171,6 @@ > #_weakref _weakref.c # basic weak reference support > #_testcapi _testcapimodule.c # Python C API test module > #_random _randommodule.c # Random number generator > -#atexit atexitmodule.c # Register functions to be run at > interpreter-shutdown > #_elementtree -I$(srcdir)/Modules/expat -DHAVE_EXPAT_CONFIG_H > -DUSE_PYEXPAT_CAPI _elementtree.c # elementtree accelerator > #_pickle _pickle.c # pickle accelerator > #_datetime _datetimemodule.c # datetime accelerator > > -- > 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 lukasz at langa.pl Wed Jun 5 17:52:56 2013 From: lukasz at langa.pl (=?utf-8?Q?=C5=81ukasz_Langa?=) Date: Wed, 5 Jun 2013 17:52:56 +0200 Subject: [Python-checkins] [Python-Dev] cpython: Add reference implementation for PEP 443 In-Reply-To: References: <3bQQxs6YX7zRbZ@mail.python.org> Message-ID: <0F939F6D-63D1-4D93-9EA7-39C7C09E0F6A@langa.pl> Dnia 5 cze 2013 o godz. 16:31 Brett Cannon napisa?(a): > Any chance you could move your definitions for "generic function" and "single dispatch" to the glossary and just link to them here? Sure thing. -- ? From python-checkins at python.org Wed Jun 5 20:41:11 2013 From: python-checkins at python.org (terry.reedy) Date: Wed, 5 Jun 2013 20:41:11 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogSXNzdWUxODEzMDog?= =?utf-8?q?Test_class_idlelib=2EconfigSectionNameDialog=2EGetCfgSectionNam?= =?utf-8?q?eDialog=2E?= Message-ID: <3bQf3C3gWJz7LkX@mail.python.org> http://hg.python.org/cpython/rev/db4ecaf852e3 changeset: 84040:db4ecaf852e3 branch: 3.3 parent: 84019:ffdee6b36305 user: Terry Jan Reedy date: Wed Jun 05 14:22:26 2013 -0400 summary: Issue18130: Test class idlelib.configSectionNameDialog.GetCfgSectionNameDialog. Fix bug in existing human test and add instructions; fix two bugs in tested code; remove redundancies, add spaces, and change two internal method names. Add mock_tk with mocks for tkinter.Variable subclasses and tkinter.messagebox. Use mocks in test_config_name to unittest methods that are otherwise gui-free. files: Lib/idlelib/configSectionNameDialog.py | 111 +++++---- Lib/idlelib/idle_test/mock_tk.py | 63 +++++ Lib/idlelib/idle_test/test_config_name.py | 75 ++++++ 3 files changed, 198 insertions(+), 51 deletions(-) diff --git a/Lib/idlelib/configSectionNameDialog.py b/Lib/idlelib/configSectionNameDialog.py --- a/Lib/idlelib/configSectionNameDialog.py +++ b/Lib/idlelib/configSectionNameDialog.py @@ -1,97 +1,106 @@ """ Dialog that allows user to specify a new config file section name. Used to get new highlight theme and keybinding set names. +The 'return value' for the dialog, used two placed in configDialog.py, +is the .result attribute set in the Ok and Cancel methods. """ from tkinter import * import tkinter.messagebox as tkMessageBox class GetCfgSectionNameDialog(Toplevel): - def __init__(self,parent,title,message,usedNames): + def __init__(self, parent, title, message, used_names): """ message - string, informational message to display - usedNames - list, list of names already in use for validity check + used_names - string collection, names already in use for validity check """ Toplevel.__init__(self, parent) self.configure(borderwidth=5) - self.resizable(height=FALSE,width=FALSE) + self.resizable(height=FALSE, width=FALSE) self.title(title) self.transient(parent) self.grab_set() self.protocol("WM_DELETE_WINDOW", self.Cancel) self.parent = parent - self.message=message - self.usedNames=usedNames - self.result='' - self.CreateWidgets() - self.withdraw() #hide while setting geometry + self.message = message + self.used_names = used_names + self.create_widgets() + self.withdraw() #hide while setting geometry self.update_idletasks() #needs to be done here so that the winfo_reqwidth is valid self.messageInfo.config(width=self.frameMain.winfo_reqwidth()) - self.geometry("+%d+%d" % - ((parent.winfo_rootx()+((parent.winfo_width()/2) - -(self.winfo_reqwidth()/2)), - parent.winfo_rooty()+((parent.winfo_height()/2) - -(self.winfo_reqheight()/2)) )) ) #centre dialog over parent - self.deiconify() #geometry set, unhide + self.geometry( + "+%d+%d" % ( + parent.winfo_rootx() + + (parent.winfo_width()/2 - self.winfo_reqwidth()/2), + parent.winfo_rooty() + + (parent.winfo_height()/2 - self.winfo_reqheight()/2) + ) ) #centre dialog over parent + self.deiconify() #geometry set, unhide self.wait_window() - def CreateWidgets(self): - self.name=StringVar(self) - self.fontSize=StringVar(self) - self.frameMain = Frame(self,borderwidth=2,relief=SUNKEN) - self.frameMain.pack(side=TOP,expand=TRUE,fill=BOTH) - self.messageInfo=Message(self.frameMain,anchor=W,justify=LEFT,padx=5,pady=5, - text=self.message)#,aspect=200) - entryName=Entry(self.frameMain,textvariable=self.name,width=30) + def create_widgets(self): + self.name = StringVar(self.parent) + self.fontSize = StringVar(self.parent) + self.frameMain = Frame(self, borderwidth=2, relief=SUNKEN) + self.frameMain.pack(side=TOP, expand=TRUE, fill=BOTH) + self.messageInfo = Message(self.frameMain, anchor=W, justify=LEFT, + padx=5, pady=5, text=self.message) #,aspect=200) + entryName = Entry(self.frameMain, textvariable=self.name, width=30) entryName.focus_set() - self.messageInfo.pack(padx=5,pady=5)#,expand=TRUE,fill=BOTH) - entryName.pack(padx=5,pady=5) - frameButtons=Frame(self) - frameButtons.pack(side=BOTTOM,fill=X) - self.buttonOk = Button(frameButtons,text='Ok', - width=8,command=self.Ok) - self.buttonOk.grid(row=0,column=0,padx=5,pady=5) - self.buttonCancel = Button(frameButtons,text='Cancel', - width=8,command=self.Cancel) - self.buttonCancel.grid(row=0,column=1,padx=5,pady=5) + self.messageInfo.pack(padx=5, pady=5) #, expand=TRUE, fill=BOTH) + entryName.pack(padx=5, pady=5) - def NameOk(self): - #simple validity check for a sensible - #ConfigParser file section name - nameOk=1 - name=self.name.get() - name.strip() + frameButtons = Frame(self, pady=2) + frameButtons.pack(side=BOTTOM) + self.buttonOk = Button(frameButtons, text='Ok', + width=8, command=self.Ok) + self.buttonOk.pack(side=LEFT, padx=5) + self.buttonCancel = Button(frameButtons, text='Cancel', + width=8, command=self.Cancel) + self.buttonCancel.pack(side=RIGHT, padx=5) + + def name_ok(self): + ''' After stripping entered name, check that it is a sensible + ConfigParser file section name. Return it if it is, '' if not. + ''' + name = self.name.get().strip() if not name: #no name specified tkMessageBox.showerror(title='Name Error', message='No name specified.', parent=self) - nameOk=0 elif len(name)>30: #name too long tkMessageBox.showerror(title='Name Error', message='Name too long. It should be no more than '+ '30 characters.', parent=self) - nameOk=0 - elif name in self.usedNames: + name = '' + elif name in self.used_names: tkMessageBox.showerror(title='Name Error', message='This name is already in use.', parent=self) - nameOk=0 - return nameOk + name = '' + return name def Ok(self, event=None): - if self.NameOk(): - self.result=self.name.get().strip() + name = self.name_ok() + if name: + self.result = name self.destroy() def Cancel(self, event=None): - self.result='' + self.result = '' self.destroy() if __name__ == '__main__': - #test the dialog - root=Tk() + import unittest + unittest.main('idlelib.idle_test.test_config_name', verbosity=2, exit=False) + + # also human test the dialog + root = Tk() def run(): - keySeq='' dlg=GetCfgSectionNameDialog(root,'Get Name', - 'The information here should need to be word wrapped. Test.') + "After the text entered with [Ok] is stripped, , " + "'abc', or more that 30 chars are errors. " + "Close with a valid entry (printed), [Cancel], or [X]", + {'abc'}) print(dlg.result) - Button(root,text='Dialog',command=run).pack() + Message(root, text='').pack() # will be needed for oher dialog tests + Button(root, text='Click to begin dialog test', command=run).pack() root.mainloop() diff --git a/Lib/idlelib/idle_test/mock_tk.py b/Lib/idlelib/idle_test/mock_tk.py new file mode 100644 --- /dev/null +++ b/Lib/idlelib/idle_test/mock_tk.py @@ -0,0 +1,63 @@ +"""Classes that replace tkinter gui objects used by an object being tested. +A gui object is anything with a master or parent paramenter, which is typically +required in spite of what the doc strings say. +""" + +class Var: + "Use for String/Int/BooleanVar: incomplete" + def __init__(self, master=None, value=None, name=None): + self.master = master + self.value = value + self.name = name + def set(self, value): + self.value = value + def get(self): + return self.value + +class Mbox_func: + """Generic mock for messagebox functions. All have same call signature. + Mbox instantiates once for each function. Tester uses attributes. + """ + def __init__(self): + self.result = None # The return for all show funcs + def __call__(self, title, message, *args, **kwds): + # Save all args for possible examination by tester + self.title = title + self.message = message + self.args = args + self.kwds = kwds + return self.result # Set by tester for ask functions + +class Mbox: + """Mock for tkinter.messagebox with an Mbox_func for each function. + This module was 'tkMessageBox' in 2.x; hence the 'import as' in 3.x. + Example usage in test_module.py for testing functios in module.py: + --- +from idlelib.idle_test.mock_tk import Mbox +import module + +orig_mbox = module.tkMessageBox +showerror = Mbox.showerror # example, for attribute access in test methods + +class Test(unittest.TestCase): + + @classmethod + def setUpClass(cls): + module.tkMessageBox = Mbox + + @classmethod + def tearDownClass(cls): + module.tkMessageBox = orig_mbox + --- + When tkMessageBox functions are the only gui making calls in a method, + this replacement makes the method gui-free and unit-testable. + For 'ask' functions, set func.result return before calling method. + """ + askokcancel = Mbox_func() # True or False + askquestion = Mbox_func() # 'yes' or 'no' + askretrycancel = Mbox_func() # True or False + askyesno = Mbox_func() # True or False + askyesnocancel = Mbox_func() # True, False, or None + showerror = Mbox_func() # None + showinfo = Mbox_func() # None + showwarning = Mbox_func() # None diff --git a/Lib/idlelib/idle_test/test_config_name.py b/Lib/idlelib/idle_test/test_config_name.py new file mode 100644 --- /dev/null +++ b/Lib/idlelib/idle_test/test_config_name.py @@ -0,0 +1,75 @@ +"""Unit tests for idlelib.configSectionNameDialog""" +import unittest +from idlelib.idle_test.mock_tk import Var, Mbox +from idlelib import configSectionNameDialog as name_dialog_module + +name_dialog = name_dialog_module.GetCfgSectionNameDialog + +class Dummy_name_dialog: + # Mock for testing the following methods of name_dialog + name_ok = name_dialog.name_ok + Ok = name_dialog.Ok + Cancel = name_dialog.Cancel + # Attributes, constant or variable, needed for tests + used_names = ['used'] + name = Var() + result = None + destroyed = False + def destroy(self): + self.destroyed = True + +# name_ok calls Mbox.showerror if name is not ok +orig_mbox = name_dialog_module.tkMessageBox +showerror = Mbox.showerror + +class TestConfigName(unittest.TestCase): + dialog = Dummy_name_dialog() + + @classmethod + def setUpClass(cls): + name_dialog_module.tkMessageBox = Mbox + + @classmethod + def tearDownClass(cls): + name_dialog_module.tkMessageBox = orig_mbox + + def test_blank_name(self): + self.dialog.name.set(' ') + self.assertEqual(self.dialog.name_ok(), '') + self.assertEqual(showerror.title, 'Name Error') + self.assertIn('No', showerror.message) + + def test_used_name(self): + self.dialog.name.set('used') + self.assertEqual(self.dialog.name_ok(), '') + self.assertEqual(showerror.title, 'Name Error') + self.assertIn('use', showerror.message) + + def test_long_name(self): + self.dialog.name.set('good'*8) + self.assertEqual(self.dialog.name_ok(), '') + self.assertEqual(showerror.title, 'Name Error') + self.assertIn('too long', showerror.message) + + def test_good_name(self): + self.dialog.name.set(' good ') + showerror.title = 'No Error' # should not be called + self.assertEqual(self.dialog.name_ok(), 'good') + self.assertEqual(showerror.title, 'No Error') + + def test_ok(self): + self.dialog.destroyed = False + self.dialog.name.set('good') + self.dialog.Ok() + self.assertEqual(self.dialog.result, 'good') + self.assertTrue(self.dialog.destroyed) + + def test_cancel(self): + self.dialog.destroyed = False + self.dialog.Cancel() + self.assertEqual(self.dialog.result, '') + self.assertTrue(self.dialog.destroyed) + + +if __name__ == '__main__': + unittest.main(verbosity=2, exit=False) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 5 20:41:12 2013 From: python-checkins at python.org (terry.reedy) Date: Wed, 5 Jun 2013 20:41:12 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_Merge_with_3=2E3?= Message-ID: <3bQf3D6wLyz7Lmm@mail.python.org> http://hg.python.org/cpython/rev/367377d800a5 changeset: 84041:367377d800a5 parent: 84039:dfcb64f51f7b parent: 84040:db4ecaf852e3 user: Terry Jan Reedy date: Wed Jun 05 14:23:53 2013 -0400 summary: Merge with 3.3 files: Lib/idlelib/configSectionNameDialog.py | 111 +++++---- Lib/idlelib/idle_test/mock_tk.py | 63 +++++ Lib/idlelib/idle_test/test_config_name.py | 75 ++++++ 3 files changed, 198 insertions(+), 51 deletions(-) diff --git a/Lib/idlelib/configSectionNameDialog.py b/Lib/idlelib/configSectionNameDialog.py --- a/Lib/idlelib/configSectionNameDialog.py +++ b/Lib/idlelib/configSectionNameDialog.py @@ -1,97 +1,106 @@ """ Dialog that allows user to specify a new config file section name. Used to get new highlight theme and keybinding set names. +The 'return value' for the dialog, used two placed in configDialog.py, +is the .result attribute set in the Ok and Cancel methods. """ from tkinter import * import tkinter.messagebox as tkMessageBox class GetCfgSectionNameDialog(Toplevel): - def __init__(self,parent,title,message,usedNames): + def __init__(self, parent, title, message, used_names): """ message - string, informational message to display - usedNames - list, list of names already in use for validity check + used_names - string collection, names already in use for validity check """ Toplevel.__init__(self, parent) self.configure(borderwidth=5) - self.resizable(height=FALSE,width=FALSE) + self.resizable(height=FALSE, width=FALSE) self.title(title) self.transient(parent) self.grab_set() self.protocol("WM_DELETE_WINDOW", self.Cancel) self.parent = parent - self.message=message - self.usedNames=usedNames - self.result='' - self.CreateWidgets() - self.withdraw() #hide while setting geometry + self.message = message + self.used_names = used_names + self.create_widgets() + self.withdraw() #hide while setting geometry self.update_idletasks() #needs to be done here so that the winfo_reqwidth is valid self.messageInfo.config(width=self.frameMain.winfo_reqwidth()) - self.geometry("+%d+%d" % - ((parent.winfo_rootx()+((parent.winfo_width()/2) - -(self.winfo_reqwidth()/2)), - parent.winfo_rooty()+((parent.winfo_height()/2) - -(self.winfo_reqheight()/2)) )) ) #centre dialog over parent - self.deiconify() #geometry set, unhide + self.geometry( + "+%d+%d" % ( + parent.winfo_rootx() + + (parent.winfo_width()/2 - self.winfo_reqwidth()/2), + parent.winfo_rooty() + + (parent.winfo_height()/2 - self.winfo_reqheight()/2) + ) ) #centre dialog over parent + self.deiconify() #geometry set, unhide self.wait_window() - def CreateWidgets(self): - self.name=StringVar(self) - self.fontSize=StringVar(self) - self.frameMain = Frame(self,borderwidth=2,relief=SUNKEN) - self.frameMain.pack(side=TOP,expand=TRUE,fill=BOTH) - self.messageInfo=Message(self.frameMain,anchor=W,justify=LEFT,padx=5,pady=5, - text=self.message)#,aspect=200) - entryName=Entry(self.frameMain,textvariable=self.name,width=30) + def create_widgets(self): + self.name = StringVar(self.parent) + self.fontSize = StringVar(self.parent) + self.frameMain = Frame(self, borderwidth=2, relief=SUNKEN) + self.frameMain.pack(side=TOP, expand=TRUE, fill=BOTH) + self.messageInfo = Message(self.frameMain, anchor=W, justify=LEFT, + padx=5, pady=5, text=self.message) #,aspect=200) + entryName = Entry(self.frameMain, textvariable=self.name, width=30) entryName.focus_set() - self.messageInfo.pack(padx=5,pady=5)#,expand=TRUE,fill=BOTH) - entryName.pack(padx=5,pady=5) - frameButtons=Frame(self) - frameButtons.pack(side=BOTTOM,fill=X) - self.buttonOk = Button(frameButtons,text='Ok', - width=8,command=self.Ok) - self.buttonOk.grid(row=0,column=0,padx=5,pady=5) - self.buttonCancel = Button(frameButtons,text='Cancel', - width=8,command=self.Cancel) - self.buttonCancel.grid(row=0,column=1,padx=5,pady=5) + self.messageInfo.pack(padx=5, pady=5) #, expand=TRUE, fill=BOTH) + entryName.pack(padx=5, pady=5) - def NameOk(self): - #simple validity check for a sensible - #ConfigParser file section name - nameOk=1 - name=self.name.get() - name.strip() + frameButtons = Frame(self, pady=2) + frameButtons.pack(side=BOTTOM) + self.buttonOk = Button(frameButtons, text='Ok', + width=8, command=self.Ok) + self.buttonOk.pack(side=LEFT, padx=5) + self.buttonCancel = Button(frameButtons, text='Cancel', + width=8, command=self.Cancel) + self.buttonCancel.pack(side=RIGHT, padx=5) + + def name_ok(self): + ''' After stripping entered name, check that it is a sensible + ConfigParser file section name. Return it if it is, '' if not. + ''' + name = self.name.get().strip() if not name: #no name specified tkMessageBox.showerror(title='Name Error', message='No name specified.', parent=self) - nameOk=0 elif len(name)>30: #name too long tkMessageBox.showerror(title='Name Error', message='Name too long. It should be no more than '+ '30 characters.', parent=self) - nameOk=0 - elif name in self.usedNames: + name = '' + elif name in self.used_names: tkMessageBox.showerror(title='Name Error', message='This name is already in use.', parent=self) - nameOk=0 - return nameOk + name = '' + return name def Ok(self, event=None): - if self.NameOk(): - self.result=self.name.get().strip() + name = self.name_ok() + if name: + self.result = name self.destroy() def Cancel(self, event=None): - self.result='' + self.result = '' self.destroy() if __name__ == '__main__': - #test the dialog - root=Tk() + import unittest + unittest.main('idlelib.idle_test.test_config_name', verbosity=2, exit=False) + + # also human test the dialog + root = Tk() def run(): - keySeq='' dlg=GetCfgSectionNameDialog(root,'Get Name', - 'The information here should need to be word wrapped. Test.') + "After the text entered with [Ok] is stripped, , " + "'abc', or more that 30 chars are errors. " + "Close with a valid entry (printed), [Cancel], or [X]", + {'abc'}) print(dlg.result) - Button(root,text='Dialog',command=run).pack() + Message(root, text='').pack() # will be needed for oher dialog tests + Button(root, text='Click to begin dialog test', command=run).pack() root.mainloop() diff --git a/Lib/idlelib/idle_test/mock_tk.py b/Lib/idlelib/idle_test/mock_tk.py new file mode 100644 --- /dev/null +++ b/Lib/idlelib/idle_test/mock_tk.py @@ -0,0 +1,63 @@ +"""Classes that replace tkinter gui objects used by an object being tested. +A gui object is anything with a master or parent paramenter, which is typically +required in spite of what the doc strings say. +""" + +class Var: + "Use for String/Int/BooleanVar: incomplete" + def __init__(self, master=None, value=None, name=None): + self.master = master + self.value = value + self.name = name + def set(self, value): + self.value = value + def get(self): + return self.value + +class Mbox_func: + """Generic mock for messagebox functions. All have same call signature. + Mbox instantiates once for each function. Tester uses attributes. + """ + def __init__(self): + self.result = None # The return for all show funcs + def __call__(self, title, message, *args, **kwds): + # Save all args for possible examination by tester + self.title = title + self.message = message + self.args = args + self.kwds = kwds + return self.result # Set by tester for ask functions + +class Mbox: + """Mock for tkinter.messagebox with an Mbox_func for each function. + This module was 'tkMessageBox' in 2.x; hence the 'import as' in 3.x. + Example usage in test_module.py for testing functios in module.py: + --- +from idlelib.idle_test.mock_tk import Mbox +import module + +orig_mbox = module.tkMessageBox +showerror = Mbox.showerror # example, for attribute access in test methods + +class Test(unittest.TestCase): + + @classmethod + def setUpClass(cls): + module.tkMessageBox = Mbox + + @classmethod + def tearDownClass(cls): + module.tkMessageBox = orig_mbox + --- + When tkMessageBox functions are the only gui making calls in a method, + this replacement makes the method gui-free and unit-testable. + For 'ask' functions, set func.result return before calling method. + """ + askokcancel = Mbox_func() # True or False + askquestion = Mbox_func() # 'yes' or 'no' + askretrycancel = Mbox_func() # True or False + askyesno = Mbox_func() # True or False + askyesnocancel = Mbox_func() # True, False, or None + showerror = Mbox_func() # None + showinfo = Mbox_func() # None + showwarning = Mbox_func() # None diff --git a/Lib/idlelib/idle_test/test_config_name.py b/Lib/idlelib/idle_test/test_config_name.py new file mode 100644 --- /dev/null +++ b/Lib/idlelib/idle_test/test_config_name.py @@ -0,0 +1,75 @@ +"""Unit tests for idlelib.configSectionNameDialog""" +import unittest +from idlelib.idle_test.mock_tk import Var, Mbox +from idlelib import configSectionNameDialog as name_dialog_module + +name_dialog = name_dialog_module.GetCfgSectionNameDialog + +class Dummy_name_dialog: + # Mock for testing the following methods of name_dialog + name_ok = name_dialog.name_ok + Ok = name_dialog.Ok + Cancel = name_dialog.Cancel + # Attributes, constant or variable, needed for tests + used_names = ['used'] + name = Var() + result = None + destroyed = False + def destroy(self): + self.destroyed = True + +# name_ok calls Mbox.showerror if name is not ok +orig_mbox = name_dialog_module.tkMessageBox +showerror = Mbox.showerror + +class TestConfigName(unittest.TestCase): + dialog = Dummy_name_dialog() + + @classmethod + def setUpClass(cls): + name_dialog_module.tkMessageBox = Mbox + + @classmethod + def tearDownClass(cls): + name_dialog_module.tkMessageBox = orig_mbox + + def test_blank_name(self): + self.dialog.name.set(' ') + self.assertEqual(self.dialog.name_ok(), '') + self.assertEqual(showerror.title, 'Name Error') + self.assertIn('No', showerror.message) + + def test_used_name(self): + self.dialog.name.set('used') + self.assertEqual(self.dialog.name_ok(), '') + self.assertEqual(showerror.title, 'Name Error') + self.assertIn('use', showerror.message) + + def test_long_name(self): + self.dialog.name.set('good'*8) + self.assertEqual(self.dialog.name_ok(), '') + self.assertEqual(showerror.title, 'Name Error') + self.assertIn('too long', showerror.message) + + def test_good_name(self): + self.dialog.name.set(' good ') + showerror.title = 'No Error' # should not be called + self.assertEqual(self.dialog.name_ok(), 'good') + self.assertEqual(showerror.title, 'No Error') + + def test_ok(self): + self.dialog.destroyed = False + self.dialog.name.set('good') + self.dialog.Ok() + self.assertEqual(self.dialog.result, 'good') + self.assertTrue(self.dialog.destroyed) + + def test_cancel(self): + self.dialog.destroyed = False + self.dialog.Cancel() + self.assertEqual(self.dialog.result, '') + self.assertTrue(self.dialog.destroyed) + + +if __name__ == '__main__': + unittest.main(verbosity=2, exit=False) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 5 20:41:14 2013 From: python-checkins at python.org (terry.reedy) Date: Wed, 5 Jun 2013 20:41:14 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUxODEzMDog?= =?utf-8?q?Test_class_idlelib=2EconfigSectionNameDialog=2EGetCfgSectionNam?= =?utf-8?q?eDialog=2E?= Message-ID: <3bQf3G2gllz7LlW@mail.python.org> http://hg.python.org/cpython/rev/31a67a0194ec changeset: 84042:31a67a0194ec branch: 2.7 parent: 84015:e9d0fb934b46 user: Terry Jan Reedy date: Wed Jun 05 14:24:42 2013 -0400 summary: Issue18130: Test class idlelib.configSectionNameDialog.GetCfgSectionNameDialog. Fix bug in existing human test and add instructions; fix two bugs in tested code; remove redundancies, add spaces, and change two internal method names. Add mock_tk with mocks for tkinter.Variable subclasses and tkinter.messagebox. Use mocks in test_config_name to unittest methods that are otherwise gui-free. files: Lib/idlelib/configSectionNameDialog.py | 125 +++++---- Lib/idlelib/idle_test/mock_tk.py | 63 +++++ Lib/idlelib/idle_test/test_config_name.py | 75 ++++++ 3 files changed, 202 insertions(+), 61 deletions(-) diff --git a/Lib/idlelib/configSectionNameDialog.py b/Lib/idlelib/configSectionNameDialog.py --- a/Lib/idlelib/configSectionNameDialog.py +++ b/Lib/idlelib/configSectionNameDialog.py @@ -1,97 +1,100 @@ """ Dialog that allows user to specify a new config file section name. Used to get new highlight theme and keybinding set names. +The 'return value' for the dialog, used two placed in configDialog.py, +is the .result attribute set in the Ok and Cancel methods. """ from Tkinter import * import tkMessageBox - class GetCfgSectionNameDialog(Toplevel): - def __init__(self,parent,title,message,usedNames): + def __init__(self, parent, title, message, used_names): """ message - string, informational message to display - usedNames - list, list of names already in use for validity check + used_names - string collection, names already in use for validity check """ Toplevel.__init__(self, parent) self.configure(borderwidth=5) - self.resizable(height=FALSE,width=FALSE) + self.resizable(height=FALSE, width=FALSE) self.title(title) self.transient(parent) self.grab_set() self.protocol("WM_DELETE_WINDOW", self.Cancel) self.parent = parent - self.message=message - self.usedNames=usedNames - self.result='' - self.CreateWidgets() - self.withdraw() #hide while setting geometry + self.message = message + self.used_names = used_names + self.create_widgets() + self.withdraw() #hide while setting geometry self.update_idletasks() #needs to be done here so that the winfo_reqwidth is valid self.messageInfo.config(width=self.frameMain.winfo_reqwidth()) - self.geometry("+%d+%d" % - ((parent.winfo_rootx()+((parent.winfo_width()/2) - -(self.winfo_reqwidth()/2)), - parent.winfo_rooty()+((parent.winfo_height()/2) - -(self.winfo_reqheight()/2)) )) ) #centre dialog over parent - self.deiconify() #geometry set, unhide + self.geometry( + "+%d+%d" % ( + parent.winfo_rootx() + + (parent.winfo_width()/2 - self.winfo_reqwidth()/2), + parent.winfo_rooty() + + (parent.winfo_height()/2 - self.winfo_reqheight()/2) + ) ) #centre dialog over parent + self.deiconify() #geometry set, unhide self.wait_window() + def create_widgets(self): + self.name = StringVar(self.parent) + self.fontSize = StringVar(self.parent) + self.frameMain = Frame(self, borderwidth=2, relief=SUNKEN) + self.frameMain.pack(side=TOP, expand=TRUE, fill=BOTH) + self.messageInfo = Message(self.frameMain, anchor=W, justify=LEFT, + padx=5, pady=5, text=self.message) #,aspect=200) + entryName = Entry(self.frameMain, textvariable=self.name, width=30) + entryName.focus_set() + self.messageInfo.pack(padx=5, pady=5) #, expand=TRUE, fill=BOTH) + entryName.pack(padx=5, pady=5) + frameButtons = Frame(self, pady=2) + frameButtons.pack(side=BOTTOM) + self.buttonOk = Button(frameButtons, text='Ok', + width=8, command=self.Ok) + self.buttonOk.pack(side=LEFT, padx=5) + self.buttonCancel = Button(frameButtons, text='Cancel', + width=8, command=self.Cancel) + self.buttonCancel.pack(side=RIGHT, padx=5) - def CreateWidgets(self): - self.name=StringVar(self) - self.fontSize=StringVar(self) - self.frameMain = Frame(self,borderwidth=2,relief=SUNKEN) - self.frameMain.pack(side=TOP,expand=TRUE,fill=BOTH) - self.messageInfo=Message(self.frameMain,anchor=W,justify=LEFT,padx=5,pady=5, - text=self.message)#,aspect=200) - entryName=Entry(self.frameMain,textvariable=self.name,width=30) - entryName.focus_set() - self.messageInfo.pack(padx=5,pady=5)#,expand=TRUE,fill=BOTH) - entryName.pack(padx=5,pady=5) - frameButtons=Frame(self) - frameButtons.pack(side=BOTTOM,fill=X) - self.buttonOk = Button(frameButtons,text='Ok', - width=8,command=self.Ok) - self.buttonOk.grid(row=0,column=0,padx=5,pady=5) - self.buttonCancel = Button(frameButtons,text='Cancel', - width=8,command=self.Cancel) - self.buttonCancel.grid(row=0,column=1,padx=5,pady=5) - - def NameOk(self): - #simple validity check for a sensible - #ConfigParser file section name - nameOk=1 - name=self.name.get() - name.strip() + def name_ok(self): + ''' After stripping entered name, check that it is a sensible + ConfigParser file section name. Return it if it is, '' if not. + ''' + name = self.name.get().strip() if not name: #no name specified tkMessageBox.showerror(title='Name Error', message='No name specified.', parent=self) - nameOk=0 elif len(name)>30: #name too long tkMessageBox.showerror(title='Name Error', message='Name too long. It should be no more than '+ '30 characters.', parent=self) - nameOk=0 - elif name in self.usedNames: + name = '' + elif name in self.used_names: tkMessageBox.showerror(title='Name Error', message='This name is already in use.', parent=self) - nameOk=0 - return nameOk + name = '' + return name + def Ok(self, event=None): + name = self.name_ok() + if name: + self.result = name + self.destroy() + def Cancel(self, event=None): + self.result = '' + self.destroy() +if __name__ == '__main__': + import unittest + unittest.main('idlelib.idle_test.test_config_name', verbosity=2, exit=False) - def Ok(self, event=None): - if self.NameOk(): - self.result=self.name.get().strip() - self.destroy() - - def Cancel(self, event=None): - self.result='' - self.destroy() - -if __name__ == '__main__': - #test the dialog - root=Tk() + # also human test the dialog + root = Tk() def run(): - keySeq='' dlg=GetCfgSectionNameDialog(root,'Get Name', - 'The information here should need to be word wrapped. Test.') + "After the text entered with [Ok] is stripped, , " + "'abc', or more that 30 chars are errors. " + "Close with a valid entry (printed), [Cancel], or [X]", + {'abc'}) print dlg.result - Button(root,text='Dialog',command=run).pack() + Message(root, text='').pack() # will be needed for oher dialog tests + Button(root, text='Click to begin dialog test', command=run).pack() root.mainloop() diff --git a/Lib/idlelib/idle_test/mock_tk.py b/Lib/idlelib/idle_test/mock_tk.py new file mode 100644 --- /dev/null +++ b/Lib/idlelib/idle_test/mock_tk.py @@ -0,0 +1,63 @@ +"""Classes that replace tkinter gui objects used by an object being tested. +A gui object is anything with a master or parent paramenter, which is typically +required in spite of what the doc strings say. +""" + +class Var(object): + "Use for String/Int/BooleanVar: incomplete" + def __init__(self, master=None, value=None, name=None): + self.master = master + self.value = value + self.name = name + def set(self, value): + self.value = value + def get(self): + return self.value + +class Mbox_func(object): + """Generic mock for messagebox functions. All have same call signature. + Mbox instantiates once for each function. Tester uses attributes. + """ + def __init__(self): + self.result = None # The return for all show funcs + def __call__(self, title, message, *args, **kwds): + # Save all args for possible examination by tester + self.title = title + self.message = message + self.args = args + self.kwds = kwds + return self.result # Set by tester for ask functions + +class Mbox(object): + """Mock for tkinter.messagebox with an Mbox_func for each function. + This module was 'tkMessageBox' in 2.x; hence the 'import as' in 3.x. + Example usage in test_module.py for testing functios in module.py: + --- +from idlelib.idle_test.mock_tk import Mbox +import module + +orig_mbox = module.tkMessageBox +showerror = Mbox.showerror # example, for attribute access in test methods + +class Test(unittest.TestCase): + + @classmethod + def setUpClass(cls): + module.tkMessageBox = Mbox + + @classmethod + def tearDownClass(cls): + module.tkMessageBox = orig_mbox + --- + When tkMessageBox functions are the only gui making calls in a method, + this replacement makes the method gui-free and unit-testable. + For 'ask' functions, set func.result return before calling method. + """ + askokcancel = Mbox_func() # True or False + askquestion = Mbox_func() # 'yes' or 'no' + askretrycancel = Mbox_func() # True or False + askyesno = Mbox_func() # True or False + askyesnocancel = Mbox_func() # True, False, or None + showerror = Mbox_func() # None + showinfo = Mbox_func() # None + showwarning = Mbox_func() # None diff --git a/Lib/idlelib/idle_test/test_config_name.py b/Lib/idlelib/idle_test/test_config_name.py new file mode 100644 --- /dev/null +++ b/Lib/idlelib/idle_test/test_config_name.py @@ -0,0 +1,75 @@ +"""Unit tests for idlelib.configSectionNameDialog""" +import unittest +from idlelib.idle_test.mock_tk import Var, Mbox +from idlelib import configSectionNameDialog as name_dialog_module + +name_dialog = name_dialog_module.GetCfgSectionNameDialog + +class Dummy_name_dialog(object): + # Mock for testing the following methods of name_dialog + name_ok = name_dialog.name_ok.im_func + Ok = name_dialog.Ok.im_func + Cancel = name_dialog.Cancel.im_func + # Attributes, constant or variable, needed for tests + used_names = ['used'] + name = Var() + result = None + destroyed = False + def destroy(self): + self.destroyed = True + +# name_ok calls Mbox.showerror if name is not ok +orig_mbox = name_dialog_module.tkMessageBox +showerror = Mbox.showerror + +class TestConfigName(unittest.TestCase): + dialog = Dummy_name_dialog() + + @classmethod + def setUpClass(cls): + name_dialog_module.tkMessageBox = Mbox + + @classmethod + def tearDownClass(cls): + name_dialog_module.tkMessageBox = orig_mbox + + def test_blank_name(self): + self.dialog.name.set(' ') + self.assertEqual(self.dialog.name_ok(), '') + self.assertEqual(showerror.title, 'Name Error') + self.assertIn('No', showerror.message) + + def test_used_name(self): + self.dialog.name.set('used') + self.assertEqual(self.dialog.name_ok(), '') + self.assertEqual(showerror.title, 'Name Error') + self.assertIn('use', showerror.message) + + def test_long_name(self): + self.dialog.name.set('good'*8) + self.assertEqual(self.dialog.name_ok(), '') + self.assertEqual(showerror.title, 'Name Error') + self.assertIn('too long', showerror.message) + + def test_good_name(self): + self.dialog.name.set(' good ') + showerror.title = 'No Error' # should not be called + self.assertEqual(self.dialog.name_ok(), 'good') + self.assertEqual(showerror.title, 'No Error') + + def test_ok(self): + self.dialog.destroyed = False + self.dialog.name.set('good') + self.dialog.Ok() + self.assertEqual(self.dialog.result, 'good') + self.assertTrue(self.dialog.destroyed) + + def test_cancel(self): + self.dialog.destroyed = False + self.dialog.Cancel() + self.assertEqual(self.dialog.result, '') + self.assertTrue(self.dialog.destroyed) + + +if __name__ == '__main__': + unittest.main(verbosity=2, exit=False) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 5 20:41:15 2013 From: python-checkins at python.org (terry.reedy) Date: Wed, 5 Jun 2013 20:41:15 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogSXNzdWUgMTgxMzA6?= =?utf-8?q?_delete_extra_spaces?= Message-ID: <3bQf3H4fFYz7Ln0@mail.python.org> http://hg.python.org/cpython/rev/382f4718e765 changeset: 84043:382f4718e765 branch: 3.3 parent: 84040:db4ecaf852e3 user: Terry Jan Reedy date: Wed Jun 05 14:36:33 2013 -0400 summary: Issue 18130: delete extra spaces files: Lib/idlelib/configSectionNameDialog.py | 2 +- Lib/idlelib/idle_test/test_config_name.py | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Lib/idlelib/configSectionNameDialog.py b/Lib/idlelib/configSectionNameDialog.py --- a/Lib/idlelib/configSectionNameDialog.py +++ b/Lib/idlelib/configSectionNameDialog.py @@ -91,7 +91,7 @@ if __name__ == '__main__': import unittest unittest.main('idlelib.idle_test.test_config_name', verbosity=2, exit=False) - + # also human test the dialog root = Tk() def run(): diff --git a/Lib/idlelib/idle_test/test_config_name.py b/Lib/idlelib/idle_test/test_config_name.py --- a/Lib/idlelib/idle_test/test_config_name.py +++ b/Lib/idlelib/idle_test/test_config_name.py @@ -24,7 +24,7 @@ class TestConfigName(unittest.TestCase): dialog = Dummy_name_dialog() - + @classmethod def setUpClass(cls): name_dialog_module.tkMessageBox = Mbox @@ -38,13 +38,13 @@ self.assertEqual(self.dialog.name_ok(), '') self.assertEqual(showerror.title, 'Name Error') self.assertIn('No', showerror.message) - + def test_used_name(self): self.dialog.name.set('used') self.assertEqual(self.dialog.name_ok(), '') self.assertEqual(showerror.title, 'Name Error') self.assertIn('use', showerror.message) - + def test_long_name(self): self.dialog.name.set('good'*8) self.assertEqual(self.dialog.name_ok(), '') @@ -56,7 +56,7 @@ showerror.title = 'No Error' # should not be called self.assertEqual(self.dialog.name_ok(), 'good') self.assertEqual(showerror.title, 'No Error') - + def test_ok(self): self.dialog.destroyed = False self.dialog.name.set('good') @@ -69,7 +69,7 @@ self.dialog.Cancel() self.assertEqual(self.dialog.result, '') self.assertTrue(self.dialog.destroyed) - + if __name__ == '__main__': unittest.main(verbosity=2, exit=False) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 5 20:41:16 2013 From: python-checkins at python.org (terry.reedy) Date: Wed, 5 Jun 2013 20:41:16 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_Merge_with_3=2E3?= Message-ID: <3bQf3J6VL2z7Lkn@mail.python.org> http://hg.python.org/cpython/rev/9c06a9a8bba1 changeset: 84044:9c06a9a8bba1 parent: 84041:367377d800a5 parent: 84043:382f4718e765 user: Terry Jan Reedy date: Wed Jun 05 14:36:50 2013 -0400 summary: Merge with 3.3 files: Lib/idlelib/configSectionNameDialog.py | 2 +- Lib/idlelib/idle_test/test_config_name.py | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Lib/idlelib/configSectionNameDialog.py b/Lib/idlelib/configSectionNameDialog.py --- a/Lib/idlelib/configSectionNameDialog.py +++ b/Lib/idlelib/configSectionNameDialog.py @@ -91,7 +91,7 @@ if __name__ == '__main__': import unittest unittest.main('idlelib.idle_test.test_config_name', verbosity=2, exit=False) - + # also human test the dialog root = Tk() def run(): diff --git a/Lib/idlelib/idle_test/test_config_name.py b/Lib/idlelib/idle_test/test_config_name.py --- a/Lib/idlelib/idle_test/test_config_name.py +++ b/Lib/idlelib/idle_test/test_config_name.py @@ -24,7 +24,7 @@ class TestConfigName(unittest.TestCase): dialog = Dummy_name_dialog() - + @classmethod def setUpClass(cls): name_dialog_module.tkMessageBox = Mbox @@ -38,13 +38,13 @@ self.assertEqual(self.dialog.name_ok(), '') self.assertEqual(showerror.title, 'Name Error') self.assertIn('No', showerror.message) - + def test_used_name(self): self.dialog.name.set('used') self.assertEqual(self.dialog.name_ok(), '') self.assertEqual(showerror.title, 'Name Error') self.assertIn('use', showerror.message) - + def test_long_name(self): self.dialog.name.set('good'*8) self.assertEqual(self.dialog.name_ok(), '') @@ -56,7 +56,7 @@ showerror.title = 'No Error' # should not be called self.assertEqual(self.dialog.name_ok(), 'good') self.assertEqual(showerror.title, 'No Error') - + def test_ok(self): self.dialog.destroyed = False self.dialog.name.set('good') @@ -69,7 +69,7 @@ self.dialog.Cancel() self.assertEqual(self.dialog.result, '') self.assertTrue(self.dialog.destroyed) - + if __name__ == '__main__': unittest.main(verbosity=2, exit=False) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 5 21:03:34 2013 From: python-checkins at python.org (benjamin.peterson) Date: Wed, 5 Jun 2013 21:03:34 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?peps=3A_obscure_the_email_address_in_?= =?utf-8?q?bdfl-delegate=2C_too?= Message-ID: <3bQfY24CDZz7LnR@mail.python.org> http://hg.python.org/peps/rev/51170b413ebc changeset: 4922:51170b413ebc user: Benjamin Peterson date: Wed Jun 05 12:03:27 2013 -0700 summary: obscure the email address in bdfl-delegate, too files: pep2html.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/pep2html.py b/pep2html.py --- a/pep2html.py +++ b/pep2html.py @@ -202,7 +202,7 @@ print >> outfile, '' print >> outfile, '
\n' for k, v in header: - if k.lower() in ('author', 'discussions-to'): + if k.lower() in ('author', 'bdfl-delegate', 'discussions-to'): mailtos = [] for part in re.split(',\s*', v): if '@' in part: -- Repository URL: http://hg.python.org/peps From python-checkins at python.org Thu Jun 6 00:31:41 2013 From: python-checkins at python.org (richard.oudkerk) Date: Thu, 6 Jun 2013 00:31:41 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2317931=3A_Resolve_?= =?utf-8?q?confusion_on_Windows_between_pids_and_process_handles=2E?= Message-ID: <3bQl996bPdz7LkX@mail.python.org> http://hg.python.org/cpython/rev/0410bf251e10 changeset: 84045:0410bf251e10 user: Richard Oudkerk date: Wed Jun 05 23:29:30 2013 +0100 summary: Issue #17931: Resolve confusion on Windows between pids and process handles. files: Include/longobject.h | 13 +++++++++++++ Misc/NEWS | 5 ++--- Modules/posixmodule.c | 25 +++++++++---------------- PC/msvcrtmodule.c | 5 +++-- PC/pyconfig.h | 4 ++-- 5 files changed, 29 insertions(+), 23 deletions(-) diff --git a/Include/longobject.h b/Include/longobject.h --- a/Include/longobject.h +++ b/Include/longobject.h @@ -52,6 +52,19 @@ #error "sizeof(pid_t) is neither sizeof(int), sizeof(long) or sizeof(long long)" #endif /* SIZEOF_PID_T */ +#if SIZEOF_VOID_P == SIZEOF_INT +# define _Py_PARSE_INTPTR "i" +# define _Py_PARSE_UINTPTR "I" +#elif SIZEOF_VOID_P == SIZEOF_LONG +# define _Py_PARSE_INTPTR "l" +# define _Py_PARSE_UINTPTR "k" +#elif defined(SIZEOF_LONG_LONG) && SIZEOF_VOID_P == SIZEOF_LONG_LONG +# define _Py_PARSE_INTPTR "L" +# define _Py_PARSE_UINTPTR "K" +#else +# error "void* different in size from int, long and long long" +#endif /* SIZEOF_VOID_P */ + /* Used by Python/mystrtoul.c. */ #ifndef Py_LIMITED_API PyAPI_DATA(unsigned char) _PyLong_DigitValue[256]; diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,9 +10,8 @@ Core and Builtins ----------------- -- Issue #17931: Fix PyLong_FromPid() on Windows 64-bit: processes are - identified by their HANDLE which is a pointer (and not a long, which is - smaller). +- Issue #17931: Resolve confusion on Windows between pids and process + handles. - Tweak the exception message when the magic number or size value in a bytecode file is truncated. diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -5014,11 +5014,7 @@ if (spawnval == -1) return posix_error(); else -#if SIZEOF_LONG == SIZEOF_VOID_P - return Py_BuildValue("l", (long) spawnval); -#else - return Py_BuildValue("L", (PY_LONG_LONG) spawnval); -#endif + return Py_BuildValue(_Py_PARSE_INTPTR, spawnval); } @@ -5104,11 +5100,7 @@ if (spawnval == -1) (void) posix_error(); else -#if SIZEOF_LONG == SIZEOF_VOID_P - res = Py_BuildValue("l", (long) spawnval); -#else - res = Py_BuildValue("L", (PY_LONG_LONG) spawnval); -#endif + res = Py_BuildValue(_Py_PARSE_INTPTR, spawnval); while (--envc >= 0) PyMem_DEL(envlist[envc]); @@ -6178,16 +6170,17 @@ win32_kill(PyObject *self, PyObject *args) { PyObject *result; - DWORD pid, sig, err; + pid_t pid; + DWORD sig, err; HANDLE handle; - if (!PyArg_ParseTuple(args, "kk:kill", &pid, &sig)) + if (!PyArg_ParseTuple(args, _Py_PARSE_PID "k:kill", &pid, &sig)) return NULL; /* Console processes which share a common console can be sent CTRL+C or CTRL+BREAK events, provided they handle said events. */ if (sig == CTRL_C_EVENT || sig == CTRL_BREAK_EVENT) { - if (GenerateConsoleCtrlEvent(sig, pid) == 0) { + if (GenerateConsoleCtrlEvent(sig, (DWORD)pid) == 0) { err = GetLastError(); PyErr_SetFromWindowsErr(err); } @@ -6197,7 +6190,7 @@ /* If the signal is outside of what GenerateConsoleCtrlEvent can use, attempt to open and terminate the process. */ - handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); + handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, (DWORD)pid); if (handle == NULL) { err = GetLastError(); return PyErr_SetFromWindowsErr(err); @@ -6603,7 +6596,7 @@ Py_intptr_t pid; int status, options; - if (!PyArg_ParseTuple(args, _Py_PARSE_PID "i:waitpid", &pid, &options)) + if (!PyArg_ParseTuple(args, _Py_PARSE_INTPTR "i:waitpid", &pid, &options)) return NULL; Py_BEGIN_ALLOW_THREADS pid = _cwait(&status, pid, options); @@ -6612,7 +6605,7 @@ return posix_error(); /* shift the status left a byte so this is more like the POSIX waitpid */ - return Py_BuildValue("Ni", PyLong_FromPid(pid), status << 8); + return Py_BuildValue(_Py_PARSE_INTPTR "i", pid, status << 8); } #endif /* HAVE_WAITPID || HAVE_CWAIT */ diff --git a/PC/msvcrtmodule.c b/PC/msvcrtmodule.c --- a/PC/msvcrtmodule.c +++ b/PC/msvcrtmodule.c @@ -113,11 +113,12 @@ static PyObject * msvcrt_open_osfhandle(PyObject *self, PyObject *args) { - long handle; + Py_intptr_t handle; int flags; int fd; - if (!PyArg_ParseTuple(args, "li:open_osfhandle", &handle, &flags)) + if (!PyArg_ParseTuple(args, _Py_PARSE_INTPTR "i:open_osfhandle", + &handle, &flags)) return NULL; fd = _open_osfhandle(handle, flags); diff --git a/PC/pyconfig.h b/PC/pyconfig.h --- a/PC/pyconfig.h +++ b/PC/pyconfig.h @@ -723,8 +723,8 @@ /* The size of `wchar_t', as computed by sizeof. */ #define SIZEOF_WCHAR_T 2 -/* The size of `pid_t' (HANDLE). */ -#define SIZEOF_PID_T SIZEOF_VOID_P +/* The size of `pid_t', as computed by sizeof. */ +#define SIZEOF_PID_T SIZEOF_INT /* Define if you have the dl library (-ldl). */ /* #undef HAVE_LIBDL */ -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 6 00:37:55 2013 From: python-checkins at python.org (brett.cannon) Date: Thu, 6 Jun 2013 00:37:55 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_tweak_exception_message_?= =?utf-8?q?=28again=29?= Message-ID: <3bQlJM5B3szSYH@mail.python.org> http://hg.python.org/cpython/rev/6b189f29b1e6 changeset: 84046:6b189f29b1e6 user: Brett Cannon date: Wed Jun 05 18:37:50 2013 -0400 summary: tweak exception message (again) files: Lib/importlib/_bootstrap.py | 4 +- Python/importlib.h | 4627 +++++++++++----------- 2 files changed, 2316 insertions(+), 2315 deletions(-) diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py --- a/Lib/importlib/_bootstrap.py +++ b/Lib/importlib/_bootstrap.py @@ -668,11 +668,11 @@ _verbose_message(message) raise ImportError(message, **exc_details) elif len(raw_timestamp) != 4: - message = 'reached EOF while reading magic number in {!r}'.format(name) + message = 'reached EOF while reading timestamp in {!r}'.format(name) _verbose_message(message) raise EOFError(message) elif len(raw_size) != 4: - message = 'reached EOF while reading size in {!r}'.format(name) + message = 'reached EOF while reading size of source in {!r}'.format(name) _verbose_message(message) raise EOFError(message) if source_stats is not None: diff --git a/Python/importlib.h b/Python/importlib.h --- a/Python/importlib.h +++ b/Python/importlib.h [stripped] -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Thu Jun 6 05:51:35 2013 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Thu, 06 Jun 2013 05:51:35 +0200 Subject: [Python-checkins] Daily reference leaks (6b189f29b1e6): sum=2779 Message-ID: results for 6b189f29b1e6 on branch "default" -------------------------------------------- test_capi leaked [778, 778, 778] references, sum=2334 test_capi leaked [147, 149, 149] memory blocks, sum=445 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogQ0mxId', '-x'] From solipsis at pitrou.net Fri Jun 7 05:52:37 2013 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Fri, 07 Jun 2013 05:52:37 +0200 Subject: [Python-checkins] Daily reference leaks (6b189f29b1e6): sum=2779 Message-ID: results for 6b189f29b1e6 on branch "default" -------------------------------------------- test_capi leaked [778, 778, 778] references, sum=2334 test_capi leaked [147, 149, 149] memory blocks, sum=445 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogRg7d2Y', '-x'] From python-checkins at python.org Fri Jun 7 16:21:55 2013 From: python-checkins at python.org (vinay.sajip) Date: Fri, 7 Jun 2013 16:21:55 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Closes_=2311959=3A_SMTPSer?= =?utf-8?q?ver_and_SMTPChannel_now_take_an_optional_map=2C_use_of?= Message-ID: <3bRmC761nzzShX@mail.python.org> http://hg.python.org/cpython/rev/ed498f477549 changeset: 84047:ed498f477549 user: Vinay Sajip date: Fri Jun 07 15:21:41 2013 +0100 summary: Closes #11959: SMTPServer and SMTPChannel now take an optional map, use of which avoids affecting global state. files: Doc/library/smtpd.rst | 19 +++++++- Lib/smtpd.py | 12 +++-- Lib/test/test_logging.py | 64 +-------------------------- Misc/NEWS | 3 + 4 files changed, 30 insertions(+), 68 deletions(-) diff --git a/Doc/library/smtpd.rst b/Doc/library/smtpd.rst --- a/Doc/library/smtpd.rst +++ b/Doc/library/smtpd.rst @@ -27,7 +27,8 @@ ------------------ -.. class:: SMTPServer(localaddr, remoteaddr, data_size_limit=33554432) +.. class:: SMTPServer(localaddr, remoteaddr, data_size_limit=33554432, + map=None) Create a new :class:`SMTPServer` object, which binds to local address *localaddr*. It will treat *remoteaddr* as an upstream SMTP relayer. It @@ -38,6 +39,8 @@ accepted in a ``DATA`` command. A value of ``None`` or ``0`` means no limit. + A dictionary can be specified in *map* to avoid using a global socket map. + .. method:: process_message(peer, mailfrom, rcpttos, data) Raise :exc:`NotImplementedError` exception. Override this in subclasses to @@ -53,6 +56,9 @@ Override this in subclasses to use a custom :class:`SMTPChannel` for managing SMTP clients. + .. versionchanged:: 3.4 + The *map* argument was added. + DebuggingServer Objects ----------------------- @@ -90,11 +96,20 @@ SMTPChannel Objects ------------------- -.. class:: SMTPChannel(server, conn, addr) +.. class:: SMTPChannel(server, conn, addr, data_size_limit=33554432, + map=None)) Create a new :class:`SMTPChannel` object which manages the communication between the server and a single SMTP client. + *conn* and *addr* are as per the instance variables described below. + + *data_size_limit* specifies the maximum number of bytes that will be + accepted in a ``DATA`` command. A value of ``None`` or ``0`` means no + limit. + + A dictionary can be specified in *map* to avoid using a global socket map. + To use a custom SMTPChannel implementation you need to override the :attr:`SMTPServer.channel_class` of your :class:`SMTPServer`. diff --git a/Lib/smtpd.py b/Lib/smtpd.py --- a/Lib/smtpd.py +++ b/Lib/smtpd.py @@ -121,8 +121,9 @@ }) max_command_size_limit = max(command_size_limits.values()) - def __init__(self, server, conn, addr, data_size_limit=DATA_SIZE_DEFAULT): - asynchat.async_chat.__init__(self, conn) + def __init__(self, server, conn, addr, data_size_limit=DATA_SIZE_DEFAULT, + map=None): + asynchat.async_chat.__init__(self, conn, map=map) self.smtp_server = server self.conn = conn self.addr = addr @@ -576,11 +577,11 @@ channel_class = SMTPChannel def __init__(self, localaddr, remoteaddr, - data_size_limit=DATA_SIZE_DEFAULT): + data_size_limit=DATA_SIZE_DEFAULT, map=None): self._localaddr = localaddr self._remoteaddr = remoteaddr self.data_size_limit = data_size_limit - asyncore.dispatcher.__init__(self) + asyncore.dispatcher.__init__(self, map=map) try: self.create_socket(socket.AF_INET, socket.SOCK_STREAM) # try to re-use a server port if possible @@ -597,7 +598,8 @@ def handle_accepted(self, conn, addr): print('Incoming connection from %s' % repr(addr), file=DEBUGSTREAM) - channel = self.channel_class(self, conn, addr, self.data_size_limit) + channel = self.channel_class(self, conn, addr, self.data_size_limit, + self._map) # API for "doing something useful with the message" def process_message(self, peer, mailfrom, rcpttos, data): diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -659,41 +659,6 @@ # -- if it proves to be of wider utility than just test_logging if threading: - class TestSMTPChannel(smtpd.SMTPChannel): - """ - This derived class has had to be created because smtpd does not - support use of custom channel maps, although they are allowed by - asyncore's design. Issue #11959 has been raised to address this, - and if resolved satisfactorily, some of this code can be removed. - """ - def __init__(self, server, conn, addr, sockmap): - asynchat.async_chat.__init__(self, conn, sockmap) - self.smtp_server = server - self.conn = conn - self.addr = addr - self.data_size_limit = None - self.received_lines = [] - self.smtp_state = self.COMMAND - self.seen_greeting = '' - self.mailfrom = None - self.rcpttos = [] - self.received_data = '' - self.fqdn = socket.getfqdn() - self.num_bytes = 0 - try: - self.peer = conn.getpeername() - except OSError as err: - # a race condition may occur if the other end is closing - # before we can get the peername - self.close() - if err.args[0] != errno.ENOTCONN: - raise - return - self.push('220 %s %s' % (self.fqdn, smtpd.__version__)) - self.set_terminator(b'\r\n') - self.extended_smtp = False - - class TestSMTPServer(smtpd.SMTPServer): """ This class implements a test SMTP server. @@ -714,37 +679,14 @@ :func:`asyncore.loop`. This avoids changing the :mod:`asyncore` module's global state. """ - channel_class = TestSMTPChannel def __init__(self, addr, handler, poll_interval, sockmap): - self._localaddr = addr - self._remoteaddr = None - self.data_size_limit = None - self.sockmap = sockmap - asyncore.dispatcher.__init__(self, map=sockmap) - try: - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - sock.setblocking(0) - self.set_socket(sock, map=sockmap) - # try to re-use a server port if possible - self.set_reuse_addr() - self.bind(addr) - self.port = sock.getsockname()[1] - self.listen(5) - except: - self.close() - raise + smtpd.SMTPServer.__init__(self, addr, None, map=sockmap) + self.port = self.socket.getsockname()[1] self._handler = handler self._thread = None self.poll_interval = poll_interval - def handle_accepted(self, conn, addr): - """ - Redefined only because the base class does not pass in a - map, forcing use of a global in :mod:`asyncore`. - """ - channel = self.channel_class(self, conn, addr, self.sockmap) - def process_message(self, peer, mailfrom, rcpttos, data): """ Delegates to the handler passed in to the server's constructor. @@ -775,7 +717,7 @@ :func:`asyncore.loop`. """ try: - asyncore.loop(poll_interval, map=self.sockmap) + asyncore.loop(poll_interval, map=self._map) except OSError: # On FreeBSD 8, closing the server repeatably # raises this error. We swallow it if the diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -112,6 +112,9 @@ Library ------- +- Issue #11959: SMTPServer and SMTPChannel now take an optional map, use of + which avoids affecting global state. + - Issue #18109: os.uname() now decodes fields from the locale encoding, and socket.gethostname() now decodes the hostname from the locale encoding, instead of using the UTF-8 encoding in strict mode. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 7 16:37:50 2013 From: python-checkins at python.org (vinay.sajip) Date: Fri, 7 Jun 2013 16:37:50 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2317903=3A_Added_pa?= =?utf-8?q?th_search_changes_to_launcher=2E?= Message-ID: <3bRmYV4jHGzS7w@mail.python.org> http://hg.python.org/cpython/rev/a174d79cef2e changeset: 84048:a174d79cef2e user: Vinay Sajip date: Fri Jun 07 15:37:28 2013 +0100 summary: Issue #17903: Added path search changes to launcher. files: PC/launcher.c | 56 ++++++++++++++++++++++++++++---------- 1 files changed, 41 insertions(+), 15 deletions(-) diff --git a/PC/launcher.c b/PC/launcher.c --- a/PC/launcher.c +++ b/PC/launcher.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2012 Vinay Sajip. + * Copyright (C) 2011-2013 Vinay Sajip. * Licensed to PSF under a contributor agreement. * * Based on the work of: @@ -18,7 +18,7 @@ /* Build options. */ #define SKIP_PREFIX -/* #define SEARCH_PATH */ +#define SEARCH_PATH /* Just for now - static definition */ @@ -595,12 +595,17 @@ } } -static wchar_t * builtin_virtual_paths [] = { - L"/usr/bin/env python", - L"/usr/bin/python", - L"/usr/local/bin/python", - L"python", - NULL +typedef struct { + wchar_t *shebang; + BOOL search; +} SHEBANG; + +static SHEBANG builtin_virtual_paths [] = { + { L"/usr/bin/env python", TRUE }, + { L"/usr/bin/python", FALSE }, + { L"/usr/local/bin/python", FALSE }, + { L"python", FALSE }, + { NULL, FALSE }, }; /* For now, a static array of commands. */ @@ -776,10 +781,10 @@ static BOOL parse_shebang(wchar_t * shebang_line, int nchars, wchar_t ** command, - wchar_t ** suffix) + wchar_t ** suffix, BOOL *search) { BOOL rc = FALSE; - wchar_t ** vpp; + SHEBANG * vpp; size_t plen; wchar_t * p; wchar_t zapped; @@ -789,15 +794,17 @@ *command = NULL; /* failure return */ *suffix = NULL; + *search = FALSE; if ((*shebang_line++ == L'#') && (*shebang_line++ == L'!')) { shebang_line = skip_whitespace(shebang_line); if (*shebang_line) { *command = shebang_line; - for (vpp = builtin_virtual_paths; *vpp; ++vpp) { - plen = wcslen(*vpp); - if (wcsncmp(shebang_line, *vpp, plen) == 0) { + for (vpp = builtin_virtual_paths; vpp->shebang; ++vpp) { + plen = wcslen(vpp->shebang); + if (wcsncmp(shebang_line, vpp->shebang, plen) == 0) { rc = TRUE; + *search = vpp->search; /* We can do this because all builtin commands contain * "python". */ @@ -805,7 +812,7 @@ break; } } - if (*vpp == NULL) { + if (vpp->shebang == NULL) { /* * Not found in builtins - look in customised commands. * @@ -1012,8 +1019,10 @@ int i, j, nchars = 0; int header_len; BOOL is_virt; + BOOL search; wchar_t * command; wchar_t * suffix; + COMMAND *cmd = NULL; INSTALLED_PYTHON * ip; if (rc == 0) { @@ -1125,7 +1134,7 @@ if (nchars > 0) { shebang_line[--nchars] = L'\0'; is_virt = parse_shebang(shebang_line, nchars, &command, - &suffix); + &suffix, &search); if (command != NULL) { debug(L"parse_shebang: found command: %s\n", command); if (!is_virt) { @@ -1141,6 +1150,23 @@ error(RC_BAD_VIRTUAL_PATH, L"Unknown virtual \ path '%s'", command); command += 6; /* skip past "python" */ + if (search && ((*command == L'\0') || isspace(*command))) { + /* Command is eligible for path search, and there + * is no version specification. + */ + debug(L"searching PATH for python executable\n"); + cmd = find_on_path(L"python"); + debug(L"Python on path: %s\n", cmd ? cmd->value : L""); + if (cmd) { + debug(L"located python on PATH: %s\n", cmd->value); + invoke_child(cmd->value, suffix, cmdline); + /* Exit here, as we have found the command */ + return; + } + /* FALL THROUGH: No python found on PATH, so fall + * back to locating the correct installed python. + */ + } if (*command && !validate_version(command)) error(RC_BAD_VIRTUAL_PATH, L"Invalid version \ specification: '%s'.\nIn the first line of the script, 'python' needs to be \ -- Repository URL: http://hg.python.org/cpython From brett at python.org Fri Jun 7 17:16:05 2013 From: brett at python.org (Brett Cannon) Date: Fri, 7 Jun 2013 11:16:05 -0400 Subject: [Python-checkins] cpython: Issue #17931: Resolve confusion on Windows between pids and process handles. In-Reply-To: <3bQl996bPdz7LkX@mail.python.org> References: <3bQl996bPdz7LkX@mail.python.org> Message-ID: I think this CL introduced a memory leak. The daily leak report went from 0 to not 0 between June 4 and June 5 and this is the only CL that touched C code. On Wed, Jun 5, 2013 at 6:31 PM, richard.oudkerk wrote: > http://hg.python.org/cpython/rev/0410bf251e10 > changeset: 84045:0410bf251e10 > user: Richard Oudkerk > date: Wed Jun 05 23:29:30 2013 +0100 > summary: > Issue #17931: Resolve confusion on Windows between pids and process > handles. > > files: > Include/longobject.h | 13 +++++++++++++ > Misc/NEWS | 5 ++--- > Modules/posixmodule.c | 25 +++++++++---------------- > PC/msvcrtmodule.c | 5 +++-- > PC/pyconfig.h | 4 ++-- > 5 files changed, 29 insertions(+), 23 deletions(-) > > > diff --git a/Include/longobject.h b/Include/longobject.h > --- a/Include/longobject.h > +++ b/Include/longobject.h > @@ -52,6 +52,19 @@ > #error "sizeof(pid_t) is neither sizeof(int), sizeof(long) or sizeof(long > long)" > #endif /* SIZEOF_PID_T */ > > +#if SIZEOF_VOID_P == SIZEOF_INT > +# define _Py_PARSE_INTPTR "i" > +# define _Py_PARSE_UINTPTR "I" > +#elif SIZEOF_VOID_P == SIZEOF_LONG > +# define _Py_PARSE_INTPTR "l" > +# define _Py_PARSE_UINTPTR "k" > +#elif defined(SIZEOF_LONG_LONG) && SIZEOF_VOID_P == SIZEOF_LONG_LONG > +# define _Py_PARSE_INTPTR "L" > +# define _Py_PARSE_UINTPTR "K" > +#else > +# error "void* different in size from int, long and long long" > +#endif /* SIZEOF_VOID_P */ > + > /* Used by Python/mystrtoul.c. */ > #ifndef Py_LIMITED_API > PyAPI_DATA(unsigned char) _PyLong_DigitValue[256]; > diff --git a/Misc/NEWS b/Misc/NEWS > --- a/Misc/NEWS > +++ b/Misc/NEWS > @@ -10,9 +10,8 @@ > Core and Builtins > ----------------- > > -- Issue #17931: Fix PyLong_FromPid() on Windows 64-bit: processes are > - identified by their HANDLE which is a pointer (and not a long, which is > - smaller). > +- Issue #17931: Resolve confusion on Windows between pids and process > + handles. > > - Tweak the exception message when the magic number or size value in a > bytecode > file is truncated. > diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c > --- a/Modules/posixmodule.c > +++ b/Modules/posixmodule.c > @@ -5014,11 +5014,7 @@ > if (spawnval == -1) > return posix_error(); > else > -#if SIZEOF_LONG == SIZEOF_VOID_P > - return Py_BuildValue("l", (long) spawnval); > -#else > - return Py_BuildValue("L", (PY_LONG_LONG) spawnval); > -#endif > + return Py_BuildValue(_Py_PARSE_INTPTR, spawnval); > } > > > @@ -5104,11 +5100,7 @@ > if (spawnval == -1) > (void) posix_error(); > else > -#if SIZEOF_LONG == SIZEOF_VOID_P > - res = Py_BuildValue("l", (long) spawnval); > -#else > - res = Py_BuildValue("L", (PY_LONG_LONG) spawnval); > -#endif > + res = Py_BuildValue(_Py_PARSE_INTPTR, spawnval); > > while (--envc >= 0) > PyMem_DEL(envlist[envc]); > @@ -6178,16 +6170,17 @@ > win32_kill(PyObject *self, PyObject *args) > { > PyObject *result; > - DWORD pid, sig, err; > + pid_t pid; > + DWORD sig, err; > HANDLE handle; > > - if (!PyArg_ParseTuple(args, "kk:kill", &pid, &sig)) > + if (!PyArg_ParseTuple(args, _Py_PARSE_PID "k:kill", &pid, &sig)) > return NULL; > > /* Console processes which share a common console can be sent CTRL+C > or > CTRL+BREAK events, provided they handle said events. */ > if (sig == CTRL_C_EVENT || sig == CTRL_BREAK_EVENT) { > - if (GenerateConsoleCtrlEvent(sig, pid) == 0) { > + if (GenerateConsoleCtrlEvent(sig, (DWORD)pid) == 0) { > err = GetLastError(); > PyErr_SetFromWindowsErr(err); > } > @@ -6197,7 +6190,7 @@ > > /* If the signal is outside of what GenerateConsoleCtrlEvent can use, > attempt to open and terminate the process. */ > - handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); > + handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, (DWORD)pid); > if (handle == NULL) { > err = GetLastError(); > return PyErr_SetFromWindowsErr(err); > @@ -6603,7 +6596,7 @@ > Py_intptr_t pid; > int status, options; > > - if (!PyArg_ParseTuple(args, _Py_PARSE_PID "i:waitpid", &pid, > &options)) > + if (!PyArg_ParseTuple(args, _Py_PARSE_INTPTR "i:waitpid", &pid, > &options)) > return NULL; > Py_BEGIN_ALLOW_THREADS > pid = _cwait(&status, pid, options); > @@ -6612,7 +6605,7 @@ > return posix_error(); > > /* shift the status left a byte so this is more like the POSIX > waitpid */ > - return Py_BuildValue("Ni", PyLong_FromPid(pid), status << 8); > + return Py_BuildValue(_Py_PARSE_INTPTR "i", pid, status << 8); > } > #endif /* HAVE_WAITPID || HAVE_CWAIT */ > > diff --git a/PC/msvcrtmodule.c b/PC/msvcrtmodule.c > --- a/PC/msvcrtmodule.c > +++ b/PC/msvcrtmodule.c > @@ -113,11 +113,12 @@ > static PyObject * > msvcrt_open_osfhandle(PyObject *self, PyObject *args) > { > - long handle; > + Py_intptr_t handle; > int flags; > int fd; > > - if (!PyArg_ParseTuple(args, "li:open_osfhandle", &handle, &flags)) > + if (!PyArg_ParseTuple(args, _Py_PARSE_INTPTR "i:open_osfhandle", > + &handle, &flags)) > return NULL; > > fd = _open_osfhandle(handle, flags); > diff --git a/PC/pyconfig.h b/PC/pyconfig.h > --- a/PC/pyconfig.h > +++ b/PC/pyconfig.h > @@ -723,8 +723,8 @@ > /* The size of `wchar_t', as computed by sizeof. */ > #define SIZEOF_WCHAR_T 2 > > -/* The size of `pid_t' (HANDLE). */ > -#define SIZEOF_PID_T SIZEOF_VOID_P > +/* The size of `pid_t', as computed by sizeof. */ > +#define SIZEOF_PID_T SIZEOF_INT > > /* Define if you have the dl library (-ldl). */ > /* #undef HAVE_LIBDL */ > > -- > 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 python-checkins at python.org Fri Jun 7 17:45:50 2013 From: python-checkins at python.org (brett.cannon) Date: Fri, 7 Jun 2013 17:45:50 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2317314=3A_Stop_usi?= =?utf-8?q?ng_imp_in_multiprocessing=2Eforking_and_move_over?= Message-ID: <3bRp3y70yJzS7w@mail.python.org> http://hg.python.org/cpython/rev/97adaa820353 changeset: 84049:97adaa820353 user: Brett Cannon date: Fri Jun 07 11:45:41 2013 -0400 summary: Issue #17314: Stop using imp in multiprocessing.forking and move over to importlib. files: Lib/multiprocessing/forking.py | 22 ++++++++++++---------- Misc/NEWS | 2 ++ 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/Lib/multiprocessing/forking.py b/Lib/multiprocessing/forking.py --- a/Lib/multiprocessing/forking.py +++ b/Lib/multiprocessing/forking.py @@ -450,6 +450,7 @@ # Main modules not actually called __main__.py may # contain additional code that should still be executed import imp + import importlib if main_path is None: dirs = None @@ -460,16 +461,17 @@ assert main_name not in sys.modules, main_name sys.modules.pop('__mp_main__', None) - file, path_name, etc = imp.find_module(main_name, dirs) + # We should not try to load __main__ + # since that would execute 'if __name__ == "__main__"' + # clauses, potentially causing a psuedo fork bomb. + loader = importlib.find_loader(main_name, path=dirs) + main_module = imp.new_module(main_name) try: - # We should not do 'imp.load_module("__main__", ...)' - # since that would execute 'if __name__ == "__main__"' - # clauses, potentially causing a psuedo fork bomb. - main_module = imp.load_module( - '__mp_main__', file, path_name, etc - ) - finally: - if file: - file.close() + loader.init_module_attrs(main_module) + except AttributeError: # init_module_attrs is optional + pass + main_module.__name__ = '__mp_main__' + code = loader.get_code(main_name) + exec(code, main_module.__dict__) sys.modules['__main__'] = sys.modules['__mp_main__'] = main_module diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -112,6 +112,8 @@ Library ------- +- Issue #17314: Move multiprocessing.forking over to importlib. + - Issue #11959: SMTPServer and SMTPChannel now take an optional map, use of which avoids affecting global state. -- Repository URL: http://hg.python.org/cpython From thomas at python.org Fri Jun 7 17:54:48 2013 From: thomas at python.org (Thomas Wouters) Date: Fri, 7 Jun 2013 17:54:48 +0200 Subject: [Python-checkins] cpython: Issue #17931: Resolve confusion on Windows between pids and process handles. In-Reply-To: References: <3bQl996bPdz7LkX@mail.python.org> Message-ID: On Fri, Jun 7, 2013 at 5:16 PM, Brett Cannon wrote: > I think this CL introduced a memory leak. The daily leak report went from > 0 to not 0 between June 4 and June 5 and this is the only CL that touched C > code. > It wasn't introduced by C code :) The refleak report is induced by the PEP 443 implementation, see my message to python-dev. -- Thomas Wouters Hi! I'm an email virus! Think twice before sending your email to help me spread! -------------- next part -------------- An HTML attachment was scrubbed... URL: From python-checkins at python.org Fri Jun 7 19:18:46 2013 From: python-checkins at python.org (brett.cannon) Date: Fri, 7 Jun 2013 19:18:46 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogSXNzdWUgIzE4MDU1?= =?utf-8?q?=3A_Move_to_importlib_from_imp_for_IDLE=2E?= Message-ID: <3bRr7B6Jd7zRFF@mail.python.org> http://hg.python.org/cpython/rev/a0d8ae880ae6 changeset: 84050:a0d8ae880ae6 branch: 3.3 parent: 84043:382f4718e765 user: Brett Cannon date: Fri Jun 07 13:17:48 2013 -0400 summary: Issue #18055: Move to importlib from imp for IDLE. files: Lib/idlelib/EditorWindow.py | 57 ++++++++---------------- Misc/NEWS | 2 + 2 files changed, 21 insertions(+), 38 deletions(-) diff --git a/Lib/idlelib/EditorWindow.py b/Lib/idlelib/EditorWindow.py --- a/Lib/idlelib/EditorWindow.py +++ b/Lib/idlelib/EditorWindow.py @@ -1,5 +1,5 @@ -import imp import importlib +import importlib.abc import os import re import string @@ -35,34 +35,6 @@ release += '%s%s' % (level[0], serial) return release -def _find_module(fullname, path=None): - """Version of imp.find_module() that handles hierarchical module names""" - - file = None - for tgt in fullname.split('.'): - if file is not None: - file.close() # close intermediate files - (file, filename, descr) = imp.find_module(tgt, path) - if descr[2] == imp.PY_SOURCE: - break # find but not load the source file - module = imp.load_module(tgt, file, filename, descr) - try: - path = module.__path__ - except AttributeError: - raise ImportError('No source for module ' + module.__name__) - if descr[2] != imp.PY_SOURCE: - # If all of the above fails and didn't raise an exception,fallback - # to a straight import which can find __init__.py in a package. - m = __import__(fullname) - try: - filename = m.__file__ - except AttributeError: - pass - else: - file = None - descr = os.path.splitext(filename)[1], None, imp.PY_SOURCE - return file, filename, descr - class HelpDialog(object): @@ -687,20 +659,29 @@ return # XXX Ought to insert current file's directory in front of path try: - (f, file, (suffix, mode, type)) = _find_module(name) - except (NameError, ImportError) as msg: + loader = importlib.find_loader(name) + except (ValueError, ImportError) as msg: tkMessageBox.showerror("Import error", str(msg), parent=self.text) return - if type != imp.PY_SOURCE: - tkMessageBox.showerror("Unsupported type", - "%s is not a source module" % name, parent=self.text) + if loader is None: + tkMessageBox.showerror("Import error", "module not found", + parent=self.text) return - if f: - f.close() + if not isinstance(loader, importlib.abc.SourceLoader): + tkMessageBox.showerror("Import error", "not a source-based module", + parent=self.text) + return + try: + file_path = loader.get_filename(name) + except AttributeError: + tkMessageBox.showerror("Import error", + "loader does not support get_filename", + parent=self.text) + return if self.flist: - self.flist.open(file) + self.flist.open(file_path) else: - self.io.loadfile(file) + self.io.loadfile(file_path) def open_class_browser(self, event=None): filename = self.io.filename diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -71,6 +71,8 @@ IDLE ---- +- Issue #18055: Move IDLE off of imp and on to importlib. + - Issue #15392: Create a unittest framework for IDLE. Initial patch by Rajagopalasarma Jayakrishnan. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 7 19:18:48 2013 From: python-checkins at python.org (brett.cannon) Date: Fri, 7 Jun 2013 19:18:48 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_merge_w/_3=2E3_for_issue_=2318055?= Message-ID: <3bRr7D15clzRMv@mail.python.org> http://hg.python.org/cpython/rev/3a3ec484ce95 changeset: 84051:3a3ec484ce95 parent: 84049:97adaa820353 parent: 84050:a0d8ae880ae6 user: Brett Cannon date: Fri Jun 07 13:18:36 2013 -0400 summary: merge w/ 3.3 for issue #18055 files: Lib/idlelib/EditorWindow.py | 57 ++++++++---------------- Misc/NEWS | 2 + 2 files changed, 21 insertions(+), 38 deletions(-) diff --git a/Lib/idlelib/EditorWindow.py b/Lib/idlelib/EditorWindow.py --- a/Lib/idlelib/EditorWindow.py +++ b/Lib/idlelib/EditorWindow.py @@ -1,5 +1,5 @@ -import imp import importlib +import importlib.abc import os import re import string @@ -35,34 +35,6 @@ release += '%s%s' % (level[0], serial) return release -def _find_module(fullname, path=None): - """Version of imp.find_module() that handles hierarchical module names""" - - file = None - for tgt in fullname.split('.'): - if file is not None: - file.close() # close intermediate files - (file, filename, descr) = imp.find_module(tgt, path) - if descr[2] == imp.PY_SOURCE: - break # find but not load the source file - module = imp.load_module(tgt, file, filename, descr) - try: - path = module.__path__ - except AttributeError: - raise ImportError('No source for module ' + module.__name__) - if descr[2] != imp.PY_SOURCE: - # If all of the above fails and didn't raise an exception,fallback - # to a straight import which can find __init__.py in a package. - m = __import__(fullname) - try: - filename = m.__file__ - except AttributeError: - pass - else: - file = None - descr = os.path.splitext(filename)[1], None, imp.PY_SOURCE - return file, filename, descr - class HelpDialog(object): @@ -687,20 +659,29 @@ return # XXX Ought to insert current file's directory in front of path try: - (f, file, (suffix, mode, type)) = _find_module(name) - except (NameError, ImportError) as msg: + loader = importlib.find_loader(name) + except (ValueError, ImportError) as msg: tkMessageBox.showerror("Import error", str(msg), parent=self.text) return - if type != imp.PY_SOURCE: - tkMessageBox.showerror("Unsupported type", - "%s is not a source module" % name, parent=self.text) + if loader is None: + tkMessageBox.showerror("Import error", "module not found", + parent=self.text) return - if f: - f.close() + if not isinstance(loader, importlib.abc.SourceLoader): + tkMessageBox.showerror("Import error", "not a source-based module", + parent=self.text) + return + try: + file_path = loader.get_filename(name) + except AttributeError: + tkMessageBox.showerror("Import error", + "loader does not support get_filename", + parent=self.text) + return if self.flist: - self.flist.open(file) + self.flist.open(file_path) else: - self.io.loadfile(file) + self.io.loadfile(file_path) def open_class_browser(self, event=None): filename = self.io.filename diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -435,6 +435,8 @@ IDLE ---- +- Issue #18055: Move IDLE off of imp and on to importlib. + - Issue #15392: Create a unittest framework for IDLE. Initial patch by Rajagopalasarma Jayakrishnan. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 7 19:27:02 2013 From: python-checkins at python.org (brett.cannon) Date: Fri, 7 Jun 2013 19:27:02 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=237732=3A_Move_an_i?= =?utf-8?q?mp=2Efind=5Fmodule_test_from_test=5Fimport_to?= Message-ID: <3bRrJk2WF3zRMv@mail.python.org> http://hg.python.org/cpython/rev/bf882390713c changeset: 84052:bf882390713c user: Brett Cannon date: Fri Jun 07 13:26:53 2013 -0400 summary: Issue #7732: Move an imp.find_module test from test_import to test_imp. files: Lib/test/test_imp.py | 11 +++++++++++ Lib/test/test_import.py | 10 ---------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/Lib/test/test_imp.py b/Lib/test/test_imp.py --- a/Lib/test/test_imp.py +++ b/Lib/test/test_imp.py @@ -273,6 +273,17 @@ return imp.load_module(name, None, *found[1:]) + @unittest.skipIf(sys.dont_write_bytecode, + "test meaningful only when writing bytecode") + def test_bug7732(self): + source = support.TESTFN + '.py' + os.mkdir(source) + try: + self.assertRaisesRegex(ImportError, '^No module', + imp.find_module, support.TESTFN, ["."]) + finally: + os.rmdir(source) + class ReloadTests(unittest.TestCase): diff --git a/Lib/test/test_import.py b/Lib/test/test_import.py --- a/Lib/test/test_import.py +++ b/Lib/test/test_import.py @@ -127,16 +127,6 @@ finally: del sys.path[0] - @skip_if_dont_write_bytecode - def test_bug7732(self): - source = TESTFN + '.py' - os.mkdir(source) - try: - self.assertRaisesRegex(ImportError, '^No module', - imp.find_module, TESTFN, ["."]) - finally: - os.rmdir(source) - def test_module_with_large_stack(self, module='longlist'): # Regression test for http://bugs.python.org/issue561858. filename = module + '.py' -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 7 22:26:31 2013 From: python-checkins at python.org (lukasz.langa) Date: Fri, 7 Jun 2013 22:26:31 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Fixed_=2318150=3A_duplicat?= =?utf-8?q?e_test_inside_TestSingleDispatch?= Message-ID: <3bRwHq4TpJzSFM@mail.python.org> http://hg.python.org/cpython/rev/a16bebe653b1 changeset: 84053:a16bebe653b1 user: ?ukasz Langa date: Fri Jun 07 22:25:27 2013 +0200 summary: Fixed #18150: duplicate test inside TestSingleDispatch Thanks to Vajrasky Kok for the patch files: Lib/test/test_functools.py | 33 +++++++++++-------------- Misc/ACKS | 1 + 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py --- a/Lib/test/test_functools.py +++ b/Lib/test/test_functools.py @@ -868,29 +868,24 @@ @functools.singledispatch def g(obj): return "base" - class C: + class A: pass - class D(C): + class C(A): pass - def g_C(c): - return "C" - g.register(C, g_C) - self.assertEqual(g(C()), "C") - self.assertEqual(g(D()), "C") - - def test_classic_classes(self): - @functools.singledispatch - def g(obj): - return "base" - class C: + class B(A): pass - class D(C): + class D(C, B): pass - def g_C(c): - return "C" - g.register(C, g_C) - self.assertEqual(g(C()), "C") - self.assertEqual(g(D()), "C") + def g_A(a): + return "A" + def g_B(b): + return "B" + g.register(A, g_A) + g.register(B, g_B) + self.assertEqual(g(A()), "A") + self.assertEqual(g(B()), "B") + self.assertEqual(g(C()), "A") + self.assertEqual(g(D()), "B") def test_register_decorator(self): @functools.singledispatch diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -666,6 +666,7 @@ Greg Kochanski Damon Kohler Marko Kohtala +Vajrasky Kok Guido Kollerie Jacek Konieczny ???? ????????? -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 7 22:54:38 2013 From: python-checkins at python.org (lukasz.langa) Date: Fri, 7 Jun 2013 22:54:38 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_moved_the_single-dispatch_?= =?utf-8?q?generic_function_definitions_to_the_glossary?= Message-ID: <3bRwwG035Xz7LkB@mail.python.org> http://hg.python.org/cpython/rev/42519153cb08 changeset: 84054:42519153cb08 user: ?ukasz Langa date: Fri Jun 07 22:54:03 2013 +0200 summary: moved the single-dispatch generic function definitions to the glossary files: Doc/glossary.rst | 13 +++++++++++++ Doc/library/functools.rst | 8 ++------ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/Doc/glossary.rst b/Doc/glossary.rst --- a/Doc/glossary.rst +++ b/Doc/glossary.rst @@ -302,6 +302,15 @@ >>> sum(i*i for i in range(10)) # sum of squares 0, 1, 4, ... 81 285 + generic function + A function composed of multiple functions implementing the same operation + for different types. Which implementation should be used during a call is + determined by the dispatch algorithm. + + See also the :term:`single dispatch` glossary entry, the + :func:`functools.singledispatch` decorator, and :pep:`443`. + + GIL See :term:`global interpreter lock`. @@ -745,6 +754,10 @@ mapping rather than a sequence because the lookups use arbitrary :term:`immutable` keys rather than integers. + single dispatch + A form of :term:`generic function` dispatch where the implementation is + chosen based on the type of a single argument. + slice An object usually containing a portion of a :term:`sequence`. A slice is created using the subscript notation, ``[]`` with colons between numbers diff --git a/Doc/library/functools.rst b/Doc/library/functools.rst --- a/Doc/library/functools.rst +++ b/Doc/library/functools.rst @@ -189,12 +189,8 @@ .. decorator:: singledispatch(default) - Transforms a function into a single-dispatch generic function. A **generic - function** is composed of multiple functions implementing the same operation - for different types. Which implementation should be used during a call is - determined by the dispatch algorithm. When the implementation is chosen - based on the type of a single argument, this is known as **single - dispatch**. + Transforms a function into a :term:`single-dispatch ` :term:`generic function`. To define a generic function, decorate it with the ``@singledispatch`` decorator. Note that the dispatch happens on the type of the first argument, -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Sat Jun 8 05:48:23 2013 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Sat, 08 Jun 2013 05:48:23 +0200 Subject: [Python-checkins] Daily reference leaks (42519153cb08): sum=2779 Message-ID: results for 42519153cb08 on branch "default" -------------------------------------------- test_capi leaked [778, 778, 778] references, sum=2334 test_capi leaked [147, 149, 149] memory blocks, sum=445 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogOzVZi0', '-x'] From python-checkins at python.org Sat Jun 8 06:38:11 2013 From: python-checkins at python.org (terry.reedy) Date: Sat, 8 Jun 2013 06:38:11 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogIzE4MTUxLCBwYXJ0?= =?utf-8?q?_1=3A_Backport_idlelilb_portion_of_Andrew_Svetlov=27s_3=2E4_pat?= =?utf-8?q?ch?= Message-ID: <3bS7C70vDszR2m@mail.python.org> http://hg.python.org/cpython/rev/2fe64ce5da05 changeset: 84055:2fe64ce5da05 branch: 3.3 parent: 84050:a0d8ae880ae6 user: Terry Jan Reedy date: Sat Jun 08 00:22:45 2013 -0400 summary: #18151, part 1: Backport idlelilb portion of Andrew Svetlov's 3.4 patch changing IOError to OSError (#16715). files: Lib/idlelib/EditorWindow.py | 2 +- Lib/idlelib/GrepDialog.py | 2 +- Lib/idlelib/IOBinding.py | 4 ++-- Lib/idlelib/OutputWindow.py | 2 +- Lib/idlelib/PyShell.py | 8 ++++---- Lib/idlelib/configHandler.py | 18 +++++++----------- Lib/idlelib/rpc.py | 2 +- Lib/idlelib/textView.py | 2 +- 8 files changed, 18 insertions(+), 22 deletions(-) diff --git a/Lib/idlelib/EditorWindow.py b/Lib/idlelib/EditorWindow.py --- a/Lib/idlelib/EditorWindow.py +++ b/Lib/idlelib/EditorWindow.py @@ -901,7 +901,7 @@ with open(self.recent_files_path, 'w', encoding='utf_8', errors='replace') as rf_file: rf_file.writelines(rf_list) - except IOError as err: + except OSError as err: if not getattr(self.root, "recentfilelist_error_displayed", False): self.root.recentfilelist_error_displayed = True tkMessageBox.showerror(title='IDLE Error', diff --git a/Lib/idlelib/GrepDialog.py b/Lib/idlelib/GrepDialog.py --- a/Lib/idlelib/GrepDialog.py +++ b/Lib/idlelib/GrepDialog.py @@ -82,7 +82,7 @@ for fn in list: try: f = open(fn, errors='replace') - except IOError as msg: + except OSError as msg: print(msg) continue lineno = 0 diff --git a/Lib/idlelib/IOBinding.py b/Lib/idlelib/IOBinding.py --- a/Lib/idlelib/IOBinding.py +++ b/Lib/idlelib/IOBinding.py @@ -213,7 +213,7 @@ f.seek(0) bytes = f.read() f.close() - except IOError as msg: + except OSError as msg: tkMessageBox.showerror("I/O Error", str(msg), master=self.text) return False chars, converted = self._decode(two_lines, bytes) @@ -378,7 +378,7 @@ f.flush() f.close() return True - except IOError as msg: + except OSError as msg: tkMessageBox.showerror("I/O Error", str(msg), master=self.text) return False diff --git a/Lib/idlelib/OutputWindow.py b/Lib/idlelib/OutputWindow.py --- a/Lib/idlelib/OutputWindow.py +++ b/Lib/idlelib/OutputWindow.py @@ -106,7 +106,7 @@ f = open(filename, "r") f.close() break - except IOError: + except OSError: continue else: return None diff --git a/Lib/idlelib/PyShell.py b/Lib/idlelib/PyShell.py --- a/Lib/idlelib/PyShell.py +++ b/Lib/idlelib/PyShell.py @@ -59,7 +59,7 @@ try: file.write(warnings.formatwarning(message, category, filename, lineno, line=line)) - except IOError: + except OSError: pass ## file (probably __stderr__) is invalid, warning dropped. warnings.showwarning = idle_showwarning def idle_formatwarning(message, category, filename, lineno, line=None): @@ -213,7 +213,7 @@ try: with open(self.breakpointPath, "r") as fp: lines = fp.readlines() - except IOError: + except OSError: lines = [] try: with open(self.breakpointPath, "w") as new_file: @@ -224,7 +224,7 @@ breaks = self.breakpoints if breaks: new_file.write(filename + '=' + str(breaks) + '\n') - except IOError as err: + except OSError as err: if not getattr(self.root, "breakpoint_error_displayed", False): self.root.breakpoint_error_displayed = True tkMessageBox.showerror(title='IDLE Error', @@ -532,7 +532,7 @@ return try: response = clt.pollresponse(self.active_seq, wait=0.05) - except (EOFError, IOError, KeyboardInterrupt): + except (EOFError, OSError, KeyboardInterrupt): # lost connection or subprocess terminated itself, restart # [the KBI is from rpc.SocketIO.handle_EOF()] if self.tkconsole.closing: diff --git a/Lib/idlelib/configHandler.py b/Lib/idlelib/configHandler.py --- a/Lib/idlelib/configHandler.py +++ b/Lib/idlelib/configHandler.py @@ -142,7 +142,7 @@ fname = self.file try: cfgFile = open(fname, 'w') - except IOError: + except OSError: os.unlink(fname) cfgFile = open(fname, 'w') with cfgFile: @@ -207,7 +207,7 @@ userDir+',\n but the path does not exist.\n') try: sys.stderr.write(warn) - except IOError: + except OSError: pass userDir = '~' if userDir == "~": # still no path to home! @@ -217,7 +217,7 @@ if not os.path.exists(userDir): try: os.mkdir(userDir) - except (OSError, IOError): + except OSError: warn = ('\n Warning: unable to create user config directory\n'+ userDir+'\n Check path and permissions.\n Exiting!\n\n') sys.stderr.write(warn) @@ -251,7 +251,7 @@ raw=raw))) try: sys.stderr.write(warning) - except IOError: + except OSError: pass try: if self.defaultCfg[configType].has_option(section,option): @@ -268,13 +268,11 @@ (option, section, default)) try: sys.stderr.write(warning) - except IOError: + except OSError: pass return default - def SetOption(self, configType, section, option, value): """In user's config file, set section's option to value. - """ self.userCfg[configType].SetOption(section, option, value) @@ -380,7 +378,7 @@ (element, themeName, theme[element])) try: sys.stderr.write(warning) - except IOError: + except OSError: pass colour=cfgParser.Get(themeName,element,default=theme[element]) theme[element]=colour @@ -637,13 +635,11 @@ (event, keySetName, keyBindings[event])) try: sys.stderr.write(warning) - except IOError: + except OSError: pass return keyBindings - def GetExtraHelpSourceList(self,configSet): """Fetch list of extra help sources from a given configSet. - Valid configSets are 'user' or 'default'. Return a list of tuples of the form (menu_item , path_to_help_file , option), or return the empty list. 'option' is the sequence number of the help resource. 'option' diff --git a/Lib/idlelib/rpc.py b/Lib/idlelib/rpc.py --- a/Lib/idlelib/rpc.py +++ b/Lib/idlelib/rpc.py @@ -339,7 +339,7 @@ r, w, x = select.select([], [self.sock], []) n = self.sock.send(s[:BUFSIZE]) except (AttributeError, TypeError): - raise IOError("socket no longer exists") + raise OSError("socket no longer exists") except socket.error: raise else: diff --git a/Lib/idlelib/textView.py b/Lib/idlelib/textView.py --- a/Lib/idlelib/textView.py +++ b/Lib/idlelib/textView.py @@ -66,7 +66,7 @@ try: with open(filename, 'r', encoding=encoding) as file: contents = file.read() - except IOError: + except OSError: import tkinter.messagebox as tkMessageBox tkMessageBox.showerror(title='File Load Error', message='Unable to load file %r .' % filename, -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 8 06:38:12 2013 From: python-checkins at python.org (terry.reedy) Date: Sat, 8 Jun 2013 06:38:12 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_=2318151_null_merge_with_3=2E3=2E?= Message-ID: <3bS7C82qkjzRKx@mail.python.org> http://hg.python.org/cpython/rev/0be613638523 changeset: 84056:0be613638523 parent: 84054:42519153cb08 parent: 84055:2fe64ce5da05 user: Terry Jan Reedy date: Sat Jun 08 00:35:51 2013 -0400 summary: #18151 null merge with 3.3. files: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 8 17:53:59 2013 From: python-checkins at python.org (richard.oudkerk) Date: Sat, 8 Jun 2013 17:53:59 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2315528=3A_Delay_im?= =?utf-8?q?porting_atexit_until_weakref=2Efinalize=28=29_used=2E?= Message-ID: <3bSQBv3nlyzNk8@mail.python.org> http://hg.python.org/cpython/rev/d6ad9d7468f7 changeset: 84057:d6ad9d7468f7 user: Richard Oudkerk date: Sat Jun 08 16:52:29 2013 +0100 summary: Issue #15528: Delay importing atexit until weakref.finalize() used. files: Lib/weakref.py | 10 +++++++--- 1 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Lib/weakref.py b/Lib/weakref.py --- a/Lib/weakref.py +++ b/Lib/weakref.py @@ -23,7 +23,6 @@ import collections # Import after _weakref to avoid circular import. import sys import itertools -import atexit ProxyTypes = (ProxyType, CallableProxyType) @@ -464,11 +463,18 @@ _shutdown = False _index_iter = itertools.count() _dirty = False + _registered_with_atexit = False class _Info: __slots__ = ("weakref", "func", "args", "kwargs", "atexit", "index") def __init__(self, obj, func, *args, **kwargs): + if not self._registered_with_atexit: + # We may register the exit function more than once because + # of a thread race, but that is harmless + import atexit + atexit.register(self._exitfunc) + finalize._registered_with_atexit = True info = self._Info() info.weakref = ref(obj, self) info.func = func @@ -569,5 +575,3 @@ finalize._shutdown = True if reenable_gc: gc.enable() - -atexit.register(finalize._exitfunc) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 8 23:10:25 2013 From: python-checkins at python.org (ezio.melotti) Date: Sat, 8 Jun 2013 23:10:25 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogIzE3NjkxOiB0ZXN0?= =?utf-8?q?=5Funivnewlines_now_works_with_unittest_test_discovery=2E__Patc?= =?utf-8?q?h_by?= Message-ID: <3bSYD14KsYz7Ljl@mail.python.org> http://hg.python.org/cpython/rev/a2e093e98d45 changeset: 84058:a2e093e98d45 branch: 3.3 parent: 84055:2fe64ce5da05 user: Ezio Melotti date: Sun Jun 09 00:07:06 2013 +0300 summary: #17691: test_univnewlines now works with unittest test discovery. Patch by Zachary Ware. files: Lib/test/test_univnewlines.py | 37 +++++++++------------- Misc/NEWS | 3 + 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/Lib/test/test_univnewlines.py b/Lib/test/test_univnewlines.py --- a/Lib/test/test_univnewlines.py +++ b/Lib/test/test_univnewlines.py @@ -30,7 +30,13 @@ DATA_MIXED = "\n".join(DATA_TEMPLATE) + "\r" DATA_SPLIT = [x + "\n" for x in DATA_TEMPLATE] -class TestGenericUnivNewlines(unittest.TestCase): +class CTest: + open = io.open + +class PyTest: + open = staticmethod(pyio.open) + +class TestGenericUnivNewlines: # use a class variable DATA to define the data to write to the file # and a class variable NEWLINE to set the expected newlines value READMODE = 'r' @@ -85,10 +91,14 @@ class TestCRNewlines(TestGenericUnivNewlines): NEWLINE = '\r' DATA = DATA_CR +class CTestCRNewlines(CTest, TestCRNewlines, unittest.TestCase): pass +class PyTestCRNewlines(PyTest, TestCRNewlines, unittest.TestCase): pass class TestLFNewlines(TestGenericUnivNewlines): NEWLINE = '\n' DATA = DATA_LF +class CTestLFNewlines(CTest, TestLFNewlines, unittest.TestCase): pass +class PyTestLFNewlines(PyTest, TestLFNewlines, unittest.TestCase): pass class TestCRLFNewlines(TestGenericUnivNewlines): NEWLINE = '\r\n' @@ -100,29 +110,14 @@ data = fp.readline() pos = fp.tell() self.assertEqual(repr(fp.newlines), repr(self.NEWLINE)) +class CTestCRLFNewlines(CTest, TestCRLFNewlines, unittest.TestCase): pass +class PyTestCRLFNewlines(PyTest, TestCRLFNewlines, unittest.TestCase): pass class TestMixedNewlines(TestGenericUnivNewlines): NEWLINE = ('\r', '\n') DATA = DATA_MIXED - - -def test_main(): - base_tests = (TestCRNewlines, - TestLFNewlines, - TestCRLFNewlines, - TestMixedNewlines) - tests = [] - # Test the C and Python implementations. - for test in base_tests: - class CTest(test): - open = io.open - CTest.__name__ = "C" + test.__name__ - class PyTest(test): - open = staticmethod(pyio.open) - PyTest.__name__ = "Py" + test.__name__ - tests.append(CTest) - tests.append(PyTest) - support.run_unittest(*tests) +class CTestMixedNewlines(CTest, TestMixedNewlines, unittest.TestCase): pass +class PyTestMixedNewlines(PyTest, TestMixedNewlines, unittest.TestCase): pass if __name__ == '__main__': - test_main() + unittest.main() diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -84,6 +84,9 @@ Tests ----- +- Issue #17691: test_univnewlines now works with unittest test discovery. + Patch by Zachary Ware. + - Issue #18094: test_uuid no more reports skipped tests as passed. - Issue #11995: test_pydoc doesn't import all sys.path modules anymore. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 8 23:10:26 2013 From: python-checkins at python.org (ezio.melotti) Date: Sat, 8 Jun 2013 23:10:26 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?b?KTogIzE3NjkxOiBtZXJnZSB3aXRoIDMuMy4=?= Message-ID: <3bSYD26Fy0z7Lkd@mail.python.org> http://hg.python.org/cpython/rev/d1b5e41acf28 changeset: 84059:d1b5e41acf28 parent: 84057:d6ad9d7468f7 parent: 84058:a2e093e98d45 user: Ezio Melotti date: Sun Jun 09 00:10:04 2013 +0300 summary: #17691: merge with 3.3. files: Lib/test/test_univnewlines.py | 37 +++++++++------------- Misc/NEWS | 3 + 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/Lib/test/test_univnewlines.py b/Lib/test/test_univnewlines.py --- a/Lib/test/test_univnewlines.py +++ b/Lib/test/test_univnewlines.py @@ -30,7 +30,13 @@ DATA_MIXED = "\n".join(DATA_TEMPLATE) + "\r" DATA_SPLIT = [x + "\n" for x in DATA_TEMPLATE] -class TestGenericUnivNewlines(unittest.TestCase): +class CTest: + open = io.open + +class PyTest: + open = staticmethod(pyio.open) + +class TestGenericUnivNewlines: # use a class variable DATA to define the data to write to the file # and a class variable NEWLINE to set the expected newlines value READMODE = 'r' @@ -85,10 +91,14 @@ class TestCRNewlines(TestGenericUnivNewlines): NEWLINE = '\r' DATA = DATA_CR +class CTestCRNewlines(CTest, TestCRNewlines, unittest.TestCase): pass +class PyTestCRNewlines(PyTest, TestCRNewlines, unittest.TestCase): pass class TestLFNewlines(TestGenericUnivNewlines): NEWLINE = '\n' DATA = DATA_LF +class CTestLFNewlines(CTest, TestLFNewlines, unittest.TestCase): pass +class PyTestLFNewlines(PyTest, TestLFNewlines, unittest.TestCase): pass class TestCRLFNewlines(TestGenericUnivNewlines): NEWLINE = '\r\n' @@ -100,29 +110,14 @@ data = fp.readline() pos = fp.tell() self.assertEqual(repr(fp.newlines), repr(self.NEWLINE)) +class CTestCRLFNewlines(CTest, TestCRLFNewlines, unittest.TestCase): pass +class PyTestCRLFNewlines(PyTest, TestCRLFNewlines, unittest.TestCase): pass class TestMixedNewlines(TestGenericUnivNewlines): NEWLINE = ('\r', '\n') DATA = DATA_MIXED - - -def test_main(): - base_tests = (TestCRNewlines, - TestLFNewlines, - TestCRLFNewlines, - TestMixedNewlines) - tests = [] - # Test the C and Python implementations. - for test in base_tests: - class CTest(test): - open = io.open - CTest.__name__ = "C" + test.__name__ - class PyTest(test): - open = staticmethod(pyio.open) - PyTest.__name__ = "Py" + test.__name__ - tests.append(CTest) - tests.append(PyTest) - support.run_unittest(*tests) +class CTestMixedNewlines(CTest, TestMixedNewlines, unittest.TestCase): pass +class PyTestMixedNewlines(PyTest, TestMixedNewlines, unittest.TestCase): pass if __name__ == '__main__': - test_main() + unittest.main() diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -379,6 +379,9 @@ - Issue #12820: add tests for the xml.dom.minicompat module. Patch by John Chandler and Phil Connell. +- Issue #17691: test_univnewlines now works with unittest test discovery. + Patch by Zachary Ware. + - Issue #17790: test_set now works with unittest test discovery. Patch by Zachary Ware. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 9 00:05:37 2013 From: python-checkins at python.org (ezio.melotti) Date: Sun, 9 Jun 2013 00:05:37 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogIzE4MTI2OiB1cGRh?= =?utf-8?q?te_NumPy_links_in_the_documentation=2E__Patch_by_Yury_V=2E_Zayt?= =?utf-8?q?sev=2E?= Message-ID: <3bSZRj5hqyz7LlJ@mail.python.org> http://hg.python.org/cpython/rev/dacd8f430e0e changeset: 84060:dacd8f430e0e branch: 2.7 parent: 84042:31a67a0194ec user: Ezio Melotti date: Sun Jun 09 01:04:21 2013 +0300 summary: #18126: update NumPy links in the documentation. Patch by Yury V. Zaytsev. files: Doc/faq/programming.rst | 2 +- Doc/library/array.rst | 6 ++---- Misc/ACKS | 1 + 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Doc/faq/programming.rst b/Doc/faq/programming.rst --- a/Doc/faq/programming.rst +++ b/Doc/faq/programming.rst @@ -1204,7 +1204,7 @@ A = [[None] * w for i in range(h)] Or, you can use an extension that provides a matrix datatype; `Numeric Python -`_ is the best known. +`_ is the best known. How do I apply a method to a sequence of objects? diff --git a/Doc/library/array.rst b/Doc/library/array.rst --- a/Doc/library/array.rst +++ b/Doc/library/array.rst @@ -268,9 +268,7 @@ Packing and unpacking of External Data Representation (XDR) data as used in some remote procedure call systems. - `The Numerical Python Manual `_ + `The Numerical Python Documentation `_ The Numeric Python extension (NumPy) defines another array type; see - http://numpy.sourceforge.net/ for further information about Numerical Python. - (A PDF version of the NumPy manual is available at - http://numpy.sourceforge.net/numdoc/numdoc.pdf). + http://www.numpy.org/ for further information about Numerical Python. diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1139,6 +1139,7 @@ Milan Zamazal Artur Zaprzala Mike Zarnstorff +Yury V. Zaytsev Siebren van der Zee Nickolai Zeldovich Uwe Zessin -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 9 00:05:39 2013 From: python-checkins at python.org (ezio.melotti) Date: Sun, 9 Jun 2013 00:05:39 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogIzE4MTI2OiB1cGRh?= =?utf-8?q?te_NumPy_links_in_the_documentation=2E__Patch_by_Yury_V=2E_Zayt?= =?utf-8?q?sev=2E?= Message-ID: <3bSZRl0RGzz7LlK@mail.python.org> http://hg.python.org/cpython/rev/aafa11c1dd61 changeset: 84061:aafa11c1dd61 branch: 3.3 parent: 84058:a2e093e98d45 user: Ezio Melotti date: Sun Jun 09 01:04:21 2013 +0300 summary: #18126: update NumPy links in the documentation. Patch by Yury V. Zaytsev. files: Doc/faq/programming.rst | 2 +- Doc/library/array.rst | 6 ++---- Misc/ACKS | 1 + 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Doc/faq/programming.rst b/Doc/faq/programming.rst --- a/Doc/faq/programming.rst +++ b/Doc/faq/programming.rst @@ -1092,7 +1092,7 @@ A = [[None] * w for i in range(h)] Or, you can use an extension that provides a matrix datatype; `Numeric Python -`_ is the best known. +`_ is the best known. How do I apply a method to a sequence of objects? diff --git a/Doc/library/array.rst b/Doc/library/array.rst --- a/Doc/library/array.rst +++ b/Doc/library/array.rst @@ -271,9 +271,7 @@ Packing and unpacking of External Data Representation (XDR) data as used in some remote procedure call systems. - `The Numerical Python Manual `_ + `The Numerical Python Documentation `_ The Numeric Python extension (NumPy) defines another array type; see - http://numpy.sourceforge.net/ for further information about Numerical Python. - (A PDF version of the NumPy manual is available at - http://numpy.sourceforge.net/numdoc/numdoc.pdf). + http://www.numpy.org/ for further information about Numerical Python. diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1361,6 +1361,7 @@ Milan Zamazal Artur Zaprzala Mike Zarnstorff +Yury V. Zaytsev Siebren van der Zee Nickolai Zeldovich Yuxiao Zeng -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 9 00:05:40 2013 From: python-checkins at python.org (ezio.melotti) Date: Sun, 9 Jun 2013 00:05:40 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?b?KTogIzE4MTI2OiBtZXJnZSB3aXRoIDMuMy4=?= Message-ID: <3bSZRm2XsZz7Lkc@mail.python.org> http://hg.python.org/cpython/rev/1f0b6462ea3c changeset: 84062:1f0b6462ea3c parent: 84059:d1b5e41acf28 parent: 84061:aafa11c1dd61 user: Ezio Melotti date: Sun Jun 09 01:05:16 2013 +0300 summary: #18126: merge with 3.3. files: Doc/faq/programming.rst | 2 +- Doc/library/array.rst | 6 ++---- Misc/ACKS | 1 + 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Doc/faq/programming.rst b/Doc/faq/programming.rst --- a/Doc/faq/programming.rst +++ b/Doc/faq/programming.rst @@ -1092,7 +1092,7 @@ A = [[None] * w for i in range(h)] Or, you can use an extension that provides a matrix datatype; `Numeric Python -`_ is the best known. +`_ is the best known. How do I apply a method to a sequence of objects? diff --git a/Doc/library/array.rst b/Doc/library/array.rst --- a/Doc/library/array.rst +++ b/Doc/library/array.rst @@ -271,9 +271,7 @@ Packing and unpacking of External Data Representation (XDR) data as used in some remote procedure call systems. - `The Numerical Python Manual `_ + `The Numerical Python Documentation `_ The Numeric Python extension (NumPy) defines another array type; see - http://numpy.sourceforge.net/ for further information about Numerical Python. - (A PDF version of the NumPy manual is available at - http://numpy.sourceforge.net/numdoc/numdoc.pdf). + http://www.numpy.org/ for further information about Numerical Python. diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1395,6 +1395,7 @@ Milan Zamazal Artur Zaprzala Mike Zarnstorff +Yury V. Zaytsev Siebren van der Zee Nickolai Zeldovich Yuxiao Zeng -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Sun Jun 9 05:48:30 2013 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Sun, 09 Jun 2013 05:48:30 +0200 Subject: [Python-checkins] Daily reference leaks (1f0b6462ea3c): sum=0 Message-ID: results for 1f0b6462ea3c on branch "default" -------------------------------------------- Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogQt4MKO', '-x'] From python-checkins at python.org Sun Jun 9 15:59:01 2013 From: python-checkins at python.org (serhiy.storchaka) Date: Sun, 9 Jun 2013 15:59:01 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogSXNzdWUgIzE4MDM4?= =?utf-8?q?=3A_SyntaxError_raised_during_compilation_sources_with_illegal?= Message-ID: <3bSzbn5NVSz7Ljj@mail.python.org> http://hg.python.org/cpython/rev/15aa786b723b changeset: 84063:15aa786b723b branch: 3.3 parent: 84061:aafa11c1dd61 user: Serhiy Storchaka date: Sun Jun 09 16:51:52 2013 +0300 summary: Issue #18038: SyntaxError raised during compilation sources with illegal encoding now always contains an encoding name. files: Lib/test/test_pep263.py | 18 ++++++++++++++++++ Misc/NEWS | 3 +++ Parser/tokenizer.c | 14 +++++++------- 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/Lib/test/test_pep263.py b/Lib/test/test_pep263.py --- a/Lib/test/test_pep263.py +++ b/Lib/test/test_pep263.py @@ -55,6 +55,24 @@ # two bytes in common with the UTF-8 BOM self.assertRaises(SyntaxError, eval, b'\xef\xbb\x20') + def test_error_message(self): + compile(b'# -*- coding: iso-8859-15 -*-\n', 'dummy', 'exec') + compile(b'\xef\xbb\xbf\n', 'dummy', 'exec') + compile(b'\xef\xbb\xbf# -*- coding: utf-8 -*-\n', 'dummy', 'exec') + with self.assertRaisesRegexp(SyntaxError, 'fake'): + compile(b'# -*- coding: fake -*-\n', 'dummy', 'exec') + with self.assertRaisesRegexp(SyntaxError, 'iso-8859-15'): + compile(b'\xef\xbb\xbf# -*- coding: iso-8859-15 -*-\n', + 'dummy', 'exec') + with self.assertRaisesRegexp(SyntaxError, 'BOM'): + compile(b'\xef\xbb\xbf# -*- coding: iso-8859-15 -*-\n', + 'dummy', 'exec') + with self.assertRaisesRegexp(SyntaxError, 'fake'): + compile(b'\xef\xbb\xbf# -*- coding: fake -*-\n', 'dummy', 'exec') + with self.assertRaisesRegexp(SyntaxError, 'BOM'): + compile(b'\xef\xbb\xbf# -*- coding: fake -*-\n', 'dummy', 'exec') + + def test_main(): support.run_unittest(PEP263Test) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -12,6 +12,9 @@ Core and Builtins ----------------- +- Issue #18038: SyntaxError raised during compilation sources with illegal + encoding now always contains an encoding name. + - Issue #17644: Fix a crash in str.format when curly braces are used in square brackets. diff --git a/Parser/tokenizer.c b/Parser/tokenizer.c --- a/Parser/tokenizer.c +++ b/Parser/tokenizer.c @@ -291,20 +291,20 @@ tok->encoding = cs; tok->decoding_state = STATE_NORMAL; } - else + else { + PyErr_Format(PyExc_SyntaxError, + "encoding problem: %s", cs); PyMem_FREE(cs); + } } } else { /* then, compare cs with BOM */ r = (strcmp(tok->encoding, cs) == 0); + if (!r) + PyErr_Format(PyExc_SyntaxError, + "encoding problem: %s with BOM", cs); PyMem_FREE(cs); } } - if (!r) { - cs = tok->encoding; - if (!cs) - cs = "with BOM"; - PyErr_Format(PyExc_SyntaxError, "encoding problem: %s", cs); - } return r; } -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 9 15:59:03 2013 From: python-checkins at python.org (serhiy.storchaka) Date: Sun, 9 Jun 2013 15:59:03 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2318038=3A_SyntaxError_raised_during_compilation_?= =?utf-8?q?sources_with_illegal?= Message-ID: <3bSzbq0Hgtz7LmY@mail.python.org> http://hg.python.org/cpython/rev/39e2f0059ee2 changeset: 84064:39e2f0059ee2 parent: 84062:1f0b6462ea3c parent: 84063:15aa786b723b user: Serhiy Storchaka date: Sun Jun 09 16:53:55 2013 +0300 summary: Issue #18038: SyntaxError raised during compilation sources with illegal encoding now always contains an encoding name. files: Lib/test/test_pep263.py | 18 ++++++++++++++++++ Misc/NEWS | 3 +++ Parser/tokenizer.c | 14 +++++++------- 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/Lib/test/test_pep263.py b/Lib/test/test_pep263.py --- a/Lib/test/test_pep263.py +++ b/Lib/test/test_pep263.py @@ -55,6 +55,24 @@ # two bytes in common with the UTF-8 BOM self.assertRaises(SyntaxError, eval, b'\xef\xbb\x20') + def test_error_message(self): + compile(b'# -*- coding: iso-8859-15 -*-\n', 'dummy', 'exec') + compile(b'\xef\xbb\xbf\n', 'dummy', 'exec') + compile(b'\xef\xbb\xbf# -*- coding: utf-8 -*-\n', 'dummy', 'exec') + with self.assertRaisesRegexp(SyntaxError, 'fake'): + compile(b'# -*- coding: fake -*-\n', 'dummy', 'exec') + with self.assertRaisesRegexp(SyntaxError, 'iso-8859-15'): + compile(b'\xef\xbb\xbf# -*- coding: iso-8859-15 -*-\n', + 'dummy', 'exec') + with self.assertRaisesRegexp(SyntaxError, 'BOM'): + compile(b'\xef\xbb\xbf# -*- coding: iso-8859-15 -*-\n', + 'dummy', 'exec') + with self.assertRaisesRegexp(SyntaxError, 'fake'): + compile(b'\xef\xbb\xbf# -*- coding: fake -*-\n', 'dummy', 'exec') + with self.assertRaisesRegexp(SyntaxError, 'BOM'): + compile(b'\xef\xbb\xbf# -*- coding: fake -*-\n', 'dummy', 'exec') + + def test_main(): support.run_unittest(PEP263Test) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,9 @@ Core and Builtins ----------------- +- Issue #18038: SyntaxError raised during compilation sources with illegal + encoding now always contains an encoding name. + - Issue #17931: Resolve confusion on Windows between pids and process handles. diff --git a/Parser/tokenizer.c b/Parser/tokenizer.c --- a/Parser/tokenizer.c +++ b/Parser/tokenizer.c @@ -291,20 +291,20 @@ tok->encoding = cs; tok->decoding_state = STATE_NORMAL; } - else + else { + PyErr_Format(PyExc_SyntaxError, + "encoding problem: %s", cs); PyMem_FREE(cs); + } } } else { /* then, compare cs with BOM */ r = (strcmp(tok->encoding, cs) == 0); + if (!r) + PyErr_Format(PyExc_SyntaxError, + "encoding problem: %s with BOM", cs); PyMem_FREE(cs); } } - if (!r) { - cs = tok->encoding; - if (!cs) - cs = "with BOM"; - PyErr_Format(PyExc_SyntaxError, "encoding problem: %s", cs); - } return r; } -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 9 15:59:04 2013 From: python-checkins at python.org (serhiy.storchaka) Date: Sun, 9 Jun 2013 15:59:04 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzE4MDM4?= =?utf-8?q?=3A_SyntaxError_raised_during_compilation_sources_with_illegal?= Message-ID: <3bSzbr2cCMz7LlR@mail.python.org> http://hg.python.org/cpython/rev/570b5b4040b1 changeset: 84065:570b5b4040b1 branch: 2.7 parent: 84060:dacd8f430e0e user: Serhiy Storchaka date: Sun Jun 09 16:54:56 2013 +0300 summary: Issue #18038: SyntaxError raised during compilation sources with illegal encoding now always contains an encoding name. files: Lib/test/test_pep263.py | 18 ++++++++++++++++++ Misc/NEWS | 3 +++ Parser/tokenizer.c | 14 +++++++------- 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/Lib/test/test_pep263.py b/Lib/test/test_pep263.py --- a/Lib/test/test_pep263.py +++ b/Lib/test/test_pep263.py @@ -41,6 +41,24 @@ # two bytes in common with the UTF-8 BOM self.assertRaises(SyntaxError, eval, '\xef\xbb\x20') + def test_error_message(self): + compile('# -*- coding: iso-8859-15 -*-\n', 'dummy', 'exec') + compile('\xef\xbb\xbf\n', 'dummy', 'exec') + compile('\xef\xbb\xbf# -*- coding: utf-8 -*-\n', 'dummy', 'exec') + with self.assertRaisesRegexp(SyntaxError, 'fake'): + compile('# -*- coding: fake -*-\n', 'dummy', 'exec') + with self.assertRaisesRegexp(SyntaxError, 'iso-8859-15'): + compile('\xef\xbb\xbf# -*- coding: iso-8859-15 -*-\n', + 'dummy', 'exec') + with self.assertRaisesRegexp(SyntaxError, 'BOM'): + compile('\xef\xbb\xbf# -*- coding: iso-8859-15 -*-\n', + 'dummy', 'exec') + with self.assertRaisesRegexp(SyntaxError, 'fake'): + compile('\xef\xbb\xbf# -*- coding: fake -*-\n', 'dummy', 'exec') + with self.assertRaisesRegexp(SyntaxError, 'BOM'): + compile('\xef\xbb\xbf# -*- coding: fake -*-\n', 'dummy', 'exec') + + def test_main(): test_support.run_unittest(PEP263Test) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -9,6 +9,9 @@ Core and Builtins ----------------- +- Issue #18038: SyntaxError raised during compilation sources with illegal + encoding now always contains an encoding name. + - Issue #18019: Fix crash in the repr of dictionaries containing their own views. diff --git a/Parser/tokenizer.c b/Parser/tokenizer.c --- a/Parser/tokenizer.c +++ b/Parser/tokenizer.c @@ -277,8 +277,11 @@ tok->encoding = cs; tok->decoding_state = -1; } - else + else { + PyErr_Format(PyExc_SyntaxError, + "encoding problem: %s", cs); PyMem_FREE(cs); + } #else /* Without Unicode support, we cannot process the coding spec. Since there @@ -289,15 +292,12 @@ } } else { /* then, compare cs with BOM */ r = (strcmp(tok->encoding, cs) == 0); + if (!r) + PyErr_Format(PyExc_SyntaxError, + "encoding problem: %s with BOM", cs); PyMem_FREE(cs); } } - if (!r) { - cs = tok->encoding; - if (!cs) - cs = "with BOM"; - PyErr_Format(PyExc_SyntaxError, "encoding problem: %s", cs); - } return r; } -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 9 16:13:11 2013 From: python-checkins at python.org (serhiy.storchaka) Date: Sun, 9 Jun 2013 16:13:11 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogSXNzdWUgIzE1MjM5?= =?utf-8?q?=3A_Make_mkstringprep=2Epy_work_again_on_Python_3=2E?= Message-ID: <3bSzw73ZW0z7Ll1@mail.python.org> http://hg.python.org/cpython/rev/8f95d77443da changeset: 84066:8f95d77443da branch: 3.3 parent: 84063:15aa786b723b user: Serhiy Storchaka date: Sun Jun 09 17:08:00 2013 +0300 summary: Issue #15239: Make mkstringprep.py work again on Python 3. files: Misc/NEWS | 5 ++ Tools/unicode/mkstringprep.py | 40 +++++++++++++--------- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -107,6 +107,11 @@ - Issue #17977: The documentation for the cadefault argument's default value in urllib.request.urlopen() is fixed to match the code. +Tools/Demos +----------- + +- Issue #15239: Make mkstringprep.py work again on Python 3. + What's New in Python 3.3.2? =========================== diff --git a/Tools/unicode/mkstringprep.py b/Tools/unicode/mkstringprep.py --- a/Tools/unicode/mkstringprep.py +++ b/Tools/unicode/mkstringprep.py @@ -1,4 +1,5 @@ -import re, unicodedata, sys +import re, sys +from unicodedata import ucd_3_2_0 as unicodedata if sys.maxunicode == 65535: raise RuntimeError("need UCS-4 Python") @@ -37,16 +38,20 @@ tuple.append((prev,prev+span+1)) else: single.append(prev) - tuple = " + ".join(["list(range(%d,%d))" % t for t in tuple]) + if not single and len(tuple) == 1: + tuple = "range(%d,%d)" % tuple[0] + else: + tuple = " + ".join("list(range(%d,%d))" % t for t in tuple) if not single: return "set(%s)" % tuple if not tuple: - return "set(%s)" % repr(single) - return "set(%s + %s)" % (repr(single),tuple) + return "set(%r)" % (single,) + return "set(%r + %s)" % (single, tuple) ############## Read the tables in the RFC ####################### -data = open("rfc3454.txt").readlines() +with open("rfc3454.txt") as f: + data = f.readlines() tables = [] curname = None @@ -55,8 +60,7 @@ if not l: continue # Skip RFC page breaks - if l.startswith("Hoffman & Blanchet") or\ - l.startswith("RFC 3454"): + if l.startswith(("Hoffman & Blanchet", "RFC 3454")): continue # Find start/end lines m = re.match("----- (Start|End) Table ([A-Z](.[0-9])+) -----", l) @@ -71,6 +75,8 @@ else: if not curname: raise RuntimeError("End without start", l) + if curname != m.group(2): + raise RuntimeError("Unexpected end", l) curname = None continue if not curname: @@ -113,10 +119,10 @@ and mappings, for which a mapping function is provided. \"\"\" -import unicodedata +from unicodedata import ucd_3_2_0 as unicodedata """) -print("assert unicodedata.unidata_version == %s" % repr(unicodedata.unidata_version)) +print("assert unicodedata.unidata_version == %r" % (unicodedata.unidata_version,)) # A.1 is the table of unassigned characters # XXX Plane 15 PUA is listed as unassigned in Python. @@ -173,15 +179,15 @@ b3_exceptions = {} for k,v in table_b2.items(): - if map(ord, unichr(k).lower()) != v: - b3_exceptions[k] = u"".join(map(unichr,v)) + if list(map(ord, chr(k).lower())) != v: + b3_exceptions[k] = "".join(map(chr,v)) b3 = sorted(b3_exceptions.items()) print(""" b3_exceptions = {""") -for i,(k,v) in enumerate(b3): - print("0x%x:%s," % (k, repr(v)), end=' ') +for i, kv in enumerate(b3): + print("0x%x:%a," % kv, end=' ') if i % 4 == 3: print() print("}") @@ -224,7 +230,7 @@ def map_table_b2(a): al = map_table_b3(a) b = unicodedata.normalize("NFKC", al) - bl = u"".join([map_table_b3(ch) for ch in b]) + bl = "".join([map_table_b3(ch) for ch in b]) c = unicodedata.normalize("NFKC", bl) if b != c: return c @@ -240,7 +246,7 @@ print(""" def in_table_c11(code): - return code == u" " + return code == " " """) # C.1.2 is the rest of all space characters @@ -249,12 +255,12 @@ assert name == "C.1.2" # table = set(table.keys()) -# Zs = set(gen_category(["Zs"])) - set([0x20]) +# Zs = set(gen_category(["Zs"])) - {0x20} # assert Zs == table print(""" def in_table_c12(code): - return unicodedata.category(code) == "Zs" and code != u" " + return unicodedata.category(code) == "Zs" and code != " " def in_table_c11_c12(code): return unicodedata.category(code) == "Zs" -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 9 16:13:12 2013 From: python-checkins at python.org (serhiy.storchaka) Date: Sun, 9 Jun 2013 16:13:12 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2315239=3A_Make_mkstringprep=2Epy_work_again_on_P?= =?utf-8?q?ython_3=2E?= Message-ID: <3bSzw86cWgz7Lm5@mail.python.org> http://hg.python.org/cpython/rev/4abe61a412be changeset: 84067:4abe61a412be parent: 84064:39e2f0059ee2 parent: 84066:8f95d77443da user: Serhiy Storchaka date: Sun Jun 09 17:11:48 2013 +0300 summary: Issue #15239: Make mkstringprep.py work again on Python 3. files: Misc/NEWS | 2 + Tools/unicode/mkstringprep.py | 40 +++++++++++++--------- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -1787,6 +1787,8 @@ Tools/Demos ----------- +- Issue #15239: Make mkstringprep.py work again on Python 3. + - Issue #17028: Allowed Python arguments to be supplied to the Windows launcher. diff --git a/Tools/unicode/mkstringprep.py b/Tools/unicode/mkstringprep.py --- a/Tools/unicode/mkstringprep.py +++ b/Tools/unicode/mkstringprep.py @@ -1,4 +1,5 @@ -import re, unicodedata, sys +import re, sys +from unicodedata import ucd_3_2_0 as unicodedata if sys.maxunicode == 65535: raise RuntimeError("need UCS-4 Python") @@ -37,16 +38,20 @@ tuple.append((prev,prev+span+1)) else: single.append(prev) - tuple = " + ".join(["list(range(%d,%d))" % t for t in tuple]) + if not single and len(tuple) == 1: + tuple = "range(%d,%d)" % tuple[0] + else: + tuple = " + ".join("list(range(%d,%d))" % t for t in tuple) if not single: return "set(%s)" % tuple if not tuple: - return "set(%s)" % repr(single) - return "set(%s + %s)" % (repr(single),tuple) + return "set(%r)" % (single,) + return "set(%r + %s)" % (single, tuple) ############## Read the tables in the RFC ####################### -data = open("rfc3454.txt").readlines() +with open("rfc3454.txt") as f: + data = f.readlines() tables = [] curname = None @@ -55,8 +60,7 @@ if not l: continue # Skip RFC page breaks - if l.startswith("Hoffman & Blanchet") or\ - l.startswith("RFC 3454"): + if l.startswith(("Hoffman & Blanchet", "RFC 3454")): continue # Find start/end lines m = re.match("----- (Start|End) Table ([A-Z](.[0-9])+) -----", l) @@ -71,6 +75,8 @@ else: if not curname: raise RuntimeError("End without start", l) + if curname != m.group(2): + raise RuntimeError("Unexpected end", l) curname = None continue if not curname: @@ -113,10 +119,10 @@ and mappings, for which a mapping function is provided. \"\"\" -import unicodedata +from unicodedata import ucd_3_2_0 as unicodedata """) -print("assert unicodedata.unidata_version == %s" % repr(unicodedata.unidata_version)) +print("assert unicodedata.unidata_version == %r" % (unicodedata.unidata_version,)) # A.1 is the table of unassigned characters # XXX Plane 15 PUA is listed as unassigned in Python. @@ -173,15 +179,15 @@ b3_exceptions = {} for k,v in table_b2.items(): - if map(ord, unichr(k).lower()) != v: - b3_exceptions[k] = u"".join(map(unichr,v)) + if list(map(ord, chr(k).lower())) != v: + b3_exceptions[k] = "".join(map(chr,v)) b3 = sorted(b3_exceptions.items()) print(""" b3_exceptions = {""") -for i,(k,v) in enumerate(b3): - print("0x%x:%s," % (k, repr(v)), end=' ') +for i, kv in enumerate(b3): + print("0x%x:%a," % kv, end=' ') if i % 4 == 3: print() print("}") @@ -224,7 +230,7 @@ def map_table_b2(a): al = map_table_b3(a) b = unicodedata.normalize("NFKC", al) - bl = u"".join([map_table_b3(ch) for ch in b]) + bl = "".join([map_table_b3(ch) for ch in b]) c = unicodedata.normalize("NFKC", bl) if b != c: return c @@ -240,7 +246,7 @@ print(""" def in_table_c11(code): - return code == u" " + return code == " " """) # C.1.2 is the rest of all space characters @@ -249,12 +255,12 @@ assert name == "C.1.2" # table = set(table.keys()) -# Zs = set(gen_category(["Zs"])) - set([0x20]) +# Zs = set(gen_category(["Zs"])) - {0x20} # assert Zs == table print(""" def in_table_c12(code): - return unicodedata.category(code) == "Zs" and code != u" " + return unicodedata.category(code) == "Zs" and code != " " def in_table_c11_c12(code): return unicodedata.category(code) == "Zs" -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 9 18:03:10 2013 From: python-checkins at python.org (christian.heimes) Date: Sun, 9 Jun 2013 18:03:10 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2318143=3A_Implemen?= =?utf-8?q?t_ssl=2Eget=5Fdefault=5Fverify=5Fpaths=28=29_in_order_to_debug?= Message-ID: <3bT2M26dvtzSSY@mail.python.org> http://hg.python.org/cpython/rev/a4d31e56075d changeset: 84068:a4d31e56075d user: Christian Heimes date: Sun Jun 09 18:02:55 2013 +0200 summary: Issue #18143: Implement ssl.get_default_verify_paths() in order to debug the default locations for cafile and capath. files: Doc/library/ssl.rst | 20 ++++++++++++++- Lib/ssl.py | 20 +++++++++++++++ Lib/test/test_ssl.py | 13 +++++++++ Misc/NEWS | 3 ++ Modules/_ssl.c | 42 ++++++++++++++++++++++++++++++++ 5 files changed, 97 insertions(+), 1 deletions(-) diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -343,6 +343,23 @@ Given a certificate as an ASCII PEM string, returns a DER-encoded sequence of bytes for that same certificate. +.. function:: get_default_verify_paths() + + Returns a named tuple with paths to OpenSSL's default cafile and capath. + The paths are the same as used by + :meth:`SSLContext.set_default_verify_paths`. The return value is a + :term:`named tuple` ``DefaultVerifyPaths``: + + * :attr:`cafile` - resolved path to cafile or None if the file doesn't exist, + * :attr:`capath` - resolved path to capath or None if the directory doesn't exist, + * :attr:`openssl_cafile_env` - OpenSSL's environment key that points to a cafile, + * :attr:`openssl_cafile` - hard coded path to a cafile, + * :attr:`openssl_capath_env` - OpenSSL's environment key that points to a capath, + * :attr:`openssl_capath` - hard coded path to a capath directory + + .. versionadded:: 3.4 + + Constants ^^^^^^^^^ @@ -787,7 +804,8 @@ other peers' certificates when :data:`verify_mode` is other than :data:`CERT_NONE`. At least one of *cafile* or *capath* must be specified. - The *cafile* string, if present, is the path to a file of concatenated + The *cafile* string, if present, is the p + ath to a file of concatenated CA certificates in PEM format. See the discussion of :ref:`ssl-certificates` for more information about how to arrange the certificates in this file. diff --git a/Lib/ssl.py b/Lib/ssl.py --- a/Lib/ssl.py +++ b/Lib/ssl.py @@ -89,6 +89,8 @@ import textwrap import re +import os +import collections import _ssl # if we can't import it, let the error propagate @@ -222,6 +224,24 @@ "subjectAltName fields were found") +DefaultVerifyPaths = collections.namedtuple("DefaultVerifyPaths", + "cafile capath openssl_cafile_env openssl_cafile openssl_capath_env " + "openssl_capath") + +def get_default_verify_paths(): + """Return paths to default cafile and capath. + """ + parts = _ssl.get_default_verify_paths() + + # environment vars shadow paths + cafile = os.environ.get(parts[0], parts[1]) + capath = os.environ.get(parts[2], parts[3]) + + return DefaultVerifyPaths(cafile if os.path.isfile(cafile) else None, + capath if os.path.isdir(capath) else None, + *parts) + + class SSLContext(_SSLContext): """An SSLContext holds various SSL-related configuration options and data, such as certificates and possibly a private key.""" diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -394,6 +394,19 @@ support.gc_collect() self.assertIn(r, str(cm.warning.args[0])) + def test_get_default_verify_paths(self): + paths = ssl.get_default_verify_paths() + self.assertEqual(len(paths), 6) + self.assertIsInstance(paths, ssl.DefaultVerifyPaths) + + with support.EnvironmentVarGuard() as env: + env["SSL_CERT_DIR"] = CAPATH + env["SSL_CERT_FILE"] = CERTFILE + paths = ssl.get_default_verify_paths() + self.assertEqual(paths.cafile, CERTFILE) + self.assertEqual(paths.capath, CAPATH) + + class ContextTests(unittest.TestCase): @skip_if_broken_ubuntu_ssl diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -115,6 +115,9 @@ Library ------- +- Issue #18143: Implement ssl.get_default_verify_paths() in order to debug + the default locations for cafile and capath. + - Issue #17314: Move multiprocessing.forking over to importlib. - Issue #11959: SMTPServer and SMTPChannel now take an optional map, use of diff --git a/Modules/_ssl.c b/Modules/_ssl.c --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -2761,6 +2761,46 @@ #endif +PyDoc_STRVAR(PySSL_get_default_verify_paths_doc, +"get_default_verify_paths() -> tuple\n\ +\n\ +Return search paths and environment vars that are used by SSLContext's\n\ +set_default_verify_paths() to load default CAs. The values are\n\ +'cert_file_env', 'cert_file', 'cert_dir_env', 'cert_dir'."); + +static PyObject * +get_default_verify_paths(PyObject *self) +{ + PyObject *ofile_env = NULL; + PyObject *ofile = NULL; + PyObject *odir_env = NULL; + PyObject *odir = NULL; + +#define convert(info, target) { \ + const char *tmp = (info); \ + target = NULL; \ + if (!tmp) { Py_INCREF(Py_None); target = Py_None; } \ + else if ((target = PyUnicode_DecodeFSDefault(tmp)) == NULL) { \ + target = PyBytes_FromString(tmp); } \ + if (!target) goto error; \ + } while(0) + + convert(X509_get_default_cert_file_env(), ofile_env); + convert(X509_get_default_cert_file(), ofile); + convert(X509_get_default_cert_dir_env(), odir_env); + convert(X509_get_default_cert_dir(), odir); +#undef convert + + return Py_BuildValue("(OOOO)", ofile_env, ofile, odir_env, odir); + + error: + Py_XDECREF(ofile_env); + Py_XDECREF(ofile); + Py_XDECREF(odir_env); + Py_XDECREF(odir); + return NULL; +} + /* List of functions exported by this module. */ @@ -2779,6 +2819,8 @@ PySSL_RAND_egd_doc}, {"RAND_status", (PyCFunction)PySSL_RAND_status, METH_NOARGS, PySSL_RAND_status_doc}, + {"get_default_verify_paths", (PyCFunction)get_default_verify_paths, + METH_NOARGS, PySSL_get_default_verify_paths_doc}, #endif {NULL, NULL} /* Sentinel */ }; -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 9 18:07:25 2013 From: python-checkins at python.org (christian.heimes) Date: Sun, 9 Jun 2013 18:07:25 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_removed_accidental_new_lin?= =?utf-8?q?e?= Message-ID: <3bT2Rx5cH7z7Ljh@mail.python.org> http://hg.python.org/cpython/rev/716e8b23974a changeset: 84069:716e8b23974a user: Christian Heimes date: Sun Jun 09 18:07:16 2013 +0200 summary: removed accidental new line files: Doc/library/ssl.rst | 3 +-- 1 files changed, 1 insertions(+), 2 deletions(-) diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -804,8 +804,7 @@ other peers' certificates when :data:`verify_mode` is other than :data:`CERT_NONE`. At least one of *cafile* or *capath* must be specified. - The *cafile* string, if present, is the p - ath to a file of concatenated + The *cafile* string, if present, is the path to a file of concatenated CA certificates in PEM format. See the discussion of :ref:`ssl-certificates` for more information about how to arrange the certificates in this file. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 9 18:30:03 2013 From: python-checkins at python.org (christian.heimes) Date: Sun, 9 Jun 2013 18:30:03 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_get=5Fdefault=5Fverify=5Fp?= =?utf-8?q?aths_doesn=27t_belong_inside_the_ifdef_block?= Message-ID: <3bT2y30qsfzT1s@mail.python.org> http://hg.python.org/cpython/rev/4fdbe5d05922 changeset: 84070:4fdbe5d05922 user: Christian Heimes date: Sun Jun 09 18:29:54 2013 +0200 summary: get_default_verify_paths doesn't belong inside the ifdef block files: Modules/_ssl.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Modules/_ssl.c b/Modules/_ssl.c --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -2819,9 +2819,9 @@ PySSL_RAND_egd_doc}, {"RAND_status", (PyCFunction)PySSL_RAND_status, METH_NOARGS, PySSL_RAND_status_doc}, +#endif {"get_default_verify_paths", (PyCFunction)get_default_verify_paths, METH_NOARGS, PySSL_get_default_verify_paths_doc}, -#endif {NULL, NULL} /* Sentinel */ }; -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 9 19:03:42 2013 From: python-checkins at python.org (christian.heimes) Date: Sun, 9 Jun 2013 19:03:42 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2317134=3A_Add_ssl?= =?utf-8?q?=2Eenum=5Fcert=5Fstore=28=29_as_interface_to_Windows=27_cert_st?= =?utf-8?q?ore=2E?= Message-ID: <3bT3ht1TnTzSRJ@mail.python.org> http://hg.python.org/cpython/rev/10d325f674f5 changeset: 84071:10d325f674f5 user: Christian Heimes date: Sun Jun 09 19:03:31 2013 +0200 summary: Issue #17134: Add ssl.enum_cert_store() as interface to Windows' cert store. files: Doc/library/ssl.rst | 23 ++++ Lib/ssl.py | 4 + Lib/test/test_ssl.py | 23 ++++ Misc/NEWS | 2 + Modules/_ssl.c | 134 +++++++++++++++++++++++++++- PC/VS9.0/_socket.vcproj | 16 +- PCbuild/_ssl.vcxproj | 16 +- 7 files changed, 201 insertions(+), 17 deletions(-) diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -359,6 +359,20 @@ .. versionadded:: 3.4 +.. function:: enum_cert_store(store_name, cert_type='certificate') + + Retrieve certificates from Windows' system cert store. *store_name* may be + one of ``CA``, ``ROOT`` or ``MY``. Windows may provide additional cert + stores, too. *cert_type* is either ``certificate`` for X.509 certificates + or ``crl`` for X.509 certificate revocation lists. + + The function returns a list of (bytes, encoding_type) tuples. The + encoding_type flag can be interpreted with :const:`X509_ASN_ENCODING` or + :const:`PKCS_7_ASN_ENCODING`. + + Availability: Windows. + + .. versionadded:: 3.4 Constants ^^^^^^^^^ @@ -598,6 +612,15 @@ .. versionadded:: 3.4 +.. data:: X509_ASN_ENCODING + PKCS_7_ASN_ENCODING + + Encoding flags for :func:`enum_cert_store`. + + Availability: Windows. + + .. versionadded:: 3.4 + SSL Sockets ----------- diff --git a/Lib/ssl.py b/Lib/ssl.py --- a/Lib/ssl.py +++ b/Lib/ssl.py @@ -89,6 +89,7 @@ import textwrap import re +import sys import os import collections @@ -139,6 +140,9 @@ _PROTOCOL_NAMES[PROTOCOL_TLSv1_1] = "TLSv1.1" _PROTOCOL_NAMES[PROTOCOL_TLSv1_2] = "TLSv1.2" +if sys.platform == "win32": + from _ssl import enum_cert_store, X509_ASN_ENCODING, PKCS_7_ASN_ENCODING + from socket import getnameinfo as _getnameinfo from socket import socket, AF_INET, SOCK_STREAM, create_connection import base64 # for DER-to-PEM translation diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -407,6 +407,29 @@ self.assertEqual(paths.capath, CAPATH) + @unittest.skipUnless(sys.platform == "win32", "Windows specific") + def test_enum_cert_store(self): + self.assertEqual(ssl.X509_ASN_ENCODING, 1) + self.assertEqual(ssl.PKCS_7_ASN_ENCODING, 0x00010000) + + self.assertEqual(ssl.enum_cert_store("CA"), + ssl.enum_cert_store("CA", "certificate")) + ssl.enum_cert_store("CA", "crl") + self.assertEqual(ssl.enum_cert_store("ROOT"), + ssl.enum_cert_store("ROOT", "certificate")) + ssl.enum_cert_store("ROOT", "crl") + + self.assertRaises(TypeError, ssl.enum_cert_store) + self.assertRaises(WindowsError, ssl.enum_cert_store, "") + self.assertRaises(ValueError, ssl.enum_cert_store, "CA", "wrong") + + ca = ssl.enum_cert_store("CA") + self.assertIsInstance(ca, list) + self.assertIsInstance(ca[0], tuple) + self.assertEqual(len(ca[0]), 2) + self.assertIsInstance(ca[0][0], bytes) + self.assertIsInstance(ca[0][1], int) + class ContextTests(unittest.TestCase): @skip_if_broken_ubuntu_ssl diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -115,6 +115,8 @@ Library ------- +- Issue #17134: Add ssl.enum_cert_store() as interface to Windows' cert store. + - Issue #18143: Implement ssl.get_default_verify_paths() in order to debug the default locations for cafile and capath. diff --git a/Modules/_ssl.c b/Modules/_ssl.c --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -2801,7 +2801,129 @@ return NULL; } - +#ifdef _MSC_VER +PyDoc_STRVAR(PySSL_enum_cert_store_doc, +"enum_cert_store(store_name, cert_type='certificate') -> []\n\ +\n\ +Retrieve certificates from Windows' cert store. store_name may be one of\n\ +'CA', 'ROOT' or 'MY'. The system may provide more cert storages, too.\n\ +cert_type must be either 'certificate' or 'crl'.\n\ +The function returns a list of (bytes, encoding_type) tuples. The\n\ +encoding_type flag can be interpreted with X509_ASN_ENCODING or\n\ +PKCS_7_ASN_ENCODING."); + +static PyObject * +PySSL_enum_cert_store(PyObject *self, PyObject *args, PyObject *kwds) +{ + char *kwlist[] = {"store_name", "cert_type", NULL}; + char *store_name; + char *cert_type = "certificate"; + HCERTSTORE hStore = NULL; + PyObject *result = NULL; + PyObject *tup = NULL, *cert = NULL, *enc = NULL; + int ok = 1; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|s:enum_cert_store", + kwlist, &store_name, &cert_type)) { + return NULL; + } + + if ((strcmp(cert_type, "certificate") != 0) && + (strcmp(cert_type, "crl") != 0)) { + return PyErr_Format(PyExc_ValueError, + "cert_type must be 'certificate' or 'crl', " + "not %.100s", cert_type); + } + + if ((result = PyList_New(0)) == NULL) { + return NULL; + } + + if ((hStore = CertOpenSystemStore(NULL, store_name)) == NULL) { + Py_DECREF(result); + return PyErr_SetFromWindowsErr(GetLastError()); + } + + if (strcmp(cert_type, "certificate") == 0) { + PCCERT_CONTEXT pCertCtx = NULL; + while (pCertCtx = CertEnumCertificatesInStore(hStore, pCertCtx)) { + cert = PyBytes_FromStringAndSize((const char*)pCertCtx->pbCertEncoded, + pCertCtx->cbCertEncoded); + if (!cert) { + ok = 0; + break; + } + if ((enc = PyLong_FromLong(pCertCtx->dwCertEncodingType)) == NULL) { + ok = 0; + break; + } + if ((tup = PyTuple_New(2)) == NULL) { + ok = 0; + break; + } + PyTuple_SET_ITEM(tup, 0, cert); cert = NULL; + PyTuple_SET_ITEM(tup, 1, enc); enc = NULL; + + if (PyList_Append(result, tup) < 0) { + ok = 0; + break; + } + Py_CLEAR(tup); + } + if (pCertCtx) { + /* loop ended with an error, need to clean up context manually */ + CertFreeCertificateContext(pCertCtx); + } + } else { + PCCRL_CONTEXT pCrlCtx = NULL; + while (pCrlCtx = CertEnumCRLsInStore(hStore, pCrlCtx)) { + cert = PyBytes_FromStringAndSize((const char*)pCrlCtx->pbCrlEncoded, + pCrlCtx->cbCrlEncoded); + if (!cert) { + ok = 0; + break; + } + if ((enc = PyLong_FromLong(pCrlCtx->dwCertEncodingType)) == NULL) { + ok = 0; + break; + } + if ((tup = PyTuple_New(2)) == NULL) { + ok = 0; + break; + } + PyTuple_SET_ITEM(tup, 0, cert); cert = NULL; + PyTuple_SET_ITEM(tup, 1, enc); enc = NULL; + + if (PyList_Append(result, tup) < 0) { + ok = 0; + break; + } + Py_CLEAR(tup); + } + if (pCrlCtx) { + /* loop ended with an error, need to clean up context manually */ + CertFreeCRLContext(pCrlCtx); + } + } + + /* In error cases cert, enc and tup may not be NULL */ + Py_XDECREF(cert); + Py_XDECREF(enc); + Py_XDECREF(tup); + + if (!CertCloseStore(hStore, 0)) { + /* This error case might shadow another exception.*/ + Py_DECREF(result); + return PyErr_SetFromWindowsErr(GetLastError()); + } + if (ok) { + return result; + } else { + Py_DECREF(result); + return NULL; + } +} +#endif /* List of functions exported by this module. */ @@ -2822,6 +2944,10 @@ #endif {"get_default_verify_paths", (PyCFunction)get_default_verify_paths, METH_NOARGS, PySSL_get_default_verify_paths_doc}, +#ifdef _MSC_VER + {"enum_cert_store", (PyCFunction)PySSL_enum_cert_store, + METH_VARARGS | METH_KEYWORDS, PySSL_enum_cert_store_doc}, +#endif {NULL, NULL} /* Sentinel */ }; @@ -3034,6 +3160,12 @@ PyModule_AddIntConstant(m, "CERT_REQUIRED", PY_SSL_CERT_REQUIRED); +#ifdef _MSC_VER + /* Windows dwCertEncodingType */ + PyModule_AddIntMacro(m, X509_ASN_ENCODING); + PyModule_AddIntMacro(m, PKCS_7_ASN_ENCODING); +#endif + /* Alert Descriptions from ssl.h */ /* note RESERVED constants no longer intended for use have been removed */ /* http://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-6 */ diff --git a/PC/VS9.0/_socket.vcproj b/PC/VS9.0/_socket.vcproj --- a/PC/VS9.0/_socket.vcproj +++ b/PC/VS9.0/_socket.vcproj @@ -54,7 +54,7 @@ /> @@ -423,7 +423,7 @@ /> diff --git a/PCbuild/_ssl.vcxproj b/PCbuild/_ssl.vcxproj --- a/PCbuild/_ssl.vcxproj +++ b/PCbuild/_ssl.vcxproj @@ -158,7 +158,7 @@ - ws2_32.lib;$(opensslDir)\out32\libeay32.lib;$(opensslDir)\out32\ssleay32.lib;%(AdditionalDependencies) + ws2_32.lib;crypt32.lib;$(opensslDir)\out32\libeay32.lib;$(opensslDir)\out32\ssleay32.lib;%(AdditionalDependencies) @@ -173,7 +173,7 @@ - ws2_32.lib;$(opensslDir)\out64\libeay32.lib;$(opensslDir)\out64\ssleay32.lib;%(AdditionalDependencies) + ws2_32.lib;crypt32.lib;$(opensslDir)\out64\libeay32.lib;$(opensslDir)\out64\ssleay32.lib;%(AdditionalDependencies) @@ -185,7 +185,7 @@ - ws2_32.lib;$(opensslDir)\out32\libeay32.lib;$(opensslDir)\out32\ssleay32.lib;%(AdditionalDependencies) + ws2_32.lib;crypt32.lib;$(opensslDir)\out32\libeay32.lib;$(opensslDir)\out32\ssleay32.lib;%(AdditionalDependencies) @@ -200,7 +200,7 @@ - ws2_32.lib;$(opensslDir)\out64\libeay32.lib;$(opensslDir)\out64\ssleay32.lib;%(AdditionalDependencies) + ws2_32.lib;crypt32.lib;$(opensslDir)\out64\libeay32.lib;$(opensslDir)\out64\ssleay32.lib;%(AdditionalDependencies) @@ -212,7 +212,7 @@ - ws2_32.lib;$(opensslDir)\out32\libeay32.lib;$(opensslDir)\out32\ssleay32.lib;%(AdditionalDependencies) + ws2_32.lib;crypt32.lib;$(opensslDir)\out32\libeay32.lib;$(opensslDir)\out32\ssleay32.lib;%(AdditionalDependencies) @@ -227,7 +227,7 @@ - ws2_32.lib;$(opensslDir)\out64\libeay32.lib;$(opensslDir)\out64\ssleay32.lib;%(AdditionalDependencies) + ws2_32.lib;crypt32.lib;$(opensslDir)\out64\libeay32.lib;$(opensslDir)\out64\ssleay32.lib;%(AdditionalDependencies) MachineX64 @@ -240,7 +240,7 @@ - ws2_32.lib;$(opensslDir)\out32\libeay32.lib;$(opensslDir)\out32\ssleay32.lib;%(AdditionalDependencies) + ws2_32.lib;crypt32.lib;$(opensslDir)\out32\libeay32.lib;$(opensslDir)\out32\ssleay32.lib;%(AdditionalDependencies) @@ -255,7 +255,7 @@ - ws2_32.lib;$(opensslDir)\out64\libeay32.lib;$(opensslDir)\out64\ssleay32.lib;%(AdditionalDependencies) + ws2_32.lib;crypt32.lib;$(opensslDir)\out64\libeay32.lib;$(opensslDir)\out64\ssleay32.lib;%(AdditionalDependencies) MachineX64 -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 9 20:10:48 2013 From: python-checkins at python.org (serhiy.storchaka) Date: Sun, 9 Jun 2013 20:10:48 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogSXNzdWUgIzE2MTAy?= =?utf-8?q?=3A_Make_uuid=2E=5Fnetbios=5Fgetnode=28=29_work_again_on_Python?= =?utf-8?b?IDMu?= Message-ID: <3bT5BJ1XKXzSnY@mail.python.org> http://hg.python.org/cpython/rev/27f55ff12f41 changeset: 84072:27f55ff12f41 branch: 3.3 parent: 84066:8f95d77443da user: Serhiy Storchaka date: Sun Jun 09 21:08:05 2013 +0300 summary: Issue #16102: Make uuid._netbios_getnode() work again on Python 3. files: Lib/uuid.py | 2 +- Misc/NEWS | 2 ++ 2 files changed, 3 insertions(+), 1 deletions(-) diff --git a/Lib/uuid.py b/Lib/uuid.py --- a/Lib/uuid.py +++ b/Lib/uuid.py @@ -406,7 +406,7 @@ if win32wnet.Netbios(ncb) != 0: continue status._unpack() - bytes = map(ord, status.adapter_address) + bytes = status.adapter_address return ((bytes[0]<<40) + (bytes[1]<<32) + (bytes[2]<<24) + (bytes[3]<<16) + (bytes[4]<<8) + bytes[5]) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -27,6 +27,8 @@ Library ------- +- Issue #16102: Make uuid._netbios_getnode() work again on Python 3. + - Issue #18109: os.uname() now decodes fields from the locale encoding, and socket.gethostname() now decodes the hostname from the locale encoding, instead of using the UTF-8 encoding in strict mode. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 9 20:10:49 2013 From: python-checkins at python.org (serhiy.storchaka) Date: Sun, 9 Jun 2013 20:10:49 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?b?KTogSXNzdWUgIzE2MTAyOiBNYWtlIHV1aWQuX25ldGJpb3NfZ2V0bm9kZSgp?= =?utf-8?q?_work_again_on_Python_3=2E?= Message-ID: <3bT5BK3XlPzT1s@mail.python.org> http://hg.python.org/cpython/rev/4a0017722910 changeset: 84073:4a0017722910 parent: 84071:10d325f674f5 parent: 84072:27f55ff12f41 user: Serhiy Storchaka date: Sun Jun 09 21:10:13 2013 +0300 summary: Issue #16102: Make uuid._netbios_getnode() work again on Python 3. files: Lib/uuid.py | 2 +- Misc/NEWS | 2 ++ 2 files changed, 3 insertions(+), 1 deletions(-) diff --git a/Lib/uuid.py b/Lib/uuid.py --- a/Lib/uuid.py +++ b/Lib/uuid.py @@ -406,7 +406,7 @@ if win32wnet.Netbios(ncb) != 0: continue status._unpack() - bytes = map(ord, status.adapter_address) + bytes = status.adapter_address return ((bytes[0]<<40) + (bytes[1]<<32) + (bytes[2]<<24) + (bytes[3]<<16) + (bytes[4]<<8) + bytes[5]) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -115,6 +115,8 @@ Library ------- +- Issue #16102: Make uuid._netbios_getnode() work again on Python 3. + - Issue #17134: Add ssl.enum_cert_store() as interface to Windows' cert store. - Issue #18143: Implement ssl.get_default_verify_paths() in order to debug -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Mon Jun 10 05:51:51 2013 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Mon, 10 Jun 2013 05:51:51 +0200 Subject: [Python-checkins] Daily reference leaks (4a0017722910): sum=53 Message-ID: results for 4a0017722910 on branch "default" -------------------------------------------- test_unittest leaked [-1, 2, 0] memory blocks, sum=1 test_ssl leaked [8, 8, 8] references, sum=24 test_ssl leaked [8, 10, 10] memory blocks, sum=28 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogyU1vRV', '-x'] From python-checkins at python.org Mon Jun 10 10:37:28 2013 From: python-checkins at python.org (ronald.oussoren) Date: Mon, 10 Jun 2013 10:37:28 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_Ensure_that_th?= =?utf-8?q?e_fix_for_=2317269_also_works_on_OSX_10=2E4?= Message-ID: <3bTSQJ3RmKzQct@mail.python.org> http://hg.python.org/cpython/rev/4d1e4bc6c5b5 changeset: 84074:4d1e4bc6c5b5 branch: 2.7 parent: 84065:570b5b4040b1 user: Ronald Oussoren date: Mon Jun 10 10:35:36 2013 +0200 summary: Ensure that the fix for #17269 also works on OSX 10.4 AI_NUMERICSERV isn't defined on OSX 10.4. files: Lib/test/test_socket.py | 3 ++- Modules/socketmodule.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -666,7 +666,8 @@ socket.AI_PASSIVE) # Issue 17269 - socket.getaddrinfo("localhost", None, 0, 0, 0, socket.AI_NUMERICSERV) + if hasattr(socket, 'AI_NUMERICSERV'): + socket.getaddrinfo("localhost", None, 0, 0, 0, socket.AI_NUMERICSERV) def check_sendall_interrupted(self, with_timeout): # socketpair() is not stricly required, but it makes things easier. diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -4179,7 +4179,7 @@ "getaddrinfo() argument 2 must be integer or string"); goto err; } -#ifdef __APPLE__ +#if defined(__APPLE__) && defined(AI_NUMERICSERV) if ((flags & AI_NUMERICSERV) && (pptr == NULL || (pptr[0] == '0' && pptr[1] == 0))) { /* On OSX upto at least OSX 10.8 getaddrinfo crashes * if AI_NUMERICSERV is set and the servname is NULL or "0". -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 10 10:37:29 2013 From: python-checkins at python.org (ronald.oussoren) Date: Mon, 10 Jun 2013 10:37:29 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E3=29=3A_Ensure_that_th?= =?utf-8?q?e_fix_for_=2317269_also_works_on_OSX_10=2E4?= Message-ID: <3bTSQK5YNrzRfs@mail.python.org> http://hg.python.org/cpython/rev/ef103e7e7af2 changeset: 84075:ef103e7e7af2 branch: 3.3 parent: 84072:27f55ff12f41 user: Ronald Oussoren date: Mon Jun 10 10:36:28 2013 +0200 summary: Ensure that the fix for #17269 also works on OSX 10.4 AI_NUMERICSERV isn't defined on OSX 10.4. files: Lib/test/test_socket.py | 3 ++- Modules/socketmodule.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -1167,7 +1167,8 @@ self.assertRaises(UnicodeEncodeError, socket.getaddrinfo, 'localhost', '\uD800') # Issue 17269 - socket.getaddrinfo("localhost", None, 0, 0, 0, socket.AI_NUMERICSERV) + if hasattr(socket, 'AI_NUMERICSERV'): + socket.getaddrinfo("localhost", None, 0, 0, 0, socket.AI_NUMERICSERV) def test_getnameinfo(self): # only IP addresses are allowed diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -5041,7 +5041,7 @@ PyErr_SetString(PyExc_OSError, "Int or String expected"); goto err; } -#ifdef __APPLE__ +#if defined(__APPLE__) && defined(AI_NUMERICSERV) if ((flags & AI_NUMERICSERV) && (pptr == NULL || (pptr[0] == '0' && pptr[1] == 0))) { /* On OSX upto at least OSX 10.8 getaddrinfo crashes * if AI_NUMERICSERV is set and the servname is NULL or "0". -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 10 10:37:31 2013 From: python-checkins at python.org (ronald.oussoren) Date: Mon, 10 Jun 2013 10:37:31 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_=283=2E3-=3Edefault=29_Ensure_that_the_fix_for_=2317269_?= =?utf-8?q?also_works_on_OSX_10=2E4?= Message-ID: <3bTSQM0S8szSZn@mail.python.org> http://hg.python.org/cpython/rev/062f1985a5b7 changeset: 84076:062f1985a5b7 parent: 84073:4a0017722910 parent: 84075:ef103e7e7af2 user: Ronald Oussoren date: Mon Jun 10 10:37:12 2013 +0200 summary: (3.3->default) Ensure that the fix for #17269 also works on OSX 10.4 AI_NUMERICSERV isn't defined on OSX 10.4. files: Lib/test/test_socket.py | 3 ++- Modules/socketmodule.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -1202,7 +1202,8 @@ self.assertRaises(UnicodeEncodeError, socket.getaddrinfo, 'localhost', '\uD800') # Issue 17269 - socket.getaddrinfo("localhost", None, 0, 0, 0, socket.AI_NUMERICSERV) + if hasattr(socket, 'AI_NUMERICSERV'): + socket.getaddrinfo("localhost", None, 0, 0, 0, socket.AI_NUMERICSERV) def test_getnameinfo(self): # only IP addresses are allowed diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -4975,7 +4975,7 @@ PyErr_SetString(PyExc_OSError, "Int or String expected"); goto err; } -#ifdef __APPLE__ +#if defined(__APPLE__) && defined(AI_NUMERICSERV) if ((flags & AI_NUMERICSERV) && (pptr == NULL || (pptr[0] == '0' && pptr[1] == 0))) { /* On OSX upto at least OSX 10.8 getaddrinfo crashes * if AI_NUMERICSERV is set and the servname is NULL or "0". -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 10 10:47:28 2013 From: python-checkins at python.org (christian.heimes) Date: Mon, 10 Jun 2013 10:47:28 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_fixd_refleak?= Message-ID: <3bTSdr5GRqzRNm@mail.python.org> http://hg.python.org/cpython/rev/6860263c05b3 changeset: 84077:6860263c05b3 user: Christian Heimes date: Mon Jun 10 10:47:22 2013 +0200 summary: fixd refleak files: Modules/_ssl.c | 10 +++++++++- 1 files changed, 9 insertions(+), 1 deletions(-) diff --git a/Modules/_ssl.c b/Modules/_ssl.c --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -2775,6 +2775,7 @@ PyObject *ofile = NULL; PyObject *odir_env = NULL; PyObject *odir = NULL; + PyObject *tup = NULL; #define convert(info, target) { \ const char *tmp = (info); \ @@ -2791,7 +2792,14 @@ convert(X509_get_default_cert_dir(), odir); #undef convert - return Py_BuildValue("(OOOO)", ofile_env, ofile, odir_env, odir); + if ((tup = PyTuple_New(4)) == NULL) { + goto error; + } + PyTuple_SET_ITEM(tup, 0, ofile_env); + PyTuple_SET_ITEM(tup, 1, ofile); + PyTuple_SET_ITEM(tup, 2, odir_env); + PyTuple_SET_ITEM(tup, 3, odir); + return tup; error: Py_XDECREF(ofile_env); -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 10 16:46:57 2013 From: python-checkins at python.org (richard.oudkerk) Date: Mon, 10 Jun 2013 16:46:57 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogSXNzdWUgIzE4MTgw?= =?utf-8?q?=3A_Fix_ref_leak_in_=5FPyImport=5FGetDynLoadWindows=28=29=2E?= Message-ID: <3bTccd5hhpzRgM@mail.python.org> http://hg.python.org/cpython/rev/ec854f76d6b9 changeset: 84078:ec854f76d6b9 branch: 3.3 parent: 84075:ef103e7e7af2 user: Richard Oudkerk date: Mon Jun 10 15:38:54 2013 +0100 summary: Issue #18180: Fix ref leak in _PyImport_GetDynLoadWindows(). files: Misc/NEWS | 2 ++ Python/dynload_win.c | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -12,6 +12,8 @@ Core and Builtins ----------------- +- Issue #18180: Fix ref leak in _PyImport_GetDynLoadWindows(). + - Issue #18038: SyntaxError raised during compilation sources with illegal encoding now always contains an encoding name. diff --git a/Python/dynload_win.c b/Python/dynload_win.c --- a/Python/dynload_win.c +++ b/Python/dynload_win.c @@ -262,8 +262,9 @@ theLength)); } if (message != NULL) { - PyErr_SetImportError(message, PyUnicode_FromString(shortname), - pathname); + PyObject *shortname_obj = PyUnicode_FromString(shortname); + PyErr_SetImportError(message, shortname_obj, pathname); + Py_XDECREF(shortname_obj); Py_DECREF(message); } return NULL; -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 10 16:46:59 2013 From: python-checkins at python.org (richard.oudkerk) Date: Mon, 10 Jun 2013 16:46:59 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?b?KTogTWVyZ2Uu?= Message-ID: <3bTccg1pwczS0l@mail.python.org> http://hg.python.org/cpython/rev/df79692a1e7e changeset: 84079:df79692a1e7e parent: 84077:6860263c05b3 parent: 84078:ec854f76d6b9 user: Richard Oudkerk date: Mon Jun 10 15:45:30 2013 +0100 summary: Merge. files: Misc/NEWS | 2 ++ Python/dynload_win.c | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,8 @@ Core and Builtins ----------------- +- Issue #18180: Fix ref leak in _PyImport_GetDynLoadWindows(). + - Issue #18038: SyntaxError raised during compilation sources with illegal encoding now always contains an encoding name. diff --git a/Python/dynload_win.c b/Python/dynload_win.c --- a/Python/dynload_win.c +++ b/Python/dynload_win.c @@ -262,8 +262,9 @@ theLength)); } if (message != NULL) { - PyErr_SetImportError(message, PyUnicode_FromString(shortname), - pathname); + PyObject *shortname_obj = PyUnicode_FromString(shortname); + PyErr_SetImportError(message, shortname_obj, pathname); + Py_XDECREF(shortname_obj); Py_DECREF(message); } return NULL; -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 10 17:33:15 2013 From: python-checkins at python.org (richard.oudkerk) Date: Mon, 10 Jun 2013 17:33:15 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzE4MTc0?= =?utf-8?q?=3A_Fix_fd_leaks_in_tests=2E?= Message-ID: <3bTdf32tGBzPkG@mail.python.org> http://hg.python.org/cpython/rev/a7381fe515e8 changeset: 84080:a7381fe515e8 branch: 2.7 parent: 84074:4d1e4bc6c5b5 user: Richard Oudkerk date: Mon Jun 10 16:27:45 2013 +0100 summary: Issue #18174: Fix fd leaks in tests. files: Lib/test/test_openpty.py | 2 ++ Lib/test/test_subprocess.py | 3 ++- Lib/test/test_uuid.py | 1 + 3 files changed, 5 insertions(+), 1 deletions(-) diff --git a/Lib/test/test_openpty.py b/Lib/test/test_openpty.py --- a/Lib/test/test_openpty.py +++ b/Lib/test/test_openpty.py @@ -10,6 +10,8 @@ class OpenptyTest(unittest.TestCase): def test(self): master, slave = os.openpty() + self.addCleanup(os.close, master) + self.addCleanup(os.close, slave) if not os.isatty(slave): self.fail("Slave-end of pty is not a terminal.") diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -806,7 +806,8 @@ self._testcase.assertNotIn( fd, (p2cwrite, c2pread, errread)) finally: - map(os.close, devzero_fds) + for fd in devzero_fds: + os.close(fd) @unittest.skipIf(not os.path.exists("/dev/zero"), "/dev/zero required.") def test_preexec_errpipe_does_not_double_close_pipes(self): diff --git a/Lib/test/test_uuid.py b/Lib/test/test_uuid.py --- a/Lib/test/test_uuid.py +++ b/Lib/test/test_uuid.py @@ -448,6 +448,7 @@ else: os.close(fds[1]) + self.addCleanup(os.close, fds[0]) parent_value = uuid.uuid4().hex os.waitpid(pid, 0) child_value = os.read(fds[0], 100) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 10 17:33:16 2013 From: python-checkins at python.org (richard.oudkerk) Date: Mon, 10 Jun 2013 17:33:16 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogSXNzdWUgIzE4MTc0?= =?utf-8?q?=3A_Fix_fd_leaks_in_tests=2E?= Message-ID: <3bTdf44yl1zRxV@mail.python.org> http://hg.python.org/cpython/rev/46fe1bb0723c changeset: 84081:46fe1bb0723c branch: 3.3 parent: 84078:ec854f76d6b9 user: Richard Oudkerk date: Mon Jun 10 16:29:19 2013 +0100 summary: Issue #18174: Fix fd leaks in tests. files: Lib/test/test_openpty.py | 2 ++ Lib/test/test_subprocess.py | 3 ++- Lib/test/test_uuid.py | 1 + 3 files changed, 5 insertions(+), 1 deletions(-) diff --git a/Lib/test/test_openpty.py b/Lib/test/test_openpty.py --- a/Lib/test/test_openpty.py +++ b/Lib/test/test_openpty.py @@ -10,6 +10,8 @@ class OpenptyTest(unittest.TestCase): def test(self): master, slave = os.openpty() + self.addCleanup(os.close, master) + self.addCleanup(os.close, slave) if not os.isatty(slave): self.fail("Slave-end of pty is not a terminal.") diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -1263,7 +1263,8 @@ self.stderr.fileno()), msg="At least one fd was closed early.") finally: - map(os.close, devzero_fds) + for fd in devzero_fds: + os.close(fd) @unittest.skipIf(not os.path.exists("/dev/zero"), "/dev/zero required.") def test_preexec_errpipe_does_not_double_close_pipes(self): diff --git a/Lib/test/test_uuid.py b/Lib/test/test_uuid.py --- a/Lib/test/test_uuid.py +++ b/Lib/test/test_uuid.py @@ -458,6 +458,7 @@ else: os.close(fds[1]) + self.addCleanup(os.close, fds[0]) parent_value = uuid.uuid4().hex os.waitpid(pid, 0) child_value = os.read(fds[0], 100).decode('latin-1') -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 10 17:33:18 2013 From: python-checkins at python.org (richard.oudkerk) Date: Mon, 10 Jun 2013 17:33:18 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?b?KTogTWVyZ2Uu?= Message-ID: <3bTdf60kJwzSNX@mail.python.org> http://hg.python.org/cpython/rev/69a165d8dc98 changeset: 84082:69a165d8dc98 parent: 84079:df79692a1e7e parent: 84081:46fe1bb0723c user: Richard Oudkerk date: Mon Jun 10 16:31:39 2013 +0100 summary: Merge. files: Lib/test/test_openpty.py | 2 ++ Lib/test/test_subprocess.py | 3 ++- Lib/test/test_uuid.py | 1 + 3 files changed, 5 insertions(+), 1 deletions(-) diff --git a/Lib/test/test_openpty.py b/Lib/test/test_openpty.py --- a/Lib/test/test_openpty.py +++ b/Lib/test/test_openpty.py @@ -10,6 +10,8 @@ class OpenptyTest(unittest.TestCase): def test(self): master, slave = os.openpty() + self.addCleanup(os.close, master) + self.addCleanup(os.close, slave) if not os.isatty(slave): self.fail("Slave-end of pty is not a terminal.") diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -1296,7 +1296,8 @@ self.stderr.fileno()), msg="At least one fd was closed early.") finally: - map(os.close, devzero_fds) + for fd in devzero_fds: + os.close(fd) @unittest.skipIf(not os.path.exists("/dev/zero"), "/dev/zero required.") def test_preexec_errpipe_does_not_double_close_pipes(self): diff --git a/Lib/test/test_uuid.py b/Lib/test/test_uuid.py --- a/Lib/test/test_uuid.py +++ b/Lib/test/test_uuid.py @@ -458,6 +458,7 @@ else: os.close(fds[1]) + self.addCleanup(os.close, fds[0]) parent_value = uuid.uuid4().hex os.waitpid(pid, 0) child_value = os.read(fds[0], 100).decode('latin-1') -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 10 18:24:13 2013 From: python-checkins at python.org (benjamin.peterson) Date: Mon, 10 Jun 2013 18:24:13 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogcmVtb3ZlIE1BWF9N?= =?utf-8?q?AXCHAR_because_it=27s_unsafe_for_computing_maximum_codepoitn_va?= =?utf-8?q?lue?= Message-ID: <3bTfms1YvnzRnq@mail.python.org> http://hg.python.org/cpython/rev/89b106d298a9 changeset: 84083:89b106d298a9 branch: 3.3 parent: 84081:46fe1bb0723c user: Benjamin Peterson date: Mon Jun 10 09:19:46 2013 -0700 summary: remove MAX_MAXCHAR because it's unsafe for computing maximum codepoitn value (see #18183) files: Lib/test/test_unicode.py | 3 + Misc/NEWS | 3 + Objects/unicodeobject.c | 57 ++++++++++++--------------- 3 files changed, 32 insertions(+), 31 deletions(-) diff --git a/Lib/test/test_unicode.py b/Lib/test/test_unicode.py --- a/Lib/test/test_unicode.py +++ b/Lib/test/test_unicode.py @@ -566,6 +566,9 @@ self.assertEqual('\U0008fffe'.lower(), '\U0008fffe') self.assertEqual('\u2177'.lower(), '\u2177') + # See issue #18183 for this one. + '\U00010000\U00100000'.lower() + def test_casefold(self): self.assertEqual('hello'.casefold(), 'hello') self.assertEqual('hELlo'.casefold(), 'hello') diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -12,6 +12,9 @@ Core and Builtins ----------------- +- Issue #18183: Fix various unicode operations on strings with large unicode + codepoints. + - Issue #18180: Fix ref leak in _PyImport_GetDynLoadWindows(). - Issue #18038: SyntaxError raised during compilation sources with illegal diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -112,11 +112,6 @@ #define _PyUnicode_DATA_ANY(op) \ (((PyUnicodeObject*)(op))->data.any) -/* Optimized version of Py_MAX() to compute the maximum character: - use it when your are computing the second argument of PyUnicode_New() */ -#define MAX_MAXCHAR(maxchar1, maxchar2) \ - ((maxchar1) | (maxchar2)) - #undef PyUnicode_READY #define PyUnicode_READY(op) \ (assert(_PyUnicode_CHECK(op)), \ @@ -2495,7 +2490,7 @@ case 'c': { Py_UCS4 ordinal = va_arg(count, int); - maxchar = MAX_MAXCHAR(maxchar, ordinal); + maxchar = Py_MAX(maxchar, ordinal); n++; break; } @@ -2591,7 +2586,7 @@ /* since PyUnicode_DecodeUTF8 returns already flexible unicode objects, there is no need to call ready on them */ argmaxchar = PyUnicode_MAX_CHAR_VALUE(str); - maxchar = MAX_MAXCHAR(maxchar, argmaxchar); + maxchar = Py_MAX(maxchar, argmaxchar); n += PyUnicode_GET_LENGTH(str); /* Remember the str and switch to the next slot */ *callresult++ = str; @@ -2604,7 +2599,7 @@ if (PyUnicode_READY(obj) == -1) goto fail; argmaxchar = PyUnicode_MAX_CHAR_VALUE(obj); - maxchar = MAX_MAXCHAR(maxchar, argmaxchar); + maxchar = Py_MAX(maxchar, argmaxchar); n += PyUnicode_GET_LENGTH(obj); break; } @@ -2619,7 +2614,7 @@ if (PyUnicode_READY(obj) == -1) goto fail; argmaxchar = PyUnicode_MAX_CHAR_VALUE(obj); - maxchar = MAX_MAXCHAR(maxchar, argmaxchar); + maxchar = Py_MAX(maxchar, argmaxchar); n += PyUnicode_GET_LENGTH(obj); *callresult++ = NULL; } @@ -2632,7 +2627,7 @@ goto fail; } argmaxchar = PyUnicode_MAX_CHAR_VALUE(str_obj); - maxchar = MAX_MAXCHAR(maxchar, argmaxchar); + maxchar = Py_MAX(maxchar, argmaxchar); n += PyUnicode_GET_LENGTH(str_obj); *callresult++ = str_obj; } @@ -2651,7 +2646,7 @@ goto fail; } argmaxchar = PyUnicode_MAX_CHAR_VALUE(str); - maxchar = MAX_MAXCHAR(maxchar, argmaxchar); + maxchar = Py_MAX(maxchar, argmaxchar); n += PyUnicode_GET_LENGTH(str); /* Remember the str and switch to the next slot */ *callresult++ = str; @@ -2670,7 +2665,7 @@ goto fail; } argmaxchar = PyUnicode_MAX_CHAR_VALUE(repr); - maxchar = MAX_MAXCHAR(maxchar, argmaxchar); + maxchar = Py_MAX(maxchar, argmaxchar); n += PyUnicode_GET_LENGTH(repr); /* Remember the repr and switch to the next slot */ *callresult++ = repr; @@ -2689,7 +2684,7 @@ goto fail; } argmaxchar = PyUnicode_MAX_CHAR_VALUE(ascii); - maxchar = MAX_MAXCHAR(maxchar, argmaxchar); + maxchar = Py_MAX(maxchar, argmaxchar); n += PyUnicode_GET_LENGTH(ascii); /* Remember the repr and switch to the next slot */ *callresult++ = ascii; @@ -8628,11 +8623,11 @@ } if (fixed != 0) { modified = 1; - maxchar = MAX_MAXCHAR(maxchar, fixed); + maxchar = Py_MAX(maxchar, fixed); PyUnicode_WRITE(kind, data, i, fixed); } else - maxchar = MAX_MAXCHAR(maxchar, ch); + maxchar = Py_MAX(maxchar, ch); } } @@ -8673,7 +8668,7 @@ int decimal = Py_UNICODE_TODECIMAL(ch); if (decimal >= 0) ch = '0' + decimal; - maxchar = MAX_MAXCHAR(maxchar, ch); + maxchar = Py_MAX(maxchar, ch); } } @@ -8914,7 +8909,7 @@ if (unicode == NULL) { *maxchar = 127; if (len != n_digits) { - *maxchar = MAX_MAXCHAR(*maxchar, + *maxchar = Py_MAX(*maxchar, PyUnicode_MAX_CHAR_VALUE(thousands_sep)); } } @@ -9309,14 +9304,14 @@ c = PyUnicode_READ(kind, data, 0); n_res = _PyUnicode_ToUpperFull(c, mapped); for (j = 0; j < n_res; j++) { - *maxchar = MAX_MAXCHAR(*maxchar, mapped[j]); + *maxchar = Py_MAX(*maxchar, mapped[j]); res[k++] = mapped[j]; } for (i = 1; i < length; i++) { c = PyUnicode_READ(kind, data, i); n_res = lower_ucs4(kind, data, length, i, c, mapped); for (j = 0; j < n_res; j++) { - *maxchar = MAX_MAXCHAR(*maxchar, mapped[j]); + *maxchar = Py_MAX(*maxchar, mapped[j]); res[k++] = mapped[j]; } } @@ -9341,7 +9336,7 @@ mapped[0] = c; } for (j = 0; j < n_res; j++) { - *maxchar = MAX_MAXCHAR(*maxchar, mapped[j]); + *maxchar = Py_MAX(*maxchar, mapped[j]); res[k++] = mapped[j]; } } @@ -9362,7 +9357,7 @@ else n_res = _PyUnicode_ToUpperFull(c, mapped); for (j = 0; j < n_res; j++) { - *maxchar = MAX_MAXCHAR(*maxchar, mapped[j]); + *maxchar = Py_MAX(*maxchar, mapped[j]); res[k++] = mapped[j]; } } @@ -9391,7 +9386,7 @@ Py_UCS4 mapped[3]; int j, n_res = _PyUnicode_ToFoldedFull(c, mapped); for (j = 0; j < n_res; j++) { - *maxchar = MAX_MAXCHAR(*maxchar, mapped[j]); + *maxchar = Py_MAX(*maxchar, mapped[j]); res[k++] = mapped[j]; } } @@ -9416,7 +9411,7 @@ n_res = _PyUnicode_ToTitleFull(c, mapped); for (j = 0; j < n_res; j++) { - *maxchar = MAX_MAXCHAR(*maxchar, mapped[j]); + *maxchar = Py_MAX(*maxchar, mapped[j]); res[k++] = mapped[j]; } @@ -9571,7 +9566,7 @@ goto onError; sz += PyUnicode_GET_LENGTH(item); item_maxchar = PyUnicode_MAX_CHAR_VALUE(item); - maxchar = MAX_MAXCHAR(maxchar, item_maxchar); + maxchar = Py_MAX(maxchar, item_maxchar); if (i != 0) sz += seplen; if (sz < old_sz || sz > PY_SSIZE_T_MAX) { @@ -9747,7 +9742,7 @@ return NULL; } maxchar = PyUnicode_MAX_CHAR_VALUE(self); - maxchar = MAX_MAXCHAR(maxchar, fill); + maxchar = Py_MAX(maxchar, fill); u = PyUnicode_New(left + _PyUnicode_LENGTH(self) + right, maxchar); if (!u) return NULL; @@ -10061,7 +10056,7 @@ /* Replacing str1 with str2 may cause a maxchar reduction in the result string. */ mayshrink = (maxchar_str2 < maxchar); - maxchar = MAX_MAXCHAR(maxchar, maxchar_str2); + maxchar = Py_MAX(maxchar, maxchar_str2); if (len1 == len2) { /* same length */ @@ -10647,7 +10642,7 @@ maxchar = PyUnicode_MAX_CHAR_VALUE(u); maxchar2 = PyUnicode_MAX_CHAR_VALUE(v); - maxchar = MAX_MAXCHAR(maxchar, maxchar2); + maxchar = Py_MAX(maxchar, maxchar2); /* Concat the two Unicode strings */ w = PyUnicode_New(new_len, maxchar); @@ -10734,7 +10729,7 @@ else { maxchar = PyUnicode_MAX_CHAR_VALUE(left); maxchar2 = PyUnicode_MAX_CHAR_VALUE(right); - maxchar = MAX_MAXCHAR(maxchar, maxchar2); + maxchar = Py_MAX(maxchar, maxchar2); /* Concat the two Unicode strings */ res = PyUnicode_New(new_len, maxchar); @@ -13846,15 +13841,15 @@ if (!(flags & F_LJUST)) { if (sign) { if ((width-1) > len) - bufmaxchar = MAX_MAXCHAR(bufmaxchar, fill); + bufmaxchar = Py_MAX(bufmaxchar, fill); } else { if (width > len) - bufmaxchar = MAX_MAXCHAR(bufmaxchar, fill); + bufmaxchar = Py_MAX(bufmaxchar, fill); } } maxchar = _PyUnicode_FindMaxChar(temp, 0, pindex+len); - bufmaxchar = MAX_MAXCHAR(bufmaxchar, maxchar); + bufmaxchar = Py_MAX(bufmaxchar, maxchar); buflen = width; if (sign && len == width) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 10 18:24:14 2013 From: python-checkins at python.org (benjamin.peterson) Date: Mon, 10 Jun 2013 18:24:14 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?b?KTogbWVyZ2UgMy4zICgjMTgxODMp?= Message-ID: <3bTfmt4zGMzSNX@mail.python.org> http://hg.python.org/cpython/rev/668aba845fb2 changeset: 84084:668aba845fb2 parent: 84082:69a165d8dc98 parent: 84083:89b106d298a9 user: Benjamin Peterson date: Mon Jun 10 09:24:01 2013 -0700 summary: merge 3.3 (#18183) files: Lib/test/test_unicode.py | 3 + Misc/NEWS | 3 + Objects/unicodeobject.c | 43 ++++++++++++--------------- 3 files changed, 25 insertions(+), 24 deletions(-) diff --git a/Lib/test/test_unicode.py b/Lib/test/test_unicode.py --- a/Lib/test/test_unicode.py +++ b/Lib/test/test_unicode.py @@ -577,6 +577,9 @@ self.assertEqual('\U0008fffe'.lower(), '\U0008fffe') self.assertEqual('\u2177'.lower(), '\u2177') + # See issue #18183 for this one. + '\U00010000\U00100000'.lower() + def test_casefold(self): self.assertEqual('hello'.casefold(), 'hello') self.assertEqual('hELlo'.casefold(), 'hello') diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,9 @@ Core and Builtins ----------------- +- Issue #18183: Fix various unicode operations on strings with large unicode + codepoints. + - Issue #18180: Fix ref leak in _PyImport_GetDynLoadWindows(). - Issue #18038: SyntaxError raised during compilation sources with illegal diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -104,11 +104,6 @@ #define _PyUnicode_DATA_ANY(op) \ (((PyUnicodeObject*)(op))->data.any) -/* Optimized version of Py_MAX() to compute the maximum character: - use it when your are computing the second argument of PyUnicode_New() */ -#define MAX_MAXCHAR(maxchar1, maxchar2) \ - ((maxchar1) | (maxchar2)) - #undef PyUnicode_READY #define PyUnicode_READY(op) \ (assert(_PyUnicode_CHECK(op)), \ @@ -8609,11 +8604,11 @@ } if (fixed != 0) { modified = 1; - maxchar = MAX_MAXCHAR(maxchar, fixed); + maxchar = Py_MAX(maxchar, fixed); PyUnicode_WRITE(kind, data, i, fixed); } else - maxchar = MAX_MAXCHAR(maxchar, ch); + maxchar = Py_MAX(maxchar, ch); } } @@ -8654,7 +8649,7 @@ int decimal = Py_UNICODE_TODECIMAL(ch); if (decimal >= 0) ch = '0' + decimal; - maxchar = MAX_MAXCHAR(maxchar, ch); + maxchar = Py_MAX(maxchar, ch); } } @@ -8895,7 +8890,7 @@ if (unicode == NULL) { *maxchar = 127; if (len != n_digits) { - *maxchar = MAX_MAXCHAR(*maxchar, + *maxchar = Py_MAX(*maxchar, PyUnicode_MAX_CHAR_VALUE(thousands_sep)); } } @@ -9289,14 +9284,14 @@ c = PyUnicode_READ(kind, data, 0); n_res = _PyUnicode_ToUpperFull(c, mapped); for (j = 0; j < n_res; j++) { - *maxchar = MAX_MAXCHAR(*maxchar, mapped[j]); + *maxchar = Py_MAX(*maxchar, mapped[j]); res[k++] = mapped[j]; } for (i = 1; i < length; i++) { c = PyUnicode_READ(kind, data, i); n_res = lower_ucs4(kind, data, length, i, c, mapped); for (j = 0; j < n_res; j++) { - *maxchar = MAX_MAXCHAR(*maxchar, mapped[j]); + *maxchar = Py_MAX(*maxchar, mapped[j]); res[k++] = mapped[j]; } } @@ -9321,7 +9316,7 @@ mapped[0] = c; } for (j = 0; j < n_res; j++) { - *maxchar = MAX_MAXCHAR(*maxchar, mapped[j]); + *maxchar = Py_MAX(*maxchar, mapped[j]); res[k++] = mapped[j]; } } @@ -9342,7 +9337,7 @@ else n_res = _PyUnicode_ToUpperFull(c, mapped); for (j = 0; j < n_res; j++) { - *maxchar = MAX_MAXCHAR(*maxchar, mapped[j]); + *maxchar = Py_MAX(*maxchar, mapped[j]); res[k++] = mapped[j]; } } @@ -9371,7 +9366,7 @@ Py_UCS4 mapped[3]; int j, n_res = _PyUnicode_ToFoldedFull(c, mapped); for (j = 0; j < n_res; j++) { - *maxchar = MAX_MAXCHAR(*maxchar, mapped[j]); + *maxchar = Py_MAX(*maxchar, mapped[j]); res[k++] = mapped[j]; } } @@ -9396,7 +9391,7 @@ n_res = _PyUnicode_ToTitleFull(c, mapped); for (j = 0; j < n_res; j++) { - *maxchar = MAX_MAXCHAR(*maxchar, mapped[j]); + *maxchar = Py_MAX(*maxchar, mapped[j]); res[k++] = mapped[j]; } @@ -9551,7 +9546,7 @@ goto onError; sz += PyUnicode_GET_LENGTH(item); item_maxchar = PyUnicode_MAX_CHAR_VALUE(item); - maxchar = MAX_MAXCHAR(maxchar, item_maxchar); + maxchar = Py_MAX(maxchar, item_maxchar); if (i != 0) sz += seplen; if (sz < old_sz || sz > PY_SSIZE_T_MAX) { @@ -9735,7 +9730,7 @@ return NULL; } maxchar = PyUnicode_MAX_CHAR_VALUE(self); - maxchar = MAX_MAXCHAR(maxchar, fill); + maxchar = Py_MAX(maxchar, fill); u = PyUnicode_New(left + _PyUnicode_LENGTH(self) + right, maxchar); if (!u) return NULL; @@ -10075,7 +10070,7 @@ /* Replacing str1 with str2 may cause a maxchar reduction in the result string. */ mayshrink = (maxchar_str2 < maxchar_str1) && (maxchar == maxchar_str1); - maxchar = MAX_MAXCHAR(maxchar, maxchar_str2); + maxchar = Py_MAX(maxchar, maxchar_str2); if (len1 == len2) { /* same length */ @@ -10749,7 +10744,7 @@ maxchar = PyUnicode_MAX_CHAR_VALUE(u); maxchar2 = PyUnicode_MAX_CHAR_VALUE(v); - maxchar = MAX_MAXCHAR(maxchar, maxchar2); + maxchar = Py_MAX(maxchar, maxchar2); /* Concat the two Unicode strings */ w = PyUnicode_New(new_len, maxchar); @@ -10831,7 +10826,7 @@ else { maxchar = PyUnicode_MAX_CHAR_VALUE(left); maxchar2 = PyUnicode_MAX_CHAR_VALUE(right); - maxchar = MAX_MAXCHAR(maxchar, maxchar2); + maxchar = Py_MAX(maxchar, maxchar2); /* Concat the two Unicode strings */ res = PyUnicode_New(new_len, maxchar); @@ -12993,7 +12988,7 @@ } newlen = writer->pos + length; - maxchar = MAX_MAXCHAR(maxchar, writer->min_char); + maxchar = Py_MAX(maxchar, writer->min_char); if (writer->buffer == NULL) { assert(!writer->readonly); @@ -14133,16 +14128,16 @@ if (!(arg->flags & F_LJUST)) { if (arg->sign) { if ((arg->width-1) > len) - maxchar = MAX_MAXCHAR(maxchar, fill); + maxchar = Py_MAX(maxchar, fill); } else { if (arg->width > len) - maxchar = MAX_MAXCHAR(maxchar, fill); + maxchar = Py_MAX(maxchar, fill); } } if (PyUnicode_MAX_CHAR_VALUE(str) > maxchar) { Py_UCS4 strmaxchar = _PyUnicode_FindMaxChar(str, 0, pindex+len); - maxchar = MAX_MAXCHAR(maxchar, strmaxchar); + maxchar = Py_MAX(maxchar, strmaxchar); } buflen = arg->width; -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Tue Jun 11 05:50:55 2013 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Tue, 11 Jun 2013 05:50:55 +0200 Subject: [Python-checkins] Daily reference leaks (668aba845fb2): sum=0 Message-ID: results for 668aba845fb2 on branch "default" -------------------------------------------- Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflog2wxycP', '-x'] From python-checkins at python.org Tue Jun 11 06:08:17 2013 From: python-checkins at python.org (roger.serwy) Date: Tue, 11 Jun 2013 06:08:17 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogIzE3NTExOiBLZWVw?= =?utf-8?q?_IDLE_find_dialog_open_after_clicking_=22Find_Next=22=2E?= Message-ID: <3bTyPF5GZPzQqp@mail.python.org> http://hg.python.org/cpython/rev/73de0794ae71 changeset: 84085:73de0794ae71 branch: 2.7 parent: 84080:a7381fe515e8 user: Roger Serwy date: Mon Jun 10 23:01:20 2013 -0500 summary: #17511: Keep IDLE find dialog open after clicking "Find Next". Original patch by Sarah K. files: Lib/idlelib/SearchDialog.py | 5 ++--- Misc/ACKS | 1 + Misc/NEWS | 3 +++ 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Lib/idlelib/SearchDialog.py b/Lib/idlelib/SearchDialog.py --- a/Lib/idlelib/SearchDialog.py +++ b/Lib/idlelib/SearchDialog.py @@ -24,13 +24,12 @@ def create_widgets(self): f = SearchDialogBase.create_widgets(self) - self.make_button("Find", self.default_command, 1) + self.make_button("Find Next", self.default_command, 1) def default_command(self, event=None): if not self.engine.getprog(): return - if self.find_again(self.text): - self.close() + self.find_again(self.text) def find_again(self, text): if not self.engine.getpat(): diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -502,6 +502,7 @@ Sijin Joseph Andreas Jung Tattoo Mabonzo K. +Sarah K. Bohuslav Kabrda Bob Kahn Kurt B. Kaiser diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -50,6 +50,9 @@ IDLE ---- +- Issue #17511: Keep IDLE find dialog open after clicking "Find Next". + Original patch by Sarah K. + - Issue #15392: Create a unittest framework for IDLE. Preliminary patch by Rajagopalasarma Jayakrishnan -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 11 06:08:19 2013 From: python-checkins at python.org (roger.serwy) Date: Tue, 11 Jun 2013 06:08:19 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogIzE3NTExOiBLZWVw?= =?utf-8?q?_IDLE_find_dialog_open_after_clicking_=22Find_Next=22=2E?= Message-ID: <3bTyPH0H78zRg8@mail.python.org> http://hg.python.org/cpython/rev/dac11f143581 changeset: 84086:dac11f143581 branch: 3.3 parent: 84083:89b106d298a9 user: Roger Serwy date: Mon Jun 10 23:01:20 2013 -0500 summary: #17511: Keep IDLE find dialog open after clicking "Find Next". Original patch by Sarah K. files: Lib/idlelib/SearchDialog.py | 5 ++--- Misc/ACKS | 1 + Misc/NEWS | 3 +++ 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Lib/idlelib/SearchDialog.py b/Lib/idlelib/SearchDialog.py --- a/Lib/idlelib/SearchDialog.py +++ b/Lib/idlelib/SearchDialog.py @@ -24,13 +24,12 @@ def create_widgets(self): f = SearchDialogBase.create_widgets(self) - self.make_button("Find", self.default_command, 1) + self.make_button("Find Next", self.default_command, 1) def default_command(self, event=None): if not self.engine.getprog(): return - if self.find_again(self.text): - self.close() + self.find_again(self.text) def find_again(self, text): if not self.engine.getpat(): diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -606,6 +606,7 @@ Sijin Joseph Andreas Jung Tattoo Mabonzo K. +Sarah K. Bohuslav Kabrda Bob Kahn Kurt B. Kaiser diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -81,6 +81,9 @@ IDLE ---- +- Issue #17511: Keep IDLE find dialog open after clicking "Find Next". + Original patch by Sarah K. + - Issue #18055: Move IDLE off of imp and on to importlib. - Issue #15392: Create a unittest framework for IDLE. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 11 06:08:20 2013 From: python-checkins at python.org (roger.serwy) Date: Tue, 11 Jun 2013 06:08:20 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?b?KTogIzE3NTExOiBtZXJnZSB3aXRoIDMuMy4=?= Message-ID: <3bTyPJ29HqzSVJ@mail.python.org> http://hg.python.org/cpython/rev/67714c4d9725 changeset: 84087:67714c4d9725 parent: 84084:668aba845fb2 parent: 84086:dac11f143581 user: Roger Serwy date: Mon Jun 10 23:02:56 2013 -0500 summary: #17511: merge with 3.3. files: Lib/idlelib/SearchDialog.py | 5 ++--- Misc/ACKS | 1 + Misc/NEWS | 3 +++ 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Lib/idlelib/SearchDialog.py b/Lib/idlelib/SearchDialog.py --- a/Lib/idlelib/SearchDialog.py +++ b/Lib/idlelib/SearchDialog.py @@ -24,13 +24,12 @@ def create_widgets(self): f = SearchDialogBase.create_widgets(self) - self.make_button("Find", self.default_command, 1) + self.make_button("Find Next", self.default_command, 1) def default_command(self, event=None): if not self.engine.getprog(): return - if self.find_again(self.text): - self.close() + self.find_again(self.text) def find_again(self, text): if not self.engine.getpat(): diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -617,6 +617,7 @@ Sijin Joseph Andreas Jung Tattoo Mabonzo K. +Sarah K. Bohuslav Kabrda Alexey Kachayev Bob Kahn diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -453,6 +453,9 @@ IDLE ---- +- Issue #17511: Keep IDLE find dialog open after clicking "Find Next". + Original patch by Sarah K. + - Issue #18055: Move IDLE off of imp and on to importlib. - Issue #15392: Create a unittest framework for IDLE. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 11 23:09:53 2013 From: python-checkins at python.org (brett.cannon) Date: Tue, 11 Jun 2013 23:09:53 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2318157=3A_stop_usi?= =?utf-8?q?ng_imp=2Eload=5Fmodule=28=29_in_imp=2E?= Message-ID: <3bVP414Gm2z7Ljk@mail.python.org> http://hg.python.org/cpython/rev/2c747be20d05 changeset: 84088:2c747be20d05 user: Brett Cannon date: Tue Jun 11 17:09:36 2013 -0400 summary: Issue #18157: stop using imp.load_module() in imp. files: Lib/pydoc.py | 24 ++++++++++++------------ Misc/NEWS | 2 ++ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/Lib/pydoc.py b/Lib/pydoc.py --- a/Lib/pydoc.py +++ b/Lib/pydoc.py @@ -53,6 +53,7 @@ import builtins import imp +import importlib._bootstrap import importlib.machinery import inspect import io @@ -269,18 +270,17 @@ """Import a Python source file or compiled file given its path.""" magic = imp.get_magic() with open(path, 'rb') as file: - if file.read(len(magic)) == magic: - kind = imp.PY_COMPILED - else: - kind = imp.PY_SOURCE - file.seek(0) - filename = os.path.basename(path) - name, ext = os.path.splitext(filename) - try: - module = imp.load_module(name, file, path, (ext, 'r', kind)) - except: - raise ErrorDuringImport(path, sys.exc_info()) - return module + is_bytecode = magic == file.read(len(magic)) + filename = os.path.basename(path) + name, ext = os.path.splitext(filename) + if is_bytecode: + loader = importlib._bootstrap.SourcelessFileLoader(name, path) + else: + loader = importlib._bootstrap.SourceFileLoader(name, path) + try: + return loader.load_module(name) + except: + raise ErrorDuringImport(path, sys.exc_info()) def safeimport(path, forceload=0, cache={}): """Import a module; handle errors; return None if the module isn't found. diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -120,6 +120,8 @@ Library ------- +- Issue #18157: Stop using imp.load_module() in pydoc. + - Issue #16102: Make uuid._netbios_getnode() work again on Python 3. - Issue #17134: Add ssl.enum_cert_store() as interface to Windows' cert store. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 11 23:12:39 2013 From: python-checkins at python.org (brett.cannon) Date: Tue, 11 Jun 2013 23:12:39 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2318158=3A_delete_t?= =?utf-8?q?est=5Fimporthooks=2E_Redundant_in_the_face_of?= Message-ID: <3bVP7C6Rjcz7LjS@mail.python.org> http://hg.python.org/cpython/rev/176fe1f8496f changeset: 84089:176fe1f8496f user: Brett Cannon date: Tue Jun 11 17:12:30 2013 -0400 summary: Issue #18158: delete test_importhooks. Redundant in the face of test_importlib. files: Lib/test/test_importhooks.py | 250 ----------------------- 1 files changed, 0 insertions(+), 250 deletions(-) diff --git a/Lib/test/test_importhooks.py b/Lib/test/test_importhooks.py deleted file mode 100644 --- a/Lib/test/test_importhooks.py +++ /dev/null @@ -1,250 +0,0 @@ -import sys -import imp -import os -import unittest -from test import support - - -test_src = """\ -def get_name(): - return __name__ -def get_file(): - return __file__ -""" - -absimp = "import sub\n" -relimp = "from . import sub\n" -deeprelimp = "from .... import sub\n" -futimp = "from __future__ import absolute_import\n" - -reload_src = test_src+"""\ -reloaded = True -""" - -test_co = compile(test_src, "", "exec") -reload_co = compile(reload_src, "", "exec") - -test2_oldabs_co = compile(absimp + test_src, "", "exec") -test2_newabs_co = compile(futimp + absimp + test_src, "", "exec") -test2_newrel_co = compile(relimp + test_src, "", "exec") -test2_deeprel_co = compile(deeprelimp + test_src, "", "exec") -test2_futrel_co = compile(futimp + relimp + test_src, "", "exec") - -test_path = "!!!_test_!!!" - - -class TestImporter: - - modules = { - "hooktestmodule": (False, test_co), - "hooktestpackage": (True, test_co), - "hooktestpackage.sub": (True, test_co), - "hooktestpackage.sub.subber": (True, test_co), - "hooktestpackage.oldabs": (False, test2_oldabs_co), - "hooktestpackage.newabs": (False, test2_newabs_co), - "hooktestpackage.newrel": (False, test2_newrel_co), - "hooktestpackage.sub.subber.subest": (True, test2_deeprel_co), - "hooktestpackage.futrel": (False, test2_futrel_co), - "sub": (False, test_co), - "reloadmodule": (False, test_co), - } - - def __init__(self, path=test_path): - if path != test_path: - # if our class is on sys.path_hooks, we must raise - # ImportError for any path item that we can't handle. - raise ImportError - self.path = path - - def _get__path__(self): - raise NotImplementedError - - def find_module(self, fullname, path=None): - if fullname in self.modules: - return self - else: - return None - - def load_module(self, fullname): - ispkg, code = self.modules[fullname] - mod = sys.modules.setdefault(fullname,imp.new_module(fullname)) - mod.__file__ = "<%s>" % self.__class__.__name__ - mod.__loader__ = self - if ispkg: - mod.__path__ = self._get__path__() - exec(code, mod.__dict__) - return mod - - -class MetaImporter(TestImporter): - def _get__path__(self): - return [] - -class PathImporter(TestImporter): - def _get__path__(self): - return [self.path] - - -class ImportBlocker: - """Place an ImportBlocker instance on sys.meta_path and you - can be sure the modules you specified can't be imported, even - if it's a builtin.""" - def __init__(self, *namestoblock): - self.namestoblock = dict.fromkeys(namestoblock) - def find_module(self, fullname, path=None): - if fullname in self.namestoblock: - return self - return None - def load_module(self, fullname): - raise ImportError("I dare you") - - -class ImpWrapper: - - def __init__(self, path=None): - if path is not None and not os.path.isdir(path): - raise ImportError - self.path = path - - def find_module(self, fullname, path=None): - subname = fullname.split(".")[-1] - if subname != fullname and self.path is None: - return None - if self.path is None: - path = None - else: - path = [self.path] - try: - file, filename, stuff = imp.find_module(subname, path) - except ImportError: - return None - return ImpLoader(file, filename, stuff) - - -class ImpLoader: - - def __init__(self, file, filename, stuff): - self.file = file - self.filename = filename - self.stuff = stuff - - def load_module(self, fullname): - mod = imp.load_module(fullname, self.file, self.filename, self.stuff) - if self.file: - self.file.close() - mod.__loader__ = self # for introspection - return mod - - -class ImportHooksBaseTestCase(unittest.TestCase): - - def setUp(self): - self.path = sys.path[:] - self.meta_path = sys.meta_path[:] - self.path_hooks = sys.path_hooks[:] - sys.path_importer_cache.clear() - self.modules_before = support.modules_setup() - - def tearDown(self): - sys.path[:] = self.path - sys.meta_path[:] = self.meta_path - sys.path_hooks[:] = self.path_hooks - sys.path_importer_cache.clear() - support.modules_cleanup(*self.modules_before) - - -class ImportHooksTestCase(ImportHooksBaseTestCase): - - def doTestImports(self, importer=None): - import hooktestmodule - import hooktestpackage - import hooktestpackage.sub - import hooktestpackage.sub.subber - self.assertEqual(hooktestmodule.get_name(), - "hooktestmodule") - self.assertEqual(hooktestpackage.get_name(), - "hooktestpackage") - self.assertEqual(hooktestpackage.sub.get_name(), - "hooktestpackage.sub") - self.assertEqual(hooktestpackage.sub.subber.get_name(), - "hooktestpackage.sub.subber") - if importer: - self.assertEqual(hooktestmodule.__loader__, importer) - self.assertEqual(hooktestpackage.__loader__, importer) - self.assertEqual(hooktestpackage.sub.__loader__, importer) - self.assertEqual(hooktestpackage.sub.subber.__loader__, importer) - - TestImporter.modules['reloadmodule'] = (False, test_co) - import reloadmodule - self.assertFalse(hasattr(reloadmodule,'reloaded')) - - import hooktestpackage.newrel - self.assertEqual(hooktestpackage.newrel.get_name(), - "hooktestpackage.newrel") - self.assertEqual(hooktestpackage.newrel.sub, - hooktestpackage.sub) - - import hooktestpackage.sub.subber.subest as subest - self.assertEqual(subest.get_name(), - "hooktestpackage.sub.subber.subest") - self.assertEqual(subest.sub, - hooktestpackage.sub) - - import hooktestpackage.futrel - self.assertEqual(hooktestpackage.futrel.get_name(), - "hooktestpackage.futrel") - self.assertEqual(hooktestpackage.futrel.sub, - hooktestpackage.sub) - - import sub - self.assertEqual(sub.get_name(), "sub") - - import hooktestpackage.oldabs - self.assertEqual(hooktestpackage.oldabs.get_name(), - "hooktestpackage.oldabs") - self.assertEqual(hooktestpackage.oldabs.sub, sub) - - import hooktestpackage.newabs - self.assertEqual(hooktestpackage.newabs.get_name(), - "hooktestpackage.newabs") - self.assertEqual(hooktestpackage.newabs.sub, sub) - - def testMetaPath(self): - i = MetaImporter() - sys.meta_path.append(i) - self.doTestImports(i) - - def testPathHook(self): - sys.path_hooks.insert(0, PathImporter) - sys.path.append(test_path) - self.doTestImports() - - def testBlocker(self): - mname = "exceptions" # an arbitrary harmless builtin module - support.unload(mname) - sys.meta_path.append(ImportBlocker(mname)) - self.assertRaises(ImportError, __import__, mname) - - def testImpWrapper(self): - i = ImpWrapper() - sys.meta_path.append(i) - sys.path_hooks.insert(0, ImpWrapper) - mnames = ( - "colorsys", "urllib.parse", "distutils.core", "sys", - ) - for mname in mnames: - parent = mname.split(".")[0] - for n in list(sys.modules): - if n.startswith(parent): - del sys.modules[n] - for mname in mnames: - m = __import__(mname, globals(), locals(), ["__dummy__"]) - # to make sure we actually handled the import - self.assertTrue(hasattr(m, "__loader__")) - - -def test_main(): - support.run_unittest(ImportHooksTestCase) - -if __name__ == "__main__": - test_main() -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 11 23:22:46 2013 From: python-checkins at python.org (brett.cannon) Date: Tue, 11 Jun 2013 23:22:46 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_typo_fix?= Message-ID: <3bVPLt3Wg3z7LkH@mail.python.org> http://hg.python.org/cpython/rev/32527aa9ad16 changeset: 84090:32527aa9ad16 user: Brett Cannon date: Tue Jun 11 17:22:39 2013 -0400 summary: typo fix files: Lib/importlib/_bootstrap.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py --- a/Lib/importlib/_bootstrap.py +++ b/Lib/importlib/_bootstrap.py @@ -376,7 +376,7 @@ # keyword-only defaults) # Python 3.4a1 3260 (add LOAD_CLASSDEREF; allow locals of class to override # free vars) -# Python 3.4a1 3270 (various tweaks to the __class_ closure) +# Python 3.4a1 3270 (various tweaks to the __class__ closure) # Python 3.4a1 3280 (remove implicit class argument) # # MAGIC must change whenever the bytecode emitted by the compiler may no -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 11 23:34:10 2013 From: python-checkins at python.org (brett.cannon) Date: Tue, 11 Jun 2013 23:34:10 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_explanatory_comment?= Message-ID: <3bVPc26Jsnz7Lk0@mail.python.org> http://hg.python.org/cpython/rev/d4ee62a57a75 changeset: 84091:d4ee62a57a75 user: Brett Cannon date: Tue Jun 11 17:34:04 2013 -0400 summary: explanatory comment files: Lib/importlib/_bootstrap.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py --- a/Lib/importlib/_bootstrap.py +++ b/Lib/importlib/_bootstrap.py @@ -384,7 +384,7 @@ # due to the addition of new opcodes). _MAGIC_BYTES = (3280).to_bytes(2, 'little') + b'\r\n' -_RAW_MAGIC_NUMBER = int.from_bytes(_MAGIC_BYTES, 'little') +_RAW_MAGIC_NUMBER = int.from_bytes(_MAGIC_BYTES, 'little') # For import.c _PYCACHE = '__pycache__' -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 11 23:40:53 2013 From: python-checkins at python.org (ned.deily) Date: Tue, 11 Jun 2013 23:40:53 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogSXNzdWUgIzE4MTg3?= =?utf-8?q?=3A_Fix_broken_link_in_venv_documentation=2E_Patch_by_Berker_Pe?= =?utf-8?q?ksag=2E?= Message-ID: <3bVPln6qmtz7LkD@mail.python.org> http://hg.python.org/cpython/rev/b1eeda9db91d changeset: 84092:b1eeda9db91d branch: 3.3 parent: 84086:dac11f143581 user: Ned Deily date: Tue Jun 11 14:38:39 2013 -0700 summary: Issue #18187: Fix broken link in venv documentation. Patch by Berker Peksag. files: Doc/library/venv.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/library/venv.rst b/Doc/library/venv.rst --- a/Doc/library/venv.rst +++ b/Doc/library/venv.rst @@ -11,7 +11,7 @@ .. versionadded:: 3.3 -**Source code:** :source:`Lib/venv.py` +**Source code:** :source:`Lib/venv` -------------- -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 11 23:40:55 2013 From: python-checkins at python.org (ned.deily) Date: Tue, 11 Jun 2013 23:40:55 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2318187=3A_merge_from_3=2E3?= Message-ID: <3bVPlq1bXLz7LkP@mail.python.org> http://hg.python.org/cpython/rev/e6fc120012e5 changeset: 84093:e6fc120012e5 parent: 84091:d4ee62a57a75 parent: 84092:b1eeda9db91d user: Ned Deily date: Tue Jun 11 14:40:23 2013 -0700 summary: Issue #18187: merge from 3.3 files: Doc/library/venv.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/library/venv.rst b/Doc/library/venv.rst --- a/Doc/library/venv.rst +++ b/Doc/library/venv.rst @@ -11,7 +11,7 @@ .. versionadded:: 3.3 -**Source code:** :source:`Lib/venv.py` +**Source code:** :source:`Lib/venv` -------------- -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 12 00:01:19 2013 From: python-checkins at python.org (ned.deily) Date: Wed, 12 Jun 2013 00:01:19 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzE4MTg2?= =?utf-8?q?=3A_remove_obsolete_2=2E2_compatibility_comment=2E?= Message-ID: <3bVQCM3fSkzN1w@mail.python.org> http://hg.python.org/cpython/rev/4b2fdd4dd700 changeset: 84094:4b2fdd4dd700 branch: 2.7 parent: 84085:73de0794ae71 user: Ned Deily date: Tue Jun 11 15:00:21 2013 -0700 summary: Issue #18186: remove obsolete 2.2 compatibility comment. files: Lib/subprocess.py | 2 -- 1 files changed, 0 insertions(+), 2 deletions(-) diff --git a/Lib/subprocess.py b/Lib/subprocess.py --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -2,8 +2,6 @@ # # For more information about this module, see PEP 324. # -# This module should remain compatible with Python 2.2, see PEP 291. -# # Copyright (c) 2003-2005 by Peter Astrand # # Licensed to PSF under a Contributor Agreement. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 12 05:13:36 2013 From: python-checkins at python.org (roger.serwy) Date: Wed, 12 Jun 2013 05:13:36 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogIzU0OTI6IEF2b2lk?= =?utf-8?q?_traceback_when_exiting_IDLE_caused_by_a_race_condition=2E?= Message-ID: <3bVY7h4VqJzQJ4@mail.python.org> http://hg.python.org/cpython/rev/ca8e86711403 changeset: 84095:ca8e86711403 branch: 2.7 user: Roger Serwy date: Tue Jun 11 22:13:17 2013 -0500 summary: #5492: Avoid traceback when exiting IDLE caused by a race condition. files: Lib/idlelib/PyShell.py | 11 +++++------ Misc/NEWS | 2 ++ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Lib/idlelib/PyShell.py b/Lib/idlelib/PyShell.py --- a/Lib/idlelib/PyShell.py +++ b/Lib/idlelib/PyShell.py @@ -370,6 +370,7 @@ self.port = PORT self.original_compiler_flags = self.compile.compiler.flags + _afterid = None rpcclt = None rpcpid = None @@ -497,6 +498,8 @@ threading.Thread(target=self.__request_interrupt).start() def kill_subprocess(self): + if self._afterid is not None: + self.tkconsole.text.after_cancel(self._afterid) try: self.rpcclt.close() except AttributeError: # no socket @@ -569,8 +572,8 @@ pass # Reschedule myself if not self.tkconsole.closing: - self.tkconsole.text.after(self.tkconsole.pollinterval, - self.poll_subprocess) + self._afterid = self.tkconsole.text.after( + self.tkconsole.pollinterval, self.poll_subprocess) debugger = None @@ -987,10 +990,6 @@ self.stop_readline() self.canceled = True self.closing = True - # Wait for poll_subprocess() rescheduling to stop - self.text.after(2 * self.pollinterval, self.close2) - - def close2(self): return EditorWindow.close(self) def _close(self): diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -50,6 +50,8 @@ IDLE ---- +- Issue #5492: Avoid traceback when exiting IDLE caused by a race condition. + - Issue #17511: Keep IDLE find dialog open after clicking "Find Next". Original patch by Sarah K. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 12 05:13:38 2013 From: python-checkins at python.org (roger.serwy) Date: Wed, 12 Jun 2013 05:13:38 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogIzU0OTI6IEF2b2lk?= =?utf-8?q?_traceback_when_exiting_IDLE_caused_by_a_race_condition=2E?= Message-ID: <3bVY7k0tLQzQlP@mail.python.org> http://hg.python.org/cpython/rev/da852f5250af changeset: 84096:da852f5250af branch: 3.3 parent: 84092:b1eeda9db91d user: Roger Serwy date: Tue Jun 11 22:13:17 2013 -0500 summary: #5492: Avoid traceback when exiting IDLE caused by a race condition. files: Lib/idlelib/PyShell.py | 11 +++++------ Misc/NEWS | 2 ++ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Lib/idlelib/PyShell.py b/Lib/idlelib/PyShell.py --- a/Lib/idlelib/PyShell.py +++ b/Lib/idlelib/PyShell.py @@ -367,6 +367,7 @@ self.port = PORT self.original_compiler_flags = self.compile.compiler.flags + _afterid = None rpcclt = None rpcsubproc = None @@ -486,6 +487,8 @@ threading.Thread(target=self.__request_interrupt).start() def kill_subprocess(self): + if self._afterid is not None: + self.tkconsole.text.after_cancel(self._afterid) try: self.rpcclt.listening_sock.close() except AttributeError: # no socket @@ -561,8 +564,8 @@ pass # Reschedule myself if not self.tkconsole.closing: - self.tkconsole.text.after(self.tkconsole.pollinterval, - self.poll_subprocess) + self._afterid = self.tkconsole.text.after( + self.tkconsole.pollinterval, self.poll_subprocess) debugger = None @@ -973,10 +976,6 @@ self.stop_readline() self.canceled = True self.closing = True - # Wait for poll_subprocess() rescheduling to stop - self.text.after(2 * self.pollinterval, self.close2) - - def close2(self): return EditorWindow.close(self) def _close(self): diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -81,6 +81,8 @@ IDLE ---- +- Issue #5492: Avoid traceback when exiting IDLE caused by a race condition. + - Issue #17511: Keep IDLE find dialog open after clicking "Find Next". Original patch by Sarah K. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 12 05:13:39 2013 From: python-checkins at python.org (roger.serwy) Date: Wed, 12 Jun 2013 05:13:39 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?b?KTogIzU0OTI6IG1lcmdlIHdpdGggMy4z?= Message-ID: <3bVY7l2xTvzQXQ@mail.python.org> http://hg.python.org/cpython/rev/187da33826eb changeset: 84097:187da33826eb parent: 84093:e6fc120012e5 parent: 84096:da852f5250af user: Roger Serwy date: Tue Jun 11 22:13:51 2013 -0500 summary: #5492: merge with 3.3 files: Lib/idlelib/PyShell.py | 11 +++++------ Misc/NEWS | 2 ++ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Lib/idlelib/PyShell.py b/Lib/idlelib/PyShell.py --- a/Lib/idlelib/PyShell.py +++ b/Lib/idlelib/PyShell.py @@ -367,6 +367,7 @@ self.port = PORT self.original_compiler_flags = self.compile.compiler.flags + _afterid = None rpcclt = None rpcsubproc = None @@ -486,6 +487,8 @@ threading.Thread(target=self.__request_interrupt).start() def kill_subprocess(self): + if self._afterid is not None: + self.tkconsole.text.after_cancel(self._afterid) try: self.rpcclt.listening_sock.close() except AttributeError: # no socket @@ -561,8 +564,8 @@ pass # Reschedule myself if not self.tkconsole.closing: - self.tkconsole.text.after(self.tkconsole.pollinterval, - self.poll_subprocess) + self._afterid = self.tkconsole.text.after( + self.tkconsole.pollinterval, self.poll_subprocess) debugger = None @@ -973,10 +976,6 @@ self.stop_readline() self.canceled = True self.closing = True - # Wait for poll_subprocess() rescheduling to stop - self.text.after(2 * self.pollinterval, self.close2) - - def close2(self): return EditorWindow.close(self) def _close(self): diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -455,6 +455,8 @@ IDLE ---- +- Issue #5492: Avoid traceback when exiting IDLE caused by a race condition. + - Issue #17511: Keep IDLE find dialog open after clicking "Find Next". Original patch by Sarah K. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 12 05:24:49 2013 From: python-checkins at python.org (roger.serwy) Date: Wed, 12 Jun 2013 05:24:49 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogIzE4MTk2OiBBdm9p?= =?utf-8?q?d_displaying_spurious_SystemExit_tracebacks=2E?= Message-ID: <3bVYNd1bc6zQKf@mail.python.org> http://hg.python.org/cpython/rev/0e56d4e37777 changeset: 84098:0e56d4e37777 branch: 3.3 parent: 84096:da852f5250af user: Roger Serwy date: Tue Jun 11 22:25:14 2013 -0500 summary: #18196: Avoid displaying spurious SystemExit tracebacks. files: Lib/idlelib/run.py | 4 ++++ Misc/NEWS | 2 ++ 2 files changed, 6 insertions(+), 0 deletions(-) diff --git a/Lib/idlelib/run.py b/Lib/idlelib/run.py --- a/Lib/idlelib/run.py +++ b/Lib/idlelib/run.py @@ -339,6 +339,10 @@ exec(code, self.locals) finally: interruptable = False + except SystemExit: + # Scripts that raise SystemExit should just + # return to the interactive prompt + pass except: self.usr_exc_info = sys.exc_info() if quitting: diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -81,6 +81,8 @@ IDLE ---- +- Issue #18196: Avoid displaying spurious SystemExit tracebacks. + - Issue #5492: Avoid traceback when exiting IDLE caused by a race condition. - Issue #17511: Keep IDLE find dialog open after clicking "Find Next". -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 12 05:24:50 2013 From: python-checkins at python.org (roger.serwy) Date: Wed, 12 Jun 2013 05:24:50 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_=2318196=3A_merge_with_3=2E3?= Message-ID: <3bVYNf3YRxzQjj@mail.python.org> http://hg.python.org/cpython/rev/479aad3bb122 changeset: 84099:479aad3bb122 parent: 84097:187da33826eb parent: 84098:0e56d4e37777 user: Roger Serwy date: Tue Jun 11 22:25:34 2013 -0500 summary: #18196: merge with 3.3 files: Lib/idlelib/run.py | 4 ++++ Misc/NEWS | 2 ++ 2 files changed, 6 insertions(+), 0 deletions(-) diff --git a/Lib/idlelib/run.py b/Lib/idlelib/run.py --- a/Lib/idlelib/run.py +++ b/Lib/idlelib/run.py @@ -339,6 +339,10 @@ exec(code, self.locals) finally: interruptable = False + except SystemExit: + # Scripts that raise SystemExit should just + # return to the interactive prompt + pass except: self.usr_exc_info = sys.exc_info() if quitting: diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -455,6 +455,8 @@ IDLE ---- +- Issue #18196: Avoid displaying spurious SystemExit tracebacks. + - Issue #5492: Avoid traceback when exiting IDLE caused by a race condition. - Issue #17511: Keep IDLE find dialog open after clicking "Find Next". -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Wed Jun 12 05:50:51 2013 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Wed, 12 Jun 2013 05:50:51 +0200 Subject: [Python-checkins] Daily reference leaks (e6fc120012e5): sum=6 Message-ID: results for e6fc120012e5 on branch "default" -------------------------------------------- test_support leaked [0, -1, 1] references, sum=0 test_support leaked [0, -1, 3] memory blocks, sum=2 test_concurrent_futures leaked [2, 1, 1] memory blocks, sum=4 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogp6B6A7', '-x'] From python-checkins at python.org Wed Jun 12 08:29:05 2013 From: python-checkins at python.org (serhiy.storchaka) Date: Wed, 12 Jun 2013 08:29:05 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E3=29=3A_Add_tests_for_?= =?utf-8?q?issue_=2318183=2E?= Message-ID: <3bVdTF4f2nzNCr@mail.python.org> http://hg.python.org/cpython/rev/b11507395ce4 changeset: 84100:b11507395ce4 branch: 3.3 parent: 84098:0e56d4e37777 user: Serhiy Storchaka date: Wed Jun 12 09:20:44 2013 +0300 summary: Add tests for issue #18183. files: Lib/test/test_unicode.py | 18 +++++++++++++++--- 1 files changed, 15 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_unicode.py b/Lib/test/test_unicode.py --- a/Lib/test/test_unicode.py +++ b/Lib/test/test_unicode.py @@ -566,9 +566,6 @@ self.assertEqual('\U0008fffe'.lower(), '\U0008fffe') self.assertEqual('\u2177'.lower(), '\u2177') - # See issue #18183 for this one. - '\U00010000\U00100000'.lower() - def test_casefold(self): self.assertEqual('hello'.casefold(), 'hello') self.assertEqual('hELlo'.casefold(), 'hello') @@ -686,6 +683,17 @@ self.assertRaises(TypeError, "abc".__contains__) + def test_issue18183(self): + '\U00010000\U00100000'.lower() + '\U00010000\U00100000'.casefold() + '\U00010000\U00100000'.upper() + '\U00010000\U00100000'.capitalize() + '\U00010000\U00100000'.title() + '\U00010000\U00100000'.swapcase() + '\U00100000'.center(3, '\U00010000') + '\U00100000'.ljust(3, '\U00010000') + '\U00100000'.rjust(3, '\U00010000') + def test_format(self): self.assertEqual(''.format(), '') self.assertEqual('a'.format(), 'a') @@ -2016,6 +2024,10 @@ # test "%c" self.assertEqual(PyUnicode_FromFormat(b'%c', c_int(0xabcd)), '\uabcd') self.assertEqual(PyUnicode_FromFormat(b'%c', c_int(0x10ffff)), '\U0010ffff') + # Issue #18183 + self.assertEqual( + PyUnicode_FromFormat(b'%c%c', c_int(0x10000), c_int(0x100000)), + '\U00010000\U00100000') # test "%" self.assertEqual(PyUnicode_FromFormat(b'%'), '%') -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 12 08:29:06 2013 From: python-checkins at python.org (serhiy.storchaka) Date: Wed, 12 Jun 2013 08:29:06 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_Add_tests_for_issue_=2318183=2E?= Message-ID: <3bVdTG6gD2zQRq@mail.python.org> http://hg.python.org/cpython/rev/17c9f1627baf changeset: 84101:17c9f1627baf parent: 84099:479aad3bb122 parent: 84100:b11507395ce4 user: Serhiy Storchaka date: Wed Jun 12 09:28:20 2013 +0300 summary: Add tests for issue #18183. files: Lib/test/test_unicode.py | 17 ++++++++++++++--- 1 files changed, 14 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_unicode.py b/Lib/test/test_unicode.py --- a/Lib/test/test_unicode.py +++ b/Lib/test/test_unicode.py @@ -577,9 +577,6 @@ self.assertEqual('\U0008fffe'.lower(), '\U0008fffe') self.assertEqual('\u2177'.lower(), '\u2177') - # See issue #18183 for this one. - '\U00010000\U00100000'.lower() - def test_casefold(self): self.assertEqual('hello'.casefold(), 'hello') self.assertEqual('hELlo'.casefold(), 'hello') @@ -697,6 +694,17 @@ self.assertRaises(TypeError, "abc".__contains__) + def test_issue18183(self): + '\U00010000\U00100000'.lower() + '\U00010000\U00100000'.casefold() + '\U00010000\U00100000'.upper() + '\U00010000\U00100000'.capitalize() + '\U00010000\U00100000'.title() + '\U00010000\U00100000'.swapcase() + '\U00100000'.center(3, '\U00010000') + '\U00100000'.ljust(3, '\U00010000') + '\U00100000'.rjust(3, '\U00010000') + def test_format(self): self.assertEqual(''.format(), '') self.assertEqual('a'.format(), 'a') @@ -2040,6 +2048,9 @@ b'%c', c_int(0xabcd)) check_format('\U0010ffff', b'%c', c_int(0x10ffff)) + # Issue #18183 + check_format('\U00010000\U00100000', + b'%c%c', c_int(0x10000), c_int(0x100000)) # test "%" check_format('%', -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 12 21:57:10 2013 From: python-checkins at python.org (brett.cannon) Date: Wed, 12 Jun 2013 21:57:10 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Move_code_from_test=5Fimpo?= =?utf-8?q?rthooks_into_test=5Fzipimport=2E?= Message-ID: <3bVzPf5t9rzM9b@mail.python.org> http://hg.python.org/cpython/rev/3e10025346c1 changeset: 84102:3e10025346c1 user: Brett Cannon date: Wed Jun 12 15:57:01 2013 -0400 summary: Move code from test_importhooks into test_zipimport. files: Lib/test/test_zipimport.py | 26 +++++++++++++++++++++++++- 1 files changed, 25 insertions(+), 1 deletions(-) diff --git a/Lib/test/test_zipimport.py b/Lib/test/test_zipimport.py --- a/Lib/test/test_zipimport.py +++ b/Lib/test/test_zipimport.py @@ -7,7 +7,6 @@ import unittest from test import support -from test.test_importhooks import ImportHooksBaseTestCase, test_src, test_co from zipfile import ZipFile, ZipInfo, ZIP_STORED, ZIP_DEFLATED @@ -17,6 +16,14 @@ import inspect import io from traceback import extract_tb, extract_stack, print_tb + +test_src = """\ +def get_name(): + return __name__ +def get_file(): + return __file__ +""" +test_co = compile(test_src, "", "exec") raise_src = 'def do_raise(): raise TypeError\n' def make_pyc(co, mtime, size): @@ -46,6 +53,23 @@ pyc_ext = ('.pyc' if __debug__ else '.pyo') +class ImportHooksBaseTestCase(unittest.TestCase): + + def setUp(self): + self.path = sys.path[:] + self.meta_path = sys.meta_path[:] + self.path_hooks = sys.path_hooks[:] + sys.path_importer_cache.clear() + self.modules_before = support.modules_setup() + + def tearDown(self): + sys.path[:] = self.path + sys.meta_path[:] = self.meta_path + sys.path_hooks[:] = self.path_hooks + sys.path_importer_cache.clear() + support.modules_cleanup(*self.modules_before) + + class UncompressedZipImportTestCase(ImportHooksBaseTestCase): compression = ZIP_STORED -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 12 22:59:56 2013 From: python-checkins at python.org (brett.cannon) Date: Wed, 12 Jun 2013 22:59:56 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2315767=3A_Introduc?= =?utf-8?q?e_ModuleNotFoundError=2C_a_subclass_of?= Message-ID: <3bW0p40Z8PzSy9@mail.python.org> http://hg.python.org/cpython/rev/8a0ed9f63c6e changeset: 84103:8a0ed9f63c6e user: Brett Cannon date: Wed Jun 12 16:59:46 2013 -0400 summary: Issue #15767: Introduce ModuleNotFoundError, a subclass of ImportError. The exception is raised by import when a module could not be found. Technically this is defined as no viable loader could be found for the specified module. This includes ``from ... import`` statements so that the module usage is consistent for all situations where import couldn't find what was requested. This should allow for the common idiom of:: try: import something except ImportError: pass to be updated to using ModuleNotFoundError and not accidentally mask ImportError messages that should propagate (e.g. issues with a loader). This work was driven by the fact that the ``from ... import`` statement needed to be able to tell the difference between an ImportError that simply couldn't find a module (and thus silence the exception so that ceval can raise it) and an ImportError that represented an actual problem. files: Doc/c-api/exceptions.rst | 2 + Doc/library/exceptions.rst | 13 +- Doc/whatsnew/3.4.rst | 3 + Include/pyerrors.h | 1 + Lib/importlib/_bootstrap.py | 15 +- Lib/pydoc.py | 2 +- Lib/test/exception_hierarchy.txt | 1 + Lib/test/test_exceptions.py | 3 - Lib/test/test_import.py | 25 +- Lib/test/test_importlib/import_/test_api.py | 4 + Lib/test/test_importlib/import_/test_fromlist.py | 8 +- Lib/test/test_pydoc.py | 2 +- Lib/test/test_site.py | 2 +- Misc/NEWS | 3 + Objects/exceptions.c | 9 + Python/ceval.c | 2 +- Python/importlib.h | 737 ++++----- 17 files changed, 424 insertions(+), 408 deletions(-) diff --git a/Doc/c-api/exceptions.rst b/Doc/c-api/exceptions.rst --- a/Doc/c-api/exceptions.rst +++ b/Doc/c-api/exceptions.rst @@ -686,6 +686,8 @@ +-----------------------------------------+---------------------------------+----------+ | :c:data:`PyExc_ImportError` | :exc:`ImportError` | | +-----------------------------------------+---------------------------------+----------+ +| :c:data:`PyExc_ModuleNotFoundError` | :exc:`ModuleNotFoundError` | | ++-----------------------------------------+---------------------------------+----------+ | :c:data:`PyExc_IndexError` | :exc:`IndexError` | | +-----------------------------------------+---------------------------------+----------+ | :c:data:`PyExc_InterruptedError` | :exc:`InterruptedError` | | diff --git a/Doc/library/exceptions.rst b/Doc/library/exceptions.rst --- a/Doc/library/exceptions.rst +++ b/Doc/library/exceptions.rst @@ -169,8 +169,8 @@ .. exception:: ImportError - Raised when an :keyword:`import` statement fails to find the module definition - or when a ``from ... import`` fails to find a name that is to be imported. + Raised when the :keyword:`import` statement has troubles trying to load a + module. The :attr:`name` and :attr:`path` attributes can be set using keyword-only arguments to the constructor. When set they represent the name of the module @@ -180,6 +180,15 @@ .. versionchanged:: 3.3 Added the :attr:`name` and :attr:`path` attributes. +.. exception:: ModuleNotFoundError + + A subclass of :exc:`ImportError` which is raised by :keyword:`import` when a + module could not be located. This includes ``from ... import`` statements as + the specific attribute being requested cannot be known a priori to be a module + or some other type of object. + + .. versionadded:: 3.4 + .. exception:: IndexError diff --git a/Doc/whatsnew/3.4.rst b/Doc/whatsnew/3.4.rst --- a/Doc/whatsnew/3.4.rst +++ b/Doc/whatsnew/3.4.rst @@ -137,6 +137,9 @@ * Unicode database updated to UCD version 6.2. +* Import now raises the new exception :exc:`ModuleNotFoundError` (subclass of + :exc:`ImportError`) when it cannot find something. + New Modules diff --git a/Include/pyerrors.h b/Include/pyerrors.h --- a/Include/pyerrors.h +++ b/Include/pyerrors.h @@ -152,6 +152,7 @@ PyAPI_DATA(PyObject *) PyExc_FloatingPointError; PyAPI_DATA(PyObject *) PyExc_OSError; PyAPI_DATA(PyObject *) PyExc_ImportError; +PyAPI_DATA(PyObject *) PyExc_ModuleNotFoundError; PyAPI_DATA(PyObject *) PyExc_IndexError; PyAPI_DATA(PyObject *) PyExc_KeyError; PyAPI_DATA(PyObject *) PyExc_KeyboardInterrupt; diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py --- a/Lib/importlib/_bootstrap.py +++ b/Lib/importlib/_bootstrap.py @@ -1553,11 +1553,7 @@ raise ImportError(msg, name=name) loader = _find_module(name, path) if loader is None: - exc = ImportError(_ERR_MSG.format(name), name=name) - # TODO(brett): switch to a proper ModuleNotFound exception in Python - # 3.4. - exc._not_found = True - raise exc + raise ModuleNotFoundError(_ERR_MSG.format(name), name=name) elif name not in sys.modules: # The parent import may have already imported this module. loader.load_module(name) @@ -1643,15 +1639,12 @@ from_name = '{}.{}'.format(module.__name__, x) try: _call_with_frames_removed(import_, from_name) - except ImportError as exc: + except ModuleNotFoundError as exc: # Backwards-compatibility dictates we ignore failed # imports triggered by fromlist for modules that don't # exist. - # TODO(brett): In Python 3.4, have import raise - # ModuleNotFound and catch that. - if getattr(exc, '_not_found', False): - if exc.name == from_name: - continue + if exc.name == from_name: + continue raise return module diff --git a/Lib/pydoc.py b/Lib/pydoc.py --- a/Lib/pydoc.py +++ b/Lib/pydoc.py @@ -317,7 +317,7 @@ elif exc is SyntaxError: # A SyntaxError occurred before we could execute the module. raise ErrorDuringImport(value.filename, info) - elif exc is ImportError and value.name == path: + elif issubclass(exc, ImportError) and value.name == path: # No such module in the path. return None else: diff --git a/Lib/test/exception_hierarchy.txt b/Lib/test/exception_hierarchy.txt --- a/Lib/test/exception_hierarchy.txt +++ b/Lib/test/exception_hierarchy.txt @@ -13,6 +13,7 @@ +-- BufferError +-- EOFError +-- ImportError + +-- ModuleNotFoundError +-- LookupError | +-- IndexError | +-- KeyError diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -953,8 +953,5 @@ self.assertEqual(str(arg), str(exc)) -def test_main(): - run_unittest(ExceptionTests, ImportErrorTests) - if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_import.py b/Lib/test/test_import.py --- a/Lib/test/test_import.py +++ b/Lib/test/test_import.py @@ -68,7 +68,15 @@ def tearDown(self): unload(TESTFN) - setUp = tearDown + def test_import_raises_ModuleNotFoundError(self): + with self.assertRaises(ModuleNotFoundError): + import something_that_should_not_exist_anywhere + + def test_from_import_raises_ModuleNotFoundError(self): + with self.assertRaises(ModuleNotFoundError): + from something_that_should_not_exist_anywhere import blah + with self.assertRaises(ModuleNotFoundError): + from importlib import something_that_should_not_exist_anywhere def test_case_sensitivity(self): # Brief digression to test that import is case-sensitive: if we got @@ -487,7 +495,7 @@ header = f.read(12) code = marshal.load(f) constants = list(code.co_consts) - foreign_code = test_main.__code__ + foreign_code = importlib.import_module.__code__ pos = constants.index(1) constants[pos] = foreign_code code = type(code)(code.co_argcount, code.co_kwonlyargcount, @@ -1014,16 +1022,5 @@ importlib.SourceLoader.load_module = old_load_module -def test_main(verbose=None): - run_unittest(ImportTests, PycacheTests, FilePermissionTests, - PycRewritingTests, PathsTests, RelativeImportTests, - OverridingImportBuiltinTests, - ImportlibBootstrapTests, - TestSymbolicallyLinkedPackage, - ImportTracebackTests) - - if __name__ == '__main__': - # Test needs to be a package, so we can do relative imports. - from test.test_import import test_main - test_main() + unittest.main() diff --git a/Lib/test/test_importlib/import_/test_api.py b/Lib/test/test_importlib/import_/test_api.py --- a/Lib/test/test_importlib/import_/test_api.py +++ b/Lib/test/test_importlib/import_/test_api.py @@ -22,6 +22,10 @@ """Test API-specific details for __import__ (e.g. raising the right exception when passing in an int for the module name).""" + def test_raises_ModuleNotFoundError(self): + with self.assertRaises(ModuleNotFoundError): + util.import_('some module that does not exist') + def test_name_requires_rparition(self): # Raise TypeError if a non-string is passed in for the module name. with self.assertRaises(TypeError): diff --git a/Lib/test/test_importlib/import_/test_fromlist.py b/Lib/test/test_importlib/import_/test_fromlist.py --- a/Lib/test/test_importlib/import_/test_fromlist.py +++ b/Lib/test/test_importlib/import_/test_fromlist.py @@ -69,16 +69,16 @@ self.assertTrue(hasattr(module, 'module')) self.assertEqual(module.module.__name__, 'pkg.module') - def test_module_from_package_triggers_ImportError(self): - # If a submodule causes an ImportError because it tries to import - # a module which doesn't exist, that should let the ImportError + def test_module_from_package_triggers_ModuleNotFoundError(self): + # If a submodule causes an ModuleNotFoundError because it tries to import + # a module which doesn't exist, that should let the ModuleNotFoundError # propagate. def module_code(): import i_do_not_exist with util.mock_modules('pkg.__init__', 'pkg.mod', module_code={'pkg.mod': module_code}) as importer: with util.import_state(meta_path=[importer]): - with self.assertRaises(ImportError) as exc: + with self.assertRaises(ModuleNotFoundError) as exc: import_util.import_('pkg', fromlist=['mod']) self.assertEqual('i_do_not_exist', exc.exception.name) diff --git a/Lib/test/test_pydoc.py b/Lib/test/test_pydoc.py --- a/Lib/test/test_pydoc.py +++ b/Lib/test/test_pydoc.py @@ -206,7 +206,7 @@ missing_pattern = "no Python documentation found for '%s'" # output pattern for module with bad imports -badimport_pattern = "problem in %s - ImportError: No module named %r" +badimport_pattern = "problem in %s - ModuleNotFoundError: No module named %r" def run_pydoc(module_name, *args, **env): """ diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py --- a/Lib/test/test_site.py +++ b/Lib/test/test_site.py @@ -131,7 +131,7 @@ re.escape(os.path.join(pth_dir, pth_fn))) # XXX: ditto previous XXX comment. self.assertRegex(err_out.getvalue(), 'Traceback') - self.assertRegex(err_out.getvalue(), 'ImportError') + self.assertRegex(err_out.getvalue(), 'ModuleNotFoundError') @unittest.skipIf(sys.platform == "win32", "Windows does not raise an " "error for file paths containing null characters") diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,9 @@ Core and Builtins ----------------- +- Issue #15767: Introduce ModuleNotFoundError which is raised when a module + could not be found. + - Issue #18183: Fix various unicode operations on strings with large unicode codepoints. diff --git a/Objects/exceptions.c b/Objects/exceptions.c --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -710,6 +710,13 @@ "module."); /* + * ModuleNotFoundError extends ImportError + */ + +MiddlingExtendsException(PyExc_ImportError, ModuleNotFoundError, ImportError, + "Module not found."); + +/* * OSError extends Exception */ @@ -2395,6 +2402,7 @@ PRE_INIT(SystemExit) PRE_INIT(KeyboardInterrupt) PRE_INIT(ImportError) + PRE_INIT(ModuleNotFoundError) PRE_INIT(OSError) PRE_INIT(EOFError) PRE_INIT(RuntimeError) @@ -2465,6 +2473,7 @@ POST_INIT(SystemExit) POST_INIT(KeyboardInterrupt) POST_INIT(ImportError) + POST_INIT(ModuleNotFoundError) POST_INIT(OSError) INIT_ALIAS(EnvironmentError, OSError) INIT_ALIAS(IOError, OSError) diff --git a/Python/ceval.c b/Python/ceval.c --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4588,7 +4588,7 @@ x = PyObject_GetAttr(v, name); if (x == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) { - PyErr_Format(PyExc_ImportError, "cannot import name %S", name); + PyErr_Format(PyExc_ModuleNotFoundError, "cannot import name %S", name); } return x; } diff --git a/Python/importlib.h b/Python/importlib.h --- a/Python/importlib.h +++ b/Python/importlib.h [stripped] -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 13 01:57:31 2013 From: python-checkins at python.org (brett.cannon) Date: Thu, 13 Jun 2013 01:57:31 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Move_test=5Fzipfile_to_uni?= =?utf-8?b?dHRlc3QubWFpbigp?= Message-ID: <3bW4kz5Gf9z7LjN@mail.python.org> http://hg.python.org/cpython/rev/78b8238d679f changeset: 84104:78b8238d679f user: Brett Cannon date: Wed Jun 12 19:57:19 2013 -0400 summary: Move test_zipfile to unittest.main() files: Lib/test/test_zipfile.py | 8 +------- 1 files changed, 1 insertions(+), 7 deletions(-) diff --git a/Lib/test/test_zipfile.py b/Lib/test/test_zipfile.py --- a/Lib/test/test_zipfile.py +++ b/Lib/test/test_zipfile.py @@ -1911,11 +1911,5 @@ unlink(TESTFN2) -def test_main(): - run_unittest(TestsWithSourceFile, TestZip64InSmallFiles, OtherTests, - PyZipFileTests, DecryptionTests, TestsWithMultipleOpens, - TestWithDirectory, UniversalNewlineTests, - TestsWithRandomBinaryFiles) - if __name__ == "__main__": - test_main() + unittest.main() -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 13 02:04:28 2013 From: python-checkins at python.org (brett.cannon) Date: Thu, 13 Jun 2013 02:04:28 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Spruce_up_test=5Fxmlrpc_by?= =?utf-8?q?_using_ModuleNotFoundError_and_moving_to?= Message-ID: <3bW4v01lB2zSZL@mail.python.org> http://hg.python.org/cpython/rev/281857369a78 changeset: 84105:281857369a78 user: Brett Cannon date: Wed Jun 12 20:04:19 2013 -0400 summary: Spruce up test_xmlrpc by using ModuleNotFoundError and moving to unittest.main(). files: Lib/test/test_xmlrpc.py | 29 +++++++---------------------- 1 files changed, 7 insertions(+), 22 deletions(-) diff --git a/Lib/test/test_xmlrpc.py b/Lib/test/test_xmlrpc.py --- a/Lib/test/test_xmlrpc.py +++ b/Lib/test/test_xmlrpc.py @@ -14,8 +14,12 @@ from test import support try: + import gzip +except ModuleNotFoundError: + gzip = None +try: import threading -except ImportError: +except ModuleNotFoundError: threading = None alist = [{'astring': 'foo at bar.baz.spam', @@ -785,6 +789,7 @@ #A test case that verifies that gzip encoding works in both directions #(for a request and the response) + at unittest.skipIf(gzip is None, 'requires gzip') class GzipServerTestCase(BaseServerTestCase): #a request handler that supports keep-alive and logs requests into a #class variable @@ -1074,25 +1079,5 @@ self.assertTrue(server.use_builtin_types) - at support.reap_threads -def test_main(): - xmlrpc_tests = [XMLRPCTestCase, HelperTestCase, DateTimeTestCase, - BinaryTestCase, FaultTestCase] - xmlrpc_tests.append(UseBuiltinTypesTestCase) - xmlrpc_tests.append(SimpleServerTestCase) - xmlrpc_tests.append(KeepaliveServerTestCase1) - xmlrpc_tests.append(KeepaliveServerTestCase2) - try: - import gzip - xmlrpc_tests.append(GzipServerTestCase) - except ImportError: - pass #gzip not supported in this build - xmlrpc_tests.append(MultiPathServerTestCase) - xmlrpc_tests.append(ServerProxyTestCase) - xmlrpc_tests.append(FailingServerTestCase) - xmlrpc_tests.append(CGIHandlerTestCase) - - support.run_unittest(*xmlrpc_tests) - if __name__ == "__main__": - test_main() + support.reap_threads(unittest.main)() -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 13 02:12:38 2013 From: python-checkins at python.org (brett.cannon) Date: Thu, 13 Jun 2013 02:12:38 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbjogTW92ZSB0ZXN0X19fYWxsX18g?= =?utf-8?q?over_to_unittest=2Emain=28=29_and_use_ModuleNotFoundError?= Message-ID: <3bW54Q4xGQzRrw@mail.python.org> http://hg.python.org/cpython/rev/c4d7228421df changeset: 84106:c4d7228421df user: Brett Cannon date: Wed Jun 12 20:12:30 2013 -0400 summary: Move test___all__ over to unittest.main() and use ModuleNotFoundError files: Lib/test/regrtest.py | 16 ++++++++-------- Lib/test/support.py | 14 +++++++------- Lib/test/test___all__.py | 7 ++----- Lib/xmlrpc/server.py | 2 +- Lib/zipfile.py | 6 +++--- 5 files changed, 21 insertions(+), 24 deletions(-) diff --git a/Lib/test/regrtest.py b/Lib/test/regrtest.py --- a/Lib/test/regrtest.py +++ b/Lib/test/regrtest.py @@ -146,11 +146,11 @@ try: import threading -except ImportError: +except ModuleNotFoundError: threading = None try: import multiprocessing.process -except ImportError: +except ModuleNotFoundError: multiprocessing = None @@ -180,7 +180,7 @@ if sys.platform == 'darwin': try: import resource - except ImportError: + except ModuleNotFoundError: pass else: soft, hard = resource.getrlimit(resource.RLIMIT_STACK) @@ -571,7 +571,7 @@ if findleaks: try: import gc - except ImportError: + except ModuleNotFoundError: print('No GC available, disabling findleaks.') findleaks = False else: @@ -692,7 +692,7 @@ if use_mp: try: from threading import Thread - except ImportError: + except ModuleNotFoundError: print("Multiprocess option requires thread support") sys.exit(2) from queue import Queue @@ -1396,7 +1396,7 @@ pic = sys.path_importer_cache.copy() try: import zipimport - except ImportError: + except ModuleNotFoundError: zdc = None # Run unmodified on platforms without zipimport support else: zdc = zipimport._zip_directory_cache.copy() @@ -1473,7 +1473,7 @@ sys.path_importer_cache.update(pic) try: import zipimport - except ImportError: + except ModuleNotFoundError: pass # Run unmodified on platforms without zipimport support else: zipimport._zip_directory_cache.clear() @@ -1510,7 +1510,7 @@ doctest.master = None try: import ctypes - except ImportError: + except ModuleNotFoundError: # Don't worry about resetting the cache if ctypes is not supported pass else: diff --git a/Lib/test/support.py b/Lib/test/support.py --- a/Lib/test/support.py +++ b/Lib/test/support.py @@ -29,27 +29,27 @@ try: import _thread, threading -except ImportError: +except ModuleNotFoundError: _thread = None threading = None try: import multiprocessing.process -except ImportError: +except ModuleNotFoundError: multiprocessing = None try: import zlib -except ImportError: +except ModuleNotFoundError: zlib = None try: import bz2 -except ImportError: +except ModuleNotFoundError: bz2 = None try: import lzma -except ImportError: +except ModuleNotFoundError: lzma = None __all__ = [ @@ -116,7 +116,7 @@ with _ignore_deprecated_imports(deprecated): try: return importlib.import_module(name) - except ImportError as msg: + except ModuleNotFoundError as msg: if sys.platform.startswith(tuple(required_on)): raise raise unittest.SkipTest(str(msg)) @@ -188,7 +188,7 @@ if not _save_and_block_module(blocked_name, orig_modules): names_to_remove.append(blocked_name) fresh_module = importlib.import_module(name) - except ImportError: + except ModuleNotFoundError: fresh_module = None finally: for orig_name, module in orig_modules.items(): diff --git a/Lib/test/test___all__.py b/Lib/test/test___all__.py --- a/Lib/test/test___all__.py +++ b/Lib/test/test___all__.py @@ -75,7 +75,7 @@ try: import rlcompleter import locale - except ImportError: + except ModuleNotFoundError: pass else: locale.setlocale(locale.LC_CTYPE, 'C') @@ -113,8 +113,5 @@ print('Following modules failed to be imported:', failed_imports) -def test_main(): - support.run_unittest(AllTest) - if __name__ == "__main__": - test_main() + unittest.main() diff --git a/Lib/xmlrpc/server.py b/Lib/xmlrpc/server.py --- a/Lib/xmlrpc/server.py +++ b/Lib/xmlrpc/server.py @@ -116,7 +116,7 @@ import traceback try: import fcntl -except ImportError: +except ModuleNotFoundError: fcntl = None def resolve_dotted_attribute(obj, attr, allow_dotted_names=True): diff --git a/Lib/zipfile.py b/Lib/zipfile.py --- a/Lib/zipfile.py +++ b/Lib/zipfile.py @@ -18,18 +18,18 @@ try: import zlib # We may need its compression method crc32 = zlib.crc32 -except ImportError: +except ModuleNotFoundError: zlib = None crc32 = binascii.crc32 try: import bz2 # We may need its compression method -except ImportError: +except ModuleNotFoundError: bz2 = None try: import lzma # We may need its compression method -except ImportError: +except ModuleNotFoundError: lzma = None __all__ = ["BadZipFile", "BadZipfile", "error", -- Repository URL: http://hg.python.org/cpython From brett at python.org Thu Jun 13 02:13:49 2013 From: brett at python.org (Brett Cannon) Date: Wed, 12 Jun 2013 20:13:49 -0400 Subject: [Python-checkins] cpython: Move test___all__ over to unittest.main() and use ModuleNotFoundError In-Reply-To: <3bW54Q4xGQzRrw@mail.python.org> References: <3bW54Q4xGQzRrw@mail.python.org> Message-ID: Obviously the commit message is a little misleading since changes I was about to stage accidentally went in on this change. Sorry about that. Same basic concept of the changes, just to more modules. On Wed, Jun 12, 2013 at 8:12 PM, brett.cannon wrote: > http://hg.python.org/cpython/rev/c4d7228421df > changeset: 84106:c4d7228421df > user: Brett Cannon > date: Wed Jun 12 20:12:30 2013 -0400 > summary: > Move test___all__ over to unittest.main() and use ModuleNotFoundError > > files: > Lib/test/regrtest.py | 16 ++++++++-------- > Lib/test/support.py | 14 +++++++------- > Lib/test/test___all__.py | 7 ++----- > Lib/xmlrpc/server.py | 2 +- > Lib/zipfile.py | 6 +++--- > 5 files changed, 21 insertions(+), 24 deletions(-) > > > diff --git a/Lib/test/regrtest.py b/Lib/test/regrtest.py > --- a/Lib/test/regrtest.py > +++ b/Lib/test/regrtest.py > @@ -146,11 +146,11 @@ > > try: > import threading > -except ImportError: > +except ModuleNotFoundError: > threading = None > try: > import multiprocessing.process > -except ImportError: > +except ModuleNotFoundError: > multiprocessing = None > > > @@ -180,7 +180,7 @@ > if sys.platform == 'darwin': > try: > import resource > - except ImportError: > + except ModuleNotFoundError: > pass > else: > soft, hard = resource.getrlimit(resource.RLIMIT_STACK) > @@ -571,7 +571,7 @@ > if findleaks: > try: > import gc > - except ImportError: > + except ModuleNotFoundError: > print('No GC available, disabling findleaks.') > findleaks = False > else: > @@ -692,7 +692,7 @@ > if use_mp: > try: > from threading import Thread > - except ImportError: > + except ModuleNotFoundError: > print("Multiprocess option requires thread support") > sys.exit(2) > from queue import Queue > @@ -1396,7 +1396,7 @@ > pic = sys.path_importer_cache.copy() > try: > import zipimport > - except ImportError: > + except ModuleNotFoundError: > zdc = None # Run unmodified on platforms without zipimport support > else: > zdc = zipimport._zip_directory_cache.copy() > @@ -1473,7 +1473,7 @@ > sys.path_importer_cache.update(pic) > try: > import zipimport > - except ImportError: > + except ModuleNotFoundError: > pass # Run unmodified on platforms without zipimport support > else: > zipimport._zip_directory_cache.clear() > @@ -1510,7 +1510,7 @@ > doctest.master = None > try: > import ctypes > - except ImportError: > + except ModuleNotFoundError: > # Don't worry about resetting the cache if ctypes is not supported > pass > else: > diff --git a/Lib/test/support.py b/Lib/test/support.py > --- a/Lib/test/support.py > +++ b/Lib/test/support.py > @@ -29,27 +29,27 @@ > > try: > import _thread, threading > -except ImportError: > +except ModuleNotFoundError: > _thread = None > threading = None > try: > import multiprocessing.process > -except ImportError: > +except ModuleNotFoundError: > multiprocessing = None > > try: > import zlib > -except ImportError: > +except ModuleNotFoundError: > zlib = None > > try: > import bz2 > -except ImportError: > +except ModuleNotFoundError: > bz2 = None > > try: > import lzma > -except ImportError: > +except ModuleNotFoundError: > lzma = None > > __all__ = [ > @@ -116,7 +116,7 @@ > with _ignore_deprecated_imports(deprecated): > try: > return importlib.import_module(name) > - except ImportError as msg: > + except ModuleNotFoundError as msg: > if sys.platform.startswith(tuple(required_on)): > raise > raise unittest.SkipTest(str(msg)) > @@ -188,7 +188,7 @@ > if not _save_and_block_module(blocked_name, orig_modules): > names_to_remove.append(blocked_name) > fresh_module = importlib.import_module(name) > - except ImportError: > + except ModuleNotFoundError: > fresh_module = None > finally: > for orig_name, module in orig_modules.items(): > diff --git a/Lib/test/test___all__.py b/Lib/test/test___all__.py > --- a/Lib/test/test___all__.py > +++ b/Lib/test/test___all__.py > @@ -75,7 +75,7 @@ > try: > import rlcompleter > import locale > - except ImportError: > + except ModuleNotFoundError: > pass > else: > locale.setlocale(locale.LC_CTYPE, 'C') > @@ -113,8 +113,5 @@ > print('Following modules failed to be imported:', > failed_imports) > > > -def test_main(): > - support.run_unittest(AllTest) > - > if __name__ == "__main__": > - test_main() > + unittest.main() > diff --git a/Lib/xmlrpc/server.py b/Lib/xmlrpc/server.py > --- a/Lib/xmlrpc/server.py > +++ b/Lib/xmlrpc/server.py > @@ -116,7 +116,7 @@ > import traceback > try: > import fcntl > -except ImportError: > +except ModuleNotFoundError: > fcntl = None > > def resolve_dotted_attribute(obj, attr, allow_dotted_names=True): > diff --git a/Lib/zipfile.py b/Lib/zipfile.py > --- a/Lib/zipfile.py > +++ b/Lib/zipfile.py > @@ -18,18 +18,18 @@ > try: > import zlib # We may need its compression method > crc32 = zlib.crc32 > -except ImportError: > +except ModuleNotFoundError: > zlib = None > crc32 = binascii.crc32 > > try: > import bz2 # We may need its compression method > -except ImportError: > +except ModuleNotFoundError: > bz2 = None > > try: > import lzma # We may need its compression method > -except ImportError: > +except ModuleNotFoundError: > lzma = None > > __all__ = ["BadZipFile", "BadZipfile", "error", > > -- > 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 python-checkins at python.org Thu Jun 13 03:26:09 2013 From: python-checkins at python.org (brett.cannon) Date: Thu, 13 Jun 2013 03:26:09 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Partially_revert_changeset?= =?utf-8?q?_=23281857369a78_to_make_sure_threads_are?= Message-ID: <3bW6jF1CXrz7LjP@mail.python.org> http://hg.python.org/cpython/rev/017c431d1798 changeset: 84107:017c431d1798 user: Brett Cannon date: Wed Jun 12 21:25:23 2013 -0400 summary: Partially revert changeset #281857369a78 to make sure threads are reaped in all situations. files: Lib/test/test_xmlrpc.py | 12 +++++++++++- 1 files changed, 11 insertions(+), 1 deletions(-) diff --git a/Lib/test/test_xmlrpc.py b/Lib/test/test_xmlrpc.py --- a/Lib/test/test_xmlrpc.py +++ b/Lib/test/test_xmlrpc.py @@ -1079,5 +1079,15 @@ self.assertTrue(server.use_builtin_types) + at support.reap_threads +def test_main(): + support.run_unittest(XMLRPCTestCase, HelperTestCase, DateTimeTestCase, + BinaryTestCase, FaultTestCase, UseBuiltinTypesTestCase, + SimpleServerTestCase, KeepaliveServerTestCase1, + KeepaliveServerTestCase2, GzipServerTestCase, + MultiPathServerTestCase, ServerProxyTestCase, FailingServerTestCase, + CGIHandlerTestCase) + + if __name__ == "__main__": - support.reap_threads(unittest.main)() + test_main() -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 13 03:26:10 2013 From: python-checkins at python.org (brett.cannon) Date: Thu, 13 Jun 2013 03:26:10 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Update_various_test_module?= =?utf-8?q?s_to_use_unittest=2Emain=28=29_for_test_discovery?= Message-ID: <3bW6jG4bXwz7LkJ@mail.python.org> http://hg.python.org/cpython/rev/05f7883ee92d changeset: 84108:05f7883ee92d user: Brett Cannon date: Wed Jun 12 21:25:59 2013 -0400 summary: Update various test modules to use unittest.main() for test discovery instead of manually listing tests for test.support.run_unittest(). files: Lib/test/test_ast.py | 5 +--- Lib/test/test_asynchat.py | 6 +---- Lib/test/test_buffer.py | 6 +---- Lib/test/test_codeccallbacks.py | 4 +-- Lib/test/test_contextlib.py | 6 +---- Lib/test/test_faulthandler.py | 5 +--- Lib/test/test_fileinput.py | 18 +------------- Lib/test/test_frozen.py | 4 +-- Lib/test/test_getargs2.py | 26 ++++---------------- Lib/test/test_hashlib.py | 4 +-- Lib/test/test_ioctl.py | 4 +-- Lib/test/test_runpy.py | 9 +------ Lib/test/test_sched.py | 5 +--- Lib/test/test_shutil.py | 6 +---- Lib/test/test_site.py | 4 +-- Lib/test/test_strftime.py | 4 +-- Lib/test/test_sundry.py | 5 +--- Lib/test/test_urllib2net.py | 11 +------- 18 files changed, 24 insertions(+), 108 deletions(-) diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py --- a/Lib/test/test_ast.py +++ b/Lib/test/test_ast.py @@ -951,9 +951,6 @@ compile(mod, fn, "exec") -def test_main(): - support.run_unittest(AST_Tests, ASTHelpers_Test, ASTValidatorTests) - def main(): if __name__ != '__main__': return @@ -966,7 +963,7 @@ print("]") print("main()") raise SystemExit - test_main() + unittest.main() #### EVERYTHING BELOW IS GENERATED ##### exec_results = [ diff --git a/Lib/test/test_asynchat.py b/Lib/test/test_asynchat.py --- a/Lib/test/test_asynchat.py +++ b/Lib/test/test_asynchat.py @@ -283,9 +283,5 @@ self.assertEqual(f.pop(), (0, None)) -def test_main(verbose=None): - support.run_unittest(TestAsynchat, TestAsynchat_WithPoll, - TestHelperFunctions, TestFifo) - if __name__ == "__main__": - test_main(verbose=True) + unittest.main() diff --git a/Lib/test/test_buffer.py b/Lib/test/test_buffer.py --- a/Lib/test/test_buffer.py +++ b/Lib/test/test_buffer.py @@ -4283,9 +4283,5 @@ self.assertRaises(BufferError, memoryview, x) -def test_main(): - support.run_unittest(TestBufferProtocol) - - if __name__ == "__main__": - test_main() + unittest.main() diff --git a/Lib/test/test_codeccallbacks.py b/Lib/test/test_codeccallbacks.py --- a/Lib/test/test_codeccallbacks.py +++ b/Lib/test/test_codeccallbacks.py @@ -875,8 +875,6 @@ with self.assertRaises(TypeError): data.decode(encoding, "test.replacing") -def test_main(): - test.support.run_unittest(CodecCallbackTest) if __name__ == "__main__": - test_main() + unittest.main() 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 @@ -617,9 +617,5 @@ 'Hello'[50] -# This is needed to make the test actually run under regrtest.py! -def test_main(): - support.run_unittest(__name__) - if __name__ == "__main__": - test_main() + unittest.main() diff --git a/Lib/test/test_faulthandler.py b/Lib/test/test_faulthandler.py --- a/Lib/test/test_faulthandler.py +++ b/Lib/test/test_faulthandler.py @@ -588,8 +588,5 @@ self.check_register(chain=True) -def test_main(): - support.run_unittest(FaultHandlerTests) - if __name__ == "__main__": - test_main() + unittest.main() diff --git a/Lib/test/test_fileinput.py b/Lib/test/test_fileinput.py --- a/Lib/test/test_fileinput.py +++ b/Lib/test/test_fileinput.py @@ -835,22 +835,6 @@ self.assertIs(kwargs.pop('encoding'), encoding) self.assertFalse(kwargs) -def test_main(): - run_unittest( - BufferSizesTests, - FileInputTests, - Test_fileinput_input, - Test_fileinput_close, - Test_fileinput_nextfile, - Test_fileinput_filename, - Test_fileinput_lineno, - Test_fileinput_filelineno, - Test_fileinput_fileno, - Test_fileinput_isfirstline, - Test_fileinput_isstdin, - Test_hook_compressed, - Test_hook_encoded, - ) if __name__ == "__main__": - test_main() + unittest.main() diff --git a/Lib/test/test_frozen.py b/Lib/test/test_frozen.py --- a/Lib/test/test_frozen.py +++ b/Lib/test/test_frozen.py @@ -72,8 +72,6 @@ del sys.modules['__phello__'] del sys.modules['__phello__.spam'] -def test_main(): - run_unittest(FrozenTests) if __name__ == "__main__": - test_main() + unittest.main() diff --git a/Lib/test/test_getargs2.py b/Lib/test/test_getargs2.py --- a/Lib/test/test_getargs2.py +++ b/Lib/test/test_getargs2.py @@ -1,6 +1,10 @@ import unittest from test import support from _testcapi import getargs_keywords, getargs_keyword_only +try: + from _testcapi import getargs_L, getargs_K +except ImportError: + getargs_L = None # PY_LONG_LONG not available # > How about the following counterproposal. This also changes some of # > the other format codes to be a little more regular. @@ -182,6 +186,7 @@ self.assertRaises(OverflowError, getargs_n, VERY_LARGE) + at unittest.skipIf(getargs_L is None, 'PY_LONG_LONG is not available') class LongLong_TestCase(unittest.TestCase): def test_L(self): from _testcapi import getargs_L @@ -534,24 +539,5 @@ self.assertIsNone(getargs_Z_hash(None)) -def test_main(): - tests = [ - Signed_TestCase, - Unsigned_TestCase, - Boolean_TestCase, - Tuple_TestCase, - Keywords_TestCase, - KeywordOnly_TestCase, - Bytes_TestCase, - Unicode_TestCase, - ] - try: - from _testcapi import getargs_L, getargs_K - except ImportError: - pass # PY_LONG_LONG not available - else: - tests.append(LongLong_TestCase) - support.run_unittest(*tests) - if __name__ == "__main__": - test_main() + unittest.main() diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py --- a/Lib/test/test_hashlib.py +++ b/Lib/test/test_hashlib.py @@ -494,8 +494,6 @@ self.assertEqual(expected_hash, hasher.hexdigest()) -def test_main(): - support.run_unittest(HashLibTestCase) if __name__ == "__main__": - test_main() + unittest.main() diff --git a/Lib/test/test_ioctl.py b/Lib/test/test_ioctl.py --- a/Lib/test/test_ioctl.py +++ b/Lib/test/test_ioctl.py @@ -86,8 +86,6 @@ os.close(mfd) os.close(sfd) -def test_main(): - run_unittest(IoctlTests) if __name__ == "__main__": - test_main() + unittest.main() diff --git a/Lib/test/test_runpy.py b/Lib/test/test_runpy.py --- a/Lib/test/test_runpy.py +++ b/Lib/test/test_runpy.py @@ -575,12 +575,5 @@ self.assertEqual(result['s'], "non-ASCII: h\xe9") -def test_main(): - run_unittest( - ExecutionLayerTestCase, - RunModuleTestCase, - RunPathTestCase - ) - if __name__ == "__main__": - test_main() + unittest.main() diff --git a/Lib/test/test_sched.py b/Lib/test/test_sched.py --- a/Lib/test/test_sched.py +++ b/Lib/test/test_sched.py @@ -197,8 +197,5 @@ self.assertEqual(l, []) -def test_main(): - support.run_unittest(TestCase) - if __name__ == "__main__": - test_main() + unittest.main() diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py --- a/Lib/test/test_shutil.py +++ b/Lib/test/test_shutil.py @@ -1719,9 +1719,5 @@ self.assertEqual(expected, actual) -def test_main(): - support.run_unittest(TestShutil, TestMove, TestCopyFile, - TermsizeTests, TestWhich) - if __name__ == '__main__': - test_main() + unittest.main() diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py --- a/Lib/test/test_site.py +++ b/Lib/test/test_site.py @@ -401,8 +401,6 @@ else: self.fail("sitecustomize not imported automatically") -def test_main(): - run_unittest(HelperFunctionsTests, ImportSideEffectTests) if __name__ == "__main__": - test_main() + unittest.main() diff --git a/Lib/test/test_strftime.py b/Lib/test/test_strftime.py --- a/Lib/test/test_strftime.py +++ b/Lib/test/test_strftime.py @@ -176,8 +176,6 @@ (e[0], e[2])) print(" Expected %s, but got %s" % (e[1], result)) -def test_main(): - support.run_unittest(StrftimeTest) if __name__ == '__main__': - test_main() + unittest.main() diff --git a/Lib/test/test_sundry.py b/Lib/test/test_sundry.py --- a/Lib/test/test_sundry.py +++ b/Lib/test/test_sundry.py @@ -54,8 +54,5 @@ print("skipping tty") -def test_main(): - support.run_unittest(TestUntestedModules) - if __name__ == "__main__": - test_main() + unittest.main() diff --git a/Lib/test/test_urllib2net.py b/Lib/test/test_urllib2net.py --- a/Lib/test/test_urllib2net.py +++ b/Lib/test/test_urllib2net.py @@ -338,13 +338,6 @@ self.assertEqual(u.fp.fp.raw._sock.gettimeout(), 60) -def test_main(): +if __name__ == "__main__": support.requires("network") - support.run_unittest(AuthTests, - OtherNetworkTests, - CloseSocketTest, - TimeoutTest, - ) - -if __name__ == "__main__": - test_main() + unittest.main() -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 13 05:29:29 2013 From: python-checkins at python.org (brett.cannon) Date: Thu, 13 Jun 2013 05:29:29 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2315767=3A_Touch_up?= =?utf-8?q?_ModuleNotFoundError_usage_by_import=2E?= Message-ID: <3bW9RY1VTpzS4T@mail.python.org> http://hg.python.org/cpython/rev/3a50025f1900 changeset: 84109:3a50025f1900 user: Brett Cannon date: Wed Jun 12 23:29:18 2013 -0400 summary: Issue #15767: Touch up ModuleNotFoundError usage by import. Forgot to raise ModuleNotFoundError when None is found in sys.modules. This led to introducing the C function PyErr_SetImportErrorSubclass() to make setting ModuleNotFoundError easier. Also updated the reference docs to mention ModuleNotFoundError appropriately. Updated the docs for ModuleNotFoundError to mention the None in sys.modules case. Lastly, it was noticed that PyErr_SetImportError() was not setting an exception when returning None in one case. That issue is now fixed. files: Doc/c-api/exceptions.rst | 7 +++++++ Doc/library/exceptions.rst | 3 ++- Doc/reference/import.rst | 12 ++++++------ Doc/whatsnew/3.4.rst | 3 +++ Include/pyerrors.h | 3 +++ Lib/importlib/_bootstrap.py | 2 +- Misc/NEWS | 4 ++++ Python/errors.c | 25 ++++++++++++++++++++++--- Python/import.c | 3 ++- Python/importlib.h | 4 ++-- 10 files changed, 52 insertions(+), 14 deletions(-) diff --git a/Doc/c-api/exceptions.rst b/Doc/c-api/exceptions.rst --- a/Doc/c-api/exceptions.rst +++ b/Doc/c-api/exceptions.rst @@ -292,6 +292,13 @@ .. versionadded:: 3.3 +.. c:function:: PyObject* PyErr_SetImportErrorSubclass(PyObject *msg, PyObject *name, PyObject *path) + + Much like :c:func:`PyErr_SetImportError` but this function allows for + specifying a subclass of :exc:`ImportError` to raise. + + .. versionadded:: 3.4 + .. c:function:: void PyErr_SyntaxLocationEx(char *filename, int lineno, int col_offset) diff --git a/Doc/library/exceptions.rst b/Doc/library/exceptions.rst --- a/Doc/library/exceptions.rst +++ b/Doc/library/exceptions.rst @@ -185,7 +185,8 @@ A subclass of :exc:`ImportError` which is raised by :keyword:`import` when a module could not be located. This includes ``from ... import`` statements as the specific attribute being requested cannot be known a priori to be a module - or some other type of object. + or some other type of object. It is also raised when ``None`` is found in + :data:`sys.modules`. .. versionadded:: 3.4 diff --git a/Doc/reference/import.rst b/Doc/reference/import.rst --- a/Doc/reference/import.rst +++ b/Doc/reference/import.rst @@ -37,7 +37,7 @@ When a module is first imported, Python searches for the module and if found, it creates a module object [#fnmo]_, initializing it. If the named module -cannot be found, an :exc:`ImportError` is raised. Python implements various +cannot be found, an :exc:`ModuleNotFoundError` is raised. Python implements various strategies to search for the named module when the import machinery is invoked. These strategies can be modified and extended by using various hooks described in the sections below. @@ -168,7 +168,7 @@ This name will be used in various phases of the import search, and it may be the dotted path to a submodule, e.g. ``foo.bar.baz``. In this case, Python first tries to import ``foo``, then ``foo.bar``, and finally ``foo.bar.baz``. -If any of the intermediate imports fail, an :exc:`ImportError` is raised. +If any of the intermediate imports fail, an :exc:`ModuleNotFoundError` is raised. The module cache @@ -187,7 +187,7 @@ During import, the module name is looked up in :data:`sys.modules` and if present, the associated value is the module satisfying the import, and the process completes. However, if the value is ``None``, then an -:exc:`ImportError` is raised. If the module name is missing, Python will +:exc:`ModuleNotFoundError` is raised. If the module name is missing, Python will continue searching for the module. :data:`sys.modules` is writable. Deleting a key may not destroy the @@ -195,7 +195,7 @@ but it will invalidate the cache entry for the named module, causing Python to search anew for the named module upon its next import. The key can also be assigned to ``None``, forcing the next import -of the module to result in an :exc:`ImportError`. +of the module to result in an :exc:`ModuleNotFoundError`. Beware though, as if you keep a reference to the module object, invalidate its cache entry in :data:`sys.modules`, and then re-import the @@ -284,7 +284,7 @@ If the meta path finder knows how to handle the named module, it returns a loader object. If it cannot handle the named module, it returns ``None``. If :data:`sys.meta_path` processing reaches the end of its list without returning -a loader, then an :exc:`ImportError` is raised. Any other exceptions raised +a loader, then an :exc:`ModuleNotFoundError` is raised. Any other exceptions raised are simply propagated up, aborting the import process. The :meth:`find_module()` method of meta path finders is called with two @@ -647,7 +647,7 @@ To selectively prevent import of some modules from a hook early on the meta path (rather than disabling the standard import system entirely), -it is sufficient to raise :exc:`ImportError` directly from +it is sufficient to raise :exc:`ModuleNotFoundError` directly from :meth:`find_module` instead of returning ``None``. The latter indicates that the meta path search should continue. while raising an exception terminates it immediately. diff --git a/Doc/whatsnew/3.4.rst b/Doc/whatsnew/3.4.rst --- a/Doc/whatsnew/3.4.rst +++ b/Doc/whatsnew/3.4.rst @@ -269,3 +269,6 @@ * Frozen packages no longer set ``__path__`` to a list containg the package name but an empty list instead. Determing if a module is a package should be done using ``hasattr(module, '__path__')``. + +* :c:func:`PyErr_SetImportError` now sets :exc:`TypeError` when its **msg** + argument is not set. Previously only ``NULL`` was returned. diff --git a/Include/pyerrors.h b/Include/pyerrors.h --- a/Include/pyerrors.h +++ b/Include/pyerrors.h @@ -268,6 +268,9 @@ PyAPI_FUNC(PyObject *) PyErr_SetExcWithArgsKwargs(PyObject *, PyObject *, PyObject *); + +PyAPI_FUNC(PyObject *) PyErr_SetImportErrorSubclass(PyObject *, PyObject *, + PyObject *, PyObject *); PyAPI_FUNC(PyObject *) PyErr_SetImportError(PyObject *, PyObject *, PyObject *); diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py --- a/Lib/importlib/_bootstrap.py +++ b/Lib/importlib/_bootstrap.py @@ -1614,7 +1614,7 @@ _imp.release_lock() message = ("import of {} halted; " "None in sys.modules".format(name)) - raise ImportError(message, name=name) + raise ModuleNotFoundError(message, name=name) _lock_unlock_module(name) return module diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -443,6 +443,10 @@ C-API ----- +- Issue #15767: Added PyErr_SetImportErrorSubclass(). + +- PyErr_SetImportError() now sets TypeError when its msg argument is set. + - Issue #9369: The types of `char*` arguments of PyObject_CallFunction() and PyObject_CallMethod() now changed to `const char*`. Based on patches by J?rg M?ller and Lars Buitinck. diff --git a/Python/errors.c b/Python/errors.c --- a/Python/errors.c +++ b/Python/errors.c @@ -619,12 +619,25 @@ #endif /* MS_WINDOWS */ PyObject * -PyErr_SetImportError(PyObject *msg, PyObject *name, PyObject *path) +PyErr_SetImportErrorSubclass(PyObject *exception, PyObject *msg, + PyObject *name, PyObject *path) { + int issubclass; PyObject *args, *kwargs, *error; - if (msg == NULL) + issubclass = PyObject_IsSubclass(exception, PyExc_ImportError); + if (issubclass < 0) { return NULL; + } + else if (!issubclass) { + PyErr_SetString(PyExc_TypeError, "expected a subclass of ImportError"); + return NULL; + } + + if (msg == NULL) { + PyErr_SetString(PyExc_TypeError, "expected a message argument"); + return NULL; + } args = PyTuple_New(1); if (args == NULL) @@ -649,7 +662,7 @@ PyDict_SetItemString(kwargs, "name", name); PyDict_SetItemString(kwargs, "path", path); - error = PyObject_Call(PyExc_ImportError, args, kwargs); + error = PyObject_Call(exception, args, kwargs); if (error != NULL) { PyErr_SetObject((PyObject *)Py_TYPE(error), error); Py_DECREF(error); @@ -661,6 +674,12 @@ return NULL; } +PyObject * +PyErr_SetImportError(PyObject *msg, PyObject *name, PyObject *path) +{ + return PyErr_SetImportErrorSubclass(PyExc_ImportError, msg, name, path); +} + void _PyErr_BadInternalCall(const char *filename, int lineno) { diff --git a/Python/import.c b/Python/import.c --- a/Python/import.c +++ b/Python/import.c @@ -1436,7 +1436,8 @@ PyObject *msg = PyUnicode_FromFormat("import of %R halted; " "None in sys.modules", abs_name); if (msg != NULL) { - PyErr_SetImportError(msg, abs_name, NULL); + PyErr_SetImportErrorSubclass(PyExc_ModuleNotFoundError, msg, + abs_name, NULL); Py_DECREF(msg); } mod = NULL; diff --git a/Python/importlib.h b/Python/importlib.h --- a/Python/importlib.h +++ b/Python/importlib.h [stripped] -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 13 05:38:58 2013 From: python-checkins at python.org (brett.cannon) Date: Thu, 13 Jun 2013 05:38:58 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2315767=3A_Add_an_e?= =?utf-8?q?xplicit_test_for_raising_ModuleNotFoundError?= Message-ID: <3bW9fV3JZ1zSBf@mail.python.org> http://hg.python.org/cpython/rev/c6c0faaf65d7 changeset: 84110:c6c0faaf65d7 user: Brett Cannon date: Wed Jun 12 23:38:50 2013 -0400 summary: Issue #15767: Add an explicit test for raising ModuleNotFoundError when None in sys.modules. files: Lib/test/test_importlib/import_/test_api.py | 7 +++++++ 1 files changed, 7 insertions(+), 0 deletions(-) diff --git a/Lib/test/test_importlib/import_/test_api.py b/Lib/test/test_importlib/import_/test_api.py --- a/Lib/test/test_importlib/import_/test_api.py +++ b/Lib/test/test_importlib/import_/test_api.py @@ -26,6 +26,13 @@ with self.assertRaises(ModuleNotFoundError): util.import_('some module that does not exist') + def test_raises_ModuleNotFoundError_for_None(self): + # None in sys.modules should raise ModuleNotFoundError. + with importlib_test_util.uncache('not_here'): + sys.modules['not_here'] = None + with self.assertRaises(ModuleNotFoundError): + util.import_('not_here') + def test_name_requires_rparition(self): # Raise TypeError if a non-string is passed in for the module name. with self.assertRaises(TypeError): -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Thu Jun 13 05:50:15 2013 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Thu, 13 Jun 2013 05:50:15 +0200 Subject: [Python-checkins] Daily reference leaks (05f7883ee92d): sum=0 Message-ID: results for 05f7883ee92d on branch "default" -------------------------------------------- Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogUFNR7J', '-x'] From python-checkins at python.org Thu Jun 13 09:12:55 2013 From: python-checkins at python.org (serhiy.storchaka) Date: Thu, 13 Jun 2013 09:12:55 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2318048=3A_Rename_t?= =?utf-8?q?est=5Fpep263=2Epy_to_test=5Fsource=5Fencoding=2Epy=2E?= Message-ID: <3bWGPM2nBVzRhf@mail.python.org> http://hg.python.org/cpython/rev/00a199c265c3 changeset: 84111:00a199c265c3 user: Serhiy Storchaka date: Thu Jun 13 09:48:15 2013 +0300 summary: Issue #18048: Rename test_pep263.py to test_source_encoding.py. files: Lib/test/test_pep263.py | 0 1 files changed, 0 insertions(+), 0 deletions(-) diff --git a/Lib/test/test_pep263.py b/Lib/test/test_source_encoding.py rename from Lib/test/test_pep263.py rename to Lib/test/test_source_encoding.py -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 13 09:12:56 2013 From: python-checkins at python.org (serhiy.storchaka) Date: Thu, 13 Jun 2013 09:12:56 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2318048=3A_Rename_t?= =?utf-8?q?est=5Fcoding=2Epy_to_test=5Fsource=5Fencoding=2Epy=2E?= Message-ID: <3bWGPN4Zm0zSdC@mail.python.org> http://hg.python.org/cpython/rev/3b906421245d changeset: 84112:3b906421245d parent: 84110:c6c0faaf65d7 user: Serhiy Storchaka date: Thu Jun 13 09:50:42 2013 +0300 summary: Issue #18048: Rename test_coding.py to test_source_encoding.py. files: Lib/test/test_coding.py | 0 1 files changed, 0 insertions(+), 0 deletions(-) diff --git a/Lib/test/test_coding.py b/Lib/test/test_source_encoding.py rename from Lib/test/test_coding.py rename to Lib/test/test_source_encoding.py -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 13 09:12:58 2013 From: python-checkins at python.org (serhiy.storchaka) Date: Thu, 13 Jun 2013 09:12:58 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_default_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2318048=3A_Merge_test=5Fpep263=2Epy_and_test=5Fco?= =?utf-8?q?ding=2Epy_into?= Message-ID: <3bWGPQ0NWFz7Ljj@mail.python.org> http://hg.python.org/cpython/rev/464e8fd7300d changeset: 84113:464e8fd7300d parent: 84112:3b906421245d parent: 84111:00a199c265c3 user: Serhiy Storchaka date: Thu Jun 13 10:08:00 2013 +0300 summary: Issue #18048: Merge test_pep263.py and test_coding.py into test_source_encoding.py. files: Lib/test/test_pep263.py | 80 ------------------- Lib/test/test_source_encoding.py | 83 ++++++++++++++++++- 2 files changed, 76 insertions(+), 87 deletions(-) diff --git a/Lib/test/test_pep263.py b/Lib/test/test_pep263.py deleted file mode 100644 --- a/Lib/test/test_pep263.py +++ /dev/null @@ -1,80 +0,0 @@ -# -*- coding: koi8-r -*- - -import unittest -from test import support - -class PEP263Test(unittest.TestCase): - - def test_pep263(self): - self.assertEqual( - "?????".encode("utf-8"), - b'\xd0\x9f\xd0\xb8\xd1\x82\xd0\xbe\xd0\xbd' - ) - self.assertEqual( - "\?".encode("utf-8"), - b'\\\xd0\x9f' - ) - - def test_compilestring(self): - # see #1882 - c = compile(b"\n# coding: utf-8\nu = '\xc3\xb3'\n", "dummy", "exec") - d = {} - exec(c, d) - self.assertEqual(d['u'], '\xf3') - - def test_issue2301(self): - try: - compile(b"# coding: cp932\nprint '\x94\x4e'", "dummy", "exec") - except SyntaxError as v: - self.assertEqual(v.text, "print '\u5e74'\n") - else: - self.fail() - - def test_issue4626(self): - c = compile("# coding=latin-1\n\u00c6 = '\u00c6'", "dummy", "exec") - d = {} - exec(c, d) - self.assertEqual(d['\xc6'], '\xc6') - - def test_issue3297(self): - c = compile("a, b = '\U0001010F', '\\U0001010F'", "dummy", "exec") - d = {} - exec(c, d) - self.assertEqual(d['a'], d['b']) - self.assertEqual(len(d['a']), len(d['b'])) - self.assertEqual(ascii(d['a']), ascii(d['b'])) - - def test_issue7820(self): - # Ensure that check_bom() restores all bytes in the right order if - # check_bom() fails in pydebug mode: a buffer starts with the first - # byte of a valid BOM, but next bytes are different - - # one byte in common with the UTF-16-LE BOM - self.assertRaises(SyntaxError, eval, b'\xff\x20') - - # two bytes in common with the UTF-8 BOM - self.assertRaises(SyntaxError, eval, b'\xef\xbb\x20') - - def test_error_message(self): - compile(b'# -*- coding: iso-8859-15 -*-\n', 'dummy', 'exec') - compile(b'\xef\xbb\xbf\n', 'dummy', 'exec') - compile(b'\xef\xbb\xbf# -*- coding: utf-8 -*-\n', 'dummy', 'exec') - with self.assertRaisesRegexp(SyntaxError, 'fake'): - compile(b'# -*- coding: fake -*-\n', 'dummy', 'exec') - with self.assertRaisesRegexp(SyntaxError, 'iso-8859-15'): - compile(b'\xef\xbb\xbf# -*- coding: iso-8859-15 -*-\n', - 'dummy', 'exec') - with self.assertRaisesRegexp(SyntaxError, 'BOM'): - compile(b'\xef\xbb\xbf# -*- coding: iso-8859-15 -*-\n', - 'dummy', 'exec') - with self.assertRaisesRegexp(SyntaxError, 'fake'): - compile(b'\xef\xbb\xbf# -*- coding: fake -*-\n', 'dummy', 'exec') - with self.assertRaisesRegexp(SyntaxError, 'BOM'): - compile(b'\xef\xbb\xbf# -*- coding: fake -*-\n', 'dummy', 'exec') - - -def test_main(): - support.run_unittest(PEP263Test) - -if __name__=="__main__": - test_main() diff --git a/Lib/test/test_source_encoding.py b/Lib/test/test_source_encoding.py --- a/Lib/test/test_source_encoding.py +++ b/Lib/test/test_source_encoding.py @@ -1,8 +1,80 @@ -import test.support, unittest +# -*- coding: koi8-r -*- + +import unittest from test.support import TESTFN, unlink, unload -import importlib, os, sys +import importlib +import os +import sys -class CodingTest(unittest.TestCase): +class SourceEncodingTest(unittest.TestCase): + + def test_pep263(self): + self.assertEqual( + "?????".encode("utf-8"), + b'\xd0\x9f\xd0\xb8\xd1\x82\xd0\xbe\xd0\xbd' + ) + self.assertEqual( + "\?".encode("utf-8"), + b'\\\xd0\x9f' + ) + + def test_compilestring(self): + # see #1882 + c = compile(b"\n# coding: utf-8\nu = '\xc3\xb3'\n", "dummy", "exec") + d = {} + exec(c, d) + self.assertEqual(d['u'], '\xf3') + + def test_issue2301(self): + try: + compile(b"# coding: cp932\nprint '\x94\x4e'", "dummy", "exec") + except SyntaxError as v: + self.assertEqual(v.text, "print '\u5e74'\n") + else: + self.fail() + + def test_issue4626(self): + c = compile("# coding=latin-1\n\u00c6 = '\u00c6'", "dummy", "exec") + d = {} + exec(c, d) + self.assertEqual(d['\xc6'], '\xc6') + + def test_issue3297(self): + c = compile("a, b = '\U0001010F', '\\U0001010F'", "dummy", "exec") + d = {} + exec(c, d) + self.assertEqual(d['a'], d['b']) + self.assertEqual(len(d['a']), len(d['b'])) + self.assertEqual(ascii(d['a']), ascii(d['b'])) + + def test_issue7820(self): + # Ensure that check_bom() restores all bytes in the right order if + # check_bom() fails in pydebug mode: a buffer starts with the first + # byte of a valid BOM, but next bytes are different + + # one byte in common with the UTF-16-LE BOM + self.assertRaises(SyntaxError, eval, b'\xff\x20') + + # two bytes in common with the UTF-8 BOM + self.assertRaises(SyntaxError, eval, b'\xef\xbb\x20') + + def test_error_message(self): + compile(b'# -*- coding: iso-8859-15 -*-\n', 'dummy', 'exec') + compile(b'\xef\xbb\xbf\n', 'dummy', 'exec') + compile(b'\xef\xbb\xbf# -*- coding: utf-8 -*-\n', 'dummy', 'exec') + with self.assertRaisesRegexp(SyntaxError, 'fake'): + compile(b'# -*- coding: fake -*-\n', 'dummy', 'exec') + with self.assertRaisesRegexp(SyntaxError, 'iso-8859-15'): + compile(b'\xef\xbb\xbf# -*- coding: iso-8859-15 -*-\n', + 'dummy', 'exec') + with self.assertRaisesRegexp(SyntaxError, 'BOM'): + compile(b'\xef\xbb\xbf# -*- coding: iso-8859-15 -*-\n', + 'dummy', 'exec') + with self.assertRaisesRegexp(SyntaxError, 'fake'): + compile(b'\xef\xbb\xbf# -*- coding: fake -*-\n', 'dummy', 'exec') + with self.assertRaisesRegexp(SyntaxError, 'BOM'): + compile(b'\xef\xbb\xbf# -*- coding: fake -*-\n', 'dummy', 'exec') + def test_bad_coding(self): module_name = 'bad_coding' self.verify_bad_module(module_name) @@ -58,8 +130,5 @@ self.assertTrue(c.exception.args[0].startswith(expected)) -def test_main(): - test.support.run_unittest(CodingTest) - if __name__ == "__main__": - test_main() + unittest.main() -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 14 02:57:40 2013 From: python-checkins at python.org (brett.cannon) Date: Fri, 14 Jun 2013 02:57:40 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2318200=3A_Update_t?= =?utf-8?q?he_stdlib_=28except_tests=29_to_use?= Message-ID: <3bWk1w4xwxzRhj@mail.python.org> http://hg.python.org/cpython/rev/8d28d44f3a9a changeset: 84114:8d28d44f3a9a user: Brett Cannon date: Thu Jun 13 20:57:26 2013 -0400 summary: Issue #18200: Update the stdlib (except tests) to use ModuleNotFoundError. files: Lib/_dummy_thread.py | 2 +- Lib/_osx_support.py | 2 +- Lib/_pyio.py | 4 ++-- Lib/_strptime.py | 2 +- Lib/bisect.py | 2 +- Lib/bz2.py | 2 +- Lib/cmd.py | 4 ++-- Lib/code.py | 2 +- Lib/collections/__init__.py | 2 +- Lib/copy.py | 2 +- Lib/datetime.py | 2 +- Lib/decimal.py | 8 ++++---- Lib/distutils/archive_util.py | 2 +- Lib/distutils/ccompiler.py | 7 +++---- Lib/distutils/dist.py | 9 ++++----- Lib/distutils/msvccompiler.py | 4 ++-- Lib/distutils/util.py | 2 +- Lib/encodings/__init__.py | 7 +++---- Lib/ftplib.py | 2 +- Lib/functools.py | 6 +++--- Lib/getopt.py | 2 +- Lib/getpass.py | 2 +- Lib/hashlib.py | 4 ++-- Lib/heapq.py | 2 +- Lib/http/client.py | 2 +- Lib/http/cookiejar.py | 2 +- Lib/http/server.py | 2 +- Lib/imaplib.py | 2 +- Lib/imp.py | 2 +- Lib/importlib/__init__.py | 2 +- Lib/importlib/abc.py | 2 +- Lib/inspect.py | 2 +- Lib/json/decoder.py | 2 +- Lib/json/encoder.py | 4 ++-- Lib/json/scanner.py | 2 +- Lib/lib2to3/refactor.py | 2 +- Lib/locale.py | 2 +- Lib/logging/__init__.py | 2 +- Lib/logging/config.py | 2 +- Lib/logging/handlers.py | 4 ++-- Lib/macpath.py | 2 +- Lib/mailbox.py | 2 +- Lib/mimetypes.py | 2 +- Lib/multiprocessing/connection.py | 2 +- Lib/multiprocessing/forking.py | 2 +- Lib/nntplib.py | 2 +- Lib/ntpath.py | 4 ++-- Lib/operator.py | 2 +- Lib/optparse.py | 2 +- Lib/os.py | 14 +++++++------- Lib/pdb.py | 2 +- Lib/pickle.py | 4 ++-- Lib/platform.py | 14 +++++++------- Lib/poplib.py | 2 +- Lib/pstats.py | 2 +- Lib/pty.py | 2 +- Lib/pydoc.py | 4 ++-- Lib/queue.py | 4 ++-- Lib/quopri.py | 2 +- Lib/reprlib.py | 2 +- Lib/rlcompleter.py | 2 +- Lib/sched.py | 4 ++-- Lib/shutil.py | 10 +++++----- Lib/site.py | 6 +++--- Lib/smtpd.py | 2 +- Lib/smtplib.py | 2 +- Lib/socket.py | 2 +- Lib/socketserver.py | 2 +- Lib/sqlite3/test/dbapi.py | 2 +- Lib/sqlite3/test/types.py | 2 +- Lib/sre_compile.py | 2 +- Lib/ssl.py | 4 ++-- Lib/subprocess.py | 2 +- Lib/tarfile.py | 12 ++++++------ Lib/tempfile.py | 4 ++-- Lib/threading.py | 6 +++--- Lib/trace.py | 4 ++-- Lib/urllib/request.py | 6 +++--- Lib/venv/__init__.py | 2 +- Lib/warnings.py | 6 +++--- Lib/xml/etree/ElementTree.py | 10 +++++----- Lib/xml/sax/expatreader.py | 17 +++-------------- Lib/xmlrpc/client.py | 2 +- 83 files changed, 144 insertions(+), 158 deletions(-) diff --git a/Lib/_dummy_thread.py b/Lib/_dummy_thread.py --- a/Lib/_dummy_thread.py +++ b/Lib/_dummy_thread.py @@ -7,7 +7,7 @@ try: import _thread - except ImportError: + except ModuleNotFoundError: import _dummy_thread as _thread """ diff --git a/Lib/_osx_support.py b/Lib/_osx_support.py --- a/Lib/_osx_support.py +++ b/Lib/_osx_support.py @@ -62,7 +62,7 @@ try: import tempfile fp = tempfile.NamedTemporaryFile() - except ImportError: + except ModuleNotFoundError: fp = open("/tmp/_osx_support.%s"%( os.getpid(),), "w+b") diff --git a/Lib/_pyio.py b/Lib/_pyio.py --- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -9,7 +9,7 @@ # Import _thread instead of threading to reduce startup cost try: from _thread import allocate_lock as Lock -except ImportError: +except ModuleNotFoundError: from _dummy_thread import allocate_lock as Lock import io @@ -1486,7 +1486,7 @@ if encoding is None: try: import locale - except ImportError: + except ModuleNotFoundError: # Importing locale may fail if Python is being built encoding = "ascii" else: diff --git a/Lib/_strptime.py b/Lib/_strptime.py --- a/Lib/_strptime.py +++ b/Lib/_strptime.py @@ -21,7 +21,7 @@ timezone as datetime_timezone) try: from _thread import allocate_lock as _thread_allocate_lock -except ImportError: +except ModuleNotFoundError: from _dummy_thread import allocate_lock as _thread_allocate_lock __all__ = [] diff --git a/Lib/bisect.py b/Lib/bisect.py --- a/Lib/bisect.py +++ b/Lib/bisect.py @@ -88,5 +88,5 @@ # Overwrite above definitions with a fast C implementation try: from _bisect import * -except ImportError: +except ModuleNotFoundError: pass diff --git a/Lib/bz2.py b/Lib/bz2.py --- a/Lib/bz2.py +++ b/Lib/bz2.py @@ -14,7 +14,7 @@ try: from threading import RLock -except ImportError: +except ModuleNotFoundError: from dummy_threading import RLock from _bz2 import BZ2Compressor, BZ2Decompressor diff --git a/Lib/cmd.py b/Lib/cmd.py --- a/Lib/cmd.py +++ b/Lib/cmd.py @@ -109,7 +109,7 @@ self.old_completer = readline.get_completer() readline.set_completer(self.complete) readline.parse_and_bind(self.completekey+": complete") - except ImportError: + except ModuleNotFoundError: pass try: if intro is not None: @@ -143,7 +143,7 @@ try: import readline readline.set_completer(self.old_completer) - except ImportError: + except ModuleNotFoundError: pass diff --git a/Lib/code.py b/Lib/code.py --- a/Lib/code.py +++ b/Lib/code.py @@ -293,7 +293,7 @@ else: try: import readline - except ImportError: + except ModuleNotFoundError: pass console.interact(banner) diff --git a/Lib/collections/__init__.py b/Lib/collections/__init__.py --- a/Lib/collections/__init__.py +++ b/Lib/collections/__init__.py @@ -395,7 +395,7 @@ try: # Load C helper function if available from _collections import _count_elements -except ImportError: +except ModuleNotFoundError: pass class Counter(dict): diff --git a/Lib/copy.py b/Lib/copy.py --- a/Lib/copy.py +++ b/Lib/copy.py @@ -59,7 +59,7 @@ try: from org.python.core import PyStringMap -except ImportError: +except ModuleNotFoundError: PyStringMap = None __all__ = ["Error", "copy", "deepcopy"] diff --git a/Lib/datetime.py b/Lib/datetime.py --- a/Lib/datetime.py +++ b/Lib/datetime.py @@ -2116,7 +2116,7 @@ try: from _datetime import * -except ImportError: +except ModuleNotFoundError: pass else: # Clean up unused names diff --git a/Lib/decimal.py b/Lib/decimal.py --- a/Lib/decimal.py +++ b/Lib/decimal.py @@ -149,7 +149,7 @@ try: from collections import namedtuple as _namedtuple DecimalTuple = _namedtuple('DecimalTuple', 'sign digits exponent') -except ImportError: +except ModuleNotFoundError: DecimalTuple = lambda *args: args # Rounding @@ -430,7 +430,7 @@ try: import threading -except ImportError: +except ModuleNotFoundError: # Python was compiled without threads; create a mock object instead class MockThreading(object): def local(self, sys=sys): @@ -6147,7 +6147,7 @@ # don't care too much if locale isn't present. try: import locale as _locale -except ImportError: +except ModuleNotFoundError: pass def _parse_format_specifier(format_spec, _localeconv=None): @@ -6391,7 +6391,7 @@ try: import _decimal -except ImportError: +except ModuleNotFoundError: pass else: s1 = set(dir()) diff --git a/Lib/distutils/archive_util.py b/Lib/distutils/archive_util.py --- a/Lib/distutils/archive_util.py +++ b/Lib/distutils/archive_util.py @@ -9,7 +9,7 @@ try: import zipfile -except ImportError: +except ModuleNotFoundError: zipfile = None diff --git a/Lib/distutils/ccompiler.py b/Lib/distutils/ccompiler.py --- a/Lib/distutils/ccompiler.py +++ b/Lib/distutils/ccompiler.py @@ -3,7 +3,7 @@ Contains CCompiler, an abstract base class that defines the interface for the Distutils compiler abstraction model.""" -import sys, os, re +import importlib, sys, os, re from distutils.errors import * from distutils.spawn import spawn from distutils.file_util import move_file @@ -1013,10 +1013,9 @@ try: module_name = "distutils." + module_name - __import__ (module_name) - module = sys.modules[module_name] + module = importlib.import_module(module_name) klass = vars(module)[class_name] - except ImportError: + except ModuleNotFoundError: raise DistutilsModuleError( "can't compile C/C++ code: unable to load module '%s'" % \ module_name) diff --git a/Lib/distutils/dist.py b/Lib/distutils/dist.py --- a/Lib/distutils/dist.py +++ b/Lib/distutils/dist.py @@ -4,11 +4,11 @@ being built/installed/distributed. """ -import sys, os, re +import importlib, sys, os, re try: import warnings -except ImportError: +except ModuleNotFoundError: warnings = None from distutils.errors import * @@ -788,9 +788,8 @@ klass_name = command try: - __import__ (module_name) - module = sys.modules[module_name] - except ImportError: + module = importlib.import_module(module_name) + except ModuleNotFoundError: continue try: diff --git a/Lib/distutils/msvccompiler.py b/Lib/distutils/msvccompiler.py --- a/Lib/distutils/msvccompiler.py +++ b/Lib/distutils/msvccompiler.py @@ -28,7 +28,7 @@ RegEnumValue = winreg.EnumValue RegError = winreg.error -except ImportError: +except ModuleNotFoundError: try: import win32api import win32con @@ -39,7 +39,7 @@ RegEnumKey = win32api.RegEnumKey RegEnumValue = win32api.RegEnumValue RegError = win32api.error - except ImportError: + except ModuleNotFoundError: log.info("Warning: Can't read registry to find the " "necessary compiler setting\n" "Make sure that Python modules winreg, " diff --git a/Lib/distutils/util.py b/Lib/distutils/util.py --- a/Lib/distutils/util.py +++ b/Lib/distutils/util.py @@ -388,7 +388,7 @@ try: from tempfile import mkstemp (script_fd, script_name) = mkstemp(".py") - except ImportError: + except ModuleNotFoundError: from tempfile import mktemp (script_fd, script_name) = None, mktemp(".py") log.info("writing byte-compilation script '%s'", script_name) diff --git a/Lib/encodings/__init__.py b/Lib/encodings/__init__.py --- a/Lib/encodings/__init__.py +++ b/Lib/encodings/__init__.py @@ -29,11 +29,11 @@ """#" import codecs +import importlib from . import aliases _cache = {} _unknown = '--unknown--' -_import_tail = ['*'] _aliases = aliases.aliases class CodecRegistryError(LookupError, SystemError): @@ -94,9 +94,8 @@ try: # Import is absolute to prevent the possibly malicious import of a # module with side-effects that is not in the 'encodings' package. - mod = __import__('encodings.' + modname, fromlist=_import_tail, - level=0) - except ImportError: + mod = importlib.import_module('encodings.' + modname) + except ModuleNotFoundError: pass else: break diff --git a/Lib/ftplib.py b/Lib/ftplib.py --- a/Lib/ftplib.py +++ b/Lib/ftplib.py @@ -667,7 +667,7 @@ try: import ssl -except ImportError: +except ModuleNotFoundError: _SSLSocket = None else: _SSLSocket = ssl.SSLSocket diff --git a/Lib/functools.py b/Lib/functools.py --- a/Lib/functools.py +++ b/Lib/functools.py @@ -15,7 +15,7 @@ try: from _functools import reduce -except ImportError: +except ModuleNotFoundError: pass from abc import get_cache_token from collections import namedtuple @@ -143,7 +143,7 @@ try: from _functools import cmp_to_key -except ImportError: +except ModuleNotFoundError: pass @@ -166,7 +166,7 @@ try: from _functools import partial -except ImportError: +except ModuleNotFoundError: pass diff --git a/Lib/getopt.py b/Lib/getopt.py --- a/Lib/getopt.py +++ b/Lib/getopt.py @@ -36,7 +36,7 @@ import os try: from gettext import gettext as _ -except ImportError: +except ModuleNotFoundError: # Bootstrapping Python: gettext's dependencies not built yet def _(s): return s diff --git a/Lib/getpass.py b/Lib/getpass.py --- a/Lib/getpass.py +++ b/Lib/getpass.py @@ -164,7 +164,7 @@ except (ImportError, AttributeError): try: import msvcrt - except ImportError: + except ModuleNotFoundError: getpass = fallback_getpass else: getpass = win_getpass diff --git a/Lib/hashlib.py b/Lib/hashlib.py --- a/Lib/hashlib.py +++ b/Lib/hashlib.py @@ -98,7 +98,7 @@ return _sha3.sha3_384 elif bs == '512': return _sha3.sha3_512 - except ImportError: + except ModuleNotFoundError: pass # no extension module, this hash is unsupported. raise ValueError('unsupported hash type ' + name) @@ -143,7 +143,7 @@ __get_hash = __get_openssl_constructor algorithms_available = algorithms_available.union( _hashlib.openssl_md_meth_names) -except ImportError: +except ModuleNotFoundError: new = __py_new __get_hash = __get_builtin_constructor diff --git a/Lib/heapq.py b/Lib/heapq.py --- a/Lib/heapq.py +++ b/Lib/heapq.py @@ -343,7 +343,7 @@ # If available, use C implementation try: from _heapq import * -except ImportError: +except ModuleNotFoundError: pass def merge(*iterables): diff --git a/Lib/http/client.py b/Lib/http/client.py --- a/Lib/http/client.py +++ b/Lib/http/client.py @@ -1156,7 +1156,7 @@ try: import ssl -except ImportError: +except ModuleNotFoundError: pass else: class HTTPSConnection(HTTPConnection): diff --git a/Lib/http/cookiejar.py b/Lib/http/cookiejar.py --- a/Lib/http/cookiejar.py +++ b/Lib/http/cookiejar.py @@ -35,7 +35,7 @@ import urllib.parse, urllib.request try: import threading as _threading -except ImportError: +except ModuleNotFoundError: import dummy_threading as _threading import http.client # only for the default HTTP port from calendar import timegm diff --git a/Lib/http/server.py b/Lib/http/server.py --- a/Lib/http/server.py +++ b/Lib/http/server.py @@ -904,7 +904,7 @@ return nobody try: import pwd - except ImportError: + except ModuleNotFoundError: return -1 try: nobody = pwd.getpwnam('nobody')[2] diff --git a/Lib/imaplib.py b/Lib/imaplib.py --- a/Lib/imaplib.py +++ b/Lib/imaplib.py @@ -29,7 +29,7 @@ try: import ssl HAVE_SSL = True -except ImportError: +except ModuleNotFoundError: HAVE_SSL = False __all__ = ["IMAP4", "IMAP4_stream", "Internaldate2tuple", diff --git a/Lib/imp.py b/Lib/imp.py --- a/Lib/imp.py +++ b/Lib/imp.py @@ -12,7 +12,7 @@ _fix_co_filename) try: from _imp import load_dynamic -except ImportError: +except ModuleNotFoundError: # Platform doesn't support dynamic loading. load_dynamic = None diff --git a/Lib/importlib/__init__.py b/Lib/importlib/__init__.py --- a/Lib/importlib/__init__.py +++ b/Lib/importlib/__init__.py @@ -14,7 +14,7 @@ try: import _frozen_importlib as _bootstrap -except ImportError: +except ModuleNotFoundError: from . import _bootstrap _bootstrap._setup(sys, _imp) else: diff --git a/Lib/importlib/abc.py b/Lib/importlib/abc.py --- a/Lib/importlib/abc.py +++ b/Lib/importlib/abc.py @@ -3,7 +3,7 @@ from . import machinery try: import _frozen_importlib -except ImportError as exc: +except ModuleNotFoundError as exc: if exc.name != '_frozen_importlib': raise _frozen_importlib = None diff --git a/Lib/inspect.py b/Lib/inspect.py --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -51,7 +51,7 @@ # back to hardcording so the dependency is optional try: from dis import COMPILER_FLAG_NAMES as _flag_names -except ImportError: +except ModuleNotFoundError: CO_OPTIMIZED, CO_NEWLOCALS = 0x1, 0x2 CO_VARARGS, CO_VARKEYWORDS = 0x4, 0x8 CO_NESTED, CO_GENERATOR, CO_NOFREE = 0x10, 0x20, 0x40 diff --git a/Lib/json/decoder.py b/Lib/json/decoder.py --- a/Lib/json/decoder.py +++ b/Lib/json/decoder.py @@ -5,7 +5,7 @@ from json import scanner try: from _json import scanstring as c_scanstring -except ImportError: +except ModuleNotFoundError: c_scanstring = None __all__ = ['JSONDecoder'] diff --git a/Lib/json/encoder.py b/Lib/json/encoder.py --- a/Lib/json/encoder.py +++ b/Lib/json/encoder.py @@ -4,11 +4,11 @@ try: from _json import encode_basestring_ascii as c_encode_basestring_ascii -except ImportError: +except ModuleNotFoundError: c_encode_basestring_ascii = None try: from _json import make_encoder as c_make_encoder -except ImportError: +except ModuleNotFoundError: c_make_encoder = None ESCAPE = re.compile(r'[\x00-\x1f\\"\b\f\n\r\t]') diff --git a/Lib/json/scanner.py b/Lib/json/scanner.py --- a/Lib/json/scanner.py +++ b/Lib/json/scanner.py @@ -3,7 +3,7 @@ import re try: from _json import make_scanner as c_make_scanner -except ImportError: +except ModuleNotFoundError: c_make_scanner = None __all__ = ['make_scanner'] diff --git a/Lib/lib2to3/refactor.py b/Lib/lib2to3/refactor.py --- a/Lib/lib2to3/refactor.py +++ b/Lib/lib2to3/refactor.py @@ -706,7 +706,7 @@ items, write, doctests_only) try: import multiprocessing - except ImportError: + except ModuleNotFoundError: raise MultiprocessingUnsupported if self.queue is not None: raise RuntimeError("already doing multiple processes") diff --git a/Lib/locale.py b/Lib/locale.py --- a/Lib/locale.py +++ b/Lib/locale.py @@ -47,7 +47,7 @@ from _locale import * -except ImportError: +except ModuleNotFoundError: # Locale emulation diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py --- a/Lib/logging/__init__.py +++ b/Lib/logging/__init__.py @@ -37,7 +37,7 @@ try: import threading -except ImportError: #pragma: no cover +except ModuleNotFoundError: #pragma: no cover threading = None __author__ = "Vinay Sajip " diff --git a/Lib/logging/config.py b/Lib/logging/config.py --- a/Lib/logging/config.py +++ b/Lib/logging/config.py @@ -30,7 +30,7 @@ try: import _thread as thread import threading -except ImportError: #pragma: no cover +except ModuleNotFoundError: #pragma: no cover thread = None from socketserver import ThreadingTCPServer, StreamRequestHandler diff --git a/Lib/logging/handlers.py b/Lib/logging/handlers.py --- a/Lib/logging/handlers.py +++ b/Lib/logging/handlers.py @@ -29,7 +29,7 @@ import queue try: import threading -except ImportError: #pragma: no cover +except ModuleNotFoundError: #pragma: no cover threading = None # @@ -995,7 +995,7 @@ logging.ERROR : win32evtlog.EVENTLOG_ERROR_TYPE, logging.CRITICAL: win32evtlog.EVENTLOG_ERROR_TYPE, } - except ImportError: + except ModuleNotFoundError: print("The Python Win32 extensions for NT (service, event "\ "logging) appear not to be available.") self._welu = None diff --git a/Lib/macpath.py b/Lib/macpath.py --- a/Lib/macpath.py +++ b/Lib/macpath.py @@ -187,7 +187,7 @@ path = abspath(path) try: import Carbon.File - except ImportError: + except ModuleNotFoundError: return path if not path: return path diff --git a/Lib/mailbox.py b/Lib/mailbox.py --- a/Lib/mailbox.py +++ b/Lib/mailbox.py @@ -23,7 +23,7 @@ import contextlib try: import fcntl -except ImportError: +except ModuleNotFoundError: fcntl = None __all__ = [ 'Mailbox', 'Maildir', 'mbox', 'MH', 'Babyl', 'MMDF', diff --git a/Lib/mimetypes.py b/Lib/mimetypes.py --- a/Lib/mimetypes.py +++ b/Lib/mimetypes.py @@ -29,7 +29,7 @@ import urllib.parse try: import winreg as _winreg -except ImportError: +except ModuleNotFoundError: _winreg = None __all__ = [ diff --git a/Lib/multiprocessing/connection.py b/Lib/multiprocessing/connection.py --- a/Lib/multiprocessing/connection.py +++ b/Lib/multiprocessing/connection.py @@ -27,7 +27,7 @@ try: import _winapi from _winapi import WAIT_OBJECT_0, WAIT_TIMEOUT, INFINITE -except ImportError: +except ModuleNotFoundError: if sys.platform == 'win32': raise _winapi = None diff --git a/Lib/multiprocessing/forking.py b/Lib/multiprocessing/forking.py --- a/Lib/multiprocessing/forking.py +++ b/Lib/multiprocessing/forking.py @@ -73,7 +73,7 @@ try: from functools import partial -except ImportError: +except ModuleNotFoundError: pass else: def _reduce_partial(p): diff --git a/Lib/nntplib.py b/Lib/nntplib.py --- a/Lib/nntplib.py +++ b/Lib/nntplib.py @@ -71,7 +71,7 @@ try: import ssl -except ImportError: +except ModuleNotFoundError: _have_ssl = False else: _have_ssl = True diff --git a/Lib/ntpath.py b/Lib/ntpath.py --- a/Lib/ntpath.py +++ b/Lib/ntpath.py @@ -566,7 +566,7 @@ try: from nt import _getfullpathname -except ImportError: # not running on Windows - mock up something sensible +except ModuleNotFoundError: # not running on Windows - mock up something sensible def abspath(path): """Return the absolute version of a path.""" if not isabs(path): @@ -659,6 +659,6 @@ # This is overkill on Windows - just pass the path to GetFileAttributes # and check the attribute from there. from nt import _isdir as isdir -except ImportError: +except ModuleNotFoundError: # Use genericpath.isdir as imported above. pass diff --git a/Lib/operator.py b/Lib/operator.py --- a/Lib/operator.py +++ b/Lib/operator.py @@ -360,7 +360,7 @@ try: from _operator import * -except ImportError: +except ModuleNotFoundError: pass else: from _operator import __doc__ diff --git a/Lib/optparse.py b/Lib/optparse.py --- a/Lib/optparse.py +++ b/Lib/optparse.py @@ -87,7 +87,7 @@ try: from gettext import gettext, ngettext -except ImportError: +except ModuleNotFoundError: def gettext(message): return message diff --git a/Lib/os.py b/Lib/os.py --- a/Lib/os.py +++ b/Lib/os.py @@ -52,13 +52,13 @@ try: from posix import _exit __all__.append('_exit') - except ImportError: + except ModuleNotFoundError: pass import posixpath as path try: from posix import _have_functions - except ImportError: + except ModuleNotFoundError: pass elif 'nt' in _names: @@ -68,7 +68,7 @@ try: from nt import _exit __all__.append('_exit') - except ImportError: + except ModuleNotFoundError: pass import ntpath as path @@ -78,7 +78,7 @@ try: from nt import _have_functions - except ImportError: + except ModuleNotFoundError: pass elif 'ce' in _names: @@ -88,7 +88,7 @@ try: from ce import _exit __all__.append('_exit') - except ImportError: + except ModuleNotFoundError: pass # We can use the standard Windows path. import ntpath as path @@ -99,11 +99,11 @@ try: from ce import _have_functions - except ImportError: + except ModuleNotFoundError: pass else: - raise ImportError('no os specific module found') + raise ModuleNotFoundError('no os specific module found') sys.modules['os.path'] = path from os.path import (curdir, pardir, sep, pathsep, defpath, extsep, altsep, diff --git a/Lib/pdb.py b/Lib/pdb.py --- a/Lib/pdb.py +++ b/Lib/pdb.py @@ -158,7 +158,7 @@ import readline # remove some common file name delimiters readline.set_completer_delims(' \t\n`@#$%^&*()=+[{]}\\|;:\'",<>?') - except ImportError: + except ModuleNotFoundError: pass self.allow_kbdint = False self.nosigint = nosigint diff --git a/Lib/pickle.py b/Lib/pickle.py --- a/Lib/pickle.py +++ b/Lib/pickle.py @@ -90,7 +90,7 @@ # Jython has PyStringMap; it's a dict subclass with string keys try: from org.python.core import PyStringMap -except ImportError: +except ModuleNotFoundError: PyStringMap = None # Pickle opcodes. See pickletools.py for extensive docs. The listing @@ -1296,7 +1296,7 @@ # Use the faster _pickle if possible try: from _pickle import * -except ImportError: +except ModuleNotFoundError: Pickler, Unpickler = _Pickler, _Unpickler # Doctest diff --git a/Lib/platform.py b/Lib/platform.py --- a/Lib/platform.py +++ b/Lib/platform.py @@ -460,7 +460,7 @@ try: # Use win32api if available from win32api import RegQueryValueEx - except ImportError: + except ModuleNotFoundError: # On Python 2.0 and later, emulate using winreg import winreg RegQueryValueEx = winreg.QueryValueEx @@ -503,7 +503,7 @@ RegCloseKey, GetVersionEx from win32con import HKEY_LOCAL_MACHINE, VER_PLATFORM_WIN32_NT, \ VER_PLATFORM_WIN32_WINDOWS, VER_NT_WORKSTATION - except ImportError: + except ModuleNotFoundError: # Emulate the win32api module using Python APIs try: sys.getwindowsversion @@ -661,7 +661,7 @@ # Check whether the version info module is available try: import _gestalt - except ImportError: + except ModuleNotFoundError: return None # Get the infos sysv, sysa = _mac_ver_lookup(('sysv','sysa')) @@ -697,7 +697,7 @@ try: import plistlib - except ImportError: + except ModuleNotFoundError: return None pl = plistlib.readPlist(fn) @@ -762,7 +762,7 @@ # Import the needed APIs try: import java.lang - except ImportError: + except ModuleNotFoundError: return release,vendor,vminfo,osinfo vendor = _java_getprop('java.vendor', vendor) @@ -874,7 +874,7 @@ """ try: import socket - except ImportError: + except ModuleNotFoundError: # No sockets... return default try: @@ -1138,7 +1138,7 @@ # Get processor information try: import vms_lib - except ImportError: + except ModuleNotFoundError: pass else: csid, cpu_number = vms_lib.getsyi('SYI$_CPU',0) diff --git a/Lib/poplib.py b/Lib/poplib.py --- a/Lib/poplib.py +++ b/Lib/poplib.py @@ -20,7 +20,7 @@ try: import ssl HAVE_SSL = True -except ImportError: +except ModuleNotFoundError: HAVE_SSL = False __all__ = ["POP3","error_proto"] diff --git a/Lib/pstats.py b/Lib/pstats.py --- a/Lib/pstats.py +++ b/Lib/pstats.py @@ -528,7 +528,7 @@ import cmd try: import readline - except ImportError: + except ModuleNotFoundError: pass class ProfileBrowser(cmd.Cmd): diff --git a/Lib/pty.py b/Lib/pty.py --- a/Lib/pty.py +++ b/Lib/pty.py @@ -67,7 +67,7 @@ result = os.open(tty_name, os.O_RDWR) try: from fcntl import ioctl, I_PUSH - except ImportError: + except ModuleNotFoundError: return result try: ioctl(result, I_PUSH, "ptem") diff --git a/Lib/pydoc.py b/Lib/pydoc.py --- a/Lib/pydoc.py +++ b/Lib/pydoc.py @@ -1893,7 +1893,7 @@ def showtopic(self, topic, more_xrefs=''): try: import pydoc_data.topics - except ImportError: + except ModuleNotFoundError: self.output.write(''' Sorry, topic and keyword documentation is not available because the module "pydoc_data.topics" could not be found. @@ -1933,7 +1933,7 @@ """ try: import pydoc_data.topics - except ImportError: + except ModuleNotFoundError: return(''' Sorry, topic and keyword documentation is not available because the module "pydoc_data.topics" could not be found. diff --git a/Lib/queue.py b/Lib/queue.py --- a/Lib/queue.py +++ b/Lib/queue.py @@ -2,13 +2,13 @@ try: import threading -except ImportError: +except ModuleNotFoundError: import dummy_threading as threading from collections import deque from heapq import heappush, heappop try: from time import monotonic as time -except ImportError: +except ModuleNotFoundError: from time import time __all__ = ['Empty', 'Full', 'Queue', 'PriorityQueue', 'LifoQueue'] diff --git a/Lib/quopri.py b/Lib/quopri.py --- a/Lib/quopri.py +++ b/Lib/quopri.py @@ -13,7 +13,7 @@ try: from binascii import a2b_qp, b2a_qp -except ImportError: +except ModuleNotFoundError: a2b_qp = None b2a_qp = None diff --git a/Lib/reprlib.py b/Lib/reprlib.py --- a/Lib/reprlib.py +++ b/Lib/reprlib.py @@ -6,7 +6,7 @@ from itertools import islice try: from _thread import get_ident -except ImportError: +except ModuleNotFoundError: from _dummy_thread import get_ident def recursive_repr(fillvalue='...'): diff --git a/Lib/rlcompleter.py b/Lib/rlcompleter.py --- a/Lib/rlcompleter.py +++ b/Lib/rlcompleter.py @@ -154,7 +154,7 @@ try: import readline -except ImportError: +except ModuleNotFoundError: pass else: readline.set_completer(Completer().complete) diff --git a/Lib/sched.py b/Lib/sched.py --- a/Lib/sched.py +++ b/Lib/sched.py @@ -33,11 +33,11 @@ from collections import namedtuple try: import threading -except ImportError: +except ModuleNotFoundError: import dummy_threading as threading try: from time import monotonic as _time -except ImportError: +except ModuleNotFoundError: from time import time as _time __all__ = ["scheduler"] diff --git a/Lib/shutil.py b/Lib/shutil.py --- a/Lib/shutil.py +++ b/Lib/shutil.py @@ -17,17 +17,17 @@ import bz2 del bz2 _BZ2_SUPPORTED = True -except ImportError: +except ModuleNotFoundError: _BZ2_SUPPORTED = False try: from pwd import getpwnam -except ImportError: +except ModuleNotFoundError: getpwnam = None try: from grp import getgrnam -except ImportError: +except ModuleNotFoundError: getgrnam = None __all__ = ["copyfileobj", "copyfile", "copymode", "copystat", "copy", "copy2", @@ -668,7 +668,7 @@ # command. try: import zipfile - except ImportError: + except ModuleNotFoundError: zipfile = None if zipfile is None: @@ -858,7 +858,7 @@ """ try: import zipfile - except ImportError: + except ModuleNotFoundError: raise ReadError('zlib not supported, cannot unpack this archive.') if not zipfile.is_zipfile(filename): diff --git a/Lib/site.py b/Lib/site.py --- a/Lib/site.py +++ b/Lib/site.py @@ -469,7 +469,7 @@ try: import readline import rlcompleter - except ImportError: + except ModuleNotFoundError: return # Reading the initialization (config) file may not be enough to set a @@ -570,7 +570,7 @@ """Run custom site specific code, if available.""" try: import sitecustomize - except ImportError: + except ModuleNotFoundError: pass except Exception as err: if os.environ.get("PYTHONVERBOSE"): @@ -586,7 +586,7 @@ """Run custom user specific code, if available.""" try: import usercustomize - except ImportError: + except ModuleNotFoundError: pass except Exception as err: if os.environ.get("PYTHONVERBOSE"): diff --git a/Lib/smtpd.py b/Lib/smtpd.py --- a/Lib/smtpd.py +++ b/Lib/smtpd.py @@ -846,7 +846,7 @@ if options.setuid: try: import pwd - except ImportError: + except ModuleNotFoundError: print('Cannot import module "pwd"; try running with -n option.', file=sys.stderr) sys.exit(1) nobody = pwd.getpwnam('nobody')[2] diff --git a/Lib/smtplib.py b/Lib/smtplib.py --- a/Lib/smtplib.py +++ b/Lib/smtplib.py @@ -171,7 +171,7 @@ try: import ssl -except ImportError: +except ModuleNotFoundError: _have_ssl = False else: _have_ssl = True diff --git a/Lib/socket.py b/Lib/socket.py --- a/Lib/socket.py +++ b/Lib/socket.py @@ -51,7 +51,7 @@ try: import errno -except ImportError: +except ModuleNotFoundError: errno = None EBADF = getattr(errno, 'EBADF', 9) EAGAIN = getattr(errno, 'EAGAIN', 11) diff --git a/Lib/socketserver.py b/Lib/socketserver.py --- a/Lib/socketserver.py +++ b/Lib/socketserver.py @@ -136,7 +136,7 @@ import errno try: import threading -except ImportError: +except ModuleNotFoundError: import dummy_threading as threading __all__ = ["TCPServer","UDPServer","ForkingUDPServer","ForkingTCPServer", diff --git a/Lib/sqlite3/test/dbapi.py b/Lib/sqlite3/test/dbapi.py --- a/Lib/sqlite3/test/dbapi.py +++ b/Lib/sqlite3/test/dbapi.py @@ -25,7 +25,7 @@ import sqlite3 as sqlite try: import threading -except ImportError: +except ModuleNotFoundError: threading = None from test.support import TESTFN, unlink diff --git a/Lib/sqlite3/test/types.py b/Lib/sqlite3/test/types.py --- a/Lib/sqlite3/test/types.py +++ b/Lib/sqlite3/test/types.py @@ -26,7 +26,7 @@ import sqlite3 as sqlite try: import zlib -except ImportError: +except ModuleNotFoundError: zlib = None diff --git a/Lib/sre_compile.py b/Lib/sre_compile.py --- a/Lib/sre_compile.py +++ b/Lib/sre_compile.py @@ -295,7 +295,7 @@ def _optimize_unicode(charset, fixup): try: import array - except ImportError: + except ModuleNotFoundError: return charset charmap = [0]*65536 negate = 0 diff --git a/Lib/ssl.py b/Lib/ssl.py --- a/Lib/ssl.py +++ b/Lib/ssl.py @@ -127,14 +127,14 @@ try: from _ssl import PROTOCOL_SSLv2 _SSLv2_IF_EXISTS = PROTOCOL_SSLv2 -except ImportError: +except ModuleNotFoundError: _SSLv2_IF_EXISTS = None else: _PROTOCOL_NAMES[PROTOCOL_SSLv2] = "SSLv2" try: from _ssl import PROTOCOL_TLSv1_1, PROTOCOL_TLSv1_2 -except ImportError: +except ModuleNotFoundError: pass else: _PROTOCOL_NAMES[PROTOCOL_TLSv1_1] = "TLSv1.1" diff --git a/Lib/subprocess.py b/Lib/subprocess.py --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -353,7 +353,7 @@ import errno try: from time import monotonic as _time -except ImportError: +except ModuleNotFoundError: from time import time as _time # Exception classes used by this module. diff --git a/Lib/tarfile.py b/Lib/tarfile.py --- a/Lib/tarfile.py +++ b/Lib/tarfile.py @@ -50,7 +50,7 @@ try: import grp, pwd -except ImportError: +except ModuleNotFoundError: grp = pwd = None # os.symlink on Windows prior to 6.0 raises NotImplementedError @@ -381,7 +381,7 @@ if comptype == "gz": try: import zlib - except ImportError: + except ModuleNotFoundError: raise CompressionError("zlib module is not available") self.zlib = zlib self.crc = zlib.crc32(b"") @@ -394,7 +394,7 @@ elif comptype == "bz2": try: import bz2 - except ImportError: + except ModuleNotFoundError: raise CompressionError("bz2 module is not available") if mode == "r": self.dbuf = b"" @@ -406,7 +406,7 @@ elif comptype == "xz": try: import lzma - except ImportError: + except ModuleNotFoundError: raise CompressionError("lzma module is not available") if mode == "r": self.dbuf = b"" @@ -1654,7 +1654,7 @@ try: import bz2 - except ImportError: + except ModuleNotFoundError: raise CompressionError("bz2 module is not available") fileobj = bz2.BZ2File(fileobj or name, mode, @@ -1678,7 +1678,7 @@ try: import lzma - except ImportError: + except ModuleNotFoundError: raise CompressionError("lzma module is not available") fileobj = lzma.LZMAFile(fileobj or name, mode, preset=preset) diff --git a/Lib/tempfile.py b/Lib/tempfile.py --- a/Lib/tempfile.py +++ b/Lib/tempfile.py @@ -36,7 +36,7 @@ try: import fcntl as _fcntl -except ImportError: +except ModuleNotFoundError: def _set_cloexec(fd): pass else: @@ -53,7 +53,7 @@ try: import _thread -except ImportError: +except ModuleNotFoundError: import _dummy_thread as _thread _allocate_lock = _thread.allocate_lock diff --git a/Lib/threading.py b/Lib/threading.py --- a/Lib/threading.py +++ b/Lib/threading.py @@ -6,14 +6,14 @@ from time import sleep as _sleep try: from time import monotonic as _time -except ImportError: +except ModuleNotFoundError: from time import time as _time from traceback import format_exc as _format_exc from _weakrefset import WeakSet from itertools import islice as _islice try: from _collections import deque as _deque -except ImportError: +except ModuleNotFoundError: from collections import deque as _deque # Note regarding PEP 8 compliant names @@ -922,7 +922,7 @@ try: from _thread import _local as local -except ImportError: +except ModuleNotFoundError: from _threading_local import local diff --git a/Lib/trace.py b/Lib/trace.py --- a/Lib/trace.py +++ b/Lib/trace.py @@ -61,12 +61,12 @@ from warnings import warn as _warn try: from time import monotonic as _time -except ImportError: +except ModuleNotFoundError: from time import time as _time try: import threading -except ImportError: +except ModuleNotFoundError: _settrace = sys.settrace def _unsettrace(): diff --git a/Lib/urllib/request.py b/Lib/urllib/request.py --- a/Lib/urllib/request.py +++ b/Lib/urllib/request.py @@ -110,7 +110,7 @@ # check for SSL try: import ssl -except ImportError: +except ModuleNotFoundError: _have_ssl = False else: _have_ssl = True @@ -2512,7 +2512,7 @@ proxies = {} try: import winreg - except ImportError: + except ModuleNotFoundError: # Std module, so should be around - but you never know! return proxies try: @@ -2560,7 +2560,7 @@ def proxy_bypass_registry(host): try: import winreg - except ImportError: + except ModuleNotFoundError: # Std modules, so should be around - but you never know! return 0 try: diff --git a/Lib/venv/__init__.py b/Lib/venv/__init__.py --- a/Lib/venv/__init__.py +++ b/Lib/venv/__init__.py @@ -35,7 +35,7 @@ import sysconfig try: import threading -except ImportError: +except ModuleNotFoundError: threading = None logger = logging.getLogger(__name__) diff --git a/Lib/warnings.py b/Lib/warnings.py --- a/Lib/warnings.py +++ b/Lib/warnings.py @@ -144,8 +144,8 @@ module = category[:i] klass = category[i+1:] try: - m = __import__(module, None, None, [klass]) - except ImportError: + m = __import__(module, fromlist[klass]) + except ModuleNotFoundError: raise _OptionError("invalid module name: %r" % (module,)) try: cat = getattr(m, klass) @@ -362,7 +362,7 @@ defaultaction = _defaultaction onceregistry = _onceregistry _warnings_defaults = True -except ImportError: +except ModuleNotFoundError: filters = [] defaultaction = "default" onceregistry = {} 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 @@ -1439,13 +1439,13 @@ def __init__(self, html=0, target=None, encoding=None): try: from xml.parsers import expat - except ImportError: + except ModuleNotFoundError: try: import pyexpat as expat - except ImportError: - raise ImportError( - "No module named expat; use SimpleXMLTreeBuilder instead" - ) + except ModuleNotFoundError: + raise ModuleNotFoundError( + "No module named expat; use SimpleXMLTreeBuilder instead", + name='expat') parser = expat.ParserCreate(encoding, "}") if target is None: target = TreeBuilder() diff --git a/Lib/xml/sax/expatreader.py b/Lib/xml/sax/expatreader.py --- a/Lib/xml/sax/expatreader.py +++ b/Lib/xml/sax/expatreader.py @@ -20,7 +20,7 @@ try: from xml.parsers import expat -except ImportError: +except ModuleNotFoundError: raise SAXReaderNotAvailable("expat not supported", None) else: if not hasattr(expat, "ParserCreate"): @@ -30,18 +30,7 @@ AttributesImpl = xmlreader.AttributesImpl AttributesNSImpl = xmlreader.AttributesNSImpl -# If we're using a sufficiently recent version of Python, we can use -# weak references to avoid cycles between the parser and content -# handler, otherwise we'll just have to pretend. -try: - import _weakref -except ImportError: - def _mkproxy(o): - return o -else: - import weakref - _mkproxy = weakref.proxy - del weakref, _weakref +import weakref # --- ExpatLocator @@ -52,7 +41,7 @@ a circular reference between the parser and the content handler. """ def __init__(self, parser): - self._ref = _mkproxy(parser) + self._ref = weakref.proxy(parser) def getColumnNumber(self): parser = self._ref diff --git a/Lib/xmlrpc/client.py b/Lib/xmlrpc/client.py --- a/Lib/xmlrpc/client.py +++ b/Lib/xmlrpc/client.py @@ -139,7 +139,7 @@ from io import BytesIO try: import gzip -except ImportError: +except ModuleNotFoundError: gzip = None #python can be built without zlib/gzip support # -------------------------------------------------------------------- -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 14 03:15:28 2013 From: python-checkins at python.org (richard.jones) Date: Fri, 14 Jun 2013 03:15:28 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?peps=3A_clarify_some_wording_based_on?= =?utf-8?q?_feedback_from_Marcus_Smith=3B_note_resolution_of?= Message-ID: <3bWkQS54yFzP9C@mail.python.org> http://hg.python.org/peps/rev/85b25c4fb26d changeset: 4923:85b25c4fb26d user: Richard Jones date: Fri Jun 14 11:15:22 2013 +1000 summary: clarify some wording based on feedback from Marcus Smith; note resolution of Fedora issue files: pep-0439.txt | 47 ++++++++++++++++++++++----------------- 1 files changed, 26 insertions(+), 21 deletions(-) diff --git a/pep-0439.txt b/pep-0439.txt --- a/pep-0439.txt +++ b/pep-0439.txt @@ -63,14 +63,15 @@ ----------------- The Python installation includes an executable called "pip3" (see PEP 394 for -naming rationale etc.) that attempts to import pip machinery. If it can -then the pip command proceeds as normal. If it cannot it will bootstrap pip by -downloading the pip implementation wheel file. Once installed, the pip command -proceeds as normal. +naming rationale etc.) that attempts to import pip machinery. If it can then +the pip command proceeds as normal. If it cannot it will bootstrap pip by +downloading the pip implementation wheel file. Once installed, the pip +command proceeds as normal. Once the bootstrap process is complete the "pip3" +command is no longer the bootstrap but rather the full pip command. -A boostrap is used in the place of a the full pip code so that we -don't have to bundle pip and also the install tool is upgradeable -outside of the regular Python upgrade timeframe and processes. +A boostrap is used in the place of a the full pip code so that we don't have +to bundle pip and also pip is upgradeable outside of the regular Python +upgrade timeframe and processes. To avoid issues with sudo we will have the bootstrap default to installing the pip implementation to the per-user site-packages @@ -114,13 +115,12 @@ it) and display further instructions for downloading and installing the file manually. -Manual installation of the pip implementation will be supported -through the manual download of the wheel file and "pip3 install -". - -This installation will not perform standard pip installation steps of -saving the file to a cache directory or updating any local database of -installed files. +Some users may have no Internet access suitable for fetching the pip +implementation file. Manual installation of the pip implementation will be +supported through the manual download of the wheel file and "pip3 install +". This installation - since it uses only the bootstrap +code - will not perform standard pip installation steps of saving the file to +a cache directory or updating any local database of installed files. The download of the pip implementation install file should be performed securely. The transport from pypi.python.org will be done over HTTPS but the CA @@ -195,16 +195,16 @@ Risks ===== -The Fedora variant of Linux has had a separate program called "pip" (a -Perl package installer) available for install for some time. The -current Python "pip" program is installed as "pip-python". It is -hoped that the Fedora community will resolve this issue by renaming -the Perl installer. - The key that is used to sign the pip implementation download might be compromised and this PEP currently proposes no mechanism for key revocation. +There is a Perl package installer also named "pip". It is quite rare and not +commonly used. The Fedora variant of Linux has historically named Python's +"pip" as "python-pip" and Perl's "pip" as "perl-pip". This policy has been +altered[3] so that future Fedora installations will use the name "pip" for +Python's "pip". Existing installations will still have the old name for the +Python "pip", though the potential for confusion is now much reduced. References @@ -216,6 +216,9 @@ .. [2] pip issue tracking work needed for this PEP https://github.com/pypa/pip/issues/863 +.. [3] Fedora's python-pip package does not provide /usr/bin/pip + https://bugzilla.redhat.com/show_bug.cgi?id=958377 + Acknowledgments =============== @@ -223,7 +226,9 @@ Nick Coghlan for his thoughts on the proposal and dealing with the Red Hat issue. -Jannis Leidel and Carl Meyer for their thoughts. +Jannis Leidel and Carl Meyer for their thoughts. Marcus Smith for feedback. + +Marcela Ma?l??ov? for resolving the Fedora issue. Copyright -- Repository URL: http://hg.python.org/peps From python-checkins at python.org Fri Jun 14 03:18:51 2013 From: python-checkins at python.org (brett.cannon) Date: Fri, 14 Jun 2013 03:18:51 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Move_test=5Fpep352_over_to?= =?utf-8?q?_unittest=2Emain=28=29?= Message-ID: <3bWkVM0wM9zP5m@mail.python.org> http://hg.python.org/cpython/rev/af27c661d4fb changeset: 84115:af27c661d4fb user: Brett Cannon date: Thu Jun 13 21:18:43 2013 -0400 summary: Move test_pep352 over to unittest.main() files: Lib/test/test_pep352.py | 4 +--- 1 files changed, 1 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_pep352.py b/Lib/test/test_pep352.py --- a/Lib/test/test_pep352.py +++ b/Lib/test/test_pep352.py @@ -180,8 +180,6 @@ # Catching a string is bad. self.catch_fails("spam") -def test_main(): - run_unittest(ExceptionClassTests, UsageTests) if __name__ == '__main__': - test_main() + unittest.main() -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 14 03:36:26 2013 From: python-checkins at python.org (richard.jones) Date: Fri, 14 Jun 2013 03:36:26 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?peps=3A_slight_clarification?= Message-ID: <3bWktf50KRzMfc@mail.python.org> http://hg.python.org/peps/rev/0a051a14592b changeset: 4924:0a051a14592b user: Richard Jones date: Fri Jun 14 11:36:16 2013 +1000 summary: slight clarification files: pep-0439.txt | 7 ++++--- 1 files changed, 4 insertions(+), 3 deletions(-) diff --git a/pep-0439.txt b/pep-0439.txt --- a/pep-0439.txt +++ b/pep-0439.txt @@ -202,9 +202,10 @@ There is a Perl package installer also named "pip". It is quite rare and not commonly used. The Fedora variant of Linux has historically named Python's "pip" as "python-pip" and Perl's "pip" as "perl-pip". This policy has been -altered[3] so that future Fedora installations will use the name "pip" for -Python's "pip". Existing installations will still have the old name for the -Python "pip", though the potential for confusion is now much reduced. +altered[3] so that future and upgraded Fedora installations will use the name +"pip" for Python's "pip". Existing (non-upgraded) installations will still +have the old name for the Python "pip", though the potential for confusion is +now much reduced. References -- Repository URL: http://hg.python.org/peps From solipsis at pitrou.net Fri Jun 14 05:49:05 2013 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Fri, 14 Jun 2013 05:49:05 +0200 Subject: [Python-checkins] Daily reference leaks (af27c661d4fb): sum=0 Message-ID: results for af27c661d4fb on branch "default" -------------------------------------------- Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogMnOdYG', '-x'] From python-checkins at python.org Fri Jun 14 07:06:49 2013 From: python-checkins at python.org (raymond.hettinger) Date: Fri, 14 Jun 2013 07:06:49 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_Fix_comment_bl?= =?utf-8?q?ocks=2E__Adjust_blocksize_to_a_power-of-two_for_better_divmod?= Message-ID: <3bWqYP3sqgzQdN@mail.python.org> http://hg.python.org/cpython/rev/5accb0ac8bfb changeset: 84116:5accb0ac8bfb branch: 2.7 parent: 84095:ca8e86711403 user: Raymond Hettinger date: Fri Jun 14 01:06:33 2013 -0400 summary: Fix comment blocks. Adjust blocksize to a power-of-two for better divmod computations. files: Lib/test/test_deque.py | 2 +- Modules/_collectionsmodule.c | 18 +++++++----------- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/Lib/test/test_deque.py b/Lib/test/test_deque.py --- a/Lib/test/test_deque.py +++ b/Lib/test/test_deque.py @@ -522,7 +522,7 @@ @test_support.cpython_only def test_sizeof(self): - BLOCKLEN = 62 + BLOCKLEN = 64 basesize = test_support.calcobjsize('2P4PlP') blocksize = struct.calcsize('2P%dP' % BLOCKLEN) self.assertEqual(object.__sizeof__(deque()), basesize) diff --git a/Modules/_collectionsmodule.c b/Modules/_collectionsmodule.c --- a/Modules/_collectionsmodule.c +++ b/Modules/_collectionsmodule.c @@ -8,12 +8,13 @@ */ /* The block length may be set to any number over 1. Larger numbers - * reduce the number of calls to the memory allocator but take more - * memory. Ideally, BLOCKLEN should be set with an eye to the - * length of a cache line. + * reduce the number of calls to the memory allocator, give faster + * indexing and rotation, and reduce the link::data overhead ratio. + * Ideally, the block length should be a power-of-two for faster + * division/modulo computations during indexing. */ -#define BLOCKLEN 62 +#define BLOCKLEN 64 #define CENTER ((BLOCKLEN - 1) / 2) /* A `dequeobject` is composed of a doubly-linked list of `block` nodes. @@ -58,13 +59,8 @@ static block * newblock(block *leftlink, block *rightlink, Py_ssize_t len) { block *b; - /* To prevent len from overflowing PY_SSIZE_T_MAX on 64-bit machines, we - * refuse to allocate new blocks if the current len is dangerously - * close. There is some extra margin to prevent spurious arithmetic - * overflows at various places. The following check ensures that - * the blocks allocated to the deque, in the worst case, can only - * have PY_SSIZE_T_MAX-2 entries in total. - */ + /* To prevent len from overflowing PY_SSIZE_T_MAX on 32-bit machines, we + * refuse to allocate new blocks if the current len is nearing overflow. */ if (len >= PY_SSIZE_T_MAX - 2*BLOCKLEN) { PyErr_SetString(PyExc_OverflowError, "cannot add more blocks to the deque"); -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 14 09:31:14 2013 From: python-checkins at python.org (ethan.furman) Date: Fri, 14 Jun 2013 09:31:14 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Closes_issue_17947=2E__Add?= =?utf-8?q?s_PEP-0435_=28Enum=2C_IntEnum=29_to_the_stdlib=2E?= Message-ID: <3bWtm22z9dz7Ljj@mail.python.org> http://hg.python.org/cpython/rev/fae92309c3be changeset: 84117:fae92309c3be parent: 84115:af27c661d4fb user: Ethan Furman date: Fri Jun 14 00:30:27 2013 -0700 summary: Closes issue 17947. Adds PEP-0435 (Enum, IntEnum) to the stdlib. files: Doc/library/datatypes.rst | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/Doc/library/datatypes.rst b/Doc/library/datatypes.rst --- a/Doc/library/datatypes.rst +++ b/Doc/library/datatypes.rst @@ -30,3 +30,4 @@ copy.rst pprint.rst reprlib.rst + enum.rst -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 14 15:01:33 2013 From: python-checkins at python.org (christian.heimes) Date: Fri, 14 Jun 2013 15:01:33 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_comparing_with_http=3A//hg?= =?utf-8?q?=2Epython=2Eorg/cpython/?= Message-ID: <3bX2590lktz7LkH@mail.python.org> http://hg.python.org/cpython/rev/4751f1b54540 changeset: 84118:4751f1b54540 user: Christian Heimes date: Fri Jun 14 15:01:03 2013 +0200 summary: comparing with http://hg.python.org/cpython/ searching for changes changeset: 84118:98343392fd81 tag: tip user: Christian Heimes date: Fri Jun 14 14:54:48 2013 +0200 files: PC/VS9.0/_socket.vcproj PC/VS9.0/_ssl.vcproj description: Fix compilation of Python with VS 2008 Contrary to VS 2010 the compiler doesn't like semicolon seperated dependency names files: PC/VS9.0/_socket.vcproj | 16 ++++++++-------- PC/VS9.0/_ssl.vcproj | 18 +++++++++--------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/PC/VS9.0/_socket.vcproj b/PC/VS9.0/_socket.vcproj --- a/PC/VS9.0/_socket.vcproj +++ b/PC/VS9.0/_socket.vcproj @@ -54,7 +54,7 @@ /> @@ -423,7 +423,7 @@ /> diff --git a/PC/VS9.0/_ssl.vcproj b/PC/VS9.0/_ssl.vcproj --- a/PC/VS9.0/_ssl.vcproj +++ b/PC/VS9.0/_ssl.vcproj @@ -1,7 +1,7 @@ http://hg.python.org/cpython/rev/5f7e12eb65b8 changeset: 84119:5f7e12eb65b8 user: Christian Heimes date: Fri Jun 14 15:14:29 2013 +0200 summary: Simplify return value of ssl.get_default_verify_paths prefix function with PySSL_, too. Other module level functions have a prefix, too. files: Modules/_ssl.c | 14 +++----------- 1 files changed, 3 insertions(+), 11 deletions(-) diff --git a/Modules/_ssl.c b/Modules/_ssl.c --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -2769,13 +2769,12 @@ 'cert_file_env', 'cert_file', 'cert_dir_env', 'cert_dir'."); static PyObject * -get_default_verify_paths(PyObject *self) +PySSL_get_default_verify_paths(PyObject *self) { PyObject *ofile_env = NULL; PyObject *ofile = NULL; PyObject *odir_env = NULL; PyObject *odir = NULL; - PyObject *tup = NULL; #define convert(info, target) { \ const char *tmp = (info); \ @@ -2792,14 +2791,7 @@ convert(X509_get_default_cert_dir(), odir); #undef convert - if ((tup = PyTuple_New(4)) == NULL) { - goto error; - } - PyTuple_SET_ITEM(tup, 0, ofile_env); - PyTuple_SET_ITEM(tup, 1, ofile); - PyTuple_SET_ITEM(tup, 2, odir_env); - PyTuple_SET_ITEM(tup, 3, odir); - return tup; + return Py_BuildValue("NNNN", ofile_env, ofile, odir_env, odir); error: Py_XDECREF(ofile_env); @@ -2950,7 +2942,7 @@ {"RAND_status", (PyCFunction)PySSL_RAND_status, METH_NOARGS, PySSL_RAND_status_doc}, #endif - {"get_default_verify_paths", (PyCFunction)get_default_verify_paths, + {"get_default_verify_paths", (PyCFunction)PySSL_get_default_verify_paths, METH_NOARGS, PySSL_get_default_verify_paths_doc}, #ifdef _MSC_VER {"enum_cert_store", (PyCFunction)PySSL_enum_cert_store, -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 14 15:48:33 2013 From: python-checkins at python.org (christian.heimes) Date: Fri, 14 Jun 2013 15:48:33 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogSXNzdWUgIzE1MTcy?= =?utf-8?q?=3A_Document_NASM_2=2E10+_as_requirement_for_building_OpenSSL_1?= =?utf-8?q?=2E0=2E1_on?= Message-ID: <3bX37P5S0dz7Lk9@mail.python.org> http://hg.python.org/cpython/rev/ee3952965934 changeset: 84120:ee3952965934 branch: 3.3 parent: 84100:b11507395ce4 user: Christian Heimes date: Fri Jun 14 15:40:28 2013 +0200 summary: Issue #15172: Document NASM 2.10+ as requirement for building OpenSSL 1.0.1 on Windows files: Misc/NEWS | 5 +++++ PCbuild/readme.txt | 7 +++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -126,6 +126,11 @@ - Issue #15239: Make mkstringprep.py work again on Python 3. +Build +----- + +- Issue #15172: Document NASM 2.10+ as requirement for building OpenSSL 1.0.1 + on Windows. What's New in Python 3.3.2? =========================== diff --git a/PCbuild/readme.txt b/PCbuild/readme.txt --- a/PCbuild/readme.txt +++ b/PCbuild/readme.txt @@ -152,9 +152,12 @@ You can (theoretically) use any version of OpenSSL you like - the build process will automatically select the latest version. - You must install the NASM assembler from + You must install the NASM assembler 2.10 or newer from http://nasm.sf.net - for x86 builds. Put nasmw.exe anywhere in your PATH. + for x86 builds. Put nasmw.exe anywhere in your PATH. More recent + versions of OpenSSL may need a later version of NASM. If OpenSSL's self + tests don't pass, you should first try to update NASM and do a full + rebuild of OpenSSL. Note: recent releases of nasm only have nasm.exe. Just rename it to nasmw.exe. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 14 15:48:35 2013 From: python-checkins at python.org (christian.heimes) Date: Fri, 14 Jun 2013 15:48:35 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2315172=3A_Document_NASM_2=2E10+_as_requirement_f?= =?utf-8?q?or_building_OpenSSL_1=2E0=2E1_on?= Message-ID: <3bX37R0gTQz7LkK@mail.python.org> http://hg.python.org/cpython/rev/1b2cbdc9c1d4 changeset: 84121:1b2cbdc9c1d4 parent: 84119:5f7e12eb65b8 parent: 84120:ee3952965934 user: Christian Heimes date: Fri Jun 14 15:48:16 2013 +0200 summary: Issue #15172: Document NASM 2.10+ as requirement for building OpenSSL 1.0.1 on Windows files: Misc/NEWS | 2 ++ PCbuild/readme.txt | 7 +++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -493,6 +493,8 @@ - Issue #17547: In configure, explicitly pass -Wformat for the benefit for GCC 4.8. +- Issue #15172: Document NASM 2.10+ as requirement for building OpenSSL 1.0.1 + on Windows. What's New in Python 3.3.1 release candidate 1? =============================================== diff --git a/PCbuild/readme.txt b/PCbuild/readme.txt --- a/PCbuild/readme.txt +++ b/PCbuild/readme.txt @@ -152,9 +152,12 @@ You can (theoretically) use any version of OpenSSL you like - the build process will automatically select the latest version. - You must install the NASM assembler from + You must install the NASM assembler 2.10 or newer from http://nasm.sf.net - for x86 builds. Put nasmw.exe anywhere in your PATH. + for x86 builds. Put nasmw.exe anywhere in your PATH. More recent + versions of OpenSSL may need a later version of NASM. If OpenSSL's self + tests don't pass, you should first try to update NASM and do a full + rebuild of OpenSSL. Note: recent releases of nasm only have nasm.exe. Just rename it to nasmw.exe. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 14 16:42:55 2013 From: python-checkins at python.org (brett.cannon) Date: Fri, 14 Jun 2013 16:42:55 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Remove_a_dead_import_line?= =?utf-8?q?=2E?= Message-ID: <3bX4L75Lz0z7Ljj@mail.python.org> http://hg.python.org/cpython/rev/e61d7da84680 changeset: 84122:e61d7da84680 user: Brett Cannon date: Fri Jun 14 10:42:48 2013 -0400 summary: Remove a dead import line. Noticed by Serhly Storchaka. files: Lib/test/test_pep352.py | 1 - 1 files changed, 0 insertions(+), 1 deletions(-) diff --git a/Lib/test/test_pep352.py b/Lib/test/test_pep352.py --- a/Lib/test/test_pep352.py +++ b/Lib/test/test_pep352.py @@ -1,7 +1,6 @@ import unittest import builtins import warnings -from test.support import run_unittest import os from platform import system as platform_system -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 14 21:04:37 2013 From: python-checkins at python.org (brett.cannon) Date: Fri, 14 Jun 2013 21:04:37 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2318193=3A_Add_impo?= =?utf-8?q?rtlib=2Ereload=28=29=2C_documenting_=28but_not?= Message-ID: <3bXB8500BTz7Lkr@mail.python.org> http://hg.python.org/cpython/rev/01da7bf11ca1 changeset: 84123:01da7bf11ca1 user: Brett Cannon date: Fri Jun 14 15:04:26 2013 -0400 summary: Issue #18193: Add importlib.reload(), documenting (but not implementing in code) the deprecation of imp.reload(). Thanks to Berker Peksag for the patch. files: Doc/library/imp.rst | 3 + Doc/library/importlib.rst | 67 +++++++++++++++++ Lib/imp.py | 28 +----- Lib/importlib/__init__.py | 34 ++++++++- Lib/test/test_importlib/test_api.py | 12 +++ Misc/NEWS | 2 + 6 files changed, 122 insertions(+), 24 deletions(-) diff --git a/Doc/library/imp.rst b/Doc/library/imp.rst --- a/Doc/library/imp.rst +++ b/Doc/library/imp.rst @@ -171,6 +171,9 @@ the class does not affect the method definitions of the instances --- they continue to use the old class definition. The same is true for derived classes. + .. deprecated:: 3.4 + Use :func:`importlib.reload` instead. + The following functions are conveniences for handling :pep:`3147` byte-compiled file paths. diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst --- a/Doc/library/importlib.rst +++ b/Doc/library/importlib.rst @@ -115,6 +115,73 @@ .. versionadded:: 3.3 +.. function:: reload(module) + + Reload a previously imported *module*. The argument must be a module object, + so it must have been successfully imported before. This is useful if you + have edited the module source file using an external editor and want to try + out the new version without leaving the Python interpreter. The return value + is the module object (the same as the *module* argument). + + When :func:`.reload` is executed: + + * Python modules' code is recompiled and the module-level code re-executed, + defining a new set of objects which are bound to names in the module's + dictionary by reusing the :term:`loader` which originally loaded the + module. The ``init`` function of extension modules is not called a second + time. + + * As with all other objects in Python the old objects are only reclaimed + after their reference counts drop to zero. + + * The names in the module namespace are updated to point to any new or + changed objects. + + * Other references to the old objects (such as names external to the module) are + not rebound to refer to the new objects and must be updated in each namespace + where they occur if that is desired. + + There are a number of other caveats: + + If a module is syntactically correct but its initialization fails, the first + :keyword:`import` statement for it does not bind its name locally, but does + store a (partially initialized) module object in ``sys.modules``. To reload + the module you must first :keyword:`import` it again (this will bind the name + to the partially initialized module object) before you can :func:`reload` it. + + When a module is reloaded, its dictionary (containing the module's global + variables) is retained. Redefinitions of names will override the old + definitions, so this is generally not a problem. If the new version of a + module does not define a name that was defined by the old version, the old + definition remains. This feature can be used to the module's advantage if it + maintains a global table or cache of objects --- with a :keyword:`try` + statement it can test for the table's presence and skip its initialization if + desired:: + + try: + cache + except NameError: + cache = {} + + It is legal though generally not very useful to reload built-in or + dynamically loaded modules (this is not true for e.g. :mod:`sys`, + :mod:`__main__`, :mod:`__builtin__` and other key modules where reloading is + frowned upon). In many cases, however, extension modules are not designed to + be initialized more than once, and may fail in arbitrary ways when reloaded. + + If a module imports objects from another module using :keyword:`from` ... + :keyword:`import` ..., calling :func:`reload` for the other module does not + redefine the objects imported from it --- one way around this is to + re-execute the :keyword:`from` statement, another is to use :keyword:`import` + and qualified names (*module.name*) instead. + + If a module instantiates instances of a class, reloading the module that + defines the class does not affect the method definitions of the instances --- + they continue to use the old class definition. The same is true for derived + classes. + + .. versionadded:: 3.4 + :mod:`importlib.abc` -- Abstract base classes related to import --------------------------------------------------------------- diff --git a/Lib/imp.py b/Lib/imp.py --- a/Lib/imp.py +++ b/Lib/imp.py @@ -23,6 +23,7 @@ from importlib import _bootstrap from importlib import machinery +import importlib import os import sys import tokenize @@ -246,31 +247,12 @@ return file, file_path, (suffix, mode, type_) -_RELOADING = {} +def reload(module): + """**DEPRECATED** -def reload(module): - """Reload the module and return it. + Reload the module and return it. The module must have been successfully imported before. """ - if not module or type(module) != type(sys): - raise TypeError("reload() argument must be module") - name = module.__name__ - if name not in sys.modules: - msg = "module {} not in sys.modules" - raise ImportError(msg.format(name), name=name) - if name in _RELOADING: - return _RELOADING[name] - _RELOADING[name] = module - try: - parent_name = name.rpartition('.')[0] - if parent_name and parent_name not in sys.modules: - msg = "parent {!r} not in sys.modules" - raise ImportError(msg.format(parentname), name=parent_name) - return module.__loader__.load_module(name) - finally: - try: - del _RELOADING[name] - except KeyError: - pass + return importlib.reload(module) diff --git a/Lib/importlib/__init__.py b/Lib/importlib/__init__.py --- a/Lib/importlib/__init__.py +++ b/Lib/importlib/__init__.py @@ -1,5 +1,5 @@ """A pure Python implementation of import.""" -__all__ = ['__import__', 'import_module', 'invalidate_caches'] +__all__ = ['__import__', 'import_module', 'invalidate_caches', 'reload'] # Bootstrap help ##################################################### @@ -11,6 +11,7 @@ # initialised below if the frozen one is not available). import _imp # Just the builtin component, NOT the full Python module import sys +import types try: import _frozen_importlib as _bootstrap @@ -90,3 +91,34 @@ break level += 1 return _bootstrap._gcd_import(name[level:], package, level) + + +_RELOADING = {} + + +def reload(module): + """Reload the module and return it. + + The module must have been successfully imported before. + + """ + if not module or not isinstance(module, types.ModuleType): + raise TypeError("reload() argument must be module") + name = module.__name__ + if name not in sys.modules: + msg = "module {} not in sys.modules" + raise ImportError(msg.format(name), name=name) + if name in _RELOADING: + return _RELOADING[name] + _RELOADING[name] = module + try: + parent_name = name.rpartition('.')[0] + if parent_name and parent_name not in sys.modules: + msg = "parent {!r} not in sys.modules" + raise ImportError(msg.format(parentname), name=parent_name) + return module.__loader__.load_module(name) + finally: + try: + del _RELOADING[name] + except KeyError: + pass diff --git a/Lib/test/test_importlib/test_api.py b/Lib/test/test_importlib/test_api.py --- a/Lib/test/test_importlib/test_api.py +++ b/Lib/test/test_importlib/test_api.py @@ -151,6 +151,18 @@ self.assertIsNone(importlib.find_loader('nevergoingtofindthismodule')) +class ReloadTests(unittest.TestCase): + + """Test module reloading for builtin and extension modules.""" + + def test_reload_modules(self): + for mod in ('tokenize', 'time', 'marshal'): + with self.subTest(module=mod): + with support.CleanImport(mod): + module = importlib.import_module(mod) + importlib.reload(module) + + class InvalidateCacheTests(unittest.TestCase): def test_method_called(self): diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -123,6 +123,8 @@ Library ------- +- Issue #18193: Add importlib.reload(). + - Issue #18157: Stop using imp.load_module() in pydoc. - Issue #16102: Make uuid._netbios_getnode() work again on Python 3. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 15 00:20:24 2013 From: python-checkins at python.org (ned.deily) Date: Sat, 15 Jun 2013 00:20:24 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2318149=3A_Add_file?= =?utf-8?q?cmp=2Eclear=5Fcache=28=29_to_manually_clear_the_filecmp_cache?= =?utf-8?q?=2E?= Message-ID: <3bXGV01trMz7Ljp@mail.python.org> http://hg.python.org/cpython/rev/bfd53dcb02ff changeset: 84124:bfd53dcb02ff user: Ned Deily date: Fri Jun 14 15:19:11 2013 -0700 summary: Issue #18149: Add filecmp.clear_cache() to manually clear the filecmp cache. Patch by Mark Levitt files: Doc/library/filecmp.rst | 13 +++++++++++++ Lib/filecmp.py | 11 ++++++++--- Lib/test/test_filecmp.py | 7 +++++++ Misc/ACKS | 1 + Misc/NEWS | 3 +++ 5 files changed, 32 insertions(+), 3 deletions(-) diff --git a/Doc/library/filecmp.rst b/Doc/library/filecmp.rst --- a/Doc/library/filecmp.rst +++ b/Doc/library/filecmp.rst @@ -27,6 +27,10 @@ Note that no external programs are called from this function, giving it portability and efficiency. + This function uses a cache for past comparisons and the results, + with a cache invalidation mechanism relying on stale signatures + or by explicitly calling :func:`clear_cache`. + .. function:: cmpfiles(dir1, dir2, common, shallow=True) @@ -48,6 +52,15 @@ one of the three returned lists. +.. function:: clear_cache() + + .. versionadded:: 3.4 + + Clear the filecmp cache. This may be useful if a file is compared so quickly + after it is modified that it is within the mtime resolution of + the underlying filesystem. + + .. _dircmp-objects: The :class:`dircmp` class diff --git a/Lib/filecmp.py b/Lib/filecmp.py --- a/Lib/filecmp.py +++ b/Lib/filecmp.py @@ -6,6 +6,7 @@ Functions: cmp(f1, f2, shallow=True) -> int cmpfiles(a, b, common) -> ([], [], []) + clear_cache() """ @@ -13,7 +14,7 @@ import stat from itertools import filterfalse -__all__ = ['cmp', 'dircmp', 'cmpfiles', 'DEFAULT_IGNORES'] +__all__ = ['clear_cache', 'cmp', 'dircmp', 'cmpfiles', 'DEFAULT_IGNORES'] _cache = {} BUFSIZE = 8*1024 @@ -21,6 +22,9 @@ DEFAULT_IGNORES = [ 'RCS', 'CVS', 'tags', '.git', '.hg', '.bzr', '_darcs', '__pycache__'] +def clear_cache(): + """Clear the filecmp cache.""" + _cache.clear() def cmp(f1, f2, shallow=True): """Compare two files. @@ -39,7 +43,8 @@ True if the files are the same, False otherwise. This function uses a cache for past comparisons and the results, - with a cache invalidation mechanism relying on stale signatures. + with a cache invalidation mechanism relying on stale signatures + or by explicitly calling clear_cache(). """ @@ -56,7 +61,7 @@ if outcome is None: outcome = _do_cmp(f1, f2) if len(_cache) > 100: # limit the maximum size of the cache - _cache.clear() + clear_cache() _cache[f1, f2, s1, s2] = outcome return outcome diff --git a/Lib/test/test_filecmp.py b/Lib/test/test_filecmp.py --- a/Lib/test/test_filecmp.py +++ b/Lib/test/test_filecmp.py @@ -39,6 +39,13 @@ self.assertFalse(filecmp.cmp(self.name, self.dir), "File and directory compare as equal") + def test_cache_clear(self): + first_compare = filecmp.cmp(self.name, self.name_same, shallow=False) + second_compare = filecmp.cmp(self.name, self.name_diff, shallow=False) + filecmp.clear_cache() + self.assertTrue(len(filecmp._cache) == 0, + "Cache not cleared after calling clear_cache") + class DirCompareTestCase(unittest.TestCase): def setUp(self): tmpdir = tempfile.gettempdir() diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -738,6 +738,7 @@ Christopher Tur Lesniewski-Laas Alain Leufroy Mark Levinson +Mark Levitt William Lewis Akira Li Xuanji Li diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -123,6 +123,9 @@ Library ------- +- Issue #18149: Add filecmp.clear_cache() to manually clear the filecmp cache. + Patch by Mark Levitt + - Issue #18193: Add importlib.reload(). - Issue #18157: Stop using imp.load_module() in pydoc. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 15 00:33:30 2013 From: python-checkins at python.org (brett.cannon) Date: Sat, 15 Jun 2013 00:33:30 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2317222=3A_Raise_Fi?= =?utf-8?q?leExistsError_when_py=5Fcompile=2Ecompile_would?= Message-ID: <3bXGn61cGRz7LkN@mail.python.org> http://hg.python.org/cpython/rev/46ef1d2af352 changeset: 84125:46ef1d2af352 parent: 84123:01da7bf11ca1 user: Brett Cannon date: Fri Jun 14 18:33:00 2013 -0400 summary: Issue #17222: Raise FileExistsError when py_compile.compile would overwrite a symlink or non-regular file with a regular file. files: Doc/library/py_compile.rst | 11 ++++++++++- Doc/whatsnew/3.4.rst | 5 +++++ Lib/py_compile.py | 14 ++++++++++++++ Lib/test/test_py_compile.py | 20 ++++++++++++++++++++ Misc/NEWS | 3 +++ 5 files changed, 52 insertions(+), 1 deletions(-) diff --git a/Doc/library/py_compile.rst b/Doc/library/py_compile.rst --- a/Doc/library/py_compile.rst +++ b/Doc/library/py_compile.rst @@ -41,6 +41,13 @@ is raised. This function returns the path to byte-compiled file, i.e. whatever *cfile* value was used. + If the path that *cfile* becomes (either explicitly specified or computed) + is a symlink or non-regular file, :exc:`FileExistsError` will be raised. + This is to act as a warning that import will turn those paths into regular + files if it is allowed to write byte-compiled files to those paths. This is + a side-effect of import using file renaming to place the final byte-compiled + file into place to prevent concurrent file writing issues. + *optimize* controls the optimization level and is passed to the built-in :func:`compile` function. The default of ``-1`` selects the optimization level of the current interpreter. @@ -53,7 +60,9 @@ .. versionchanged:: 3.4 Changed code to use :mod:`importlib` for the byte-code cache file writing. This means file creation/writing semantics now match what :mod:`importlib` - does, e.g. permissions, write-and-move semantics, etc. + does, e.g. permissions, write-and-move semantics, etc. Also added the + caveat that :exc:`FileExistsError` is raised if *cfile* is a symlink or + non-regular file. .. function:: main(args=None) diff --git a/Doc/whatsnew/3.4.rst b/Doc/whatsnew/3.4.rst --- a/Doc/whatsnew/3.4.rst +++ b/Doc/whatsnew/3.4.rst @@ -272,3 +272,8 @@ * :c:func:`PyErr_SetImportError` now sets :exc:`TypeError` when its **msg** argument is not set. Previously only ``NULL`` was returned. + +* :func:`py_compile.compile` now raises :exc:`FileExistsError` if the file path + it would write to is a symlink or a non-regular file. This is to act as a + warning that import will overwrite those files with a regular file regardless + of what type of file path they were originally. diff --git a/Lib/py_compile.py b/Lib/py_compile.py --- a/Lib/py_compile.py +++ b/Lib/py_compile.py @@ -7,6 +7,7 @@ import importlib._bootstrap import importlib.machinery import os +import os.path import sys import traceback @@ -96,12 +97,25 @@ See compileall.py for a script/module that uses this module to byte-compile all installed files (or all files in selected directories). + + Do note that FileExistsError is raised if cfile ends up pointing at a + non-regular file or symlink. Because the compilation uses a file renaming, + the resulting file would be regular and thus not the same type of file as + it was previously. """ if cfile is None: if optimize >= 0: cfile = imp.cache_from_source(file, debug_override=not optimize) else: cfile = imp.cache_from_source(file) + if os.path.islink(cfile): + msg = ('{} is a symlink and will be changed into a regular file if ' + 'import writes a byte-compiled file to it') + raise FileExistsError(msg.format(file, cfile)) + elif os.path.exists(cfile) and not os.path.isfile(cfile): + msg = ('{} is a non-regular file and will be changed into a regular ' + 'one if import writes a byte-compiled file to it') + raise FileExistsError(msg.format(file, cfile)) loader = importlib.machinery.SourceFileLoader('', file) source_bytes = loader.get_data(file) try: diff --git a/Lib/test/test_py_compile.py b/Lib/test/test_py_compile.py --- a/Lib/test/test_py_compile.py +++ b/Lib/test/test_py_compile.py @@ -36,6 +36,26 @@ self.assertTrue(os.path.exists(self.pyc_path)) self.assertFalse(os.path.exists(self.cache_path)) + def test_do_not_overwrite_symlinks(self): + # In the face of a cfile argument being a symlink, bail out. + # Issue #17222 + try: + os.symlink(self.pyc_path + '.actual', self.pyc_path) + except OSError: + self.skipTest('need to be able to create a symlink for a file') + else: + assert os.path.islink(self.pyc_path) + with self.assertRaises(FileExistsError): + py_compile.compile(self.source_path, self.pyc_path) + + @unittest.skipIf(not os.path.exists(os.devnull) or os.path.isfile(os.devnull), + 'requires os.devnull and for it to be a non-regular file') + def test_do_not_overwrite_nonregular_files(self): + # In the face of a cfile argument being a non-regular file, bail out. + # Issue #17222 + with self.assertRaises(FileExistsError): + py_compile.compile(self.source_path, os.devnull) + def test_cache_path(self): py_compile.compile(self.source_path) self.assertTrue(os.path.exists(self.cache_path)) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -1005,6 +1005,9 @@ Python file. Patch by Ben Morgan. - Have py_compile use importlib as much as possible to avoid code duplication. + Code now raises FileExistsError if the file path to be used for the + byte-compiled file is a symlink or non-regular file as a warning that import + will not keep the file path type if it writes to that path. - Issue #180022: Have site.addpackage() consider already known paths even when none are explicitly passed in. Bug report and fix by Kirill. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 15 00:33:31 2013 From: python-checkins at python.org (brett.cannon) Date: Sat, 15 Jun 2013 00:33:31 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_default_-=3E_default?= =?utf-8?q?=29=3A_merge?= Message-ID: <3bXGn74Vnzz7Lk4@mail.python.org> http://hg.python.org/cpython/rev/8ee9f50b0538 changeset: 84126:8ee9f50b0538 parent: 84125:46ef1d2af352 parent: 84124:bfd53dcb02ff user: Brett Cannon date: Fri Jun 14 18:33:21 2013 -0400 summary: merge files: Doc/library/filecmp.rst | 13 +++++++++++++ Lib/filecmp.py | 11 ++++++++--- Lib/test/test_filecmp.py | 7 +++++++ Misc/ACKS | 1 + Misc/NEWS | 3 +++ 5 files changed, 32 insertions(+), 3 deletions(-) diff --git a/Doc/library/filecmp.rst b/Doc/library/filecmp.rst --- a/Doc/library/filecmp.rst +++ b/Doc/library/filecmp.rst @@ -27,6 +27,10 @@ Note that no external programs are called from this function, giving it portability and efficiency. + This function uses a cache for past comparisons and the results, + with a cache invalidation mechanism relying on stale signatures + or by explicitly calling :func:`clear_cache`. + .. function:: cmpfiles(dir1, dir2, common, shallow=True) @@ -48,6 +52,15 @@ one of the three returned lists. +.. function:: clear_cache() + + .. versionadded:: 3.4 + + Clear the filecmp cache. This may be useful if a file is compared so quickly + after it is modified that it is within the mtime resolution of + the underlying filesystem. + + .. _dircmp-objects: The :class:`dircmp` class diff --git a/Lib/filecmp.py b/Lib/filecmp.py --- a/Lib/filecmp.py +++ b/Lib/filecmp.py @@ -6,6 +6,7 @@ Functions: cmp(f1, f2, shallow=True) -> int cmpfiles(a, b, common) -> ([], [], []) + clear_cache() """ @@ -13,7 +14,7 @@ import stat from itertools import filterfalse -__all__ = ['cmp', 'dircmp', 'cmpfiles', 'DEFAULT_IGNORES'] +__all__ = ['clear_cache', 'cmp', 'dircmp', 'cmpfiles', 'DEFAULT_IGNORES'] _cache = {} BUFSIZE = 8*1024 @@ -21,6 +22,9 @@ DEFAULT_IGNORES = [ 'RCS', 'CVS', 'tags', '.git', '.hg', '.bzr', '_darcs', '__pycache__'] +def clear_cache(): + """Clear the filecmp cache.""" + _cache.clear() def cmp(f1, f2, shallow=True): """Compare two files. @@ -39,7 +43,8 @@ True if the files are the same, False otherwise. This function uses a cache for past comparisons and the results, - with a cache invalidation mechanism relying on stale signatures. + with a cache invalidation mechanism relying on stale signatures + or by explicitly calling clear_cache(). """ @@ -56,7 +61,7 @@ if outcome is None: outcome = _do_cmp(f1, f2) if len(_cache) > 100: # limit the maximum size of the cache - _cache.clear() + clear_cache() _cache[f1, f2, s1, s2] = outcome return outcome diff --git a/Lib/test/test_filecmp.py b/Lib/test/test_filecmp.py --- a/Lib/test/test_filecmp.py +++ b/Lib/test/test_filecmp.py @@ -39,6 +39,13 @@ self.assertFalse(filecmp.cmp(self.name, self.dir), "File and directory compare as equal") + def test_cache_clear(self): + first_compare = filecmp.cmp(self.name, self.name_same, shallow=False) + second_compare = filecmp.cmp(self.name, self.name_diff, shallow=False) + filecmp.clear_cache() + self.assertTrue(len(filecmp._cache) == 0, + "Cache not cleared after calling clear_cache") + class DirCompareTestCase(unittest.TestCase): def setUp(self): tmpdir = tempfile.gettempdir() diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -738,6 +738,7 @@ Christopher Tur Lesniewski-Laas Alain Leufroy Mark Levinson +Mark Levitt William Lewis Akira Li Xuanji Li diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -123,6 +123,9 @@ Library ------- +- Issue #18149: Add filecmp.clear_cache() to manually clear the filecmp cache. + Patch by Mark Levitt + - Issue #18193: Add importlib.reload(). - Issue #18157: Stop using imp.load_module() in pydoc. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 15 00:44:11 2013 From: python-checkins at python.org (victor.stinner) Date: Sat, 15 Jun 2013 00:44:11 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=233329=3A_Add_new_A?= =?utf-8?q?PIs_to_customize_memory_allocators?= Message-ID: <3bXH1R0dqGz7LkT@mail.python.org> http://hg.python.org/cpython/rev/6661a8154eb3 changeset: 84127:6661a8154eb3 user: Victor Stinner date: Sat Jun 15 00:37:46 2013 +0200 summary: Issue #3329: Add new APIs to customize memory allocators * Add a new PyMemAllocators structure * New functions: - PyMem_RawMalloc(), PyMem_RawRealloc(), PyMem_RawFree(): GIL-free memory allocator functions - PyMem_GetRawAllocators(), PyMem_SetRawAllocators() - PyMem_GetAllocators(), PyMem_SetAllocators() - PyMem_SetupDebugHooks() - _PyObject_GetArenaAllocators(), _PyObject_SetArenaAllocators() * Add unit test for PyMem_Malloc(0) and PyObject_Malloc(0) * Add unit test for new get/set allocators functions * PyObject_Malloc() now falls back on PyMem_Malloc() instead of malloc() if size is bigger than SMALL_REQUEST_THRESHOLD, and PyObject_Realloc() falls back on PyMem_Realloc() instead of realloc() * PyMem_Malloc() and PyMem_Realloc() now always call malloc() and realloc(), instead of calling PyObject_Malloc() and PyObject_Realloc() in debug mode files: Doc/c-api/memory.rst | 121 ++++++- Include/objimpl.h | 67 +- Include/pymem.h | 91 +++- Modules/_testcapimodule.c | 178 +++++++++ Objects/object.c | 20 - Objects/obmalloc.c | 501 ++++++++++++++++++------- 6 files changed, 769 insertions(+), 209 deletions(-) diff --git a/Doc/c-api/memory.rst b/Doc/c-api/memory.rst --- a/Doc/c-api/memory.rst +++ b/Doc/c-api/memory.rst @@ -84,6 +84,46 @@ for the I/O buffer escapes completely the Python memory manager. +Raw Memory Interface +==================== + +The following function are wrappers to system allocators: :c:func:`malloc`, +:c:func:`realloc`, :c:func:`free`. These functions are thread-safe, the +:term:`GIL ` does not need to be held to use these +functions. + +The behaviour of requesting zero bytes is not defined: return *NULL* or a +distinct non-*NULL* pointer depending on the platform. Use +:c:func:`PyMem_Malloc` and :c:func:`PyMem_Realloc` to have a well defined +behaviour. + +.. versionadded:: 3.4 + +.. c:function:: void* PyMem_RawMalloc(size_t n) + + Allocates *n* bytes and returns a pointer of type :c:type:`void\*` to the + allocated memory, or *NULL* if the request fails. The memory + will not have been initialized in any way. + + +.. c:function:: void* PyMem_RawRealloc(void *p, size_t n) + + Resizes the memory block pointed to by *p* to *n* bytes. The contents will + be unchanged to the minimum of the old and the new sizes. If *p* is *NULL*, + the call is equivalent to ``PyMem_RawMalloc(n)``. Unless *p* is *NULL*, it + must have been returned by a previous call to :c:func:`PyMem_RawMalloc` or + :c:func:`PyMem_RawRealloc`. If the request fails, :c:func:`PyMem_RawRealloc` + returns *NULL* and *p* remains a valid pointer to the previous memory area. + + +.. c:function:: void PyMem_RawFree(void *p) + + Frees the memory block pointed to by *p*, which must have been returned by a + previous call to :c:func:`PyMem_RawMalloc` or :c:func:`PyMem_RawRealloc`. + Otherwise, or if ``PyMem_Free(p)`` has been called before, undefined + behavior occurs. If *p* is *NULL*, no operation is performed. + + .. _memoryinterface: Memory Interface @@ -91,8 +131,12 @@ The following function sets, modeled after the ANSI C standard, but specifying behavior when requesting zero bytes, are available for allocating and releasing -memory from the Python heap: +memory from the Python heap. +.. warning:: + + The :term:`GIL ` must be held when using these + functions. .. c:function:: void* PyMem_Malloc(size_t n) @@ -155,6 +199,81 @@ :c:func:`PyMem_NEW`, :c:func:`PyMem_RESIZE`, :c:func:`PyMem_DEL`. +Customize Memory Allocators +=========================== + +.. versionadded:: 3.4 + +.. c:type:: PyMemAllocators + + Structure used to describe memory allocator. This structure has + four fields: + + +----------------------------------------------------------+-----------------+ + | Field | Meaning | + +==========================================================+=================+ + | ``void *ctx`` | user data | + +----------------------------------------------------------+-----------------+ + | ``void* malloc(void *ctx, size_t size)`` | allocate memory | + +----------------------------------------------------------+-----------------+ + | ``void* realloc(void *ctx, void *ptr, size_t new_size)`` | allocate memory | + | | or resize a | + | | memory block | + +----------------------------------------------------------+-----------------+ + | ``void free(void *ctx, void *ptr)`` | release memory | + +----------------------------------------------------------+-----------------+ + +.. c:function:: void PyMem_GetRawAllocators(PyMemAllocators *allocators) + + Get internal functions of :c:func:`PyMem_RawMalloc`, :c:func:`PyMem_RawRealloc` + and :c:func:`PyMem_RawFree`. + +.. c:function:: void PyMem_SetRawAllocators(PyMemAllocators *allocators) + + Set internal functions of :c:func:`PyMem_RawMalloc`, :c:func:`PyMem_RawRealloc` + and :c:func:`PyMem_RawFree`. + + :c:func:`PyMem_SetupDebugHooks` should be called to reinstall debug hooks if + new functions do no call original functions anymore. + +.. c:function:: void PyMem_GetAllocators(PyMemAllocators *allocators) + + Get internal functions of :c:func:`PyMem_Malloc`, :c:func:`PyMem_Realloc` + and :c:func:`PyMem_Free`. + +.. c:function:: void PyMem_SetAllocators(PyMemAllocators *allocators) + + Set internal functions of :c:func:`PyMem_Malloc`, :c:func:`PyMem_Realloc` + and :c:func:`PyMem_Free`. + + ``malloc(ctx, 0)`` and ``realloc(ctx, ptr, 0)`` must not return *NULL*: it + would be treated as an error. + + :c:func:`PyMem_SetupDebugHooks` should be called to reinstall debug hooks if + new functions do no call original functions anymore. + +.. c:function:: void PyMem_SetupDebugHooks(void) + + Setup hooks to detect bugs in the following Python memory allocator + functions: + + - :c:func:`PyMem_RawMalloc`, :c:func:`PyMem_RawRealloc`, + :c:func:`PyMem_RawFree` + - :c:func:`PyMem_Malloc`, :c:func:`PyMem_Realloc`, :c:func:`PyMem_Free` + - :c:func:`PyObject_Malloc`, :c:func:`PyObject_Realloc`, + :c:func:`PyObject_Free` + + Newly allocated memory is filled with the byte ``0xCB``, freed memory is + filled with the byte ``0xDB``. Additionnal checks: + + - detect API violations, ex: :c:func:`PyObject_Free` called on a buffer + allocated by :c:func:`PyMem_Malloc` + - detect write before the start of the buffer (buffer underflow) + - detect write after the end of the buffer (buffer overflow) + + The function does nothing if Python is not compiled is debug mode. + + .. _memoryexamples: Examples diff --git a/Include/objimpl.h b/Include/objimpl.h --- a/Include/objimpl.h +++ b/Include/objimpl.h @@ -94,9 +94,9 @@ the object gets initialized via PyObject_{Init, InitVar} after obtaining the raw memory. */ -PyAPI_FUNC(void *) PyObject_Malloc(size_t); -PyAPI_FUNC(void *) PyObject_Realloc(void *, size_t); -PyAPI_FUNC(void) PyObject_Free(void *); +PyAPI_FUNC(void *) PyObject_Malloc(size_t size); +PyAPI_FUNC(void *) PyObject_Realloc(void *ptr, size_t new_size); +PyAPI_FUNC(void) PyObject_Free(void *ptr); /* This function returns the number of allocated memory blocks, regardless of size */ PyAPI_FUNC(Py_ssize_t) _Py_GetAllocatedBlocks(void); @@ -106,41 +106,46 @@ #ifndef Py_LIMITED_API PyAPI_FUNC(void) _PyObject_DebugMallocStats(FILE *out); #endif /* #ifndef Py_LIMITED_API */ -#ifdef PYMALLOC_DEBUG /* WITH_PYMALLOC && PYMALLOC_DEBUG */ -PyAPI_FUNC(void *) _PyObject_DebugMalloc(size_t nbytes); -PyAPI_FUNC(void *) _PyObject_DebugRealloc(void *p, size_t nbytes); -PyAPI_FUNC(void) _PyObject_DebugFree(void *p); -PyAPI_FUNC(void) _PyObject_DebugDumpAddress(const void *p); -PyAPI_FUNC(void) _PyObject_DebugCheckAddress(const void *p); -PyAPI_FUNC(void *) _PyObject_DebugMallocApi(char api, size_t nbytes); -PyAPI_FUNC(void *) _PyObject_DebugReallocApi(char api, void *p, size_t nbytes); -PyAPI_FUNC(void) _PyObject_DebugFreeApi(char api, void *p); -PyAPI_FUNC(void) _PyObject_DebugCheckAddressApi(char api, const void *p); -PyAPI_FUNC(void *) _PyMem_DebugMalloc(size_t nbytes); -PyAPI_FUNC(void *) _PyMem_DebugRealloc(void *p, size_t nbytes); -PyAPI_FUNC(void) _PyMem_DebugFree(void *p); -#define PyObject_MALLOC _PyObject_DebugMalloc -#define PyObject_Malloc _PyObject_DebugMalloc -#define PyObject_REALLOC _PyObject_DebugRealloc -#define PyObject_Realloc _PyObject_DebugRealloc -#define PyObject_FREE _PyObject_DebugFree -#define PyObject_Free _PyObject_DebugFree +#endif -#else /* WITH_PYMALLOC && ! PYMALLOC_DEBUG */ +/* Macros */ #define PyObject_MALLOC PyObject_Malloc #define PyObject_REALLOC PyObject_Realloc #define PyObject_FREE PyObject_Free -#endif +#define PyObject_Del PyObject_Free +#define PyObject_DEL PyObject_Free -#else /* ! WITH_PYMALLOC */ -#define PyObject_MALLOC PyMem_MALLOC -#define PyObject_REALLOC PyMem_REALLOC -#define PyObject_FREE PyMem_FREE +/* Get internal functions of PyObject_Malloc(), PyObject_Realloc() and + PyObject_Free(). *ctx_p is an arbitrary user value. */ +PyAPI_FUNC(void) PyObject_GetAllocators(PyMemAllocators *allocators); -#endif /* WITH_PYMALLOC */ +/* Set internal functions of PyObject_Malloc(), PyObject_Realloc() and PyObject_Free(). + ctx is an arbitrary user value. -#define PyObject_Del PyObject_Free -#define PyObject_DEL PyObject_FREE + malloc(ctx, 0) and realloc(ctx, ptr, 0) must not return NULL: it would be + treated as an error. + + PyMem_SetupDebugHooks() should be called to reinstall debug hooks if new + functions do no call original functions anymore. */ +PyAPI_FUNC(void) PyObject_SetAllocators(PyMemAllocators *allocators); + +/* Get internal functions allocating and deallocating arenas for + PyObject_Malloc(), PyObject_Realloc() and PyObject_Free(). + *ctx_p is an arbitrary user value. */ +PyAPI_FUNC(void) _PyObject_GetArenaAllocators( + void **ctx_p, + void* (**malloc_p) (void *ctx, size_t size), + void (**free_p) (void *ctx, void *ptr, size_t size) + ); + +/* Get internal functions allocating and deallocating arenas for + PyObject_Malloc(), PyObject_Realloc() and PyObject_Free(). + ctx is an arbitrary user value. */ +PyAPI_FUNC(void) _PyObject_SetArenaAllocators( + void *ctx, + void* (*malloc) (void *ctx, size_t size), + void (*free) (void *ctx, void *ptr, size_t size) + ); /* * Generic object allocator interface diff --git a/Include/pymem.h b/Include/pymem.h --- a/Include/pymem.h +++ b/Include/pymem.h @@ -11,6 +11,40 @@ extern "C" { #endif +typedef struct { + /* user context passed as the first argument to the 3 functions */ + void *ctx; + + /* allocate memory */ + void* (*malloc) (void *ctx, size_t size); + + /* allocate memory or resize a memory buffer */ + void* (*realloc) (void *ctx, void *ptr, size_t new_size); + + /* release memory */ + void (*free) (void *ctx, void *ptr); +} PyMemAllocators; + +/* Raw memory allocators, system functions: malloc(), realloc(), free(). + + These functions are thread-safe, the GIL does not need to be held. */ + +/* Get internal functions of PyMem_RawMalloc(), PyMem_RawRealloc() and + PyMem_RawFree(). *ctx_p is an arbitrary user value. */ +PyAPI_FUNC(void) PyMem_GetRawAllocators(PyMemAllocators *allocators); + +/* Set internal functions of PyMem_RawMalloc(), PyMem_RawRealloc() and + PyMem_RawFree(). ctx is an arbitrary user value. + + PyMem_SetupDebugHooks() should be called to reinstall debug hooks if new + functions do no call original functions anymore. */ +PyAPI_FUNC(void) PyMem_SetRawAllocators(PyMemAllocators *allocators); + +PyAPI_FUNC(void *) PyMem_RawMalloc(size_t size); +PyAPI_FUNC(void *) PyMem_RawRealloc(void *ptr, size_t new_size); +PyAPI_FUNC(void) PyMem_RawFree(void *ptr); + + /* BEWARE: Each interface exports both functions and macros. Extension modules should @@ -49,21 +83,11 @@ performed on failure (no exception is set, no warning is printed, etc). */ -PyAPI_FUNC(void *) PyMem_Malloc(size_t); -PyAPI_FUNC(void *) PyMem_Realloc(void *, size_t); -PyAPI_FUNC(void) PyMem_Free(void *); - -/* Starting from Python 1.6, the wrappers Py_{Malloc,Realloc,Free} are - no longer supported. They used to call PyErr_NoMemory() on failure. */ +PyAPI_FUNC(void *) PyMem_Malloc(size_t size); +PyAPI_FUNC(void *) PyMem_Realloc(void *ptr, size_t new_size); +PyAPI_FUNC(void) PyMem_Free(void *ptr); /* Macros. */ -#ifdef PYMALLOC_DEBUG -/* Redirect all memory operations to Python's debugging allocator. */ -#define PyMem_MALLOC _PyMem_DebugMalloc -#define PyMem_REALLOC _PyMem_DebugRealloc -#define PyMem_FREE _PyMem_DebugFree - -#else /* ! PYMALLOC_DEBUG */ /* PyMem_MALLOC(0) means malloc(1). Some systems would return NULL for malloc(0), which would be treated as an error. Some platforms @@ -71,13 +95,9 @@ pymalloc. To solve these problems, allocate an extra byte. */ /* Returns NULL to indicate error if a negative size or size larger than Py_ssize_t can represent is supplied. Helps prevents security holes. */ -#define PyMem_MALLOC(n) ((size_t)(n) > (size_t)PY_SSIZE_T_MAX ? NULL \ - : malloc((n) ? (n) : 1)) -#define PyMem_REALLOC(p, n) ((size_t)(n) > (size_t)PY_SSIZE_T_MAX ? NULL \ - : realloc((p), (n) ? (n) : 1)) -#define PyMem_FREE free - -#endif /* PYMALLOC_DEBUG */ +#define PyMem_MALLOC(n) PyMem_Malloc(n) +#define PyMem_REALLOC(p, n) PyMem_Realloc(p, n) +#define PyMem_FREE(p) PyMem_Free(p) /* * Type-oriented memory interface @@ -115,6 +135,37 @@ #define PyMem_Del PyMem_Free #define PyMem_DEL PyMem_FREE +/* Get internal functions of PyMem_Malloc(), PyMem_Realloc() + and PyMem_Free() */ +PyAPI_FUNC(void) PyMem_GetAllocators(PyMemAllocators *allocators); + +/* Set internal functions of PyMem_Malloc(), PyMem_Realloc() and PyMem_Free(). + + malloc(ctx, 0) and realloc(ctx, ptr, 0) must not return NULL: it would be + treated as an error. + + PyMem_SetupDebugHooks() should be called to reinstall debug hooks if new + functions do no call original functions anymore. */ +PyAPI_FUNC(void) PyMem_SetAllocators(PyMemAllocators *allocators); + +/* Setup hooks to detect bugs in the following Python memory allocator + functions: + + - PyMem_RawMalloc(), PyMem_RawRealloc(), PyMem_RawFree() + - PyMem_Malloc(), PyMem_Realloc(), PyMem_Free() + - PyObject_Malloc(), PyObject_Realloc() and PyObject_Free() + + Newly allocated memory is filled with the byte 0xCB, freed memory is filled + with the byte 0xDB. Additionnal checks: + + - detect API violations, ex: PyObject_Free() called on a buffer allocated + by PyMem_Malloc() + - detect write before the start of the buffer (buffer underflow) + - detect write after the end of the buffer (buffer overflow) + + The function does nothing if Python is not compiled is debug mode. */ +PyAPI_FUNC(void) PyMem_SetupDebugHooks(void); + #ifdef __cplusplus } #endif diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -2511,6 +2511,176 @@ Py_RETURN_NONE; } +static PyObject * +test_pymem_alloc0(PyObject *self) +{ + void *ptr; + + ptr = PyMem_Malloc(0); + if (ptr == NULL) { + PyErr_SetString(PyExc_RuntimeError, "PyMem_Malloc(0) returns NULL"); + return NULL; + } + PyMem_Free(ptr); + + ptr = PyObject_Malloc(0); + if (ptr == NULL) { + PyErr_SetString(PyExc_RuntimeError, "PyObject_Malloc(0) returns NULL"); + return NULL; + } + PyObject_Free(ptr); + + Py_RETURN_NONE; +} + +typedef struct { + PyMemAllocators alloc; + + size_t malloc_size; + void *realloc_ptr; + size_t realloc_new_size; + void *free_ptr; +} alloc_hook_t; + +static void* hook_malloc (void* ctx, size_t size) +{ + alloc_hook_t *hook = (alloc_hook_t *)ctx; + hook->malloc_size = size; + return hook->alloc.malloc(hook->alloc.ctx, size); +} + +static void* hook_realloc (void* ctx, void* ptr, size_t new_size) +{ + alloc_hook_t *hook = (alloc_hook_t *)ctx; + hook->realloc_ptr = ptr; + hook->realloc_new_size = new_size; + return hook->alloc.realloc(hook->alloc.ctx, ptr, new_size); +} + +static void hook_free (void *ctx, void *ptr) +{ + alloc_hook_t *hook = (alloc_hook_t *)ctx; + printf("HOOK\n"); + hook->free_ptr = ptr; + hook->alloc.free(hook->alloc.ctx, ptr); +} + +static PyObject * +test_setallocators(char api) +{ + PyObject *res = NULL; + const char *error_msg; + alloc_hook_t hook; + PyMemAllocators alloc; + size_t size, size2; + void *ptr, *ptr2; + + hook.malloc_size = 0; + hook.realloc_ptr = NULL; + hook.realloc_new_size = 0; + hook.free_ptr = NULL; + + alloc.ctx = &hook; + alloc.malloc = &hook_malloc; + alloc.realloc = &hook_realloc; + alloc.free = &hook_free; + if (api == 'o') { + PyObject_GetAllocators(&hook.alloc); + PyObject_SetAllocators(&alloc); + } + else if (api == 'r') { + PyMem_GetRawAllocators(&hook.alloc); + PyMem_SetRawAllocators(&alloc); + } + else { + PyMem_GetAllocators(&hook.alloc); + PyMem_SetAllocators(&alloc); + } + + size = 42; + if (api == 'o') + ptr = PyObject_Malloc(size); + else if (api == 'r') + ptr = PyMem_RawMalloc(size); + else + ptr = PyMem_Malloc(size); + if (ptr == NULL) { + error_msg = "malloc failed"; + goto fail; + } + + if (hook.malloc_size != size) { + error_msg = "malloc invalid size"; + goto fail; + } + + size2 = 200; + if (api == 'o') + ptr2 = PyObject_Realloc(ptr, size2); + else if (api == 'r') + ptr2 = PyMem_RawRealloc(ptr, size2); + else + ptr2 = PyMem_Realloc(ptr, size2); + if (ptr2 == NULL) { + error_msg = "realloc failed"; + goto fail; + } + + if (hook.realloc_ptr != ptr + || hook.realloc_new_size != size2) { + error_msg = "realloc invalid parameters"; + goto fail; + } + + if (api == 'o') + PyObject_Free(ptr2); + else if (api == 'r') + PyMem_RawFree(ptr2); + else { + printf("PyMem_Free\n"); + PyMem_Free(ptr2); + } + + if (hook.free_ptr != ptr2) { + error_msg = "free invalid pointer"; + goto fail; + } + + Py_INCREF(Py_None); + res = Py_None; + goto finally; + +fail: + PyErr_SetString(PyExc_RuntimeError, error_msg); + +finally: + if (api == 'o') + PyObject_SetAllocators(&hook.alloc); + else if (api == 'r') + PyMem_SetRawAllocators(&hook.alloc); + else + PyMem_SetAllocators(&hook.alloc); + return res; +} + +static PyObject * +test_pymem_setrawallocators(PyObject *self) +{ + return test_setallocators('r'); +} + +static PyObject * +test_pymem_setallocators(PyObject *self) +{ + return test_setallocators('m'); +} + +static PyObject * +test_pyobject_setallocators(PyObject *self) +{ + return test_setallocators('o'); +} + static PyMethodDef TestMethods[] = { {"raise_exception", raise_exception, METH_VARARGS}, {"raise_memoryerror", (PyCFunction)raise_memoryerror, METH_NOARGS}, @@ -2611,6 +2781,14 @@ {"pytime_object_to_time_t", test_pytime_object_to_time_t, METH_VARARGS}, {"pytime_object_to_timeval", test_pytime_object_to_timeval, METH_VARARGS}, {"pytime_object_to_timespec", test_pytime_object_to_timespec, METH_VARARGS}, + {"test_pymem", + (PyCFunction)test_pymem_alloc0, METH_NOARGS}, + {"test_pymem_alloc0", + (PyCFunction)test_pymem_setrawallocators, METH_NOARGS}, + {"test_pymem_setallocators", + (PyCFunction)test_pymem_setallocators, METH_NOARGS}, + {"test_pyobject_setallocators", + (PyCFunction)test_pyobject_setallocators, METH_NOARGS}, {NULL, NULL} /* sentinel */ }; diff --git a/Objects/object.c b/Objects/object.c --- a/Objects/object.c +++ b/Objects/object.c @@ -1859,26 +1859,6 @@ Py_ssize_t (*_Py_abstract_hack)(PyObject *) = PyObject_Size; -/* Python's malloc wrappers (see pymem.h) */ - -void * -PyMem_Malloc(size_t nbytes) -{ - return PyMem_MALLOC(nbytes); -} - -void * -PyMem_Realloc(void *p, size_t nbytes) -{ - return PyMem_REALLOC(p, nbytes); -} - -void -PyMem_Free(void *p) -{ - PyMem_FREE(p); -} - void _PyObject_DebugTypeStats(FILE *out) { diff --git a/Objects/obmalloc.c b/Objects/obmalloc.c --- a/Objects/obmalloc.c +++ b/Objects/obmalloc.c @@ -1,5 +1,327 @@ #include "Python.h" +/* Python's malloc wrappers (see pymem.h) */ + +/* Forward declaration */ + +#ifdef PYMALLOC_DEBUG /* WITH_PYMALLOC && PYMALLOC_DEBUG */ +static void* _PyMem_DebugMalloc(void *ctx, size_t size); +static void _PyMem_DebugFree(void *ctx, void *p); +static void* _PyMem_DebugRealloc(void *ctx, void *ptr, size_t size); + +static void _PyObject_DebugDumpAddress(const void *p); +static void _PyMem_DebugCheckAddress(char api_id, const void *p); +#endif + +#ifdef WITH_PYMALLOC +static void* _PyObject_Malloc(void *ctx, size_t size); +static void _PyObject_Free(void *ctx, void *p); +static void* _PyObject_Realloc(void *ctx, void *ptr, size_t size); +#endif + + +static void * +_PyMem_RawMalloc(void *ctx, size_t size) +{ + return malloc(size); +} + +static void * +_PyMem_RawRealloc(void *ctx, void *ptr, size_t size) +{ + return realloc(ptr, size); +} + +static void +_PyMem_RawFree(void *ctx, void *ptr) +{ + return free(ptr); +} + +static void * +_PyMem_Malloc(void *ctx, size_t size) +{ + /* PyMem_Malloc(0) means malloc(1). Some systems would return NULL + for malloc(0), which would be treated as an error. Some platforms would + return a pointer with no memory behind it, which would break pymalloc. + To solve these problems, allocate an extra byte. */ + if (size == 0) + size = 1; + return malloc(size); +} + +static void * +_PyMem_Realloc(void *ctx, void *ptr, size_t size) +{ + if (size == 0) + size = 1; + return realloc(ptr, size); +} + +#ifdef ARENAS_USE_MMAP +static void * +_PyObject_ArenaMmap(void *ctx, size_t size) +{ + void *ptr; + ptr = mmap(NULL, size, PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + if (ptr == MAP_FAILED) + return NULL; + assert(ptr != NULL); + return ptr; +} + +static void +_PyObject_ArenaMunmap(void *ctx, void *ptr, size_t size) +{ + return munmap(ptr, size); +} +#else +static void * +_PyObject_ArenaMalloc(void *ctx, size_t size) +{ + return malloc(size); +} + +static void +_PyObject_ArenaFree(void *ctx, void *ptr, size_t size) +{ + free(ptr); +} +#endif + +#define PYRAW_FUNCS _PyMem_RawMalloc, _PyMem_RawRealloc, _PyMem_RawFree +#define PYMEM_FUNCS _PyMem_Malloc, _PyMem_Realloc, _PyMem_RawFree +#ifdef WITH_PYMALLOC +#define PYOBJECT_FUNCS _PyObject_Malloc, _PyObject_Realloc, _PyObject_Free +#else +#define PYOBJECT_FUNCS PYMEM_FUNCS +#endif + +#ifdef PYMALLOC_DEBUG +typedef struct { + /* We tag each block with an API ID in order to tag API violations */ + char api_id; + PyMemAllocators alloc; +} debug_alloc_api_t; +static struct { + debug_alloc_api_t raw; + debug_alloc_api_t mem; + debug_alloc_api_t obj; +} _PyMem_Debug = { + {'r', {NULL, PYRAW_FUNCS}}, + {'m', {NULL, PYMEM_FUNCS}}, + {'o', {NULL, PYOBJECT_FUNCS}} + }; + +#define PYDEBUG_FUNCS _PyMem_DebugMalloc, _PyMem_DebugRealloc, _PyMem_DebugFree +#endif + +static PyMemAllocators _PyMem_Raw = { +#ifdef PYMALLOC_DEBUG + &_PyMem_Debug.raw, PYDEBUG_FUNCS +#else + NULL, PYMEM_FUNCS +#endif + }; + +static PyMemAllocators _PyMem = { +#ifdef PYMALLOC_DEBUG + &_PyMem_Debug.mem, PYDEBUG_FUNCS +#else + NULL, PYMEM_FUNCS +#endif + }; + +static PyMemAllocators _PyObject = { +#ifdef PYMALLOC_DEBUG + &_PyMem_Debug.obj, PYDEBUG_FUNCS +#else + NULL, PYOBJECT_FUNCS +#endif + }; + +#undef PYRAW_FUNCS +#undef PYMEM_FUNCS +#undef PYOBJECT_FUNCS +#undef PYDEBUG_FUNCS + +static struct { + void *ctx; + void* (*malloc) (void*, size_t); + void (*free) (void*, void*, size_t); +} _PyObject_Arena = {NULL, +#ifdef ARENAS_USE_MMAP + _PyObject_ArenaMmap, _PyObject_ArenaMunmap +#else + _PyObject_ArenaMalloc, _PyObject_ArenaFree +#endif + }; + +void +PyMem_SetupDebugHooks(void) +{ +#ifdef PYMALLOC_DEBUG + PyMemAllocators alloc; + + alloc.malloc = _PyMem_DebugMalloc; + alloc.realloc = _PyMem_DebugRealloc; + alloc.free = _PyMem_DebugFree; + + if (_PyMem_Raw.malloc != _PyMem_DebugMalloc) { + alloc.ctx = &_PyMem_Debug.raw; + PyMem_GetAllocators(&_PyMem_Debug.raw.alloc); + PyMem_SetAllocators(&alloc); + } + + if (_PyMem.malloc != _PyMem_DebugMalloc) { + alloc.ctx = &_PyMem_Debug.mem; + PyMem_GetAllocators(&_PyMem_Debug.mem.alloc); + PyMem_SetAllocators(&alloc); + } + + if (_PyObject.malloc != _PyMem_DebugMalloc) { + alloc.ctx = &_PyMem_Debug.obj; + PyObject_GetAllocators(&_PyMem_Debug.obj.alloc); + PyObject_SetAllocators(&alloc); + } +#endif +} + +void +PyMem_GetRawAllocators(PyMemAllocators *allocators) +{ + *allocators = _PyMem_Raw; +} + +void +PyMem_SetRawAllocators(PyMemAllocators *allocators) +{ + _PyMem_Raw = *allocators; +} + +void +PyMem_GetAllocators(PyMemAllocators *allocators) +{ + *allocators = _PyMem; +} + +void +PyMem_SetAllocators(PyMemAllocators *allocators) +{ + _PyMem = *allocators; +} + +void +PyObject_GetAllocators(PyMemAllocators *allocators) +{ + *allocators = _PyObject; +} + +void +PyObject_SetAllocators(PyMemAllocators *allocators) +{ + _PyObject = *allocators; +} + +void +_PyObject_GetArenaAllocators(void **ctx_p, + void* (**malloc_p) (void *ctx, size_t size), + void (**free_p) (void *ctx, void *ptr, size_t size)) +{ + *malloc_p = _PyObject_Arena.malloc; + *free_p = _PyObject_Arena.free; + *ctx_p = _PyObject_Arena.ctx; +} + +void +_PyObject_SetArenaAllocators(void *ctx, + void* (*malloc) (void *ctx, size_t size), + void (*free) (void *ctx, void *ptr, size_t size)) +{ + _PyObject_Arena.malloc = malloc; + _PyObject_Arena.free = free; + _PyObject_Arena.ctx = ctx; +} + +void * +PyMem_RawMalloc(size_t size) +{ + return _PyMem_Raw.malloc(_PyMem_Raw.ctx, size); +} + +void* +PyMem_RawRealloc(void *ptr, size_t new_size) +{ + return _PyMem_Raw.realloc(_PyMem_Raw.ctx, ptr, new_size); +} + +void PyMem_RawFree(void *ptr) +{ + _PyMem_Raw.free(_PyMem_Raw.ctx, ptr); +} + +void * +PyMem_Malloc(size_t size) +{ + /* + * Limit ourselves to PY_SSIZE_T_MAX bytes to prevent security holes. + * Most python internals blindly use a signed Py_ssize_t to track + * things without checking for overflows or negatives. + * As size_t is unsigned, checking for size < 0 is not required. + */ + if (size > (size_t)PY_SSIZE_T_MAX) + return NULL; + + return _PyMem.malloc(_PyMem.ctx, size); +} + +void * +PyMem_Realloc(void *ptr, size_t new_size) +{ + if (new_size > (size_t)PY_SSIZE_T_MAX) + return NULL; + + return _PyMem.realloc(_PyMem.ctx, ptr, new_size); +} + +void +PyMem_Free(void *ptr) +{ + _PyMem.free(_PyMem.ctx, ptr); +} + +void * +PyObject_Malloc(size_t size) +{ + /* + * Limit ourselves to PY_SSIZE_T_MAX bytes to prevent security holes. + * Most python internals blindly use a signed Py_ssize_t to track + * things without checking for overflows or negatives. + * As size_t is unsigned, checking for size < 0 is not required. + */ + if (size > (size_t)PY_SSIZE_T_MAX) + return NULL; + + return _PyObject.malloc(_PyObject.ctx, size); +} + +void * +PyObject_Realloc(void *ptr, size_t new_size) +{ + if (new_size > (size_t)PY_SSIZE_T_MAX) + return NULL; + + return _PyObject.realloc(_PyObject.ctx, ptr, new_size); +} + +void +PyObject_Free(void *ptr) +{ + _PyObject.free(_PyObject.ctx, ptr); +} + + #ifdef WITH_PYMALLOC #ifdef HAVE_MMAP @@ -545,7 +867,6 @@ struct arena_object* arenaobj; uint excess; /* number of bytes above pool alignment */ void *address; - int err; #ifdef PYMALLOC_DEBUG if (Py_GETENV("PYTHONMALLOCSTATS")) @@ -567,11 +888,12 @@ return NULL; /* overflow */ #endif nbytes = numarenas * sizeof(*arenas); - arenaobj = (struct arena_object *)realloc(arenas, nbytes); + arenaobj = (struct arena_object *)PyMem_Realloc(arenas, nbytes); if (arenaobj == NULL) return NULL; arenas = arenaobj; + /* We might need to fix pointers that were copied. However, * new_arena only gets called when all the pages in the * previous arenas are full. Thus, there are *no* pointers @@ -598,15 +920,8 @@ arenaobj = unused_arena_objects; unused_arena_objects = arenaobj->nextarena; assert(arenaobj->address == 0); -#ifdef ARENAS_USE_MMAP - address = mmap(NULL, ARENA_SIZE, PROT_READ|PROT_WRITE, - MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); - err = (address == MAP_FAILED); -#else - address = malloc(ARENA_SIZE); - err = (address == 0); -#endif - if (err) { + address = _PyObject_Arena.malloc(_PyObject_Arena.ctx, ARENA_SIZE); + if (address == NULL) { /* The allocation failed: return NULL after putting the * arenaobj back. */ @@ -769,9 +1084,8 @@ * Unless the optimizer reorders everything, being too smart... */ -#undef PyObject_Malloc -void * -PyObject_Malloc(size_t nbytes) +static void * +_PyObject_Malloc(void *ctx, size_t nbytes) { block *bp; poolp pool; @@ -788,17 +1102,6 @@ #endif /* - * Limit ourselves to PY_SSIZE_T_MAX bytes to prevent security holes. - * Most python internals blindly use a signed Py_ssize_t to track - * things without checking for overflows or negatives. - * As size_t is unsigned, checking for nbytes < 0 is not required. - */ - if (nbytes > PY_SSIZE_T_MAX) { - _Py_AllocatedBlocks--; - return NULL; - } - - /* * This implicitly redirects malloc(0). */ if ((nbytes - 1) < SMALL_REQUEST_THRESHOLD) { @@ -970,10 +1273,8 @@ * last chance to serve the request) or when the max memory limit * has been reached. */ - if (nbytes == 0) - nbytes = 1; { - void *result = malloc(nbytes); + void *result = PyMem_Malloc(nbytes); if (!result) _Py_AllocatedBlocks--; return result; @@ -982,9 +1283,8 @@ /* free */ -#undef PyObject_Free -void -PyObject_Free(void *p) +static void +_PyObject_Free(void *ctx, void *p) { poolp pool; block *lastfree; @@ -1093,11 +1393,8 @@ unused_arena_objects = ao; /* Free the entire arena. */ -#ifdef ARENAS_USE_MMAP - munmap((void *)ao->address, ARENA_SIZE); -#else - free((void *)ao->address); -#endif + _PyObject_Arena.free(_PyObject_Arena.ctx, + (void *)ao->address, ARENA_SIZE); ao->address = 0; /* mark unassociated */ --narenas_currently_allocated; @@ -1206,7 +1503,7 @@ redirect: #endif /* We didn't allocate this address. */ - free(p); + PyMem_Free(p); } /* realloc. If p is NULL, this acts like malloc(nbytes). Else if nbytes==0, @@ -1214,9 +1511,8 @@ * return a non-NULL result. */ -#undef PyObject_Realloc -void * -PyObject_Realloc(void *p, size_t nbytes) +static void * +_PyObject_Realloc(void *ctx, void *p, size_t nbytes) { void *bp; poolp pool; @@ -1226,16 +1522,7 @@ #endif if (p == NULL) - return PyObject_Malloc(nbytes); - - /* - * Limit ourselves to PY_SSIZE_T_MAX bytes to prevent security holes. - * Most python internals blindly use a signed Py_ssize_t to track - * things without checking for overflows or negatives. - * As size_t is unsigned, checking for nbytes < 0 is not required. - */ - if (nbytes > PY_SSIZE_T_MAX) - return NULL; + return _PyObject_Malloc(ctx, nbytes); #ifdef WITH_VALGRIND /* Treat running_on_valgrind == -1 the same as 0 */ @@ -1263,10 +1550,10 @@ } size = nbytes; } - bp = PyObject_Malloc(nbytes); + bp = _PyObject_Malloc(ctx, nbytes); if (bp != NULL) { memcpy(bp, p, size); - PyObject_Free(p); + _PyObject_Free(ctx, p); } return bp; } @@ -1284,14 +1571,14 @@ * at p. Instead we punt: let C continue to manage this block. */ if (nbytes) - return realloc(p, nbytes); + return PyMem_Realloc(p, nbytes); /* C doesn't define the result of realloc(p, 0) (it may or may not * return NULL then), but Python's docs promise that nbytes==0 never * returns NULL. We don't pass 0 to realloc(), to avoid that endcase * to begin with. Even then, we can't be sure that realloc() won't * return NULL. */ - bp = realloc(p, 1); + bp = PyMem_Realloc(p, 1); return bp ? bp : p; } @@ -1301,24 +1588,6 @@ /* pymalloc not enabled: Redirect the entry points to malloc. These will * only be used by extensions that are compiled with pymalloc enabled. */ -void * -PyObject_Malloc(size_t n) -{ - return PyMem_MALLOC(n); -} - -void * -PyObject_Realloc(void *p, size_t n) -{ - return PyMem_REALLOC(p, n); -} - -void -PyObject_Free(void *p) -{ - PyMem_FREE(p); -} - Py_ssize_t _Py_GetAllocatedBlocks(void) { @@ -1344,10 +1613,6 @@ #define DEADBYTE 0xDB /* dead (newly freed) memory */ #define FORBIDDENBYTE 0xFB /* untouchable bytes at each end of a block */ -/* We tag each block with an API ID in order to tag API violations */ -#define _PYMALLOC_MEM_ID 'm' /* the PyMem_Malloc() API */ -#define _PYMALLOC_OBJ_ID 'o' /* The PyObject_Malloc() API */ - static size_t serialno = 0; /* incremented on each debug {m,re}alloc */ /* serialno is always incremented via calling this routine. The point is @@ -1430,58 +1695,18 @@ p[2*S+n: 2*S+n+S] Copies of FORBIDDENBYTE. Used to catch over- writes and reads. p[2*S+n+S: 2*S+n+2*S] - A serial number, incremented by 1 on each call to _PyObject_DebugMalloc - and _PyObject_DebugRealloc. + A serial number, incremented by 1 on each call to _PyMem_DebugMalloc + and _PyMem_DebugRealloc. This is a big-endian size_t. If "bad memory" is detected later, the serial number gives an excellent way to set a breakpoint on the next run, to capture the instant at which this block was passed out. */ -/* debug replacements for the PyMem_* memory API */ -void * -_PyMem_DebugMalloc(size_t nbytes) +static void * +_PyMem_DebugMalloc(void *ctx, size_t nbytes) { - return _PyObject_DebugMallocApi(_PYMALLOC_MEM_ID, nbytes); -} -void * -_PyMem_DebugRealloc(void *p, size_t nbytes) -{ - return _PyObject_DebugReallocApi(_PYMALLOC_MEM_ID, p, nbytes); -} -void -_PyMem_DebugFree(void *p) -{ - _PyObject_DebugFreeApi(_PYMALLOC_MEM_ID, p); -} - -/* debug replacements for the PyObject_* memory API */ -void * -_PyObject_DebugMalloc(size_t nbytes) -{ - return _PyObject_DebugMallocApi(_PYMALLOC_OBJ_ID, nbytes); -} -void * -_PyObject_DebugRealloc(void *p, size_t nbytes) -{ - return _PyObject_DebugReallocApi(_PYMALLOC_OBJ_ID, p, nbytes); -} -void -_PyObject_DebugFree(void *p) -{ - _PyObject_DebugFreeApi(_PYMALLOC_OBJ_ID, p); -} -void -_PyObject_DebugCheckAddress(const void *p) -{ - _PyObject_DebugCheckAddressApi(_PYMALLOC_OBJ_ID, p); -} - - -/* generic debug memory api, with an "id" to identify the API in use */ -void * -_PyObject_DebugMallocApi(char id, size_t nbytes) -{ + debug_alloc_api_t *api = (debug_alloc_api_t *)ctx; uchar *p; /* base address of malloc'ed block */ uchar *tail; /* p + 2*SST + nbytes == pointer to tail pad bytes */ size_t total; /* nbytes + 4*SST */ @@ -1492,14 +1717,14 @@ /* overflow: can't represent total as a size_t */ return NULL; - p = (uchar *)PyObject_Malloc(total); + p = (uchar *)api->alloc.malloc(api->alloc.ctx, total); if (p == NULL) return NULL; /* at p, write size (SST bytes), id (1 byte), pad (SST-1 bytes) */ write_size_t(p, nbytes); - p[SST] = (uchar)id; - memset(p + SST + 1 , FORBIDDENBYTE, SST-1); + p[SST] = (uchar)api->api_id; + memset(p + SST + 1, FORBIDDENBYTE, SST-1); if (nbytes > 0) memset(p + 2*SST, CLEANBYTE, nbytes); @@ -1517,25 +1742,27 @@ Then fills the original bytes with DEADBYTE. Then calls the underlying free. */ -void -_PyObject_DebugFreeApi(char api, void *p) +static void +_PyMem_DebugFree(void *ctx, void *p) { + debug_alloc_api_t *api = (debug_alloc_api_t *)ctx; uchar *q = (uchar *)p - 2*SST; /* address returned from malloc */ size_t nbytes; if (p == NULL) return; - _PyObject_DebugCheckAddressApi(api, p); + _PyMem_DebugCheckAddress(api->api_id, p); nbytes = read_size_t(q); nbytes += 4*SST; if (nbytes > 0) memset(q, DEADBYTE, nbytes); - PyObject_Free(q); + api->alloc.free(api->alloc.ctx, q); } -void * -_PyObject_DebugReallocApi(char api, void *p, size_t nbytes) +static void * +_PyMem_DebugRealloc(void *ctx, void *p, size_t nbytes) { + debug_alloc_api_t *api = (debug_alloc_api_t *)ctx; uchar *q = (uchar *)p; uchar *tail; size_t total; /* nbytes + 4*SST */ @@ -1543,9 +1770,9 @@ int i; if (p == NULL) - return _PyObject_DebugMallocApi(api, nbytes); + return _PyMem_DebugMalloc(ctx, nbytes); - _PyObject_DebugCheckAddressApi(api, p); + _PyMem_DebugCheckAddress(api->api_id, p); bumpserialno(); original_nbytes = read_size_t(q - 2*SST); total = nbytes + 4*SST; @@ -1562,12 +1789,12 @@ * case we didn't get the chance to mark the old memory with DEADBYTE, * but we live with that. */ - q = (uchar *)PyObject_Realloc(q - 2*SST, total); + q = (uchar *)api->alloc.realloc(api->alloc.ctx, q - 2*SST, total); if (q == NULL) return NULL; write_size_t(q, nbytes); - assert(q[SST] == (uchar)api); + assert(q[SST] == (uchar)api->api_id); for (i = 1; i < SST; ++i) assert(q[SST + i] == FORBIDDENBYTE); q += 2*SST; @@ -1589,8 +1816,8 @@ * and call Py_FatalError to kill the program. * The API id, is also checked. */ - void -_PyObject_DebugCheckAddressApi(char api, const void *p) +static void +_PyMem_DebugCheckAddress(char api, const void *p) { const uchar *q = (const uchar *)p; char msgbuf[64]; @@ -1642,7 +1869,7 @@ } /* Display info to stderr about the memory block at p. */ -void +static void _PyObject_DebugDumpAddress(const void *p) { const uchar *q = (const uchar *)p; -- Repository URL: http://hg.python.org/cpython From brett at python.org Sat Jun 15 00:57:01 2013 From: brett at python.org (Brett Cannon) Date: Fri, 14 Jun 2013 18:57:01 -0400 Subject: [Python-checkins] cpython: Closes issue 17947. Adds PEP-0435 (Enum, IntEnum) to the stdlib. In-Reply-To: <3bWtm22z9dz7Ljj@mail.python.org> References: <3bWtm22z9dz7Ljj@mail.python.org> Message-ID: Ethan, did you forget to run ``hg add`` before committing? If not then why the heck did we argue over enums for so long if this was all it took to make everyone happy? =) On Fri, Jun 14, 2013 at 3:31 AM, ethan.furman wrote: > http://hg.python.org/cpython/rev/fae92309c3be > changeset: 84117:fae92309c3be > parent: 84115:af27c661d4fb > user: Ethan Furman > date: Fri Jun 14 00:30:27 2013 -0700 > summary: > Closes issue 17947. Adds PEP-0435 (Enum, IntEnum) to the stdlib. > > files: > Doc/library/datatypes.rst | 1 + > 1 files changed, 1 insertions(+), 0 deletions(-) > > > diff --git a/Doc/library/datatypes.rst b/Doc/library/datatypes.rst > --- a/Doc/library/datatypes.rst > +++ b/Doc/library/datatypes.rst > @@ -30,3 +30,4 @@ > copy.rst > pprint.rst > reprlib.rst > + enum.rst > > -- > 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 python-checkins at python.org Sat Jun 15 01:02:43 2013 From: python-checkins at python.org (brett.cannon) Date: Sat, 15 Jun 2013 01:02:43 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2318192=3A_Introduc?= =?utf-8?q?e_importlib=2Eutil=2EMAGIC=5FNUMBER_and_document_the?= Message-ID: <3bXHQq4Bbgz7Ljs@mail.python.org> http://hg.python.org/cpython/rev/5619bc2d8207 changeset: 84128:5619bc2d8207 user: Brett Cannon date: Fri Jun 14 19:02:34 2013 -0400 summary: Issue #18192: Introduce importlib.util.MAGIC_NUMBER and document the deprecation of imp.get_magic(). files: Doc/library/imp.rst | 3 +++ Doc/library/importlib.rst | 7 +++++++ Lib/imp.py | 8 ++++++-- Lib/importlib/_bootstrap.py | 8 ++++---- Lib/importlib/util.py | 1 + Lib/test/test_importlib/test_util.py | 11 +++++++++++ Misc/NEWS | 3 +++ Python/importlib.h | 4 ++-- 8 files changed, 37 insertions(+), 8 deletions(-) diff --git a/Doc/library/imp.rst b/Doc/library/imp.rst --- a/Doc/library/imp.rst +++ b/Doc/library/imp.rst @@ -21,6 +21,9 @@ Return the magic string value used to recognize byte-compiled code files (:file:`.pyc` files). (This value may be different for each Python version.) + .. deprecated:: 3.4 + Use :attr:`importlib.util.MAGIC_NUMBER` instead. + .. function:: get_suffixes() diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst --- a/Doc/library/importlib.rst +++ b/Doc/library/importlib.rst @@ -879,6 +879,13 @@ This module contains the various objects that help in the construction of an :term:`importer`. +.. attribute:: MAGIC_NUMBER + + The bytes which represent the bytecode version number. If you need help with + loading/writing bytecode then consider :class:`importlib.abc.SourceLoader`. + + .. versionadded:: 3.4 + .. function:: resolve_name(name, package) Resolve a relative module name to an absolute one. diff --git a/Lib/imp.py b/Lib/imp.py --- a/Lib/imp.py +++ b/Lib/imp.py @@ -23,6 +23,7 @@ from importlib import _bootstrap from importlib import machinery +from importlib import util import importlib import os import sys @@ -44,8 +45,11 @@ def get_magic(): - """Return the magic number for .pyc or .pyo files.""" - return _bootstrap._MAGIC_BYTES + """**DEPRECATED** + + Return the magic number for .pyc or .pyo files. + """ + return util.MAGIC_NUMBER def get_tag(): diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py --- a/Lib/importlib/_bootstrap.py +++ b/Lib/importlib/_bootstrap.py @@ -383,8 +383,8 @@ # longer be understood by older implementations of the eval loop (usually # due to the addition of new opcodes). -_MAGIC_BYTES = (3280).to_bytes(2, 'little') + b'\r\n' -_RAW_MAGIC_NUMBER = int.from_bytes(_MAGIC_BYTES, 'little') # For import.c +MAGIC_NUMBER = (3280).to_bytes(2, 'little') + b'\r\n' +_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c _PYCACHE = '__pycache__' @@ -663,7 +663,7 @@ magic = data[:4] raw_timestamp = data[4:8] raw_size = data[8:12] - if magic != _MAGIC_BYTES: + if magic != MAGIC_NUMBER: message = 'bad magic number in {!r}: {!r}'.format(name, magic) _verbose_message(message) raise ImportError(message, **exc_details) @@ -711,7 +711,7 @@ def _code_to_bytecode(code, mtime=0, source_size=0): """Compile a code object into bytecode for writing out to a byte-compiled file.""" - data = bytearray(_MAGIC_BYTES) + data = bytearray(MAGIC_NUMBER) data.extend(_w_long(mtime)) data.extend(_w_long(source_size)) data.extend(marshal.dumps(code)) diff --git a/Lib/importlib/util.py b/Lib/importlib/util.py --- a/Lib/importlib/util.py +++ b/Lib/importlib/util.py @@ -1,5 +1,6 @@ """Utility code for constructing importers, etc.""" +from ._bootstrap import MAGIC_NUMBER from ._bootstrap import module_to_load from ._bootstrap import set_loader from ._bootstrap import set_package diff --git a/Lib/test/test_importlib/test_util.py b/Lib/test/test_importlib/test_util.py --- a/Lib/test/test_importlib/test_util.py +++ b/Lib/test/test_importlib/test_util.py @@ -313,5 +313,16 @@ util.resolve_name('..bacon', 'spam') +class MagicNumberTests(unittest.TestCase): + + def test_length(self): + # Should be 4 bytes. + self.assertEqual(len(util.MAGIC_NUMBER), 4) + + def test_incorporates_rn(self): + # The magic number uses \r\n to come out wrong when splitting on lines. + self.assertTrue(util.MAGIC_NUMBER.endswith(b'\r\n')) + + if __name__ == '__main__': unittest.main() diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -123,6 +123,9 @@ Library ------- +- Issue #18192: Introduce importlib.util.MAGIC_NUMBER and document as deprecated + imp.get_magic(). + - Issue #18149: Add filecmp.clear_cache() to manually clear the filecmp cache. Patch by Mark Levitt diff --git a/Python/importlib.h b/Python/importlib.h --- a/Python/importlib.h +++ b/Python/importlib.h [stripped] -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 15 01:20:06 2013 From: python-checkins at python.org (brett.cannon) Date: Sat, 15 Jun 2013 01:20:06 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2317907=3A_Document?= =?utf-8?q?_types=2EModuleType=27s_constructor_and_attributes=2C?= Message-ID: <3bXHpt3K6Kz7Ljs@mail.python.org> http://hg.python.org/cpython/rev/be50f1403f4d changeset: 84129:be50f1403f4d user: Brett Cannon date: Fri Jun 14 19:19:57 2013 -0400 summary: Issue #17907: Document types.ModuleType's constructor and attributes, allowing for documenting imp.new_module() as deprecated. files: Doc/library/imp.rst | 3 +++ Doc/library/types.rst | 30 ++++++++++++++++++++++++++++-- Misc/NEWS | 3 +++ 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/Doc/library/imp.rst b/Doc/library/imp.rst --- a/Doc/library/imp.rst +++ b/Doc/library/imp.rst @@ -112,6 +112,9 @@ Return a new empty module object called *name*. This object is *not* inserted in ``sys.modules``. + .. deprecated:: 3.4 + Use :class:`types.ModuleType` instead. + .. function:: reload(module) diff --git a/Doc/library/types.rst b/Doc/library/types.rst --- a/Doc/library/types.rst +++ b/Doc/library/types.rst @@ -107,9 +107,35 @@ C".) -.. data:: ModuleType +.. class:: ModuleType(name, doc=None) - The type of modules. + The type of :term:`modules `. Constructor takes the name of the + module to be created and optionally its :term:`docstring`. + + .. attribute:: __doc__ + + The :term:`docstring` of the module. Defaults to ``None``. + + .. attribute:: __loader__ + + The :term:`loader` which loaded the module. Defaults to ``None``. + + .. versionchanged:: 3.4 + Defaults to ``None``. Previously the attribute was optional. + + .. attribute:: __name__ + + The name of the module. + + .. attribute:: __package__ + + Which :term:`package` a module belongs to. If the module is top-level + (i.e. not a part of any specific package) then the attribute should be set + to ``''``, else it should be set to the name of the package (which can be + :attr:`__name__` if the module is a package itself). Defaults to ``None``. + + .. versionchanged:: 3.4 + Defaults to ``None``. Previously the attribute was optional. .. data:: TracebackType diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -123,6 +123,9 @@ Library ------- +- Issue #17907: Document imp.new_module() as deprecated in favour of + types.ModuleType. + - Issue #18192: Introduce importlib.util.MAGIC_NUMBER and document as deprecated imp.get_magic(). -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 15 01:56:12 2013 From: python-checkins at python.org (ethan.furman) Date: Sat, 15 Jun 2013 01:56:12 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Closes_issue_17947=2E__Add?= =?utf-8?q?s_PEP-0435_=28Adding_an_Enum_type_to_the_Python_standard?= Message-ID: <3bXJcX1qwVz7Lkc@mail.python.org> http://hg.python.org/cpython/rev/e7a01c7f69fe changeset: 84130:e7a01c7f69fe user: Ethan Furman date: Fri Jun 14 16:55:46 2013 -0700 summary: Closes issue 17947. Adds PEP-0435 (Adding an Enum type to the Python standard library). Missing files added. News entry added. files: Doc/library/enum.rst | 542 +++++++++++++++++ Lib/enum.py | 465 +++++++++++++++ Lib/test/test_enum.py | 921 ++++++++++++++++++++++++++++++ Misc/NEWS | 2 + 4 files changed, 1930 insertions(+), 0 deletions(-) diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst new file mode 100644 --- /dev/null +++ b/Doc/library/enum.rst @@ -0,0 +1,542 @@ +:mod:`enum` --- Support for enumerations +======================================== + +.. module:: enum +.. :synopsis: enumerations are sets of symbolic names bound to unique, constant + values. +.. :moduleauthor:: Ethan Furman +.. :sectionauthor:: Barry Warsaw , +.. :sectionauthor:: Eli Bendersky , +.. :sectionauthor:: Ethan Furman + +**Source code:** :source:`Lib/enum.py` + +---------------- + +An enumeration is a set of symbolic names (members) bound to unique, constant +values. Within an enumeration, the members can be compared by identity, and +the enumeration itself can be iterated over. + +This module defines two enumeration classes that can be used to define unique +sets of names and values: :class:`Enum` and :class:`IntEnum`. + +Creating an Enum +---------------- + +Enumerations are created using the :keyword:`class` syntax, which makes them +easy to read and write. An alternative creation method is described in +`Functional API`_. To define an enumeration, subclass :class:`Enum` as +follows:: + + >>> from enum import Enum + >>> class Color(Enum): + ... red = 1 + ... green = 2 + ... blue = 3 + +**A note on nomenclature**: we call :class:`Color` an *enumeration* (or *enum*) +and :attr:`Color.red`, :attr:`Color.green` are *enumeration members* (or +*enum members*). Enumeration members also have *values* (the value of +:attr:`Color.red` is ``1``, etc.) + +Enumeration members have human readable string representations:: + + >>> print(Color.red) + Color.red + +...while their ``repr`` has more information:: + + >>> print(repr(Color.red)) + + +The *type* of an enumeration member is the enumeration it belongs to:: + + >>> type(Color.red) + + >>> isinstance(Color.green, Color) + True + >>> + +Enum members also have a property that contains just their item name:: + + >>> print(Color.red.name) + red + +Enumerations support iteration, in definition order:: + + >>> class Shake(Enum): + ... vanilla = 7 + ... chocolate = 4 + ... cookies = 9 + ... mint = 3 + ... + >>> for shake in Shake: + ... print(shake) + ... + Shake.vanilla + Shake.chocolate + Shake.cookies + Shake.mint + +Enumeration members are hashable, so they can be used in dictionaries and sets:: + + >>> apples = {} + >>> apples[Color.red] = 'red delicious' + >>> apples[Color.green] = 'granny smith' + >>> apples == {Color.red: 'red delicious', Color.green: 'granny smith'} + True + + +Programmatic access to enumeration members +------------------------------------------ + +Sometimes it's useful to access members in enumerations programmatically (i.e. +situations where ``Color.red`` won't do because the exact color is not known +at program-writing time). ``Enum`` allows such access:: + + >>> Color(1) + + >>> Color(3) + + +If you want to access enum members by *name*, use item access:: + + >>> Color['red'] + + >>> Color['green'] + + + +Duplicating enum members and values +----------------------------------- + +Having two enum members with the same name is invalid:: + + >>> class Shape(Enum): + ... square = 2 + ... square = 3 + ... + Traceback (most recent call last): + ... + TypeError: Attempted to reuse key: 'square' + +However, two enum members are allowed to have the same value. Given two members +A and B with the same value (and A defined first), B is an alias to A. By-value +lookup of the value of A and B will return A. By-name lookup of B will also +return A:: + + >>> class Shape(Enum): + ... square = 2 + ... diamond = 1 + ... circle = 3 + ... alias_for_square = 2 + ... + >>> Shape.square + + >>> Shape.alias_for_square + + >>> Shape(2) + + +Iterating over the members of an enum does not provide the aliases:: + + >>> list(Shape) + [, , ] + +The special attribute ``__members__`` is an ordered dictionary mapping names +to members. It includes all names defined in the enumeration, including the +aliases:: + + >>> for name, member in Shape.__members__.items(): + ... name, member + ... + ('square', ) + ('diamond', ) + ('circle', ) + ('alias_for_square', ) + +The ``__members__`` attribute can be used for detailed programmatic access to +the enumeration members. For example, finding all the aliases:: + + >>> [name for name, member in Shape.__members__.items() if member.name != name] + ['alias_for_square'] + +Comparisons +----------- + +Enumeration members are compared by identity:: + + >>> Color.red is Color.red + True + >>> Color.red is Color.blue + False + >>> Color.red is not Color.blue + True + +Ordered comparisons between enumeration values are *not* supported. Enum +members are not integers (but see `IntEnum`_ below):: + + >>> Color.red < Color.blue + Traceback (most recent call last): + File "", line 1, in + TypeError: unorderable types: Color() < Color() + +Equality comparisons are defined though:: + + >>> Color.blue == Color.red + False + >>> Color.blue != Color.red + True + >>> Color.blue == Color.blue + True + +Comparisons against non-enumeration values will always compare not equal +(again, class:`IntEnum` was explicitly designed to behave differently, see +below):: + + >>> Color.blue == 2 + False + + +Allowed members and attributes of enumerations +---------------------------------------------- + +The examples above use integers for enumeration values. Using integers is +short and handy (and provided by default by the `Functional API`_), but not +strictly enforced. In the vast majority of use-cases, one doesn't care what +the actual value of an enumeration is. But if the value *is* important, +enumerations can have arbitrary values. + +Enumerations are Python classes, and can have methods and special methods as +usual. If we have this enumeration:: + + >>> class Mood(Enum): + ... funky = 1 + ... happy = 3 + ... + ... def describe(self): + ... # self is the member here + ... return self.name, self.value + ... + ... def __str__(self): + ... return 'my custom str! {0}'.format(self.value) + ... + ... @classmethod + ... def favorite_mood(cls): + ... # cls here is the enumeration + ... return cls.happy + +Then:: + + >>> Mood.favorite_mood() + + >>> Mood.happy.describe() + ('happy', 3) + >>> str(Mood.funky) + 'my custom str! 1' + +The rules for what is allowed are as follows: _sunder_ names (starting and +ending with a single underscore) are reserved by enum and cannot be used; +all other attributes defined within an enumeration will become members of this +enumeration, with the exception of *__dunder__* names and descriptors (methods +are also descriptors). + +Note: if your enumeration defines :meth:`__new__` and/or :meth:`__init__` then +whatever value(s) were given to the enum member will be passed into those +methods. See `Planet`_ for an example. + + +Restricted subclassing of enumerations +-------------------------------------- + +Subclassing an enumeration is allowed only if the enumeration does not define +any members. So this is forbidden:: + + >>> class MoreColor(Color): + ... pink = 17 + Traceback (most recent call last): + ... + TypeError: Cannot extend enumerations + +But this is allowed:: + + >>> class Foo(Enum): + ... def some_behavior(self): + ... pass + ... + >>> class Bar(Foo): + ... happy = 1 + ... sad = 2 + ... + +Allowing subclassing of enums that define members would lead to a violation of +some important invariants of types and instances. On the other hand, it makes +sense to allow sharing some common behavior between a group of enumerations. +(See `OrderedEnum`_ for an example.) + + +Pickling +-------- + +Enumerations can be pickled and unpickled:: + + >>> from test.test_enum import Fruit + >>> from pickle import dumps, loads + >>> Fruit.tomato is loads(dumps(Fruit.tomato)) + True + +The usual restrictions for pickling apply: picklable enums must be defined in +the top level of a module, since unpickling requires them to be importable +from that module. + +.. warning:: + + In order to support the singleton nature of enumeration members, pickle + protocol version 2 or higher must be used. + + +Functional API +-------------- + +The :class:`Enum` class is callable, providing the following functional API:: + + >>> Animal = Enum('Animal', 'ant bee cat dog') + >>> Animal + + >>> Animal.ant + + >>> Animal.ant.value + 1 + >>> list(Animal) + [, , , ] + +The semantics of this API resemble :class:`namedtuple`. The first argument +of the call to :class:`Enum` is the name of the enumeration. + +The second argument is the *source* of enumeration member names. It can be a +whitespace-separated string of names, a sequence of names, a sequence of +2-tuples with key/value pairs, or a mapping (e.g. dictionary) of names to +values. The last two options enable assigning arbitrary values to +enumerations; the others auto-assign increasing integers starting with 1. A +new class derived from :class:`Enum` is returned. In other words, the above +assignment to :class:`Animal` is equivalent to:: + + >>> class Animals(Enum): + ... ant = 1 + ... bee = 2 + ... cat = 3 + ... dog = 4 + +Pickling enums created with the functional API can be tricky as frame stack +implementation details are used to try and figure out which module the +enumeration is being created in (e.g. it will fail if you use a utility +function in separate module, and also may not work on IronPython or Jython). +The solution is to specify the module name explicitly as follows:: + + >>> Animals = Enum('Animals', 'ant bee cat dog', module=__name__) + +Derived Enumerations +==================== + +IntEnum +------- + +A variation of :class:`Enum` is provided which is also a subclass of +:class:`int`. Members of an :class:`IntEnum` can be compared to integers; +by extension, integer enumerations of different types can also be compared +to each other:: + + >>> from enum import IntEnum + >>> class Shape(IntEnum): + ... circle = 1 + ... square = 2 + ... + >>> class Request(IntEnum): + ... post = 1 + ... get = 2 + ... + >>> Shape == 1 + False + >>> Shape.circle == 1 + True + >>> Shape.circle == Request.post + True + +However, they still can't be compared to standard :class:`Enum` enumerations:: + + >>> class Shape(IntEnum): + ... circle = 1 + ... square = 2 + ... + >>> class Color(Enum): + ... red = 1 + ... green = 2 + ... + >>> Shape.circle == Color.red + False + +:class:`IntEnum` values behave like integers in other ways you'd expect:: + + >>> int(Shape.circle) + 1 + >>> ['a', 'b', 'c'][Shape.circle] + 'b' + >>> [i for i in range(Shape.square)] + [0, 1] + +For the vast majority of code, :class:`Enum` is strongly recommended, +since :class:`IntEnum` breaks some semantic promises of an enumeration (by +being comparable to integers, and thus by transitivity to other +unrelated enumerations). It should be used only in special cases where +there's no other choice; for example, when integer constants are +replaced with enumerations and backwards compatibility is required with code +that still expects integers. + + +Others +------ + +While :class:`IntEnum` is part of the :mod:`enum` module, it would be very +simple to implement independently:: + + class IntEnum(int, Enum): + pass + +This demonstrates how similar derived enumerations can be defined; for example +a :class:`StrEnum` that mixes in :class:`str` instead of :class:`int`. + +Some rules: + +1. When subclassing :class:`Enum`, mix-in types must appear before + :class:`Enum` itself in the sequence of bases, as in the :class:`IntEnum` + example above. +2. While :class:`Enum` can have members of any type, once you mix in an + additional type, all the members must have values of that type, e.g. + :class:`int` above. This restriction does not apply to mix-ins which only + add methods and don't specify another data type such as :class:`int` or + :class:`str`. +3. When another data type is mixed in, the :attr:`value` attribute is *not the + same* as the enum member itself, although it is equivalant and will compare + equal. + + +Interesting examples +==================== + +While :class:`Enum` and :class:`IntEnum` are expected to cover the majority of +use-cases, they cannot cover them all. Here are recipes for some different +types of enumerations that can be used directly, or as examples for creating +one's own. + + +AutoNumber +---------- + +Avoids having to specify the value for each enumeration member:: + + >>> class AutoNumber(Enum): + ... def __new__(cls): + ... value = len(cls.__members__) + 1 + ... obj = object.__new__(cls) + ... obj._value = value + ... return obj + ... + >>> class Color(AutoNumber): + ... red = () + ... green = () + ... blue = () + ... + >>> Color.green.value == 2 + True + + +UniqueEnum +---------- + +Raises an error if a duplicate member name is found instead of creating an +alias:: + + >>> class UniqueEnum(Enum): + ... def __init__(self, *args): + ... cls = self.__class__ + ... if any(self.value == e.value for e in cls): + ... a = self.name + ... e = cls(self.value).name + ... raise ValueError( + ... "aliases not allowed in UniqueEnum: %r --> %r" + ... % (a, e)) + ... + >>> class Color(UniqueEnum): + ... red = 1 + ... green = 2 + ... blue = 3 + ... grene = 2 + Traceback (most recent call last): + ... + ValueError: aliases not allowed in UniqueEnum: 'grene' --> 'green' + + +OrderedEnum +----------- + +An ordered enumeration that is not based on :class:`IntEnum` and so maintains +the normal :class:`Enum` invariants (such as not being comparable to other +enumerations):: + + >>> class OrderedEnum(Enum): + ... def __ge__(self, other): + ... if self.__class__ is other.__class__: + ... return self._value >= other._value + ... return NotImplemented + ... def __gt__(self, other): + ... if self.__class__ is other.__class__: + ... return self._value > other._value + ... return NotImplemented + ... def __le__(self, other): + ... if self.__class__ is other.__class__: + ... return self._value <= other._value + ... return NotImplemented + ... def __lt__(self, other): + ... if self.__class__ is other.__class__: + ... return self._value < other._value + ... return NotImplemented + ... + >>> class Grade(OrderedEnum): + ... A = 5 + ... B = 4 + ... C = 3 + ... D = 2 + ... F = 1 + ... + >>> Grade.C < Grade.A + True + + +Planet +------ + +If :meth:`__new__` or :meth:`__init__` is defined the value of the enum member +will be passed to those methods:: + + >>> class Planet(Enum): + ... MERCURY = (3.303e+23, 2.4397e6) + ... VENUS = (4.869e+24, 6.0518e6) + ... EARTH = (5.976e+24, 6.37814e6) + ... MARS = (6.421e+23, 3.3972e6) + ... JUPITER = (1.9e+27, 7.1492e7) + ... SATURN = (5.688e+26, 6.0268e7) + ... URANUS = (8.686e+25, 2.5559e7) + ... NEPTUNE = (1.024e+26, 2.4746e7) + ... def __init__(self, mass, radius): + ... self.mass = mass # in kilograms + ... self.radius = radius # in meters + ... @property + ... def surface_gravity(self): + ... # universal gravitational constant (m3 kg-1 s-2) + ... G = 6.67300E-11 + ... return G * self.mass / (self.radius * self.radius) + ... + >>> Planet.EARTH.value + (5.976e+24, 6378140.0) + >>> Planet.EARTH.surface_gravity + 9.802652743337129 diff --git a/Lib/enum.py b/Lib/enum.py new file mode 100644 --- /dev/null +++ b/Lib/enum.py @@ -0,0 +1,465 @@ +"""Python Enumerations""" + +import sys +from collections import OrderedDict +from types import MappingProxyType + +__all__ = ['Enum', 'IntEnum'] + + +class _RouteClassAttributeToGetattr: + """Route attribute access on a class to __getattr__. + + This is a descriptor, used to define attributes that act differently when + accessed through an instance and through a class. Instance access remains + normal, but access to an attribute through a class will be routed to the + class's __getattr__ method; this is done by raising AttributeError. + + """ + def __init__(self, fget=None): + self.fget = fget + + def __get__(self, instance, ownerclass=None): + if instance is None: + raise AttributeError() + return self.fget(instance) + + def __set__(self, instance, value): + raise AttributeError("can't set attribute") + + def __delete__(self, instance): + raise AttributeError("can't delete attribute") + + +def _is_dunder(name): + """Returns True if a __dunder__ name, False otherwise.""" + return (name[:2] == name[-2:] == '__' and + name[2:3] != '_' and + name[-3:-2] != '_') + + +def _is_sunder(name): + """Returns True if a _sunder_ name, False otherwise.""" + return (name[0] == name[-1] == '_' and + name[1:2] != '_' and + name[-2:-1] != '_') + + +def _make_class_unpicklable(cls): + """Make the given class un-picklable.""" + def _break_on_call_reduce(self): + raise TypeError('%r cannot be pickled' % self) + cls.__reduce__ = _break_on_call_reduce + cls.__module__ = '' + + +class _EnumDict(dict): + """Keeps track of definition order of the enum items. + + EnumMeta will use the names found in self._member_names as the + enumeration member names. + + """ + def __init__(self): + super().__init__() + self._member_names = [] + + def __setitem__(self, key, value): + """Changes anything not dundered or that doesn't have __get__. + + If a descriptor is added with the same name as an enum member, the name + is removed from _member_names (this may leave a hole in the numerical + sequence of values). + + If an enum member name is used twice, an error is raised; duplicate + values are not checked for. + + Single underscore (sunder) names are reserved. + + """ + if _is_sunder(key): + raise ValueError('_names_ are reserved for future Enum use') + elif _is_dunder(key) or hasattr(value, '__get__'): + if key in self._member_names: + # overwriting an enum with a method? then remove the name from + # _member_names or it will become an enum anyway when the class + # is created + self._member_names.remove(key) + else: + if key in self._member_names: + raise TypeError('Attempted to reuse key: %r' % key) + self._member_names.append(key) + super().__setitem__(key, value) + + +# Dummy value for Enum as EnumMeta explicity checks for it, but of course until +# EnumMeta finishes running the first time the Enum class doesn't exist. This +# is also why there are checks in EnumMeta like `if Enum is not None` +Enum = None + + +class EnumMeta(type): + """Metaclass for Enum""" + @classmethod + def __prepare__(metacls, cls, bases): + return _EnumDict() + + def __new__(metacls, cls, bases, classdict): + # an Enum class is final once enumeration items have been defined; it + # cannot be mixed with other types (int, float, etc.) if it has an + # inherited __new__ unless a new __new__ is defined (or the resulting + # class will fail). + member_type, first_enum = metacls._get_mixins_(bases) + __new__, save_new, use_args = metacls._find_new_(classdict, member_type, + first_enum) + + # save enum items into separate mapping so they don't get baked into + # the new class + members = {k: classdict[k] for k in classdict._member_names} + for name in classdict._member_names: + del classdict[name] + + # check for illegal enum names (any others?) + invalid_names = set(members) & {'mro', } + if invalid_names: + raise ValueError('Invalid enum member name: {0}'.format( + ','.join(invalid_names))) + + # create our new Enum type + enum_class = super().__new__(metacls, cls, bases, classdict) + enum_class._member_names = [] # names in definition order + enum_class._member_map = OrderedDict() # name->value map + + # Reverse value->name map for hashable values. + enum_class._value2member_map = {} + + # check for a __getnewargs__, and if not present sabotage + # pickling, since it won't work anyway + if (member_type is not object and + member_type.__dict__.get('__getnewargs__') is None + ): + _make_class_unpicklable(enum_class) + + # instantiate them, checking for duplicates as we go + # we instantiate first instead of checking for duplicates first in case + # a custom __new__ is doing something funky with the values -- such as + # auto-numbering ;) + for member_name in classdict._member_names: + value = members[member_name] + if not isinstance(value, tuple): + args = (value, ) + else: + args = value + if member_type is tuple: # special case for tuple enums + args = (args, ) # wrap it one more time + if not use_args: + enum_member = __new__(enum_class) + enum_member._value = value + else: + enum_member = __new__(enum_class, *args) + if not hasattr(enum_member, '_value'): + enum_member._value = member_type(*args) + enum_member._member_type = member_type + enum_member._name = member_name + enum_member.__init__(*args) + # If another member with the same value was already defined, the + # new member becomes an alias to the existing one. + for name, canonical_member in enum_class._member_map.items(): + if canonical_member.value == enum_member._value: + enum_member = canonical_member + break + else: + # Aliases don't appear in member names (only in __members__). + enum_class._member_names.append(member_name) + enum_class._member_map[member_name] = enum_member + try: + # This may fail if value is not hashable. We can't add the value + # to the map, and by-value lookups for this value will be + # linear. + enum_class._value2member_map[value] = enum_member + except TypeError: + pass + + # double check that repr and friends are not the mixin's or various + # things break (such as pickle) + for name in ('__repr__', '__str__', '__getnewargs__'): + class_method = getattr(enum_class, name) + obj_method = getattr(member_type, name, None) + enum_method = getattr(first_enum, name, None) + if obj_method is not None and obj_method is class_method: + setattr(enum_class, name, enum_method) + + # replace any other __new__ with our own (as long as Enum is not None, + # anyway) -- again, this is to support pickle + if Enum is not None: + # if the user defined their own __new__, save it before it gets + # clobbered in case they subclass later + if save_new: + enum_class.__new_member__ = __new__ + enum_class.__new__ = Enum.__new__ + return enum_class + + def __call__(cls, value, names=None, *, module=None, type=None): + """Either returns an existing member, or creates a new enum class. + + This method is used both when an enum class is given a value to match + to an enumeration member (i.e. Color(3)) and for the functional API + (i.e. Color = Enum('Color', names='red green blue')). + + When used for the functional API: `module`, if set, will be stored in + the new class' __module__ attribute; `type`, if set, will be mixed in + as the first base class. + + Note: if `module` is not set this routine will attempt to discover the + calling module by walking the frame stack; if this is unsuccessful + the resulting class will not be pickleable. + + """ + if names is None: # simple value lookup + return cls.__new__(cls, value) + # otherwise, functional API: we're creating a new Enum type + return cls._create_(value, names, module=module, type=type) + + def __contains__(cls, member): + return isinstance(member, cls) and member.name in cls._member_map + + def __dir__(self): + return ['__class__', '__doc__', '__members__'] + self._member_names + + @property + def __members__(cls): + """Returns a mapping of member name->value. + + This mapping lists all enum members, including aliases. Note that this + is a read-only view of the internal mapping. + + """ + return MappingProxyType(cls._member_map) + + def __getattr__(cls, name): + """Return the enum member matching `name` + + We use __getattr__ instead of descriptors or inserting into the enum + class' __dict__ in order to support `name` and `value` being both + properties for enum members (which live in the class' __dict__) and + enum members themselves. + + """ + if _is_dunder(name): + raise AttributeError(name) + try: + return cls._member_map[name] + except KeyError: + raise AttributeError(name) from None + + def __getitem__(cls, name): + return cls._member_map[name] + + def __iter__(cls): + return (cls._member_map[name] for name in cls._member_names) + + def __len__(cls): + return len(cls._member_names) + + def __repr__(cls): + return "" % cls.__name__ + + def _create_(cls, class_name, names=None, *, module=None, type=None): + """Convenience method to create a new Enum class. + + `names` can be: + + * A string containing member names, separated either with spaces or + commas. Values are auto-numbered from 1. + * An iterable of member names. Values are auto-numbered from 1. + * An iterable of (member name, value) pairs. + * A mapping of member name -> value. + + """ + metacls = cls.__class__ + bases = (cls, ) if type is None else (type, cls) + classdict = metacls.__prepare__(class_name, bases) + + # special processing needed for names? + if isinstance(names, str): + names = names.replace(',', ' ').split() + if isinstance(names, (tuple, list)) and isinstance(names[0], str): + names = [(e, i) for (i, e) in enumerate(names, 1)] + + # Here, names is either an iterable of (name, value) or a mapping. + for item in names: + if isinstance(item, str): + member_name, member_value = item, names[item] + else: + member_name, member_value = item + classdict[member_name] = member_value + enum_class = metacls.__new__(metacls, class_name, bases, classdict) + + # TODO: replace the frame hack if a blessed way to know the calling + # module is ever developed + if module is None: + try: + module = sys._getframe(2).f_globals['__name__'] + except (AttributeError, ValueError) as exc: + pass + if module is None: + _make_class_unpicklable(enum_class) + else: + enum_class.__module__ = module + + return enum_class + + @staticmethod + def _get_mixins_(bases): + """Returns the type for creating enum members, and the first inherited + enum class. + + bases: the tuple of bases that was given to __new__ + + """ + if not bases: + return object, Enum + + # double check that we are not subclassing a class with existing + # enumeration members; while we're at it, see if any other data + # type has been mixed in so we can use the correct __new__ + member_type = first_enum = None + for base in bases: + if (base is not Enum and + issubclass(base, Enum) and + base._member_names): + raise TypeError("Cannot extend enumerations") + # base is now the last base in bases + if not issubclass(base, Enum): + raise TypeError("new enumerations must be created as " + "`ClassName([mixin_type,] enum_type)`") + + # get correct mix-in type (either mix-in type of Enum subclass, or + # first base if last base is Enum) + if not issubclass(bases[0], Enum): + member_type = bases[0] # first data type + first_enum = bases[-1] # enum type + else: + for base in bases[0].__mro__: + # most common: (IntEnum, int, Enum, object) + # possible: (, , + # , , + # ) + if issubclass(base, Enum): + if first_enum is None: + first_enum = base + else: + if member_type is None: + member_type = base + + return member_type, first_enum + + @staticmethod + def _find_new_(classdict, member_type, first_enum): + """Returns the __new__ to be used for creating the enum members. + + classdict: the class dictionary given to __new__ + member_type: the data type whose __new__ will be used by default + first_enum: enumeration to check for an overriding __new__ + + """ + # now find the correct __new__, checking to see of one was defined + # by the user; also check earlier enum classes in case a __new__ was + # saved as __new_member__ + __new__ = classdict.get('__new__', None) + + # should __new__ be saved as __new_member__ later? + save_new = __new__ is not None + + if __new__ is None: + # check all possibles for __new_member__ before falling back to + # __new__ + for method in ('__new_member__', '__new__'): + for possible in (member_type, first_enum): + target = getattr(possible, method, None) + if target not in { + None, + None.__new__, + object.__new__, + Enum.__new__, + }: + __new__ = target + break + if __new__ is not None: + break + else: + __new__ = object.__new__ + + # if a non-object.__new__ is used then whatever value/tuple was + # assigned to the enum member name will be passed to __new__ and to the + # new enum member's __init__ + if __new__ is object.__new__: + use_args = False + else: + use_args = True + + return __new__, save_new, use_args + + +class Enum(metaclass=EnumMeta): + """Generic enumeration. + + Derive from this class to define new enumerations. + + """ + def __new__(cls, value): + # all enum instances are actually created during class construction + # without calling this method; this method is called by the metaclass' + # __call__ (i.e. Color(3) ), and by pickle + if type(value) is cls: + # For lookups like Color(Color.red) + return value + # by-value search for a matching enum member + # see if it's in the reverse mapping (for hashable values) + if value in cls._value2member_map: + return cls._value2member_map[value] + # not there, now do long search -- O(n) behavior + for member in cls._member_map.values(): + if member.value == value: + return member + raise ValueError("%s is not a valid %s" % (value, cls.__name__)) + + def __repr__(self): + return "<%s.%s: %r>" % ( + self.__class__.__name__, self._name, self._value) + + def __str__(self): + return "%s.%s" % (self.__class__.__name__, self._name) + + def __dir__(self): + return (['__class__', '__doc__', 'name', 'value']) + + def __eq__(self, other): + if type(other) is self.__class__: + return self is other + return NotImplemented + + def __getnewargs__(self): + return (self._value, ) + + def __hash__(self): + return hash(self._name) + + # _RouteClassAttributeToGetattr is used to provide access to the `name` + # and `value` properties of enum members while keeping some measure of + # protection from modification, while still allowing for an enumeration + # to have members named `name` and `value`. This works because enumeration + # members are not set directly on the enum class -- __getattr__ is + # used to look them up. + + @_RouteClassAttributeToGetattr + def name(self): + return self._name + + @_RouteClassAttributeToGetattr + def value(self): + return self._value + + +class IntEnum(int, Enum): + """Enum where members are also (and must be) ints""" diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py new file mode 100644 --- /dev/null +++ b/Lib/test/test_enum.py @@ -0,0 +1,921 @@ +import enum +import unittest +from collections import OrderedDict +from pickle import dumps, loads, PicklingError +from enum import Enum, IntEnum + +# for pickle tests +try: + class Stooges(Enum): + LARRY = 1 + CURLY = 2 + MOE = 3 +except Exception as exc: + Stooges = exc + +try: + class IntStooges(int, Enum): + LARRY = 1 + CURLY = 2 + MOE = 3 +except Exception as exc: + IntStooges = exc + +try: + class FloatStooges(float, Enum): + LARRY = 1.39 + CURLY = 2.72 + MOE = 3.142596 +except Exception as exc: + FloatStooges = exc + +# for pickle test and subclass tests +try: + class StrEnum(str, Enum): + 'accepts only string values' + class Name(StrEnum): + BDFL = 'Guido van Rossum' + FLUFL = 'Barry Warsaw' +except Exception as exc: + Name = exc + +try: + Question = Enum('Question', 'who what when where why', module=__name__) +except Exception as exc: + Question = exc + +try: + Answer = Enum('Answer', 'him this then there because') +except Exception as exc: + Answer = exc + +# for doctests +try: + class Fruit(Enum): + tomato = 1 + banana = 2 + cherry = 3 +except Exception: + pass + +class TestEnum(unittest.TestCase): + def setUp(self): + class Season(Enum): + SPRING = 1 + SUMMER = 2 + AUTUMN = 3 + WINTER = 4 + self.Season = Season + + def test_enum_in_enum_out(self): + Season = self.Season + self.assertIs(Season(Season.WINTER), Season.WINTER) + + def test_enum_value(self): + Season = self.Season + self.assertEqual(Season.SPRING.value, 1) + + def test_intenum_value(self): + self.assertEqual(IntStooges.CURLY.value, 2) + + def test_dir_on_class(self): + Season = self.Season + self.assertEqual( + set(dir(Season)), + set(['__class__', '__doc__', '__members__', + 'SPRING', 'SUMMER', 'AUTUMN', 'WINTER']), + ) + + def test_dir_on_item(self): + Season = self.Season + self.assertEqual( + set(dir(Season.WINTER)), + set(['__class__', '__doc__', 'name', 'value']), + ) + + def test_enum(self): + Season = self.Season + lst = list(Season) + self.assertEqual(len(lst), len(Season)) + self.assertEqual(len(Season), 4, Season) + self.assertEqual( + [Season.SPRING, Season.SUMMER, Season.AUTUMN, Season.WINTER], lst) + + for i, season in enumerate('SPRING SUMMER AUTUMN WINTER'.split(), 1): + e = Season(i) + self.assertEqual(e, getattr(Season, season)) + self.assertEqual(e.value, i) + self.assertNotEqual(e, i) + self.assertEqual(e.name, season) + self.assertIn(e, Season) + self.assertIs(type(e), Season) + self.assertIsInstance(e, Season) + self.assertEqual(str(e), 'Season.' + season) + self.assertEqual( + repr(e), + ''.format(season, i), + ) + + def test_value_name(self): + Season = self.Season + self.assertEqual(Season.SPRING.name, 'SPRING') + self.assertEqual(Season.SPRING.value, 1) + with self.assertRaises(AttributeError): + Season.SPRING.name = 'invierno' + with self.assertRaises(AttributeError): + Season.SPRING.value = 2 + + def test_invalid_names(self): + with self.assertRaises(ValueError): + class Wrong(Enum): + mro = 9 + with self.assertRaises(ValueError): + class Wrong(Enum): + _create_= 11 + with self.assertRaises(ValueError): + class Wrong(Enum): + _get_mixins_ = 9 + with self.assertRaises(ValueError): + class Wrong(Enum): + _find_new_ = 1 + with self.assertRaises(ValueError): + class Wrong(Enum): + _any_name_ = 9 + + def test_contains(self): + Season = self.Season + self.assertIn(Season.AUTUMN, Season) + self.assertNotIn(3, Season) + + val = Season(3) + self.assertIn(val, Season) + + class OtherEnum(Enum): + one = 1; two = 2 + self.assertNotIn(OtherEnum.two, Season) + + def test_comparisons(self): + Season = self.Season + with self.assertRaises(TypeError): + Season.SPRING < Season.WINTER + with self.assertRaises(TypeError): + Season.SPRING > 4 + + self.assertNotEqual(Season.SPRING, 1) + + class Part(Enum): + SPRING = 1 + CLIP = 2 + BARREL = 3 + + self.assertNotEqual(Season.SPRING, Part.SPRING) + with self.assertRaises(TypeError): + Season.SPRING < Part.CLIP + + def test_enum_duplicates(self): + class Season(Enum): + SPRING = 1 + SUMMER = 2 + AUTUMN = FALL = 3 + WINTER = 4 + ANOTHER_SPRING = 1 + lst = list(Season) + self.assertEqual( + lst, + [Season.SPRING, Season.SUMMER, + Season.AUTUMN, Season.WINTER, + ]) + self.assertIs(Season.FALL, Season.AUTUMN) + self.assertEqual(Season.FALL.value, 3) + self.assertEqual(Season.AUTUMN.value, 3) + self.assertIs(Season(3), Season.AUTUMN) + self.assertIs(Season(1), Season.SPRING) + self.assertEqual(Season.FALL.name, 'AUTUMN') + self.assertEqual( + [k for k,v in Season.__members__.items() if v.name != k], + ['FALL', 'ANOTHER_SPRING'], + ) + + def test_enum_with_value_name(self): + class Huh(Enum): + name = 1 + value = 2 + self.assertEqual( + list(Huh), + [Huh.name, Huh.value], + ) + self.assertIs(type(Huh.name), Huh) + self.assertEqual(Huh.name.name, 'name') + self.assertEqual(Huh.name.value, 1) + def test_hash(self): + Season = self.Season + dates = {} + dates[Season.WINTER] = '1225' + dates[Season.SPRING] = '0315' + dates[Season.SUMMER] = '0704' + dates[Season.AUTUMN] = '1031' + self.assertEqual(dates[Season.AUTUMN], '1031') + + def test_intenum_from_scratch(self): + class phy(int, Enum): + pi = 3 + tau = 2 * pi + self.assertTrue(phy.pi < phy.tau) + + def test_intenum_inherited(self): + class IntEnum(int, Enum): + pass + class phy(IntEnum): + pi = 3 + tau = 2 * pi + self.assertTrue(phy.pi < phy.tau) + + def test_floatenum_from_scratch(self): + class phy(float, Enum): + pi = 3.141596 + tau = 2 * pi + self.assertTrue(phy.pi < phy.tau) + + def test_floatenum_inherited(self): + class FloatEnum(float, Enum): + pass + class phy(FloatEnum): + pi = 3.141596 + tau = 2 * pi + self.assertTrue(phy.pi < phy.tau) + + def test_strenum_from_scratch(self): + class phy(str, Enum): + pi = 'Pi' + tau = 'Tau' + self.assertTrue(phy.pi < phy.tau) + + def test_strenum_inherited(self): + class StrEnum(str, Enum): + pass + class phy(StrEnum): + pi = 'Pi' + tau = 'Tau' + self.assertTrue(phy.pi < phy.tau) + + + def test_intenum(self): + class WeekDay(IntEnum): + SUNDAY = 1 + MONDAY = 2 + TUESDAY = 3 + WEDNESDAY = 4 + THURSDAY = 5 + FRIDAY = 6 + SATURDAY = 7 + + self.assertEqual(['a', 'b', 'c'][WeekDay.MONDAY], 'c') + self.assertEqual([i for i in range(WeekDay.TUESDAY)], [0, 1, 2]) + + lst = list(WeekDay) + self.assertEqual(len(lst), len(WeekDay)) + self.assertEqual(len(WeekDay), 7) + target = 'SUNDAY MONDAY TUESDAY WEDNESDAY THURSDAY FRIDAY SATURDAY' + target = target.split() + for i, weekday in enumerate(target, 1): + e = WeekDay(i) + self.assertEqual(e, i) + self.assertEqual(int(e), i) + self.assertEqual(e.name, weekday) + self.assertIn(e, WeekDay) + self.assertEqual(lst.index(e)+1, i) + self.assertTrue(0 < e < 8) + self.assertIs(type(e), WeekDay) + self.assertIsInstance(e, int) + self.assertIsInstance(e, Enum) + + def test_intenum_duplicates(self): + class WeekDay(IntEnum): + SUNDAY = 1 + MONDAY = 2 + TUESDAY = TEUSDAY = 3 + WEDNESDAY = 4 + THURSDAY = 5 + FRIDAY = 6 + SATURDAY = 7 + self.assertIs(WeekDay.TEUSDAY, WeekDay.TUESDAY) + self.assertEqual(WeekDay(3).name, 'TUESDAY') + self.assertEqual([k for k,v in WeekDay.__members__.items() + if v.name != k], ['TEUSDAY', ]) + + def test_pickle_enum(self): + if isinstance(Stooges, Exception): + raise Stooges + self.assertIs(Stooges.CURLY, loads(dumps(Stooges.CURLY))) + self.assertIs(Stooges, loads(dumps(Stooges))) + + def test_pickle_int(self): + if isinstance(IntStooges, Exception): + raise IntStooges + self.assertIs(IntStooges.CURLY, loads(dumps(IntStooges.CURLY))) + self.assertIs(IntStooges, loads(dumps(IntStooges))) + + def test_pickle_float(self): + if isinstance(FloatStooges, Exception): + raise FloatStooges + self.assertIs(FloatStooges.CURLY, loads(dumps(FloatStooges.CURLY))) + self.assertIs(FloatStooges, loads(dumps(FloatStooges))) + + def test_pickle_enum_function(self): + if isinstance(Answer, Exception): + raise Answer + self.assertIs(Answer.him, loads(dumps(Answer.him))) + self.assertIs(Answer, loads(dumps(Answer))) + + def test_pickle_enum_function_with_module(self): + if isinstance(Question, Exception): + raise Question + self.assertIs(Question.who, loads(dumps(Question.who))) + self.assertIs(Question, loads(dumps(Question))) + + def test_exploding_pickle(self): + BadPickle = Enum('BadPickle', 'dill sweet bread-n-butter') + enum._make_class_unpicklable(BadPickle) + globals()['BadPickle'] = BadPickle + with self.assertRaises(TypeError): + dumps(BadPickle.dill) + with self.assertRaises(PicklingError): + dumps(BadPickle) + + def test_string_enum(self): + class SkillLevel(str, Enum): + master = 'what is the sound of one hand clapping?' + journeyman = 'why did the chicken cross the road?' + apprentice = 'knock, knock!' + self.assertEqual(SkillLevel.apprentice, 'knock, knock!') + + def test_getattr_getitem(self): + class Period(Enum): + morning = 1 + noon = 2 + evening = 3 + night = 4 + self.assertIs(Period(2), Period.noon) + self.assertIs(getattr(Period, 'night'), Period.night) + self.assertIs(Period['morning'], Period.morning) + + def test_getattr_dunder(self): + Season = self.Season + self.assertTrue(getattr(Season, '__eq__')) + + def test_iteration_order(self): + class Season(Enum): + SUMMER = 2 + WINTER = 4 + AUTUMN = 3 + SPRING = 1 + self.assertEqual( + list(Season), + [Season.SUMMER, Season.WINTER, Season.AUTUMN, Season.SPRING], + ) + + def test_programatic_function_string(self): + SummerMonth = Enum('SummerMonth', 'june july august') + lst = list(SummerMonth) + self.assertEqual(len(lst), len(SummerMonth)) + self.assertEqual(len(SummerMonth), 3, SummerMonth) + self.assertEqual( + [SummerMonth.june, SummerMonth.july, SummerMonth.august], + lst, + ) + for i, month in enumerate('june july august'.split(), 1): + e = SummerMonth(i) + self.assertEqual(int(e.value), i) + self.assertNotEqual(e, i) + self.assertEqual(e.name, month) + self.assertIn(e, SummerMonth) + self.assertIs(type(e), SummerMonth) + + def test_programatic_function_string_list(self): + SummerMonth = Enum('SummerMonth', ['june', 'july', 'august']) + lst = list(SummerMonth) + self.assertEqual(len(lst), len(SummerMonth)) + self.assertEqual(len(SummerMonth), 3, SummerMonth) + self.assertEqual( + [SummerMonth.june, SummerMonth.july, SummerMonth.august], + lst, + ) + for i, month in enumerate('june july august'.split(), 1): + e = SummerMonth(i) + self.assertEqual(int(e.value), i) + self.assertNotEqual(e, i) + self.assertEqual(e.name, month) + self.assertIn(e, SummerMonth) + self.assertIs(type(e), SummerMonth) + + def test_programatic_function_iterable(self): + SummerMonth = Enum( + 'SummerMonth', + (('june', 1), ('july', 2), ('august', 3)) + ) + lst = list(SummerMonth) + self.assertEqual(len(lst), len(SummerMonth)) + self.assertEqual(len(SummerMonth), 3, SummerMonth) + self.assertEqual( + [SummerMonth.june, SummerMonth.july, SummerMonth.august], + lst, + ) + for i, month in enumerate('june july august'.split(), 1): + e = SummerMonth(i) + self.assertEqual(int(e.value), i) + self.assertNotEqual(e, i) + self.assertEqual(e.name, month) + self.assertIn(e, SummerMonth) + self.assertIs(type(e), SummerMonth) + + def test_programatic_function_from_dict(self): + SummerMonth = Enum( + 'SummerMonth', + OrderedDict((('june', 1), ('july', 2), ('august', 3))) + ) + lst = list(SummerMonth) + self.assertEqual(len(lst), len(SummerMonth)) + self.assertEqual(len(SummerMonth), 3, SummerMonth) + self.assertEqual( + [SummerMonth.june, SummerMonth.july, SummerMonth.august], + lst, + ) + for i, month in enumerate('june july august'.split(), 1): + e = SummerMonth(i) + self.assertEqual(int(e.value), i) + self.assertNotEqual(e, i) + self.assertEqual(e.name, month) + self.assertIn(e, SummerMonth) + self.assertIs(type(e), SummerMonth) + + def test_programatic_function_type(self): + SummerMonth = Enum('SummerMonth', 'june july august', type=int) + lst = list(SummerMonth) + self.assertEqual(len(lst), len(SummerMonth)) + self.assertEqual(len(SummerMonth), 3, SummerMonth) + self.assertEqual( + [SummerMonth.june, SummerMonth.july, SummerMonth.august], + lst, + ) + for i, month in enumerate('june july august'.split(), 1): + e = SummerMonth(i) + self.assertEqual(e, i) + self.assertEqual(e.name, month) + self.assertIn(e, SummerMonth) + self.assertIs(type(e), SummerMonth) + + def test_programatic_function_type_from_subclass(self): + SummerMonth = IntEnum('SummerMonth', 'june july august') + lst = list(SummerMonth) + self.assertEqual(len(lst), len(SummerMonth)) + self.assertEqual(len(SummerMonth), 3, SummerMonth) + self.assertEqual( + [SummerMonth.june, SummerMonth.july, SummerMonth.august], + lst, + ) + for i, month in enumerate('june july august'.split(), 1): + e = SummerMonth(i) + self.assertEqual(e, i) + self.assertEqual(e.name, month) + self.assertIn(e, SummerMonth) + self.assertIs(type(e), SummerMonth) + + def test_subclassing(self): + if isinstance(Name, Exception): + raise Name + self.assertEqual(Name.BDFL, 'Guido van Rossum') + self.assertTrue(Name.BDFL, Name('Guido van Rossum')) + self.assertIs(Name.BDFL, getattr(Name, 'BDFL')) + self.assertIs(Name.BDFL, loads(dumps(Name.BDFL))) + + def test_extending(self): + class Color(Enum): + red = 1 + green = 2 + blue = 3 + with self.assertRaises(TypeError): + class MoreColor(Color): + cyan = 4 + magenta = 5 + yellow = 6 + + def test_exclude_methods(self): + class whatever(Enum): + this = 'that' + these = 'those' + def really(self): + return 'no, not %s' % self.value + self.assertIsNot(type(whatever.really), whatever) + self.assertEqual(whatever.this.really(), 'no, not that') + + def test_overwrite_enums(self): + class Why(Enum): + question = 1 + answer = 2 + propisition = 3 + def question(self): + print(42) + self.assertIsNot(type(Why.question), Why) + self.assertNotIn(Why.question, Why._member_names) + self.assertNotIn(Why.question, Why) + + def test_wrong_inheritance_order(self): + with self.assertRaises(TypeError): + class Wrong(Enum, str): + NotHere = 'error before this point' + + def test_intenum_transitivity(self): + class number(IntEnum): + one = 1 + two = 2 + three = 3 + class numero(IntEnum): + uno = 1 + dos = 2 + tres = 3 + self.assertEqual(number.one, numero.uno) + self.assertEqual(number.two, numero.dos) + self.assertEqual(number.three, numero.tres) + + def test_wrong_enum_in_call(self): + class Monochrome(Enum): + black = 0 + white = 1 + class Gender(Enum): + male = 0 + female = 1 + self.assertRaises(ValueError, Monochrome, Gender.male) + + def test_wrong_enum_in_mixed_call(self): + class Monochrome(IntEnum): + black = 0 + white = 1 + class Gender(Enum): + male = 0 + female = 1 + self.assertRaises(ValueError, Monochrome, Gender.male) + + def test_mixed_enum_in_call_1(self): + class Monochrome(IntEnum): + black = 0 + white = 1 + class Gender(IntEnum): + male = 0 + female = 1 + self.assertIs(Monochrome(Gender.female), Monochrome.white) + + def test_mixed_enum_in_call_2(self): + class Monochrome(Enum): + black = 0 + white = 1 + class Gender(IntEnum): + male = 0 + female = 1 + self.assertIs(Monochrome(Gender.male), Monochrome.black) + + def test_flufl_enum(self): + class Fluflnum(Enum): + def __int__(self): + return int(self.value) + class MailManOptions(Fluflnum): + option1 = 1 + option2 = 2 + option3 = 3 + self.assertEqual(int(MailManOptions.option1), 1) + + def test_no_such_enum_member(self): + class Color(Enum): + red = 1 + green = 2 + blue = 3 + with self.assertRaises(ValueError): + Color(4) + with self.assertRaises(KeyError): + Color['chartreuse'] + + def test_new_repr(self): + class Color(Enum): + red = 1 + green = 2 + blue = 3 + def __repr__(self): + return "don't you just love shades of %s?" % self.name + self.assertEqual( + repr(Color.blue), + "don't you just love shades of blue?", + ) + + def test_inherited_repr(self): + class MyEnum(Enum): + def __repr__(self): + return "My name is %s." % self.name + class MyIntEnum(int, MyEnum): + this = 1 + that = 2 + theother = 3 + self.assertEqual(repr(MyIntEnum.that), "My name is that.") + + def test_multiple_mixin_mro(self): + class auto_enum(type(Enum)): + def __new__(metacls, cls, bases, classdict): + temp = type(classdict)() + names = set(classdict._member_names) + i = 0 + for k in classdict._member_names: + v = classdict[k] + if v is Ellipsis: + v = i + else: + i = v + i += 1 + temp[k] = v + for k, v in classdict.items(): + if k not in names: + temp[k] = v + return super(auto_enum, metacls).__new__( + metacls, cls, bases, temp) + + class AutoNumberedEnum(Enum, metaclass=auto_enum): + pass + + class AutoIntEnum(IntEnum, metaclass=auto_enum): + pass + + class TestAutoNumber(AutoNumberedEnum): + a = ... + b = 3 + c = ... + + class TestAutoInt(AutoIntEnum): + a = ... + b = 3 + c = ... + + def test_subclasses_with_getnewargs(self): + class NamedInt(int): + def __new__(cls, *args): + _args = args + name, *args = args + if len(args) == 0: + raise TypeError("name and value must be specified") + self = int.__new__(cls, *args) + self._intname = name + self._args = _args + return self + def __getnewargs__(self): + return self._args + @property + def __name__(self): + return self._intname + def __repr__(self): + # repr() is updated to include the name and type info + return "{}({!r}, {})".format(type(self).__name__, + self.__name__, + int.__repr__(self)) + def __str__(self): + # str() is unchanged, even if it relies on the repr() fallback + base = int + base_str = base.__str__ + if base_str.__objclass__ is object: + return base.__repr__(self) + return base_str(self) + # for simplicity, we only define one operator that + # propagates expressions + def __add__(self, other): + temp = int(self) + int( other) + if isinstance(self, NamedInt) and isinstance(other, NamedInt): + return NamedInt( + '({0} + {1})'.format(self.__name__, other.__name__), + temp ) + else: + return temp + + class NEI(NamedInt, Enum): + x = ('the-x', 1) + y = ('the-y', 2) + + self.assertIs(NEI.__new__, Enum.__new__) + self.assertEqual(repr(NEI.x + NEI.y), "NamedInt('(the-x + the-y)', 3)") + globals()['NamedInt'] = NamedInt + globals()['NEI'] = NEI + NI5 = NamedInt('test', 5) + self.assertEqual(NI5, 5) + self.assertEqual(loads(dumps(NI5)), 5) + self.assertEqual(NEI.y.value, 2) + self.assertIs(loads(dumps(NEI.y)), NEI.y) + + def test_subclasses_without_getnewargs(self): + class NamedInt(int): + def __new__(cls, *args): + _args = args + name, *args = args + if len(args) == 0: + raise TypeError("name and value must be specified") + self = int.__new__(cls, *args) + self._intname = name + self._args = _args + return self + @property + def __name__(self): + return self._intname + def __repr__(self): + # repr() is updated to include the name and type info + return "{}({!r}, {})".format(type(self).__name__, + self.__name__, + int.__repr__(self)) + def __str__(self): + # str() is unchanged, even if it relies on the repr() fallback + base = int + base_str = base.__str__ + if base_str.__objclass__ is object: + return base.__repr__(self) + return base_str(self) + # for simplicity, we only define one operator that + # propagates expressions + def __add__(self, other): + temp = int(self) + int( other) + if isinstance(self, NamedInt) and isinstance(other, NamedInt): + return NamedInt( + '({0} + {1})'.format(self.__name__, other.__name__), + temp ) + else: + return temp + + class NEI(NamedInt, Enum): + x = ('the-x', 1) + y = ('the-y', 2) + + self.assertIs(NEI.__new__, Enum.__new__) + self.assertEqual(repr(NEI.x + NEI.y), "NamedInt('(the-x + the-y)', 3)") + globals()['NamedInt'] = NamedInt + globals()['NEI'] = NEI + NI5 = NamedInt('test', 5) + self.assertEqual(NI5, 5) + self.assertEqual(NEI.y.value, 2) + with self.assertRaises(TypeError): + dumps(NEI.x) + with self.assertRaises(PicklingError): + dumps(NEI) + + def test_tuple_subclass(self): + class SomeTuple(tuple, Enum): + first = (1, 'for the money') + second = (2, 'for the show') + third = (3, 'for the music') + self.assertIs(type(SomeTuple.first), SomeTuple) + self.assertIsInstance(SomeTuple.second, tuple) + self.assertEqual(SomeTuple.third, (3, 'for the music')) + globals()['SomeTuple'] = SomeTuple + self.assertIs(loads(dumps(SomeTuple.first)), SomeTuple.first) + + def test_duplicate_values_give_unique_enum_items(self): + class AutoNumber(Enum): + first = () + second = () + third = () + def __new__(cls): + value = len(cls.__members__) + 1 + obj = object.__new__(cls) + obj._value = value + return obj + def __int__(self): + return int(self._value) + self.assertEqual( + list(AutoNumber), + [AutoNumber.first, AutoNumber.second, AutoNumber.third], + ) + self.assertEqual(int(AutoNumber.second), 2) + self.assertIs(AutoNumber(1), AutoNumber.first) + + def test_inherited_new_from_enhanced_enum(self): + class AutoNumber(Enum): + def __new__(cls): + value = len(cls.__members__) + 1 + obj = object.__new__(cls) + obj._value = value + return obj + def __int__(self): + return int(self._value) + class Color(AutoNumber): + red = () + green = () + blue = () + self.assertEqual(list(Color), [Color.red, Color.green, Color.blue]) + self.assertEqual(list(map(int, Color)), [1, 2, 3]) + + def test_inherited_new_from_mixed_enum(self): + class AutoNumber(IntEnum): + def __new__(cls): + value = len(cls.__members__) + 1 + obj = int.__new__(cls, value) + obj._value = value + return obj + class Color(AutoNumber): + red = () + green = () + blue = () + self.assertEqual(list(Color), [Color.red, Color.green, Color.blue]) + self.assertEqual(list(map(int, Color)), [1, 2, 3]) + + def test_ordered_mixin(self): + class OrderedEnum(Enum): + def __ge__(self, other): + if self.__class__ is other.__class__: + return self._value >= other._value + return NotImplemented + def __gt__(self, other): + if self.__class__ is other.__class__: + return self._value > other._value + return NotImplemented + def __le__(self, other): + if self.__class__ is other.__class__: + return self._value <= other._value + return NotImplemented + def __lt__(self, other): + if self.__class__ is other.__class__: + return self._value < other._value + return NotImplemented + class Grade(OrderedEnum): + A = 5 + B = 4 + C = 3 + D = 2 + F = 1 + self.assertGreater(Grade.A, Grade.B) + self.assertLessEqual(Grade.F, Grade.C) + self.assertLess(Grade.D, Grade.A) + self.assertGreaterEqual(Grade.B, Grade.B) + def test_extending2(self): + class Shade(Enum): + def shade(self): + print(self.name) + class Color(Shade): + red = 1 + green = 2 + blue = 3 + with self.assertRaises(TypeError): + class MoreColor(Color): + cyan = 4 + magenta = 5 + yellow = 6 + + def test_extending3(self): + class Shade(Enum): + def shade(self): + return self.name + class Color(Shade): + def hex(self): + return '%s hexlified!' % self.value + class MoreColor(Color): + cyan = 4 + magenta = 5 + yellow = 6 + self.assertEqual(MoreColor.magenta.hex(), '5 hexlified!') + + + def test_no_duplicates(self): + class UniqueEnum(Enum): + def __init__(self, *args): + cls = self.__class__ + if any(self.value == e.value for e in cls): + a = self.name + e = cls(self.value).name + raise ValueError( + "aliases not allowed in UniqueEnum: %r --> %r" + % (a, e) + ) + class Color(UniqueEnum): + red = 1 + green = 2 + blue = 3 + with self.assertRaises(ValueError): + class Color(UniqueEnum): + red = 1 + green = 2 + blue = 3 + grene = 2 + + def test_init(self): + class Planet(Enum): + MERCURY = (3.303e+23, 2.4397e6) + VENUS = (4.869e+24, 6.0518e6) + EARTH = (5.976e+24, 6.37814e6) + MARS = (6.421e+23, 3.3972e6) + JUPITER = (1.9e+27, 7.1492e7) + SATURN = (5.688e+26, 6.0268e7) + URANUS = (8.686e+25, 2.5559e7) + NEPTUNE = (1.024e+26, 2.4746e7) + def __init__(self, mass, radius): + self.mass = mass # in kilograms + self.radius = radius # in meters + @property + def surface_gravity(self): + # universal gravitational constant (m3 kg-1 s-2) + G = 6.67300E-11 + return G * self.mass / (self.radius * self.radius) + self.assertEqual(round(Planet.EARTH.surface_gravity, 2), 9.80) + self.assertEqual(Planet.EARTH.value, (5.976e+24, 6.37814e6)) + + +if __name__ == '__main__': + unittest.main() diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -381,6 +381,8 @@ - Implement PEP 443 "Single-dispatch generic functions". +- Implement PEP 435 "Adding an Enum type to the Python standard library". + Tests ----- -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 15 03:38:03 2013 From: python-checkins at python.org (victor.stinner) Date: Sat, 15 Jun 2013 03:38:03 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Revert_changeset_6661a8154?= =?utf-8?q?eb3=3A_Issue_=233329=3A_Add_new_APIs_to_customize_memory?= Message-ID: <3bXLt349w4z7Lkf@mail.python.org> http://hg.python.org/cpython/rev/b1455dd08000 changeset: 84131:b1455dd08000 parent: 84128:5619bc2d8207 user: Victor Stinner date: Sat Jun 15 03:37:01 2013 +0200 summary: Revert changeset 6661a8154eb3: Issue #3329: Add new APIs to customize memory allocators The new API require more discussion. files: Doc/c-api/memory.rst | 121 +------ Include/objimpl.h | 71 +- Include/pymem.h | 91 +--- Modules/_testcapimodule.c | 178 --------- Objects/object.c | 20 + Objects/obmalloc.c | 501 +++++++------------------ 6 files changed, 211 insertions(+), 771 deletions(-) diff --git a/Doc/c-api/memory.rst b/Doc/c-api/memory.rst --- a/Doc/c-api/memory.rst +++ b/Doc/c-api/memory.rst @@ -84,46 +84,6 @@ for the I/O buffer escapes completely the Python memory manager. -Raw Memory Interface -==================== - -The following function are wrappers to system allocators: :c:func:`malloc`, -:c:func:`realloc`, :c:func:`free`. These functions are thread-safe, the -:term:`GIL ` does not need to be held to use these -functions. - -The behaviour of requesting zero bytes is not defined: return *NULL* or a -distinct non-*NULL* pointer depending on the platform. Use -:c:func:`PyMem_Malloc` and :c:func:`PyMem_Realloc` to have a well defined -behaviour. - -.. versionadded:: 3.4 - -.. c:function:: void* PyMem_RawMalloc(size_t n) - - Allocates *n* bytes and returns a pointer of type :c:type:`void\*` to the - allocated memory, or *NULL* if the request fails. The memory - will not have been initialized in any way. - - -.. c:function:: void* PyMem_RawRealloc(void *p, size_t n) - - Resizes the memory block pointed to by *p* to *n* bytes. The contents will - be unchanged to the minimum of the old and the new sizes. If *p* is *NULL*, - the call is equivalent to ``PyMem_RawMalloc(n)``. Unless *p* is *NULL*, it - must have been returned by a previous call to :c:func:`PyMem_RawMalloc` or - :c:func:`PyMem_RawRealloc`. If the request fails, :c:func:`PyMem_RawRealloc` - returns *NULL* and *p* remains a valid pointer to the previous memory area. - - -.. c:function:: void PyMem_RawFree(void *p) - - Frees the memory block pointed to by *p*, which must have been returned by a - previous call to :c:func:`PyMem_RawMalloc` or :c:func:`PyMem_RawRealloc`. - Otherwise, or if ``PyMem_Free(p)`` has been called before, undefined - behavior occurs. If *p* is *NULL*, no operation is performed. - - .. _memoryinterface: Memory Interface @@ -131,12 +91,8 @@ The following function sets, modeled after the ANSI C standard, but specifying behavior when requesting zero bytes, are available for allocating and releasing -memory from the Python heap. +memory from the Python heap: -.. warning:: - - The :term:`GIL ` must be held when using these - functions. .. c:function:: void* PyMem_Malloc(size_t n) @@ -199,81 +155,6 @@ :c:func:`PyMem_NEW`, :c:func:`PyMem_RESIZE`, :c:func:`PyMem_DEL`. -Customize Memory Allocators -=========================== - -.. versionadded:: 3.4 - -.. c:type:: PyMemAllocators - - Structure used to describe memory allocator. This structure has - four fields: - - +----------------------------------------------------------+-----------------+ - | Field | Meaning | - +==========================================================+=================+ - | ``void *ctx`` | user data | - +----------------------------------------------------------+-----------------+ - | ``void* malloc(void *ctx, size_t size)`` | allocate memory | - +----------------------------------------------------------+-----------------+ - | ``void* realloc(void *ctx, void *ptr, size_t new_size)`` | allocate memory | - | | or resize a | - | | memory block | - +----------------------------------------------------------+-----------------+ - | ``void free(void *ctx, void *ptr)`` | release memory | - +----------------------------------------------------------+-----------------+ - -.. c:function:: void PyMem_GetRawAllocators(PyMemAllocators *allocators) - - Get internal functions of :c:func:`PyMem_RawMalloc`, :c:func:`PyMem_RawRealloc` - and :c:func:`PyMem_RawFree`. - -.. c:function:: void PyMem_SetRawAllocators(PyMemAllocators *allocators) - - Set internal functions of :c:func:`PyMem_RawMalloc`, :c:func:`PyMem_RawRealloc` - and :c:func:`PyMem_RawFree`. - - :c:func:`PyMem_SetupDebugHooks` should be called to reinstall debug hooks if - new functions do no call original functions anymore. - -.. c:function:: void PyMem_GetAllocators(PyMemAllocators *allocators) - - Get internal functions of :c:func:`PyMem_Malloc`, :c:func:`PyMem_Realloc` - and :c:func:`PyMem_Free`. - -.. c:function:: void PyMem_SetAllocators(PyMemAllocators *allocators) - - Set internal functions of :c:func:`PyMem_Malloc`, :c:func:`PyMem_Realloc` - and :c:func:`PyMem_Free`. - - ``malloc(ctx, 0)`` and ``realloc(ctx, ptr, 0)`` must not return *NULL*: it - would be treated as an error. - - :c:func:`PyMem_SetupDebugHooks` should be called to reinstall debug hooks if - new functions do no call original functions anymore. - -.. c:function:: void PyMem_SetupDebugHooks(void) - - Setup hooks to detect bugs in the following Python memory allocator - functions: - - - :c:func:`PyMem_RawMalloc`, :c:func:`PyMem_RawRealloc`, - :c:func:`PyMem_RawFree` - - :c:func:`PyMem_Malloc`, :c:func:`PyMem_Realloc`, :c:func:`PyMem_Free` - - :c:func:`PyObject_Malloc`, :c:func:`PyObject_Realloc`, - :c:func:`PyObject_Free` - - Newly allocated memory is filled with the byte ``0xCB``, freed memory is - filled with the byte ``0xDB``. Additionnal checks: - - - detect API violations, ex: :c:func:`PyObject_Free` called on a buffer - allocated by :c:func:`PyMem_Malloc` - - detect write before the start of the buffer (buffer underflow) - - detect write after the end of the buffer (buffer overflow) - - The function does nothing if Python is not compiled is debug mode. - - .. _memoryexamples: Examples diff --git a/Include/objimpl.h b/Include/objimpl.h --- a/Include/objimpl.h +++ b/Include/objimpl.h @@ -94,9 +94,9 @@ the object gets initialized via PyObject_{Init, InitVar} after obtaining the raw memory. */ -PyAPI_FUNC(void *) PyObject_Malloc(size_t size); -PyAPI_FUNC(void *) PyObject_Realloc(void *ptr, size_t new_size); -PyAPI_FUNC(void) PyObject_Free(void *ptr); +PyAPI_FUNC(void *) PyObject_Malloc(size_t); +PyAPI_FUNC(void *) PyObject_Realloc(void *, size_t); +PyAPI_FUNC(void) PyObject_Free(void *); /* This function returns the number of allocated memory blocks, regardless of size */ PyAPI_FUNC(Py_ssize_t) _Py_GetAllocatedBlocks(void); @@ -106,46 +106,41 @@ #ifndef Py_LIMITED_API PyAPI_FUNC(void) _PyObject_DebugMallocStats(FILE *out); #endif /* #ifndef Py_LIMITED_API */ -#endif +#ifdef PYMALLOC_DEBUG /* WITH_PYMALLOC && PYMALLOC_DEBUG */ +PyAPI_FUNC(void *) _PyObject_DebugMalloc(size_t nbytes); +PyAPI_FUNC(void *) _PyObject_DebugRealloc(void *p, size_t nbytes); +PyAPI_FUNC(void) _PyObject_DebugFree(void *p); +PyAPI_FUNC(void) _PyObject_DebugDumpAddress(const void *p); +PyAPI_FUNC(void) _PyObject_DebugCheckAddress(const void *p); +PyAPI_FUNC(void *) _PyObject_DebugMallocApi(char api, size_t nbytes); +PyAPI_FUNC(void *) _PyObject_DebugReallocApi(char api, void *p, size_t nbytes); +PyAPI_FUNC(void) _PyObject_DebugFreeApi(char api, void *p); +PyAPI_FUNC(void) _PyObject_DebugCheckAddressApi(char api, const void *p); +PyAPI_FUNC(void *) _PyMem_DebugMalloc(size_t nbytes); +PyAPI_FUNC(void *) _PyMem_DebugRealloc(void *p, size_t nbytes); +PyAPI_FUNC(void) _PyMem_DebugFree(void *p); +#define PyObject_MALLOC _PyObject_DebugMalloc +#define PyObject_Malloc _PyObject_DebugMalloc +#define PyObject_REALLOC _PyObject_DebugRealloc +#define PyObject_Realloc _PyObject_DebugRealloc +#define PyObject_FREE _PyObject_DebugFree +#define PyObject_Free _PyObject_DebugFree -/* Macros */ +#else /* WITH_PYMALLOC && ! PYMALLOC_DEBUG */ #define PyObject_MALLOC PyObject_Malloc #define PyObject_REALLOC PyObject_Realloc #define PyObject_FREE PyObject_Free +#endif + +#else /* ! WITH_PYMALLOC */ +#define PyObject_MALLOC PyMem_MALLOC +#define PyObject_REALLOC PyMem_REALLOC +#define PyObject_FREE PyMem_FREE + +#endif /* WITH_PYMALLOC */ + #define PyObject_Del PyObject_Free -#define PyObject_DEL PyObject_Free - -/* Get internal functions of PyObject_Malloc(), PyObject_Realloc() and - PyObject_Free(). *ctx_p is an arbitrary user value. */ -PyAPI_FUNC(void) PyObject_GetAllocators(PyMemAllocators *allocators); - -/* Set internal functions of PyObject_Malloc(), PyObject_Realloc() and PyObject_Free(). - ctx is an arbitrary user value. - - malloc(ctx, 0) and realloc(ctx, ptr, 0) must not return NULL: it would be - treated as an error. - - PyMem_SetupDebugHooks() should be called to reinstall debug hooks if new - functions do no call original functions anymore. */ -PyAPI_FUNC(void) PyObject_SetAllocators(PyMemAllocators *allocators); - -/* Get internal functions allocating and deallocating arenas for - PyObject_Malloc(), PyObject_Realloc() and PyObject_Free(). - *ctx_p is an arbitrary user value. */ -PyAPI_FUNC(void) _PyObject_GetArenaAllocators( - void **ctx_p, - void* (**malloc_p) (void *ctx, size_t size), - void (**free_p) (void *ctx, void *ptr, size_t size) - ); - -/* Get internal functions allocating and deallocating arenas for - PyObject_Malloc(), PyObject_Realloc() and PyObject_Free(). - ctx is an arbitrary user value. */ -PyAPI_FUNC(void) _PyObject_SetArenaAllocators( - void *ctx, - void* (*malloc) (void *ctx, size_t size), - void (*free) (void *ctx, void *ptr, size_t size) - ); +#define PyObject_DEL PyObject_FREE /* * Generic object allocator interface diff --git a/Include/pymem.h b/Include/pymem.h --- a/Include/pymem.h +++ b/Include/pymem.h @@ -11,40 +11,6 @@ extern "C" { #endif -typedef struct { - /* user context passed as the first argument to the 3 functions */ - void *ctx; - - /* allocate memory */ - void* (*malloc) (void *ctx, size_t size); - - /* allocate memory or resize a memory buffer */ - void* (*realloc) (void *ctx, void *ptr, size_t new_size); - - /* release memory */ - void (*free) (void *ctx, void *ptr); -} PyMemAllocators; - -/* Raw memory allocators, system functions: malloc(), realloc(), free(). - - These functions are thread-safe, the GIL does not need to be held. */ - -/* Get internal functions of PyMem_RawMalloc(), PyMem_RawRealloc() and - PyMem_RawFree(). *ctx_p is an arbitrary user value. */ -PyAPI_FUNC(void) PyMem_GetRawAllocators(PyMemAllocators *allocators); - -/* Set internal functions of PyMem_RawMalloc(), PyMem_RawRealloc() and - PyMem_RawFree(). ctx is an arbitrary user value. - - PyMem_SetupDebugHooks() should be called to reinstall debug hooks if new - functions do no call original functions anymore. */ -PyAPI_FUNC(void) PyMem_SetRawAllocators(PyMemAllocators *allocators); - -PyAPI_FUNC(void *) PyMem_RawMalloc(size_t size); -PyAPI_FUNC(void *) PyMem_RawRealloc(void *ptr, size_t new_size); -PyAPI_FUNC(void) PyMem_RawFree(void *ptr); - - /* BEWARE: Each interface exports both functions and macros. Extension modules should @@ -83,11 +49,21 @@ performed on failure (no exception is set, no warning is printed, etc). */ -PyAPI_FUNC(void *) PyMem_Malloc(size_t size); -PyAPI_FUNC(void *) PyMem_Realloc(void *ptr, size_t new_size); -PyAPI_FUNC(void) PyMem_Free(void *ptr); +PyAPI_FUNC(void *) PyMem_Malloc(size_t); +PyAPI_FUNC(void *) PyMem_Realloc(void *, size_t); +PyAPI_FUNC(void) PyMem_Free(void *); + +/* Starting from Python 1.6, the wrappers Py_{Malloc,Realloc,Free} are + no longer supported. They used to call PyErr_NoMemory() on failure. */ /* Macros. */ +#ifdef PYMALLOC_DEBUG +/* Redirect all memory operations to Python's debugging allocator. */ +#define PyMem_MALLOC _PyMem_DebugMalloc +#define PyMem_REALLOC _PyMem_DebugRealloc +#define PyMem_FREE _PyMem_DebugFree + +#else /* ! PYMALLOC_DEBUG */ /* PyMem_MALLOC(0) means malloc(1). Some systems would return NULL for malloc(0), which would be treated as an error. Some platforms @@ -95,9 +71,13 @@ pymalloc. To solve these problems, allocate an extra byte. */ /* Returns NULL to indicate error if a negative size or size larger than Py_ssize_t can represent is supplied. Helps prevents security holes. */ -#define PyMem_MALLOC(n) PyMem_Malloc(n) -#define PyMem_REALLOC(p, n) PyMem_Realloc(p, n) -#define PyMem_FREE(p) PyMem_Free(p) +#define PyMem_MALLOC(n) ((size_t)(n) > (size_t)PY_SSIZE_T_MAX ? NULL \ + : malloc((n) ? (n) : 1)) +#define PyMem_REALLOC(p, n) ((size_t)(n) > (size_t)PY_SSIZE_T_MAX ? NULL \ + : realloc((p), (n) ? (n) : 1)) +#define PyMem_FREE free + +#endif /* PYMALLOC_DEBUG */ /* * Type-oriented memory interface @@ -135,37 +115,6 @@ #define PyMem_Del PyMem_Free #define PyMem_DEL PyMem_FREE -/* Get internal functions of PyMem_Malloc(), PyMem_Realloc() - and PyMem_Free() */ -PyAPI_FUNC(void) PyMem_GetAllocators(PyMemAllocators *allocators); - -/* Set internal functions of PyMem_Malloc(), PyMem_Realloc() and PyMem_Free(). - - malloc(ctx, 0) and realloc(ctx, ptr, 0) must not return NULL: it would be - treated as an error. - - PyMem_SetupDebugHooks() should be called to reinstall debug hooks if new - functions do no call original functions anymore. */ -PyAPI_FUNC(void) PyMem_SetAllocators(PyMemAllocators *allocators); - -/* Setup hooks to detect bugs in the following Python memory allocator - functions: - - - PyMem_RawMalloc(), PyMem_RawRealloc(), PyMem_RawFree() - - PyMem_Malloc(), PyMem_Realloc(), PyMem_Free() - - PyObject_Malloc(), PyObject_Realloc() and PyObject_Free() - - Newly allocated memory is filled with the byte 0xCB, freed memory is filled - with the byte 0xDB. Additionnal checks: - - - detect API violations, ex: PyObject_Free() called on a buffer allocated - by PyMem_Malloc() - - detect write before the start of the buffer (buffer underflow) - - detect write after the end of the buffer (buffer overflow) - - The function does nothing if Python is not compiled is debug mode. */ -PyAPI_FUNC(void) PyMem_SetupDebugHooks(void); - #ifdef __cplusplus } #endif diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -2511,176 +2511,6 @@ Py_RETURN_NONE; } -static PyObject * -test_pymem_alloc0(PyObject *self) -{ - void *ptr; - - ptr = PyMem_Malloc(0); - if (ptr == NULL) { - PyErr_SetString(PyExc_RuntimeError, "PyMem_Malloc(0) returns NULL"); - return NULL; - } - PyMem_Free(ptr); - - ptr = PyObject_Malloc(0); - if (ptr == NULL) { - PyErr_SetString(PyExc_RuntimeError, "PyObject_Malloc(0) returns NULL"); - return NULL; - } - PyObject_Free(ptr); - - Py_RETURN_NONE; -} - -typedef struct { - PyMemAllocators alloc; - - size_t malloc_size; - void *realloc_ptr; - size_t realloc_new_size; - void *free_ptr; -} alloc_hook_t; - -static void* hook_malloc (void* ctx, size_t size) -{ - alloc_hook_t *hook = (alloc_hook_t *)ctx; - hook->malloc_size = size; - return hook->alloc.malloc(hook->alloc.ctx, size); -} - -static void* hook_realloc (void* ctx, void* ptr, size_t new_size) -{ - alloc_hook_t *hook = (alloc_hook_t *)ctx; - hook->realloc_ptr = ptr; - hook->realloc_new_size = new_size; - return hook->alloc.realloc(hook->alloc.ctx, ptr, new_size); -} - -static void hook_free (void *ctx, void *ptr) -{ - alloc_hook_t *hook = (alloc_hook_t *)ctx; - printf("HOOK\n"); - hook->free_ptr = ptr; - hook->alloc.free(hook->alloc.ctx, ptr); -} - -static PyObject * -test_setallocators(char api) -{ - PyObject *res = NULL; - const char *error_msg; - alloc_hook_t hook; - PyMemAllocators alloc; - size_t size, size2; - void *ptr, *ptr2; - - hook.malloc_size = 0; - hook.realloc_ptr = NULL; - hook.realloc_new_size = 0; - hook.free_ptr = NULL; - - alloc.ctx = &hook; - alloc.malloc = &hook_malloc; - alloc.realloc = &hook_realloc; - alloc.free = &hook_free; - if (api == 'o') { - PyObject_GetAllocators(&hook.alloc); - PyObject_SetAllocators(&alloc); - } - else if (api == 'r') { - PyMem_GetRawAllocators(&hook.alloc); - PyMem_SetRawAllocators(&alloc); - } - else { - PyMem_GetAllocators(&hook.alloc); - PyMem_SetAllocators(&alloc); - } - - size = 42; - if (api == 'o') - ptr = PyObject_Malloc(size); - else if (api == 'r') - ptr = PyMem_RawMalloc(size); - else - ptr = PyMem_Malloc(size); - if (ptr == NULL) { - error_msg = "malloc failed"; - goto fail; - } - - if (hook.malloc_size != size) { - error_msg = "malloc invalid size"; - goto fail; - } - - size2 = 200; - if (api == 'o') - ptr2 = PyObject_Realloc(ptr, size2); - else if (api == 'r') - ptr2 = PyMem_RawRealloc(ptr, size2); - else - ptr2 = PyMem_Realloc(ptr, size2); - if (ptr2 == NULL) { - error_msg = "realloc failed"; - goto fail; - } - - if (hook.realloc_ptr != ptr - || hook.realloc_new_size != size2) { - error_msg = "realloc invalid parameters"; - goto fail; - } - - if (api == 'o') - PyObject_Free(ptr2); - else if (api == 'r') - PyMem_RawFree(ptr2); - else { - printf("PyMem_Free\n"); - PyMem_Free(ptr2); - } - - if (hook.free_ptr != ptr2) { - error_msg = "free invalid pointer"; - goto fail; - } - - Py_INCREF(Py_None); - res = Py_None; - goto finally; - -fail: - PyErr_SetString(PyExc_RuntimeError, error_msg); - -finally: - if (api == 'o') - PyObject_SetAllocators(&hook.alloc); - else if (api == 'r') - PyMem_SetRawAllocators(&hook.alloc); - else - PyMem_SetAllocators(&hook.alloc); - return res; -} - -static PyObject * -test_pymem_setrawallocators(PyObject *self) -{ - return test_setallocators('r'); -} - -static PyObject * -test_pymem_setallocators(PyObject *self) -{ - return test_setallocators('m'); -} - -static PyObject * -test_pyobject_setallocators(PyObject *self) -{ - return test_setallocators('o'); -} - static PyMethodDef TestMethods[] = { {"raise_exception", raise_exception, METH_VARARGS}, {"raise_memoryerror", (PyCFunction)raise_memoryerror, METH_NOARGS}, @@ -2781,14 +2611,6 @@ {"pytime_object_to_time_t", test_pytime_object_to_time_t, METH_VARARGS}, {"pytime_object_to_timeval", test_pytime_object_to_timeval, METH_VARARGS}, {"pytime_object_to_timespec", test_pytime_object_to_timespec, METH_VARARGS}, - {"test_pymem", - (PyCFunction)test_pymem_alloc0, METH_NOARGS}, - {"test_pymem_alloc0", - (PyCFunction)test_pymem_setrawallocators, METH_NOARGS}, - {"test_pymem_setallocators", - (PyCFunction)test_pymem_setallocators, METH_NOARGS}, - {"test_pyobject_setallocators", - (PyCFunction)test_pyobject_setallocators, METH_NOARGS}, {NULL, NULL} /* sentinel */ }; diff --git a/Objects/object.c b/Objects/object.c --- a/Objects/object.c +++ b/Objects/object.c @@ -1859,6 +1859,26 @@ Py_ssize_t (*_Py_abstract_hack)(PyObject *) = PyObject_Size; +/* Python's malloc wrappers (see pymem.h) */ + +void * +PyMem_Malloc(size_t nbytes) +{ + return PyMem_MALLOC(nbytes); +} + +void * +PyMem_Realloc(void *p, size_t nbytes) +{ + return PyMem_REALLOC(p, nbytes); +} + +void +PyMem_Free(void *p) +{ + PyMem_FREE(p); +} + void _PyObject_DebugTypeStats(FILE *out) { diff --git a/Objects/obmalloc.c b/Objects/obmalloc.c --- a/Objects/obmalloc.c +++ b/Objects/obmalloc.c @@ -1,327 +1,5 @@ #include "Python.h" -/* Python's malloc wrappers (see pymem.h) */ - -/* Forward declaration */ - -#ifdef PYMALLOC_DEBUG /* WITH_PYMALLOC && PYMALLOC_DEBUG */ -static void* _PyMem_DebugMalloc(void *ctx, size_t size); -static void _PyMem_DebugFree(void *ctx, void *p); -static void* _PyMem_DebugRealloc(void *ctx, void *ptr, size_t size); - -static void _PyObject_DebugDumpAddress(const void *p); -static void _PyMem_DebugCheckAddress(char api_id, const void *p); -#endif - -#ifdef WITH_PYMALLOC -static void* _PyObject_Malloc(void *ctx, size_t size); -static void _PyObject_Free(void *ctx, void *p); -static void* _PyObject_Realloc(void *ctx, void *ptr, size_t size); -#endif - - -static void * -_PyMem_RawMalloc(void *ctx, size_t size) -{ - return malloc(size); -} - -static void * -_PyMem_RawRealloc(void *ctx, void *ptr, size_t size) -{ - return realloc(ptr, size); -} - -static void -_PyMem_RawFree(void *ctx, void *ptr) -{ - return free(ptr); -} - -static void * -_PyMem_Malloc(void *ctx, size_t size) -{ - /* PyMem_Malloc(0) means malloc(1). Some systems would return NULL - for malloc(0), which would be treated as an error. Some platforms would - return a pointer with no memory behind it, which would break pymalloc. - To solve these problems, allocate an extra byte. */ - if (size == 0) - size = 1; - return malloc(size); -} - -static void * -_PyMem_Realloc(void *ctx, void *ptr, size_t size) -{ - if (size == 0) - size = 1; - return realloc(ptr, size); -} - -#ifdef ARENAS_USE_MMAP -static void * -_PyObject_ArenaMmap(void *ctx, size_t size) -{ - void *ptr; - ptr = mmap(NULL, size, PROT_READ|PROT_WRITE, - MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); - if (ptr == MAP_FAILED) - return NULL; - assert(ptr != NULL); - return ptr; -} - -static void -_PyObject_ArenaMunmap(void *ctx, void *ptr, size_t size) -{ - return munmap(ptr, size); -} -#else -static void * -_PyObject_ArenaMalloc(void *ctx, size_t size) -{ - return malloc(size); -} - -static void -_PyObject_ArenaFree(void *ctx, void *ptr, size_t size) -{ - free(ptr); -} -#endif - -#define PYRAW_FUNCS _PyMem_RawMalloc, _PyMem_RawRealloc, _PyMem_RawFree -#define PYMEM_FUNCS _PyMem_Malloc, _PyMem_Realloc, _PyMem_RawFree -#ifdef WITH_PYMALLOC -#define PYOBJECT_FUNCS _PyObject_Malloc, _PyObject_Realloc, _PyObject_Free -#else -#define PYOBJECT_FUNCS PYMEM_FUNCS -#endif - -#ifdef PYMALLOC_DEBUG -typedef struct { - /* We tag each block with an API ID in order to tag API violations */ - char api_id; - PyMemAllocators alloc; -} debug_alloc_api_t; -static struct { - debug_alloc_api_t raw; - debug_alloc_api_t mem; - debug_alloc_api_t obj; -} _PyMem_Debug = { - {'r', {NULL, PYRAW_FUNCS}}, - {'m', {NULL, PYMEM_FUNCS}}, - {'o', {NULL, PYOBJECT_FUNCS}} - }; - -#define PYDEBUG_FUNCS _PyMem_DebugMalloc, _PyMem_DebugRealloc, _PyMem_DebugFree -#endif - -static PyMemAllocators _PyMem_Raw = { -#ifdef PYMALLOC_DEBUG - &_PyMem_Debug.raw, PYDEBUG_FUNCS -#else - NULL, PYMEM_FUNCS -#endif - }; - -static PyMemAllocators _PyMem = { -#ifdef PYMALLOC_DEBUG - &_PyMem_Debug.mem, PYDEBUG_FUNCS -#else - NULL, PYMEM_FUNCS -#endif - }; - -static PyMemAllocators _PyObject = { -#ifdef PYMALLOC_DEBUG - &_PyMem_Debug.obj, PYDEBUG_FUNCS -#else - NULL, PYOBJECT_FUNCS -#endif - }; - -#undef PYRAW_FUNCS -#undef PYMEM_FUNCS -#undef PYOBJECT_FUNCS -#undef PYDEBUG_FUNCS - -static struct { - void *ctx; - void* (*malloc) (void*, size_t); - void (*free) (void*, void*, size_t); -} _PyObject_Arena = {NULL, -#ifdef ARENAS_USE_MMAP - _PyObject_ArenaMmap, _PyObject_ArenaMunmap -#else - _PyObject_ArenaMalloc, _PyObject_ArenaFree -#endif - }; - -void -PyMem_SetupDebugHooks(void) -{ -#ifdef PYMALLOC_DEBUG - PyMemAllocators alloc; - - alloc.malloc = _PyMem_DebugMalloc; - alloc.realloc = _PyMem_DebugRealloc; - alloc.free = _PyMem_DebugFree; - - if (_PyMem_Raw.malloc != _PyMem_DebugMalloc) { - alloc.ctx = &_PyMem_Debug.raw; - PyMem_GetAllocators(&_PyMem_Debug.raw.alloc); - PyMem_SetAllocators(&alloc); - } - - if (_PyMem.malloc != _PyMem_DebugMalloc) { - alloc.ctx = &_PyMem_Debug.mem; - PyMem_GetAllocators(&_PyMem_Debug.mem.alloc); - PyMem_SetAllocators(&alloc); - } - - if (_PyObject.malloc != _PyMem_DebugMalloc) { - alloc.ctx = &_PyMem_Debug.obj; - PyObject_GetAllocators(&_PyMem_Debug.obj.alloc); - PyObject_SetAllocators(&alloc); - } -#endif -} - -void -PyMem_GetRawAllocators(PyMemAllocators *allocators) -{ - *allocators = _PyMem_Raw; -} - -void -PyMem_SetRawAllocators(PyMemAllocators *allocators) -{ - _PyMem_Raw = *allocators; -} - -void -PyMem_GetAllocators(PyMemAllocators *allocators) -{ - *allocators = _PyMem; -} - -void -PyMem_SetAllocators(PyMemAllocators *allocators) -{ - _PyMem = *allocators; -} - -void -PyObject_GetAllocators(PyMemAllocators *allocators) -{ - *allocators = _PyObject; -} - -void -PyObject_SetAllocators(PyMemAllocators *allocators) -{ - _PyObject = *allocators; -} - -void -_PyObject_GetArenaAllocators(void **ctx_p, - void* (**malloc_p) (void *ctx, size_t size), - void (**free_p) (void *ctx, void *ptr, size_t size)) -{ - *malloc_p = _PyObject_Arena.malloc; - *free_p = _PyObject_Arena.free; - *ctx_p = _PyObject_Arena.ctx; -} - -void -_PyObject_SetArenaAllocators(void *ctx, - void* (*malloc) (void *ctx, size_t size), - void (*free) (void *ctx, void *ptr, size_t size)) -{ - _PyObject_Arena.malloc = malloc; - _PyObject_Arena.free = free; - _PyObject_Arena.ctx = ctx; -} - -void * -PyMem_RawMalloc(size_t size) -{ - return _PyMem_Raw.malloc(_PyMem_Raw.ctx, size); -} - -void* -PyMem_RawRealloc(void *ptr, size_t new_size) -{ - return _PyMem_Raw.realloc(_PyMem_Raw.ctx, ptr, new_size); -} - -void PyMem_RawFree(void *ptr) -{ - _PyMem_Raw.free(_PyMem_Raw.ctx, ptr); -} - -void * -PyMem_Malloc(size_t size) -{ - /* - * Limit ourselves to PY_SSIZE_T_MAX bytes to prevent security holes. - * Most python internals blindly use a signed Py_ssize_t to track - * things without checking for overflows or negatives. - * As size_t is unsigned, checking for size < 0 is not required. - */ - if (size > (size_t)PY_SSIZE_T_MAX) - return NULL; - - return _PyMem.malloc(_PyMem.ctx, size); -} - -void * -PyMem_Realloc(void *ptr, size_t new_size) -{ - if (new_size > (size_t)PY_SSIZE_T_MAX) - return NULL; - - return _PyMem.realloc(_PyMem.ctx, ptr, new_size); -} - -void -PyMem_Free(void *ptr) -{ - _PyMem.free(_PyMem.ctx, ptr); -} - -void * -PyObject_Malloc(size_t size) -{ - /* - * Limit ourselves to PY_SSIZE_T_MAX bytes to prevent security holes. - * Most python internals blindly use a signed Py_ssize_t to track - * things without checking for overflows or negatives. - * As size_t is unsigned, checking for size < 0 is not required. - */ - if (size > (size_t)PY_SSIZE_T_MAX) - return NULL; - - return _PyObject.malloc(_PyObject.ctx, size); -} - -void * -PyObject_Realloc(void *ptr, size_t new_size) -{ - if (new_size > (size_t)PY_SSIZE_T_MAX) - return NULL; - - return _PyObject.realloc(_PyObject.ctx, ptr, new_size); -} - -void -PyObject_Free(void *ptr) -{ - _PyObject.free(_PyObject.ctx, ptr); -} - - #ifdef WITH_PYMALLOC #ifdef HAVE_MMAP @@ -867,6 +545,7 @@ struct arena_object* arenaobj; uint excess; /* number of bytes above pool alignment */ void *address; + int err; #ifdef PYMALLOC_DEBUG if (Py_GETENV("PYTHONMALLOCSTATS")) @@ -888,12 +567,11 @@ return NULL; /* overflow */ #endif nbytes = numarenas * sizeof(*arenas); - arenaobj = (struct arena_object *)PyMem_Realloc(arenas, nbytes); + arenaobj = (struct arena_object *)realloc(arenas, nbytes); if (arenaobj == NULL) return NULL; arenas = arenaobj; - /* We might need to fix pointers that were copied. However, * new_arena only gets called when all the pages in the * previous arenas are full. Thus, there are *no* pointers @@ -920,8 +598,15 @@ arenaobj = unused_arena_objects; unused_arena_objects = arenaobj->nextarena; assert(arenaobj->address == 0); - address = _PyObject_Arena.malloc(_PyObject_Arena.ctx, ARENA_SIZE); - if (address == NULL) { +#ifdef ARENAS_USE_MMAP + address = mmap(NULL, ARENA_SIZE, PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + err = (address == MAP_FAILED); +#else + address = malloc(ARENA_SIZE); + err = (address == 0); +#endif + if (err) { /* The allocation failed: return NULL after putting the * arenaobj back. */ @@ -1084,8 +769,9 @@ * Unless the optimizer reorders everything, being too smart... */ -static void * -_PyObject_Malloc(void *ctx, size_t nbytes) +#undef PyObject_Malloc +void * +PyObject_Malloc(size_t nbytes) { block *bp; poolp pool; @@ -1102,6 +788,17 @@ #endif /* + * Limit ourselves to PY_SSIZE_T_MAX bytes to prevent security holes. + * Most python internals blindly use a signed Py_ssize_t to track + * things without checking for overflows or negatives. + * As size_t is unsigned, checking for nbytes < 0 is not required. + */ + if (nbytes > PY_SSIZE_T_MAX) { + _Py_AllocatedBlocks--; + return NULL; + } + + /* * This implicitly redirects malloc(0). */ if ((nbytes - 1) < SMALL_REQUEST_THRESHOLD) { @@ -1273,8 +970,10 @@ * last chance to serve the request) or when the max memory limit * has been reached. */ + if (nbytes == 0) + nbytes = 1; { - void *result = PyMem_Malloc(nbytes); + void *result = malloc(nbytes); if (!result) _Py_AllocatedBlocks--; return result; @@ -1283,8 +982,9 @@ /* free */ -static void -_PyObject_Free(void *ctx, void *p) +#undef PyObject_Free +void +PyObject_Free(void *p) { poolp pool; block *lastfree; @@ -1393,8 +1093,11 @@ unused_arena_objects = ao; /* Free the entire arena. */ - _PyObject_Arena.free(_PyObject_Arena.ctx, - (void *)ao->address, ARENA_SIZE); +#ifdef ARENAS_USE_MMAP + munmap((void *)ao->address, ARENA_SIZE); +#else + free((void *)ao->address); +#endif ao->address = 0; /* mark unassociated */ --narenas_currently_allocated; @@ -1503,7 +1206,7 @@ redirect: #endif /* We didn't allocate this address. */ - PyMem_Free(p); + free(p); } /* realloc. If p is NULL, this acts like malloc(nbytes). Else if nbytes==0, @@ -1511,8 +1214,9 @@ * return a non-NULL result. */ -static void * -_PyObject_Realloc(void *ctx, void *p, size_t nbytes) +#undef PyObject_Realloc +void * +PyObject_Realloc(void *p, size_t nbytes) { void *bp; poolp pool; @@ -1522,7 +1226,16 @@ #endif if (p == NULL) - return _PyObject_Malloc(ctx, nbytes); + return PyObject_Malloc(nbytes); + + /* + * Limit ourselves to PY_SSIZE_T_MAX bytes to prevent security holes. + * Most python internals blindly use a signed Py_ssize_t to track + * things without checking for overflows or negatives. + * As size_t is unsigned, checking for nbytes < 0 is not required. + */ + if (nbytes > PY_SSIZE_T_MAX) + return NULL; #ifdef WITH_VALGRIND /* Treat running_on_valgrind == -1 the same as 0 */ @@ -1550,10 +1263,10 @@ } size = nbytes; } - bp = _PyObject_Malloc(ctx, nbytes); + bp = PyObject_Malloc(nbytes); if (bp != NULL) { memcpy(bp, p, size); - _PyObject_Free(ctx, p); + PyObject_Free(p); } return bp; } @@ -1571,14 +1284,14 @@ * at p. Instead we punt: let C continue to manage this block. */ if (nbytes) - return PyMem_Realloc(p, nbytes); + return realloc(p, nbytes); /* C doesn't define the result of realloc(p, 0) (it may or may not * return NULL then), but Python's docs promise that nbytes==0 never * returns NULL. We don't pass 0 to realloc(), to avoid that endcase * to begin with. Even then, we can't be sure that realloc() won't * return NULL. */ - bp = PyMem_Realloc(p, 1); + bp = realloc(p, 1); return bp ? bp : p; } @@ -1588,6 +1301,24 @@ /* pymalloc not enabled: Redirect the entry points to malloc. These will * only be used by extensions that are compiled with pymalloc enabled. */ +void * +PyObject_Malloc(size_t n) +{ + return PyMem_MALLOC(n); +} + +void * +PyObject_Realloc(void *p, size_t n) +{ + return PyMem_REALLOC(p, n); +} + +void +PyObject_Free(void *p) +{ + PyMem_FREE(p); +} + Py_ssize_t _Py_GetAllocatedBlocks(void) { @@ -1613,6 +1344,10 @@ #define DEADBYTE 0xDB /* dead (newly freed) memory */ #define FORBIDDENBYTE 0xFB /* untouchable bytes at each end of a block */ +/* We tag each block with an API ID in order to tag API violations */ +#define _PYMALLOC_MEM_ID 'm' /* the PyMem_Malloc() API */ +#define _PYMALLOC_OBJ_ID 'o' /* The PyObject_Malloc() API */ + static size_t serialno = 0; /* incremented on each debug {m,re}alloc */ /* serialno is always incremented via calling this routine. The point is @@ -1695,18 +1430,58 @@ p[2*S+n: 2*S+n+S] Copies of FORBIDDENBYTE. Used to catch over- writes and reads. p[2*S+n+S: 2*S+n+2*S] - A serial number, incremented by 1 on each call to _PyMem_DebugMalloc - and _PyMem_DebugRealloc. + A serial number, incremented by 1 on each call to _PyObject_DebugMalloc + and _PyObject_DebugRealloc. This is a big-endian size_t. If "bad memory" is detected later, the serial number gives an excellent way to set a breakpoint on the next run, to capture the instant at which this block was passed out. */ -static void * -_PyMem_DebugMalloc(void *ctx, size_t nbytes) +/* debug replacements for the PyMem_* memory API */ +void * +_PyMem_DebugMalloc(size_t nbytes) { - debug_alloc_api_t *api = (debug_alloc_api_t *)ctx; + return _PyObject_DebugMallocApi(_PYMALLOC_MEM_ID, nbytes); +} +void * +_PyMem_DebugRealloc(void *p, size_t nbytes) +{ + return _PyObject_DebugReallocApi(_PYMALLOC_MEM_ID, p, nbytes); +} +void +_PyMem_DebugFree(void *p) +{ + _PyObject_DebugFreeApi(_PYMALLOC_MEM_ID, p); +} + +/* debug replacements for the PyObject_* memory API */ +void * +_PyObject_DebugMalloc(size_t nbytes) +{ + return _PyObject_DebugMallocApi(_PYMALLOC_OBJ_ID, nbytes); +} +void * +_PyObject_DebugRealloc(void *p, size_t nbytes) +{ + return _PyObject_DebugReallocApi(_PYMALLOC_OBJ_ID, p, nbytes); +} +void +_PyObject_DebugFree(void *p) +{ + _PyObject_DebugFreeApi(_PYMALLOC_OBJ_ID, p); +} +void +_PyObject_DebugCheckAddress(const void *p) +{ + _PyObject_DebugCheckAddressApi(_PYMALLOC_OBJ_ID, p); +} + + +/* generic debug memory api, with an "id" to identify the API in use */ +void * +_PyObject_DebugMallocApi(char id, size_t nbytes) +{ uchar *p; /* base address of malloc'ed block */ uchar *tail; /* p + 2*SST + nbytes == pointer to tail pad bytes */ size_t total; /* nbytes + 4*SST */ @@ -1717,14 +1492,14 @@ /* overflow: can't represent total as a size_t */ return NULL; - p = (uchar *)api->alloc.malloc(api->alloc.ctx, total); + p = (uchar *)PyObject_Malloc(total); if (p == NULL) return NULL; /* at p, write size (SST bytes), id (1 byte), pad (SST-1 bytes) */ write_size_t(p, nbytes); - p[SST] = (uchar)api->api_id; - memset(p + SST + 1, FORBIDDENBYTE, SST-1); + p[SST] = (uchar)id; + memset(p + SST + 1 , FORBIDDENBYTE, SST-1); if (nbytes > 0) memset(p + 2*SST, CLEANBYTE, nbytes); @@ -1742,27 +1517,25 @@ Then fills the original bytes with DEADBYTE. Then calls the underlying free. */ -static void -_PyMem_DebugFree(void *ctx, void *p) +void +_PyObject_DebugFreeApi(char api, void *p) { - debug_alloc_api_t *api = (debug_alloc_api_t *)ctx; uchar *q = (uchar *)p - 2*SST; /* address returned from malloc */ size_t nbytes; if (p == NULL) return; - _PyMem_DebugCheckAddress(api->api_id, p); + _PyObject_DebugCheckAddressApi(api, p); nbytes = read_size_t(q); nbytes += 4*SST; if (nbytes > 0) memset(q, DEADBYTE, nbytes); - api->alloc.free(api->alloc.ctx, q); + PyObject_Free(q); } -static void * -_PyMem_DebugRealloc(void *ctx, void *p, size_t nbytes) +void * +_PyObject_DebugReallocApi(char api, void *p, size_t nbytes) { - debug_alloc_api_t *api = (debug_alloc_api_t *)ctx; uchar *q = (uchar *)p; uchar *tail; size_t total; /* nbytes + 4*SST */ @@ -1770,9 +1543,9 @@ int i; if (p == NULL) - return _PyMem_DebugMalloc(ctx, nbytes); + return _PyObject_DebugMallocApi(api, nbytes); - _PyMem_DebugCheckAddress(api->api_id, p); + _PyObject_DebugCheckAddressApi(api, p); bumpserialno(); original_nbytes = read_size_t(q - 2*SST); total = nbytes + 4*SST; @@ -1789,12 +1562,12 @@ * case we didn't get the chance to mark the old memory with DEADBYTE, * but we live with that. */ - q = (uchar *)api->alloc.realloc(api->alloc.ctx, q - 2*SST, total); + q = (uchar *)PyObject_Realloc(q - 2*SST, total); if (q == NULL) return NULL; write_size_t(q, nbytes); - assert(q[SST] == (uchar)api->api_id); + assert(q[SST] == (uchar)api); for (i = 1; i < SST; ++i) assert(q[SST + i] == FORBIDDENBYTE); q += 2*SST; @@ -1816,8 +1589,8 @@ * and call Py_FatalError to kill the program. * The API id, is also checked. */ -static void -_PyMem_DebugCheckAddress(char api, const void *p) + void +_PyObject_DebugCheckAddressApi(char api, const void *p) { const uchar *q = (const uchar *)p; char msgbuf[64]; @@ -1869,7 +1642,7 @@ } /* Display info to stderr about the memory block at p. */ -static void +void _PyObject_DebugDumpAddress(const void *p) { const uchar *q = (const uchar *)p; -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 15 03:38:05 2013 From: python-checkins at python.org (victor.stinner) Date: Sat, 15 Jun 2013 03:38:05 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_default_-=3E_default?= =?utf-8?q?=29=3A_Merge_heads?= Message-ID: <3bXLt53bdGz7LkY@mail.python.org> http://hg.python.org/cpython/rev/3cced3170f98 changeset: 84132:3cced3170f98 parent: 84131:b1455dd08000 parent: 84130:e7a01c7f69fe user: Victor Stinner date: Sat Jun 15 03:37:45 2013 +0200 summary: Merge heads files: Doc/library/enum.rst | 542 +++++++++++++++++ Doc/library/imp.rst | 3 + Doc/library/types.rst | 30 +- Lib/enum.py | 465 +++++++++++++++ Lib/test/test_enum.py | 921 ++++++++++++++++++++++++++++++ Misc/NEWS | 5 + 6 files changed, 1964 insertions(+), 2 deletions(-) diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst new file mode 100644 --- /dev/null +++ b/Doc/library/enum.rst @@ -0,0 +1,542 @@ +:mod:`enum` --- Support for enumerations +======================================== + +.. module:: enum +.. :synopsis: enumerations are sets of symbolic names bound to unique, constant + values. +.. :moduleauthor:: Ethan Furman +.. :sectionauthor:: Barry Warsaw , +.. :sectionauthor:: Eli Bendersky , +.. :sectionauthor:: Ethan Furman + +**Source code:** :source:`Lib/enum.py` + +---------------- + +An enumeration is a set of symbolic names (members) bound to unique, constant +values. Within an enumeration, the members can be compared by identity, and +the enumeration itself can be iterated over. + +This module defines two enumeration classes that can be used to define unique +sets of names and values: :class:`Enum` and :class:`IntEnum`. + +Creating an Enum +---------------- + +Enumerations are created using the :keyword:`class` syntax, which makes them +easy to read and write. An alternative creation method is described in +`Functional API`_. To define an enumeration, subclass :class:`Enum` as +follows:: + + >>> from enum import Enum + >>> class Color(Enum): + ... red = 1 + ... green = 2 + ... blue = 3 + +**A note on nomenclature**: we call :class:`Color` an *enumeration* (or *enum*) +and :attr:`Color.red`, :attr:`Color.green` are *enumeration members* (or +*enum members*). Enumeration members also have *values* (the value of +:attr:`Color.red` is ``1``, etc.) + +Enumeration members have human readable string representations:: + + >>> print(Color.red) + Color.red + +...while their ``repr`` has more information:: + + >>> print(repr(Color.red)) + + +The *type* of an enumeration member is the enumeration it belongs to:: + + >>> type(Color.red) + + >>> isinstance(Color.green, Color) + True + >>> + +Enum members also have a property that contains just their item name:: + + >>> print(Color.red.name) + red + +Enumerations support iteration, in definition order:: + + >>> class Shake(Enum): + ... vanilla = 7 + ... chocolate = 4 + ... cookies = 9 + ... mint = 3 + ... + >>> for shake in Shake: + ... print(shake) + ... + Shake.vanilla + Shake.chocolate + Shake.cookies + Shake.mint + +Enumeration members are hashable, so they can be used in dictionaries and sets:: + + >>> apples = {} + >>> apples[Color.red] = 'red delicious' + >>> apples[Color.green] = 'granny smith' + >>> apples == {Color.red: 'red delicious', Color.green: 'granny smith'} + True + + +Programmatic access to enumeration members +------------------------------------------ + +Sometimes it's useful to access members in enumerations programmatically (i.e. +situations where ``Color.red`` won't do because the exact color is not known +at program-writing time). ``Enum`` allows such access:: + + >>> Color(1) + + >>> Color(3) + + +If you want to access enum members by *name*, use item access:: + + >>> Color['red'] + + >>> Color['green'] + + + +Duplicating enum members and values +----------------------------------- + +Having two enum members with the same name is invalid:: + + >>> class Shape(Enum): + ... square = 2 + ... square = 3 + ... + Traceback (most recent call last): + ... + TypeError: Attempted to reuse key: 'square' + +However, two enum members are allowed to have the same value. Given two members +A and B with the same value (and A defined first), B is an alias to A. By-value +lookup of the value of A and B will return A. By-name lookup of B will also +return A:: + + >>> class Shape(Enum): + ... square = 2 + ... diamond = 1 + ... circle = 3 + ... alias_for_square = 2 + ... + >>> Shape.square + + >>> Shape.alias_for_square + + >>> Shape(2) + + +Iterating over the members of an enum does not provide the aliases:: + + >>> list(Shape) + [, , ] + +The special attribute ``__members__`` is an ordered dictionary mapping names +to members. It includes all names defined in the enumeration, including the +aliases:: + + >>> for name, member in Shape.__members__.items(): + ... name, member + ... + ('square', ) + ('diamond', ) + ('circle', ) + ('alias_for_square', ) + +The ``__members__`` attribute can be used for detailed programmatic access to +the enumeration members. For example, finding all the aliases:: + + >>> [name for name, member in Shape.__members__.items() if member.name != name] + ['alias_for_square'] + +Comparisons +----------- + +Enumeration members are compared by identity:: + + >>> Color.red is Color.red + True + >>> Color.red is Color.blue + False + >>> Color.red is not Color.blue + True + +Ordered comparisons between enumeration values are *not* supported. Enum +members are not integers (but see `IntEnum`_ below):: + + >>> Color.red < Color.blue + Traceback (most recent call last): + File "", line 1, in + TypeError: unorderable types: Color() < Color() + +Equality comparisons are defined though:: + + >>> Color.blue == Color.red + False + >>> Color.blue != Color.red + True + >>> Color.blue == Color.blue + True + +Comparisons against non-enumeration values will always compare not equal +(again, class:`IntEnum` was explicitly designed to behave differently, see +below):: + + >>> Color.blue == 2 + False + + +Allowed members and attributes of enumerations +---------------------------------------------- + +The examples above use integers for enumeration values. Using integers is +short and handy (and provided by default by the `Functional API`_), but not +strictly enforced. In the vast majority of use-cases, one doesn't care what +the actual value of an enumeration is. But if the value *is* important, +enumerations can have arbitrary values. + +Enumerations are Python classes, and can have methods and special methods as +usual. If we have this enumeration:: + + >>> class Mood(Enum): + ... funky = 1 + ... happy = 3 + ... + ... def describe(self): + ... # self is the member here + ... return self.name, self.value + ... + ... def __str__(self): + ... return 'my custom str! {0}'.format(self.value) + ... + ... @classmethod + ... def favorite_mood(cls): + ... # cls here is the enumeration + ... return cls.happy + +Then:: + + >>> Mood.favorite_mood() + + >>> Mood.happy.describe() + ('happy', 3) + >>> str(Mood.funky) + 'my custom str! 1' + +The rules for what is allowed are as follows: _sunder_ names (starting and +ending with a single underscore) are reserved by enum and cannot be used; +all other attributes defined within an enumeration will become members of this +enumeration, with the exception of *__dunder__* names and descriptors (methods +are also descriptors). + +Note: if your enumeration defines :meth:`__new__` and/or :meth:`__init__` then +whatever value(s) were given to the enum member will be passed into those +methods. See `Planet`_ for an example. + + +Restricted subclassing of enumerations +-------------------------------------- + +Subclassing an enumeration is allowed only if the enumeration does not define +any members. So this is forbidden:: + + >>> class MoreColor(Color): + ... pink = 17 + Traceback (most recent call last): + ... + TypeError: Cannot extend enumerations + +But this is allowed:: + + >>> class Foo(Enum): + ... def some_behavior(self): + ... pass + ... + >>> class Bar(Foo): + ... happy = 1 + ... sad = 2 + ... + +Allowing subclassing of enums that define members would lead to a violation of +some important invariants of types and instances. On the other hand, it makes +sense to allow sharing some common behavior between a group of enumerations. +(See `OrderedEnum`_ for an example.) + + +Pickling +-------- + +Enumerations can be pickled and unpickled:: + + >>> from test.test_enum import Fruit + >>> from pickle import dumps, loads + >>> Fruit.tomato is loads(dumps(Fruit.tomato)) + True + +The usual restrictions for pickling apply: picklable enums must be defined in +the top level of a module, since unpickling requires them to be importable +from that module. + +.. warning:: + + In order to support the singleton nature of enumeration members, pickle + protocol version 2 or higher must be used. + + +Functional API +-------------- + +The :class:`Enum` class is callable, providing the following functional API:: + + >>> Animal = Enum('Animal', 'ant bee cat dog') + >>> Animal + + >>> Animal.ant + + >>> Animal.ant.value + 1 + >>> list(Animal) + [, , , ] + +The semantics of this API resemble :class:`namedtuple`. The first argument +of the call to :class:`Enum` is the name of the enumeration. + +The second argument is the *source* of enumeration member names. It can be a +whitespace-separated string of names, a sequence of names, a sequence of +2-tuples with key/value pairs, or a mapping (e.g. dictionary) of names to +values. The last two options enable assigning arbitrary values to +enumerations; the others auto-assign increasing integers starting with 1. A +new class derived from :class:`Enum` is returned. In other words, the above +assignment to :class:`Animal` is equivalent to:: + + >>> class Animals(Enum): + ... ant = 1 + ... bee = 2 + ... cat = 3 + ... dog = 4 + +Pickling enums created with the functional API can be tricky as frame stack +implementation details are used to try and figure out which module the +enumeration is being created in (e.g. it will fail if you use a utility +function in separate module, and also may not work on IronPython or Jython). +The solution is to specify the module name explicitly as follows:: + + >>> Animals = Enum('Animals', 'ant bee cat dog', module=__name__) + +Derived Enumerations +==================== + +IntEnum +------- + +A variation of :class:`Enum` is provided which is also a subclass of +:class:`int`. Members of an :class:`IntEnum` can be compared to integers; +by extension, integer enumerations of different types can also be compared +to each other:: + + >>> from enum import IntEnum + >>> class Shape(IntEnum): + ... circle = 1 + ... square = 2 + ... + >>> class Request(IntEnum): + ... post = 1 + ... get = 2 + ... + >>> Shape == 1 + False + >>> Shape.circle == 1 + True + >>> Shape.circle == Request.post + True + +However, they still can't be compared to standard :class:`Enum` enumerations:: + + >>> class Shape(IntEnum): + ... circle = 1 + ... square = 2 + ... + >>> class Color(Enum): + ... red = 1 + ... green = 2 + ... + >>> Shape.circle == Color.red + False + +:class:`IntEnum` values behave like integers in other ways you'd expect:: + + >>> int(Shape.circle) + 1 + >>> ['a', 'b', 'c'][Shape.circle] + 'b' + >>> [i for i in range(Shape.square)] + [0, 1] + +For the vast majority of code, :class:`Enum` is strongly recommended, +since :class:`IntEnum` breaks some semantic promises of an enumeration (by +being comparable to integers, and thus by transitivity to other +unrelated enumerations). It should be used only in special cases where +there's no other choice; for example, when integer constants are +replaced with enumerations and backwards compatibility is required with code +that still expects integers. + + +Others +------ + +While :class:`IntEnum` is part of the :mod:`enum` module, it would be very +simple to implement independently:: + + class IntEnum(int, Enum): + pass + +This demonstrates how similar derived enumerations can be defined; for example +a :class:`StrEnum` that mixes in :class:`str` instead of :class:`int`. + +Some rules: + +1. When subclassing :class:`Enum`, mix-in types must appear before + :class:`Enum` itself in the sequence of bases, as in the :class:`IntEnum` + example above. +2. While :class:`Enum` can have members of any type, once you mix in an + additional type, all the members must have values of that type, e.g. + :class:`int` above. This restriction does not apply to mix-ins which only + add methods and don't specify another data type such as :class:`int` or + :class:`str`. +3. When another data type is mixed in, the :attr:`value` attribute is *not the + same* as the enum member itself, although it is equivalant and will compare + equal. + + +Interesting examples +==================== + +While :class:`Enum` and :class:`IntEnum` are expected to cover the majority of +use-cases, they cannot cover them all. Here are recipes for some different +types of enumerations that can be used directly, or as examples for creating +one's own. + + +AutoNumber +---------- + +Avoids having to specify the value for each enumeration member:: + + >>> class AutoNumber(Enum): + ... def __new__(cls): + ... value = len(cls.__members__) + 1 + ... obj = object.__new__(cls) + ... obj._value = value + ... return obj + ... + >>> class Color(AutoNumber): + ... red = () + ... green = () + ... blue = () + ... + >>> Color.green.value == 2 + True + + +UniqueEnum +---------- + +Raises an error if a duplicate member name is found instead of creating an +alias:: + + >>> class UniqueEnum(Enum): + ... def __init__(self, *args): + ... cls = self.__class__ + ... if any(self.value == e.value for e in cls): + ... a = self.name + ... e = cls(self.value).name + ... raise ValueError( + ... "aliases not allowed in UniqueEnum: %r --> %r" + ... % (a, e)) + ... + >>> class Color(UniqueEnum): + ... red = 1 + ... green = 2 + ... blue = 3 + ... grene = 2 + Traceback (most recent call last): + ... + ValueError: aliases not allowed in UniqueEnum: 'grene' --> 'green' + + +OrderedEnum +----------- + +An ordered enumeration that is not based on :class:`IntEnum` and so maintains +the normal :class:`Enum` invariants (such as not being comparable to other +enumerations):: + + >>> class OrderedEnum(Enum): + ... def __ge__(self, other): + ... if self.__class__ is other.__class__: + ... return self._value >= other._value + ... return NotImplemented + ... def __gt__(self, other): + ... if self.__class__ is other.__class__: + ... return self._value > other._value + ... return NotImplemented + ... def __le__(self, other): + ... if self.__class__ is other.__class__: + ... return self._value <= other._value + ... return NotImplemented + ... def __lt__(self, other): + ... if self.__class__ is other.__class__: + ... return self._value < other._value + ... return NotImplemented + ... + >>> class Grade(OrderedEnum): + ... A = 5 + ... B = 4 + ... C = 3 + ... D = 2 + ... F = 1 + ... + >>> Grade.C < Grade.A + True + + +Planet +------ + +If :meth:`__new__` or :meth:`__init__` is defined the value of the enum member +will be passed to those methods:: + + >>> class Planet(Enum): + ... MERCURY = (3.303e+23, 2.4397e6) + ... VENUS = (4.869e+24, 6.0518e6) + ... EARTH = (5.976e+24, 6.37814e6) + ... MARS = (6.421e+23, 3.3972e6) + ... JUPITER = (1.9e+27, 7.1492e7) + ... SATURN = (5.688e+26, 6.0268e7) + ... URANUS = (8.686e+25, 2.5559e7) + ... NEPTUNE = (1.024e+26, 2.4746e7) + ... def __init__(self, mass, radius): + ... self.mass = mass # in kilograms + ... self.radius = radius # in meters + ... @property + ... def surface_gravity(self): + ... # universal gravitational constant (m3 kg-1 s-2) + ... G = 6.67300E-11 + ... return G * self.mass / (self.radius * self.radius) + ... + >>> Planet.EARTH.value + (5.976e+24, 6378140.0) + >>> Planet.EARTH.surface_gravity + 9.802652743337129 diff --git a/Doc/library/imp.rst b/Doc/library/imp.rst --- a/Doc/library/imp.rst +++ b/Doc/library/imp.rst @@ -112,6 +112,9 @@ Return a new empty module object called *name*. This object is *not* inserted in ``sys.modules``. + .. deprecated:: 3.4 + Use :class:`types.ModuleType` instead. + .. function:: reload(module) diff --git a/Doc/library/types.rst b/Doc/library/types.rst --- a/Doc/library/types.rst +++ b/Doc/library/types.rst @@ -107,9 +107,35 @@ C".) -.. data:: ModuleType +.. class:: ModuleType(name, doc=None) - The type of modules. + The type of :term:`modules `. Constructor takes the name of the + module to be created and optionally its :term:`docstring`. + + .. attribute:: __doc__ + + The :term:`docstring` of the module. Defaults to ``None``. + + .. attribute:: __loader__ + + The :term:`loader` which loaded the module. Defaults to ``None``. + + .. versionchanged:: 3.4 + Defaults to ``None``. Previously the attribute was optional. + + .. attribute:: __name__ + + The name of the module. + + .. attribute:: __package__ + + Which :term:`package` a module belongs to. If the module is top-level + (i.e. not a part of any specific package) then the attribute should be set + to ``''``, else it should be set to the name of the package (which can be + :attr:`__name__` if the module is a package itself). Defaults to ``None``. + + .. versionchanged:: 3.4 + Defaults to ``None``. Previously the attribute was optional. .. data:: TracebackType diff --git a/Lib/enum.py b/Lib/enum.py new file mode 100644 --- /dev/null +++ b/Lib/enum.py @@ -0,0 +1,465 @@ +"""Python Enumerations""" + +import sys +from collections import OrderedDict +from types import MappingProxyType + +__all__ = ['Enum', 'IntEnum'] + + +class _RouteClassAttributeToGetattr: + """Route attribute access on a class to __getattr__. + + This is a descriptor, used to define attributes that act differently when + accessed through an instance and through a class. Instance access remains + normal, but access to an attribute through a class will be routed to the + class's __getattr__ method; this is done by raising AttributeError. + + """ + def __init__(self, fget=None): + self.fget = fget + + def __get__(self, instance, ownerclass=None): + if instance is None: + raise AttributeError() + return self.fget(instance) + + def __set__(self, instance, value): + raise AttributeError("can't set attribute") + + def __delete__(self, instance): + raise AttributeError("can't delete attribute") + + +def _is_dunder(name): + """Returns True if a __dunder__ name, False otherwise.""" + return (name[:2] == name[-2:] == '__' and + name[2:3] != '_' and + name[-3:-2] != '_') + + +def _is_sunder(name): + """Returns True if a _sunder_ name, False otherwise.""" + return (name[0] == name[-1] == '_' and + name[1:2] != '_' and + name[-2:-1] != '_') + + +def _make_class_unpicklable(cls): + """Make the given class un-picklable.""" + def _break_on_call_reduce(self): + raise TypeError('%r cannot be pickled' % self) + cls.__reduce__ = _break_on_call_reduce + cls.__module__ = '' + + +class _EnumDict(dict): + """Keeps track of definition order of the enum items. + + EnumMeta will use the names found in self._member_names as the + enumeration member names. + + """ + def __init__(self): + super().__init__() + self._member_names = [] + + def __setitem__(self, key, value): + """Changes anything not dundered or that doesn't have __get__. + + If a descriptor is added with the same name as an enum member, the name + is removed from _member_names (this may leave a hole in the numerical + sequence of values). + + If an enum member name is used twice, an error is raised; duplicate + values are not checked for. + + Single underscore (sunder) names are reserved. + + """ + if _is_sunder(key): + raise ValueError('_names_ are reserved for future Enum use') + elif _is_dunder(key) or hasattr(value, '__get__'): + if key in self._member_names: + # overwriting an enum with a method? then remove the name from + # _member_names or it will become an enum anyway when the class + # is created + self._member_names.remove(key) + else: + if key in self._member_names: + raise TypeError('Attempted to reuse key: %r' % key) + self._member_names.append(key) + super().__setitem__(key, value) + + +# Dummy value for Enum as EnumMeta explicity checks for it, but of course until +# EnumMeta finishes running the first time the Enum class doesn't exist. This +# is also why there are checks in EnumMeta like `if Enum is not None` +Enum = None + + +class EnumMeta(type): + """Metaclass for Enum""" + @classmethod + def __prepare__(metacls, cls, bases): + return _EnumDict() + + def __new__(metacls, cls, bases, classdict): + # an Enum class is final once enumeration items have been defined; it + # cannot be mixed with other types (int, float, etc.) if it has an + # inherited __new__ unless a new __new__ is defined (or the resulting + # class will fail). + member_type, first_enum = metacls._get_mixins_(bases) + __new__, save_new, use_args = metacls._find_new_(classdict, member_type, + first_enum) + + # save enum items into separate mapping so they don't get baked into + # the new class + members = {k: classdict[k] for k in classdict._member_names} + for name in classdict._member_names: + del classdict[name] + + # check for illegal enum names (any others?) + invalid_names = set(members) & {'mro', } + if invalid_names: + raise ValueError('Invalid enum member name: {0}'.format( + ','.join(invalid_names))) + + # create our new Enum type + enum_class = super().__new__(metacls, cls, bases, classdict) + enum_class._member_names = [] # names in definition order + enum_class._member_map = OrderedDict() # name->value map + + # Reverse value->name map for hashable values. + enum_class._value2member_map = {} + + # check for a __getnewargs__, and if not present sabotage + # pickling, since it won't work anyway + if (member_type is not object and + member_type.__dict__.get('__getnewargs__') is None + ): + _make_class_unpicklable(enum_class) + + # instantiate them, checking for duplicates as we go + # we instantiate first instead of checking for duplicates first in case + # a custom __new__ is doing something funky with the values -- such as + # auto-numbering ;) + for member_name in classdict._member_names: + value = members[member_name] + if not isinstance(value, tuple): + args = (value, ) + else: + args = value + if member_type is tuple: # special case for tuple enums + args = (args, ) # wrap it one more time + if not use_args: + enum_member = __new__(enum_class) + enum_member._value = value + else: + enum_member = __new__(enum_class, *args) + if not hasattr(enum_member, '_value'): + enum_member._value = member_type(*args) + enum_member._member_type = member_type + enum_member._name = member_name + enum_member.__init__(*args) + # If another member with the same value was already defined, the + # new member becomes an alias to the existing one. + for name, canonical_member in enum_class._member_map.items(): + if canonical_member.value == enum_member._value: + enum_member = canonical_member + break + else: + # Aliases don't appear in member names (only in __members__). + enum_class._member_names.append(member_name) + enum_class._member_map[member_name] = enum_member + try: + # This may fail if value is not hashable. We can't add the value + # to the map, and by-value lookups for this value will be + # linear. + enum_class._value2member_map[value] = enum_member + except TypeError: + pass + + # double check that repr and friends are not the mixin's or various + # things break (such as pickle) + for name in ('__repr__', '__str__', '__getnewargs__'): + class_method = getattr(enum_class, name) + obj_method = getattr(member_type, name, None) + enum_method = getattr(first_enum, name, None) + if obj_method is not None and obj_method is class_method: + setattr(enum_class, name, enum_method) + + # replace any other __new__ with our own (as long as Enum is not None, + # anyway) -- again, this is to support pickle + if Enum is not None: + # if the user defined their own __new__, save it before it gets + # clobbered in case they subclass later + if save_new: + enum_class.__new_member__ = __new__ + enum_class.__new__ = Enum.__new__ + return enum_class + + def __call__(cls, value, names=None, *, module=None, type=None): + """Either returns an existing member, or creates a new enum class. + + This method is used both when an enum class is given a value to match + to an enumeration member (i.e. Color(3)) and for the functional API + (i.e. Color = Enum('Color', names='red green blue')). + + When used for the functional API: `module`, if set, will be stored in + the new class' __module__ attribute; `type`, if set, will be mixed in + as the first base class. + + Note: if `module` is not set this routine will attempt to discover the + calling module by walking the frame stack; if this is unsuccessful + the resulting class will not be pickleable. + + """ + if names is None: # simple value lookup + return cls.__new__(cls, value) + # otherwise, functional API: we're creating a new Enum type + return cls._create_(value, names, module=module, type=type) + + def __contains__(cls, member): + return isinstance(member, cls) and member.name in cls._member_map + + def __dir__(self): + return ['__class__', '__doc__', '__members__'] + self._member_names + + @property + def __members__(cls): + """Returns a mapping of member name->value. + + This mapping lists all enum members, including aliases. Note that this + is a read-only view of the internal mapping. + + """ + return MappingProxyType(cls._member_map) + + def __getattr__(cls, name): + """Return the enum member matching `name` + + We use __getattr__ instead of descriptors or inserting into the enum + class' __dict__ in order to support `name` and `value` being both + properties for enum members (which live in the class' __dict__) and + enum members themselves. + + """ + if _is_dunder(name): + raise AttributeError(name) + try: + return cls._member_map[name] + except KeyError: + raise AttributeError(name) from None + + def __getitem__(cls, name): + return cls._member_map[name] + + def __iter__(cls): + return (cls._member_map[name] for name in cls._member_names) + + def __len__(cls): + return len(cls._member_names) + + def __repr__(cls): + return "" % cls.__name__ + + def _create_(cls, class_name, names=None, *, module=None, type=None): + """Convenience method to create a new Enum class. + + `names` can be: + + * A string containing member names, separated either with spaces or + commas. Values are auto-numbered from 1. + * An iterable of member names. Values are auto-numbered from 1. + * An iterable of (member name, value) pairs. + * A mapping of member name -> value. + + """ + metacls = cls.__class__ + bases = (cls, ) if type is None else (type, cls) + classdict = metacls.__prepare__(class_name, bases) + + # special processing needed for names? + if isinstance(names, str): + names = names.replace(',', ' ').split() + if isinstance(names, (tuple, list)) and isinstance(names[0], str): + names = [(e, i) for (i, e) in enumerate(names, 1)] + + # Here, names is either an iterable of (name, value) or a mapping. + for item in names: + if isinstance(item, str): + member_name, member_value = item, names[item] + else: + member_name, member_value = item + classdict[member_name] = member_value + enum_class = metacls.__new__(metacls, class_name, bases, classdict) + + # TODO: replace the frame hack if a blessed way to know the calling + # module is ever developed + if module is None: + try: + module = sys._getframe(2).f_globals['__name__'] + except (AttributeError, ValueError) as exc: + pass + if module is None: + _make_class_unpicklable(enum_class) + else: + enum_class.__module__ = module + + return enum_class + + @staticmethod + def _get_mixins_(bases): + """Returns the type for creating enum members, and the first inherited + enum class. + + bases: the tuple of bases that was given to __new__ + + """ + if not bases: + return object, Enum + + # double check that we are not subclassing a class with existing + # enumeration members; while we're at it, see if any other data + # type has been mixed in so we can use the correct __new__ + member_type = first_enum = None + for base in bases: + if (base is not Enum and + issubclass(base, Enum) and + base._member_names): + raise TypeError("Cannot extend enumerations") + # base is now the last base in bases + if not issubclass(base, Enum): + raise TypeError("new enumerations must be created as " + "`ClassName([mixin_type,] enum_type)`") + + # get correct mix-in type (either mix-in type of Enum subclass, or + # first base if last base is Enum) + if not issubclass(bases[0], Enum): + member_type = bases[0] # first data type + first_enum = bases[-1] # enum type + else: + for base in bases[0].__mro__: + # most common: (IntEnum, int, Enum, object) + # possible: (, , + # , , + # ) + if issubclass(base, Enum): + if first_enum is None: + first_enum = base + else: + if member_type is None: + member_type = base + + return member_type, first_enum + + @staticmethod + def _find_new_(classdict, member_type, first_enum): + """Returns the __new__ to be used for creating the enum members. + + classdict: the class dictionary given to __new__ + member_type: the data type whose __new__ will be used by default + first_enum: enumeration to check for an overriding __new__ + + """ + # now find the correct __new__, checking to see of one was defined + # by the user; also check earlier enum classes in case a __new__ was + # saved as __new_member__ + __new__ = classdict.get('__new__', None) + + # should __new__ be saved as __new_member__ later? + save_new = __new__ is not None + + if __new__ is None: + # check all possibles for __new_member__ before falling back to + # __new__ + for method in ('__new_member__', '__new__'): + for possible in (member_type, first_enum): + target = getattr(possible, method, None) + if target not in { + None, + None.__new__, + object.__new__, + Enum.__new__, + }: + __new__ = target + break + if __new__ is not None: + break + else: + __new__ = object.__new__ + + # if a non-object.__new__ is used then whatever value/tuple was + # assigned to the enum member name will be passed to __new__ and to the + # new enum member's __init__ + if __new__ is object.__new__: + use_args = False + else: + use_args = True + + return __new__, save_new, use_args + + +class Enum(metaclass=EnumMeta): + """Generic enumeration. + + Derive from this class to define new enumerations. + + """ + def __new__(cls, value): + # all enum instances are actually created during class construction + # without calling this method; this method is called by the metaclass' + # __call__ (i.e. Color(3) ), and by pickle + if type(value) is cls: + # For lookups like Color(Color.red) + return value + # by-value search for a matching enum member + # see if it's in the reverse mapping (for hashable values) + if value in cls._value2member_map: + return cls._value2member_map[value] + # not there, now do long search -- O(n) behavior + for member in cls._member_map.values(): + if member.value == value: + return member + raise ValueError("%s is not a valid %s" % (value, cls.__name__)) + + def __repr__(self): + return "<%s.%s: %r>" % ( + self.__class__.__name__, self._name, self._value) + + def __str__(self): + return "%s.%s" % (self.__class__.__name__, self._name) + + def __dir__(self): + return (['__class__', '__doc__', 'name', 'value']) + + def __eq__(self, other): + if type(other) is self.__class__: + return self is other + return NotImplemented + + def __getnewargs__(self): + return (self._value, ) + + def __hash__(self): + return hash(self._name) + + # _RouteClassAttributeToGetattr is used to provide access to the `name` + # and `value` properties of enum members while keeping some measure of + # protection from modification, while still allowing for an enumeration + # to have members named `name` and `value`. This works because enumeration + # members are not set directly on the enum class -- __getattr__ is + # used to look them up. + + @_RouteClassAttributeToGetattr + def name(self): + return self._name + + @_RouteClassAttributeToGetattr + def value(self): + return self._value + + +class IntEnum(int, Enum): + """Enum where members are also (and must be) ints""" diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py new file mode 100644 --- /dev/null +++ b/Lib/test/test_enum.py @@ -0,0 +1,921 @@ +import enum +import unittest +from collections import OrderedDict +from pickle import dumps, loads, PicklingError +from enum import Enum, IntEnum + +# for pickle tests +try: + class Stooges(Enum): + LARRY = 1 + CURLY = 2 + MOE = 3 +except Exception as exc: + Stooges = exc + +try: + class IntStooges(int, Enum): + LARRY = 1 + CURLY = 2 + MOE = 3 +except Exception as exc: + IntStooges = exc + +try: + class FloatStooges(float, Enum): + LARRY = 1.39 + CURLY = 2.72 + MOE = 3.142596 +except Exception as exc: + FloatStooges = exc + +# for pickle test and subclass tests +try: + class StrEnum(str, Enum): + 'accepts only string values' + class Name(StrEnum): + BDFL = 'Guido van Rossum' + FLUFL = 'Barry Warsaw' +except Exception as exc: + Name = exc + +try: + Question = Enum('Question', 'who what when where why', module=__name__) +except Exception as exc: + Question = exc + +try: + Answer = Enum('Answer', 'him this then there because') +except Exception as exc: + Answer = exc + +# for doctests +try: + class Fruit(Enum): + tomato = 1 + banana = 2 + cherry = 3 +except Exception: + pass + +class TestEnum(unittest.TestCase): + def setUp(self): + class Season(Enum): + SPRING = 1 + SUMMER = 2 + AUTUMN = 3 + WINTER = 4 + self.Season = Season + + def test_enum_in_enum_out(self): + Season = self.Season + self.assertIs(Season(Season.WINTER), Season.WINTER) + + def test_enum_value(self): + Season = self.Season + self.assertEqual(Season.SPRING.value, 1) + + def test_intenum_value(self): + self.assertEqual(IntStooges.CURLY.value, 2) + + def test_dir_on_class(self): + Season = self.Season + self.assertEqual( + set(dir(Season)), + set(['__class__', '__doc__', '__members__', + 'SPRING', 'SUMMER', 'AUTUMN', 'WINTER']), + ) + + def test_dir_on_item(self): + Season = self.Season + self.assertEqual( + set(dir(Season.WINTER)), + set(['__class__', '__doc__', 'name', 'value']), + ) + + def test_enum(self): + Season = self.Season + lst = list(Season) + self.assertEqual(len(lst), len(Season)) + self.assertEqual(len(Season), 4, Season) + self.assertEqual( + [Season.SPRING, Season.SUMMER, Season.AUTUMN, Season.WINTER], lst) + + for i, season in enumerate('SPRING SUMMER AUTUMN WINTER'.split(), 1): + e = Season(i) + self.assertEqual(e, getattr(Season, season)) + self.assertEqual(e.value, i) + self.assertNotEqual(e, i) + self.assertEqual(e.name, season) + self.assertIn(e, Season) + self.assertIs(type(e), Season) + self.assertIsInstance(e, Season) + self.assertEqual(str(e), 'Season.' + season) + self.assertEqual( + repr(e), + ''.format(season, i), + ) + + def test_value_name(self): + Season = self.Season + self.assertEqual(Season.SPRING.name, 'SPRING') + self.assertEqual(Season.SPRING.value, 1) + with self.assertRaises(AttributeError): + Season.SPRING.name = 'invierno' + with self.assertRaises(AttributeError): + Season.SPRING.value = 2 + + def test_invalid_names(self): + with self.assertRaises(ValueError): + class Wrong(Enum): + mro = 9 + with self.assertRaises(ValueError): + class Wrong(Enum): + _create_= 11 + with self.assertRaises(ValueError): + class Wrong(Enum): + _get_mixins_ = 9 + with self.assertRaises(ValueError): + class Wrong(Enum): + _find_new_ = 1 + with self.assertRaises(ValueError): + class Wrong(Enum): + _any_name_ = 9 + + def test_contains(self): + Season = self.Season + self.assertIn(Season.AUTUMN, Season) + self.assertNotIn(3, Season) + + val = Season(3) + self.assertIn(val, Season) + + class OtherEnum(Enum): + one = 1; two = 2 + self.assertNotIn(OtherEnum.two, Season) + + def test_comparisons(self): + Season = self.Season + with self.assertRaises(TypeError): + Season.SPRING < Season.WINTER + with self.assertRaises(TypeError): + Season.SPRING > 4 + + self.assertNotEqual(Season.SPRING, 1) + + class Part(Enum): + SPRING = 1 + CLIP = 2 + BARREL = 3 + + self.assertNotEqual(Season.SPRING, Part.SPRING) + with self.assertRaises(TypeError): + Season.SPRING < Part.CLIP + + def test_enum_duplicates(self): + class Season(Enum): + SPRING = 1 + SUMMER = 2 + AUTUMN = FALL = 3 + WINTER = 4 + ANOTHER_SPRING = 1 + lst = list(Season) + self.assertEqual( + lst, + [Season.SPRING, Season.SUMMER, + Season.AUTUMN, Season.WINTER, + ]) + self.assertIs(Season.FALL, Season.AUTUMN) + self.assertEqual(Season.FALL.value, 3) + self.assertEqual(Season.AUTUMN.value, 3) + self.assertIs(Season(3), Season.AUTUMN) + self.assertIs(Season(1), Season.SPRING) + self.assertEqual(Season.FALL.name, 'AUTUMN') + self.assertEqual( + [k for k,v in Season.__members__.items() if v.name != k], + ['FALL', 'ANOTHER_SPRING'], + ) + + def test_enum_with_value_name(self): + class Huh(Enum): + name = 1 + value = 2 + self.assertEqual( + list(Huh), + [Huh.name, Huh.value], + ) + self.assertIs(type(Huh.name), Huh) + self.assertEqual(Huh.name.name, 'name') + self.assertEqual(Huh.name.value, 1) + def test_hash(self): + Season = self.Season + dates = {} + dates[Season.WINTER] = '1225' + dates[Season.SPRING] = '0315' + dates[Season.SUMMER] = '0704' + dates[Season.AUTUMN] = '1031' + self.assertEqual(dates[Season.AUTUMN], '1031') + + def test_intenum_from_scratch(self): + class phy(int, Enum): + pi = 3 + tau = 2 * pi + self.assertTrue(phy.pi < phy.tau) + + def test_intenum_inherited(self): + class IntEnum(int, Enum): + pass + class phy(IntEnum): + pi = 3 + tau = 2 * pi + self.assertTrue(phy.pi < phy.tau) + + def test_floatenum_from_scratch(self): + class phy(float, Enum): + pi = 3.141596 + tau = 2 * pi + self.assertTrue(phy.pi < phy.tau) + + def test_floatenum_inherited(self): + class FloatEnum(float, Enum): + pass + class phy(FloatEnum): + pi = 3.141596 + tau = 2 * pi + self.assertTrue(phy.pi < phy.tau) + + def test_strenum_from_scratch(self): + class phy(str, Enum): + pi = 'Pi' + tau = 'Tau' + self.assertTrue(phy.pi < phy.tau) + + def test_strenum_inherited(self): + class StrEnum(str, Enum): + pass + class phy(StrEnum): + pi = 'Pi' + tau = 'Tau' + self.assertTrue(phy.pi < phy.tau) + + + def test_intenum(self): + class WeekDay(IntEnum): + SUNDAY = 1 + MONDAY = 2 + TUESDAY = 3 + WEDNESDAY = 4 + THURSDAY = 5 + FRIDAY = 6 + SATURDAY = 7 + + self.assertEqual(['a', 'b', 'c'][WeekDay.MONDAY], 'c') + self.assertEqual([i for i in range(WeekDay.TUESDAY)], [0, 1, 2]) + + lst = list(WeekDay) + self.assertEqual(len(lst), len(WeekDay)) + self.assertEqual(len(WeekDay), 7) + target = 'SUNDAY MONDAY TUESDAY WEDNESDAY THURSDAY FRIDAY SATURDAY' + target = target.split() + for i, weekday in enumerate(target, 1): + e = WeekDay(i) + self.assertEqual(e, i) + self.assertEqual(int(e), i) + self.assertEqual(e.name, weekday) + self.assertIn(e, WeekDay) + self.assertEqual(lst.index(e)+1, i) + self.assertTrue(0 < e < 8) + self.assertIs(type(e), WeekDay) + self.assertIsInstance(e, int) + self.assertIsInstance(e, Enum) + + def test_intenum_duplicates(self): + class WeekDay(IntEnum): + SUNDAY = 1 + MONDAY = 2 + TUESDAY = TEUSDAY = 3 + WEDNESDAY = 4 + THURSDAY = 5 + FRIDAY = 6 + SATURDAY = 7 + self.assertIs(WeekDay.TEUSDAY, WeekDay.TUESDAY) + self.assertEqual(WeekDay(3).name, 'TUESDAY') + self.assertEqual([k for k,v in WeekDay.__members__.items() + if v.name != k], ['TEUSDAY', ]) + + def test_pickle_enum(self): + if isinstance(Stooges, Exception): + raise Stooges + self.assertIs(Stooges.CURLY, loads(dumps(Stooges.CURLY))) + self.assertIs(Stooges, loads(dumps(Stooges))) + + def test_pickle_int(self): + if isinstance(IntStooges, Exception): + raise IntStooges + self.assertIs(IntStooges.CURLY, loads(dumps(IntStooges.CURLY))) + self.assertIs(IntStooges, loads(dumps(IntStooges))) + + def test_pickle_float(self): + if isinstance(FloatStooges, Exception): + raise FloatStooges + self.assertIs(FloatStooges.CURLY, loads(dumps(FloatStooges.CURLY))) + self.assertIs(FloatStooges, loads(dumps(FloatStooges))) + + def test_pickle_enum_function(self): + if isinstance(Answer, Exception): + raise Answer + self.assertIs(Answer.him, loads(dumps(Answer.him))) + self.assertIs(Answer, loads(dumps(Answer))) + + def test_pickle_enum_function_with_module(self): + if isinstance(Question, Exception): + raise Question + self.assertIs(Question.who, loads(dumps(Question.who))) + self.assertIs(Question, loads(dumps(Question))) + + def test_exploding_pickle(self): + BadPickle = Enum('BadPickle', 'dill sweet bread-n-butter') + enum._make_class_unpicklable(BadPickle) + globals()['BadPickle'] = BadPickle + with self.assertRaises(TypeError): + dumps(BadPickle.dill) + with self.assertRaises(PicklingError): + dumps(BadPickle) + + def test_string_enum(self): + class SkillLevel(str, Enum): + master = 'what is the sound of one hand clapping?' + journeyman = 'why did the chicken cross the road?' + apprentice = 'knock, knock!' + self.assertEqual(SkillLevel.apprentice, 'knock, knock!') + + def test_getattr_getitem(self): + class Period(Enum): + morning = 1 + noon = 2 + evening = 3 + night = 4 + self.assertIs(Period(2), Period.noon) + self.assertIs(getattr(Period, 'night'), Period.night) + self.assertIs(Period['morning'], Period.morning) + + def test_getattr_dunder(self): + Season = self.Season + self.assertTrue(getattr(Season, '__eq__')) + + def test_iteration_order(self): + class Season(Enum): + SUMMER = 2 + WINTER = 4 + AUTUMN = 3 + SPRING = 1 + self.assertEqual( + list(Season), + [Season.SUMMER, Season.WINTER, Season.AUTUMN, Season.SPRING], + ) + + def test_programatic_function_string(self): + SummerMonth = Enum('SummerMonth', 'june july august') + lst = list(SummerMonth) + self.assertEqual(len(lst), len(SummerMonth)) + self.assertEqual(len(SummerMonth), 3, SummerMonth) + self.assertEqual( + [SummerMonth.june, SummerMonth.july, SummerMonth.august], + lst, + ) + for i, month in enumerate('june july august'.split(), 1): + e = SummerMonth(i) + self.assertEqual(int(e.value), i) + self.assertNotEqual(e, i) + self.assertEqual(e.name, month) + self.assertIn(e, SummerMonth) + self.assertIs(type(e), SummerMonth) + + def test_programatic_function_string_list(self): + SummerMonth = Enum('SummerMonth', ['june', 'july', 'august']) + lst = list(SummerMonth) + self.assertEqual(len(lst), len(SummerMonth)) + self.assertEqual(len(SummerMonth), 3, SummerMonth) + self.assertEqual( + [SummerMonth.june, SummerMonth.july, SummerMonth.august], + lst, + ) + for i, month in enumerate('june july august'.split(), 1): + e = SummerMonth(i) + self.assertEqual(int(e.value), i) + self.assertNotEqual(e, i) + self.assertEqual(e.name, month) + self.assertIn(e, SummerMonth) + self.assertIs(type(e), SummerMonth) + + def test_programatic_function_iterable(self): + SummerMonth = Enum( + 'SummerMonth', + (('june', 1), ('july', 2), ('august', 3)) + ) + lst = list(SummerMonth) + self.assertEqual(len(lst), len(SummerMonth)) + self.assertEqual(len(SummerMonth), 3, SummerMonth) + self.assertEqual( + [SummerMonth.june, SummerMonth.july, SummerMonth.august], + lst, + ) + for i, month in enumerate('june july august'.split(), 1): + e = SummerMonth(i) + self.assertEqual(int(e.value), i) + self.assertNotEqual(e, i) + self.assertEqual(e.name, month) + self.assertIn(e, SummerMonth) + self.assertIs(type(e), SummerMonth) + + def test_programatic_function_from_dict(self): + SummerMonth = Enum( + 'SummerMonth', + OrderedDict((('june', 1), ('july', 2), ('august', 3))) + ) + lst = list(SummerMonth) + self.assertEqual(len(lst), len(SummerMonth)) + self.assertEqual(len(SummerMonth), 3, SummerMonth) + self.assertEqual( + [SummerMonth.june, SummerMonth.july, SummerMonth.august], + lst, + ) + for i, month in enumerate('june july august'.split(), 1): + e = SummerMonth(i) + self.assertEqual(int(e.value), i) + self.assertNotEqual(e, i) + self.assertEqual(e.name, month) + self.assertIn(e, SummerMonth) + self.assertIs(type(e), SummerMonth) + + def test_programatic_function_type(self): + SummerMonth = Enum('SummerMonth', 'june july august', type=int) + lst = list(SummerMonth) + self.assertEqual(len(lst), len(SummerMonth)) + self.assertEqual(len(SummerMonth), 3, SummerMonth) + self.assertEqual( + [SummerMonth.june, SummerMonth.july, SummerMonth.august], + lst, + ) + for i, month in enumerate('june july august'.split(), 1): + e = SummerMonth(i) + self.assertEqual(e, i) + self.assertEqual(e.name, month) + self.assertIn(e, SummerMonth) + self.assertIs(type(e), SummerMonth) + + def test_programatic_function_type_from_subclass(self): + SummerMonth = IntEnum('SummerMonth', 'june july august') + lst = list(SummerMonth) + self.assertEqual(len(lst), len(SummerMonth)) + self.assertEqual(len(SummerMonth), 3, SummerMonth) + self.assertEqual( + [SummerMonth.june, SummerMonth.july, SummerMonth.august], + lst, + ) + for i, month in enumerate('june july august'.split(), 1): + e = SummerMonth(i) + self.assertEqual(e, i) + self.assertEqual(e.name, month) + self.assertIn(e, SummerMonth) + self.assertIs(type(e), SummerMonth) + + def test_subclassing(self): + if isinstance(Name, Exception): + raise Name + self.assertEqual(Name.BDFL, 'Guido van Rossum') + self.assertTrue(Name.BDFL, Name('Guido van Rossum')) + self.assertIs(Name.BDFL, getattr(Name, 'BDFL')) + self.assertIs(Name.BDFL, loads(dumps(Name.BDFL))) + + def test_extending(self): + class Color(Enum): + red = 1 + green = 2 + blue = 3 + with self.assertRaises(TypeError): + class MoreColor(Color): + cyan = 4 + magenta = 5 + yellow = 6 + + def test_exclude_methods(self): + class whatever(Enum): + this = 'that' + these = 'those' + def really(self): + return 'no, not %s' % self.value + self.assertIsNot(type(whatever.really), whatever) + self.assertEqual(whatever.this.really(), 'no, not that') + + def test_overwrite_enums(self): + class Why(Enum): + question = 1 + answer = 2 + propisition = 3 + def question(self): + print(42) + self.assertIsNot(type(Why.question), Why) + self.assertNotIn(Why.question, Why._member_names) + self.assertNotIn(Why.question, Why) + + def test_wrong_inheritance_order(self): + with self.assertRaises(TypeError): + class Wrong(Enum, str): + NotHere = 'error before this point' + + def test_intenum_transitivity(self): + class number(IntEnum): + one = 1 + two = 2 + three = 3 + class numero(IntEnum): + uno = 1 + dos = 2 + tres = 3 + self.assertEqual(number.one, numero.uno) + self.assertEqual(number.two, numero.dos) + self.assertEqual(number.three, numero.tres) + + def test_wrong_enum_in_call(self): + class Monochrome(Enum): + black = 0 + white = 1 + class Gender(Enum): + male = 0 + female = 1 + self.assertRaises(ValueError, Monochrome, Gender.male) + + def test_wrong_enum_in_mixed_call(self): + class Monochrome(IntEnum): + black = 0 + white = 1 + class Gender(Enum): + male = 0 + female = 1 + self.assertRaises(ValueError, Monochrome, Gender.male) + + def test_mixed_enum_in_call_1(self): + class Monochrome(IntEnum): + black = 0 + white = 1 + class Gender(IntEnum): + male = 0 + female = 1 + self.assertIs(Monochrome(Gender.female), Monochrome.white) + + def test_mixed_enum_in_call_2(self): + class Monochrome(Enum): + black = 0 + white = 1 + class Gender(IntEnum): + male = 0 + female = 1 + self.assertIs(Monochrome(Gender.male), Monochrome.black) + + def test_flufl_enum(self): + class Fluflnum(Enum): + def __int__(self): + return int(self.value) + class MailManOptions(Fluflnum): + option1 = 1 + option2 = 2 + option3 = 3 + self.assertEqual(int(MailManOptions.option1), 1) + + def test_no_such_enum_member(self): + class Color(Enum): + red = 1 + green = 2 + blue = 3 + with self.assertRaises(ValueError): + Color(4) + with self.assertRaises(KeyError): + Color['chartreuse'] + + def test_new_repr(self): + class Color(Enum): + red = 1 + green = 2 + blue = 3 + def __repr__(self): + return "don't you just love shades of %s?" % self.name + self.assertEqual( + repr(Color.blue), + "don't you just love shades of blue?", + ) + + def test_inherited_repr(self): + class MyEnum(Enum): + def __repr__(self): + return "My name is %s." % self.name + class MyIntEnum(int, MyEnum): + this = 1 + that = 2 + theother = 3 + self.assertEqual(repr(MyIntEnum.that), "My name is that.") + + def test_multiple_mixin_mro(self): + class auto_enum(type(Enum)): + def __new__(metacls, cls, bases, classdict): + temp = type(classdict)() + names = set(classdict._member_names) + i = 0 + for k in classdict._member_names: + v = classdict[k] + if v is Ellipsis: + v = i + else: + i = v + i += 1 + temp[k] = v + for k, v in classdict.items(): + if k not in names: + temp[k] = v + return super(auto_enum, metacls).__new__( + metacls, cls, bases, temp) + + class AutoNumberedEnum(Enum, metaclass=auto_enum): + pass + + class AutoIntEnum(IntEnum, metaclass=auto_enum): + pass + + class TestAutoNumber(AutoNumberedEnum): + a = ... + b = 3 + c = ... + + class TestAutoInt(AutoIntEnum): + a = ... + b = 3 + c = ... + + def test_subclasses_with_getnewargs(self): + class NamedInt(int): + def __new__(cls, *args): + _args = args + name, *args = args + if len(args) == 0: + raise TypeError("name and value must be specified") + self = int.__new__(cls, *args) + self._intname = name + self._args = _args + return self + def __getnewargs__(self): + return self._args + @property + def __name__(self): + return self._intname + def __repr__(self): + # repr() is updated to include the name and type info + return "{}({!r}, {})".format(type(self).__name__, + self.__name__, + int.__repr__(self)) + def __str__(self): + # str() is unchanged, even if it relies on the repr() fallback + base = int + base_str = base.__str__ + if base_str.__objclass__ is object: + return base.__repr__(self) + return base_str(self) + # for simplicity, we only define one operator that + # propagates expressions + def __add__(self, other): + temp = int(self) + int( other) + if isinstance(self, NamedInt) and isinstance(other, NamedInt): + return NamedInt( + '({0} + {1})'.format(self.__name__, other.__name__), + temp ) + else: + return temp + + class NEI(NamedInt, Enum): + x = ('the-x', 1) + y = ('the-y', 2) + + self.assertIs(NEI.__new__, Enum.__new__) + self.assertEqual(repr(NEI.x + NEI.y), "NamedInt('(the-x + the-y)', 3)") + globals()['NamedInt'] = NamedInt + globals()['NEI'] = NEI + NI5 = NamedInt('test', 5) + self.assertEqual(NI5, 5) + self.assertEqual(loads(dumps(NI5)), 5) + self.assertEqual(NEI.y.value, 2) + self.assertIs(loads(dumps(NEI.y)), NEI.y) + + def test_subclasses_without_getnewargs(self): + class NamedInt(int): + def __new__(cls, *args): + _args = args + name, *args = args + if len(args) == 0: + raise TypeError("name and value must be specified") + self = int.__new__(cls, *args) + self._intname = name + self._args = _args + return self + @property + def __name__(self): + return self._intname + def __repr__(self): + # repr() is updated to include the name and type info + return "{}({!r}, {})".format(type(self).__name__, + self.__name__, + int.__repr__(self)) + def __str__(self): + # str() is unchanged, even if it relies on the repr() fallback + base = int + base_str = base.__str__ + if base_str.__objclass__ is object: + return base.__repr__(self) + return base_str(self) + # for simplicity, we only define one operator that + # propagates expressions + def __add__(self, other): + temp = int(self) + int( other) + if isinstance(self, NamedInt) and isinstance(other, NamedInt): + return NamedInt( + '({0} + {1})'.format(self.__name__, other.__name__), + temp ) + else: + return temp + + class NEI(NamedInt, Enum): + x = ('the-x', 1) + y = ('the-y', 2) + + self.assertIs(NEI.__new__, Enum.__new__) + self.assertEqual(repr(NEI.x + NEI.y), "NamedInt('(the-x + the-y)', 3)") + globals()['NamedInt'] = NamedInt + globals()['NEI'] = NEI + NI5 = NamedInt('test', 5) + self.assertEqual(NI5, 5) + self.assertEqual(NEI.y.value, 2) + with self.assertRaises(TypeError): + dumps(NEI.x) + with self.assertRaises(PicklingError): + dumps(NEI) + + def test_tuple_subclass(self): + class SomeTuple(tuple, Enum): + first = (1, 'for the money') + second = (2, 'for the show') + third = (3, 'for the music') + self.assertIs(type(SomeTuple.first), SomeTuple) + self.assertIsInstance(SomeTuple.second, tuple) + self.assertEqual(SomeTuple.third, (3, 'for the music')) + globals()['SomeTuple'] = SomeTuple + self.assertIs(loads(dumps(SomeTuple.first)), SomeTuple.first) + + def test_duplicate_values_give_unique_enum_items(self): + class AutoNumber(Enum): + first = () + second = () + third = () + def __new__(cls): + value = len(cls.__members__) + 1 + obj = object.__new__(cls) + obj._value = value + return obj + def __int__(self): + return int(self._value) + self.assertEqual( + list(AutoNumber), + [AutoNumber.first, AutoNumber.second, AutoNumber.third], + ) + self.assertEqual(int(AutoNumber.second), 2) + self.assertIs(AutoNumber(1), AutoNumber.first) + + def test_inherited_new_from_enhanced_enum(self): + class AutoNumber(Enum): + def __new__(cls): + value = len(cls.__members__) + 1 + obj = object.__new__(cls) + obj._value = value + return obj + def __int__(self): + return int(self._value) + class Color(AutoNumber): + red = () + green = () + blue = () + self.assertEqual(list(Color), [Color.red, Color.green, Color.blue]) + self.assertEqual(list(map(int, Color)), [1, 2, 3]) + + def test_inherited_new_from_mixed_enum(self): + class AutoNumber(IntEnum): + def __new__(cls): + value = len(cls.__members__) + 1 + obj = int.__new__(cls, value) + obj._value = value + return obj + class Color(AutoNumber): + red = () + green = () + blue = () + self.assertEqual(list(Color), [Color.red, Color.green, Color.blue]) + self.assertEqual(list(map(int, Color)), [1, 2, 3]) + + def test_ordered_mixin(self): + class OrderedEnum(Enum): + def __ge__(self, other): + if self.__class__ is other.__class__: + return self._value >= other._value + return NotImplemented + def __gt__(self, other): + if self.__class__ is other.__class__: + return self._value > other._value + return NotImplemented + def __le__(self, other): + if self.__class__ is other.__class__: + return self._value <= other._value + return NotImplemented + def __lt__(self, other): + if self.__class__ is other.__class__: + return self._value < other._value + return NotImplemented + class Grade(OrderedEnum): + A = 5 + B = 4 + C = 3 + D = 2 + F = 1 + self.assertGreater(Grade.A, Grade.B) + self.assertLessEqual(Grade.F, Grade.C) + self.assertLess(Grade.D, Grade.A) + self.assertGreaterEqual(Grade.B, Grade.B) + def test_extending2(self): + class Shade(Enum): + def shade(self): + print(self.name) + class Color(Shade): + red = 1 + green = 2 + blue = 3 + with self.assertRaises(TypeError): + class MoreColor(Color): + cyan = 4 + magenta = 5 + yellow = 6 + + def test_extending3(self): + class Shade(Enum): + def shade(self): + return self.name + class Color(Shade): + def hex(self): + return '%s hexlified!' % self.value + class MoreColor(Color): + cyan = 4 + magenta = 5 + yellow = 6 + self.assertEqual(MoreColor.magenta.hex(), '5 hexlified!') + + + def test_no_duplicates(self): + class UniqueEnum(Enum): + def __init__(self, *args): + cls = self.__class__ + if any(self.value == e.value for e in cls): + a = self.name + e = cls(self.value).name + raise ValueError( + "aliases not allowed in UniqueEnum: %r --> %r" + % (a, e) + ) + class Color(UniqueEnum): + red = 1 + green = 2 + blue = 3 + with self.assertRaises(ValueError): + class Color(UniqueEnum): + red = 1 + green = 2 + blue = 3 + grene = 2 + + def test_init(self): + class Planet(Enum): + MERCURY = (3.303e+23, 2.4397e6) + VENUS = (4.869e+24, 6.0518e6) + EARTH = (5.976e+24, 6.37814e6) + MARS = (6.421e+23, 3.3972e6) + JUPITER = (1.9e+27, 7.1492e7) + SATURN = (5.688e+26, 6.0268e7) + URANUS = (8.686e+25, 2.5559e7) + NEPTUNE = (1.024e+26, 2.4746e7) + def __init__(self, mass, radius): + self.mass = mass # in kilograms + self.radius = radius # in meters + @property + def surface_gravity(self): + # universal gravitational constant (m3 kg-1 s-2) + G = 6.67300E-11 + return G * self.mass / (self.radius * self.radius) + self.assertEqual(round(Planet.EARTH.surface_gravity, 2), 9.80) + self.assertEqual(Planet.EARTH.value, (5.976e+24, 6.37814e6)) + + +if __name__ == '__main__': + unittest.main() diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -123,6 +123,9 @@ Library ------- +- Issue #17907: Document imp.new_module() as deprecated in favour of + types.ModuleType. + - Issue #18192: Introduce importlib.util.MAGIC_NUMBER and document as deprecated imp.get_magic(). @@ -378,6 +381,8 @@ - Implement PEP 443 "Single-dispatch generic functions". +- Implement PEP 435 "Adding an Enum type to the Python standard library". + Tests ----- -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 15 03:59:23 2013 From: python-checkins at python.org (brett.cannon) Date: Sat, 15 Jun 2013 03:59:23 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Fix_markup_of_the_synopsis?= =?utf-8?q?_along_with_changing_to_state_what_is_in_the?= Message-ID: <3bXMLg1wRlzPqc@mail.python.org> http://hg.python.org/cpython/rev/704a05135efe changeset: 84133:704a05135efe user: Brett Cannon date: Fri Jun 14 21:59:16 2013 -0400 summary: Fix markup of the synopsis along with changing to state what is in the module and not as a definition of what an enumeration is. files: Doc/library/enum.rst | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst --- a/Doc/library/enum.rst +++ b/Doc/library/enum.rst @@ -2,8 +2,8 @@ ======================================== .. module:: enum -.. :synopsis: enumerations are sets of symbolic names bound to unique, constant - values. + :synopsis: Implementation of an enumeration class. + .. :moduleauthor:: Ethan Furman .. :sectionauthor:: Barry Warsaw , .. :sectionauthor:: Eli Bendersky , -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 15 04:24:38 2013 From: python-checkins at python.org (victor.stinner) Date: Sat, 15 Jun 2013 04:24:38 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?peps=3A_PEP_445=3A_Add_new_APIs_to_cu?= =?utf-8?q?stomize_memory_allocators?= Message-ID: <3bXMvp596rzRMx@mail.python.org> http://hg.python.org/peps/rev/2c07212981f7 changeset: 4925:2c07212981f7 user: Victor Stinner date: Sat Jun 15 04:24:25 2013 +0200 summary: PEP 445: Add new APIs to customize memory allocators files: pep-0445.txt | 174 +++++++++++++++++++++++++++++++++++++++ 1 files changed, 174 insertions(+), 0 deletions(-) diff --git a/pep-0445.txt b/pep-0445.txt new file mode 100644 --- /dev/null +++ b/pep-0445.txt @@ -0,0 +1,174 @@ +PEP: 445 +Title: Add new APIs to customize memory allocators +Version: $Revision$ +Last-Modified: $Date$ +Author: Victor Stinner +Status: Draft +Type: Standards Track +Content-Type: text/x-rst +Created: 15-june-2013 +Python-Version: 3.4 + +Abstract +======== + +Add new APIs to customize memory allocators + + +Rationale +========= + +Use cases: + +* Application embedding Python wanting to use a custom memory allocator + to allocate all Python memory somewhere else or with a different algorithm +* Python running on embedded devices with low memory and slow CPU. + A custom memory allocator may be required to use efficiently the memory + and/or to be able to use all memory of the device. +* Debug tool to track memory leaks +* Debug tool to detect buffer underflow, buffer overflow and misuse + of Python allocator APIs +* Debug tool to inject bugs, simulate out of memory for example + +API: + +* Setup a custom memory allocator for all memory allocated by Python +* Hook memory allocator functions to call extra code before and/or after + the underlying allocator function + + +Proposal +======== + +* Add a new ``PyMemAllocators`` structure + +* Add new GIL-free memory allocator functions: + + - ``PyMem_RawMalloc()`` + - ``PyMem_RawRealloc()`` + - ``PyMem_RawFree()`` + +* Add new functions to get and set memory allocators: + + - ``PyMem_GetRawAllocators()``, ``PyMem_SetRawAllocators()`` + - ``PyMem_GetAllocators()``, ``PyMem_SetAllocators()`` + - ``PyObject_GetAllocators()``, ``PyObject_SetAllocators()`` + - ``_PyObject_GetArenaAllocators()``, ``_PyObject_SetArenaAllocators()`` + +* Add a new function to setup debug hooks after memory allocators were + replaced: + + - ``PyMem_SetupDebugHooks()`` + +* ``PyObject_Malloc()`` now falls back on ``PyMem_Malloc()`` instead of + ``malloc()`` if size is bigger than ``SMALL_REQUEST_THRESHOLD``, and + ``PyObject_Realloc()`` falls back on ``PyMem_Realloc()`` instead of + ``realloc()`` + +* ``PyMem_Malloc()`` and ``PyMem_Realloc()`` now always call ``malloc()`` and + ``realloc()``, instead of calling ``PyObject_Malloc()`` and + ``PyObject_Realloc()`` in debug mode + + +Performances +============ + +The Python benchmarks suite (-b 2n3): some tests are 1.04x faster, some tests +are 1.04 slower, significant is between 115 and -191. I don't understand these +output, but I guess that the overhead cannot be seen with such test. + +pybench: "+0.1%" (diff between -4.9% and +5.6%). + +Full output attached to the issue #3329. + + +Alternatives +============ + +Only one get and one set function +--------------------------------- + +Replace the 6 functions: + +* ``PyMem_GetRawAllocators()`` +* ``PyMem_GetAllocators()`` +* ``PyObject_GetAllocators()`` +* ``PyMem_SetRawAllocators(allocators)`` +* ``PyMem_SetAllocators(allocators)`` +, ``PyObject_SetAllocators(allocators)`` + +with 2 functions with an additional *domain* argument: + +* ``PyMem_GetAllocators(domain)`` +* ``PyMem_SetAllocators(domain, allocators)`` + + +Setup Builtin Debug Hooks +------------------------- + +To be able to use Python debug functions (like ``_PyMem_DebugMalloc()``) even +when a custom memory allocator is set, an environment variable +``PYDEBUGMALLOC`` can be added to set these debug function hooks, instead of +the new function ``PyMem_SetupDebugHooks()``. + + +Use macros to get customizable allocators +----------------------------------------- + +To have no overhead in the default configuration, customizable allocators would +be an optional feature enabled by a configuration option or by macros. + + +Pass the C filename and line number +----------------------------------- + +Use C macros using ``__FILE__`` and ``__LINE__`` to get the C filename +and line number of a memory allocation. + + +No context argument +------------------- + +Simplify the signature of allocator functions, remove the context argument: + +* ``void* malloc(size_t size)`` +* ``void* realloc(void *ptr, size_t new_size)`` +* ``void free(void *ptr)`` + +The context is a convinient way to reuse the same allocator for different APIs +(ex: PyMem and PyObject). + + +PyMem_Malloc() GIL-free +----------------------- + +There is no real reason to require the GIL when calling PyMem_Malloc(). + + +CCP API +------- + +XXX To be done (Kristj?n Valur J?nsson) XXX + + +Links +===== + +Memory allocators: + +* `Issue #3329: Add new APIs to customize memory allocators + `_ +* `pytracemalloc + `_ +* `Meliae: Python Memory Usage Analyzer + `_ +* `Guppy-PE: umbrella package combining Heapy and GSL + `_ +* `PySizer (developed for Python 2.4) + `_ + +Other: + +* `Python benchmark suite + `_ + -- Repository URL: http://hg.python.org/peps From python-checkins at python.org Sat Jun 15 04:25:53 2013 From: python-checkins at python.org (victor.stinner) Date: Sat, 15 Jun 2013 04:25:53 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?peps=3A_PEP_445=3A_fix_typo?= Message-ID: <3bXMxF64sCz7Ljw@mail.python.org> http://hg.python.org/peps/rev/2b7d3c9206b5 changeset: 4926:2b7d3c9206b5 user: Victor Stinner date: Sat Jun 15 04:25:42 2013 +0200 summary: PEP 445: fix typo files: pep-0445.txt | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/pep-0445.txt b/pep-0445.txt --- a/pep-0445.txt +++ b/pep-0445.txt @@ -95,7 +95,7 @@ * ``PyObject_GetAllocators()`` * ``PyMem_SetRawAllocators(allocators)`` * ``PyMem_SetAllocators(allocators)`` -, ``PyObject_SetAllocators(allocators)`` +* ``PyObject_SetAllocators(allocators)`` with 2 functions with an additional *domain* argument: -- Repository URL: http://hg.python.org/peps From python-checkins at python.org Sat Jun 15 04:26:39 2013 From: python-checkins at python.org (brett.cannon) Date: Sat, 15 Jun 2013 04:26:39 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2317907=3A_touch_up?= =?utf-8?q?_the_code_for_imp=2Enew=5Fmodule=28=29=2E?= Message-ID: <3bXMy76LPDzMwL@mail.python.org> http://hg.python.org/cpython/rev/9cacdb9d0c59 changeset: 84134:9cacdb9d0c59 user: Brett Cannon date: Fri Jun 14 22:26:30 2013 -0400 summary: Issue #17907: touch up the code for imp.new_module(). files: Doc/library/imp.rst | 6 + Doc/library/importlib.rst | 30 + Lib/imp.py | 13 +- Lib/importlib/_bootstrap.py | 11 +- Lib/importlib/util.py | 2 + Lib/test/test_import.py | 1 - Lib/test/test_importlib/test_util.py | 116 + Python/importlib.h | 7077 ++++++------- 8 files changed, 3699 insertions(+), 3557 deletions(-) diff --git a/Doc/library/imp.rst b/Doc/library/imp.rst --- a/Doc/library/imp.rst +++ b/Doc/library/imp.rst @@ -205,6 +205,9 @@ If :attr:`sys.implementation.cache_tag` is ``None``, then :exc:`NotImplementedError` is raised. + .. deprecated:: 3.4 + Use :func:`importlib.util.cache_from_source` instead. + .. function:: source_from_cache(path) @@ -220,6 +223,9 @@ Raise :exc:`NotImplementedError` when :attr:`sys.implementation.cache_tag` is not defined. + .. deprecated:: 3.4 + Use :func:`importlib.util.source_from_cache` instead. + .. function:: get_tag() diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst --- a/Doc/library/importlib.rst +++ b/Doc/library/importlib.rst @@ -886,6 +886,36 @@ .. versionadded:: 3.4 +.. function:: cache_from_source(path, debug_override=None) + + Return the :pep:`3147` path to the byte-compiled file associated with the + source *path*. For example, if *path* is ``/foo/bar/baz.py`` the return + value would be ``/foo/bar/__pycache__/baz.cpython-32.pyc`` for Python 3.2. + The ``cpython-32`` string comes from the current magic tag (see + :func:`get_tag`; if :attr:`sys.implementation.cache_tag` is not defined then + :exc:`NotImplementedError` will be raised). The returned path will end in + ``.pyc`` when ``__debug__`` is True or ``.pyo`` for an optimized Python + (i.e. ``__debug__`` is False). By passing in True or False for + *debug_override* you can override the system's value for ``__debug__`` for + extension selection. + + *path* need not exist. + + .. versionadded:: 3.4 + + +.. function:: source_from_cache(path) + + Given the *path* to a :pep:`3147` file name, return the associated source code + file path. For example, if *path* is + ``/foo/bar/__pycache__/baz.cpython-32.pyc`` the returned path would be + ``/foo/bar/baz.py``. *path* need not exist, however if it does not conform + to :pep:`3147` format, a ``ValueError`` is raised. If + :attr:`sys.implementation.cache_tag` is not defined, + :exc:`NotImplementedError` is raised. + + .. versionadded:: 3.4 + .. function:: resolve_name(name, package) Resolve a relative module name to an absolute one. diff --git a/Lib/imp.py b/Lib/imp.py --- a/Lib/imp.py +++ b/Lib/imp.py @@ -17,7 +17,6 @@ load_dynamic = None # Directly exposed by this module -from importlib._bootstrap import new_module from importlib._bootstrap import cache_from_source, source_from_cache @@ -28,6 +27,7 @@ import os import sys import tokenize +import types import warnings @@ -44,6 +44,17 @@ IMP_HOOK = 9 +def new_module(name): + """**DEPRECATED** + + Create a new module. + + The module is not entered into sys.modules. + + """ + return types.ModuleType(name) + + def get_magic(): """**DEPRECATED** diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py --- a/Lib/importlib/_bootstrap.py +++ b/Lib/importlib/_bootstrap.py @@ -121,15 +121,6 @@ _code_type = type(_wrap.__code__) -def new_module(name): - """Create a new module. - - The module is not entered into sys.modules. - - """ - return type(_io)(name) - - # Module-level locking ######################################################## # A dict mapping module names to weakrefs of _ModuleLock instances @@ -509,7 +500,7 @@ # This must be done before open() is called as the 'io' module # implicitly imports 'locale' and would otherwise trigger an # infinite loop. - self._module = new_module(self._name) + self._module = type(_io)(self._name) # This must be done before putting the module in sys.modules # (otherwise an optimization shortcut in import.c becomes wrong) self._module.__initializing__ = True diff --git a/Lib/importlib/util.py b/Lib/importlib/util.py --- a/Lib/importlib/util.py +++ b/Lib/importlib/util.py @@ -1,9 +1,11 @@ """Utility code for constructing importers, etc.""" from ._bootstrap import MAGIC_NUMBER +from ._bootstrap import cache_from_source from ._bootstrap import module_to_load from ._bootstrap import set_loader from ._bootstrap import set_package +from ._bootstrap import source_from_cache from ._bootstrap import _resolve_name import functools diff --git a/Lib/test/test_import.py b/Lib/test/test_import.py --- a/Lib/test/test_import.py +++ b/Lib/test/test_import.py @@ -859,7 +859,6 @@ from importlib import machinery mod = sys.modules['_frozen_importlib'] self.assertIs(machinery.FileFinder, mod.FileFinder) - self.assertIs(imp.new_module, mod.new_module) class ImportTracebackTests(unittest.TestCase): diff --git a/Lib/test/test_importlib/test_util.py b/Lib/test/test_importlib/test_util.py --- a/Lib/test/test_importlib/test_util.py +++ b/Lib/test/test_importlib/test_util.py @@ -1,6 +1,7 @@ from importlib import util from . import util as test_util import imp +import os import sys from test import support import types @@ -324,5 +325,120 @@ self.assertTrue(util.MAGIC_NUMBER.endswith(b'\r\n')) +class PEP3147Tests(unittest.TestCase): + """Tests of PEP 3147-related functions: + cache_from_source and source_from_cache. + + """ + + tag = imp.get_tag() + + @unittest.skipUnless(sys.implementation.cache_tag is not None, + 'requires sys.implementation.cache_tag not be None') + def test_cache_from_source(self): + # Given the path to a .py file, return the path to its PEP 3147 + # defined .pyc file (i.e. under __pycache__). + path = os.path.join('foo', 'bar', 'baz', 'qux.py') + expect = os.path.join('foo', 'bar', 'baz', '__pycache__', + 'qux.{}.pyc'.format(self.tag)) + self.assertEqual(util.cache_from_source(path, True), expect) + + def test_cache_from_source_no_cache_tag(self): + # No cache tag means NotImplementedError. + with support.swap_attr(sys.implementation, 'cache_tag', None): + with self.assertRaises(NotImplementedError): + util.cache_from_source('whatever.py') + + def test_cache_from_source_no_dot(self): + # Directory with a dot, filename without dot. + path = os.path.join('foo.bar', 'file') + expect = os.path.join('foo.bar', '__pycache__', + 'file{}.pyc'.format(self.tag)) + self.assertEqual(util.cache_from_source(path, True), expect) + + def test_cache_from_source_optimized(self): + # Given the path to a .py file, return the path to its PEP 3147 + # defined .pyo file (i.e. under __pycache__). + path = os.path.join('foo', 'bar', 'baz', 'qux.py') + expect = os.path.join('foo', 'bar', 'baz', '__pycache__', + 'qux.{}.pyo'.format(self.tag)) + self.assertEqual(util.cache_from_source(path, False), expect) + + def test_cache_from_source_cwd(self): + path = 'foo.py' + expect = os.path.join('__pycache__', 'foo.{}.pyc'.format(self.tag)) + self.assertEqual(util.cache_from_source(path, True), expect) + + def test_cache_from_source_override(self): + # When debug_override is not None, it can be any true-ish or false-ish + # value. + path = os.path.join('foo', 'bar', 'baz.py') + partial_expect = os.path.join('foo', 'bar', '__pycache__', + 'baz.{}.py'.format(self.tag)) + self.assertEqual(util.cache_from_source(path, []), partial_expect + 'o') + self.assertEqual(util.cache_from_source(path, [17]), + partial_expect + 'c') + # However if the bool-ishness can't be determined, the exception + # propagates. + class Bearish: + def __bool__(self): raise RuntimeError + with self.assertRaises(RuntimeError): + util.cache_from_source('/foo/bar/baz.py', Bearish()) + + @unittest.skipUnless(os.sep == '\\' and os.altsep == '/', + 'test meaningful only where os.altsep is defined') + def test_sep_altsep_and_sep_cache_from_source(self): + # Windows path and PEP 3147 where sep is right of altsep. + self.assertEqual( + util.cache_from_source('\\foo\\bar\\baz/qux.py', True), + '\\foo\\bar\\baz\\__pycache__\\qux.{}.pyc'.format(self.tag)) + + @unittest.skipUnless(sys.implementation.cache_tag is not None, + 'requires sys.implementation.cache_tag to not be ' + 'None') + def test_source_from_cache(self): + # Given the path to a PEP 3147 defined .pyc file, return the path to + # its source. This tests the good path. + path = os.path.join('foo', 'bar', 'baz', '__pycache__', + 'qux.{}.pyc'.format(self.tag)) + expect = os.path.join('foo', 'bar', 'baz', 'qux.py') + self.assertEqual(util.source_from_cache(path), expect) + + def test_source_from_cache_no_cache_tag(self): + # If sys.implementation.cache_tag is None, raise NotImplementedError. + path = os.path.join('blah', '__pycache__', 'whatever.pyc') + with support.swap_attr(sys.implementation, 'cache_tag', None): + with self.assertRaises(NotImplementedError): + util.source_from_cache(path) + + def test_source_from_cache_bad_path(self): + # When the path to a pyc file is not in PEP 3147 format, a ValueError + # is raised. + self.assertRaises( + ValueError, util.source_from_cache, '/foo/bar/bazqux.pyc') + + def test_source_from_cache_no_slash(self): + # No slashes at all in path -> ValueError + self.assertRaises( + ValueError, util.source_from_cache, 'foo.cpython-32.pyc') + + def test_source_from_cache_too_few_dots(self): + # Too few dots in final path component -> ValueError + self.assertRaises( + ValueError, util.source_from_cache, '__pycache__/foo.pyc') + + def test_source_from_cache_too_many_dots(self): + # Too many dots in final path component -> ValueError + self.assertRaises( + ValueError, util.source_from_cache, + '__pycache__/foo.cpython-32.foo.pyc') + + def test_source_from_cache_no__pycache__(self): + # Another problem with the path -> ValueError + self.assertRaises( + ValueError, util.source_from_cache, + '/foo/bar/foo.cpython-32.foo.pyc') + + if __name__ == '__main__': unittest.main() diff --git a/Python/importlib.h b/Python/importlib.h --- a/Python/importlib.h +++ b/Python/importlib.h [stripped] -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 15 04:30:07 2013 From: python-checkins at python.org (brett.cannon) Date: Sat, 15 Jun 2013 04:30:07 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Make_it_more_obvious_what_?= =?utf-8?q?things_used_in_imp_are_snuck_in_through_private_APIs?= Message-ID: <3bXN274XGGzPlS@mail.python.org> http://hg.python.org/cpython/rev/7bb54aa762e1 changeset: 84135:7bb54aa762e1 user: Brett Cannon date: Fri Jun 14 22:29:58 2013 -0400 summary: Make it more obvious what things used in imp are snuck in through private APIs files: Lib/imp.py | 19 ++++++++----------- 1 files changed, 8 insertions(+), 11 deletions(-) diff --git a/Lib/imp.py b/Lib/imp.py --- a/Lib/imp.py +++ b/Lib/imp.py @@ -16,11 +16,9 @@ # Platform doesn't support dynamic loading. load_dynamic = None -# Directly exposed by this module -from importlib._bootstrap import cache_from_source, source_from_cache +from importlib._bootstrap import (cache_from_source, source_from_cache, + SourcelessFileLoader, _ERR_MSG) - -from importlib import _bootstrap from importlib import machinery from importlib import util import importlib @@ -117,7 +115,7 @@ return super().get_data(path) -class _LoadSourceCompatibility(_HackedGetData, _bootstrap.SourceFileLoader): +class _LoadSourceCompatibility(_HackedGetData, machinery.SourceFileLoader): """Compatibility support for implementing load_source().""" @@ -131,12 +129,11 @@ module = sys.modules[name] # To allow reloading to potentially work, use a non-hacked loader which # won't rely on a now-closed file object. - module.__loader__ = _bootstrap.SourceFileLoader(name, pathname) + module.__loader__ = machinery.SourceFileLoader(name, pathname) return module -class _LoadCompiledCompatibility(_HackedGetData, - _bootstrap.SourcelessFileLoader): +class _LoadCompiledCompatibility(_HackedGetData, SourcelessFileLoader): """Compatibility support for implementing load_compiled().""" @@ -150,7 +147,7 @@ module = sys.modules[name] # To allow reloading to potentially work, use a non-hacked loader which # won't rely on a now-closed file object. - module.__loader__ = _bootstrap.SourcelessFileLoader(name, pathname) + module.__loader__ = SourcelessFileLoader(name, pathname) return module @@ -168,7 +165,7 @@ break else: raise ValueError('{!r} is not a package'.format(path)) - return _bootstrap.SourceFileLoader(name, path).load_module(name) + return machinery.SourceFileLoader(name, path).load_module(name) def load_module(name, file, filename, details): @@ -252,7 +249,7 @@ continue break # Break out of outer loop when breaking out of inner loop. else: - raise ImportError(_bootstrap._ERR_MSG.format(name), name=name) + raise ImportError(_ERR_MSG.format(name), name=name) encoding = None if mode == 'U': -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 15 04:35:49 2013 From: python-checkins at python.org (brett.cannon) Date: Sat, 15 Jun 2013 04:35:49 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2318194=3A_Introduc?= =?utf-8?q?e_importlib=2Eutil=2Ecache=5Ffrom=5Fsource=28=29_and?= Message-ID: <3bXN8j0xzqzQHY@mail.python.org> http://hg.python.org/cpython/rev/32067804942e changeset: 84136:32067804942e user: Brett Cannon date: Fri Jun 14 22:35:40 2013 -0400 summary: Issue #18194: Introduce importlib.util.cache_from_source() and source_from_cache(), finishing the work introduced in changset 4134:9cacdb9d0c59. files: Lib/imp.py | 35 +++++++++++++++++++++++++++++++++-- Misc/NEWS | 4 ++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/Lib/imp.py b/Lib/imp.py --- a/Lib/imp.py +++ b/Lib/imp.py @@ -16,8 +16,7 @@ # Platform doesn't support dynamic loading. load_dynamic = None -from importlib._bootstrap import (cache_from_source, source_from_cache, - SourcelessFileLoader, _ERR_MSG) +from importlib._bootstrap import SourcelessFileLoader, _ERR_MSG from importlib import machinery from importlib import util @@ -66,6 +65,38 @@ return sys.implementation.cache_tag +def cache_from_source(path, debug_override=None): + """**DEPRECATED** + + Given the path to a .py file, return the path to its .pyc/.pyo file. + + The .py file does not need to exist; this simply returns the path to the + .pyc/.pyo file calculated as if the .py file were imported. The extension + will be .pyc unless sys.flags.optimize is non-zero, then it will be .pyo. + + If debug_override is not None, then it must be a boolean and is used in + place of sys.flags.optimize. + + If sys.implementation.cache_tag is None then NotImplementedError is raised. + + """ + return util.cache_from_source(path, debug_override) + + +def source_from_cache(path): + """**DEPRECATED** + + Given the path to a .pyc./.pyo file, return the path to its .py file. + + The .pyc/.pyo file does not need to exist; this simply returns the path to + the .py file calculated to correspond to the .pyc/.pyo file. If path does + not conform to PEP 3147 format, ValueError will be raised. If + sys.implementation.cache_tag is None then NotImplementedError is raised. + + """ + return util.source_from_cache(path) + + def get_suffixes(): warnings.warn('imp.get_suffixes() is deprecated; use the constants ' 'defined on importlib.machinery instead', diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -123,6 +123,10 @@ Library ------- +- Issue #18194: Introduce importlib.util.cache_from_source() and + source_from_cache() while documenting the equivalent functions in imp as + deprecated. + - Issue #17907: Document imp.new_module() as deprecated in favour of types.ModuleType. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 15 04:37:18 2013 From: python-checkins at python.org (brett.cannon) Date: Sat, 15 Jun 2013 04:37:18 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Move_something_to_the_righ?= =?utf-8?q?t_section_of_What=27s_New?= Message-ID: <3bXNBQ2DGNzPtN@mail.python.org> http://hg.python.org/cpython/rev/68f8f9d0cebf changeset: 84137:68f8f9d0cebf user: Brett Cannon date: Fri Jun 14 22:37:11 2013 -0400 summary: Move something to the right section of What's New files: Doc/whatsnew/3.4.rst | 10 ++++++---- 1 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Doc/whatsnew/3.4.rst b/Doc/whatsnew/3.4.rst --- a/Doc/whatsnew/3.4.rst +++ b/Doc/whatsnew/3.4.rst @@ -225,6 +225,11 @@ :meth:`difflib.SequenceMatcher.isbpopulur`: use ``x in sm.bjunk`` and ``x in sm.bpopular``, where sm is a :class:`~difflib.SequenceMatcher` object. +* :func:`importlib.util.module_for_loader` is pending deprecation. Using + :func:`importlib.util.module_to_load` and + :meth:`importlib.abc.Loader.init_module_attrs` allows subclasses of a loader + to more easily customize module loading. + Deprecated functions and types of the C API ------------------------------------------- @@ -235,10 +240,7 @@ Deprecated features ------------------- -* :func:`importlib.util.module_for_loader` is pending deprecation. Using - :func:`importlib.util.module_to_load` and - :meth:`importlib.abc.Loader.init_module_attrs` allows subclasses of a loader - to more easily customize module loading. +* None yet. Porting to Python 3.4 -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 15 04:49:10 2013 From: python-checkins at python.org (brett.cannon) Date: Sat, 15 Jun 2013 04:49:10 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2317177=3A_Update_t?= =?utf-8?q?he_programming_FAQ_to_use_importlib?= Message-ID: <3bXNS60zCYzQHY@mail.python.org> http://hg.python.org/cpython/rev/3d3b9d456eb8 changeset: 84138:3d3b9d456eb8 user: Brett Cannon date: Fri Jun 14 22:49:00 2013 -0400 summary: Issue #17177: Update the programming FAQ to use importlib files: Doc/faq/programming.rst | 10 +++++----- 1 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Doc/faq/programming.rst b/Doc/faq/programming.rst --- a/Doc/faq/programming.rst +++ b/Doc/faq/programming.rst @@ -1738,12 +1738,12 @@ For reasons of efficiency as well as consistency, Python only reads the module file on the first time a module is imported. If it didn't, in a program consisting of many modules where each one imports the same basic module, the -basic module would be parsed and re-parsed many times. To force rereading of a +basic module would be parsed and re-parsed many times. To force re-reading of a changed module, do this:: - import imp + import importlib import modname - imp.reload(modname) + importlib.reload(modname) Warning: this technique is not 100% fool-proof. In particular, modules containing statements like :: @@ -1755,10 +1755,10 @@ updated to use the new class definition. This can result in the following paradoxical behaviour: - >>> import imp + >>> import importlib >>> import cls >>> c = cls.C() # Create an instance of C - >>> imp.reload(cls) + >>> importlib.reload(cls) >>> isinstance(c, cls.C) # isinstance is false?!? False -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 15 04:51:10 2013 From: python-checkins at python.org (brett.cannon) Date: Sat, 15 Jun 2013 04:51:10 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_make_test_more_robust_unde?= =?utf-8?q?r_Windows?= Message-ID: <3bXNVQ5WqBzQ1G@mail.python.org> http://hg.python.org/cpython/rev/54ad15b3cb6a changeset: 84139:54ad15b3cb6a user: Brett Cannon date: Fri Jun 14 22:50:57 2013 -0400 summary: make test more robust under Windows files: Lib/test/test_py_compile.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Lib/test/test_py_compile.py b/Lib/test/test_py_compile.py --- a/Lib/test/test_py_compile.py +++ b/Lib/test/test_py_compile.py @@ -41,7 +41,7 @@ # Issue #17222 try: os.symlink(self.pyc_path + '.actual', self.pyc_path) - except OSError: + except (NotImplementedError, OSError): self.skipTest('need to be able to create a symlink for a file') else: assert os.path.islink(self.pyc_path) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 15 05:04:12 2013 From: python-checkins at python.org (brett.cannon) Date: Sat, 15 Jun 2013 05:04:12 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2317177=3A_stop_usi?= =?utf-8?q?ng_imp_for_compileall=2E?= Message-ID: <3bXNnS4HQzzQr3@mail.python.org> http://hg.python.org/cpython/rev/cc27d50bd91a changeset: 84140:cc27d50bd91a user: Brett Cannon date: Fri Jun 14 23:04:02 2013 -0400 summary: Issue #17177: stop using imp for compileall. files: Lib/compileall.py | 11 +++-- Lib/test/test_compileall.py | 50 +++++++++++------------- 2 files changed, 29 insertions(+), 32 deletions(-) diff --git a/Lib/compileall.py b/Lib/compileall.py --- a/Lib/compileall.py +++ b/Lib/compileall.py @@ -13,7 +13,7 @@ import os import sys import errno -import imp +import importlib.util import py_compile import struct @@ -91,17 +91,18 @@ cfile = fullname + ('c' if __debug__ else 'o') else: if optimize >= 0: - cfile = imp.cache_from_source(fullname, - debug_override=not optimize) + cfile = importlib.util.cache_from_source( + fullname, debug_override=not optimize) else: - cfile = imp.cache_from_source(fullname) + cfile = importlib.util.cache_from_source(fullname) cache_dir = os.path.dirname(cfile) head, tail = name[:-3], name[-3:] if tail == '.py': if not force: try: mtime = int(os.stat(fullname).st_mtime) - expect = struct.pack('<4sl', imp.get_magic(), mtime) + expect = struct.pack('<4sl', importlib.util.MAGIC_NUMBER, + mtime) with open(cfile, 'rb') as chandle: actual = chandle.read(8) if expect == actual: diff --git a/Lib/test/test_compileall.py b/Lib/test/test_compileall.py --- a/Lib/test/test_compileall.py +++ b/Lib/test/test_compileall.py @@ -1,11 +1,12 @@ import sys import compileall -import imp +import importlib.util import os import py_compile import shutil import struct import subprocess +import sys import tempfile import time import unittest @@ -18,11 +19,11 @@ def setUp(self): self.directory = tempfile.mkdtemp() self.source_path = os.path.join(self.directory, '_test.py') - self.bc_path = imp.cache_from_source(self.source_path) + self.bc_path = importlib.util.cache_from_source(self.source_path) with open(self.source_path, 'w') as file: file.write('x = 123\n') self.source_path2 = os.path.join(self.directory, '_test2.py') - self.bc_path2 = imp.cache_from_source(self.source_path2) + self.bc_path2 = importlib.util.cache_from_source(self.source_path2) shutil.copyfile(self.source_path, self.source_path2) self.subdirectory = os.path.join(self.directory, '_subdir') os.mkdir(self.subdirectory) @@ -36,7 +37,7 @@ with open(self.bc_path, 'rb') as file: data = file.read(8) mtime = int(os.stat(self.source_path).st_mtime) - compare = struct.pack('<4sl', imp.get_magic(), mtime) + compare = struct.pack('<4sl', importlib.util.MAGIC_NUMBER, mtime) return data, compare def recreation_check(self, metadata): @@ -57,7 +58,8 @@ def test_mtime(self): # Test a change in mtime leads to a new .pyc. - self.recreation_check(struct.pack('<4sl', imp.get_magic(), 1)) + self.recreation_check(struct.pack('<4sl', importlib.util.MAGIC_NUMBER, + 1)) def test_magic_number(self): # Test a change in mtime leads to a new .pyc. @@ -97,14 +99,14 @@ # interpreter's creates the correct file names optimize = 1 if __debug__ else 0 compileall.compile_dir(self.directory, quiet=True, optimize=optimize) - cached = imp.cache_from_source(self.source_path, - debug_override=not optimize) + cached = importlib.util.cache_from_source(self.source_path, + debug_override=not optimize) self.assertTrue(os.path.isfile(cached)) - cached2 = imp.cache_from_source(self.source_path2, - debug_override=not optimize) + cached2 = importlib.util.cache_from_source(self.source_path2, + debug_override=not optimize) self.assertTrue(os.path.isfile(cached2)) - cached3 = imp.cache_from_source(self.source_path3, - debug_override=not optimize) + cached3 = importlib.util.cache_from_source(self.source_path3, + debug_override=not optimize) self.assertTrue(os.path.isfile(cached3)) @@ -152,10 +154,12 @@ return rc, out, err def assertCompiled(self, fn): - self.assertTrue(os.path.exists(imp.cache_from_source(fn))) + path = importlib.util.cache_from_source(fn) + self.assertTrue(os.path.exists(path)) def assertNotCompiled(self, fn): - self.assertFalse(os.path.exists(imp.cache_from_source(fn))) + path = importlib.util.cache_from_source(fn) + self.assertFalse(os.path.exists(path)) def setUp(self): self.addCleanup(self._cleanup) @@ -190,8 +194,8 @@ ['-m', 'compileall', '-q', self.pkgdir])) # Verify the __pycache__ directory contents. self.assertTrue(os.path.exists(self.pkgdir_cachedir)) - expected = sorted(base.format(imp.get_tag(), ext) for base in - ('__init__.{}.{}', 'bar.{}.{}')) + expected = sorted(base.format(sys.implementation.cache_tag, ext) + for base in ('__init__.{}.{}', 'bar.{}.{}')) self.assertEqual(sorted(os.listdir(self.pkgdir_cachedir)), expected) # Make sure there are no .pyc files in the source directory. self.assertFalse([fn for fn in os.listdir(self.pkgdir) @@ -224,7 +228,7 @@ def test_force(self): self.assertRunOK('-q', self.pkgdir) - pycpath = imp.cache_from_source(self.barfn) + pycpath = importlib.util.cache_from_source(self.barfn) # set atime/mtime backward to avoid file timestamp resolution issues os.utime(pycpath, (time.time()-60,)*2) mtime = os.stat(pycpath).st_mtime @@ -288,7 +292,7 @@ bazfn = script_helper.make_script(self.pkgdir, 'baz', 'raise Exception') self.assertRunOK('-q', '-d', 'dinsdale', self.pkgdir) fn = script_helper.make_script(self.pkgdir, 'bing', 'import baz') - pyc = imp.cache_from_source(bazfn) + pyc = importlib.util.cache_from_source(bazfn) os.rename(pyc, os.path.join(self.pkgdir, 'baz.pyc')) os.remove(bazfn) rc, out, err = script_helper.assert_python_failure(fn) @@ -299,7 +303,7 @@ '-i', os.path.join(self.directory, 'nosuchfile'), self.pkgdir) self.assertRegex(out, b'rror.*nosuchfile') self.assertNotRegex(err, b'Traceback') - self.assertFalse(os.path.exists(imp.cache_from_source( + self.assertFalse(os.path.exists(importlib.util.cache_from_source( self.pkgdir_cachedir))) def test_include_file_with_arg(self): @@ -356,13 +360,5 @@ self.assertRegex(out, b"Can't list 'badfilename'") -def test_main(): - support.run_unittest( - CommandLineTests, - CompileallTests, - EncodingTest, - ) - - if __name__ == "__main__": - test_main() + unittest.main() -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Sat Jun 15 05:54:45 2013 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Sat, 15 Jun 2013 05:54:45 +0200 Subject: [Python-checkins] Daily reference leaks (e7a01c7f69fe): sum=5 Message-ID: results for e7a01c7f69fe on branch "default" -------------------------------------------- test_support leaked [1, 0, 0] references, sum=1 test_support leaked [1, 2, 1] memory blocks, sum=4 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogtG_3Ow', '-x'] From python-checkins at python.org Sat Jun 15 07:31:09 2013 From: python-checkins at python.org (ethan.furman) Date: Sat, 15 Jun 2013 07:31:09 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?peps=3A_Marked_PEP-0435_as_Final=2E?= Message-ID: <3bXS311vPRz7Ljw@mail.python.org> http://hg.python.org/peps/rev/09ac124d966a changeset: 4927:09ac124d966a user: Ethan Furman date: Fri Jun 14 22:31:01 2013 -0700 summary: Marked PEP-0435 as Final. files: pep-0435.txt | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/pep-0435.txt b/pep-0435.txt --- a/pep-0435.txt +++ b/pep-0435.txt @@ -5,7 +5,7 @@ Author: Barry Warsaw , Eli Bendersky , Ethan Furman -Status: Accepted +Status: Final Type: Standards Track Content-Type: text/x-rst Created: 2013-02-23 -- Repository URL: http://hg.python.org/peps From ethan at stoneleaf.us Sat Jun 15 07:33:10 2013 From: ethan at stoneleaf.us (Ethan Furman) Date: Fri, 14 Jun 2013 22:33:10 -0700 Subject: [Python-checkins] cpython: Closes issue 17947. Adds PEP-0435 (Enum, IntEnum) to the stdlib. In-Reply-To: References: <3bWtm22z9dz7Ljj@mail.python.org> Message-ID: <51BBFC96.1000509@stoneleaf.us> On 06/14/2013 03:57 PM, Brett Cannon wrote: > Ethan, did you forget to run ``hg add`` before committing? If not then why the heck did we argue over enums for so long > if this was all it took to make everyone happy? =) Oops. ;) -- ~Ethan~ From python-checkins at python.org Sat Jun 15 16:52:18 2013 From: python-checkins at python.org (lukasz.langa) Date: Sat, 15 Jun 2013 16:52:18 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?peps=3A_Marked_PEP_443_as_Final=2E?= Message-ID: <3bXhVV2sxZzNs3@mail.python.org> http://hg.python.org/peps/rev/61c0d5961dfd changeset: 4928:61c0d5961dfd user: ?ukasz Langa date: Sat Jun 15 16:52:08 2013 +0200 summary: Marked PEP 443 as Final. files: pep-0443.txt | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/pep-0443.txt b/pep-0443.txt --- a/pep-0443.txt +++ b/pep-0443.txt @@ -4,7 +4,7 @@ Last-Modified: $Date$ Author: ?ukasz Langa Discussions-To: Python-Dev -Status: Accepted +Status: Final Type: Standards Track Content-Type: text/x-rst Created: 22-May-2013 -- Repository URL: http://hg.python.org/peps From python-checkins at python.org Sat Jun 15 18:28:33 2013 From: python-checkins at python.org (brett.cannon) Date: Sat, 15 Jun 2013 18:28:33 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?peps=3A_fix_spelling?= Message-ID: <3bXkdY6PdLzPTs@mail.python.org> http://hg.python.org/peps/rev/957c02c37244 changeset: 4929:957c02c37244 user: Brett Cannon date: Sat Jun 15 12:28:29 2013 -0400 summary: fix spelling files: pep-0445.txt | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/pep-0445.txt b/pep-0445.txt --- a/pep-0445.txt +++ b/pep-0445.txt @@ -135,7 +135,7 @@ * ``void* realloc(void *ptr, size_t new_size)`` * ``void free(void *ptr)`` -The context is a convinient way to reuse the same allocator for different APIs +The context is a convenient way to reuse the same allocator for different APIs (ex: PyMem and PyObject). -- Repository URL: http://hg.python.org/peps From python-checkins at python.org Sat Jun 15 19:00:02 2013 From: python-checkins at python.org (brett.cannon) Date: Sat, 15 Jun 2013 19:00:02 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2317177=3A_Stop_usi?= =?utf-8?q?ng_imp_in_distutils?= Message-ID: <3bXlKt6DDqzPTs@mail.python.org> http://hg.python.org/cpython/rev/30aa032c4bd0 changeset: 84141:30aa032c4bd0 user: Brett Cannon date: Sat Jun 15 12:59:53 2013 -0400 summary: Issue #17177: Stop using imp in distutils files: Lib/distutils/command/build_py.py | 10 +++++----- Lib/distutils/command/install_lib.py | 6 +++--- Lib/distutils/tests/test_bdist_dumb.py | 3 +-- Lib/distutils/tests/test_build_py.py | 10 ++++++---- Lib/distutils/tests/test_install.py | 4 ++-- Lib/distutils/tests/test_install_lib.py | 8 +++++--- Lib/distutils/util.py | 7 ++++--- 7 files changed, 26 insertions(+), 22 deletions(-) diff --git a/Lib/distutils/command/build_py.py b/Lib/distutils/command/build_py.py --- a/Lib/distutils/command/build_py.py +++ b/Lib/distutils/command/build_py.py @@ -3,7 +3,7 @@ Implements the Distutils 'build_py' command.""" import os -import imp +import importlib.util import sys from glob import glob @@ -312,11 +312,11 @@ outputs.append(filename) if include_bytecode: if self.compile: - outputs.append(imp.cache_from_source(filename, - debug_override=True)) + outputs.append(importlib.util.cache_from_source( + filename, debug_override=True)) if self.optimize > 0: - outputs.append(imp.cache_from_source(filename, - debug_override=False)) + outputs.append(importlib.util.cache_from_source( + filename, debug_override=False)) outputs += [ os.path.join(build_dir, filename) diff --git a/Lib/distutils/command/install_lib.py b/Lib/distutils/command/install_lib.py --- a/Lib/distutils/command/install_lib.py +++ b/Lib/distutils/command/install_lib.py @@ -4,7 +4,7 @@ (install all Python modules).""" import os -import imp +import importlib.util import sys from distutils.core import Command @@ -165,10 +165,10 @@ if ext != PYTHON_SOURCE_EXTENSION: continue if self.compile: - bytecode_files.append(imp.cache_from_source( + bytecode_files.append(importlib.util.cache_from_source( py_file, debug_override=True)) if self.optimize > 0: - bytecode_files.append(imp.cache_from_source( + bytecode_files.append(importlib.util.cache_from_source( py_file, debug_override=False)) return bytecode_files diff --git a/Lib/distutils/tests/test_bdist_dumb.py b/Lib/distutils/tests/test_bdist_dumb.py --- a/Lib/distutils/tests/test_bdist_dumb.py +++ b/Lib/distutils/tests/test_bdist_dumb.py @@ -1,7 +1,6 @@ """Tests for distutils.command.bdist_dumb.""" import os -import imp import sys import zipfile import unittest @@ -88,7 +87,7 @@ contents = sorted(os.path.basename(fn) for fn in contents) wanted = ['foo-0.1-py%s.%s.egg-info' % sys.version_info[:2], 'foo.py'] if not sys.dont_write_bytecode: - wanted.append('foo.%s.pyc' % imp.get_tag()) + wanted.append('foo.%s.pyc' % sys.implementation.cache_tag) self.assertEqual(contents, sorted(wanted)) def test_suite(): diff --git a/Lib/distutils/tests/test_build_py.py b/Lib/distutils/tests/test_build_py.py --- a/Lib/distutils/tests/test_build_py.py +++ b/Lib/distutils/tests/test_build_py.py @@ -2,7 +2,6 @@ import os import sys -import imp import unittest from distutils.command.build_py import build_py @@ -63,7 +62,8 @@ self.assertFalse(os.path.exists(pycache_dir)) else: pyc_files = os.listdir(pycache_dir) - self.assertIn("__init__.%s.pyc" % imp.get_tag(), pyc_files) + self.assertIn("__init__.%s.pyc" % sys.implementation.cache_tag, + pyc_files) def test_empty_package_dir(self): # See bugs #1668596/#1720897 @@ -102,7 +102,8 @@ found = os.listdir(cmd.build_lib) self.assertEqual(sorted(found), ['__pycache__', 'boiledeggs.py']) found = os.listdir(os.path.join(cmd.build_lib, '__pycache__')) - self.assertEqual(found, ['boiledeggs.%s.pyc' % imp.get_tag()]) + self.assertEqual(found, + ['boiledeggs.%s.pyc' % sys.implementation.cache_tag]) @unittest.skipIf(sys.dont_write_bytecode, 'byte-compile disabled') def test_byte_compile_optimized(self): @@ -119,7 +120,8 @@ found = os.listdir(cmd.build_lib) self.assertEqual(sorted(found), ['__pycache__', 'boiledeggs.py']) found = os.listdir(os.path.join(cmd.build_lib, '__pycache__')) - self.assertEqual(sorted(found), ['boiledeggs.%s.pyo' % imp.get_tag()]) + self.assertEqual(sorted(found), + ['boiledeggs.%s.pyo' % sys.implementation.cache_tag]) def test_dont_write_bytecode(self): # makes sure byte_compile is not used diff --git a/Lib/distutils/tests/test_install.py b/Lib/distutils/tests/test_install.py --- a/Lib/distutils/tests/test_install.py +++ b/Lib/distutils/tests/test_install.py @@ -1,7 +1,6 @@ """Tests for distutils.command.install.""" import os -import imp import sys import unittest import site @@ -193,7 +192,8 @@ f.close() found = [os.path.basename(line) for line in content.splitlines()] - expected = ['hello.py', 'hello.%s.pyc' % imp.get_tag(), 'sayhi', + expected = ['hello.py', 'hello.%s.pyc' % sys.implementation.cache_tag, + 'sayhi', 'UNKNOWN-0.0.0-py%s.%s.egg-info' % sys.version_info[:2]] self.assertEqual(found, expected) diff --git a/Lib/distutils/tests/test_install_lib.py b/Lib/distutils/tests/test_install_lib.py --- a/Lib/distutils/tests/test_install_lib.py +++ b/Lib/distutils/tests/test_install_lib.py @@ -1,7 +1,7 @@ """Tests for distutils.command.install_data.""" import sys import os -import imp +import importlib.util import unittest from distutils.command.install_lib import install_lib @@ -44,8 +44,10 @@ f = os.path.join(project_dir, 'foo.py') self.write_file(f, '# python file') cmd.byte_compile([f]) - pyc_file = imp.cache_from_source('foo.py', debug_override=True) - pyo_file = imp.cache_from_source('foo.py', debug_override=False) + pyc_file = importlib.util.cache_from_source('foo.py', + debug_override=True) + pyo_file = importlib.util.cache_from_source('foo.py', + debug_override=False) self.assertTrue(os.path.exists(pyc_file)) self.assertTrue(os.path.exists(pyo_file)) diff --git a/Lib/distutils/util.py b/Lib/distutils/util.py --- a/Lib/distutils/util.py +++ b/Lib/distutils/util.py @@ -6,7 +6,7 @@ import os import re -import imp +import importlib.util import sys import string from distutils.errors import DistutilsPlatformError @@ -453,9 +453,10 @@ # cfile - byte-compiled file # dfile - purported source filename (same as 'file' by default) if optimize >= 0: - cfile = imp.cache_from_source(file, debug_override=not optimize) + cfile = importlib.util.cache_from_source( + file, debug_override=not optimize) else: - cfile = imp.cache_from_source(file) + cfile = importlib.util.cache_from_source(file) dfile = file if prefix: if file[:len(prefix)] != prefix: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 15 19:23:09 2013 From: python-checkins at python.org (brett.cannon) Date: Sat, 15 Jun 2013 19:23:09 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2317177=3A_Stop_usi?= =?utf-8?q?ng_imp_in_multiprocessing?= Message-ID: <3bXlrY5ZtGzQCV@mail.python.org> http://hg.python.org/cpython/rev/0b96a16656b7 changeset: 84142:0b96a16656b7 user: Brett Cannon date: Sat Jun 15 13:23:01 2013 -0400 summary: Issue #17177: Stop using imp in multiprocessing files: Lib/multiprocessing/forking.py | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/multiprocessing/forking.py b/Lib/multiprocessing/forking.py --- a/Lib/multiprocessing/forking.py +++ b/Lib/multiprocessing/forking.py @@ -449,8 +449,8 @@ elif main_name != 'ipython': # Main modules not actually called __main__.py may # contain additional code that should still be executed - import imp import importlib + import types if main_path is None: dirs = None @@ -465,7 +465,7 @@ # since that would execute 'if __name__ == "__main__"' # clauses, potentially causing a psuedo fork bomb. loader = importlib.find_loader(main_name, path=dirs) - main_module = imp.new_module(main_name) + main_module = types.ModuleType(main_name) try: loader.init_module_attrs(main_module) except AttributeError: # init_module_attrs is optional -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 15 19:29:19 2013 From: python-checkins at python.org (andrew.kuchling) Date: Sat, 15 Jun 2013 19:29:19 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Fix_typo=3B_clarify_that_t?= =?utf-8?q?he_methods_were_removed_entirely?= Message-ID: <3bXlzg2SJGzQ3P@mail.python.org> http://hg.python.org/cpython/rev/5d57f2deffba changeset: 84143:5d57f2deffba user: Andrew Kuchling date: Sat Jun 15 13:29:09 2013 -0400 summary: Fix typo; clarify that the methods were removed entirely files: Doc/whatsnew/3.4.rst | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/whatsnew/3.4.rst b/Doc/whatsnew/3.4.rst --- a/Doc/whatsnew/3.4.rst +++ b/Doc/whatsnew/3.4.rst @@ -222,8 +222,8 @@ ------------------------------------------------ * :meth:`difflib.SequenceMatcher.isbjunk` and - :meth:`difflib.SequenceMatcher.isbpopulur`: use ``x in sm.bjunk`` and - ``x in sm.bpopular``, where sm is a :class:`~difflib.SequenceMatcher` object. + :meth:`difflib.SequenceMatcher.isbpopular` were removed: use ``x in sm.bjunk`` and + ``x in sm.bpopular``, where *sm* is a :class:`~difflib.SequenceMatcher` object. * :func:`importlib.util.module_for_loader` is pending deprecation. Using :func:`importlib.util.module_to_load` and -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 15 19:38:14 2013 From: python-checkins at python.org (brett.cannon) Date: Sat, 15 Jun 2013 19:38:14 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E3=29=3A_Drop_some_dead?= =?utf-8?q?_imports_of_imp?= Message-ID: <3bXm9y4RXxzSrd@mail.python.org> http://hg.python.org/cpython/rev/61a1aa69e83e changeset: 84144:61a1aa69e83e branch: 3.3 parent: 84120:ee3952965934 user: Brett Cannon date: Sat Jun 15 13:37:12 2013 -0400 summary: Drop some dead imports of imp files: Lib/idlelib/PathBrowser.py | 1 - Lib/idlelib/TreeWidget.py | 1 - Misc/NEWS | 2 ++ 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/idlelib/PathBrowser.py b/Lib/idlelib/PathBrowser.py --- a/Lib/idlelib/PathBrowser.py +++ b/Lib/idlelib/PathBrowser.py @@ -1,6 +1,5 @@ import os import sys -import imp import importlib.machinery from idlelib.TreeWidget import TreeItem diff --git a/Lib/idlelib/TreeWidget.py b/Lib/idlelib/TreeWidget.py --- a/Lib/idlelib/TreeWidget.py +++ b/Lib/idlelib/TreeWidget.py @@ -16,7 +16,6 @@ import os from tkinter import * -import imp from idlelib import ZoomHeight from idlelib.configHandler import idleConf diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -81,6 +81,8 @@ IDLE ---- +- Remove dead imports of imp. + - Issue #18196: Avoid displaying spurious SystemExit tracebacks. - Issue #5492: Avoid traceback when exiting IDLE caused by a race condition. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 15 19:38:15 2013 From: python-checkins at python.org (brett.cannon) Date: Sat, 15 Jun 2013 19:38:15 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?b?KTogbWVyZ2Ugdy8gMy4z?= Message-ID: <3bXm9z6LcHz7LjS@mail.python.org> http://hg.python.org/cpython/rev/48b79f11571e changeset: 84145:48b79f11571e parent: 84142:0b96a16656b7 parent: 84144:61a1aa69e83e user: Brett Cannon date: Sat Jun 15 13:37:38 2013 -0400 summary: merge w/ 3.3 files: Lib/idlelib/PathBrowser.py | 1 - Lib/idlelib/TreeWidget.py | 1 - Misc/NEWS | 2 ++ 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/idlelib/PathBrowser.py b/Lib/idlelib/PathBrowser.py --- a/Lib/idlelib/PathBrowser.py +++ b/Lib/idlelib/PathBrowser.py @@ -1,6 +1,5 @@ import os import sys -import imp import importlib.machinery from idlelib.TreeWidget import TreeItem diff --git a/Lib/idlelib/TreeWidget.py b/Lib/idlelib/TreeWidget.py --- a/Lib/idlelib/TreeWidget.py +++ b/Lib/idlelib/TreeWidget.py @@ -16,7 +16,6 @@ import os from tkinter import * -import imp from idlelib import ZoomHeight from idlelib.configHandler import idleConf diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -479,6 +479,8 @@ IDLE ---- +- Remove dead imports of imp. + - Issue #18196: Avoid displaying spurious SystemExit tracebacks. - Issue #5492: Avoid traceback when exiting IDLE caused by a race condition. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 15 19:38:17 2013 From: python-checkins at python.org (brett.cannon) Date: Sat, 15 Jun 2013 19:38:17 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_default_-=3E_default?= =?utf-8?q?=29=3A_merge?= Message-ID: <3bXmB10v5Jz7Ljj@mail.python.org> http://hg.python.org/cpython/rev/acca81736b69 changeset: 84146:acca81736b69 parent: 84145:48b79f11571e parent: 84143:5d57f2deffba user: Brett Cannon date: Sat Jun 15 13:38:07 2013 -0400 summary: merge files: Doc/whatsnew/3.4.rst | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/whatsnew/3.4.rst b/Doc/whatsnew/3.4.rst --- a/Doc/whatsnew/3.4.rst +++ b/Doc/whatsnew/3.4.rst @@ -222,8 +222,8 @@ ------------------------------------------------ * :meth:`difflib.SequenceMatcher.isbjunk` and - :meth:`difflib.SequenceMatcher.isbpopulur`: use ``x in sm.bjunk`` and - ``x in sm.bpopular``, where sm is a :class:`~difflib.SequenceMatcher` object. + :meth:`difflib.SequenceMatcher.isbpopular` were removed: use ``x in sm.bjunk`` and + ``x in sm.bpopular``, where *sm* is a :class:`~difflib.SequenceMatcher` object. * :func:`importlib.util.module_for_loader` is pending deprecation. Using :func:`importlib.util.module_to_load` and -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 15 20:01:15 2013 From: python-checkins at python.org (antoine.pitrou) Date: Sat, 15 Jun 2013 20:01:15 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?peps=3A_An_object=27s_finalizer_will_?= =?utf-8?q?only_ever_be_called_once=2C_even_if_resurrected=2E?= Message-ID: <3bXmhW2THQz7Ljs@mail.python.org> http://hg.python.org/peps/rev/b9c80ae6a17d changeset: 4930:b9c80ae6a17d user: Antoine Pitrou date: Sat Jun 15 19:59:55 2013 +0200 summary: An object's finalizer will only ever be called once, even if resurrected. files: pep-0442.txt | 3 +-- 1 files changed, 1 insertions(+), 2 deletions(-) diff --git a/pep-0442.txt b/pep-0442.txt --- a/pep-0442.txt +++ b/pep-0442.txt @@ -201,8 +201,7 @@ -------------- Following this scheme, an object's finalizer is always called exactly -once. The only exception is if an object is resurrected: the finalizer -will be called again when the object becomes unreachable again. +once, even if it was resurrected afterwards. For CI objects, the order in which finalizers are called (step 2 above) is undefined. -- Repository URL: http://hg.python.org/peps From python-checkins at python.org Sat Jun 15 20:01:16 2013 From: python-checkins at python.org (antoine.pitrou) Date: Sat, 15 Jun 2013 20:01:16 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?peps=3A_Mark_PEP_442_as_accepted?= Message-ID: <3bXmhX45bNzPtG@mail.python.org> http://hg.python.org/peps/rev/7428ee7e90f2 changeset: 4931:7428ee7e90f2 user: Antoine Pitrou date: Sat Jun 15 20:01:08 2013 +0200 summary: Mark PEP 442 as accepted files: pep-0442.txt | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pep-0442.txt b/pep-0442.txt --- a/pep-0442.txt +++ b/pep-0442.txt @@ -4,13 +4,13 @@ Last-Modified: $Date$ Author: Antoine Pitrou BDFL-Delegate: Benjamin Peterson -Status: Draft +Status: Accepted Type: Standards Track Content-Type: text/x-rst Created: 2013-05-18 Python-Version: 3.4 Post-History: 2013-05-18 -Resolution: TBD +Resolution: http://mail.python.org/pipermail/python-dev/2013-June/126746.html Abstract -- Repository URL: http://hg.python.org/peps From python-checkins at python.org Sat Jun 15 20:25:13 2013 From: python-checkins at python.org (brett.cannon) Date: Sat, 15 Jun 2013 20:25:13 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2317177=3A_Stop_usi?= =?utf-8?q?ng_imp_with_py=5Fcompile?= Message-ID: <3bXnD92Dwwz7Ljp@mail.python.org> http://hg.python.org/cpython/rev/91467f342977 changeset: 84147:91467f342977 user: Brett Cannon date: Sat Jun 15 14:07:21 2013 -0400 summary: Issue #17177: Stop using imp with py_compile files: Lib/py_compile.py | 7 ++++--- Lib/test/test_py_compile.py | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Lib/py_compile.py b/Lib/py_compile.py --- a/Lib/py_compile.py +++ b/Lib/py_compile.py @@ -3,9 +3,9 @@ This module has intimate knowledge of the format of .pyc files. """ -import imp import importlib._bootstrap import importlib.machinery +import importlib.util import os import os.path import sys @@ -105,9 +105,10 @@ """ if cfile is None: if optimize >= 0: - cfile = imp.cache_from_source(file, debug_override=not optimize) + cfile = importlib.util.cache_from_source(file, + debug_override=not optimize) else: - cfile = imp.cache_from_source(file) + cfile = importlib.util.cache_from_source(file) if os.path.islink(cfile): msg = ('{} is a symlink and will be changed into a regular file if ' 'import writes a byte-compiled file to it') diff --git a/Lib/test/test_py_compile.py b/Lib/test/test_py_compile.py --- a/Lib/test/test_py_compile.py +++ b/Lib/test/test_py_compile.py @@ -1,4 +1,4 @@ -import imp +import importlib.util import os import py_compile import shutil @@ -14,7 +14,7 @@ self.directory = tempfile.mkdtemp() self.source_path = os.path.join(self.directory, '_test.py') self.pyc_path = self.source_path + 'c' - self.cache_path = imp.cache_from_source(self.source_path) + self.cache_path = importlib.util.cache_from_source(self.source_path) self.cwd_drive = os.path.splitdrive(os.getcwd())[0] # In these tests we compute relative paths. When using Windows, the # current working directory path and the 'self.source_path' might be -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 15 20:25:14 2013 From: python-checkins at python.org (brett.cannon) Date: Sat, 15 Jun 2013 20:25:14 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2317177=3A_Stop_usi?= =?utf-8?q?ng_imp_in_pydoc?= Message-ID: <3bXnDB46L8z7LjS@mail.python.org> http://hg.python.org/cpython/rev/81cf3d6e6756 changeset: 84148:81cf3d6e6756 user: Brett Cannon date: Sat Jun 15 14:25:04 2013 -0400 summary: Issue #17177: Stop using imp in pydoc files: Lib/pydoc.py | 9 ++++----- 1 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Lib/pydoc.py b/Lib/pydoc.py --- a/Lib/pydoc.py +++ b/Lib/pydoc.py @@ -44,17 +44,16 @@ """ # Known bugs that can't be fixed here: -# - imp.load_module() cannot be prevented from clobbering existing -# loaded modules, so calling synopsis() on a binary module file -# changes the contents of any existing module with the same name. +# - synopsis() cannot be prevented from clobbering existing +# loaded modules. # - If the __file__ attribute on a module is a relative path and # the current directory is changed with os.chdir(), an incorrect # path will be displayed. import builtins -import imp import importlib._bootstrap import importlib.machinery +import importlib.util import inspect import io import os @@ -268,7 +267,7 @@ def importfile(path): """Import a Python source file or compiled file given its path.""" - magic = imp.get_magic() + magic = importlib.util.MAGIC_NUMBER with open(path, 'rb') as file: is_bytecode = magic == file.read(len(magic)) filename = os.path.basename(path) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 15 20:27:30 2013 From: python-checkins at python.org (brett.cannon) Date: Sat, 15 Jun 2013 20:27:30 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2317177=3A_switch_f?= =?utf-8?q?rom_imp=2Enew=5Fmodule_to_types=2EModuleType_for_runpy?= Message-ID: <3bXnGp1T1BzP98@mail.python.org> http://hg.python.org/cpython/rev/8fff5554125d changeset: 84149:8fff5554125d user: Brett Cannon date: Sat Jun 15 14:27:21 2013 -0400 summary: Issue #17177: switch from imp.new_module to types.ModuleType for runpy files: Lib/runpy.py | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-) diff --git a/Lib/runpy.py b/Lib/runpy.py --- a/Lib/runpy.py +++ b/Lib/runpy.py @@ -14,6 +14,7 @@ import sys import importlib.machinery # importlib first so we can test #15386 via -m import imp +import types from pkgutil import read_code, get_loader, get_importer __all__ = [ @@ -24,7 +25,7 @@ """Temporarily replace a module in sys.modules with an empty namespace""" def __init__(self, mod_name): self.mod_name = mod_name - self.module = imp.new_module(mod_name) + self.module = types.ModuleType(mod_name) self._saved_module = [] def __enter__(self): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 15 20:32:21 2013 From: python-checkins at python.org (brett.cannon) Date: Sat, 15 Jun 2013 20:32:21 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2317177=3A_Stop_usi?= =?utf-8?q?ng_imp_in_sysconfig?= Message-ID: <3bXnNP0HWmz7Ljj@mail.python.org> http://hg.python.org/cpython/rev/1e141c2cc0d7 changeset: 84150:1e141c2cc0d7 user: Brett Cannon date: Sat Jun 15 14:32:11 2013 -0400 summary: Issue #17177: Stop using imp in sysconfig files: Lib/sysconfig.py | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/sysconfig.py b/Lib/sysconfig.py --- a/Lib/sysconfig.py +++ b/Lib/sysconfig.py @@ -383,8 +383,8 @@ # get_platform() succeeds. name = '_sysconfigdata' if 'darwin' in sys.platform: - import imp - module = imp.new_module(name) + import types + module = types.ModuleType(name) module.build_time_vars = vars sys.modules[name] = module -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 15 20:48:16 2013 From: python-checkins at python.org (andrew.kuchling) Date: Sat, 15 Jun 2013 20:48:16 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogIzE4MTEzOiBPYmpl?= =?utf-8?q?cts_associated_to_a_curses=2Epanel_object_with_set=5Fuserptr=28?= =?utf-8?q?=29_were?= Message-ID: <3bXnkm4CrzzRvc@mail.python.org> http://hg.python.org/cpython/rev/aff0bdab358e changeset: 84151:aff0bdab358e branch: 2.7 parent: 84116:5accb0ac8bfb user: Andrew Kuchling date: Sat Jun 15 13:53:10 2013 -0400 summary: #18113: Objects associated to a curses.panel object with set_userptr() were leaked. Reported by Atsuo Ishimoto. files: Lib/test/test_curses.py | 13 +++++++++++++ Misc/NEWS | 3 +++ Modules/_curses_panel.c | 4 ++++ 3 files changed, 20 insertions(+), 0 deletions(-) diff --git a/Lib/test/test_curses.py b/Lib/test/test_curses.py --- a/Lib/test/test_curses.py +++ b/Lib/test/test_curses.py @@ -250,6 +250,18 @@ except curses.panel.error: pass +def test_userptr_memory_leak(stdscr): + w = curses.newwin(10, 10) + p = curses.panel.new_panel(w) + obj = object() + nrefs = sys.getrefcount(obj) + for i in range(100): + p.set_userptr(obj) + + p.set_userptr(None) + if sys.getrefcount(obj) != nrefs: + raise RuntimeError, "set_userptr leaked references" + def test_resize_term(stdscr): if hasattr(curses, 'resizeterm'): lines, cols = curses.LINES, curses.COLS @@ -268,6 +280,7 @@ module_funcs(stdscr) window_funcs(stdscr) test_userptr_without_set(stdscr) + test_userptr_memory_leak(stdscr) test_resize_term(stdscr) test_issue6243(stdscr) finally: diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -40,6 +40,9 @@ the default for linking if LDSHARED is not also overriden. This restores Distutils behavior introduced in 2.7.3 and inadvertently dropped in 2.7.4. +- Issue #18113: Fixed a refcount leak in the curses.panel module's + set_userptr() method. Reported by Atsuo Ishimoto. + Build ----- diff --git a/Modules/_curses_panel.c b/Modules/_curses_panel.c --- a/Modules/_curses_panel.c +++ b/Modules/_curses_panel.c @@ -293,6 +293,10 @@ static PyObject * PyCursesPanel_set_panel_userptr(PyCursesPanelObject *self, PyObject *obj) { + PyObject *oldobj; + PyCursesInitialised; + oldobj = (PyObject *) panel_userptr(self->pan); + Py_XDECREF(oldobj); Py_INCREF(obj); return PyCursesCheckERR(set_panel_userptr(self->pan, (void*)obj), "set_panel_userptr"); -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 15 21:03:32 2013 From: python-checkins at python.org (andrew.kuchling) Date: Sat, 15 Jun 2013 21:03:32 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogIzE4MTEzOiBPYmpl?= =?utf-8?q?cts_associated_to_a_curses=2Epanel_object_with_set=5Fuserptr=28?= =?utf-8?q?=29_were?= Message-ID: <3bXp4N3XvdzQ3P@mail.python.org> http://hg.python.org/cpython/rev/3d75bd69e5a9 changeset: 84152:3d75bd69e5a9 branch: 3.3 parent: 84144:61a1aa69e83e user: Andrew Kuchling date: Sat Jun 15 14:04:04 2013 -0400 summary: #18113: Objects associated to a curses.panel object with set_userptr() were leaked. Reported by Atsuo Ishimoto. files: Lib/test/test_curses.py | 13 +++++++++++++ Misc/NEWS | 3 +++ Modules/_curses_panel.c | 4 ++++ 3 files changed, 20 insertions(+), 0 deletions(-) diff --git a/Lib/test/test_curses.py b/Lib/test/test_curses.py --- a/Lib/test/test_curses.py +++ b/Lib/test/test_curses.py @@ -252,6 +252,18 @@ except curses.panel.error: pass +def test_userptr_memory_leak(stdscr): + w = curses.newwin(10, 10) + p = curses.panel.new_panel(w) + obj = object() + nrefs = sys.getrefcount(obj) + for i in range(100): + p.set_userptr(obj) + + p.set_userptr(None) + if sys.getrefcount(obj) != nrefs: + raise RuntimeError("set_userptr leaked references") + def test_resize_term(stdscr): if hasattr(curses, 'resizeterm'): lines, cols = curses.LINES, curses.COLS @@ -317,6 +329,7 @@ module_funcs(stdscr) window_funcs(stdscr) test_userptr_without_set(stdscr) + test_userptr_memory_leak(stdscr) test_resize_term(stdscr) test_issue6243(stdscr) test_unget_wch(stdscr) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -78,6 +78,9 @@ the default for linking if LDSHARED is not also overriden. This restores Distutils behavior introduced in 3.2.3 and inadvertently dropped in 3.3.0. +- Issue #18113: Fixed a refcount leak in the curses.panel module's + set_userptr() method. Reported by Atsuo Ishimoto. + IDLE ---- diff --git a/Modules/_curses_panel.c b/Modules/_curses_panel.c --- a/Modules/_curses_panel.c +++ b/Modules/_curses_panel.c @@ -322,6 +322,10 @@ static PyObject * PyCursesPanel_set_panel_userptr(PyCursesPanelObject *self, PyObject *obj) { + PyObject *oldobj; + PyCursesInitialised; + oldobj = (PyObject *) panel_userptr(self->pan); + Py_XDECREF(oldobj); Py_INCREF(obj); return PyCursesCheckERR(set_panel_userptr(self->pan, (void*)obj), "set_panel_userptr"); -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 15 21:11:57 2013 From: python-checkins at python.org (andrew.kuchling) Date: Sat, 15 Jun 2013 21:11:57 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_Merge_with_3=2E3?= Message-ID: <3bXpG50DKYz7Ljv@mail.python.org> http://hg.python.org/cpython/rev/fcb686e720ff changeset: 84153:fcb686e720ff parent: 84150:1e141c2cc0d7 parent: 84152:3d75bd69e5a9 user: Andrew Kuchling date: Sat Jun 15 15:10:08 2013 -0400 summary: Merge with 3.3 files: Lib/test/test_curses.py | 13 +++++++++++++ Misc/NEWS | 3 +++ Modules/_curses_panel.c | 4 ++++ 3 files changed, 20 insertions(+), 0 deletions(-) diff --git a/Lib/test/test_curses.py b/Lib/test/test_curses.py --- a/Lib/test/test_curses.py +++ b/Lib/test/test_curses.py @@ -252,6 +252,18 @@ except curses.panel.error: pass +def test_userptr_memory_leak(stdscr): + w = curses.newwin(10, 10) + p = curses.panel.new_panel(w) + obj = object() + nrefs = sys.getrefcount(obj) + for i in range(100): + p.set_userptr(obj) + + p.set_userptr(None) + if sys.getrefcount(obj) != nrefs: + raise RuntimeError("set_userptr leaked references") + def test_resize_term(stdscr): if hasattr(curses, 'resizeterm'): lines, cols = curses.LINES, curses.COLS @@ -317,6 +329,7 @@ module_funcs(stdscr) window_funcs(stdscr) test_userptr_without_set(stdscr) + test_userptr_memory_leak(stdscr) test_resize_term(stdscr) test_issue6243(stdscr) test_unget_wch(stdscr) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -383,6 +383,9 @@ the default for linking if LDSHARED is not also overriden. This restores Distutils behavior introduced in 3.2.3 and inadvertently dropped in 3.3.0. +- Issue #18113: Fixed a refcount leak in the curses.panel module's + set_userptr() method. Reported by Atsuo Ishimoto. + - Implement PEP 443 "Single-dispatch generic functions". - Implement PEP 435 "Adding an Enum type to the Python standard library". diff --git a/Modules/_curses_panel.c b/Modules/_curses_panel.c --- a/Modules/_curses_panel.c +++ b/Modules/_curses_panel.c @@ -322,6 +322,10 @@ static PyObject * PyCursesPanel_set_panel_userptr(PyCursesPanelObject *self, PyObject *obj) { + PyObject *oldobj; + PyCursesInitialised; + oldobj = (PyObject *) panel_userptr(self->pan); + Py_XDECREF(oldobj); Py_INCREF(obj); return PyCursesCheckERR(set_panel_userptr(self->pan, (void*)obj), "set_panel_userptr"); -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 15 23:11:35 2013 From: python-checkins at python.org (brett.cannon) Date: Sat, 15 Jun 2013 23:11:35 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2317177=3A_Stop_usi?= =?utf-8?q?ng_imp_in_a_bunch_of_tests?= Message-ID: <3bXrw70WyczRF7@mail.python.org> http://hg.python.org/cpython/rev/bc18b5d4920e changeset: 84154:bc18b5d4920e user: Brett Cannon date: Sat Jun 15 17:11:25 2013 -0400 summary: Issue #17177: Stop using imp in a bunch of tests files: Lib/test/script_helper.py | 2 +- Lib/test/support.py | 8 ++++---- Lib/test/test_pdb.py | 4 ++-- Lib/test/test_pkgimport.py | 2 +- Lib/test/test_pkgutil.py | 4 ++-- Lib/test/test_reprlib.py | 5 +++-- Lib/test/test_zipimport.py | 7 ++++--- 7 files changed, 17 insertions(+), 15 deletions(-) diff --git a/Lib/test/script_helper.py b/Lib/test/script_helper.py --- a/Lib/test/script_helper.py +++ b/Lib/test/script_helper.py @@ -12,7 +12,7 @@ import shutil import zipfile -from imp import source_from_cache +from importlib.util import source_from_cache from test.support import make_legacy_pyc, strip_python_stderr # Executing the interpreter in a subprocess diff --git a/Lib/test/support.py b/Lib/test/support.py --- a/Lib/test/support.py +++ b/Lib/test/support.py @@ -15,10 +15,10 @@ import warnings import unittest import importlib +import importlib.util import collections.abc import re import subprocess -import imp import time import sysconfig import fnmatch @@ -316,7 +316,7 @@ does not need to exist, however the PEP 3147 pyc file must exist. :return: The file system path to the legacy pyc file. """ - pyc_file = imp.cache_from_source(source) + pyc_file = importlib.util.cache_from_source(source) up_one = os.path.dirname(os.path.abspath(source)) legacy_pyc = os.path.join(up_one, source + ('c' if __debug__ else 'o')) os.rename(pyc_file, legacy_pyc) @@ -335,8 +335,8 @@ # combinations of PEP 3147 and legacy pyc and pyo files. unlink(source + 'c') unlink(source + 'o') - unlink(imp.cache_from_source(source, debug_override=True)) - unlink(imp.cache_from_source(source, debug_override=False)) + unlink(importlib.util.cache_from_source(source, debug_override=True)) + unlink(importlib.util.cache_from_source(source, debug_override=False)) # On some platforms, should not run gui test even if it is allowed # in `use_resources'. diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -1,9 +1,9 @@ # A test suite for pdb; not very comprehensive at the moment. import doctest -import imp import pdb import sys +import types import unittest import subprocess import textwrap @@ -464,7 +464,7 @@ # Module for testing skipping of module that makes a callback -mod = imp.new_module('module_to_skip') +mod = types.ModuleType('module_to_skip') exec('def foo_pony(callback): x = 1; callback(); return None', mod.__dict__) diff --git a/Lib/test/test_pkgimport.py b/Lib/test/test_pkgimport.py --- a/Lib/test/test_pkgimport.py +++ b/Lib/test/test_pkgimport.py @@ -6,7 +6,7 @@ import tempfile import unittest -from imp import cache_from_source +from importlib.util import cache_from_source from test.support import run_unittest, create_empty_file class TestImport(unittest.TestCase): diff --git a/Lib/test/test_pkgutil.py b/Lib/test/test_pkgutil.py --- a/Lib/test/test_pkgutil.py +++ b/Lib/test/test_pkgutil.py @@ -1,12 +1,12 @@ from test.support import run_unittest, unload, check_warnings import unittest import sys -import imp import importlib import pkgutil import os import os.path import tempfile +import types import shutil import zipfile @@ -105,7 +105,7 @@ class MyTestLoader(object): def load_module(self, fullname): # Create an empty module - mod = sys.modules.setdefault(fullname, imp.new_module(fullname)) + mod = sys.modules.setdefault(fullname, types.ModuleType(fullname)) mod.__file__ = "<%s>" % self.__class__.__name__ mod.__loader__ = self # Make it a package 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 @@ -3,11 +3,11 @@ Nick Mathewson """ -import imp import sys import os import shutil import importlib +import importlib.util import unittest from test.support import run_unittest, create_empty_file, verbose @@ -241,7 +241,8 @@ source_path_len += 2 * (len(self.longname) + 1) # a path separator + `module_name` + ".py" source_path_len += len(module_name) + 1 + len(".py") - cached_path_len = source_path_len + len(imp.cache_from_source("x.py")) - len("x.py") + cached_path_len = (source_path_len + + len(importlib.util.cache_from_source("x.py")) - len("x.py")) if os.name == 'nt' and cached_path_len >= 258: # Under Windows, the max path len is 260 including C's terminating # NUL character. diff --git a/Lib/test/test_zipimport.py b/Lib/test/test_zipimport.py --- a/Lib/test/test_zipimport.py +++ b/Lib/test/test_zipimport.py @@ -1,7 +1,7 @@ import sys import os import marshal -import imp +import importlib.util import struct import time import unittest @@ -34,7 +34,8 @@ mtime = int(mtime) else: mtime = int(-0x100000000 + int(mtime)) - pyc = imp.get_magic() + struct.pack(" http://hg.python.org/cpython/rev/f96eb1dc335f changeset: 84155:f96eb1dc335f user: Brett Cannon date: Sat Jun 15 17:32:30 2013 -0400 summary: Issue #17177: Stop using imp in zipfile files: Lib/test/test_zipfile.py | 4 ++-- Lib/zipfile.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_zipfile.py b/Lib/test/test_zipfile.py --- a/Lib/test/test_zipfile.py +++ b/Lib/test/test_zipfile.py @@ -1,7 +1,7 @@ import io import os import sys -import imp +import importlib.util import time import shutil import struct @@ -869,7 +869,7 @@ if os.altsep is not None: path_split.extend(fn.split(os.altsep)) if '__pycache__' in path_split: - fn = imp.source_from_cache(fn) + fn = importlib.util.source_from_cache(fn) else: fn = fn[:-1] diff --git a/Lib/zipfile.py b/Lib/zipfile.py --- a/Lib/zipfile.py +++ b/Lib/zipfile.py @@ -6,7 +6,7 @@ import io import os import re -import imp +import importlib.util import sys import time import stat @@ -1645,8 +1645,8 @@ file_py = pathname + ".py" file_pyc = pathname + ".pyc" file_pyo = pathname + ".pyo" - pycache_pyc = imp.cache_from_source(file_py, True) - pycache_pyo = imp.cache_from_source(file_py, False) + pycache_pyc = importlib.util.cache_from_source(file_py, True) + pycache_pyo = importlib.util.cache_from_source(file_py, False) if self._optimize == -1: # legacy mode: use whatever file is present if (os.path.isfile(file_pyo) and -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 15 23:33:39 2013 From: python-checkins at python.org (brett.cannon) Date: Sat, 15 Jun 2013 23:33:39 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=23_17177=3A_Stop_us?= =?utf-8?q?ing_imp_in_turtledemo?= Message-ID: <3bXsPb1ytnz7LkJ@mail.python.org> http://hg.python.org/cpython/rev/b2b2bdc4cf6f changeset: 84156:b2b2bdc4cf6f user: Brett Cannon date: Sat Jun 15 17:33:27 2013 -0400 summary: Issue # 17177: Stop using imp in turtledemo files: Lib/turtledemo/__main__.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Lib/turtledemo/__main__.py b/Lib/turtledemo/__main__.py --- a/Lib/turtledemo/__main__.py +++ b/Lib/turtledemo/__main__.py @@ -6,7 +6,7 @@ from idlelib.Percolator import Percolator from idlelib.ColorDelegator import ColorDelegator from idlelib.textView import view_file # TextViewer -from imp import reload +from importlib import reload import turtle import time -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 15 23:53:08 2013 From: python-checkins at python.org (brett.cannon) Date: Sat, 15 Jun 2013 23:53:08 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=23_17177=3A_Stop_us?= =?utf-8?q?ing_imp_in_setup=2Epy?= Message-ID: <3bXsr44P39z7Lkf@mail.python.org> http://hg.python.org/cpython/rev/4a1161eaed99 changeset: 84157:4a1161eaed99 user: Brett Cannon date: Sat Jun 15 17:52:59 2013 -0400 summary: Issue # 17177: Stop using imp in setup.py files: setup.py | 5 +++-- 1 files changed, 3 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ # Autodetecting setup.py script for building the Python extensions # -import sys, os, imp, re, optparse +import sys, os, importlib.machinery, re, optparse from glob import glob import sysconfig @@ -325,8 +325,9 @@ if cross_compiling: return + loader = importlib.machinery.ExtensionFileLoader(ext.name, ext_filename) try: - imp.load_dynamic(ext.name, ext_filename) + loader.load_module() except ImportError as why: self.failed.append(ext.name) self.announce('*** WARNING: renaming "%s" since importing it' -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 16 00:10:26 2013 From: python-checkins at python.org (brett.cannon) Date: Sun, 16 Jun 2013 00:10:26 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2317177=3A_update_c?= =?utf-8?q?heckpyc_to_stop_using_imp?= Message-ID: <3bXtD21qf8z7Ljr@mail.python.org> http://hg.python.org/cpython/rev/ca3bdac1f88a changeset: 84158:ca3bdac1f88a user: Brett Cannon date: Sat Jun 15 18:10:18 2013 -0400 summary: Issue #17177: update checkpyc to stop using imp files: Tools/scripts/checkpyc.py | 8 ++++---- 1 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Tools/scripts/checkpyc.py b/Tools/scripts/checkpyc.py --- a/Tools/scripts/checkpyc.py +++ b/Tools/scripts/checkpyc.py @@ -5,11 +5,11 @@ import sys import os from stat import ST_MTIME -import imp +import importlib.util # PEP 3147 compatibility (PYC Repository Directories) -cache_from_source = (imp.cache_from_source if hasattr(imp, 'get_tag') else - lambda path: path + 'c') +cache_from_source = (importlib.util.cache_from_source if sys.implementation.cache_tag + else lambda path: path + 'c') def main(): @@ -18,7 +18,7 @@ silent = (sys.argv[1] == '-s') else: verbose = silent = False - MAGIC = imp.get_magic() + MAGIC = importlib.util.MAGIC_NUMBER if not silent: print('Using MAGIC word', repr(MAGIC)) for dirname in sys.path: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 16 00:39:30 2013 From: python-checkins at python.org (brett.cannon) Date: Sun, 16 Jun 2013 00:39:30 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2317177=3A_stop_usi?= =?utf-8?q?ng_imp_in_test=5Fimportlib?= Message-ID: <3bXtsZ6Hn2z7Ljj@mail.python.org> http://hg.python.org/cpython/rev/5e8b377942f7 changeset: 84159:5e8b377942f7 user: Brett Cannon date: Sat Jun 15 18:39:21 2013 -0400 summary: Issue #17177: stop using imp in test_importlib files: Lib/test/test_importlib/extension/test_case_sensitivity.py | 9 +- Lib/test/test_importlib/extension/test_path_hook.py | 1 - Lib/test/test_importlib/extension/util.py | 1 - Lib/test/test_importlib/frozen/test_loader.py | 10 +- Lib/test/test_importlib/import_/test___loader__.py | 6 +- Lib/test/test_importlib/import_/test_api.py | 5 +- Lib/test/test_importlib/import_/test_fromlist.py | 1 - Lib/test/test_importlib/source/test_case_sensitivity.py | 6 +- Lib/test/test_importlib/source/test_file_loader.py | 19 ++- Lib/test/test_importlib/source/test_finder.py | 1 - Lib/test/test_importlib/source/test_path_hook.py | 1 - Lib/test/test_importlib/source/util.py | 1 - Lib/test/test_importlib/test_abc.py | 49 +++++---- Lib/test/test_importlib/test_api.py | 10 +- Lib/test/test_importlib/test_util.py | 30 +++--- Lib/test/test_importlib/util.py | 4 +- 16 files changed, 78 insertions(+), 76 deletions(-) diff --git a/Lib/test/test_importlib/extension/test_case_sensitivity.py b/Lib/test/test_importlib/extension/test_case_sensitivity.py --- a/Lib/test/test_importlib/extension/test_case_sensitivity.py +++ b/Lib/test/test_importlib/extension/test_case_sensitivity.py @@ -1,8 +1,9 @@ -import imp import sys from test import support import unittest + from importlib import _bootstrap +from importlib import machinery from .. import util from . import util as ext_util @@ -14,9 +15,9 @@ good_name = ext_util.NAME bad_name = good_name.upper() assert good_name != bad_name - finder = _bootstrap.FileFinder(ext_util.PATH, - (_bootstrap.ExtensionFileLoader, - _bootstrap.EXTENSION_SUFFIXES)) + finder = machinery.FileFinder(ext_util.PATH, + (machinery.ExtensionFileLoader, + machinery.EXTENSION_SUFFIXES)) return finder.find_module(bad_name) def test_case_sensitive(self): diff --git a/Lib/test/test_importlib/extension/test_path_hook.py b/Lib/test/test_importlib/extension/test_path_hook.py --- a/Lib/test/test_importlib/extension/test_path_hook.py +++ b/Lib/test/test_importlib/extension/test_path_hook.py @@ -2,7 +2,6 @@ from . import util import collections -import imp import sys import unittest diff --git a/Lib/test/test_importlib/extension/util.py b/Lib/test/test_importlib/extension/util.py --- a/Lib/test/test_importlib/extension/util.py +++ b/Lib/test/test_importlib/extension/util.py @@ -1,4 +1,3 @@ -import imp from importlib import machinery import os import sys diff --git a/Lib/test/test_importlib/frozen/test_loader.py b/Lib/test/test_importlib/frozen/test_loader.py --- a/Lib/test/test_importlib/frozen/test_loader.py +++ b/Lib/test/test_importlib/frozen/test_loader.py @@ -1,9 +1,11 @@ -from importlib import machinery -import imp -import unittest from .. import abc from .. import util + +from importlib import machinery +import unittest from test.support import captured_stdout +import types + class LoaderTests(abc.LoaderTests): @@ -85,7 +87,7 @@ name = '__hello__' with captured_stdout() as stdout: code = machinery.FrozenImporter.get_code(name) - mod = imp.new_module(name) + mod = types.ModuleType(name) exec(code, mod.__dict__) self.assertTrue(hasattr(mod, 'initialized')) self.assertEqual(stdout.getvalue(), 'Hello world!\n') diff --git a/Lib/test/test_importlib/import_/test___loader__.py b/Lib/test/test_importlib/import_/test___loader__.py --- a/Lib/test/test_importlib/import_/test___loader__.py +++ b/Lib/test/test_importlib/import_/test___loader__.py @@ -1,5 +1,5 @@ -import imp import sys +import types import unittest from .. import util @@ -19,7 +19,7 @@ class LoaderAttributeTests(unittest.TestCase): def test___loader___missing(self): - module = imp.new_module('blah') + module = types.ModuleType('blah') try: del module.__loader__ except AttributeError: @@ -31,7 +31,7 @@ self.assertEqual(loader, module.__loader__) def test___loader___is_None(self): - module = imp.new_module('blah') + module = types.ModuleType('blah') module.__loader__ = None loader = LoaderMock() loader.module = module diff --git a/Lib/test/test_importlib/import_/test_api.py b/Lib/test/test_importlib/import_/test_api.py --- a/Lib/test/test_importlib/import_/test_api.py +++ b/Lib/test/test_importlib/import_/test_api.py @@ -2,6 +2,7 @@ from . import util import imp import sys +import types import unittest @@ -48,7 +49,7 @@ def test_nonexistent_fromlist_entry(self): # If something in fromlist doesn't exist, that's okay. # issue15715 - mod = imp.new_module('fine') + mod = types.ModuleType('fine') mod.__path__ = ['XXX'] with importlib_test_util.import_state(meta_path=[BadLoaderFinder]): with importlib_test_util.uncache('fine'): @@ -59,7 +60,7 @@ # If something in fromlist triggers an exception not related to not # existing, let that exception propagate. # issue15316 - mod = imp.new_module('fine') + mod = types.ModuleType('fine') mod.__path__ = ['XXX'] with importlib_test_util.import_state(meta_path=[BadLoaderFinder]): with importlib_test_util.uncache('fine'): diff --git a/Lib/test/test_importlib/import_/test_fromlist.py b/Lib/test/test_importlib/import_/test_fromlist.py --- a/Lib/test/test_importlib/import_/test_fromlist.py +++ b/Lib/test/test_importlib/import_/test_fromlist.py @@ -1,7 +1,6 @@ """Test that the semantics relating to the 'fromlist' argument are correct.""" from .. import util from . import util as import_util -import imp import unittest class ReturnValue(unittest.TestCase): diff --git a/Lib/test/test_importlib/source/test_case_sensitivity.py b/Lib/test/test_importlib/source/test_case_sensitivity.py --- a/Lib/test/test_importlib/source/test_case_sensitivity.py +++ b/Lib/test/test_importlib/source/test_case_sensitivity.py @@ -1,9 +1,9 @@ """Test case-sensitivity (PEP 235).""" +from .. import util +from . import util as source_util + from importlib import _bootstrap from importlib import machinery -from .. import util -from . import util as source_util -import imp import os import sys from test import support as test_support diff --git a/Lib/test/test_importlib/source/test_file_loader.py b/Lib/test/test_importlib/source/test_file_loader.py --- a/Lib/test/test_importlib/source/test_file_loader.py +++ b/Lib/test/test_importlib/source/test_file_loader.py @@ -1,6 +1,7 @@ from importlib import machinery import importlib import importlib.abc +import importlib.util from .. import abc from .. import util from . import util as source_util @@ -13,6 +14,7 @@ import shutil import stat import sys +import types import unittest from test.support import make_legacy_pyc, unload @@ -112,7 +114,7 @@ value = '' name = '_temp' with source_util.create_modules(name) as mapping: - orig_module = imp.new_module(name) + orig_module = types.ModuleType(name) for attr in attributes: setattr(orig_module, attr, value) with open(mapping[name], 'w') as file: @@ -144,11 +146,11 @@ loader = machinery.SourceFileLoader('_temp', file_path) mod = loader.load_module('_temp') self.assertEqual(file_path, mod.__file__) - self.assertEqual(imp.cache_from_source(file_path), + self.assertEqual(importlib.util.cache_from_source(file_path), mod.__cached__) finally: os.unlink(file_path) - pycache = os.path.dirname(imp.cache_from_source(file_path)) + pycache = os.path.dirname(importlib.util.cache_from_source(file_path)) if os.path.exists(pycache): shutil.rmtree(pycache) @@ -157,7 +159,7 @@ # truncated rather than raise an OverflowError. with source_util.create_modules('_temp') as mapping: source = mapping['_temp'] - compiled = imp.cache_from_source(source) + compiled = importlib.util.cache_from_source(source) with open(source, 'w') as f: f.write("x = 5") try: @@ -194,7 +196,7 @@ pass py_compile.compile(mapping[name]) if not del_source: - bytecode_path = imp.cache_from_source(mapping[name]) + bytecode_path = importlib.util.cache_from_source(mapping[name]) else: os.unlink(mapping[name]) bytecode_path = make_legacy_pyc(mapping[name]) @@ -322,7 +324,8 @@ def test(name, mapping, bytecode_path): self.import_(mapping[name], name) with open(bytecode_path, 'rb') as bytecode_file: - self.assertEqual(bytecode_file.read(4), imp.get_magic()) + self.assertEqual(bytecode_file.read(4), + importlib.util.MAGIC_NUMBER) self._test_bad_magic(test) @@ -372,7 +375,7 @@ zeros = b'\x00\x00\x00\x00' with source_util.create_modules('_temp') as mapping: py_compile.compile(mapping['_temp']) - bytecode_path = imp.cache_from_source(mapping['_temp']) + bytecode_path = importlib.util.cache_from_source(mapping['_temp']) with open(bytecode_path, 'r+b') as bytecode_file: bytecode_file.seek(4) bytecode_file.write(zeros) @@ -390,7 +393,7 @@ with source_util.create_modules('_temp') as mapping: # Create bytecode that will need to be re-created. py_compile.compile(mapping['_temp']) - bytecode_path = imp.cache_from_source(mapping['_temp']) + bytecode_path = importlib.util.cache_from_source(mapping['_temp']) with open(bytecode_path, 'r+b') as bytecode_file: bytecode_file.seek(0) bytecode_file.write(b'\x00\x00\x00\x00') diff --git a/Lib/test/test_importlib/source/test_finder.py b/Lib/test/test_importlib/source/test_finder.py --- a/Lib/test/test_importlib/source/test_finder.py +++ b/Lib/test/test_importlib/source/test_finder.py @@ -3,7 +3,6 @@ from importlib import machinery import errno -import imp import os import py_compile import stat diff --git a/Lib/test/test_importlib/source/test_path_hook.py b/Lib/test/test_importlib/source/test_path_hook.py --- a/Lib/test/test_importlib/source/test_path_hook.py +++ b/Lib/test/test_importlib/source/test_path_hook.py @@ -1,7 +1,6 @@ from . import util as source_util from importlib import machinery -import imp import unittest diff --git a/Lib/test/test_importlib/source/util.py b/Lib/test/test_importlib/source/util.py --- a/Lib/test/test_importlib/source/util.py +++ b/Lib/test/test_importlib/source/util.py @@ -2,7 +2,6 @@ import contextlib import errno import functools -import imp import os import os.path import sys diff --git a/Lib/test/test_importlib/test_abc.py b/Lib/test/test_importlib/test_abc.py --- a/Lib/test/test_importlib/test_abc.py +++ b/Lib/test/test_importlib/test_abc.py @@ -1,15 +1,16 @@ import importlib +import importlib.util from importlib import abc from importlib import machinery import contextlib -import imp import inspect import io import marshal import os import sys from test import support +import types import unittest from unittest import mock @@ -140,7 +141,7 @@ self.ins.load_module('something') def test_module_repr(self): - mod = imp.new_module('blah') + mod = types.ModuleType('blah') with self.assertRaises(NotImplementedError): self.ins.module_repr(mod) original_repr = repr(mod) @@ -205,7 +206,7 @@ def test_init_module_attrs(self): loader = LoaderSubclass() - module = imp.new_module('blah') + module = types.ModuleType('blah') loader.init_module_attrs(module) self.assertEqual(module.__loader__, loader) @@ -215,7 +216,7 @@ def source_to_module(self, data, path=None): """Help with source_to_code() tests.""" - module = imp.new_module('blah') + module = types.ModuleType('blah') loader = InspectLoaderSubclass() if path is None: code = loader.source_to_code(data) @@ -257,7 +258,7 @@ def test_get_code(self): # Test success. - module = imp.new_module('blah') + module = types.ModuleType('blah') with mock.patch.object(InspectLoaderSubclass, 'get_source') as mocked: mocked.return_value = 'attr = 42' loader = InspectLoaderSubclass() @@ -289,7 +290,7 @@ def init_module_attrs(self, name): loader = InspectLoaderSubclass() - module = imp.new_module(name) + module = types.ModuleType(name) loader.init_module_attrs(module) self.assertEqual(module.__loader__, loader) return module @@ -390,7 +391,7 @@ loader = ExecutionLoaderSubclass() code = loader.get_code('blah') self.assertEqual(code.co_filename, path) - module = imp.new_module('blah') + module = types.ModuleType('blah') exec(code, module.__dict__) self.assertEqual(module.attr, 42) @@ -420,7 +421,7 @@ loader = ExecutionLoaderSubclass() code = loader.get_code('blah') self.assertEqual(code.co_filename, '') - module = imp.new_module('blah') + module = types.ModuleType('blah') exec(code, module.__dict__) self.assertEqual(module.attr, 42) @@ -444,7 +445,7 @@ path = os.path.join('some', 'path', '{}.py'.format(name)) with self.mock_methods(False, path): loader = ExecutionLoaderSubclass() - module = imp.new_module(name) + module = types.ModuleType(name) loader.init_module_attrs(module) self.assertIs(module.__loader__, loader) self.assertEqual(module.__file__, path) @@ -457,7 +458,7 @@ path = os.path.join('some', 'pkg', '__init__.py') with self.mock_methods(True, path): loader = ExecutionLoaderSubclass() - module = imp.new_module(name) + module = types.ModuleType(name) loader.init_module_attrs(module) self.assertIs(module.__loader__, loader) self.assertEqual(module.__file__, path) @@ -471,7 +472,7 @@ path = os.path.join('some', 'pkg', 'submodule.py') with self.mock_methods(False, path): loader = ExecutionLoaderSubclass() - module = imp.new_module(name) + module = types.ModuleType(name) loader.init_module_attrs(module) self.assertEqual(module.__package__, 'pkg') self.assertEqual(module.__file__, path) @@ -484,7 +485,7 @@ with self.mock_methods(False, path) as mocked_methods: mocked_methods['get_filename'].side_effect = ImportError loader = ExecutionLoaderSubclass() - module = imp.new_module(name) + module = types.ModuleType(name) loader.init_module_attrs(module) self.assertFalse(hasattr(module, '__file__')) @@ -515,9 +516,9 @@ source_mtime = 1 - def __init__(self, path, magic=imp.get_magic()): + def __init__(self, path, magic=importlib.util.MAGIC_NUMBER): super().__init__(path) - self.bytecode_path = imp.cache_from_source(self.path) + self.bytecode_path = importlib.util.cache_from_source(self.path) self.source_size = len(self.source) data = bytearray(magic) data.extend(importlib._w_long(self.source_mtime)) @@ -557,7 +558,7 @@ module_name = 'mod' self.path = os.path.join(self.package, '.'.join(['mod', 'py'])) self.name = '.'.join([self.package, module_name]) - self.cached = imp.cache_from_source(self.path) + self.cached = importlib.util.cache_from_source(self.path) self.loader = self.loader_mock(self.path, **kwargs) def verify_module(self, module): @@ -574,7 +575,7 @@ self.assertEqual(values[4], repr(self.loader)) def verify_code(self, code_object): - module = imp.new_module(self.name) + module = types.ModuleType(self.name) module.__file__ = self.path module.__cached__ = self.cached module.__package__ = self.package @@ -673,7 +674,7 @@ super().verify_code(code_object) if bytecode_written: self.assertIn(self.cached, self.loader.written) - data = bytearray(imp.get_magic()) + data = bytearray(importlib.util.MAGIC_NUMBER) data.extend(importlib._w_long(self.loader.source_mtime)) data.extend(importlib._w_long(self.loader.source_size)) data.extend(marshal.dumps(code_object)) @@ -689,7 +690,7 @@ self.loader.bytecode_path = "" # Sanity check with self.assertRaises(OSError): - bytecode_path = imp.cache_from_source(self.path) + bytecode_path = importlib.util.cache_from_source(self.path) self.loader.get_data(bytecode_path) code_object = self.loader.get_code(self.name) self.verify_code(code_object, bytecode_written=True) @@ -787,26 +788,26 @@ """Tests for importlib.abc.SourceLoader.init_module_attrs().""" def test_init_module_attrs(self): - # If __file__ set, __cached__ == imp.cached_from_source(__file__). + # If __file__ set, __cached__ == importlib.util.cached_from_source(__file__). name = 'blah' path = 'blah.py' loader = SourceOnlyLoaderMock(path) - module = imp.new_module(name) + module = types.ModuleType(name) loader.init_module_attrs(module) self.assertEqual(module.__loader__, loader) self.assertEqual(module.__package__, '') self.assertEqual(module.__file__, path) - self.assertEqual(module.__cached__, imp.cache_from_source(path)) + self.assertEqual(module.__cached__, importlib.util.cache_from_source(path)) @mock.patch('importlib._bootstrap.cache_from_source') def test_cache_from_source_NotImplementedError(self, mock_cache_from_source): - # If imp.cache_from_source() raises NotImplementedError don't set + # If importlib.util.cache_from_source() raises NotImplementedError don't set # __cached__. mock_cache_from_source.side_effect = NotImplementedError name = 'blah' path = 'blah.py' loader = SourceOnlyLoaderMock(path) - module = imp.new_module(name) + module = types.ModuleType(name) loader.init_module_attrs(module) self.assertEqual(module.__file__, path) self.assertFalse(hasattr(module, '__cached__')) @@ -817,7 +818,7 @@ mocked.side_effect = ImportError name = 'blah' loader = SourceOnlyLoaderMock('blah.py') - module = imp.new_module(name) + module = types.ModuleType(name) loader.init_module_attrs(module) self.assertFalse(hasattr(module, '__file__')) self.assertFalse(hasattr(module, '__cached__')) diff --git a/Lib/test/test_importlib/test_api.py b/Lib/test/test_importlib/test_api.py --- a/Lib/test/test_importlib/test_api.py +++ b/Lib/test/test_importlib/test_api.py @@ -1,5 +1,5 @@ from . import util -import imp + import importlib from importlib import _bootstrap from importlib import machinery @@ -99,7 +99,7 @@ # If a module with __loader__ is in sys.modules, then return it. name = 'some_mod' with util.uncache(name): - module = imp.new_module(name) + module = types.ModuleType(name) loader = 'a loader!' module.__loader__ = loader sys.modules[name] = module @@ -110,7 +110,7 @@ # If sys.modules[name].__loader__ is None, raise ValueError. name = 'some_mod' with util.uncache(name): - module = imp.new_module(name) + module = types.ModuleType(name) module.__loader__ = None sys.modules[name] = module with self.assertRaises(ValueError): @@ -121,7 +121,7 @@ # Issue #17099 name = 'some_mod' with util.uncache(name): - module = imp.new_module(name) + module = types.ModuleType(name) try: del module.__loader__ except AttributeError: @@ -189,7 +189,7 @@ def test_method_lacking(self): # There should be no issues if the method is not defined. key = 'gobbledeegook' - sys.path_importer_cache[key] = imp.NullImporter('abc') + sys.path_importer_cache[key] = None self.addCleanup(lambda: sys.path_importer_cache.__delitem__(key)) importlib.invalidate_caches() # Shouldn't trigger an exception. diff --git a/Lib/test/test_importlib/test_util.py b/Lib/test/test_importlib/test_util.py --- a/Lib/test/test_importlib/test_util.py +++ b/Lib/test/test_importlib/test_util.py @@ -1,6 +1,6 @@ from importlib import util from . import util as test_util -import imp + import os import sys from test import support @@ -40,14 +40,14 @@ def test_reload(self): # Test that the same module is in sys.modules. - created_module = imp.new_module(self.module_name) + created_module = types.ModuleType(self.module_name) sys.modules[self.module_name] = created_module with util.module_to_load(self.module_name) as module: self.assertIs(module, created_module) def test_reload_failed(self): # Test that the module was left in sys.modules. - created_module = imp.new_module(self.module_name) + created_module = types.ModuleType(self.module_name) sys.modules[self.module_name] = created_module try: with util.module_to_load(self.module_name) as module: @@ -60,7 +60,7 @@ def test_reset_name(self): # If reset_name is true then module.__name__ = name, else leave it be. odd_name = 'not your typical name' - created_module = imp.new_module(self.module_name) + created_module = types.ModuleType(self.module_name) created_module.__name__ = odd_name sys.modules[self.module_name] = created_module with util.module_to_load(self.module_name) as module: @@ -119,7 +119,7 @@ def load_module(self, module): return module name = 'a.b.c' - module = imp.new_module('a.b.c') + module = types.ModuleType('a.b.c') module.__loader__ = 42 module.__package__ = 42 with test_util.uncache(name): @@ -141,7 +141,7 @@ def test_reload_failure(self): # Test that a failure on reload leaves the module in-place. name = 'a.b.c' - module = imp.new_module(name) + module = types.ModuleType(name) with test_util.uncache(name): sys.modules[name] = module self.raise_exception(name) @@ -212,26 +212,26 @@ def test_top_level(self): # __package__ should be set to the empty string if a top-level module. # Implicitly tests when package is set to None. - module = imp.new_module('module') + module = types.ModuleType('module') module.__package__ = None self.verify(module, '') def test_package(self): # Test setting __package__ for a package. - module = imp.new_module('pkg') + module = types.ModuleType('pkg') module.__path__ = [''] module.__package__ = None self.verify(module, 'pkg') def test_submodule(self): # Test __package__ for a module in a package. - module = imp.new_module('pkg.mod') + module = types.ModuleType('pkg.mod') module.__package__ = None self.verify(module, 'pkg') def test_setting_if_missing(self): # __package__ should be set if it is missing. - module = imp.new_module('mod') + module = types.ModuleType('mod') if hasattr(module, '__package__'): delattr(module, '__package__') self.verify(module, '') @@ -239,7 +239,7 @@ def test_leaving_alone(self): # If __package__ is set and not None then leave it alone. for value in (True, False): - module = imp.new_module('mod') + module = types.ModuleType('mod') module.__package__ = value self.verify(module, value) @@ -261,7 +261,7 @@ def test_no_attribute(self): loader = self.DummyLoader() - loader.module = imp.new_module('blah') + loader.module = types.ModuleType('blah') try: del loader.module.__loader__ except AttributeError: @@ -270,13 +270,13 @@ def test_attribute_is_None(self): loader = self.DummyLoader() - loader.module = imp.new_module('blah') + loader.module = types.ModuleType('blah') loader.module.__loader__ = None self.assertEqual(loader, loader.load_module('blah').__loader__) def test_not_reset(self): loader = self.DummyLoader() - loader.module = imp.new_module('blah') + loader.module = types.ModuleType('blah') loader.module.__loader__ = 42 self.assertEqual(42, loader.load_module('blah').__loader__) @@ -331,7 +331,7 @@ """ - tag = imp.get_tag() + tag = sys.implementation.cache_tag @unittest.skipUnless(sys.implementation.cache_tag is not None, 'requires sys.implementation.cache_tag not be None') diff --git a/Lib/test/test_importlib/util.py b/Lib/test/test_importlib/util.py --- a/Lib/test/test_importlib/util.py +++ b/Lib/test/test_importlib/util.py @@ -1,9 +1,9 @@ from contextlib import contextmanager -import imp import os.path from test import support import unittest import sys +import types CASE_INSENSITIVE_FS = True @@ -98,7 +98,7 @@ package = name.rsplit('.', 1)[0] else: package = import_name - module = imp.new_module(import_name) + module = types.ModuleType(import_name) module.__loader__ = self module.__file__ = '' module.__package__ = package -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 16 03:15:10 2013 From: python-checkins at python.org (gregory.p.smith) Date: Sun, 16 Jun 2013 03:15:10 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E3=29=3A_Prevent_a_poss?= =?utf-8?q?ible_double_close_of_parent_pipe_fds_when_the_subprocess?= Message-ID: <3bXyKB25fNz7Lkh@mail.python.org> http://hg.python.org/cpython/rev/be08c8b8c574 changeset: 84160:be08c8b8c574 branch: 3.3 parent: 84152:3d75bd69e5a9 user: Gregory P. Smith date: Sat Jun 15 18:04:26 2013 -0700 summary: Prevent a possible double close of parent pipe fds when the subprocess exec runs into an error. Prevent a regular multi-close of the /dev/null fd when any of stdin, stdout and stderr was set to DEVNULL. files: Lib/subprocess.py | 43 ++++++++++++++++++++-------------- 1 files changed, 25 insertions(+), 18 deletions(-) diff --git a/Lib/subprocess.py b/Lib/subprocess.py --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -810,6 +810,7 @@ if universal_newlines: self.stderr = io.TextIOWrapper(self.stderr) + self._closed_child_pipe_fds = False try: self._execute_child(args, executable, preexec_fn, close_fds, pass_fds, cwd, env, @@ -826,19 +827,21 @@ except EnvironmentError: pass # Ignore EBADF or other errors. - # Make sure the child pipes are closed as well. - to_close = [] - if stdin == PIPE: - to_close.append(p2cread) - if stdout == PIPE: - to_close.append(c2pwrite) - if stderr == PIPE: - to_close.append(errwrite) - for fd in to_close: - try: - os.close(fd) - except EnvironmentError: - pass + if not self._closed_child_pipe_fds: + to_close = [] + if stdin == PIPE: + to_close.append(p2cread) + if stdout == PIPE: + to_close.append(c2pwrite) + if stderr == PIPE: + to_close.append(errwrite) + if hasattr(self, '_devnull'): + to_close.append(self._devnull) + for fd in to_close: + try: + os.close(fd) + except EnvironmentError: + pass raise @@ -1383,14 +1386,18 @@ # be sure the FD is closed no matter what os.close(errpipe_write) - if p2cread != -1 and p2cwrite != -1: + # self._devnull is not always defined. + devnull_fd = getattr(self, '_devnull', None) + if p2cread != -1 and p2cwrite != -1 and p2cread != devnull_fd: os.close(p2cread) - if c2pwrite != -1 and c2pread != -1: + if c2pwrite != -1 and c2pread != -1 and c2pwrite != devnull_fd: os.close(c2pwrite) - if errwrite != -1 and errread != -1: + if errwrite != -1 and errread != -1 and errwrite != devnull_fd: os.close(errwrite) - if hasattr(self, '_devnull'): - os.close(self._devnull) + if devnull_fd is not None: + os.close(devnull_fd) + # Prevent a double close of these fds from __init__ on error. + self._closed_child_pipe_fds = True # Wait for exec to fail or succeed; possibly raising an # exception (limited in size) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 16 03:15:11 2013 From: python-checkins at python.org (gregory.p.smith) Date: Sun, 16 Jun 2013 03:15:11 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E3=29=3A_news_entry_for?= =?utf-8?q?_subprocess_double_close_fix=2E?= Message-ID: <3bXyKC3y15z7Lk6@mail.python.org> http://hg.python.org/cpython/rev/7e06a99bb821 changeset: 84161:7e06a99bb821 branch: 3.3 user: Gregory P. Smith date: Sat Jun 15 18:05:17 2013 -0700 summary: news entry for subprocess double close fix. files: Misc/NEWS | 4 ++++ 1 files changed, 4 insertions(+), 0 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -32,6 +32,10 @@ Library ------- +- subprocess: Prevent a possible double close of parent pipe fds when the + subprocess exec runs into an error. Prevent a regular multi-close of the + /dev/null fd when any of stdin, stdout and stderr was set to DEVNULL. + - Issue #16102: Make uuid._netbios_getnode() work again on Python 3. - Issue #18109: os.uname() now decodes fields from the locale encoding, and -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 16 03:15:13 2013 From: python-checkins at python.org (gregory.p.smith) Date: Sun, 16 Jun 2013 03:15:13 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_Prevent_a_possible_double_close_of_parent_pipe_fds_when_?= =?utf-8?q?the_subprocess?= Message-ID: <3bXyKF028pz7Lkj@mail.python.org> http://hg.python.org/cpython/rev/7dee56b6ff34 changeset: 84162:7dee56b6ff34 parent: 84159:5e8b377942f7 parent: 84161:7e06a99bb821 user: Gregory P. Smith date: Sat Jun 15 18:14:56 2013 -0700 summary: Prevent a possible double close of parent pipe fds when the subprocess exec runs into an error. Prevent a regular multi-close of the /dev/null fd when any of stdin, stdout and stderr was set to DEVNULL. files: Lib/subprocess.py | 43 ++++++++++++++++++++-------------- Misc/NEWS | 4 +++ 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/Lib/subprocess.py b/Lib/subprocess.py --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -828,6 +828,7 @@ if universal_newlines: self.stderr = io.TextIOWrapper(self.stderr) + self._closed_child_pipe_fds = False try: self._execute_child(args, executable, preexec_fn, close_fds, pass_fds, cwd, env, @@ -844,19 +845,21 @@ except OSError: pass # Ignore EBADF or other errors. - # Make sure the child pipes are closed as well. - to_close = [] - if stdin == PIPE: - to_close.append(p2cread) - if stdout == PIPE: - to_close.append(c2pwrite) - if stderr == PIPE: - to_close.append(errwrite) - for fd in to_close: - try: - os.close(fd) - except OSError: - pass + if not self._closed_child_pipe_fds: + to_close = [] + if stdin == PIPE: + to_close.append(p2cread) + if stdout == PIPE: + to_close.append(c2pwrite) + if stderr == PIPE: + to_close.append(errwrite) + if hasattr(self, '_devnull'): + to_close.append(self._devnull) + for fd in to_close: + try: + os.close(fd) + except OSError: + pass raise @@ -1363,14 +1366,18 @@ # be sure the FD is closed no matter what os.close(errpipe_write) - if p2cread != -1 and p2cwrite != -1: + # self._devnull is not always defined. + devnull_fd = getattr(self, '_devnull', None) + if p2cread != -1 and p2cwrite != -1 and p2cread != devnull_fd: os.close(p2cread) - if c2pwrite != -1 and c2pread != -1: + if c2pwrite != -1 and c2pread != -1 and c2pwrite != devnull_fd: os.close(c2pwrite) - if errwrite != -1 and errread != -1: + if errwrite != -1 and errread != -1 and errwrite != devnull_fd: os.close(errwrite) - if hasattr(self, '_devnull'): - os.close(self._devnull) + if devnull_fd is not None: + os.close(devnull_fd) + # Prevent a double close of these fds from __init__ on error. + self._closed_child_pipe_fds = True # Wait for exec to fail or succeed; possibly raising an # exception (limited in size) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -123,6 +123,10 @@ Library ------- +- subprocess: Prevent a possible double close of parent pipe fds when the + subprocess exec runs into an error. Prevent a regular multi-close of the + /dev/null fd when any of stdin, stdout and stderr was set to DEVNULL. + - Issue #18194: Introduce importlib.util.cache_from_source() and source_from_cache() while documenting the equivalent functions in imp as deprecated. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 16 03:49:42 2013 From: python-checkins at python.org (victor.stinner) Date: Sun, 16 Jun 2013 03:49:42 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?peps=3A_PEP_445=3A_more_background?= Message-ID: <3bXz524Vnhz7LjS@mail.python.org> http://hg.python.org/peps/rev/5e26a6276514 changeset: 4932:5e26a6276514 user: Victor Stinner date: Sun Jun 16 03:49:29 2013 +0200 summary: PEP 445: more background files: pep-0445.txt | 59 +++++++++++++++++++++++++++++++++++++++- 1 files changed, 58 insertions(+), 1 deletions(-) diff --git a/pep-0445.txt b/pep-0445.txt --- a/pep-0445.txt +++ b/pep-0445.txt @@ -151,13 +151,65 @@ XXX To be done (Kristj?n Valur J?nsson) XXX +External libraries +================== + +* glib: `g_mem_set_vtable() + `_ + + +Memory allocators +================= + +The C standard library provides the well known ``malloc()`` function. Its +implementation depends on the platform and of the C library. The GNU C library +uses a modified ptmalloc2, based on "Doug Lea's Malloc" (dlmalloc). FreeBSD +uses `jemalloc `_. Google provides +tcmalloc which is part of `gperftools `_. + +``malloc()`` uses two kinds of memory: heap and memory mappings. Memory +mappings are usually used for large allocations (ex: larger than 256 KB), +whereas the heap is used for small allocations. + +The heap is handled by ``brk()`` and ``sbrk()`` system calls on Linux, and is +contiguous. Memory mappings are handled by ``mmap()`` on UNIX and +``VirtualAlloc()`` on Windows, they are discontiguous. Releasing a memory +mapping gives back the memory immediatly to the system. For the heap, memory is +only gave back to the system if it is at the end of the heap. Otherwise, the +memory will only gave back to the system when all the memory located after the +released memory are also released. This limitation causes an issue called the +"memory fragmentation": the memory usage seen by the system may be much higher +than real usage. + +Windows provides a `Low-fragmentation Heap +`_. + +The Linux kernel uses `slab allocation +`_. + +The glib library has a `Memory Slice API +`_: +efficient way to allocate groups of equal-sized chunks of memory + + Links ===== -Memory allocators: +CPython issues related to memory allocation: * `Issue #3329: Add new APIs to customize memory allocators `_ +* `Issue #13483: Use VirtualAlloc to allocate memory arenas + `_ +* `Issue #16742: PyOS_Readline drops GIL and calls PyOS_StdioReadline, which + isn't thread safe `_ +* `Issue #18203: Replace calls to malloc() with PyMem_Malloc() + `_ +* `Issue #18227: Use Python memory allocators in external libraries like zlib + or OpenSSL `_ + +Projects analyzing the memory usage of Python applications: + * `pytracemalloc `_ * `Meliae: Python Memory Usage Analyzer @@ -167,6 +219,11 @@ * `PySizer (developed for Python 2.4) `_ +APIs to set a custom memory allocator and/or hook memory allocators: + +* `GNU libc: Memory Allocation Hooks + `_ + Other: * `Python benchmark suite -- Repository URL: http://hg.python.org/peps From python-checkins at python.org Sun Jun 16 04:01:12 2013 From: python-checkins at python.org (victor.stinner) Date: Sun, 16 Jun 2013 04:01:12 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?peps=3A_PEP_445?= Message-ID: <3bXzLJ3rDzzQWx@mail.python.org> http://hg.python.org/peps/rev/add8a583c05d changeset: 4933:add8a583c05d user: Victor Stinner date: Sun Jun 16 04:01:00 2013 +0200 summary: PEP 445 files: pep-0445.txt | 10 ++++++++-- 1 files changed, 8 insertions(+), 2 deletions(-) diff --git a/pep-0445.txt b/pep-0445.txt --- a/pep-0445.txt +++ b/pep-0445.txt @@ -99,8 +99,14 @@ with 2 functions with an additional *domain* argument: -* ``PyMem_GetAllocators(domain)`` -* ``PyMem_SetAllocators(domain, allocators)`` +* ``Py_GetAllocators(domain)`` +* ``Py_SetAllocators(domain, allocators)`` + +where domain is one of these values: + +* ``PYALLOC_PYMEM`` +* ``PYALLOC_PYMEM_RAW`` +* ``PYALLOC_PYOBJECT`` Setup Builtin Debug Hooks -- Repository URL: http://hg.python.org/peps From python-checkins at python.org Sun Jun 16 04:03:28 2013 From: python-checkins at python.org (victor.stinner) Date: Sun, 16 Jun 2013 04:03:28 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?peps=3A_PEP_445=3A_reorder?= Message-ID: <3bXzNw4pGyzStl@mail.python.org> http://hg.python.org/peps/rev/e3e07f064d7f changeset: 4934:e3e07f064d7f user: Victor Stinner date: Sun Jun 16 04:03:15 2013 +0200 summary: PEP 445: reorder files: pep-0445.txt | 8 ++++---- 1 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pep-0445.txt b/pep-0445.txt --- a/pep-0445.txt +++ b/pep-0445.txt @@ -60,15 +60,15 @@ - ``PyMem_SetupDebugHooks()`` +* ``PyMem_Malloc()`` and ``PyMem_Realloc()`` now always call ``malloc()`` and + ``realloc()``, instead of calling ``PyObject_Malloc()`` and + ``PyObject_Realloc()`` in debug mode + * ``PyObject_Malloc()`` now falls back on ``PyMem_Malloc()`` instead of ``malloc()`` if size is bigger than ``SMALL_REQUEST_THRESHOLD``, and ``PyObject_Realloc()`` falls back on ``PyMem_Realloc()`` instead of ``realloc()`` -* ``PyMem_Malloc()`` and ``PyMem_Realloc()`` now always call ``malloc()`` and - ``realloc()``, instead of calling ``PyObject_Malloc()`` and - ``PyObject_Realloc()`` in debug mode - Performances ============ -- Repository URL: http://hg.python.org/peps From python-checkins at python.org Sun Jun 16 05:24:19 2013 From: python-checkins at python.org (brett.cannon) Date: Sun, 16 Jun 2013 05:24:19 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Make_test=5Fbuiltin_work_w?= =?utf-8?q?hen_executed_directly?= Message-ID: <3bY1BC39KzzSrd@mail.python.org> http://hg.python.org/cpython/rev/5b90da280515 changeset: 84163:5b90da280515 user: Brett Cannon date: Sat Jun 15 23:24:11 2013 -0400 summary: Make test_builtin work when executed directly files: Lib/test/test_builtin.py | 5 ++++- 1 files changed, 4 insertions(+), 1 deletions(-) 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 @@ -584,7 +584,10 @@ raise frozendict_error("frozendict is readonly") # read-only builtins - frozen_builtins = frozendict(__builtins__) + if isinstance(__builtins__, types.ModuleType): + frozen_builtins = frozendict(__builtins__.__dict__) + else: + frozen_builtins = frozendict(__builtins__) code = compile("__builtins__['superglobal']=2; print(superglobal)", "test", "exec") self.assertRaises(frozendict_error, exec, code, {'__builtins__': frozen_builtins}) -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Sun Jun 16 05:52:13 2013 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Sun, 16 Jun 2013 05:52:13 +0200 Subject: [Python-checkins] Daily reference leaks (7dee56b6ff34): sum=2 Message-ID: results for 7dee56b6ff34 on branch "default" -------------------------------------------- test_support leaked [0, -1, 1] references, sum=0 test_support leaked [0, -1, 3] memory blocks, sum=2 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogtBhb00', '-x'] From python-checkins at python.org Sun Jun 16 17:38:30 2013 From: python-checkins at python.org (brett.cannon) Date: Sun, 16 Jun 2013 17:38:30 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Stop_using_the_deprecated_?= =?utf-8?q?unittest=2ETestCase=2EassertRaisesRegexp=28=29?= Message-ID: <3bYKTL0ZYJz7LkX@mail.python.org> http://hg.python.org/cpython/rev/4f7c25ab2ed2 changeset: 84164:4f7c25ab2ed2 user: Brett Cannon date: Sun Jun 16 11:37:57 2013 -0400 summary: Stop using the deprecated unittest.TestCase.assertRaisesRegexp() files: Lib/test/test_source_encoding.py | 10 +++++----- 1 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_source_encoding.py b/Lib/test/test_source_encoding.py --- a/Lib/test/test_source_encoding.py +++ b/Lib/test/test_source_encoding.py @@ -62,17 +62,17 @@ compile(b'# -*- coding: iso-8859-15 -*-\n', 'dummy', 'exec') compile(b'\xef\xbb\xbf\n', 'dummy', 'exec') compile(b'\xef\xbb\xbf# -*- coding: utf-8 -*-\n', 'dummy', 'exec') - with self.assertRaisesRegexp(SyntaxError, 'fake'): + with self.assertRaisesRegex(SyntaxError, 'fake'): compile(b'# -*- coding: fake -*-\n', 'dummy', 'exec') - with self.assertRaisesRegexp(SyntaxError, 'iso-8859-15'): + with self.assertRaisesRegex(SyntaxError, 'iso-8859-15'): compile(b'\xef\xbb\xbf# -*- coding: iso-8859-15 -*-\n', 'dummy', 'exec') - with self.assertRaisesRegexp(SyntaxError, 'BOM'): + with self.assertRaisesRegex(SyntaxError, 'BOM'): compile(b'\xef\xbb\xbf# -*- coding: iso-8859-15 -*-\n', 'dummy', 'exec') - with self.assertRaisesRegexp(SyntaxError, 'fake'): + with self.assertRaisesRegex(SyntaxError, 'fake'): compile(b'\xef\xbb\xbf# -*- coding: fake -*-\n', 'dummy', 'exec') - with self.assertRaisesRegexp(SyntaxError, 'BOM'): + with self.assertRaisesRegex(SyntaxError, 'BOM'): compile(b'\xef\xbb\xbf# -*- coding: fake -*-\n', 'dummy', 'exec') def test_bad_coding(self): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 16 19:00:54 2013 From: python-checkins at python.org (andrew.kuchling) Date: Sun, 16 Jun 2013 19:00:54 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogRGVzY3JpYmUgJ3N1?= =?utf-8?q?rrogateescape=27_in_the_documentation=2E?= Message-ID: <3bYMJQ2lJtz7Ljb@mail.python.org> http://hg.python.org/cpython/rev/55f611f55952 changeset: 84165:55f611f55952 branch: 3.3 parent: 84161:7e06a99bb821 user: Andrew Kuchling date: Sun Jun 16 12:58:48 2013 -0400 summary: Describe 'surrogateescape' in the documentation. Also, improve some docstring descriptions of the 'errors' parameter. Closes #14015. files: Doc/library/codecs.rst | 6 +++- Doc/library/functions.rst | 40 ++++++++++++++++++++------ Lib/codecs.py | 1 + Modules/_io/_iomodule.c | 4 +- Modules/_io/textio.c | 5 ++- 5 files changed, 41 insertions(+), 15 deletions(-) diff --git a/Doc/library/codecs.rst b/Doc/library/codecs.rst --- a/Doc/library/codecs.rst +++ b/Doc/library/codecs.rst @@ -78,7 +78,11 @@ reference (for encoding only) * ``'backslashreplace'``: replace with backslashed escape sequences (for encoding only) - * ``'surrogateescape'``: replace with surrogate U+DCxx, see :pep:`383` + * ``'surrogateescape'``: on decoding, replace with code points in the Unicode + Private Use Area ranging from U+DC80 to U+DCFF. These private code + points will then be turned back into the same bytes when the + ``surrogateescape`` error handler is used when encoding the data. + (See :pep:`383` for more.) as well as any other error handling name defined via :func:`register_error`. diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -895,16 +895,36 @@ the list of supported encodings. *errors* is an optional string that specifies how encoding and decoding - errors are to be handled--this cannot be used in binary mode. Pass - ``'strict'`` to raise a :exc:`ValueError` exception if there is an encoding - error (the default of ``None`` has the same effect), or pass ``'ignore'`` to - ignore errors. (Note that ignoring encoding errors can lead to data loss.) - ``'replace'`` causes a replacement marker (such as ``'?'``) to be inserted - where there is malformed data. When writing, ``'xmlcharrefreplace'`` - (replace with the appropriate XML character reference) or - ``'backslashreplace'`` (replace with backslashed escape sequences) can be - used. Any other error handling name that has been registered with - :func:`codecs.register_error` is also valid. + errors are to be handled--this cannot be used in binary mode. + A variety of standard error handlers are available, though any + error handling name that has been registered with + :func:`codecs.register_error` is also valid. The standard names + are: + + * ``'strict'`` to raise a :exc:`ValueError` exception if there is + an encoding error. The default value of ``None`` has the same + effect. + + * ``'ignore'`` ignores errors. Note that ignoring encoding errors + can lead to data loss. + + * ``'replace'`` causes a replacement marker (such as ``'?'``) to be inserted + where there is malformed data. + + * ``'surrogateescape'`` will represent any incorrect bytes as code + points in the Unicode Private Use Area ranging from U+DC80 to + U+DCFF. These private code points will then be turned back into + the same bytes when the ``surrogateescape`` error handler is used + when writing data. This is useful for processing files in an + unknown encoding. + + * ``'xmlcharrefreplace'`` is only supported when writing to a file. + Characters not supported by the encoding are replaced with the + appropriate XML character reference ``&#nnn;``. + + * ``'backslashreplace'`` (also only supported when writing) + replaces unsupported characters with Python's backslashed escape + sequences. .. index:: single: universal newlines; open() built-in function diff --git a/Lib/codecs.py b/Lib/codecs.py --- a/Lib/codecs.py +++ b/Lib/codecs.py @@ -105,6 +105,7 @@ Python will use the official U+FFFD REPLACEMENT CHARACTER for the builtin Unicode codecs on decoding and '?' on encoding. + 'surrogateescape' - replace with private codepoints U+DCnn. 'xmlcharrefreplace' - Replace with the appropriate XML character reference (only for encoding). 'backslashreplace' - Replace with backslashed escape sequences diff --git a/Modules/_io/_iomodule.c b/Modules/_io/_iomodule.c --- a/Modules/_io/_iomodule.c +++ b/Modules/_io/_iomodule.c @@ -168,8 +168,8 @@ "'strict' to raise a ValueError exception if there is an encoding error\n" "(the default of None has the same effect), or pass 'ignore' to ignore\n" "errors. (Note that ignoring encoding errors can lead to data loss.)\n" -"See the documentation for codecs.register for a list of the permitted\n" -"encoding error strings.\n" +"See the documentation for codecs.register or run 'help(codecs.Codec)'\n" +"for a list of the permitted encoding error strings.\n" "\n" "newline controls how universal newlines works (it only applies to text\n" "mode). It can be None, '', '\\n', '\\r', and '\\r\\n'. It works as\n" diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -642,8 +642,9 @@ "encoding gives the name of the encoding that the stream will be\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" + "errors determines the strictness of encoding and decoding (see\n" + "help(codecs.Codec) or the documentation for codecs.register) and\n" + "defaults to \"strict\".\n" "\n" "newline controls how line endings are handled. It can be None, '',\n" "'\\n', '\\r', and '\\r\\n'. It works as follows:\n" -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 16 19:04:47 2013 From: python-checkins at python.org (andrew.kuchling) Date: Sun, 16 Jun 2013 19:04:47 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_Merge_with_3=2E3?= Message-ID: <3bYMNv5QQQz7Ljb@mail.python.org> http://hg.python.org/cpython/rev/81648329b37a changeset: 84166:81648329b37a parent: 84164:4f7c25ab2ed2 parent: 84165:55f611f55952 user: Andrew Kuchling date: Sun Jun 16 13:02:55 2013 -0400 summary: Merge with 3.3 files: Doc/library/codecs.rst | 6 +++- Doc/library/functions.rst | 40 ++++++++++++++++++++------ Lib/codecs.py | 1 + Modules/_io/_iomodule.c | 4 +- Modules/_io/textio.c | 5 ++- 5 files changed, 41 insertions(+), 15 deletions(-) diff --git a/Doc/library/codecs.rst b/Doc/library/codecs.rst --- a/Doc/library/codecs.rst +++ b/Doc/library/codecs.rst @@ -78,7 +78,11 @@ reference (for encoding only) * ``'backslashreplace'``: replace with backslashed escape sequences (for encoding only) - * ``'surrogateescape'``: replace with surrogate U+DCxx, see :pep:`383` + * ``'surrogateescape'``: on decoding, replace with code points in the Unicode + Private Use Area ranging from U+DC80 to U+DCFF. These private code + points will then be turned back into the same bytes when the + ``surrogateescape`` error handler is used when encoding the data. + (See :pep:`383` for more.) as well as any other error handling name defined via :func:`register_error`. diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -905,16 +905,36 @@ the list of supported encodings. *errors* is an optional string that specifies how encoding and decoding - errors are to be handled--this cannot be used in binary mode. Pass - ``'strict'`` to raise a :exc:`ValueError` exception if there is an encoding - error (the default of ``None`` has the same effect), or pass ``'ignore'`` to - ignore errors. (Note that ignoring encoding errors can lead to data loss.) - ``'replace'`` causes a replacement marker (such as ``'?'``) to be inserted - where there is malformed data. When writing, ``'xmlcharrefreplace'`` - (replace with the appropriate XML character reference) or - ``'backslashreplace'`` (replace with backslashed escape sequences) can be - used. Any other error handling name that has been registered with - :func:`codecs.register_error` is also valid. + errors are to be handled--this cannot be used in binary mode. + A variety of standard error handlers are available, though any + error handling name that has been registered with + :func:`codecs.register_error` is also valid. The standard names + are: + + * ``'strict'`` to raise a :exc:`ValueError` exception if there is + an encoding error. The default value of ``None`` has the same + effect. + + * ``'ignore'`` ignores errors. Note that ignoring encoding errors + can lead to data loss. + + * ``'replace'`` causes a replacement marker (such as ``'?'``) to be inserted + where there is malformed data. + + * ``'surrogateescape'`` will represent any incorrect bytes as code + points in the Unicode Private Use Area ranging from U+DC80 to + U+DCFF. These private code points will then be turned back into + the same bytes when the ``surrogateescape`` error handler is used + when writing data. This is useful for processing files in an + unknown encoding. + + * ``'xmlcharrefreplace'`` is only supported when writing to a file. + Characters not supported by the encoding are replaced with the + appropriate XML character reference ``&#nnn;``. + + * ``'backslashreplace'`` (also only supported when writing) + replaces unsupported characters with Python's backslashed escape + sequences. .. index:: single: universal newlines; open() built-in function diff --git a/Lib/codecs.py b/Lib/codecs.py --- a/Lib/codecs.py +++ b/Lib/codecs.py @@ -105,6 +105,7 @@ Python will use the official U+FFFD REPLACEMENT CHARACTER for the builtin Unicode codecs on decoding and '?' on encoding. + 'surrogateescape' - replace with private codepoints U+DCnn. 'xmlcharrefreplace' - Replace with the appropriate XML character reference (only for encoding). 'backslashreplace' - Replace with backslashed escape sequences diff --git a/Modules/_io/_iomodule.c b/Modules/_io/_iomodule.c --- a/Modules/_io/_iomodule.c +++ b/Modules/_io/_iomodule.c @@ -168,8 +168,8 @@ "'strict' to raise a ValueError exception if there is an encoding error\n" "(the default of None has the same effect), or pass 'ignore' to ignore\n" "errors. (Note that ignoring encoding errors can lead to data loss.)\n" -"See the documentation for codecs.register for a list of the permitted\n" -"encoding error strings.\n" +"See the documentation for codecs.register or run 'help(codecs.Codec)'\n" +"for a list of the permitted encoding error strings.\n" "\n" "newline controls how universal newlines works (it only applies to text\n" "mode). It can be None, '', '\\n', '\\r', and '\\r\\n'. It works as\n" diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -642,8 +642,9 @@ "encoding gives the name of the encoding that the stream will be\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" + "errors determines the strictness of encoding and decoding (see\n" + "help(codecs.Codec) or the documentation for codecs.register) and\n" + "defaults to \"strict\".\n" "\n" "newline controls how line endings are handled. It can be None, '',\n" "'\\n', '\\r', and '\\r\\n'. It works as follows:\n" -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 16 19:14:15 2013 From: python-checkins at python.org (brett.cannon) Date: Sun, 16 Jun 2013 19:14:15 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2317177=3A_The_imp_?= =?utf-8?q?module_is_pending_deprecation=2E?= Message-ID: <3bYMbq4xFSz7Ljd@mail.python.org> http://hg.python.org/cpython/rev/1b8f08c4efd5 changeset: 84167:1b8f08c4efd5 parent: 84164:4f7c25ab2ed2 user: Brett Cannon date: Sun Jun 16 13:13:40 2013 -0400 summary: Issue #17177: The imp module is pending deprecation. To make sure there is no issue with code that is both Python 2 and 3 compatible, there are no plans to remove the module any sooner than Python 4 (unless the community moves to Python 3 solidly before then). files: Doc/library/imp.rst | 7 +- Doc/whatsnew/3.4.rst | 3 + Lib/imp.py | 93 ++++----- Lib/inspect.py | 4 +- Lib/modulefinder.py | 5 +- Lib/pkgutil.py | 22 +- Lib/runpy.py | 8 +- Lib/test/test_fork1.py | 2 +- Lib/test/test_imp.py | 4 +- Lib/test/test_import.py | 32 +- Lib/test/test_importlib/import_/test_api.py | 1 - Lib/test/test_importlib/source/test_file_loader.py | 1 - Lib/test/test_socketserver.py | 2 +- Lib/test/test_threaded_import.py | 2 +- Misc/NEWS | 2 + 15 files changed, 103 insertions(+), 85 deletions(-) diff --git a/Doc/library/imp.rst b/Doc/library/imp.rst --- a/Doc/library/imp.rst +++ b/Doc/library/imp.rst @@ -2,7 +2,7 @@ ================================================================ .. deprecated:: 3.4 - The :mod:`imp` package has been deprecated in favor of :mod:`importlib`. + The :mod:`imp` package is pending deprecation in favor of :mod:`importlib`. .. module:: imp :synopsis: Access the implementation of the import statement. @@ -232,7 +232,7 @@ Return the :pep:`3147` magic tag string matching this version of Python's magic number, as returned by :func:`get_magic`. - .. note:: + .. deprecated:: 3.4 You may use :attr:`sys.implementation.cache_tag` directly starting in Python 3.3. @@ -355,6 +355,9 @@ ``None`` is inserted into ``sys.path_importer_cache`` instead of an instance of :class:`NullImporter`. + .. deprecated:: 3.4 + Insert ``None`` into ``sys.path_importer_cache`` instead. + .. _examples-imp: diff --git a/Doc/whatsnew/3.4.rst b/Doc/whatsnew/3.4.rst --- a/Doc/whatsnew/3.4.rst +++ b/Doc/whatsnew/3.4.rst @@ -230,6 +230,9 @@ :meth:`importlib.abc.Loader.init_module_attrs` allows subclasses of a loader to more easily customize module loading. +* The :mod:`imp` module is pending deprecation. To keep compatibility with + Python 2/3 code bases, the module's removal is currently not scheduled. + Deprecated functions and types of the C API ------------------------------------------- diff --git a/Lib/imp.py b/Lib/imp.py --- a/Lib/imp.py +++ b/Lib/imp.py @@ -27,6 +27,9 @@ import types import warnings +warnings.warn("the imp module is deprecated in favour of importlib; " + "see the module's documentation for alternative uses", + PendingDeprecationWarning) # DEPRECATED SEARCH_ERROR = 0 @@ -98,9 +101,7 @@ def get_suffixes(): - warnings.warn('imp.get_suffixes() is deprecated; use the constants ' - 'defined on importlib.machinery instead', - DeprecationWarning, 2) + """**DEPRECATED**""" extensions = [(s, 'rb', C_EXTENSION) for s in machinery.EXTENSION_SUFFIXES] source = [(s, 'U', PY_SOURCE) for s in machinery.SOURCE_SUFFIXES] bytecode = [(s, 'rb', PY_COMPILED) for s in machinery.BYTECODE_SUFFIXES] @@ -110,7 +111,11 @@ class NullImporter: - """Null import object.""" + """**DEPRECATED** + + Null import object. + + """ def __init__(self, path): if path == '': @@ -152,10 +157,6 @@ def load_source(name, pathname, file=None): - msg = ('imp.load_source() is deprecated; use ' - 'importlib.machinery.SourceFileLoader(name, pathname).load_module()' - ' instead') - warnings.warn(msg, DeprecationWarning, 2) _LoadSourceCompatibility(name, pathname, file).load_module(name) module = sys.modules[name] # To allow reloading to potentially work, use a non-hacked loader which @@ -170,10 +171,7 @@ def load_compiled(name, pathname, file=None): - msg = ('imp.load_compiled() is deprecated; use ' - 'importlib.machinery.SourcelessFileLoader(name, pathname).' - 'load_module() instead ') - warnings.warn(msg, DeprecationWarning, 2) + """**DEPRECATED**""" _LoadCompiledCompatibility(name, pathname, file).load_module(name) module = sys.modules[name] # To allow reloading to potentially work, use a non-hacked loader which @@ -183,10 +181,7 @@ def load_package(name, path): - msg = ('imp.load_package() is deprecated; use either ' - 'importlib.machinery.SourceFileLoader() or ' - 'importlib.machinery.SourcelessFileLoader() instead') - warnings.warn(msg, DeprecationWarning, 2) + """**DEPRECATED**""" if os.path.isdir(path): extensions = (machinery.SOURCE_SUFFIXES[:] + machinery.BYTECODE_SUFFIXES[:]) @@ -208,32 +203,30 @@ """ suffix, mode, type_ = details - with warnings.catch_warnings(): - warnings.simplefilter('ignore') - if mode and (not mode.startswith(('r', 'U')) or '+' in mode): - raise ValueError('invalid file open mode {!r}'.format(mode)) - elif file is None and type_ in {PY_SOURCE, PY_COMPILED}: - msg = 'file object required for import (type code {})'.format(type_) - raise ValueError(msg) - elif type_ == PY_SOURCE: - return load_source(name, filename, file) - elif type_ == PY_COMPILED: - return load_compiled(name, filename, file) - elif type_ == C_EXTENSION and load_dynamic is not None: - if file is None: - with open(filename, 'rb') as opened_file: - return load_dynamic(name, filename, opened_file) - else: - return load_dynamic(name, filename, file) - elif type_ == PKG_DIRECTORY: - return load_package(name, filename) - elif type_ == C_BUILTIN: - return init_builtin(name) - elif type_ == PY_FROZEN: - return init_frozen(name) + if mode and (not mode.startswith(('r', 'U')) or '+' in mode): + raise ValueError('invalid file open mode {!r}'.format(mode)) + elif file is None and type_ in {PY_SOURCE, PY_COMPILED}: + msg = 'file object required for import (type code {})'.format(type_) + raise ValueError(msg) + elif type_ == PY_SOURCE: + return load_source(name, filename, file) + elif type_ == PY_COMPILED: + return load_compiled(name, filename, file) + elif type_ == C_EXTENSION and load_dynamic is not None: + if file is None: + with open(filename, 'rb') as opened_file: + return load_dynamic(name, filename, opened_file) else: - msg = "Don't know how to import {} (type code {})".format(name, type_) - raise ImportError(msg, name=name) + return load_dynamic(name, filename, file) + elif type_ == PKG_DIRECTORY: + return load_package(name, filename) + elif type_ == C_BUILTIN: + return init_builtin(name) + elif type_ == PY_FROZEN: + return init_frozen(name) + else: + msg = "Don't know how to import {} (type code {})".format(name, type_) + raise ImportError(msg, name=name) def find_module(name, path=None): @@ -269,16 +262,14 @@ file_path = os.path.join(package_directory, package_file_name) if os.path.isfile(file_path): return None, package_directory, ('', '', PKG_DIRECTORY) - with warnings.catch_warnings(): - warnings.simplefilter('ignore') - for suffix, mode, type_ in get_suffixes(): - file_name = name + suffix - file_path = os.path.join(entry, file_name) - if os.path.isfile(file_path): - break - else: - continue - break # Break out of outer loop when breaking out of inner loop. + for suffix, mode, type_ in get_suffixes(): + file_name = name + suffix + file_path = os.path.join(entry, file_name) + if os.path.isfile(file_path): + break + else: + continue + break # Break out of outer loop when breaking out of inner loop. else: raise ImportError(_ERR_MSG.format(name), name=name) diff --git a/Lib/inspect.py b/Lib/inspect.py --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -31,7 +31,6 @@ __author__ = ('Ka-Ping Yee ', 'Yury Selivanov ') -import imp import importlib.machinery import itertools import linecache @@ -440,6 +439,9 @@ """Get the module name, suffix, mode, and module type for a given file.""" warnings.warn('inspect.getmoduleinfo() is deprecated', DeprecationWarning, 2) + with warnings.catch_warnings(): + warnings.simplefilter('ignore', PendingDeprecationWarning) + import imp filename = os.path.basename(path) suffixes = [(-len(suffix), suffix, mode, mtype) for suffix, mode, mtype in imp.get_suffixes()] diff --git a/Lib/modulefinder.py b/Lib/modulefinder.py --- a/Lib/modulefinder.py +++ b/Lib/modulefinder.py @@ -1,13 +1,16 @@ """Find modules used by a script, using introspection.""" import dis -import imp import importlib.machinery import marshal import os import sys import types import struct +import warnings +with warnings.catch_warnings(): + warnings.simplefilter('ignore', PendingDeprecationWarning) + import imp # XXX Clean up once str8's cstor matches bytes. LOAD_CONST = bytes([dis.opname.index('LOAD_CONST')]) diff --git a/Lib/pkgutil.py b/Lib/pkgutil.py --- a/Lib/pkgutil.py +++ b/Lib/pkgutil.py @@ -1,13 +1,13 @@ """Utilities to support packages.""" from functools import singledispatch as simplegeneric -import imp import importlib +import importlib.util import os import os.path import sys from types import ModuleType -from warnings import warn +import warnings __all__ = [ 'get_importer', 'iter_importers', 'get_loader', 'find_loader', @@ -21,7 +21,7 @@ import marshal magic = stream.read(4) - if magic != imp.get_magic(): + if magic != importlib.util.MAGIC_NUMBER: return None stream.read(8) # Skip timestamp and size @@ -160,6 +160,13 @@ iter_importer_modules.register( importlib.machinery.FileFinder, _iter_file_finder_modules) + +def _import_imp(): + global imp + with warnings.catch_warnings(): + warnings.simplefilter('ignore', PendingDeprecationWarning) + imp = importlib.import_module('imp') + class ImpImporter: """PEP 302 Importer that wraps Python's "classic" import algorithm @@ -172,8 +179,10 @@ """ def __init__(self, path=None): - warn("This emulation is deprecated, use 'importlib' instead", + global imp + warnings.warn("This emulation is deprecated, use 'importlib' instead", DeprecationWarning) + _import_imp() self.path = path def find_module(self, fullname, path=None): @@ -238,8 +247,9 @@ code = source = None def __init__(self, fullname, file, filename, etc): - warn("This emulation is deprecated, use 'importlib' instead", - DeprecationWarning) + warnings.warn("This emulation is deprecated, use 'importlib' instead", + DeprecationWarning) + _import_imp() self.file = file self.filename = filename self.fullname = fullname diff --git a/Lib/runpy.py b/Lib/runpy.py --- a/Lib/runpy.py +++ b/Lib/runpy.py @@ -13,7 +13,6 @@ import os import sys import importlib.machinery # importlib first so we can test #15386 via -m -import imp import types from pkgutil import read_code, get_loader, get_importer @@ -224,7 +223,12 @@ run_name = "" pkg_name = run_name.rpartition(".")[0] importer = get_importer(path_name) - if isinstance(importer, (type(None), imp.NullImporter)): + # Trying to avoid importing imp so as to not consume the deprecation warning. + is_NullImporter = False + if type(importer).__module__ == 'imp': + if type(importer).__name__ == 'NullImporter': + is_NullImporter = True + if isinstance(importer, type(None)) or is_NullImporter: # Not a valid sys.path entry, so run the code directly # execfile() doesn't help as we want to allow compiled files code, mod_loader = _get_code_from_file(run_name, path_name) diff --git a/Lib/test/test_fork1.py b/Lib/test/test_fork1.py --- a/Lib/test/test_fork1.py +++ b/Lib/test/test_fork1.py @@ -1,7 +1,7 @@ """This test checks for correct fork() behavior. """ -import imp +import _imp as imp import os import signal import sys diff --git a/Lib/test/test_imp.py b/Lib/test/test_imp.py --- a/Lib/test/test_imp.py +++ b/Lib/test/test_imp.py @@ -2,7 +2,6 @@ import _thread except ImportError: _thread = None -import imp import importlib import os import os.path @@ -11,6 +10,9 @@ from test import support import unittest import warnings +with warnings.catch_warnings(): + warnings.simplefilter('ignore', PendingDeprecationWarning) + import imp def requires_load_dynamic(meth): diff --git a/Lib/test/test_import.py b/Lib/test/test_import.py --- a/Lib/test/test_import.py +++ b/Lib/test/test_import.py @@ -1,7 +1,7 @@ # We import importlib *ASAP* in order to test #15386 import importlib +import importlib.util import builtins -import imp from test.test_importlib.import_ import util as importlib_util import marshal import os @@ -221,7 +221,7 @@ with open(source, "w") as f: f.write("a = 10\nb=20//0\n") - self.assertRaises(ZeroDivisionError, imp.reload, mod) + self.assertRaises(ZeroDivisionError, importlib.reload, mod) # But we still expect the module to be in sys.modules. mod = sys.modules.get(TESTFN) self.assertIsNot(mod, None, "expected module to be in sys.modules") @@ -287,7 +287,7 @@ import sys class C: def __del__(self): - import imp + import importlib sys.argv.insert(0, C()) """)) script_helper.assert_python_ok(testfn) @@ -298,7 +298,7 @@ sys.path.insert(0, os.curdir) try: source = TESTFN + ".py" - compiled = imp.cache_from_source(source) + compiled = importlib.util.cache_from_source(source) with open(source, 'w') as f: pass try: @@ -339,7 +339,7 @@ def test_creation_mode(self): mask = 0o022 with temp_umask(mask), _ready_to_import() as (name, path): - cached_path = imp.cache_from_source(path) + cached_path = importlib.util.cache_from_source(path) module = __import__(name) if not os.path.exists(cached_path): self.fail("__import__ did not result in creation of " @@ -357,7 +357,7 @@ # permissions of .pyc should match those of .py, regardless of mask mode = 0o600 with temp_umask(0o022), _ready_to_import() as (name, path): - cached_path = imp.cache_from_source(path) + cached_path = importlib.util.cache_from_source(path) os.chmod(path, mode) __import__(name) if not os.path.exists(cached_path): @@ -372,7 +372,7 @@ def test_cached_readonly(self): mode = 0o400 with temp_umask(0o022), _ready_to_import() as (name, path): - cached_path = imp.cache_from_source(path) + cached_path = importlib.util.cache_from_source(path) os.chmod(path, mode) __import__(name) if not os.path.exists(cached_path): @@ -412,7 +412,7 @@ bytecode_only = path + "c" else: bytecode_only = path + "o" - os.rename(imp.cache_from_source(path), bytecode_only) + os.rename(importlib.util.cache_from_source(path), bytecode_only) m = __import__(name) self.assertEqual(m.x, 'rewritten') @@ -434,7 +434,7 @@ """ dir_name = os.path.abspath(TESTFN) file_name = os.path.join(dir_name, module_name) + os.extsep + "py" - compiled_name = imp.cache_from_source(file_name) + compiled_name = importlib.util.cache_from_source(file_name) def setUp(self): self.sys_path = sys.path[:] @@ -637,7 +637,7 @@ class PycacheTests(unittest.TestCase): # Test the various PEP 3147 related behaviors. - tag = imp.get_tag() + tag = sys.implementation.cache_tag def _clean(self): forget(TESTFN) @@ -685,7 +685,7 @@ # With PEP 3147 cache layout, removing the source but leaving the pyc # file does not satisfy the import. __import__(TESTFN) - pyc_file = imp.cache_from_source(self.source) + pyc_file = importlib.util.cache_from_source(self.source) self.assertTrue(os.path.exists(pyc_file)) os.remove(self.source) forget(TESTFN) @@ -710,7 +710,7 @@ def test___cached__(self): # Modules now also have an __cached__ that points to the pyc file. m = __import__(TESTFN) - pyc_file = imp.cache_from_source(TESTFN + '.py') + pyc_file = importlib.util.cache_from_source(TESTFN + '.py') self.assertEqual(m.__cached__, os.path.join(os.curdir, pyc_file)) @skip_if_dont_write_bytecode @@ -745,10 +745,10 @@ pass importlib.invalidate_caches() m = __import__('pep3147.foo') - init_pyc = imp.cache_from_source( + init_pyc = importlib.util.cache_from_source( os.path.join('pep3147', '__init__.py')) self.assertEqual(m.__cached__, os.path.join(os.curdir, init_pyc)) - foo_pyc = imp.cache_from_source(os.path.join('pep3147', 'foo.py')) + foo_pyc = importlib.util.cache_from_source(os.path.join('pep3147', 'foo.py')) self.assertEqual(sys.modules['pep3147.foo'].__cached__, os.path.join(os.curdir, foo_pyc)) @@ -772,10 +772,10 @@ unload('pep3147') importlib.invalidate_caches() m = __import__('pep3147.foo') - init_pyc = imp.cache_from_source( + init_pyc = importlib.util.cache_from_source( os.path.join('pep3147', '__init__.py')) self.assertEqual(m.__cached__, os.path.join(os.curdir, init_pyc)) - foo_pyc = imp.cache_from_source(os.path.join('pep3147', 'foo.py')) + foo_pyc = importlib.util.cache_from_source(os.path.join('pep3147', 'foo.py')) self.assertEqual(sys.modules['pep3147.foo'].__cached__, os.path.join(os.curdir, foo_pyc)) diff --git a/Lib/test/test_importlib/import_/test_api.py b/Lib/test/test_importlib/import_/test_api.py --- a/Lib/test/test_importlib/import_/test_api.py +++ b/Lib/test/test_importlib/import_/test_api.py @@ -1,6 +1,5 @@ from .. import util as importlib_test_util from . import util -import imp import sys import types import unittest diff --git a/Lib/test/test_importlib/source/test_file_loader.py b/Lib/test/test_importlib/source/test_file_loader.py --- a/Lib/test/test_importlib/source/test_file_loader.py +++ b/Lib/test/test_importlib/source/test_file_loader.py @@ -7,7 +7,6 @@ from . import util as source_util import errno -import imp import marshal import os import py_compile diff --git a/Lib/test/test_socketserver.py b/Lib/test/test_socketserver.py --- a/Lib/test/test_socketserver.py +++ b/Lib/test/test_socketserver.py @@ -2,8 +2,8 @@ Test suite for socketserver. """ +import _imp as imp import contextlib -import imp import os import select import signal diff --git a/Lib/test/test_threaded_import.py b/Lib/test/test_threaded_import.py --- a/Lib/test/test_threaded_import.py +++ b/Lib/test/test_threaded_import.py @@ -5,8 +5,8 @@ # complains several times about module random having no attribute # randrange, and then Python hangs. +import _imp as imp import os -import imp import importlib import sys import time diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -123,6 +123,8 @@ Library ------- +- Issue #17177: The imp module is pending deprecation. + - subprocess: Prevent a possible double close of parent pipe fds when the subprocess exec runs into an error. Prevent a regular multi-close of the /dev/null fd when any of stdin, stdout and stderr was set to DEVNULL. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 16 19:14:17 2013 From: python-checkins at python.org (brett.cannon) Date: Sun, 16 Jun 2013 19:14:17 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_default_-=3E_default?= =?utf-8?q?=29=3A_merge?= Message-ID: <3bYMbs0sl3z7Ljx@mail.python.org> http://hg.python.org/cpython/rev/7ca3e27e3b1a changeset: 84168:7ca3e27e3b1a parent: 84167:1b8f08c4efd5 parent: 84166:81648329b37a user: Brett Cannon date: Sun Jun 16 13:14:06 2013 -0400 summary: merge files: Doc/library/codecs.rst | 6 +++- Doc/library/functions.rst | 40 ++++++++++++++++++++------ Lib/codecs.py | 1 + Modules/_io/_iomodule.c | 4 +- Modules/_io/textio.c | 5 ++- 5 files changed, 41 insertions(+), 15 deletions(-) diff --git a/Doc/library/codecs.rst b/Doc/library/codecs.rst --- a/Doc/library/codecs.rst +++ b/Doc/library/codecs.rst @@ -78,7 +78,11 @@ reference (for encoding only) * ``'backslashreplace'``: replace with backslashed escape sequences (for encoding only) - * ``'surrogateescape'``: replace with surrogate U+DCxx, see :pep:`383` + * ``'surrogateescape'``: on decoding, replace with code points in the Unicode + Private Use Area ranging from U+DC80 to U+DCFF. These private code + points will then be turned back into the same bytes when the + ``surrogateescape`` error handler is used when encoding the data. + (See :pep:`383` for more.) as well as any other error handling name defined via :func:`register_error`. diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -905,16 +905,36 @@ the list of supported encodings. *errors* is an optional string that specifies how encoding and decoding - errors are to be handled--this cannot be used in binary mode. Pass - ``'strict'`` to raise a :exc:`ValueError` exception if there is an encoding - error (the default of ``None`` has the same effect), or pass ``'ignore'`` to - ignore errors. (Note that ignoring encoding errors can lead to data loss.) - ``'replace'`` causes a replacement marker (such as ``'?'``) to be inserted - where there is malformed data. When writing, ``'xmlcharrefreplace'`` - (replace with the appropriate XML character reference) or - ``'backslashreplace'`` (replace with backslashed escape sequences) can be - used. Any other error handling name that has been registered with - :func:`codecs.register_error` is also valid. + errors are to be handled--this cannot be used in binary mode. + A variety of standard error handlers are available, though any + error handling name that has been registered with + :func:`codecs.register_error` is also valid. The standard names + are: + + * ``'strict'`` to raise a :exc:`ValueError` exception if there is + an encoding error. The default value of ``None`` has the same + effect. + + * ``'ignore'`` ignores errors. Note that ignoring encoding errors + can lead to data loss. + + * ``'replace'`` causes a replacement marker (such as ``'?'``) to be inserted + where there is malformed data. + + * ``'surrogateescape'`` will represent any incorrect bytes as code + points in the Unicode Private Use Area ranging from U+DC80 to + U+DCFF. These private code points will then be turned back into + the same bytes when the ``surrogateescape`` error handler is used + when writing data. This is useful for processing files in an + unknown encoding. + + * ``'xmlcharrefreplace'`` is only supported when writing to a file. + Characters not supported by the encoding are replaced with the + appropriate XML character reference ``&#nnn;``. + + * ``'backslashreplace'`` (also only supported when writing) + replaces unsupported characters with Python's backslashed escape + sequences. .. index:: single: universal newlines; open() built-in function diff --git a/Lib/codecs.py b/Lib/codecs.py --- a/Lib/codecs.py +++ b/Lib/codecs.py @@ -105,6 +105,7 @@ Python will use the official U+FFFD REPLACEMENT CHARACTER for the builtin Unicode codecs on decoding and '?' on encoding. + 'surrogateescape' - replace with private codepoints U+DCnn. 'xmlcharrefreplace' - Replace with the appropriate XML character reference (only for encoding). 'backslashreplace' - Replace with backslashed escape sequences diff --git a/Modules/_io/_iomodule.c b/Modules/_io/_iomodule.c --- a/Modules/_io/_iomodule.c +++ b/Modules/_io/_iomodule.c @@ -168,8 +168,8 @@ "'strict' to raise a ValueError exception if there is an encoding error\n" "(the default of None has the same effect), or pass 'ignore' to ignore\n" "errors. (Note that ignoring encoding errors can lead to data loss.)\n" -"See the documentation for codecs.register for a list of the permitted\n" -"encoding error strings.\n" +"See the documentation for codecs.register or run 'help(codecs.Codec)'\n" +"for a list of the permitted encoding error strings.\n" "\n" "newline controls how universal newlines works (it only applies to text\n" "mode). It can be None, '', '\\n', '\\r', and '\\r\\n'. It works as\n" diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -642,8 +642,9 @@ "encoding gives the name of the encoding that the stream will be\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" + "errors determines the strictness of encoding and decoding (see\n" + "help(codecs.Codec) or the documentation for codecs.register) and\n" + "defaults to \"strict\".\n" "\n" "newline controls how line endings are handled. It can be None, '',\n" "'\\n', '\\r', and '\\r\\n'. It works as follows:\n" -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 16 20:57:07 2013 From: python-checkins at python.org (brett.cannon) Date: Sun, 16 Jun 2013 20:57:07 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issues_=2318058=2C_18057?= =?utf-8?q?=3A_Make_importlib=2E=5Fbootstrap=2ENamespaceLoader?= Message-ID: <3bYPtW3C1Fz7Ll7@mail.python.org> http://hg.python.org/cpython/rev/ebec625b13f9 changeset: 84169:ebec625b13f9 user: Brett Cannon date: Sun Jun 16 14:56:58 2013 -0400 summary: Issues #18058, 18057: Make importlib._bootstrap.NamespaceLoader conform the the InspectLoader ABC. Perk of this is that runpy/-m can now work with namespace packages. files: Lib/importlib/_bootstrap.py | 15 +- Lib/importlib/abc.py | 2 +- Lib/test/test_namespace_pkgs.py | 29 +- Misc/NEWS | 4 + Python/importlib.h | 2185 +++++++++--------- 5 files changed, 1156 insertions(+), 1079 deletions(-) diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py --- a/Lib/importlib/_bootstrap.py +++ b/Lib/importlib/_bootstrap.py @@ -1238,12 +1238,25 @@ def module_repr(cls, module): return "".format(module.__name__) + def is_package(self, fullname): + return True + + def get_source(self, fullname): + return '' + + def get_code(self, fullname): + return compile('', '', 'exec', dont_inherit=True) + + def init_module_attrs(self, module): + module.__loader__ = self + module.__package__ = module.__name__ + def load_module(self, fullname): """Load a namespace module.""" _verbose_message('namespace module loaded with path {!r}', self._path) with module_to_load(fullname) as module: + self.init_module_attrs(module) module.__path__ = self._path - module.__package__ = fullname return module diff --git a/Lib/importlib/abc.py b/Lib/importlib/abc.py --- a/Lib/importlib/abc.py +++ b/Lib/importlib/abc.py @@ -188,7 +188,7 @@ load_module = _bootstrap._LoaderBasics.load_module _register(InspectLoader, machinery.BuiltinImporter, machinery.FrozenImporter, - machinery.ExtensionFileLoader) + machinery.ExtensionFileLoader, _bootstrap.NamespaceLoader) class ExecutionLoader(InspectLoader): diff --git a/Lib/test/test_namespace_pkgs.py b/Lib/test/test_namespace_pkgs.py --- a/Lib/test/test_namespace_pkgs.py +++ b/Lib/test/test_namespace_pkgs.py @@ -1,7 +1,11 @@ +import contextlib +from importlib._bootstrap import NamespaceLoader +import importlib.abc +import importlib.machinery +import os import sys -import contextlib +import types import unittest -import os from test.test_importlib import util from test.support import run_unittest @@ -286,9 +290,24 @@ self.assertEqual(a_test.attr, 'in module') -def test_main(): - run_unittest(*NamespacePackageTest.__subclasses__()) +class ABCTests(unittest.TestCase): + + def setUp(self): + self.loader = NamespaceLoader('foo', ['pkg'], + importlib.machinery.PathFinder) + + def test_is_package(self): + self.assertTrue(self.loader.is_package('foo')) + + def test_get_code(self): + self.assertTrue(isinstance(self.loader.get_code('foo'), types.CodeType)) + + def test_get_source(self): + self.assertEqual(self.loader.get_source('foo'), '') + + def test_abc_isinstance(self): + self.assertTrue(isinstance(self.loader, importlib.abc.InspectLoader)) if __name__ == "__main__": - test_main() + unittest.main() diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -123,6 +123,10 @@ Library ------- +- Issue #18058, 18057: Make the namespace package loader meet the + importlib.abc.InspectLoader ABC, allowing for namespace packages to work with + runpy. + - Issue #17177: The imp module is pending deprecation. - subprocess: Prevent a possible double close of parent pipe fds when the diff --git a/Python/importlib.h b/Python/importlib.h --- a/Python/importlib.h +++ b/Python/importlib.h [stripped] -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 16 21:20:55 2013 From: python-checkins at python.org (brett.cannon) Date: Sun, 16 Jun 2013 21:20:55 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Check_that_warnings=2Eshow?= =?utf-8?q?warning=28=29_is_not_changed=2E?= Message-ID: <3bYQPz0TmRzSpD@mail.python.org> http://hg.python.org/cpython/rev/59a11c81dd3c changeset: 84170:59a11c81dd3c user: Brett Cannon date: Sun Jun 16 15:20:48 2013 -0400 summary: Check that warnings.showwarning() is not changed. files: Lib/test/regrtest.py | 7 ++++++- 1 files changed, 6 insertions(+), 1 deletions(-) diff --git a/Lib/test/regrtest.py b/Lib/test/regrtest.py --- a/Lib/test/regrtest.py +++ b/Lib/test/regrtest.py @@ -1062,7 +1062,7 @@ 'sys.warnoptions', 'threading._dangling', 'multiprocessing.process._dangling', 'sysconfig._CONFIG_VARS', 'sysconfig._INSTALL_SCHEMES', - 'support.TESTFN', 'locale', + 'support.TESTFN', 'locale', 'warnings.showwarning', ) def get_sys_argv(self): @@ -1244,6 +1244,11 @@ for lc, setting in saved: locale.setlocale(lc, setting) + def get_warnings_showwarning(self): + return warnings.showwarning + def restore_warnings_showwarning(self, fxn): + warnings.showwarning = fxn + def resource_info(self): for name in self.resources: method_suffix = name.replace('.', '_') -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 16 23:23:16 2013 From: python-checkins at python.org (brett.cannon) Date: Sun, 16 Jun 2013 23:23:16 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2318115=3A_Abstract?= =?utf-8?q?_out_managing_the_cleanup_of_modules_to_use_in?= Message-ID: <3bYT785bTXz7Lkd@mail.python.org> http://hg.python.org/cpython/rev/205aa49e5cd5 changeset: 84171:205aa49e5cd5 user: Brett Cannon date: Sun Jun 16 17:23:06 2013 -0400 summary: Issue #18115: Abstract out managing the cleanup of modules to use in loaders where C code provides the loaded module. files: Lib/importlib/_bootstrap.py | 47 +- Python/importlib.h | 4919 +++++++++++----------- 2 files changed, 2498 insertions(+), 2468 deletions(-) diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py --- a/Lib/importlib/_bootstrap.py +++ b/Lib/importlib/_bootstrap.py @@ -475,8 +475,24 @@ print(message.format(*args), file=sys.stderr) +class _ManageReload: + + def __init__(self, name): + self._name = name + + def __enter__(self): + self._is_reload = self._name in sys.modules + + def __exit__(self, *args): + if any(arg is not None for arg in args) and not self._is_reload: + try: + del sys.modules[self._name] + except KeyError: + pass + + # Written as a class only because contextlib is not available. -class _ModuleManager: +class _ModuleManager(_ManageReload): """Context manager which returns the module to be loaded. @@ -490,12 +506,12 @@ The reset_name argument specifies whether to unconditionally reset the __name__ attribute if the module is found to be a reload. """ - self._name = name + super().__init__(name) self._reset_name = reset_name def __enter__(self): + super().__enter__() self._module = sys.modules.get(self._name) - self._is_reload = self._module is not None if not self._is_reload: # This must be done before open() is called as the 'io' module # implicitly imports 'locale' and would otherwise trigger an @@ -510,14 +526,12 @@ self._module.__name__ = self._name except AttributeError: pass - return self._module def __exit__(self, *args): self._module.__initializing__ = False del self._module - if any(arg is not None for arg in args) and not self._is_reload: - del sys.modules[self._name] + super().__exit__(*args) def module_to_load(name, *, reset_name=True): @@ -741,13 +755,8 @@ @_requires_builtin def load_module(cls, fullname): """Load a built-in module.""" - is_reload = fullname in sys.modules - try: + with _ManageReload(fullname): return _call_with_frames_removed(_imp.init_builtin, fullname) - except: - if not is_reload and fullname in sys.modules: - del sys.modules[fullname] - raise @classmethod @_requires_builtin @@ -792,16 +801,11 @@ @_requires_frozen def load_module(cls, fullname): """Load a frozen module.""" - is_reload = fullname in sys.modules - try: + with _ManageReload(fullname): m = _call_with_frames_removed(_imp.init_frozen, fullname) # Let our own module_repr() method produce a suitable repr. del m.__file__ return m - except: - if not is_reload and fullname in sys.modules: - del sys.modules[fullname] - raise @classmethod @_requires_frozen @@ -1147,18 +1151,13 @@ @set_loader def load_module(self, fullname): """Load an extension module.""" - is_reload = fullname in sys.modules - try: + with _ManageReload(fullname): module = _call_with_frames_removed(_imp.load_dynamic, fullname, self.path) _verbose_message('extension module loaded from {!r}', self.path) if self.is_package(fullname) and not hasattr(module, '__path__'): module.__path__ = [_path_split(self.path)[0]] return module - except: - if not is_reload and fullname in sys.modules: - del sys.modules[fullname] - raise def is_package(self, fullname): """Return True if the extension module is a package.""" diff --git a/Python/importlib.h b/Python/importlib.h --- a/Python/importlib.h +++ b/Python/importlib.h [stripped] -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 17 00:06:02 2013 From: python-checkins at python.org (brett.cannon) Date: Mon, 17 Jun 2013 00:06:02 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_importlib=2Eabc=2ESourceLo?= =?utf-8?q?ader=2Eget=5Fsource=28=29_was_re-raising_SyntaxError_and?= Message-ID: <3bYV4V3p4Jz7LjT@mail.python.org> http://hg.python.org/cpython/rev/e353f64dfd95 changeset: 84172:e353f64dfd95 user: Brett Cannon date: Sun Jun 16 18:05:54 2013 -0400 summary: importlib.abc.SourceLoader.get_source() was re-raising SyntaxError and UnicodeDecodeError as ImportError. That was over-reaching the point of raising ImportError in get_source() (which is to signal the source code was not found when it should have). Conflating the two exceptions with ImportError could lead to masking errors with the source which should be known outside of whether there was an error simply getting the source to begin with. files: Doc/whatsnew/3.4.rst | 9 + Lib/importlib/_bootstrap.py | 14 +- Misc/NEWS | 3 + Python/importlib.h | 3428 +++++++++++----------- 4 files changed, 1721 insertions(+), 1733 deletions(-) diff --git a/Doc/whatsnew/3.4.rst b/Doc/whatsnew/3.4.rst --- a/Doc/whatsnew/3.4.rst +++ b/Doc/whatsnew/3.4.rst @@ -282,3 +282,12 @@ it would write to is a symlink or a non-regular file. This is to act as a warning that import will overwrite those files with a regular file regardless of what type of file path they were originally. + +* :meth:`importlib.abc.SourceLoader.get_source` no longer raises + :exc:`ImportError` when the source code being loaded triggers a + :exc:`SyntaxError` or :exc:`UnicodeDecodeError`. As :exc:`ImportError` is + meant to be raised only when source code cannot be found but it should, it was + felt to be over-reaching/overloading of that meaning when the source code is + found but improperly structured. If you were catching ImportError before and + wish to continue to ignore syntax or decoding issues, catch all three + exceptions now. \ No newline at end of file diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py --- a/Lib/importlib/_bootstrap.py +++ b/Lib/importlib/_bootstrap.py @@ -959,25 +959,17 @@ def get_source(self, fullname): """Concrete implementation of InspectLoader.get_source.""" - import tokenize path = self.get_filename(fullname) try: source_bytes = self.get_data(path) except OSError as exc: raise ImportError("source not available through get_data()", name=fullname) from exc + import tokenize readsource = _io.BytesIO(source_bytes).readline - try: - encoding = tokenize.detect_encoding(readsource) - except SyntaxError as exc: - raise ImportError("Failed to detect encoding", - name=fullname) from exc + encoding = tokenize.detect_encoding(readsource) newline_decoder = _io.IncrementalNewlineDecoder(None, True) - try: - return newline_decoder.decode(source_bytes.decode(encoding[0])) - except UnicodeDecodeError as exc: - raise ImportError("Failed to decode source file", - name=fullname) from exc + return newline_decoder.decode(source_bytes.decode(encoding[0])) def source_to_code(self, data, path, *, _optimize=-1): """Return the code object compiled from source. diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -123,6 +123,9 @@ Library ------- +- importlib.abc.SourceLoader.get_source() no longer changes SyntaxError or + UnicodeDecodeError into ImportError. + - Issue #18058, 18057: Make the namespace package loader meet the importlib.abc.InspectLoader ABC, allowing for namespace packages to work with runpy. diff --git a/Python/importlib.h b/Python/importlib.h --- a/Python/importlib.h +++ b/Python/importlib.h [stripped] -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 17 00:38:02 2013 From: python-checkins at python.org (brett.cannon) Date: Mon, 17 Jun 2013 00:38:02 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2318076=3A_Introduc?= =?utf-8?q?e_imoportlib=2Eutil=2Edecode=5Fsource=28=29=2E?= Message-ID: <3bYVnQ3fzTzRdJ@mail.python.org> http://hg.python.org/cpython/rev/bdd60bedf933 changeset: 84173:bdd60bedf933 user: Brett Cannon date: Sun Jun 16 18:37:53 2013 -0400 summary: Issue #18076: Introduce imoportlib.util.decode_source(). The helper function makes it easier to implement imoprtlib.abc.InspectLoader.get_source() by making that function require just the raw bytes for source code and handling all other details. files: Doc/library/importlib.rst | 8 + Lib/importlib/_bootstrap.py | 18 +- Lib/importlib/util.py | 1 + Lib/test/test_importlib/test_util.py | 21 + Misc/NEWS | 2 + Python/importlib.h | 7149 +++++++------ 6 files changed, 3628 insertions(+), 3571 deletions(-) diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst --- a/Doc/library/importlib.rst +++ b/Doc/library/importlib.rst @@ -916,6 +916,14 @@ .. versionadded:: 3.4 +.. function:: decode_source(source_bytes) + + Decode the given bytes representing source code and return it as a string + with universal newlines (as required by + :meth:`importlib.abc.InspectLoader.get_source`). + + .. versionadded:: 3.4 + .. function:: resolve_name(name, package) Resolve a relative module name to an absolute one. diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py --- a/Lib/importlib/_bootstrap.py +++ b/Lib/importlib/_bootstrap.py @@ -723,6 +723,18 @@ return data +def decode_source(source_bytes): + """Decode bytes representing source code and return the string. + + Universal newline support is used in the decoding. + """ + import tokenize # To avoid bootstrap issues. + source_bytes_readline = _io.BytesIO(source_bytes).readline + encoding = tokenize.detect_encoding(source_bytes_readline) + newline_decoder = _io.IncrementalNewlineDecoder(None, True) + return newline_decoder.decode(source_bytes.decode(encoding[0])) + + # Loaders ##################################################################### class BuiltinImporter: @@ -965,11 +977,7 @@ except OSError as exc: raise ImportError("source not available through get_data()", name=fullname) from exc - import tokenize - readsource = _io.BytesIO(source_bytes).readline - encoding = tokenize.detect_encoding(readsource) - newline_decoder = _io.IncrementalNewlineDecoder(None, True) - return newline_decoder.decode(source_bytes.decode(encoding[0])) + return decode_source(source_bytes) def source_to_code(self, data, path, *, _optimize=-1): """Return the code object compiled from source. diff --git a/Lib/importlib/util.py b/Lib/importlib/util.py --- a/Lib/importlib/util.py +++ b/Lib/importlib/util.py @@ -2,6 +2,7 @@ from ._bootstrap import MAGIC_NUMBER from ._bootstrap import cache_from_source +from ._bootstrap import decode_source from ._bootstrap import module_to_load from ._bootstrap import set_loader from ._bootstrap import set_package diff --git a/Lib/test/test_importlib/test_util.py b/Lib/test/test_importlib/test_util.py --- a/Lib/test/test_importlib/test_util.py +++ b/Lib/test/test_importlib/test_util.py @@ -9,6 +9,27 @@ import warnings +class DecodeSourceBytesTests(unittest.TestCase): + + source = "string ='?'" + + def test_ut8_default(self): + source_bytes = self.source.encode('utf-8') + self.assertEqual(util.decode_source(source_bytes), self.source) + + def test_specified_encoding(self): + source = '# coding=latin-1\n' + self.source + source_bytes = source.encode('latin-1') + assert source_bytes != source.encode('utf-8') + self.assertEqual(util.decode_source(source_bytes), source) + + def test_universal_newlines(self): + source = '\r\n'.join([self.source, self.source]) + source_bytes = source.encode('utf-8') + self.assertEqual(util.decode_source(source_bytes), + '\n'.join([self.source, self.source])) + + class ModuleToLoadTests(unittest.TestCase): module_name = 'ModuleManagerTest_module' diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -123,6 +123,8 @@ Library ------- +- Issue #18076: Introduce importlib.util.decode_source(). + - importlib.abc.SourceLoader.get_source() no longer changes SyntaxError or UnicodeDecodeError into ImportError. diff --git a/Python/importlib.h b/Python/importlib.h --- a/Python/importlib.h +++ b/Python/importlib.h [stripped] -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 17 01:09:55 2013 From: python-checkins at python.org (brett.cannon) Date: Mon, 17 Jun 2013 01:09:55 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E3=29=3A_Fix_a_misnamin?= =?utf-8?q?g_of_a_method_and_an_argument?= Message-ID: <3bYWVC3jYcz7LjT@mail.python.org> http://hg.python.org/cpython/rev/e13371d5b4e4 changeset: 84174:e13371d5b4e4 branch: 3.3 parent: 84144:61a1aa69e83e user: Brett Cannon date: Sun Jun 16 19:06:55 2013 -0400 summary: Fix a misnaming of a method and an argument files: Doc/library/importlib.rst | 2 +- Lib/importlib/_bootstrap.py | 4 +- Python/importlib.h | 2156 +++++++++++----------- 3 files changed, 1081 insertions(+), 1081 deletions(-) diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst --- a/Doc/library/importlib.rst +++ b/Doc/library/importlib.rst @@ -714,7 +714,7 @@ The path the finder will search in. - .. method:: find_module(fullname) + .. method:: find_loader(fullname) Attempt to find the loader to handle *fullname* within :attr:`path`. diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py --- a/Lib/importlib/_bootstrap.py +++ b/Lib/importlib/_bootstrap.py @@ -1329,12 +1329,12 @@ """ - def __init__(self, path, *details): + def __init__(self, path, *loader_details): """Initialize with the path to search on and a variable number of 2-tuples containing the loader and the file suffixes the loader recognizes.""" loaders = [] - for loader, suffixes in details: + for loader, suffixes in loader_details: loaders.extend((suffix, loader) for suffix in suffixes) self._loaders = loaders # Base (directory) path diff --git a/Python/importlib.h b/Python/importlib.h --- a/Python/importlib.h +++ b/Python/importlib.h [stripped] -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 17 01:09:56 2013 From: python-checkins at python.org (brett.cannon) Date: Mon, 17 Jun 2013 01:09:56 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAobWVyZ2UgMy4zIC0+IDMuMyk6?= =?utf-8?q?_merge?= Message-ID: <3bYWVD75lNz7Ljg@mail.python.org> http://hg.python.org/cpython/rev/e7ab65cf1e2a changeset: 84175:e7ab65cf1e2a branch: 3.3 parent: 84174:e13371d5b4e4 parent: 84165:55f611f55952 user: Brett Cannon date: Sun Jun 16 19:07:16 2013 -0400 summary: merge files: Doc/library/codecs.rst | 6 +++- Doc/library/functions.rst | 40 ++++++++++++++++++------ Lib/codecs.py | 1 + Lib/subprocess.py | 43 +++++++++++++++----------- Lib/test/test_curses.py | 13 ++++++++ Misc/NEWS | 7 ++++ Modules/_curses_panel.c | 4 ++ Modules/_io/_iomodule.c | 4 +- Modules/_io/textio.c | 5 +- 9 files changed, 90 insertions(+), 33 deletions(-) diff --git a/Doc/library/codecs.rst b/Doc/library/codecs.rst --- a/Doc/library/codecs.rst +++ b/Doc/library/codecs.rst @@ -78,7 +78,11 @@ reference (for encoding only) * ``'backslashreplace'``: replace with backslashed escape sequences (for encoding only) - * ``'surrogateescape'``: replace with surrogate U+DCxx, see :pep:`383` + * ``'surrogateescape'``: on decoding, replace with code points in the Unicode + Private Use Area ranging from U+DC80 to U+DCFF. These private code + points will then be turned back into the same bytes when the + ``surrogateescape`` error handler is used when encoding the data. + (See :pep:`383` for more.) as well as any other error handling name defined via :func:`register_error`. diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -895,16 +895,36 @@ the list of supported encodings. *errors* is an optional string that specifies how encoding and decoding - errors are to be handled--this cannot be used in binary mode. Pass - ``'strict'`` to raise a :exc:`ValueError` exception if there is an encoding - error (the default of ``None`` has the same effect), or pass ``'ignore'`` to - ignore errors. (Note that ignoring encoding errors can lead to data loss.) - ``'replace'`` causes a replacement marker (such as ``'?'``) to be inserted - where there is malformed data. When writing, ``'xmlcharrefreplace'`` - (replace with the appropriate XML character reference) or - ``'backslashreplace'`` (replace with backslashed escape sequences) can be - used. Any other error handling name that has been registered with - :func:`codecs.register_error` is also valid. + errors are to be handled--this cannot be used in binary mode. + A variety of standard error handlers are available, though any + error handling name that has been registered with + :func:`codecs.register_error` is also valid. The standard names + are: + + * ``'strict'`` to raise a :exc:`ValueError` exception if there is + an encoding error. The default value of ``None`` has the same + effect. + + * ``'ignore'`` ignores errors. Note that ignoring encoding errors + can lead to data loss. + + * ``'replace'`` causes a replacement marker (such as ``'?'``) to be inserted + where there is malformed data. + + * ``'surrogateescape'`` will represent any incorrect bytes as code + points in the Unicode Private Use Area ranging from U+DC80 to + U+DCFF. These private code points will then be turned back into + the same bytes when the ``surrogateescape`` error handler is used + when writing data. This is useful for processing files in an + unknown encoding. + + * ``'xmlcharrefreplace'`` is only supported when writing to a file. + Characters not supported by the encoding are replaced with the + appropriate XML character reference ``&#nnn;``. + + * ``'backslashreplace'`` (also only supported when writing) + replaces unsupported characters with Python's backslashed escape + sequences. .. index:: single: universal newlines; open() built-in function diff --git a/Lib/codecs.py b/Lib/codecs.py --- a/Lib/codecs.py +++ b/Lib/codecs.py @@ -105,6 +105,7 @@ Python will use the official U+FFFD REPLACEMENT CHARACTER for the builtin Unicode codecs on decoding and '?' on encoding. + 'surrogateescape' - replace with private codepoints U+DCnn. 'xmlcharrefreplace' - Replace with the appropriate XML character reference (only for encoding). 'backslashreplace' - Replace with backslashed escape sequences diff --git a/Lib/subprocess.py b/Lib/subprocess.py --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -810,6 +810,7 @@ if universal_newlines: self.stderr = io.TextIOWrapper(self.stderr) + self._closed_child_pipe_fds = False try: self._execute_child(args, executable, preexec_fn, close_fds, pass_fds, cwd, env, @@ -826,19 +827,21 @@ except EnvironmentError: pass # Ignore EBADF or other errors. - # Make sure the child pipes are closed as well. - to_close = [] - if stdin == PIPE: - to_close.append(p2cread) - if stdout == PIPE: - to_close.append(c2pwrite