[Python-checkins] cpython (merge default -> default): merge heads

benjamin.peterson python-checkins at python.org
Wed Dec 21 18:22:36 CET 2011


http://hg.python.org/cpython/rev/9c897817abde
changeset:   74120:9c897817abde
parent:      74119:56b70a81eee7
parent:      74116:f3041e7f535d
user:        Benjamin Peterson <benjamin at python.org>
date:        Wed Dec 21 11:22:02 2011 -0600
summary:
  merge heads

files:
  Doc/library/multiprocessing.rst  |   18 ++
  Doc/library/ssl.rst              |   10 +
  Doc/library/webbrowser.rst       |   90 +++++++------
  Doc/using/cmdline.rst            |    3 +-
  Doc/whatsnew/3.0.rst             |   10 +-
  Lib/inspect.py                   |   79 ++++++----
  Lib/multiprocessing/managers.py  |    3 +-
  Lib/multiprocessing/pool.py      |   32 ++++-
  Lib/pydoc.py                     |   29 +++-
  Lib/ssl.py                       |    2 +-
  Lib/test/test_import.py          |    3 +-
  Lib/test/test_inspect.py         |   79 +++++++++++
  Lib/test/test_multiprocessing.py |   18 ++
  Lib/test/test_ssl.py             |  128 ++++++------------
  Lib/webbrowser.py                |   17 ++
  Misc/ACKS                        |    1 +
  Misc/NEWS                        |    8 +
  Modules/_ssl.c                   |   12 +
  18 files changed, 371 insertions(+), 171 deletions(-)


diff --git a/Doc/library/multiprocessing.rst b/Doc/library/multiprocessing.rst
--- a/Doc/library/multiprocessing.rst
+++ b/Doc/library/multiprocessing.rst
@@ -1669,6 +1669,24 @@
       returned iterator should be considered arbitrary.  (Only when there is
       only one worker process is the order guaranteed to be "correct".)
 
+   .. method:: starmap(func, iterable[, chunksize])
+
+      Like :meth:`map` except that the elements of the `iterable` are expected
+      to be iterables that are unpacked as arguments.
+
+      Hence an `iterable` of `[(1,2), (3, 4)]` results in `[func(1,2),
+      func(3,4)]`.
+
+      .. versionadded:: 3.3
+
+   .. method:: starmap_async(func, iterable[, chunksize[, callback[, error_back]]])
+
+      A combination of :meth:`starmap` and :meth:`map_async` that iterates over
+      `iterable` of iterables and calls `func` with the iterables unpacked.
+      Returns a result object.
+
+      .. versionadded:: 3.3
+
    .. method:: close()
 
       Prevents any more tasks from being submitted to the pool.  Once all the
diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst
--- a/Doc/library/ssl.rst
+++ b/Doc/library/ssl.rst
@@ -445,6 +445,14 @@
 
    .. versionadded:: 3.3
 
+.. data:: HAS_ECDH
+
+   Whether the OpenSSL library has built-in support for Elliptic Curve-based
+   Diffie-Hellman key exchange.  This should be true unless the feature was
+   explicitly disabled by the distributor.
+
+   .. versionadded:: 3.3
+
 .. data:: HAS_SNI
 
    Whether the OpenSSL library has built-in support for the *Server Name
@@ -711,6 +719,8 @@
    This setting doesn't apply to client sockets.  You can also use the
    :data:`OP_SINGLE_ECDH_USE` option to further improve security.
 
+   This method is not available if :data:`HAS_ECDH` is False.
+
    .. versionadded:: 3.3
 
    .. seealso::
diff --git a/Doc/library/webbrowser.rst b/Doc/library/webbrowser.rst
--- a/Doc/library/webbrowser.rst
+++ b/Doc/library/webbrowser.rst
@@ -96,47 +96,55 @@
 may be passed to the :func:`get` function and the corresponding instantiations
 for the controller classes, all defined in this module.
 
