diff --git a/Doc/library/multiprocessing.rst b/Doc/library/multiprocessing.rst index 851b3cf7f57..39ff0a60c22 100644 --- a/Doc/library/multiprocessing.rst +++ b/Doc/library/multiprocessing.rst @@ -1669,6 +1669,24 @@ with the :class:`Pool` class. 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 index 3cd9554c081..9b3306cdd14 100644 --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -445,6 +445,14 @@ Constants .. 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 @@ to speed up repeated connections from the same clients. 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 index 23ba6c54719..188647d0ce2 100644 --- a/Doc/library/webbrowser.rst +++ b/Doc/library/webbrowser.rst @@ -96,47 +96,55 @@ A number of browser types are predefined. This table gives the type names that 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 index bc38542c5b7..d9cf954ce19 100644 --- a/Doc/using/cmdline.rst +++ b/Doc/using/cmdline.rst @@ -249,7 +249,8 @@ Miscellaneous options 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 index 523b34e5740..55b65bae21b 100644 --- a/Doc/whatsnew/3.0.rst +++ b/Doc/whatsnew/3.0.rst @@ -301,6 +301,12 @@ changed. 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 @@ consulted for longer descriptions. 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 index 5a22076c975..8b800f42cdf 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -99,11 +99,11 @@ def ismethoddescriptor(object): 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 @@ def isdatadescriptor(object): 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 isabstract(object): 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 @@ def classify_class_attrs(cls): 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 @@ def classify_class_attrs(cls): kind = "class method" elif isinstance(obj, property): kind = "property" - elif (isfunction(obj_via_getattr) or - ismethoddescriptor(obj_via_getattr)): + elif ismethoddescriptor(obj): kind = "method" - else: + elif isdatadescriptor(obj): kind = "data" + else: + 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 index f42d353f0d1..5cae4c1548b 100644 --- a/Lib/multiprocessing/managers.py +++ b/Lib/multiprocessing/managers.py @@ -1066,11 +1066,12 @@ ArrayProxy = MakeProxyType('ArrayProxy', ( 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 index 0c29e644ffd..7039d1679ed 100644 --- a/Lib/multiprocessing/pool.py +++ b/Lib/multiprocessing/pool.py @@ -64,6 +64,9 @@ job_counter = itertools.count() 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 @@ class Pool(object): 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 @@ class Pool(object): 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 @@ class Pool(object): 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 index b5dbde651c2..591717bf7b2 100755 --- a/Lib/pydoc.py +++ b/Lib/pydoc.py @@ -748,8 +748,15 @@ class HTMLDoc(Doc): 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 @@ class HTMLDoc(Doc): 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 @@ location listed above. 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 index 0b2f743f227..d43d25512ef 100644 --- a/Lib/ssl.py +++ b/Lib/ssl.py @@ -86,7 +86,7 @@ from _ssl import ( 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 index 86ef40ec774..9f80b70284d 100644 --- a/Lib/test/test_import.py +++ b/Lib/test/test_import.py @@ -104,8 +104,9 @@ class ImportTests(unittest.TestCase): try: fname = TESTFN + os.extsep + "py" create_empty_file(fname) - __import__(TESTFN) fn = imp.cache_from_source(fname) + unlink(fn) + __import__(TESTFN) 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 index 7163103f86f..17c9f40ee6e 100644 --- a/Lib/test/test_inspect.py +++ b/Lib/test/test_inspect.py @@ -425,10 +425,37 @@ class TestNoEOL(GetSourceBase): 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 @@ class TestClassesAndFunctions(unittest.TestCase): 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 @@ class TestClassesAndFunctions(unittest.TestCase): '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 @@ class TestClassesAndFunctions(unittest.TestCase): 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 @@ class TestClassesAndFunctions(unittest.TestCase): 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 @@ class TestClassesAndFunctions(unittest.TestCase): 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 index b99201b2809..93cc11d96d4 100644 --- a/Lib/test/test_multiprocessing.py +++ b/Lib/test/test_multiprocessing.py @@ -8,6 +8,7 @@ import unittest import queue as pyqueue import time import io +import itertools import sys import os import gc @@ -1125,6 +1126,9 @@ def sqr(x, wait=0.0): time.sleep(wait) return x*x +def mul(x, y): + return x*y + class _TestPool(BaseTestCase): def test_apply(self): @@ -1138,6 +1142,20 @@ class _TestPool(BaseTestCase): 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 index 8bbe0f77fe2..d5497993960 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -103,6 +103,7 @@ class BasicSocketTests(unittest.TestCase): 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 @@ class ContextTests(unittest.TestCase): 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 @@ else: 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 @@ else: 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 @@ else: 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 @@ else: 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 @@ else: 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 @@ else: } 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 @@ else: 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 @@ else: 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 @@ else: 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 @@ else: 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 @@ else: 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 @@ else: 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 @@ else: 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 @@ else: 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 @@ else: 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 @@ else: 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 index 6f967b61cf2..d036355c1c4 100644 --- a/Lib/webbrowser.py +++ b/Lib/webbrowser.py @@ -299,6 +299,18 @@ class Galeon(UnixBrowser): 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 @@ def register_X_browsers(): 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 index 269518cb025..88b67e014e2 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -878,6 +878,7 @@ Michael Scharf Andreas Schawo Neil Schemenauer David Scherer +Hynek Schlawack Bob Schmertz Gregor Schmid Ralf Schmitt diff --git a/Misc/NEWS b/Misc/NEWS index 69552585722..c52149760d3 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -419,6 +419,14 @@ Core and Builtins 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 index 480543cbe16..02fe5f356ec 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -2006,6 +2006,7 @@ set_default_verify_paths(PySSLContext *self, PyObject *unused) Py_RETURN_NONE; } +#ifndef OPENSSL_NO_ECDH static PyObject * set_ecdh_curve(PySSLContext *self, PyObject *name) { @@ -2032,6 +2033,7 @@ set_ecdh_curve(PySSLContext *self, PyObject *name) EC_KEY_free(key); Py_RETURN_NONE; } +#endif static PyGetSetDef context_getsetlist[] = { {"options", (getter) get_options, @@ -2054,8 +2056,10 @@ static struct PyMethodDef context_methods[] = { 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 @@ PyInit__ssl(void) 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.