From 501da61671f88032cfde9b81060ddd82d22bf8ec Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Wed, 21 Dec 2011 09:27:41 +0100 Subject: [PATCH 1/8] Fix ssl module compilation if ECDH support was disabled in the OpenSSL build. (followup to issue #13627) --- Doc/library/ssl.rst | 10 ++++++++++ Lib/ssl.py | 2 +- Lib/test/test_ssl.py | 2 ++ Modules/_ssl.c | 12 ++++++++++++ 4 files changed, 25 insertions(+), 1 deletion(-) 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/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_ssl.py b/Lib/test/test_ssl.py index 8bbe0f77fe2..1960e143473 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") 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. From 86a8a9ae983b66ea218ccbb57d3e3a5cdf918e97 Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Wed, 21 Dec 2011 09:57:40 +0100 Subject: [PATCH 2/8] Issue #1785: Fix inspect and pydoc with misbehaving descriptors. Also fixes issue #13581: `help(type)` wouldn't display anything. --- Lib/inspect.py | 79 +++++++++++++++++++++++----------------- Lib/pydoc.py | 29 ++++++++++++--- Lib/test/test_inspect.py | 79 ++++++++++++++++++++++++++++++++++++++++ Misc/NEWS | 2 + 4 files changed, 151 insertions(+), 38 deletions(-) diff --git a/Lib/inspect.py b/Lib/inspect.py index 521d2a67d44..ffbe66fbe1f 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -100,11 +100,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. @@ -114,7 +114,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 @@ -254,12 +258,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() @@ -295,30 +310,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): @@ -327,11 +333,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/pydoc.py b/Lib/pydoc.py index d0c6f62148f..f45d46170e2 100755 --- a/Lib/pydoc.py +++ b/Lib/pydoc.py @@ -754,8 +754,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 @@ -796,7 +803,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). @@ -1180,8 +1192,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/test/test_inspect.py b/Lib/test/test_inspect.py index abbdc9f7c97..56f9929770f 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/Misc/NEWS b/Misc/NEWS index 956146faef0..1ad91c02856 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -97,6 +97,8 @@ Core and Builtins Library ------- +- Issue #1785: Fix inspect and pydoc with misbehaving descriptors. + - Issue #11813: Fix inspect.getattr_static for modules. Patch by Andreas Stührk. From de911b2915476938c66855d91df6fa4bbd77b39f Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Wed, 21 Dec 2011 11:03:24 +0100 Subject: [PATCH 3/8] Issue #12708: Add starmap() and starmap_async() methods (similar to itertools.starmap()) to multiprocessing.Pool. Patch by Hynek Schlawack. --- Doc/library/multiprocessing.rst | 18 ++++++++++++++++++ Lib/multiprocessing/managers.py | 3 ++- Lib/multiprocessing/pool.py | 32 ++++++++++++++++++++++++++++++-- Lib/test/test_multiprocessing.py | 18 ++++++++++++++++++ Misc/ACKS | 1 + Misc/NEWS | 3 +++ 6 files changed, 72 insertions(+), 3 deletions(-) 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/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/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/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 98889a5eaa9..82f9d86be9a 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -419,6 +419,9 @@ Core and Builtins Library ------- +- 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 From 08c08eb93cc0b67215d1922adc55f3ebf40b5122 Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Wed, 21 Dec 2011 11:24:15 +0100 Subject: [PATCH 4/8] Issue #13597: Fix the documentation of the "-u" command-line option, and wording of "What's new in Python 3.0" about standard streams. --- Doc/using/cmdline.rst | 3 ++- Doc/whatsnew/3.0.rst | 10 +++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Doc/using/cmdline.rst b/Doc/using/cmdline.rst index c73f1e0f3e8..9274d8e6b3f 100644 --- a/Doc/using/cmdline.rst +++ b/Doc/using/cmdline.rst @@ -247,7 +247,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 From ea6b4187cf78ce1ac260c1620b1d4c0fd069beba Mon Sep 17 00:00:00 2001 From: Senthil Kumaran Date: Wed, 21 Dec 2011 22:20:32 +0800 Subject: [PATCH 5/8] Issue 13620 - Support chrome browser in webbrowser.py module. --- Lib/webbrowser.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) 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")) From c90959947b1ec293622c1e92015e20540bc1e0c5 Mon Sep 17 00:00:00 2001 From: Senthil Kumaran Date: Wed, 21 Dec 2011 22:31:36 +0800 Subject: [PATCH 6/8] Docs and News update for Issue13620. Chrome support in webbrowser.py --- Doc/library/webbrowser.rst | 90 +++++++++++++++++++++----------------- Misc/NEWS | 3 ++ 2 files changed, 52 insertions(+), 41 deletions(-) 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/Misc/NEWS b/Misc/NEWS index 82f9d86be9a..c52149760d3 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -419,6 +419,9 @@ 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. From 28f8bee5c8faf0c275665786f90e260443739cdb Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Wed, 21 Dec 2011 15:50:42 +0100 Subject: [PATCH 7/8] Issue #13645: fix test_import failure when run immediately after test_coding. --- Lib/test/test_import.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_import.py b/Lib/test/test_import.py index 63b8776e776..c69575729d6 100644 --- a/Lib/test/test_import.py +++ b/Lib/test/test_import.py @@ -107,8 +107,9 @@ class ImportTests(unittest.TestCase): open(fname, 'w').close() os.chmod(fname, (stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)) - __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") From 65a3f4b8c57a761cfe0e6ee14565db421c50f4c0 Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Wed, 21 Dec 2011 16:52:40 +0100 Subject: [PATCH 8/8] Use context managers in test_ssl to simplify test writing. --- Lib/test/test_ssl.py | 103 +++++++++++++++---------------------------- 1 file changed, 35 insertions(+), 68 deletions(-) diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index f942d956587..e9fbc8afebb 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -881,6 +881,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) @@ -993,6 +1001,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) @@ -1020,12 +1042,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, @@ -1045,9 +1062,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): @@ -1058,12 +1072,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)]: @@ -1086,9 +1095,6 @@ else: if support.verbose: sys.stdout.write(" client: closing connection.\n") s.close() - finally: - server.stop() - server.join() def try_protocol_combo(server_protocol, client_protocol, expect_success, certsreqs=None, server_options=0, client_options=0): @@ -1157,12 +1163,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() @@ -1185,9 +1186,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""" @@ -1346,13 +1344,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)) @@ -1399,9 +1392,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.""" @@ -1437,12 +1427,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: @@ -1463,15 +1448,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.""" @@ -1484,19 +1460,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) @@ -1581,12 +1552,8 @@ else: ) # consume data s.read() - 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