-+-----------------------+-----------------------------------------+-------+
-| Type Name             | Class Name                              | Notes |
-+=======================+=========================================+=======+
-| ``'mozilla'``         | :class:`Mozilla('mozilla')`             |       |
-+-----------------------+-----------------------------------------+-------+
-| ``'firefox'``         | :class:`Mozilla('mozilla')`             |       |
-+-----------------------+-----------------------------------------+-------+
-| ``'netscape'``        | :class:`Mozilla('netscape')`            |       |
-+-----------------------+-----------------------------------------+-------+
-| ``'galeon'``          | :class:`Galeon('galeon')`               |       |
-+-----------------------+-----------------------------------------+-------+
-| ``'epiphany'``        | :class:`Galeon('epiphany')`             |       |
-+-----------------------+-----------------------------------------+-------+
-| ``'skipstone'``       | :class:`BackgroundBrowser('skipstone')` |       |
-+-----------------------+-----------------------------------------+-------+
-| ``'kfmclient'``       | :class:`Konqueror()`                    | \(1)  |
-+-----------------------+-----------------------------------------+-------+
-| ``'konqueror'``       | :class:`Konqueror()`                    | \(1)  |
-+-----------------------+-----------------------------------------+-------+
-| ``'kfm'``             | :class:`Konqueror()`                    | \(1)  |
-+-----------------------+-----------------------------------------+-------+
-| ``'mosaic'``          | :class:`BackgroundBrowser('mosaic')`    |       |
-+-----------------------+-----------------------------------------+-------+
-| ``'opera'``           | :class:`Opera()`                        |       |
-+-----------------------+-----------------------------------------+-------+
-| ``'grail'``           | :class:`Grail()`                        |       |
-+-----------------------+-----------------------------------------+-------+
-| ``'links'``           | :class:`GenericBrowser('links')`        |       |
-+-----------------------+-----------------------------------------+-------+
-| ``'elinks'``          | :class:`Elinks('elinks')`               |       |
-+-----------------------+-----------------------------------------+-------+
-| ``'lynx'``            | :class:`GenericBrowser('lynx')`         |       |
-+-----------------------+-----------------------------------------+-------+
-| ``'w3m'``             | :class:`GenericBrowser('w3m')`          |       |
-+-----------------------+-----------------------------------------+-------+
-| ``'windows-default'`` | :class:`WindowsDefault`                 | \(2)  |
-+-----------------------+-----------------------------------------+-------+
-| ``'internet-config'`` | :class:`InternetConfig`                 | \(3)  |
-+-----------------------+-----------------------------------------+-------+
-| ``'macosx'``          | :class:`MacOSX('default')`              | \(4)  |
-+-----------------------+-----------------------------------------+-------+
++------------------------+-----------------------------------------+-------+
+| Type Name              | Class Name                              | Notes |
++========================+=========================================+=======+
+| ``'mozilla'``          | :class:`Mozilla('mozilla')`             |       |
++------------------------+-----------------------------------------+-------+
+| ``'firefox'``          | :class:`Mozilla('mozilla')`             |       |
++------------------------+-----------------------------------------+-------+
+| ``'netscape'``         | :class:`Mozilla('netscape')`            |       |
++------------------------+-----------------------------------------+-------+
+| ``'galeon'``           | :class:`Galeon('galeon')`               |       |
++------------------------+-----------------------------------------+-------+
+| ``'epiphany'``         | :class:`Galeon('epiphany')`             |       |
++------------------------+-----------------------------------------+-------+
+| ``'skipstone'``        | :class:`BackgroundBrowser('skipstone')` |       |
++------------------------+-----------------------------------------+-------+
+| ``'kfmclient'``        | :class:`Konqueror()`                    | \(1)  |
++------------------------+-----------------------------------------+-------+
+| ``'konqueror'``        | :class:`Konqueror()`                    | \(1)  |
++------------------------+-----------------------------------------+-------+
+| ``'kfm'``              | :class:`Konqueror()`                    | \(1)  |
++------------------------+-----------------------------------------+-------+
+| ``'mosaic'``           | :class:`BackgroundBrowser('mosaic')`    |       |
++------------------------+-----------------------------------------+-------+
+| ``'opera'``            | :class:`Opera()`                        |       |
++------------------------+-----------------------------------------+-------+
+| ``'grail'``            | :class:`Grail()`                        |       |
++------------------------+-----------------------------------------+-------+
+| ``'links'``            | :class:`GenericBrowser('links')`        |       |
++------------------------+-----------------------------------------+-------+
+| ``'elinks'``           | :class:`Elinks('elinks')`               |       |
++------------------------+-----------------------------------------+-------+
+| ``'lynx'``             | :class:`GenericBrowser('lynx')`         |       |
++------------------------+-----------------------------------------+-------+
+| ``'w3m'``              | :class:`GenericBrowser('w3m')`          |       |
++------------------------+-----------------------------------------+-------+
+| ``'windows-default'``  | :class:`WindowsDefault`                 | \(2)  |
++------------------------+-----------------------------------------+-------+
+| ``'internet-config'``  | :class:`InternetConfig`                 | \(3)  |
++------------------------+-----------------------------------------+-------+
+| ``'macosx'``           | :class:`MacOSX('default')`              | \(4)  |
++------------------------+-----------------------------------------+-------+
+| ``'google-chrome'``    | :class:`Chrome('google-chrome')`        |       |
++------------------------+-----------------------------------------+-------+
+| ``'chrome'``           | :class:`Chrome('chrome')`               |       |
++------------------------+-----------------------------------------+-------+
+| ``'chromium'``         | :class:`Chromium('chromium')`           |       |
++------------------------+-----------------------------------------+-------+
+| ``'chromium-browser'`` | :class:`Chromium('chromium-browser')`   |       |
++------------------------+-----------------------------------------+-------+
 
 Notes:
 
diff --git a/Doc/using/cmdline.rst b/Doc/using/cmdline.rst
--- a/Doc/using/cmdline.rst
+++ b/Doc/using/cmdline.rst
@@ -249,7 +249,8 @@
 
    Force the binary layer of the stdin, stdout and stderr streams (which is
    available as their ``buffer`` attribute) to be unbuffered.  The text I/O
-   layer will still be line-buffered.
+   layer will still be line-buffered if writing to the console, or
+   block-buffered if redirected to a non-interactive file.
 
    See also :envvar:`PYTHONUNBUFFERED`.
 
diff --git a/Doc/whatsnew/3.0.rst b/Doc/whatsnew/3.0.rst
--- a/Doc/whatsnew/3.0.rst
+++ b/Doc/whatsnew/3.0.rst
@@ -301,6 +301,12 @@
   There is no longer any need for using the encoding-aware streams
   in the :mod:`codecs` module.
 
+* The initial values of :data:`sys.stdin`, :data:`sys.stdout` and
+  :data:`sys.stderr` are now unicode-only text files (i.e., they are
+  instances of :class:`io.TextIOBase`).  To read and write bytes data
+  with these streams, you need to use their :data:`io.TextIOBase.buffer`
+  attribute.
+
 * Filenames are passed to and returned from APIs as (Unicode) strings.
   This can present platform-specific problems because on some
   platforms filenames are arbitrary byte strings.  (On the other hand,
@@ -511,9 +517,7 @@
   produces a literal of type :class:`bytes`.
 
 * :ref:`pep-3116`.  The :mod:`io` module is now the standard way of
-  doing file I/O, and the initial values of :data:`sys.stdin`,
-  :data:`sys.stdout` and :data:`sys.stderr` are now instances of
-  :class:`io.TextIOBase`.  The built-in :func:`open` function is now an
+  doing file I/O.  The built-in :func:`open` function is now an
   alias for :func:`io.open` and has additional keyword arguments
   *encoding*, *errors*, *newline* and *closefd*.  Also note that an
   invalid *mode* argument now raises :exc:`ValueError`, not
diff --git a/Lib/inspect.py b/Lib/inspect.py
--- a/Lib/inspect.py
+++ b/Lib/inspect.py
@@ -99,11 +99,11 @@
     tests return false from the ismethoddescriptor() test, simply because
     the other tests promise more -- you can, e.g., count on having the
     __func__ attribute (etc) when an object passes ismethod()."""
-    return (hasattr(object, "__get__")
-            and not hasattr(object, "__set__") # else it's a data descriptor
-            and not ismethod(object)           # mutual exclusion
-            and not isfunction(object)
-            and not isclass(object))
+    if isclass(object) or ismethod(object) or isfunction(object):
+        # mutual exclusion
+        return False
+    tp = type(object)
+    return hasattr(tp, "__get__") and not hasattr(tp, "__set__")
 
 def isdatadescriptor(object):
     """Return true if the object is a data descriptor.
@@ -113,7 +113,11 @@
     Typically, data descriptors will also have __name__ and __doc__ attributes
     (properties, getsets, and members have both of these attributes), but this
     is not guaranteed."""
-    return (hasattr(object, "__set__") and hasattr(object, "__get__"))
+    if isclass(object) or ismethod(object) or isfunction(object):
+        # mutual exclusion
+        return False
+    tp = type(object)
+    return hasattr(tp, "__set__") and hasattr(tp, "__get__")
 
 if hasattr(types, 'MemberDescriptorType'):
     # CPython and equivalent
@@ -253,12 +257,23 @@
 def getmembers(object, predicate=None):
     """Return all members of an object as (name, value) pairs sorted by name.
     Optionally, only return members that satisfy a given predicate."""
+    if isclass(object):
+        mro = (object,) + getmro(object)
+    else:
+        mro = ()
     results = []
     for key in dir(object):
-        try:
-            value = getattr(object, key)
-        except AttributeError:
-            continue
+        # First try to get the value via __dict__. Some descriptors don't
+        # like calling their __get__ (see bug #1785).
+        for base in mro:
+            if key in base.__dict__:
+                value = base.__dict__[key]
+                break
+        else:
+            try:
+                value = getattr(object, key)
+            except AttributeError:
+                continue
         if not predicate or predicate(value):
             results.append((key, value))
     results.sort()
@@ -294,30 +309,21 @@
     names = dir(cls)
     result = []
     for name in names:
-        # Get the object associated with the name.
+        # Get the object associated with the name, and where it was defined.
         # Getting an obj from the __dict__ sometimes reveals more than
         # using getattr.  Static and class methods are dramatic examples.
-        if name in cls.__dict__:
-            obj = cls.__dict__[name]
+        # Furthermore, some objects may raise an Exception when fetched with
+        # getattr(). This is the case with some descriptors (bug #1785).
+        # Thus, we only use getattr() as a last resort.
+        homecls = None
+        for base in (cls,) + mro:
+            if name in base.__dict__:
+                obj = base.__dict__[name]
+                homecls = base
+                break
         else:
             obj = getattr(cls, name)
-
-        # Figure out where it was defined.
-        homecls = getattr(obj, "__objclass__", None)
-        if homecls is None:
-            # search the dicts.
-            for base in mro:
-                if name in base.__dict__:
-                    homecls = base
-                    break
-
-        # Get the object again, in order to get it from the defining
-        # __dict__ instead of via getattr (if possible).
-        if homecls is not None and name in homecls.__dict__:
-            obj = homecls.__dict__[name]
-
-        # Also get the object via getattr.
-        obj_via_getattr = getattr(cls, name)
+            homecls = getattr(obj, "__objclass__", homecls)
 
         # Classify the object.
         if isinstance(obj, staticmethod):
@@ -326,11 +332,18 @@
             kind = "class method"
         elif isinstance(obj, property):
             kind = "property"
-        elif (isfunction(obj_via_getattr) or
-              ismethoddescriptor(obj_via_getattr)):
+        elif ismethoddescriptor(obj):
             kind = "method"
+        elif isdatadescriptor(obj):
+            kind = "data"
         else:
-            kind = "data"
+            obj_via_getattr = getattr(cls, name)
+            if (isfunction(obj_via_getattr) or
+                ismethoddescriptor(obj_via_getattr)):
+                kind = "method"
+            else:
+                kind = "data"
+            obj = obj_via_getattr
 
         result.append(Attribute(name, kind, homecls, obj))
 
diff --git a/Lib/multiprocessing/managers.py b/Lib/multiprocessing/managers.py
--- a/Lib/multiprocessing/managers.py
+++ b/Lib/multiprocessing/managers.py
@@ -1066,11 +1066,12 @@
 
 PoolProxy = MakeProxyType('PoolProxy', (
     'apply', 'apply_async', 'close', 'imap', 'imap_unordered', 'join',
-    'map', 'map_async', 'terminate'
+    'map', 'map_async', 'starmap', 'starmap_async', 'terminate'
     ))
 PoolProxy._method_to_typeid_ = {
     'apply_async': 'AsyncResult',
     'map_async': 'AsyncResult',
+    'starmap_async': 'AsyncResult',
     'imap': 'Iterator',
     'imap_unordered': 'Iterator'
     }
diff --git a/Lib/multiprocessing/pool.py b/Lib/multiprocessing/pool.py
--- a/Lib/multiprocessing/pool.py
+++ b/Lib/multiprocessing/pool.py
@@ -64,6 +64,9 @@
 def mapstar(args):
     return list(map(*args))
 
+def starmapstar(args):
+    return list(itertools.starmap(args[0], args[1]))
+
 #
 # Code run by worker processes
 #
@@ -248,7 +251,25 @@
         in a list that is returned.
         '''
         assert self._state == RUN
-        return self.map_async(func, iterable, chunksize).get()
+        return self._map_async(func, iterable, mapstar, chunksize).get()
+
+    def starmap(self, func, iterable, chunksize=None):
+        '''
+        Like `map()` method but the elements of the `iterable` are expected to
+        be iterables as well and will be unpacked as arguments. Hence
+        `func` and (a, b) becomes func(a, b).
+        '''
+        assert self._state == RUN
+        return self._map_async(func, iterable, starmapstar, chunksize).get()
+
+    def starmap_async(self, func, iterable, chunksize=None, callback=None,
+            error_callback=None):
+        '''
+        Asynchronous version of `starmap()` method.
+        '''
+        assert self._state == RUN
+        return self._map_async(func, iterable, starmapstar, chunksize,
+                               callback, error_callback)
 
     def imap(self, func, iterable, chunksize=1):
         '''
@@ -302,6 +323,13 @@
         Asynchronous version of `map()` method.
         '''
         assert self._state == RUN
+        return self._map_async(func, iterable, mapstar, chunksize)
+
+    def _map_async(self, func, iterable, mapper, chunksize=None, callback=None,
+            error_callback=None):
+        '''
+        Helper function to implement map, starmap and their async counterparts.
+        '''
         if not hasattr(iterable, '__len__'):
             iterable = list(iterable)
 
@@ -315,7 +343,7 @@
         task_batches = Pool._get_tasks(func, iterable, chunksize)
         result = MapResult(self._cache, chunksize, len(iterable), callback,
                            error_callback=error_callback)
-        self._taskqueue.put((((result._job, i, mapstar, (x,), {})
+        self._taskqueue.put((((result._job, i, mapper, (x,), {})
                               for i, x in enumerate(task_batches)), None))
         return result
 
diff --git a/Lib/pydoc.py b/Lib/pydoc.py
--- a/Lib/pydoc.py
+++ b/Lib/pydoc.py
@@ -748,8 +748,15 @@
                 hr.maybe()
                 push(msg)
                 for name, kind, homecls, value in ok:
-                    push(self.document(getattr(object, name), name, mod,
-                                       funcs, classes, mdict, object))
+                    try:
+                        value = getattr(object, name)
+                    except Exception:
+                        # Some descriptors may meet a failure in their __get__.
+                        # (bug #1785)
+                        push(self._docdescriptor(name, value, mod))
+                    else:
+                        push(self.document(value, name, mod,
+                                        funcs, classes, mdict, object))
                     push('\n')
             return attrs
 
@@ -790,7 +797,12 @@
         mdict = {}
         for key, kind, homecls, value in attrs:
             mdict[key] = anchor = '#' + name + '-' + key
-            value = getattr(object, key)
+            try:
+                value = getattr(object, name)
+            except Exception:
+                # Some descriptors may meet a failure in their __get__.
+                # (bug #1785)
+                pass
             try:
                 # The value may not be hashable (e.g., a data attr with
                 # a dict or list value).
@@ -1177,8 +1189,15 @@
                 hr.maybe()
                 push(msg)
                 for name, kind, homecls, value in ok:
-                    push(self.document(getattr(object, name),
-                                       name, mod, object))
+                    try:
+                        value = getattr(object, name)
+                    except Exception:
+                        # Some descriptors may meet a failure in their __get__.
+                        # (bug #1785)
+                        push(self._docdescriptor(name, value, mod))
+                    else:
+                        push(self.document(value,
+                                        name, mod, object))
             return attrs
 
         def spilldescriptors(msg, attrs, predicate):
diff --git a/Lib/ssl.py b/Lib/ssl.py
--- a/Lib/ssl.py
+++ b/Lib/ssl.py
@@ -86,7 +86,7 @@
     SSL_ERROR_EOF,
     SSL_ERROR_INVALID_ERROR_CODE,
     )
-from _ssl import HAS_SNI
+from _ssl import HAS_SNI, HAS_ECDH
 from _ssl import (PROTOCOL_SSLv3, PROTOCOL_SSLv23,
                   PROTOCOL_TLSv1)
 from _ssl import _OPENSSL_API_VERSION
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
@@ -104,8 +104,9 @@
             try:
                 fname = TESTFN + os.extsep + "py"
                 create_empty_file(fname)
+                fn = imp.cache_from_source(fname)
+                unlink(fn)
                 __import__(TESTFN)
-                fn = imp.cache_from_source(fname)
                 if not os.path.exists(fn):
                     self.fail("__import__ did not result in creation of "
                               "either a .pyc or .pyo file")
diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py
--- a/Lib/test/test_inspect.py
+++ b/Lib/test/test_inspect.py
@@ -425,10 +425,37 @@
     def test_class(self):
         self.assertSourceEqual(self.fodderModule.X, 1, 2)
 
+
+class _BrokenDataDescriptor(object):
+    """
+    A broken data descriptor. See bug #1785.
+    """
+    def __get__(*args):
+        raise AssertionError("should not __get__ data descriptors")
+
+    def __set__(*args):
+        raise RuntimeError
+
+    def __getattr__(*args):
+        raise AssertionError("should not __getattr__ data descriptors")
+
+
+class _BrokenMethodDescriptor(object):
+    """
+    A broken method descriptor. See bug #1785.
+    """
+    def __get__(*args):
+        raise AssertionError("should not __get__ method descriptors")
+
+    def __getattr__(*args):
+        raise AssertionError("should not __getattr__ method descriptors")
+
+
 # Helper for testing classify_class_attrs.
 def attrs_wo_objs(cls):
     return [t[:3] for t in inspect.classify_class_attrs(cls)]
 
+
 class TestClassesAndFunctions(unittest.TestCase):
     def test_newstyle_mro(self):
         # The same w/ new-class MRO.
@@ -525,6 +552,9 @@
 
             datablob = '1'
 
+            dd = _BrokenDataDescriptor()
+            md = _BrokenMethodDescriptor()
+
         attrs = attrs_wo_objs(A)
         self.assertIn(('s', 'static method', A), attrs, 'missing static method')
         self.assertIn(('c', 'class method', A), attrs, 'missing class method')
@@ -533,6 +563,8 @@
                       'missing plain method: %r' % attrs)
         self.assertIn(('m1', 'method', A), attrs, 'missing plain method')
         self.assertIn(('datablob', 'data', A), attrs, 'missing data')
+        self.assertIn(('md', 'method', A), attrs, 'missing method descriptor')
+        self.assertIn(('dd', 'data', A), attrs, 'missing data descriptor')
 
         class B(A):
 
@@ -545,6 +577,8 @@
         self.assertIn(('m', 'method', B), attrs, 'missing plain method')
         self.assertIn(('m1', 'method', A), attrs, 'missing plain method')
         self.assertIn(('datablob', 'data', A), attrs, 'missing data')
+        self.assertIn(('md', 'method', A), attrs, 'missing method descriptor')
+        self.assertIn(('dd', 'data', A), attrs, 'missing data descriptor')
 
 
         class C(A):
@@ -559,6 +593,8 @@
         self.assertIn(('m', 'method', C), attrs, 'missing plain method')
         self.assertIn(('m1', 'method', A), attrs, 'missing plain method')
         self.assertIn(('datablob', 'data', A), attrs, 'missing data')
+        self.assertIn(('md', 'method', A), attrs, 'missing method descriptor')
+        self.assertIn(('dd', 'data', A), attrs, 'missing data descriptor')
 
         class D(B, C):
 
@@ -571,6 +607,49 @@
         self.assertIn(('m', 'method', B), attrs, 'missing plain method')
         self.assertIn(('m1', 'method', D), attrs, 'missing plain method')
         self.assertIn(('datablob', 'data', A), attrs, 'missing data')
+        self.assertIn(('md', 'method', A), attrs, 'missing method descriptor')
+        self.assertIn(('dd', 'data', A), attrs, 'missing data descriptor')
+
+    def test_classify_builtin_types(self):
+        # Simple sanity check that all built-in types can have their
+        # attributes classified.
+        for name in dir(__builtins__):
+            builtin = getattr(__builtins__, name)
+            if isinstance(builtin, type):
+                inspect.classify_class_attrs(builtin)
+
+    def test_getmembers_descriptors(self):
+        class A(object):
+            dd = _BrokenDataDescriptor()
+            md = _BrokenMethodDescriptor()
+
+        def pred_wrapper(pred):
+            # A quick'n'dirty way to discard standard attributes of new-style
+            # classes.
+            class Empty(object):
+                pass
+            def wrapped(x):
+                if '__name__' in dir(x) and hasattr(Empty, x.__name__):
+                    return False
+                return pred(x)
+            return wrapped
+
+        ismethoddescriptor = pred_wrapper(inspect.ismethoddescriptor)
+        isdatadescriptor = pred_wrapper(inspect.isdatadescriptor)
+
+        self.assertEqual(inspect.getmembers(A, ismethoddescriptor),
+            [('md', A.__dict__['md'])])
+        self.assertEqual(inspect.getmembers(A, isdatadescriptor),
+            [('dd', A.__dict__['dd'])])
+
+        class B(A):
+            pass
+
+        self.assertEqual(inspect.getmembers(B, ismethoddescriptor),
+            [('md', A.__dict__['md'])])
+        self.assertEqual(inspect.getmembers(B, isdatadescriptor),
+            [('dd', A.__dict__['dd'])])
+
 
 class TestGetcallargsFunctions(unittest.TestCase):
 
diff --git a/Lib/test/test_multiprocessing.py b/Lib/test/test_multiprocessing.py
--- a/Lib/test/test_multiprocessing.py
+++ b/Lib/test/test_multiprocessing.py
@@ -8,6 +8,7 @@
 import queue as pyqueue
 import time
 import io
+import itertools
 import sys
 import os
 import gc
@@ -1125,6 +1126,9 @@
     time.sleep(wait)
     return x*x
 
+def mul(x, y):
+    return x*y
+
 class _TestPool(BaseTestCase):
 
     def test_apply(self):
@@ -1138,6 +1142,20 @@
         self.assertEqual(pmap(sqr, list(range(100)), chunksize=20),
                          list(map(sqr, list(range(100)))))
 
+    def test_starmap(self):
+        psmap = self.pool.starmap
+        tuples = list(zip(range(10), range(9,-1, -1)))
+        self.assertEqual(psmap(mul, tuples),
+                         list(itertools.starmap(mul, tuples)))
+        tuples = list(zip(range(100), range(99,-1, -1)))
+        self.assertEqual(psmap(mul, tuples, chunksize=20),
+                         list(itertools.starmap(mul, tuples)))
+
+    def test_starmap_async(self):
+        tuples = list(zip(range(100), range(99,-1, -1)))
+        self.assertEqual(self.pool.starmap_async(mul, tuples).get(),
+                         list(itertools.starmap(mul, tuples)))
+
     def test_map_chunksize(self):
         try:
             self.pool.map_async(sqr, [], chunksize=1).get(timeout=TIMEOUT1)
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
@@ -103,6 +103,7 @@
         if ssl.OPENSSL_VERSION_INFO >= (1, 0):
             ssl.OP_NO_COMPRESSION
         self.assertIn(ssl.HAS_SNI, {True, False})
+        self.assertIn(ssl.HAS_ECDH, {True, False})
 
     def test_random(self):
         v = ssl.RAND_status()
@@ -561,6 +562,7 @@
         ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
         ctx.set_default_verify_paths()
 
+    @unittest.skipUnless(ssl.HAS_ECDH, "ECDH disabled on this OpenSSL build")
     def test_set_ecdh_curve(self):
         ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
         ctx.set_ecdh_curve("prime256v1")
@@ -984,6 +986,14 @@
             threading.Thread.__init__(self)
             self.daemon = True
 
+        def __enter__(self):
+            self.start(threading.Event())
+            self.flag.wait()
+
+        def __exit__(self, *args):
+            self.stop()
+            self.join()
+
         def start(self, flag=None):
             self.flag = flag
             threading.Thread.start(self)
@@ -1095,6 +1105,20 @@
         def __str__(self):
             return "<%s %s>" % (self.__class__.__name__, self.server)
 
+        def __enter__(self):
+            self.start(threading.Event())
+            self.flag.wait()
+
+        def __exit__(self, *args):
+            if support.verbose:
+                sys.stdout.write(" cleanup: stopping server.\n")
+            self.stop()
+            if support.verbose:
+                sys.stdout.write(" cleanup: joining server thread.\n")
+            self.join()
+            if support.verbose:
+                sys.stdout.write(" cleanup: successfully joined.\n")
+
         def start (self, flag=None):
             self.flag = flag
             threading.Thread.start(self)
@@ -1122,12 +1146,7 @@
                                     certreqs=ssl.CERT_REQUIRED,
                                     cacerts=CERTFILE, chatty=False,
                                     connectionchatty=False)
-        flag = threading.Event()
-        server.start(flag)
-        # wait for it to start
-        flag.wait()
-        # try to connect
-        try:
+        with server:
             try:
                 with socket.socket() as sock:
                     s = ssl.wrap_socket(sock,
@@ -1147,9 +1166,6 @@
                     sys.stdout.write("\IOError is %s\n" % str(x))
             else:
                 raise AssertionError("Use of invalid cert should have failed!")
-        finally:
-            server.stop()
-            server.join()
 
     def server_params_test(client_context, server_context, indata=b"FOO\n",
                            chatty=True, connectionchatty=False):
@@ -1160,12 +1176,7 @@
         server = ThreadedEchoServer(context=server_context,
                                     chatty=chatty,
                                     connectionchatty=False)
-        flag = threading.Event()
-        server.start(flag)
-        # wait for it to start
-        flag.wait()
-        # try to connect
-        try:
+        with server:
             s = client_context.wrap_socket(socket.socket())
             s.connect((HOST, server.port))
             for arg in [indata, bytearray(indata), memoryview(indata)]:
@@ -1193,9 +1204,6 @@
             }
             s.close()
             return stats
-        finally:
-            server.stop()
-            server.join()
 
     def try_protocol_combo(server_protocol, client_protocol, expect_success,
                            certsreqs=None, server_options=0, client_options=0):
@@ -1264,12 +1272,7 @@
             context.load_verify_locations(CERTFILE)
             context.load_cert_chain(CERTFILE)
             server = ThreadedEchoServer(context=context, chatty=False)
-            flag = threading.Event()
-            server.start(flag)
-            # wait for it to start
-            flag.wait()
-            # try to connect
-            try:
+            with server:
                 s = context.wrap_socket(socket.socket())
                 s.connect((HOST, server.port))
                 cert = s.getpeercert()
@@ -1292,9 +1295,6 @@
                 after = ssl.cert_time_to_seconds(cert['notAfter'])
                 self.assertLess(before, after)
                 s.close()
-            finally:
-                server.stop()
-                server.join()
 
         def test_empty_cert(self):
             """Connecting with an empty cert file"""
@@ -1454,13 +1454,8 @@
                                         starttls_server=True,
                                         chatty=True,
                                         connectionchatty=True)
-            flag = threading.Event()
-            server.start(flag)
-            # wait for it to start
-            flag.wait()
-            # try to connect
             wrapped = False
-            try:
+            with server:
                 s = socket.socket()
                 s.setblocking(1)
                 s.connect((HOST, server.port))
@@ -1507,9 +1502,6 @@
                     conn.close()
                 else:
                     s.close()
-            finally:
-                server.stop()
-                server.join()
 
         def test_socketserver(self):
             """Using a SocketServer to create and manage SSL connections."""
@@ -1545,12 +1537,7 @@
 
             indata = b"FOO\n"
             server = AsyncoreEchoServer(CERTFILE)
-            flag = threading.Event()
-            server.start(flag)
-            # wait for it to start
-            flag.wait()
-            # try to connect
-            try:
+            with server:
                 s = ssl.wrap_socket(socket.socket())
                 s.connect(('127.0.0.1', server.port))
                 if support.verbose:
@@ -1571,15 +1558,6 @@
                 s.close()
                 if support.verbose:
                     sys.stdout.write(" client:  connection closed.\n")
-            finally:
-                if support.verbose:
-                    sys.stdout.write(" cleanup: stopping server.\n")
-                server.stop()
-                if support.verbose:
-                    sys.stdout.write(" cleanup: joining server thread.\n")
-                server.join()
-                if support.verbose:
-                    sys.stdout.write(" cleanup: successfully joined.\n")
 
         def test_recv_send(self):
             """Test recv(), send() and friends."""
@@ -1592,19 +1570,14 @@
                                         cacerts=CERTFILE,
                                         chatty=True,
                                         connectionchatty=False)
-            flag = threading.Event()
-            server.start(flag)
-            # wait for it to start
-            flag.wait()
-            # try to connect
-            s = ssl.wrap_socket(socket.socket(),
-                                server_side=False,
-                                certfile=CERTFILE,
-                                ca_certs=CERTFILE,
-                                cert_reqs=ssl.CERT_NONE,
-                                ssl_version=ssl.PROTOCOL_TLSv1)
-            s.connect((HOST, server.port))
-            try:
+            with server:
+                s = ssl.wrap_socket(socket.socket(),
+                                    server_side=False,
+                                    certfile=CERTFILE,
+                                    ca_certs=CERTFILE,
+                                    cert_reqs=ssl.CERT_NONE,
+                                    ssl_version=ssl.PROTOCOL_TLSv1)
+                s.connect((HOST, server.port))
                 # helper methods for standardising recv* method signatures
                 def _recv_into():
                     b = bytearray(b"\0"*100)
@@ -1700,9 +1673,6 @@
 
                 s.write(b"over\n")
                 s.close()
-            finally:
-                server.stop()
-                server.join()
 
         def test_handshake_timeout(self):
             # Issue #5103: SSL handshake must respect the socket timeout
@@ -1766,19 +1736,14 @@
                                         cacerts=CERTFILE,
                                         chatty=True,
                                         connectionchatty=False)
-            flag = threading.Event()
-            server.start(flag)
-            # wait for it to start
-            flag.wait()
-            # try to connect
-            s = ssl.wrap_socket(socket.socket(),
-                                server_side=False,
-                                certfile=CERTFILE,
-                                ca_certs=CERTFILE,
-                                cert_reqs=ssl.CERT_NONE,
-                                ssl_version=ssl.PROTOCOL_TLSv1)
-            s.connect((HOST, server.port))
-            try:
+            with server:
+                s = ssl.wrap_socket(socket.socket(),
+                                    server_side=False,
+                                    certfile=CERTFILE,
+                                    ca_certs=CERTFILE,
+                                    cert_reqs=ssl.CERT_NONE,
+                                    ssl_version=ssl.PROTOCOL_TLSv1)
+                s.connect((HOST, server.port))
                 # get the data
                 cb_data = s.get_channel_binding("tls-unique")
                 if support.verbose:
@@ -1817,9 +1782,6 @@
                 self.assertEqual(peer_data_repr,
                                  repr(new_cb_data).encode("us-ascii"))
                 s.close()
-            finally:
-                server.stop()
-                server.join()
 
         def test_compression(self):
             context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
diff --git a/Lib/webbrowser.py b/Lib/webbrowser.py
--- a/Lib/webbrowser.py
+++ b/Lib/webbrowser.py
@@ -299,6 +299,18 @@
     background = True
 
 
+class Chrome(UnixBrowser):
+    "Launcher class for Google Chrome browser."
+
+    remote_args = ['%action', '%s']
+    remote_action = ""
+    remote_action_newwin = "--new-window"
+    remote_action_newtab = ""
+    background = True
+
+Chromium = Chrome
+
+
 class Opera(UnixBrowser):
     "Launcher class for Opera browser."
 
@@ -466,6 +478,11 @@
     if _iscommand("skipstone"):
         register("skipstone", None, BackgroundBrowser("skipstone"))
 
+    # Google Chrome/Chromium browsers
+    for browser in ("google-chrome", "chrome", "chromium", "chromium-browser"):
+        if _iscommand(browser):
+            register(browser, None, Chrome(browser))
+
     # Opera, quite popular
     if _iscommand("opera"):
         register("opera", None, Opera("opera"))
diff --git a/Misc/ACKS b/Misc/ACKS
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -878,6 +878,7 @@
 Andreas Schawo
 Neil Schemenauer
 David Scherer
+Hynek Schlawack
 Bob Schmertz
 Gregor Schmid
 Ralf Schmitt
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -419,6 +419,14 @@
 Library
 -------
 
+- Issue #13620: Support for Chrome browser in webbrowser.py Patch contributed
+  by Arnaud Calmettes.
+
+- Issue #12708: Add starmap() and starmap_async() methods (similar to
+  itertools.starmap()) to multiprocessing.Pool.  Patch by Hynek Schlawack.
+
+- Issue #1785: Fix inspect and pydoc with misbehaving descriptors.
+
 - Issue #13637: "a2b" functions in the binascii module now accept ASCII-only
   unicode strings.
 
diff --git a/Modules/_ssl.c b/Modules/_ssl.c
--- a/Modules/_ssl.c
+++ b/Modules/_ssl.c
@@ -2006,6 +2006,7 @@
     Py_RETURN_NONE;
 }
 
+#ifndef OPENSSL_NO_ECDH
 static PyObject *
 set_ecdh_curve(PySSLContext *self, PyObject *name)
 {
@@ -2032,6 +2033,7 @@
     EC_KEY_free(key);
     Py_RETURN_NONE;
 }
+#endif
 
 static PyGetSetDef context_getsetlist[] = {
     {"options", (getter) get_options,
@@ -2054,8 +2056,10 @@
                       METH_NOARGS, NULL},
     {"set_default_verify_paths", (PyCFunction) set_default_verify_paths,
                                  METH_NOARGS, NULL},
+#ifndef OPENSSL_NO_ECDH
     {"set_ecdh_curve", (PyCFunction) set_ecdh_curve,
                        METH_O, NULL},
+#endif
     {NULL, NULL}        /* sentinel */
 };
 
@@ -2523,6 +2527,14 @@
     Py_INCREF(r);
     PyModule_AddObject(m, "HAS_TLS_UNIQUE", r);
 
+#ifdef OPENSSL_NO_ECDH
+    r = Py_False;
+#else
+    r = Py_True;
+#endif
+    Py_INCREF(r);
+    PyModule_AddObject(m, "HAS_ECDH", r);
+
     /* OpenSSL version */
     /* SSLeay() gives us the version of the library linked against,
        which could be different from the headers version.

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list